summaryrefslogtreecommitdiffstats
path: root/sc/source/ui/view
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
commit267c6f2ac71f92999e969232431ba04678e7437e (patch)
tree358c9467650e1d0a1d7227a21dac2e3d08b622b2 /sc/source/ui/view
parentInitial commit. (diff)
downloadlibreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz
libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sc/source/ui/view')
-rw-r--r--sc/source/ui/view/SparklineShell.cxx54
-rw-r--r--sc/source/ui/view/auditsh.cxx123
-rw-r--r--sc/source/ui/view/cellmergeoption.cxx49
-rw-r--r--sc/source/ui/view/cellsh.cxx1325
-rw-r--r--sc/source/ui/view/cellsh1.cxx3600
-rw-r--r--sc/source/ui/view/cellsh2.cxx1256
-rw-r--r--sc/source/ui/view/cellsh3.cxx1096
-rw-r--r--sc/source/ui/view/cellsh4.cxx522
-rw-r--r--sc/source/ui/view/cliputil.cxx169
-rw-r--r--sc/source/ui/view/colrowba.cxx383
-rw-r--r--sc/source/ui/view/dbfunc.cxx466
-rw-r--r--sc/source/ui/view/dbfunc2.cxx41
-rw-r--r--sc/source/ui/view/dbfunc3.cxx2312
-rw-r--r--sc/source/ui/view/dbfunc4.cxx73
-rw-r--r--sc/source/ui/view/drawutil.cxx89
-rw-r--r--sc/source/ui/view/drawvie3.cxx259
-rw-r--r--sc/source/ui/view/drawvie4.cxx572
-rw-r--r--sc/source/ui/view/drawview.cxx1251
-rw-r--r--sc/source/ui/view/editsh.cxx1427
-rw-r--r--sc/source/ui/view/formatsh.cxx2108
-rw-r--r--sc/source/ui/view/gridmerg.cxx225
-rw-r--r--sc/source/ui/view/gridwin.cxx7218
-rw-r--r--sc/source/ui/view/gridwin2.cxx1312
-rw-r--r--sc/source/ui/view/gridwin3.cxx399
-rw-r--r--sc/source/ui/view/gridwin4.cxx2695
-rw-r--r--sc/source/ui/view/gridwin5.cxx420
-rw-r--r--sc/source/ui/view/gridwin_dbgutil.cxx171
-rw-r--r--sc/source/ui/view/hdrcont.cxx1124
-rw-r--r--sc/source/ui/view/hintwin.cxx182
-rw-r--r--sc/source/ui/view/imapwrap.cxx48
-rw-r--r--sc/source/ui/view/imapwrap.hxx40
-rw-r--r--sc/source/ui/view/invmerge.cxx162
-rw-r--r--sc/source/ui/view/notemark.cxx203
-rw-r--r--sc/source/ui/view/olinewin.cxx1040
-rw-r--r--sc/source/ui/view/output.cxx2773
-rw-r--r--sc/source/ui/view/output2.cxx5248
-rw-r--r--sc/source/ui/view/output3.cxx267
-rw-r--r--sc/source/ui/view/overlayobject.cxx89
-rw-r--r--sc/source/ui/view/pfuncache.cxx191
-rw-r--r--sc/source/ui/view/pgbrksh.cxx53
-rw-r--r--sc/source/ui/view/pivotsh.cxx167
-rw-r--r--sc/source/ui/view/preview.cxx1575
-rw-r--r--sc/source/ui/view/prevloc.cxx718
-rw-r--r--sc/source/ui/view/prevwsh.cxx1181
-rw-r--r--sc/source/ui/view/prevwsh2.cxx68
-rw-r--r--sc/source/ui/view/printfun.cxx3204
-rw-r--r--sc/source/ui/view/reffact.cxx299
-rw-r--r--sc/source/ui/view/scextopt.cxx218
-rw-r--r--sc/source/ui/view/select.cxx986
-rw-r--r--sc/source/ui/view/selectionstate.cxx54
-rw-r--r--sc/source/ui/view/spellcheckcontext.cxx387
-rw-r--r--sc/source/ui/view/spelldialog.cxx281
-rw-r--r--sc/source/ui/view/spelleng.cxx448
-rw-r--r--sc/source/ui/view/tabcont.cxx666
-rw-r--r--sc/source/ui/view/tabsplit.cxx127
-rw-r--r--sc/source/ui/view/tabview.cxx3162
-rw-r--r--sc/source/ui/view/tabview2.cxx1709
-rw-r--r--sc/source/ui/view/tabview3.cxx3171
-rw-r--r--sc/source/ui/view/tabview4.cxx538
-rw-r--r--sc/source/ui/view/tabview5.cxx704
-rw-r--r--sc/source/ui/view/tabvwsh.cxx121
-rw-r--r--sc/source/ui/view/tabvwsh2.cxx472
-rw-r--r--sc/source/ui/view/tabvwsh3.cxx1398
-rw-r--r--sc/source/ui/view/tabvwsh4.cxx1946
-rw-r--r--sc/source/ui/view/tabvwsh5.cxx390
-rw-r--r--sc/source/ui/view/tabvwsh8.cxx76
-rw-r--r--sc/source/ui/view/tabvwsh9.cxx203
-rw-r--r--sc/source/ui/view/tabvwsha.cxx1817
-rw-r--r--sc/source/ui/view/tabvwshb.cxx919
-rw-r--r--sc/source/ui/view/tabvwshc.cxx775
-rw-r--r--sc/source/ui/view/tabvwshd.cxx67
-rw-r--r--sc/source/ui/view/tabvwshe.cxx322
-rw-r--r--sc/source/ui/view/tabvwshf.cxx1094
-rw-r--r--sc/source/ui/view/tabvwshg.cxx120
-rw-r--r--sc/source/ui/view/tabvwshh.cxx261
-rw-r--r--sc/source/ui/view/viewdata.cxx4365
-rw-r--r--sc/source/ui/view/viewfun2.cxx3487
-rw-r--r--sc/source/ui/view/viewfun3.cxx2028
-rw-r--r--sc/source/ui/view/viewfun4.cxx791
-rw-r--r--sc/source/ui/view/viewfun5.cxx818
-rw-r--r--sc/source/ui/view/viewfun6.cxx559
-rw-r--r--sc/source/ui/view/viewfun7.cxx462
-rw-r--r--sc/source/ui/view/viewfunc.cxx3183
-rw-r--r--sc/source/ui/view/viewutil.cxx426
-rw-r--r--sc/source/ui/view/waitoff.cxx51
85 files changed, 86849 insertions, 0 deletions
diff --git a/sc/source/ui/view/SparklineShell.cxx b/sc/source/ui/view/SparklineShell.cxx
new file mode 100644
index 0000000000..3b3c4f8399
--- /dev/null
+++ b/sc/source/ui/view/SparklineShell.cxx
@@ -0,0 +1,54 @@
+/* -*- 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/.
+ *
+ */
+
+#include <scitems.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/request.hxx>
+#include <svl/whiter.hxx>
+#include <vcl/EnumContext.hxx>
+
+#include <sc.hrc>
+#include <SparklineShell.hxx>
+#include <tabvwsh.hxx>
+#include <docsh.hxx>
+#include <document.hxx>
+
+#define ShellClass_SparklineShell
+#include <scslots.hxx>
+
+namespace sc
+{
+SFX_IMPL_INTERFACE(SparklineShell, SfxShell)
+
+void SparklineShell::InitInterface_Impl() { GetStaticInterface()->RegisterPopupMenu("sparkline"); }
+
+SparklineShell::SparklineShell(ScTabViewShell* pViewShell)
+ : SfxShell(pViewShell)
+ , m_pViewShell(pViewShell)
+{
+ SetPool(&m_pViewShell->GetPool());
+ ScViewData& rViewData = m_pViewShell->GetViewData();
+ SfxUndoManager* pUndoManager = rViewData.GetSfxDocShell()->GetUndoManager();
+ SetUndoManager(pUndoManager);
+ if (!rViewData.GetDocument().IsUndoEnabled())
+ {
+ pUndoManager->SetMaxUndoActionCount(0);
+ }
+ SetName("Sparkline");
+ SfxShell::SetContextName(
+ vcl::EnumContext::GetContextName(vcl::EnumContext::Context::Sparkline));
+}
+
+SparklineShell::~SparklineShell() = default;
+
+} // end sc namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/auditsh.cxx b/sc/source/ui/view/auditsh.cxx
new file mode 100644
index 0000000000..2ea903f59e
--- /dev/null
+++ b/sc/source/ui/view/auditsh.cxx
@@ -0,0 +1,123 @@
+/* -*- 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 .
+ */
+
+#include <sfx2/bindings.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/request.hxx>
+#include <vcl/EnumContext.hxx>
+
+#include <auditsh.hxx>
+#include <tabvwsh.hxx>
+#include <sc.hrc>
+#include <document.hxx>
+
+#define ShellClass_ScAuditingShell
+#include <scslots.hxx>
+
+
+SFX_IMPL_INTERFACE(ScAuditingShell, SfxShell)
+
+void ScAuditingShell::InitInterface_Impl()
+{
+ GetStaticInterface()->RegisterPopupMenu("audit");
+}
+
+ScAuditingShell::ScAuditingShell(ScViewData& rData) :
+ SfxShell(rData.GetViewShell()),
+ rViewData( rData ),
+ nFunction( SID_FILL_ADD_PRED )
+{
+ SetPool( &rViewData.GetViewShell()->GetPool() );
+ SfxUndoManager* pMgr = rViewData.GetSfxDocShell()->GetUndoManager();
+ SetUndoManager( pMgr );
+ if ( !rViewData.GetDocument().IsUndoEnabled() )
+ {
+ pMgr->SetMaxUndoActionCount( 0 );
+ }
+ SetName("Auditing");
+ SfxShell::SetContextName(vcl::EnumContext::GetContextName(vcl::EnumContext::Context::Auditing));
+}
+
+ScAuditingShell::~ScAuditingShell()
+{
+}
+
+void ScAuditingShell::Execute( const SfxRequest& rReq )
+{
+ SfxBindings& rBindings = rViewData.GetBindings();
+ sal_uInt16 nSlot = rReq.GetSlot();
+ switch ( nSlot )
+ {
+ case SID_FILL_ADD_PRED:
+ case SID_FILL_DEL_PRED:
+ case SID_FILL_ADD_SUCC:
+ case SID_FILL_DEL_SUCC:
+ nFunction = nSlot;
+ rBindings.Invalidate( SID_FILL_ADD_PRED );
+ rBindings.Invalidate( SID_FILL_DEL_PRED );
+ rBindings.Invalidate( SID_FILL_ADD_SUCC );
+ rBindings.Invalidate( SID_FILL_DEL_SUCC );
+ break;
+ case SID_CANCEL: // Escape
+ case SID_FILL_NONE:
+ rViewData.GetViewShell()->SetAuditShell( false );
+ break;
+
+ case SID_FILL_SELECT:
+ {
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+ if ( pReqArgs )
+ {
+ const SfxInt16Item* pXItem = pReqArgs->GetItemIfSet( SID_RANGE_COL );
+ const SfxInt32Item* pYItem = pReqArgs->GetItemIfSet( SID_RANGE_ROW );
+ if ( pXItem && pYItem )
+ {
+ SCCOL nCol = static_cast<SCCOL>(pXItem->GetValue());
+ SCROW nRow = static_cast<SCROW>(pYItem->GetValue());
+ ScViewFunc* pView = rViewData.GetView();
+ pView->MoveCursorAbs( nCol, nRow, SC_FOLLOW_LINE, false, false );
+ switch ( nFunction )
+ {
+ case SID_FILL_ADD_PRED:
+ pView->DetectiveAddPred();
+ break;
+ case SID_FILL_DEL_PRED:
+ pView->DetectiveDelPred();
+ break;
+ case SID_FILL_ADD_SUCC:
+ pView->DetectiveAddSucc();
+ break;
+ case SID_FILL_DEL_SUCC:
+ pView->DetectiveDelSucc();
+ break;
+ }
+ }
+ }
+ }
+ break;
+ }
+}
+
+void ScAuditingShell::GetState( SfxItemSet& rSet )
+{
+ rSet.Put( SfxBoolItem( nFunction, true ) ); // mark active functions
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/cellmergeoption.cxx b/sc/source/ui/view/cellmergeoption.cxx
new file mode 100644
index 0000000000..524117080f
--- /dev/null
+++ b/sc/source/ui/view/cellmergeoption.cxx
@@ -0,0 +1,49 @@
+/* -*- 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/.
+ */
+
+#include <cellmergeoption.hxx>
+#include <address.hxx>
+
+ScCellMergeOption::ScCellMergeOption(const ScRange& rRange) :
+ mnStartCol(rRange.aStart.Col()),
+ mnStartRow(rRange.aStart.Row()),
+ mnEndCol(rRange.aEnd.Col()),
+ mnEndRow(rRange.aEnd.Row()),
+ mbCenter(false)
+{
+ SCTAB nTab1 = rRange.aStart.Tab();
+ SCTAB nTab2 = rRange.aEnd.Tab();
+ for (SCTAB i = nTab1; i <= nTab2; ++i)
+ maTabs.insert(i);
+}
+
+ScCellMergeOption::ScCellMergeOption(SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, bool bCenter) :
+ mnStartCol(nStartCol),
+ mnStartRow(nStartRow),
+ mnEndCol(nEndCol),
+ mnEndRow(nEndRow),
+ mbCenter(bCenter)
+{
+}
+
+ScRange ScCellMergeOption::getSingleRange(SCTAB nTab) const
+{
+ return ScRange(mnStartCol, mnStartRow, nTab, mnEndCol, mnEndRow, nTab);
+}
+
+ScRange ScCellMergeOption::getFirstSingleRange() const
+{
+ SCTAB nTab = 0;
+ if (!maTabs.empty())
+ nTab = *maTabs.begin();
+
+ return getSingleRange(nTab);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/cellsh.cxx b/sc/source/ui/view/cellsh.cxx
new file mode 100644
index 0000000000..1a866df3c8
--- /dev/null
+++ b/sc/source/ui/view/cellsh.cxx
@@ -0,0 +1,1325 @@
+/* -*- 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 .
+ */
+
+#include <scitems.hxx>
+
+#include <svl/slstitm.hxx>
+#include <svl/stritem.hxx>
+#include <svl/whiter.hxx>
+#include <svtools/cliplistener.hxx>
+#include <svtools/insdlg.hxx>
+#include <sot/formats.hxx>
+#include <svx/hlnkitem.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/childwin.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <vcl/EnumContext.hxx>
+#include <vcl/svapp.hxx>
+#include <svx/clipfmtitem.hxx>
+#include <svx/statusitem.hxx>
+
+#include <cellsh.hxx>
+#include <sc.hrc>
+#include <docsh.hxx>
+#include <attrib.hxx>
+#include <tabvwsh.hxx>
+#include <formulacell.hxx>
+#include <scmod.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <transobj.hxx>
+#include <drwtrans.hxx>
+#include <scabstdlg.hxx>
+#include <postit.hxx>
+#include <cliputil.hxx>
+#include <clipparam.hxx>
+#include <markdata.hxx>
+#include <gridwin.hxx>
+#include <Sparkline.hxx>
+#include <SparklineGroup.hxx>
+
+#define ShellClass_ScCellShell
+#define ShellClass_CellMovement
+#include <scslots.hxx>
+
+
+SFX_IMPL_INTERFACE(ScCellShell, ScFormatShell)
+
+void ScCellShell::InitInterface_Impl()
+{
+ GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT,
+ SfxVisibilityFlags::Standard | SfxVisibilityFlags::Server,
+ ToolbarId::Objectbar_Format);
+
+ GetStaticInterface()->RegisterPopupMenu("cell");
+}
+
+ScCellShell::ScCellShell(ScViewData& rData, const VclPtr<vcl::Window>& frameWin) :
+ ScFormatShell(rData),
+ pImpl( new CellShell_Impl() ),
+ bPastePossible(false),
+ pFrameWin(frameWin)
+{
+ SetName("Cell");
+ SfxShell::SetContextName(vcl::EnumContext::GetContextName(vcl::EnumContext::Context::Cell));
+}
+
+ScCellShell::~ScCellShell()
+{
+ if ( pImpl->m_xClipEvtLstnr.is() )
+ {
+ pImpl->m_xClipEvtLstnr->RemoveListener( GetViewData().GetActiveWin() );
+
+ // The listener may just now be waiting for the SolarMutex and call the link
+ // afterwards, in spite of RemoveListener. So the link has to be reset, too.
+ pImpl->m_xClipEvtLstnr->ClearCallbackLink();
+
+ pImpl->m_xClipEvtLstnr.clear();
+ }
+
+ pImpl->m_pLinkedDlg.disposeAndClear();
+ delete pImpl->m_pRequest;
+}
+
+void ScCellShell::GetBlockState( SfxItemSet& rSet )
+{
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ ScRange aMarkRange;
+ ScMarkType eMarkType = GetViewData().GetSimpleArea( aMarkRange );
+ bool bSimpleArea = (eMarkType == SC_MARK_SIMPLE);
+ bool bOnlyNotBecauseOfMatrix;
+ bool bEditable = pTabViewShell->SelectionEditable( &bOnlyNotBecauseOfMatrix );
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScDocShell* pDocShell = GetViewData().GetDocShell();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ SCCOL nCol1, nCol2;
+ SCROW nRow1, nRow2;
+ nCol1 = aMarkRange.aStart.Col();
+ nRow1 = aMarkRange.aStart.Row();
+ nCol2 = aMarkRange.aEnd.Col();
+ nRow2 = aMarkRange.aEnd.Row();
+ SCTAB nTab = GetViewData().GetTabNo();
+
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while ( nWhich )
+ {
+ bool bDisable = false;
+ bool bNeedEdit = true; // need selection be editable?
+ switch ( nWhich )
+ {
+ case FID_FILL_TO_BOTTOM: // fill to top / bottom
+ {
+ bDisable = !bSimpleArea || (nRow1 == 0 && nRow2 == 0);
+ if (!bDisable && GetViewData().SelectionForbidsCellFill())
+ bDisable = true;
+ if ( !bDisable && bEditable )
+ { // do not damage matrix
+ bDisable = rDoc.HasSelectedBlockMatrixFragment(
+ nCol1, nRow1, nCol2, nRow1, rMark ); // first row
+ }
+ }
+ break;
+ case FID_FILL_TO_TOP:
+ {
+ bDisable = (!bSimpleArea) || (nRow1 == rDoc.MaxRow() && nRow2 == rDoc.MaxRow());
+ if (!bDisable && GetViewData().SelectionForbidsCellFill())
+ bDisable = true;
+ if ( !bDisable && bEditable )
+ { // do not damage matrix
+ bDisable = rDoc.HasSelectedBlockMatrixFragment(
+ nCol1, nRow2, nCol2, nRow2, rMark ); // last row
+ }
+ }
+ break;
+ case FID_FILL_TO_RIGHT: // fill to left / right
+ {
+ bDisable = !bSimpleArea || (nCol1 == 0 && nCol2 == 0);
+ if (!bDisable && GetViewData().SelectionForbidsCellFill())
+ bDisable = true;
+ if ( !bDisable && bEditable )
+ { // do not damage matrix
+ bDisable = rDoc.HasSelectedBlockMatrixFragment(
+ nCol1, nRow1, nCol1, nRow2, rMark ); // first column
+ }
+ }
+ break;
+ case FID_FILL_TO_LEFT:
+ {
+ bDisable = (!bSimpleArea) || (nCol1 == rDoc.MaxCol() && nCol2 == rDoc.MaxCol());
+ if (!bDisable && GetViewData().SelectionForbidsCellFill())
+ bDisable = true;
+ if ( !bDisable && bEditable )
+ { // do not damage matrix
+ bDisable = rDoc.HasSelectedBlockMatrixFragment(
+ nCol2, nRow1, nCol2, nRow2, rMark ); // last column
+ }
+ }
+ break;
+
+ case SID_RANDOM_NUMBER_GENERATOR_DIALOG:
+ bDisable = !bSimpleArea || GetViewData().SelectionForbidsCellFill();
+ break;
+ case SID_SAMPLING_DIALOG:
+ case SID_DESCRIPTIVE_STATISTICS_DIALOG:
+ case SID_ANALYSIS_OF_VARIANCE_DIALOG:
+ case SID_CORRELATION_DIALOG:
+ case SID_COVARIANCE_DIALOG:
+ case SID_INSERT_SPARKLINE:
+ {
+ bDisable = !bSimpleArea;
+ }
+ break;
+ case SID_GROUP_SPARKLINES:
+ case SID_UNGROUP_SPARKLINES:
+ {
+ bDisable = !bSimpleArea;
+ }
+ break;
+
+ case SID_EDIT_SPARKLINE:
+ {
+ bDisable = !rDoc.HasSparkline(GetViewData().GetCurPos());
+ }
+ break;
+
+ case SID_DELETE_SPARKLINE:
+ case SID_EDIT_SPARKLINE_GROUP:
+ case SID_DELETE_SPARKLINE_GROUP:
+ {
+ bDisable = !rDoc.HasOneSparklineGroup(ScRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab));
+ }
+ break;
+
+ case FID_FILL_SERIES: // fill block
+ case SID_OPENDLG_TABOP: // multiple-cell operations, are at least 2 cells marked?
+ if (rDoc.GetChangeTrack()!=nullptr &&nWhich ==SID_OPENDLG_TABOP)
+ bDisable = true;
+ else
+ bDisable = (!bSimpleArea) || (nCol1 == nCol2 && nRow1 == nRow2);
+
+ if (!bDisable && GetViewData().SelectionForbidsCellFill())
+ bDisable = true;
+
+ if ( !bDisable && bEditable && nWhich == FID_FILL_SERIES )
+ { // do not damage matrix
+ bDisable = rDoc.HasSelectedBlockMatrixFragment(
+ nCol1, nRow1, nCol2, nRow1, rMark ) // first row
+ || rDoc.HasSelectedBlockMatrixFragment(
+ nCol1, nRow2, nCol2, nRow2, rMark ) // last row
+ || rDoc.HasSelectedBlockMatrixFragment(
+ nCol1, nRow1, nCol1, nRow2, rMark ) // first column
+ || rDoc.HasSelectedBlockMatrixFragment(
+ nCol2, nRow1, nCol2, nRow2, rMark ); // last column
+ }
+ break;
+ case FID_FILL_SINGLE_EDIT:
+ bDisable = false;
+ break;
+ case SID_CUT: // cut
+ bDisable = !bSimpleArea || GetObjectShell()->isContentExtractionLocked();
+ break;
+ case FID_INS_CELL: // insert cells, just simple selection
+ bDisable = (!bSimpleArea);
+ break;
+
+ case SID_PASTE:
+ case SID_PASTE_SPECIAL:
+ case SID_PASTE_UNFORMATTED:
+ case SID_PASTE_ONLY_VALUE:
+ case SID_PASTE_ONLY_TEXT:
+ case SID_PASTE_ONLY_FORMULA:
+ case SID_PASTE_TRANSPOSED:
+ case SID_PASTE_AS_LINK:
+ case SID_PASTE_TEXTIMPORT_DIALOG:
+ bDisable = GetViewData().SelectionForbidsPaste();
+ break;
+
+ case FID_INS_ROW:
+ case FID_INS_ROWS_BEFORE: // insert rows
+ case FID_INS_ROWS_AFTER:
+ {
+ sc::ColRowEditAction eAction = sc::ColRowEditAction::InsertRowsBefore;
+ if (nWhich == FID_INS_ROWS_AFTER)
+ eAction = sc::ColRowEditAction::InsertRowsAfter;
+
+ bDisable = (!bSimpleArea) || GetViewData().SimpleColMarked();
+ if (!bEditable && nCol1 == 0 && nCol2 == rDoc.MaxCol())
+ {
+ // See if row insertions are allowed.
+ bEditable = rDoc.IsEditActionAllowed(eAction, rMark, nRow1, nRow2);
+ }
+ break;
+ }
+ case FID_INS_CELLSDOWN:
+ bDisable = (!bSimpleArea) || GetViewData().SimpleColMarked();
+ break;
+
+ case FID_INS_COLUMN:
+ case FID_INS_COLUMNS_BEFORE: // insert columns
+ case FID_INS_COLUMNS_AFTER:
+ {
+ sc::ColRowEditAction eAction = sc::ColRowEditAction::InsertColumnsBefore;
+ if (nWhich == FID_INS_COLUMNS_AFTER)
+ eAction = sc::ColRowEditAction::InsertColumnsAfter;
+
+ bDisable = (!bSimpleArea && eMarkType != SC_MARK_SIMPLE_FILTERED)
+ || GetViewData().SimpleRowMarked();
+ if (!bEditable && nRow1 == 0 && nRow2 == rDoc.MaxRow())
+ {
+ // See if row insertions are allowed.
+ bEditable = rDoc.IsEditActionAllowed(eAction, rMark, nCol1, nCol2);
+ }
+ break;
+ }
+ case FID_INS_CELLSRIGHT:
+ bDisable = (!bSimpleArea) || GetViewData().SimpleRowMarked();
+ break;
+
+ case SID_COPY: // copy
+ // not editable because of matrix only? Do not damage matrix
+ //! is not called, when protected AND matrix, we will have
+ //! to live with this... is caught in Copy-Routine, otherwise
+ //! work is to be done once more
+ if ( bEditable || !bOnlyNotBecauseOfMatrix )
+ bNeedEdit = false; // allowed when protected/ReadOnly
+ bDisable = GetObjectShell()->isContentExtractionLocked();
+ break;
+
+ case SID_AUTOFORMAT: // Autoformat, at least 3x3 selected
+ bDisable = (!bSimpleArea)
+ || ((nCol2 - nCol1) < 2) || ((nRow2 - nRow1) < 2);
+ break;
+
+ case SID_CELL_FORMAT_RESET :
+ case FID_CELL_FORMAT :
+ case SID_ENABLE_HYPHENATION :
+ // not editable because of matrix only? Attribute ok nonetheless
+ if ( !bEditable && bOnlyNotBecauseOfMatrix )
+ bNeedEdit = false;
+ break;
+
+ case FID_VALIDATION:
+ {
+ if ( pDocShell && pDocShell->IsDocShared() )
+ {
+ bDisable = true;
+ }
+ }
+ break;
+ case SID_TRANSLITERATE_HALFWIDTH:
+ case SID_TRANSLITERATE_FULLWIDTH:
+ case SID_TRANSLITERATE_HIRAGANA:
+ case SID_TRANSLITERATE_KATAKANA:
+ ScViewUtil::HideDisabledSlot( rSet, GetViewData().GetBindings(), nWhich );
+ break;
+ case SID_CONVERT_FORMULA_TO_VALUE:
+ {
+ // Check and see if the marked range has at least one formula cell.
+ bDisable = !rDoc.HasFormulaCell(aMarkRange);
+ }
+ break;
+ }
+ if (!bDisable && bNeedEdit && !bEditable)
+ bDisable = true;
+
+ if (bDisable)
+ rSet.DisableItem(nWhich);
+ else if (nWhich == SID_ENABLE_HYPHENATION)
+ {
+ // toggle slots need a bool item
+ rSet.Put( SfxBoolItem( nWhich, false ) );
+ }
+ nWhich = aIter.NextWhich();
+ }
+}
+
+// functions, disabled depending on cursor position
+// Default:
+// SID_INSERT_POSTIT, SID_CHARMAP, SID_OPENDLG_FUNCTION
+
+void ScCellShell::GetCellState( SfxItemSet& rSet )
+{
+ ScDocShell* pDocShell = GetViewData().GetDocShell();
+ ScDocument& rDoc = GetViewData().GetDocShell()->GetDocument();
+ ScAddress aCursor( GetViewData().GetCurX(), GetViewData().GetCurY(),
+ GetViewData().GetTabNo() );
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while ( nWhich )
+ {
+ bool bDisable = false;
+ bool bNeedEdit = true; // need cursor position be editable?
+ switch ( nWhich )
+ {
+ case SID_THESAURUS:
+ {
+ CellType eType = rDoc.GetCellType( aCursor );
+ bDisable = ( eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT);
+ if (!bDisable)
+ {
+ // test for available languages
+ LanguageType nLang = ScViewUtil::GetEffLanguage( rDoc, aCursor );
+ bDisable = !ScModule::HasThesaurusLanguage( nLang );
+ }
+ }
+ break;
+ case SID_OPENDLG_FUNCTION:
+ {
+ ScMarkData aMarkData = GetViewData().GetMarkData();
+ aMarkData.MarkToSimple();
+ const ScRange& aRange = aMarkData.GetMarkArea();
+ if(aMarkData.IsMarked())
+ {
+ if (!rDoc.IsBlockEditable( aCursor.Tab(), aRange.aStart.Col(),aRange.aStart.Row(),
+ aRange.aEnd.Col(),aRange.aEnd.Row() ))
+ {
+ bDisable = true;
+ }
+ bNeedEdit=false;
+ }
+
+ }
+ break;
+ case SID_INSERT_POSTIT:
+ {
+ ScAddress aPos( GetViewData().GetCurX(), GetViewData().GetCurY(), GetViewData().GetTabNo() );
+ if( rDoc.GetNote(aPos) )
+ {
+ bDisable = true;
+ }
+ else
+ {
+ bDisable = false;
+ if ( pDocShell && pDocShell->IsDocShared() )
+ {
+ bDisable = true;
+ }
+ }
+ }
+ break;
+ case SID_EDIT_POSTIT:
+ {
+ ScAddress aPos( GetViewData().GetCurX(), GetViewData().GetCurY(), GetViewData().GetTabNo() );
+ bDisable = rDoc.GetNote(aPos) == nullptr;
+ }
+ break;
+ }
+ if (!bDisable && bNeedEdit)
+ if (!rDoc.IsBlockEditable( aCursor.Tab(), aCursor.Col(),aCursor.Row(),
+ aCursor.Col(),aCursor.Row() ))
+ bDisable = true;
+ if (bDisable)
+ rSet.DisableItem(nWhich);
+ nWhich = aIter.NextWhich();
+ }
+}
+
+static bool lcl_TestFormat( SvxClipboardFormatItem& rFormats, const TransferableDataHelper& rDataHelper,
+ SotClipboardFormatId nFormatId )
+{
+ if ( rDataHelper.HasFormat( nFormatId ) )
+ {
+ // translated format name strings are no longer inserted here,
+ // handled by "paste special" dialog / toolbox controller instead.
+ // Only the object type name has to be set here:
+ OUString aStrVal;
+ if ( nFormatId == SotClipboardFormatId::EMBED_SOURCE )
+ {
+ TransferableObjectDescriptor aDesc;
+ if ( rDataHelper.GetTransferableObjectDescriptor(
+ SotClipboardFormatId::OBJECTDESCRIPTOR, aDesc ) )
+ aStrVal = aDesc.maTypeName;
+ }
+ else if ( nFormatId == SotClipboardFormatId::EMBED_SOURCE_OLE
+ || nFormatId == SotClipboardFormatId::EMBEDDED_OBJ_OLE )
+ {
+ OUString aSource;
+ SvPasteObjectHelper::GetEmbeddedName( rDataHelper, aStrVal, aSource, nFormatId );
+ }
+
+ if ( !aStrVal.isEmpty() )
+ rFormats.AddClipbrdFormat( nFormatId, aStrVal );
+ else
+ rFormats.AddClipbrdFormat( nFormatId );
+
+ return true;
+ }
+
+ return false;
+}
+
+void ScCellShell::GetPossibleClipboardFormats( SvxClipboardFormatItem& rFormats )
+{
+ vcl::Window* pWin = GetViewData().GetActiveWin();
+ bool bDraw = ScDrawTransferObj::GetOwnClipboard(ScTabViewShell::GetClipData(pWin)) != nullptr;
+
+ TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( pWin ) );
+
+ lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::DRAWING );
+ lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::SVXB );
+ lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::GDIMETAFILE );
+ lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::PNG );
+ lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::BITMAP );
+ lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::EMBED_SOURCE );
+
+ if ( !bDraw )
+ {
+ lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::LINK );
+ lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::STRING );
+ lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::STRING_TSVC );
+ lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::DIF );
+ lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::RTF );
+ lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::RICHTEXT );
+ lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::HTML );
+ lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::HTML_SIMPLE );
+ lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::BIFF_8 );
+ lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::BIFF_5 );
+ }
+
+ if ( !lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::EMBED_SOURCE_OLE ) )
+ lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::EMBEDDED_OBJ_OLE );
+}
+
+// insert, insert contents
+
+static bool lcl_IsCellPastePossible( const TransferableDataHelper& rData )
+{
+ bool bPossible = false;
+ css::uno::Reference< css::datatransfer::XTransferable2 > xTransferable(rData.GetXTransferable(), css::uno::UNO_QUERY);
+ if ( ScTransferObj::GetOwnClipboard(xTransferable) || ScDrawTransferObj::GetOwnClipboard(xTransferable) )
+ bPossible = true;
+ else
+ {
+ if ( rData.HasFormat( SotClipboardFormatId::PNG ) ||
+ rData.HasFormat( SotClipboardFormatId::BITMAP ) ||
+ rData.HasFormat( SotClipboardFormatId::GDIMETAFILE ) ||
+ rData.HasFormat( SotClipboardFormatId::SVXB ) ||
+ rData.HasFormat( SotClipboardFormatId::PRIVATE ) ||
+ rData.HasFormat( SotClipboardFormatId::RTF ) ||
+ rData.HasFormat( SotClipboardFormatId::RICHTEXT ) ||
+ rData.HasFormat( SotClipboardFormatId::EMBED_SOURCE ) ||
+ rData.HasFormat( SotClipboardFormatId::LINK_SOURCE ) ||
+ rData.HasFormat( SotClipboardFormatId::EMBED_SOURCE_OLE ) ||
+ rData.HasFormat( SotClipboardFormatId::LINK_SOURCE_OLE ) ||
+ rData.HasFormat( SotClipboardFormatId::EMBEDDED_OBJ_OLE ) ||
+ rData.HasFormat( SotClipboardFormatId::STRING ) ||
+ rData.HasFormat( SotClipboardFormatId::STRING_TSVC ) ||
+ rData.HasFormat( SotClipboardFormatId::SYLK ) ||
+ rData.HasFormat( SotClipboardFormatId::LINK ) ||
+ rData.HasFormat( SotClipboardFormatId::HTML ) ||
+ rData.HasFormat( SotClipboardFormatId::HTML_SIMPLE ) ||
+ rData.HasFormat( SotClipboardFormatId::DIF ) )
+ {
+ bPossible = true;
+ }
+ }
+ return bPossible;
+}
+
+bool ScCellShell::HasClipboardFormat( SotClipboardFormatId nFormatId )
+{
+ vcl::Window* pWin = GetViewData().GetActiveWin();
+ TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( pWin ));
+ return aDataHelper.HasFormat( nFormatId );
+}
+
+IMPL_LINK( ScCellShell, ClipboardChanged, TransferableDataHelper*, pDataHelper, void )
+{
+ bPastePossible = lcl_IsCellPastePossible( *pDataHelper );
+
+ SfxBindings& rBindings = GetViewData().GetBindings();
+ rBindings.Invalidate( SID_PASTE );
+ rBindings.Invalidate( SID_PASTE_SPECIAL );
+ rBindings.Invalidate( SID_PASTE_UNFORMATTED );
+ rBindings.Invalidate( SID_PASTE_ONLY_VALUE );
+ rBindings.Invalidate( SID_PASTE_ONLY_TEXT );
+ rBindings.Invalidate( SID_PASTE_ONLY_FORMULA );
+ rBindings.Invalidate( SID_PASTE_TRANSPOSED );
+ rBindings.Invalidate( SID_PASTE_AS_LINK );
+ rBindings.Invalidate( SID_PASTE_TEXTIMPORT_DIALOG );
+ rBindings.Invalidate( SID_CLIPBOARD_FORMAT_ITEMS );
+}
+
+namespace {
+
+bool checkDestRanges(ScViewData& rViewData)
+{
+ ScRange aDummy;
+ ScMarkType eMarkType = rViewData.GetSimpleArea( aDummy);
+ if (eMarkType != SC_MARK_MULTI)
+ {
+ // Single destination range.
+ if (eMarkType != SC_MARK_SIMPLE && eMarkType != SC_MARK_SIMPLE_FILTERED)
+ return false;
+ }
+
+ // Multiple destination ranges.
+
+ // Same as ScViewData::SelectionForbidsPaste() in
+ // sc/source/ui/view/viewdata.cxx but different return details.
+
+ vcl::Window* pWin = rViewData.GetActiveWin();
+ if (!pWin)
+ return false;
+
+ const ScTransferObj* pOwnClip = ScTransferObj::GetOwnClipboard(ScTabViewShell::GetClipData(pWin));
+ if (!pOwnClip)
+ // If it's not a Calc document, we won't be picky.
+ return true;
+
+ ScDocument* pClipDoc = pOwnClip->GetDocument();
+ if (!pClipDoc)
+ return false;
+
+ ScRange aSrcRange = pClipDoc->GetClipParam().getWholeRange();
+ SCROW nRowSize = aSrcRange.aEnd.Row() - aSrcRange.aStart.Row() + 1;
+ SCCOL nColSize = aSrcRange.aEnd.Col() - aSrcRange.aStart.Col() + 1;
+
+ if (rViewData.SelectionForbidsPaste( nColSize, nRowSize))
+ return false;
+
+ ScMarkData aMark = rViewData.GetMarkData();
+ ScRangeList aRanges;
+ aMark.MarkToSimple();
+ aMark.FillRangeListWithMarks(&aRanges, false);
+
+ return ScClipUtil::CheckDestRanges(rViewData.GetDocument(), nColSize, nRowSize, aMark, aRanges);
+}
+
+}
+
+void ScCellShell::GetClipState( SfxItemSet& rSet )
+{
+// SID_PASTE
+// SID_PASTE_SPECIAL
+// SID_PASTE_UNFORMATTED
+// SID_CLIPBOARD_FORMAT_ITEMS
+
+ if ( !pImpl->m_xClipEvtLstnr.is() )
+ {
+ // create listener
+ pImpl->m_xClipEvtLstnr = new TransferableClipboardListener( LINK( this, ScCellShell, ClipboardChanged ) );
+ vcl::Window* pWin = GetViewData().GetActiveWin();
+ pImpl->m_xClipEvtLstnr->AddListener( pWin );
+
+ // get initial state
+ TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( pWin ) );
+ bPastePossible = lcl_IsCellPastePossible( aDataHelper );
+ }
+
+ bool bDisable = !bPastePossible;
+
+ // cell protection / multiple selection
+
+ if (!bDisable)
+ {
+ SCCOL nCol = GetViewData().GetCurX();
+ SCROW nRow = GetViewData().GetCurY();
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScDocument& rDoc = GetViewData().GetDocShell()->GetDocument();
+ if (!rDoc.IsBlockEditable( nTab, nCol,nRow, nCol,nRow ))
+ bDisable = true;
+
+ if (!bDisable && !checkDestRanges(GetViewData()))
+ bDisable = true;
+ }
+
+ if (bDisable)
+ {
+ rSet.DisableItem( SID_PASTE );
+ rSet.DisableItem( SID_PASTE_SPECIAL );
+ rSet.DisableItem( SID_PASTE_UNFORMATTED );
+ rSet.DisableItem( SID_PASTE_ONLY_VALUE );
+ rSet.DisableItem( SID_PASTE_ONLY_TEXT );
+ rSet.DisableItem( SID_PASTE_ONLY_FORMULA );
+ rSet.DisableItem( SID_PASTE_TRANSPOSED );
+ rSet.DisableItem( SID_PASTE_AS_LINK );
+ rSet.DisableItem( SID_PASTE_TEXTIMPORT_DIALOG );
+ rSet.DisableItem( SID_CLIPBOARD_FORMAT_ITEMS );
+ }
+ else if ( rSet.GetItemState( SID_CLIPBOARD_FORMAT_ITEMS ) != SfxItemState::UNKNOWN )
+ {
+ SvxClipboardFormatItem aFormats( SID_CLIPBOARD_FORMAT_ITEMS );
+ GetPossibleClipboardFormats( aFormats );
+ rSet.Put( aFormats );
+ }
+}
+
+// only SID_HYPERLINK_GETLINK:
+
+void ScCellShell::GetHLinkState( SfxItemSet& rSet )
+{
+ // always return an item (or inserting will be disabled)
+ // if the cell at the cursor contains only a link, return that link
+
+ SvxHyperlinkItem aHLinkItem;
+ if ( !GetViewData().GetView()->HasBookmarkAtCursor( &aHLinkItem ) )
+ {
+ // tdf#80043 - put selected text into item
+ ScViewData& rData = GetViewData();
+ ScDocument& rDoc = rData.GetDocument();
+ SCCOL nPosX = rData.GetCurX();
+ SCROW nPosY = rData.GetCurY();
+ SCTAB nTab = rData.GetTabNo();
+ aHLinkItem.SetName(rDoc.GetString(nPosX, nPosY, nTab));
+ }
+
+ rSet.Put(aHLinkItem);
+}
+
+void ScCellShell::GetState(SfxItemSet &rSet)
+{
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScViewData& rData = GetViewData();
+ ScDocument& rDoc = rData.GetDocument();
+ ScMarkData& rMark = rData.GetMarkData();
+ SCCOL nPosX = rData.GetCurX();
+ SCROW nPosY = rData.GetCurY();
+ SCTAB nTab = rData.GetTabNo();
+
+ SCTAB nTabCount = rDoc.GetTableCount();
+ SCTAB nTabSelCount = rMark.GetSelectCount();
+
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while ( nWhich )
+ {
+ switch ( nWhich )
+ {
+ case SID_DETECTIVE_REFRESH:
+ if (!rDoc.HasDetectiveOperations())
+ rSet.DisableItem( nWhich );
+ break;
+
+ case SID_RANGE_ADDRESS:
+ {
+ ScRange aRange;
+ if ( rData.GetSimpleArea( aRange ) == SC_MARK_SIMPLE )
+ {
+ OUString aStr(aRange.Format(rDoc, ScRefFlags::VALID | ScRefFlags::TAB_3D));
+ rSet.Put( SfxStringItem( nWhich, aStr ) );
+ }
+ }
+ break;
+
+ case SID_RANGE_NOTETEXT:
+ {
+ // always take cursor position, do not use top-left cell of selection
+ OUString aNoteText;
+ if ( const ScPostIt* pNote = rDoc.GetNote(nPosX, nPosY, nTab) )
+ aNoteText = pNote->GetText();
+ rSet.Put( SfxStringItem( nWhich, aNoteText ) );
+ }
+ break;
+
+ case SID_RANGE_ROW:
+ rSet.Put( SfxInt32Item( SID_RANGE_ROW, nPosY+1 ) );
+ break;
+
+ case SID_RANGE_COL:
+ rSet.Put( SfxInt16Item( SID_RANGE_COL, nPosX+1 ) );
+ break;
+
+ case SID_RANGE_TABLE:
+ rSet.Put( SfxInt16Item( SID_RANGE_TABLE, nTab+1 ) );
+ break;
+
+ case SID_RANGE_FORMULA:
+ {
+ OUString aString = rDoc.GetFormula( nPosX, nPosY, nTab );
+ if( aString.isEmpty() )
+ {
+ aString = rDoc.GetInputString( nPosX, nPosY, nTab );
+ }
+ rSet.Put( SfxStringItem( nWhich, aString ) );
+ }
+ break;
+
+ case SID_RANGE_TEXTVALUE:
+ {
+ OUString aString = rDoc.GetString(nPosX, nPosY, nTab);
+ rSet.Put( SfxStringItem( nWhich, aString ) );
+ }
+ break;
+
+ case SID_STATUS_SELMODE:
+ {
+ /* 0: STD Click cancels Sel
+ * 1: ER Click extends selection
+ * 2: ERG Click defines further selection
+ */
+ sal_uInt16 nMode = pTabViewShell->GetLockedModifiers();
+
+ switch ( nMode )
+ {
+ case KEY_SHIFT: nMode = 1; break;
+ case KEY_MOD1: nMode = 2; break; // Control-key
+ case 0:
+ default:
+ nMode = 0;
+ }
+
+ rSet.Put( SfxUInt16Item( nWhich, nMode ) );
+ }
+ break;
+
+ case SID_STATUS_DOCPOS:
+ {
+ OUString aStr = ScResId( STR_TABLE_COUNT );
+
+ aStr = aStr.replaceFirst("%1", OUString::number( nTab + 1 ) );
+ aStr = aStr.replaceFirst("%2", OUString::number( nTabCount ) );
+
+ rSet.Put( SfxStringItem( nWhich, aStr ) ); }
+ break;
+
+ case SID_ROWCOL_SELCOUNT:
+ {
+ ScRangeListRef aMarkRanges;
+ GetViewData().GetMultiArea(aMarkRanges);
+ const SCCOL nCol1 = aMarkRanges->front().aStart.Col();
+ const SCROW nRow1 = aMarkRanges->front().aStart.Row();
+ const SCCOL nCol2 = aMarkRanges->front().aEnd.Col();
+ const SCROW nRow2 = aMarkRanges->front().aEnd.Row();
+ const size_t nRanges = aMarkRanges->size();
+
+ if ((nRanges == 1 && (nCol2 != nCol1 || nRow1 != nRow2)) || nRanges > 1)
+ {
+ bool bSameRows = true;
+ bool bSameCols = true;
+ SCROW nRowsSum = 0;
+ SCCOL nColsSum = 0;
+ for (size_t i = 0; i < nRanges; ++i)
+ {
+ const ScRange& rRange = (*aMarkRanges)[i];
+ const SCCOL nRangeCol1 = rRange.aStart.Col();
+ const SCROW nRangeRow1 = rRange.aStart.Row();
+ const SCCOL nRangeCol2 = rRange.aEnd.Col();
+ const SCROW nRangeRow2 = rRange.aEnd.Row();
+ bSameRows &= (nRow1 == nRangeRow1 && nRow2 == nRangeRow2);
+ bSameCols &= (nCol1 == nRangeCol1 && nCol2 == nRangeCol2);
+ // Sum rows if the number of cols is the same or
+ // sum columns if the number of rows is the same,
+ // otherwise do not show any count of selected cells.
+ if (bSameRows || bSameCols)
+ {
+ const auto nCols = nRangeCol2 - nRangeCol1 + 1;
+ const auto nRows = (bSameCols || nRowsSum == 0) ?
+ rDoc.CountNonFilteredRows( nRangeRow1, nRangeRow2, rRange.aStart.Tab()) :
+ nRowsSum;
+ if (bSameRows)
+ {
+ nRowsSum = nRows;
+ nColsSum += nCols;
+ }
+ else if (bSameCols)
+ {
+ nRowsSum += nRows;
+ nColsSum = nCols;
+ }
+ }
+ else
+ break;
+ }
+ // Either the rows or columns are the same among selections
+ if (bSameRows || bSameCols)
+ {
+ const LocaleDataWrapper& rLocaleData
+ = Application::GetSettings().GetUILocaleDataWrapper();
+ OUString aRowArg
+ = ScResId(STR_SELCOUNT_ROWARG, nRowsSum)
+ .replaceAll("%d", rLocaleData.getNum(nRowsSum, 0));
+ OUString aColArg
+ = ScResId(STR_SELCOUNT_COLARG, nColsSum)
+ .replaceAll("%d", rLocaleData.getNum(nColsSum, 0));
+ OUString aStr = ScResId(STR_SELCOUNT);
+ aStr = aStr.replaceAll("%1", aRowArg);
+ aStr = aStr.replaceAll("%2", aColArg);
+ rSet.Put(SfxStringItem(nWhich, aStr));
+ }
+ }
+ else
+ {
+ SCSIZE nSelected, nTotal;
+ rDoc.GetFilterSelCount( nPosX, nPosY, nTab, nSelected, nTotal );
+ if( nTotal && nSelected != SCSIZE_MAX )
+ {
+ OUString aStr = ScResId( STR_FILTER_SELCOUNT );
+ aStr = aStr.replaceAll( "%1", OUString::number( nSelected ) );
+ aStr = aStr.replaceAll( "%2", OUString::number( nTotal ) );
+ rSet.Put( SfxStringItem( nWhich, aStr ) );
+ }
+ }
+ }
+ break;
+
+ // calculations etc. with date/time/Fail/position&size together
+
+ // #i34458# The SvxStatusItem belongs only into SID_TABLE_CELL. It no longer has to be
+ // duplicated in SID_ATTR_POSITION or SID_ATTR_SIZE for SvxPosSizeStatusBarControl.
+ case SID_TABLE_CELL:
+ {
+ // Test, if error under cursor
+ // (not rDoc.GetErrCode, to avoid erasing circular references)
+
+ // In interpreter may happen via rescheduled Basic
+ if ( rDoc.IsInInterpreter() )
+ rSet.Put( SvxStatusItem( SID_TABLE_CELL, "...", StatusCategory::Formula ) );
+ else
+ {
+ FormulaError nErrCode = FormulaError::NONE;
+ ScFormulaCell* pCell = rDoc.GetFormulaCell(ScAddress(nPosX, nPosY, nTab));
+ if (pCell && !pCell->IsRunning())
+ nErrCode = pCell->GetErrCode();
+
+ OUString aFuncStr;
+ if ( pTabViewShell->GetFunction( aFuncStr, nErrCode ) )
+ {
+ rSet.Put( SvxStatusItem( SID_TABLE_CELL, aFuncStr, StatusCategory::Formula ) );
+ }
+ }
+ }
+ break;
+
+ case SID_DATA_SELECT:
+ // HasSelectionData includes column content and validity,
+ // page fields have to be checked separately.
+ if ( !rDoc.HasSelectionData( nPosX, nPosY, nTab ) &&
+ !pTabViewShell->HasPageFieldDataAtCursor() )
+ rSet.DisableItem( nWhich );
+ break;
+
+ case FID_CURRENTVALIDATION:
+ if ( !rDoc.HasValidationData( nPosX, nPosY, nTab ))
+ rSet.DisableItem( nWhich );
+ break;
+
+ case SID_STATUS_SUM:
+ {
+ OUString aFuncStr;
+ if ( pTabViewShell->GetFunction( aFuncStr, FormulaError::NONE ) )
+ rSet.Put( SfxStringItem( nWhich, aFuncStr ) );
+ }
+ break;
+
+ case FID_MERGE_ON:
+ if ( rDoc.GetChangeTrack() || !pTabViewShell->TestMergeCells() )
+ rSet.DisableItem( nWhich );
+ break;
+
+ case FID_MERGE_OFF:
+ if ( rDoc.GetChangeTrack() || !pTabViewShell->TestRemoveMerge() )
+ rSet.DisableItem( nWhich );
+ break;
+
+ case FID_MERGE_TOGGLE:
+ if ( rDoc.GetChangeTrack() )
+ rSet.DisableItem( nWhich );
+ else
+ {
+ bool bCanMerge = pTabViewShell->TestMergeCells();
+ bool bCanSplit = pTabViewShell->TestRemoveMerge();
+ if( !bCanMerge && !bCanSplit )
+ rSet.DisableItem( nWhich );
+ else
+ rSet.Put( SfxBoolItem( nWhich, bCanSplit ) );
+ }
+ break;
+
+ case FID_INS_ROWBRK:
+ if ( nPosY==0 || (rDoc.HasRowBreak(nPosY, nTab) & ScBreakType::Manual) || rDoc.IsTabProtected(nTab) )
+ rSet.DisableItem( nWhich );
+ break;
+
+ case FID_INS_COLBRK:
+ if ( nPosX==0 || (rDoc.HasColBreak(nPosX, nTab) & ScBreakType::Manual) || rDoc.IsTabProtected(nTab) )
+ rSet.DisableItem( nWhich );
+ break;
+
+ case FID_DEL_ROWBRK:
+ if ( nPosY==0 || !(rDoc.HasRowBreak(nPosY, nTab) & ScBreakType::Manual) || rDoc.IsTabProtected(nTab) )
+ rSet.DisableItem( nWhich );
+ break;
+
+ case FID_DEL_COLBRK:
+ if ( nPosX==0 || !(rDoc.HasColBreak(nPosX, nTab) & ScBreakType::Manual) || rDoc.IsTabProtected(nTab) )
+ rSet.DisableItem( nWhich );
+ break;
+
+ case FID_FILL_TAB:
+ if ( nTabSelCount < 2 )
+ rSet.DisableItem( nWhich );
+ break;
+
+ case SID_INSERT_CURRENT_DATE:
+ case SID_INSERT_CURRENT_TIME:
+ {
+ if ( rDoc.IsTabProtected(nTab) &&
+ rDoc.HasAttrib(nPosX, nPosY, nTab, nPosX, nPosY, nTab, HasAttrFlags::Protected))
+ rSet.DisableItem( nWhich );
+ }
+ break;
+
+ case SID_SELECT_SCENARIO:
+ {
+ std::vector<OUString> aList;
+ Color aDummyCol;
+
+ if ( !rDoc.IsScenario(nTab) )
+ {
+ OUString aStr;
+ ScScenarioFlags nFlags;
+ SCTAB nScTab = nTab + 1;
+ bool bSheetProtected = rDoc.IsTabProtected(nTab);
+
+ while ( rDoc.IsScenario(nScTab) )
+ {
+ rDoc.GetName( nScTab, aStr );
+ aList.push_back(aStr);
+ rDoc.GetScenarioData( nScTab, aStr, aDummyCol, nFlags );
+ aList.push_back(aStr);
+ // Protection is sal_True if both Sheet and Scenario are protected
+ aList.push_back((bSheetProtected && (nFlags & ScScenarioFlags::Protected)) ? OUString("1") : OUString("0"));
+ ++nScTab;
+ }
+ }
+ else
+ {
+ OUString aComment;
+ ScScenarioFlags nDummyFlags;
+ rDoc.GetScenarioData( nTab, aComment, aDummyCol, nDummyFlags );
+ OSL_ENSURE( aList.empty(), "List not empty!" );
+ aList.push_back(aComment);
+ }
+
+ rSet.Put( SfxStringListItem( nWhich, &aList ) );
+ }
+ break;
+
+ case FID_ROW_HIDE:
+ case FID_ROW_SHOW:
+ case FID_COL_HIDE:
+ case FID_COL_SHOW:
+ case FID_COL_OPT_WIDTH:
+ case FID_ROW_OPT_HEIGHT:
+ case FID_DELETE_CELL:
+ if ( rDoc.IsTabProtected(nTab) || pDocSh->IsReadOnly())
+ rSet.DisableItem( nWhich );
+ break;
+
+ case SID_OUTLINE_MAKE:
+ {
+ if ( GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
+ GetViewData().GetCurY(), GetViewData().GetTabNo() ) )
+ {
+ //! test for data pilot operation
+ }
+ else if (rDoc.GetChangeTrack()!=nullptr || GetViewData().IsMultiMarked())
+ {
+ rSet.DisableItem( nWhich );
+ }
+ }
+ break;
+ case SID_OUTLINE_SHOW:
+ if ( GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
+ GetViewData().GetCurY(), GetViewData().GetTabNo() ) )
+ {
+ //! test for data pilot operation
+ }
+ else if (!pTabViewShell->OutlinePossible(false))
+ rSet.DisableItem( nWhich );
+ break;
+
+ case SID_OUTLINE_HIDE:
+ if ( GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
+ GetViewData().GetCurY(), GetViewData().GetTabNo() ) )
+ {
+ //! test for data pilot operation
+ }
+ else if (!pTabViewShell->OutlinePossible(true))
+ rSet.DisableItem( nWhich );
+ break;
+
+ case SID_OUTLINE_REMOVE:
+ {
+ if ( GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
+ GetViewData().GetCurY(), GetViewData().GetTabNo() ) )
+ {
+ //! test for data pilot operation
+ }
+ else
+ {
+ bool bCol, bRow;
+ pTabViewShell->TestRemoveOutline( bCol, bRow );
+ if ( !bCol && !bRow )
+ rSet.DisableItem( nWhich );
+ }
+ }
+ break;
+
+ case FID_COL_WIDTH:
+ {
+ SfxUInt16Item aWidthItem( FID_COL_WIDTH, rDoc.GetColWidth( nPosX , nTab) );
+ rSet.Put( aWidthItem );
+ if ( pDocSh->IsReadOnly())
+ rSet.DisableItem( nWhich );
+
+ //XXX disable if not conclusive
+ }
+ break;
+
+ case FID_ROW_HEIGHT:
+ {
+ SfxUInt16Item aHeightItem( FID_ROW_HEIGHT, rDoc.GetRowHeight( nPosY , nTab) );
+ rSet.Put( aHeightItem );
+ //XXX disable if not conclusive
+ if ( pDocSh->IsReadOnly())
+ rSet.DisableItem( nWhich );
+ }
+ break;
+
+ case SID_DETECTIVE_FILLMODE:
+ rSet.Put(SfxBoolItem( nWhich, pTabViewShell->IsAuditShell() ));
+ break;
+
+ case FID_INPUTLINE_STATUS:
+ OSL_FAIL( "Old update method. Use ScTabViewShell::UpdateInputHandler()." );
+ break;
+
+ case SID_SCENARIOS: // scenarios:
+ if (!(rMark.IsMarked() || rMark.IsMultiMarked())) // only, if something selected
+ rSet.DisableItem( nWhich );
+ break;
+
+ case FID_NOTE_VISIBLE:
+ {
+ const ScPostIt* pNote = rDoc.GetNote(nPosX, nPosY, nTab);
+ if ( pNote && rDoc.IsBlockEditable( nTab, nPosX,nPosY, nPosX,nPosY ) )
+ rSet.Put( SfxBoolItem( nWhich, pNote->IsCaptionShown() ) );
+ else
+ rSet.DisableItem( nWhich );
+ }
+ break;
+
+ case FID_HIDE_NOTE:
+ case FID_SHOW_NOTE:
+ {
+ bool bEnable = false;
+ bool bSearchForHidden = nWhich == FID_SHOW_NOTE;
+ if (!rMark.IsMarked() && !rMark.IsMultiMarked())
+ {
+ // Check current cell
+ const ScPostIt* pNote = rDoc.GetNote(nPosX, nPosY, nTab);
+ if ( pNote && rDoc.IsBlockEditable( nTab, nPosX,nPosY, nPosX,nPosY ) )
+ if ( pNote->IsCaptionShown() != bSearchForHidden)
+ bEnable = true;
+ }
+ else
+ {
+ // Check selection range
+ ScRangeListRef aRangesRef;
+ rData.GetMultiArea(aRangesRef);
+ ScRangeList aRanges = *aRangesRef;
+ std::vector<sc::NoteEntry> aNotes;
+ rDoc.GetNotesInRange(aRanges, aNotes);
+ for(const auto& rNote : aNotes)
+ {
+ const ScAddress& rAdr = rNote.maPos;
+ if( rDoc.IsBlockEditable( rAdr.Tab(), rAdr.Col(), rAdr.Row(), rAdr.Col(), rAdr.Row() ))
+ {
+ if (rNote.mpNote->IsCaptionShown() != bSearchForHidden)
+ {
+ bEnable = true;
+ break;
+ }
+ }
+ }
+
+ }
+ if ( !bEnable )
+ rSet.DisableItem( nWhich );
+ }
+ break;
+
+ case FID_SHOW_ALL_NOTES:
+ case FID_HIDE_ALL_NOTES:
+ case FID_DELETE_ALL_NOTES:
+ {
+ bool bHasNotes = false;
+
+ for (auto const& rTab : rMark.GetSelectedTabs())
+ {
+ if (rDoc.HasTabNotes( rTab ))
+ {
+ bHasNotes = true;
+ break;
+ }
+ }
+
+ if ( !bHasNotes )
+ rSet.DisableItem( nWhich );
+ }
+ break;
+
+ case SID_TOGGLE_NOTES:
+ {
+ bool bHasNotes = false;
+ ScRangeList aRanges;
+
+ for (auto const& rTab : rMark.GetSelectedTabs())
+ {
+ if (rDoc.HasTabNotes( rTab ))
+ {
+ bHasNotes = true;
+ aRanges.push_back(ScRange(0,0,rTab,rDoc.MaxCol(),rDoc.MaxRow(),rTab));
+ }
+ }
+
+ if ( !bHasNotes )
+ rSet.DisableItem( nWhich );
+ else
+ {
+ CommentCaptionState eState = rDoc.GetAllNoteCaptionsState( aRanges );
+ bool bAllNotesInShown = (eState != ALLHIDDEN && eState != MIXED);
+ rSet.Put( SfxBoolItem( SID_TOGGLE_NOTES, bAllNotesInShown) );
+ }
+ }
+ break;
+
+ case SID_DELETE_NOTE:
+ {
+ bool bEnable = false;
+ if ( rMark.IsMarked() || rMark.IsMultiMarked() )
+ {
+ if ( rDoc.IsSelectionEditable( rMark ) )
+ {
+ // look for at least one note in selection
+ ScRangeList aRanges;
+ rMark.FillRangeListWithMarks( &aRanges, false );
+ bEnable = rDoc.ContainsNotesInRange( aRanges );
+ }
+ }
+ else
+ {
+ bEnable = rDoc.IsBlockEditable( nTab, nPosX,nPosY, nPosX,nPosY ) &&
+ rDoc.GetNote(nPosX, nPosY, nTab);
+ }
+ if ( !bEnable )
+ rSet.DisableItem( nWhich );
+ }
+ break;
+
+ case SID_OPENDLG_CONSOLIDATE:
+ case SCITEM_CONSOLIDATEDATA:
+ {
+ if (rDoc.GetChangeTrack()!=nullptr)
+ rSet.DisableItem( nWhich);
+ }
+ break;
+
+ case SID_CHINESE_CONVERSION:
+ case SID_HANGUL_HANJA_CONVERSION:
+ ScViewUtil::HideDisabledSlot( rSet, rData.GetBindings(), nWhich );
+ break;
+
+ case FID_USE_NAME:
+ {
+ if ( pDocSh && pDocSh->IsDocShared() )
+ rSet.DisableItem( nWhich );
+ else
+ {
+ ScRange aRange;
+ if ( rData.GetSimpleArea( aRange ) != SC_MARK_SIMPLE )
+ rSet.DisableItem( nWhich );
+ }
+ }
+ break;
+
+ case FID_DEFINE_NAME:
+ case FID_INSERT_NAME:
+ case FID_ADD_NAME:
+ case SID_DEFINE_COLROWNAMERANGES:
+ {
+ if ( pDocSh && pDocSh->IsDocShared() )
+ {
+ rSet.DisableItem( nWhich );
+ }
+ }
+ break;
+
+ case FID_DEFINE_CURRENT_NAME:
+ {
+ ScAddress aCurrentAddress( nPosX, nPosY, nTab );
+
+ if ( !rDoc.IsAddressInRangeName( RangeNameScope::GLOBAL, aCurrentAddress ) &&
+ !rDoc.IsAddressInRangeName( RangeNameScope::SHEET, aCurrentAddress ))
+ {
+ rSet.DisableItem( nWhich );
+ }
+ }
+ break;
+
+ case SID_SPELL_DIALOG:
+ {
+ if (rDoc.IsTabProtected(rData.GetTabNo()))
+ {
+ bool bVisible = false;
+ SfxViewFrame* pViewFrame = ( pTabViewShell ? &pTabViewShell->GetViewFrame() : nullptr );
+ if ( pViewFrame && pViewFrame->HasChildWindow( nWhich ) )
+ {
+ SfxChildWindow* pChild = pViewFrame->GetChildWindow( nWhich );
+ std::shared_ptr<SfxDialogController> xController = pChild ? pChild->GetController() : nullptr;
+ if (xController && xController->getDialog()->get_visible())
+ {
+ bVisible = true;
+ }
+ }
+ if ( !bVisible )
+ {
+ rSet.DisableItem( nWhich );
+ }
+ }
+ }
+ break;
+
+ case SID_OPENDLG_CURRENTCONDFRMT:
+ case SID_OPENDLG_CURRENTCONDFRMT_MANAGER:
+ {
+ const SfxPoolItem* pItem = rDoc.GetAttr( nPosX, nPosY, nTab, ATTR_CONDITIONAL );
+ const ScCondFormatItem* pCondFormatItem = static_cast<const ScCondFormatItem*>(pItem);
+
+ if ( pCondFormatItem->GetCondFormatData().empty() )
+ rSet.DisableItem( nWhich );
+ else if ( pCondFormatItem->GetCondFormatData().size() == 1 )
+ rSet.DisableItem( SID_OPENDLG_CURRENTCONDFRMT_MANAGER );
+ else if ( pCondFormatItem->GetCondFormatData().size() > 1 )
+ rSet.DisableItem( SID_OPENDLG_CURRENTCONDFRMT );
+ }
+ break;
+
+ } // switch ( nWitch )
+ nWhich = aIter.NextWhich();
+ } // while ( nWitch )
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/cellsh1.cxx b/sc/source/ui/view/cellsh1.cxx
new file mode 100644
index 0000000000..558d5a8166
--- /dev/null
+++ b/sc/source/ui/view/cellsh1.cxx
@@ -0,0 +1,3600 @@
+/* -*- 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 .
+ */
+
+#include <config_features.h>
+
+#include <com/sun/star/i18n/TextConversionOption.hpp>
+#include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
+
+#include <scitems.hxx>
+#include <sfx2/viewfrm.hxx>
+
+#include <basic/sberrors.hxx>
+#include <comphelper/lok.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <svl/stritem.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <svl/zformat.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/request.hxx>
+#include <vcl/commandinfoprovider.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <svx/svxdlg.hxx>
+#include <sot/formats.hxx>
+#include <svx/postattr.hxx>
+#include <editeng/fontitem.hxx>
+#include <svx/clipfmtitem.hxx>
+#include <svx/hlnkitem.hxx>
+#include <basic/sbxcore.hxx>
+#include <editeng/editview.hxx>
+#include <svtools/cliplistener.hxx>
+
+#include <cellsh.hxx>
+#include <ftools.hxx>
+#include <sc.hrc>
+#include <document.hxx>
+#include <patattr.hxx>
+#include <scmod.hxx>
+#include <tabvwsh.hxx>
+#include <uiitems.hxx>
+#include <reffact.hxx>
+#include <inputhdl.hxx>
+#include <transobj.hxx>
+#include <drwtrans.hxx>
+#include <docfunc.hxx>
+#include <editable.hxx>
+#include <dpobject.hxx>
+#include <dpsave.hxx>
+#include <spellparam.hxx>
+#include <postit.hxx>
+#include <dpsdbtab.hxx>
+#include <dpshttab.hxx>
+#include <dbdata.hxx>
+#include <docsh.hxx>
+#include <cliputil.hxx>
+#include <markdata.hxx>
+#include <colorscale.hxx>
+#include <condformatdlg.hxx>
+#include <attrib.hxx>
+#include <condformatdlgitem.hxx>
+#include <impex.hxx>
+
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <scui_def.hxx>
+#include <scabstdlg.hxx>
+#include <tokenstringcontext.hxx>
+#include <cellvalue.hxx>
+#include <tokenarray.hxx>
+#include <formulacell.hxx>
+#include <gridwin.hxx>
+#include <searchresults.hxx>
+#include <Sparkline.hxx>
+
+#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <cppuhelper/bootstrap.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <memory>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::uno;
+
+namespace{
+InsertDeleteFlags FlagsFromString(const OUString& rFlagsStr,
+ InsertDeleteFlags nFlagsMask = InsertDeleteFlags::CONTENTS | InsertDeleteFlags::ATTRIB)
+{
+ OUString aFlagsStr = rFlagsStr.toAsciiUpperCase();
+ InsertDeleteFlags nFlags = InsertDeleteFlags::NONE;
+
+ for (sal_Int32 i=0 ; i < aFlagsStr.getLength(); ++i)
+ {
+ switch (aFlagsStr[i])
+ {
+ case 'A': return InsertDeleteFlags::ALL;
+ case 'S': nFlags |= InsertDeleteFlags::STRING & nFlagsMask; break;
+ case 'V': nFlags |= InsertDeleteFlags::VALUE & nFlagsMask; break;
+ case 'D': nFlags |= InsertDeleteFlags::DATETIME & nFlagsMask; break;
+ case 'F': nFlags |= InsertDeleteFlags::FORMULA & nFlagsMask; break;
+ case 'N': nFlags |= InsertDeleteFlags::NOTE & nFlagsMask; break;
+ case 'T': nFlags |= InsertDeleteFlags::ATTRIB & nFlagsMask; break;
+ case 'O': nFlags |= InsertDeleteFlags::OBJECTS & nFlagsMask; break;
+ }
+ }
+ return nFlags;
+}
+
+OUString FlagsToString( InsertDeleteFlags nFlags,
+ InsertDeleteFlags nFlagsMask = InsertDeleteFlags::CONTENTS | InsertDeleteFlags::ATTRIB )
+{
+ OUString aFlagsStr;
+
+ if( nFlags == InsertDeleteFlags::ALL )
+ {
+ aFlagsStr = "A";
+ }
+ else
+ {
+ nFlags &= nFlagsMask;
+
+ if( nFlags & InsertDeleteFlags::STRING ) aFlagsStr += "S";
+ if( nFlags & InsertDeleteFlags::VALUE ) aFlagsStr += "V";
+ if( nFlags & InsertDeleteFlags::DATETIME ) aFlagsStr += "D";
+ if( nFlags & InsertDeleteFlags::FORMULA ) aFlagsStr += "F";
+ if( nFlags & InsertDeleteFlags::NOTE ) aFlagsStr += "N";
+ if( nFlags & InsertDeleteFlags::ATTRIB ) aFlagsStr += "T";
+ if( nFlags & InsertDeleteFlags::OBJECTS ) aFlagsStr += "O";
+ }
+ return aFlagsStr;
+}
+
+void SetTabNoAndCursor( const ScViewData& rViewData, std::u16string_view rCellId )
+{
+ ScTabViewShell* pTabViewShell = rViewData.GetViewShell();
+ assert(pTabViewShell);
+ const ScDocument& rDoc = rViewData.GetDocShell()->GetDocument();
+ std::vector<sc::NoteEntry> aNotes;
+ rDoc.GetAllNoteEntries(aNotes);
+
+ sal_uInt32 nId = o3tl::toUInt32(rCellId);
+ auto lComp = [nId](const sc::NoteEntry& rNote) { return rNote.mpNote->GetId() == nId; };
+
+ const auto& aFoundNoteIt = std::find_if(aNotes.begin(), aNotes.end(), lComp);
+ if (aFoundNoteIt != aNotes.end())
+ {
+ ScAddress aFoundPos = aFoundNoteIt->maPos;
+ pTabViewShell->SetTabNo(aFoundPos.Tab());
+ pTabViewShell->SetCursor(aFoundPos.Col(), aFoundPos.Row());
+ }
+}
+
+void InsertCells(ScTabViewShell* pTabViewShell, SfxRequest &rReq, InsCellCmd eCmd)
+{
+ if (eCmd!=INS_NONE)
+ {
+ pTabViewShell->InsertCells( eCmd );
+
+ if( ! rReq.IsAPI() )
+ {
+ OUString aParam;
+
+ switch( eCmd )
+ {
+ case INS_CELLSDOWN: aParam = "V"; break;
+ case INS_CELLSRIGHT: aParam = ">"; break;
+ case INS_INSROWS_BEFORE: aParam = "R"; break;
+ case INS_INSCOLS_BEFORE: aParam = "C"; break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ rReq.AppendItem( SfxStringItem( FID_INS_CELL, aParam ) );
+ rReq.Done();
+ }
+ }
+}
+
+void DeleteCells(ScTabViewShell* pTabViewShell, SfxRequest &rReq, DelCellCmd eCmd)
+{
+ if (eCmd != DelCellCmd::NONE )
+ {
+ pTabViewShell->DeleteCells( eCmd );
+
+ if( ! rReq.IsAPI() )
+ {
+ OUString aParam;
+
+ switch( eCmd )
+ {
+ case DelCellCmd::CellsUp: aParam = "U"; break;
+ case DelCellCmd::CellsLeft: aParam = "L"; break;
+ case DelCellCmd::Rows: aParam = "R"; break;
+ case DelCellCmd::Cols: aParam = "C"; break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ rReq.AppendItem( SfxStringItem( FID_DELETE_CELL, aParam ) );
+ rReq.Done();
+ }
+ }
+}
+}
+
+void ScCellShell::ExecuteEdit( SfxRequest& rReq )
+{
+ ScModule* pScMod = SC_MOD();
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ SfxBindings& rBindings = pTabViewShell->GetViewFrame().GetBindings();
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+ sal_uInt16 nSlot = rReq.GetSlot();
+
+ pTabViewShell->HideListBox(); // Autofilter-DropDown-Listbox
+
+ // finish input
+ if ( GetViewData().HasEditView( GetViewData().GetActivePart() ) )
+ {
+ switch ( nSlot )
+ {
+ case FID_DEFINE_NAME:
+ case FID_ADD_NAME:
+ case FID_USE_NAME:
+ case FID_INSERT_NAME:
+ case SID_SPELL_DIALOG:
+ case SID_HANGUL_HANJA_CONVERSION:
+ case SID_OPENDLG_CONDFRMT:
+ case SID_OPENDLG_CURRENTCONDFRMT:
+ case SID_OPENDLG_COLORSCALE:
+ case SID_OPENDLG_DATABAR:
+ pScMod->InputEnterHandler();
+ pTabViewShell->UpdateInputHandler();
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ switch ( nSlot )
+ {
+
+ // insert / delete cells / rows / columns
+
+ case FID_INS_ROW:
+ case FID_INS_ROWS_BEFORE:
+ pTabViewShell->InsertCells(INS_INSROWS_BEFORE);
+ rReq.Done();
+ break;
+
+ case FID_INS_COLUMN:
+ case FID_INS_COLUMNS_BEFORE:
+ pTabViewShell->InsertCells(INS_INSCOLS_BEFORE);
+ rReq.Done();
+ break;
+
+ case FID_INS_ROWS_AFTER:
+ pTabViewShell->InsertCells(INS_INSROWS_AFTER);
+ rReq.Done();
+ break;
+
+ case FID_INS_COLUMNS_AFTER:
+ pTabViewShell->InsertCells(INS_INSCOLS_AFTER);
+ rReq.Done();
+ break;
+
+ case FID_INS_CELLSDOWN:
+ pTabViewShell->InsertCells(INS_CELLSDOWN);
+ rReq.Done();
+ break;
+
+ case FID_INS_CELLSRIGHT:
+ pTabViewShell->InsertCells(INS_CELLSRIGHT);
+ rReq.Done();
+ break;
+
+ case SID_DEL_ROWS:
+ pTabViewShell->DeleteCells( DelCellCmd::Rows );
+ rReq.Done();
+ break;
+
+ case SID_DEL_COLS:
+ pTabViewShell->DeleteCells( DelCellCmd::Cols );
+ rReq.Done();
+ break;
+
+ case FID_INS_CELL:
+ {
+ InsCellCmd eCmd=INS_NONE;
+
+ if ( pReqArgs )
+ {
+ const SfxPoolItem* pItem;
+ OUString aFlags;
+
+ if( pReqArgs->HasItem( FID_INS_CELL, &pItem ) )
+ aFlags = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ if( !aFlags.isEmpty() )
+ {
+ switch( aFlags[0] )
+ {
+ case 'V': eCmd = INS_CELLSDOWN ;break;
+ case '>': eCmd = INS_CELLSRIGHT ;break;
+ case 'R': eCmd = INS_INSROWS_BEFORE ;break;
+ case 'C': eCmd = INS_INSCOLS_BEFORE ;break;
+ }
+ }
+ }
+ else
+ {
+ if ( GetViewData().SimpleColMarked() )
+ eCmd = INS_INSCOLS_BEFORE;
+ else if ( GetViewData().SimpleRowMarked() )
+ eCmd = INS_INSROWS_BEFORE;
+ else
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+ bool bTheFlag=(rDoc.GetChangeTrack()!=nullptr);
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ VclPtr<AbstractScInsertCellDlg> pDlg(pFact->CreateScInsertCellDlg(pTabViewShell->GetFrameWeld(), bTheFlag));
+ pDlg->StartExecuteAsync([pDlg, pTabViewShell](sal_Int32 nResult){
+ if (nResult == RET_OK)
+ {
+ SfxRequest aRequest(pTabViewShell->GetViewFrame(), FID_INS_CELL);
+ InsertCells(pTabViewShell, aRequest, pDlg->GetInsCellCmd());
+ }
+ pDlg->disposeOnce();
+ });
+ }
+ }
+
+ InsertCells(pTabViewShell, rReq, eCmd);
+ }
+ break;
+
+ case FID_DELETE_CELL:
+ {
+ DelCellCmd eCmd = DelCellCmd::NONE;
+
+ if ( pReqArgs )
+ {
+ const SfxPoolItem* pItem;
+ OUString aFlags;
+
+ if( pReqArgs->HasItem( FID_DELETE_CELL, &pItem ) )
+ aFlags = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ if( !aFlags.isEmpty() )
+ {
+ switch( aFlags[0] )
+ {
+ case 'U': eCmd = DelCellCmd::CellsUp ;break;
+ case 'L': eCmd = DelCellCmd::CellsLeft ;break;
+ case 'R': eCmd = DelCellCmd::Rows ;break;
+ case 'C': eCmd = DelCellCmd::Cols ;break;
+ }
+ }
+ }
+ else
+ {
+ if ( GetViewData().SimpleColMarked() )
+ eCmd = DelCellCmd::Cols;
+ else if ( GetViewData().SimpleRowMarked() )
+ eCmd = DelCellCmd::Rows;
+ else
+ {
+ ScRange aRange;
+ ScDocument& rDoc = GetViewData().GetDocument();
+ bool bTheFlag=GetViewData().IsMultiMarked() ||
+ (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE_FILTERED) ||
+ (rDoc.GetChangeTrack() != nullptr);
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+ VclPtr<AbstractScDeleteCellDlg> pDlg(pFact->CreateScDeleteCellDlg( pTabViewShell->GetFrameWeld(), bTheFlag ));
+
+ pDlg->StartExecuteAsync([pDlg, pTabViewShell](sal_Int32 nResult){
+ if (nResult == RET_OK)
+ {
+ SfxRequest aRequest(pTabViewShell->GetViewFrame(), FID_INS_CELL);
+ DeleteCells(pTabViewShell, aRequest, pDlg->GetDelCellCmd());
+ }
+ pDlg->disposeOnce();
+ });
+ }
+ }
+ DeleteCells(pTabViewShell, rReq, eCmd);
+ }
+ break;
+
+ // delete contents from cells
+
+ case SID_DELETE_CONTENTS:
+ pTabViewShell->DeleteContents( InsertDeleteFlags::CONTENTS );
+ rReq.Done();
+ break;
+
+ case SID_DELETE:
+ {
+ InsertDeleteFlags nFlags = InsertDeleteFlags::NONE;
+
+ if ( pReqArgs!=nullptr && pTabViewShell->SelectionEditable() )
+ {
+ const SfxPoolItem* pItem;
+ OUString aFlags('A');
+
+ if( pReqArgs->HasItem( SID_DELETE, &pItem ) )
+ aFlags = static_cast<const SfxStringItem*>(pItem)->GetValue();
+
+ nFlags |= FlagsFromString(aFlags, InsertDeleteFlags::ALL);
+ }
+ else
+ {
+ ScEditableTester aTester( pTabViewShell );
+ if (aTester.IsEditable())
+ {
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScDeleteContentsDlg> pDlg(pFact->CreateScDeleteContentsDlg(pTabViewShell->GetFrameWeld()));
+ ScDocument& rDoc = GetViewData().GetDocument();
+ SCTAB nTab = GetViewData().GetTabNo();
+ if ( rDoc.IsTabProtected(nTab) )
+ pDlg->DisableObjects();
+ if (pDlg->Execute() == RET_OK)
+ {
+ nFlags = pDlg->GetDelContentsCmdBits();
+ }
+ }
+ else
+ pTabViewShell->ErrorMessage(aTester.GetMessageId());
+ }
+
+ if( nFlags != InsertDeleteFlags::NONE )
+ {
+ pTabViewShell->DeleteContents( nFlags );
+
+ if( ! rReq.IsAPI() )
+ {
+ OUString aFlags = FlagsToString( nFlags, InsertDeleteFlags::ALL );
+
+ rReq.AppendItem( SfxStringItem( SID_DELETE, aFlags ) );
+ rReq.Done();
+ }
+ }
+ }
+ break;
+
+ // fill...
+
+ case FID_FILL_TO_BOTTOM:
+ pTabViewShell->FillSimple( FILL_TO_BOTTOM );
+ rReq.Done();
+ break;
+
+ case FID_FILL_TO_RIGHT:
+ pTabViewShell->FillSimple( FILL_TO_RIGHT );
+ rReq.Done();
+ break;
+
+ case FID_FILL_TO_TOP:
+ pTabViewShell->FillSimple( FILL_TO_TOP );
+ rReq.Done();
+ break;
+
+ case FID_FILL_TO_LEFT:
+ pTabViewShell->FillSimple( FILL_TO_LEFT );
+ rReq.Done();
+ break;
+
+ case FID_FILL_TAB:
+ {
+ InsertDeleteFlags nFlags = InsertDeleteFlags::NONE;
+ ScPasteFunc nFunction = ScPasteFunc::NONE;
+ bool bSkipEmpty = false;
+ bool bAsLink = false;
+
+ if ( pReqArgs!=nullptr && pTabViewShell->SelectionEditable() )
+ {
+ const SfxPoolItem* pItem;
+ OUString aFlags('A');
+
+ if( pReqArgs->HasItem( FID_FILL_TAB, &pItem ) )
+ aFlags = static_cast<const SfxStringItem*>(pItem)->GetValue();
+
+ nFlags |= FlagsFromString(aFlags);
+ }
+ else
+ {
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScInsertContentsDlg> pDlg(pFact->CreateScInsertContentsDlg(pTabViewShell->GetFrameWeld(),
+ new OUString(ScResId(STR_FILL_TAB))));
+ pDlg->SetFillMode(true);
+
+ if (pDlg->Execute() == RET_OK)
+ {
+ nFlags = pDlg->GetInsContentsCmdBits();
+ nFunction = pDlg->GetFormulaCmdBits();
+ bSkipEmpty = pDlg->IsSkipEmptyCells();
+ bAsLink = pDlg->IsLink();
+ // there is no MoveMode with fill tabs
+ }
+ }
+
+ if( nFlags != InsertDeleteFlags::NONE )
+ {
+ pTabViewShell->FillTab( nFlags, nFunction, bSkipEmpty, bAsLink );
+
+ if( ! rReq.IsAPI() )
+ {
+ OUString aFlags = FlagsToString( nFlags );
+
+ rReq.AppendItem( SfxStringItem( FID_FILL_TAB, aFlags ) );
+ rReq.Done();
+ }
+ }
+ }
+ break;
+
+ case FID_FILL_SERIES:
+ {
+ if (GetViewData().SelectionForbidsCellFill())
+ // Slot should be already disabled, but in case it wasn't
+ // don't even attempt to do the evaluation and popup a
+ // dialog.
+ break;
+
+ SCCOL nStartCol;
+ SCROW nStartRow;
+ SCTAB nStartTab;
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ SCTAB nEndTab;
+ sal_uInt16 nPossDir = FDS_OPT_NONE;
+ FillDir eFillDir = FILL_TO_BOTTOM;
+ FillCmd eFillCmd = FILL_LINEAR;
+ FillDateCmd eFillDateCmd = FILL_DAY;
+ double fStartVal = MAXDOUBLE;
+ double fIncVal = 1;
+ double fMaxVal = MAXDOUBLE;
+ bool bDoIt = false;
+
+ GetViewData().GetSimpleArea( nStartCol, nStartRow, nStartTab,
+ nEndCol, nEndRow, nEndTab );
+
+ if( nStartCol!=nEndCol )
+ {
+ nPossDir |= FDS_OPT_HORZ;
+ eFillDir=FILL_TO_RIGHT;
+ }
+
+ if( nStartRow!=nEndRow )
+ {
+ nPossDir |= FDS_OPT_VERT;
+ eFillDir=FILL_TO_BOTTOM;
+ }
+
+ ScDocument& rDoc = GetViewData().GetDocument();
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+
+ if( pReqArgs )
+ {
+ const SfxPoolItem* pItem;
+ OUString aFillDir, aFillCmd, aFillDateCmd;
+ OUString aFillStep, aFillStart, aFillMax;
+ sal_uInt32 nKey;
+ double fTmpVal;
+
+ if( pReqArgs->HasItem( FID_FILL_SERIES, &pItem ) )
+ aFillDir = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ if( pReqArgs->HasItem( FN_PARAM_1, &pItem ) )
+ aFillCmd = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ if( pReqArgs->HasItem( FN_PARAM_2, &pItem ) )
+ aFillDateCmd = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ if( pReqArgs->HasItem( FN_PARAM_3, &pItem ) )
+ aFillStep = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ if( pReqArgs->HasItem( FN_PARAM_4, &pItem ) )
+ aFillStart = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ if( pReqArgs->HasItem( FN_PARAM_5, &pItem ) )
+ aFillMax = static_cast<const SfxStringItem*>(pItem)->GetValue();
+
+ if( !aFillDir.isEmpty() )
+ switch( aFillDir[0] )
+ {
+ case 'B': case 'b': eFillDir=FILL_TO_BOTTOM; break;
+ case 'R': case 'r': eFillDir=FILL_TO_RIGHT; break;
+ case 'T': case 't': eFillDir=FILL_TO_TOP; break;
+ case 'L': case 'l': eFillDir=FILL_TO_LEFT; break;
+ }
+
+ if( !aFillCmd.isEmpty() )
+ switch( aFillCmd[0] )
+ {
+ case 'S': case 's': eFillCmd=FILL_SIMPLE; break;
+ case 'L': case 'l': eFillCmd=FILL_LINEAR; break;
+ case 'G': case 'g': eFillCmd=FILL_GROWTH; break;
+ case 'D': case 'd': eFillCmd=FILL_DATE; break;
+ case 'A': case 'a': eFillCmd=FILL_AUTO; break;
+ }
+
+ if( !aFillDateCmd.isEmpty() )
+ switch( aFillDateCmd[0] )
+ {
+ case 'D': case 'd': eFillDateCmd=FILL_DAY; break;
+ case 'W': case 'w': eFillDateCmd=FILL_WEEKDAY; break;
+ case 'M': case 'm': eFillDateCmd=FILL_MONTH; break;
+ case 'Y': case 'y': eFillDateCmd=FILL_YEAR; break;
+ }
+
+ nKey = 0;
+ if( pFormatter->IsNumberFormat( aFillStart, nKey, fTmpVal ))
+ fStartVal = fTmpVal;
+
+ nKey = 0;
+ if( pFormatter->IsNumberFormat( aFillStep, nKey, fTmpVal ))
+ fIncVal = fTmpVal;
+
+ nKey = 0;
+ if( pFormatter->IsNumberFormat( aFillMax, nKey, fTmpVal ))
+ fMaxVal = fTmpVal;
+
+ bDoIt = true;
+
+ }
+ else // (pReqArgs == nullptr) => raise Dialog
+ {
+ sal_uInt32 nPrivFormat = rDoc.GetNumberFormat( nStartCol, nStartRow, nStartTab );
+ CellType eCellType = rDoc.GetCellType( nStartCol, nStartRow, nStartTab );
+ const SvNumberformat* pPrivEntry = pFormatter->GetEntry( nPrivFormat );
+ const SCSIZE nSelectHeight = nEndRow - nStartRow + 1;
+ const SCSIZE nSelectWidth = nEndCol - nStartCol + 1;
+
+ if (!pPrivEntry)
+ {
+ OSL_FAIL("Numberformat not found !!!");
+ }
+ else
+ {
+ SvNumFormatType nPrivType = pPrivEntry->GetType();
+ if (nPrivType & SvNumFormatType::DATE)
+ {
+ eFillCmd=FILL_DATE;
+ }
+ else if(eCellType==CELLTYPE_STRING)
+ {
+ eFillCmd=FILL_AUTO;
+ }
+ }
+
+ OUString aStartStr;
+
+ // suggest default Startvalue only, when just 1 row or column
+ if ( nStartCol == nEndCol || nStartRow == nEndRow )
+ {
+ double fInputEndVal = 0.0;
+ OUString aEndStr;
+
+ const bool forceSystemLocale = true;
+ aStartStr = rDoc.GetInputString( nStartCol, nStartRow, nStartTab, forceSystemLocale );
+ fStartVal = rDoc.GetValue( nStartCol, nStartRow, nStartTab );
+
+ if(eFillDir==FILL_TO_BOTTOM && nStartRow < nEndRow )
+ {
+ aEndStr = rDoc.GetInputString( nStartCol, nStartRow+1, nStartTab, forceSystemLocale );
+ if(!aEndStr.isEmpty())
+ {
+ fInputEndVal = rDoc.GetValue( nStartCol, nStartRow+1, nStartTab );
+ fIncVal=fInputEndVal-fStartVal;
+ }
+ }
+ else
+ {
+ if(nStartCol < nEndCol)
+ {
+ aEndStr = rDoc.GetInputString( nStartCol+1, nStartRow, nStartTab, forceSystemLocale );
+ if(!aEndStr.isEmpty())
+ {
+ fInputEndVal = rDoc.GetValue( nStartCol+1, nStartRow, nStartTab );
+ fIncVal=fInputEndVal-fStartVal;
+ }
+ }
+ }
+ if(eFillCmd==FILL_DATE)
+ {
+ const Date& rNullDate = rDoc.GetFormatTable()->GetNullDate();
+ Date aStartDate = rNullDate;
+ aStartDate.AddDays(fStartVal);
+ Date aEndDate = rNullDate;
+ aEndDate.AddDays(fInputEndVal);
+ double fTempDate=0;
+
+ if(aStartDate.GetYear()!=aEndDate.GetYear())
+ {
+ eFillDateCmd = FILL_YEAR;
+ fTempDate=aEndDate.GetYear()-aStartDate.GetYear();
+ }
+ if(aStartDate.GetMonth()!=aEndDate.GetMonth())
+ {
+ eFillDateCmd = FILL_MONTH;
+ fTempDate=fTempDate*12+aEndDate.GetMonth()-aStartDate.GetMonth();
+ }
+ if(aStartDate.GetDay()==aEndDate.GetDay())
+ {
+ fIncVal=fTempDate;
+ }
+ }
+ }
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScFillSeriesDlg> pDlg(pFact->CreateScFillSeriesDlg( pTabViewShell->GetFrameWeld(),
+ rDoc,
+ eFillDir, eFillCmd, eFillDateCmd,
+ aStartStr, fIncVal, fMaxVal,
+ nSelectHeight, nSelectWidth, nPossDir));
+
+ if ( nStartCol != nEndCol && nStartRow != nEndRow )
+ {
+ pDlg->SetEdStartValEnabled(false);
+ }
+
+ if ( pDlg->Execute() == RET_OK )
+ {
+ eFillDir = pDlg->GetFillDir();
+ eFillCmd = pDlg->GetFillCmd();
+ eFillDateCmd = pDlg->GetFillDateCmd();
+
+ if(eFillCmd==FILL_AUTO)
+ {
+ OUString aStr = pDlg->GetStartStr();
+ if(!aStr.isEmpty())
+ pTabViewShell->EnterData( nStartCol, nStartRow, nStartTab, aStr );
+ }
+ fStartVal = pDlg->GetStart();
+ fIncVal = pDlg->GetStep();
+ fMaxVal = pDlg->GetMax();
+ bDoIt = true;
+ }
+ }
+
+ if( bDoIt )
+ {
+ //nScFillModeMouseModifier = 0; // no Ctrl/Copy
+ pTabViewShell->FillSeries( eFillDir, eFillCmd, eFillDateCmd, fStartVal, fIncVal, fMaxVal );
+
+ if( ! rReq.IsAPI() )
+ {
+ OUString aPara;
+ const Color* pColor = nullptr;
+
+ switch( eFillDir )
+ {
+ case FILL_TO_BOTTOM: aPara = "B"; break;
+ case FILL_TO_RIGHT: aPara = "R"; break;
+ case FILL_TO_TOP: aPara = "T"; break;
+ case FILL_TO_LEFT: aPara = "L"; break;
+ default: break;
+ }
+ rReq.AppendItem( SfxStringItem( FID_FILL_SERIES, aPara ) );
+
+ switch( eFillCmd )
+ {
+ case FILL_SIMPLE: aPara = "S"; break;
+ case FILL_LINEAR: aPara = "L"; break;
+ case FILL_GROWTH: aPara = "G"; break;
+ case FILL_DATE: aPara = "D"; break;
+ case FILL_AUTO: aPara = "A"; break;
+ default: break;
+ }
+ rReq.AppendItem( SfxStringItem( FN_PARAM_1, aPara ) );
+
+ switch( eFillDateCmd )
+ {
+ case FILL_DAY: aPara = "D"; break;
+ case FILL_WEEKDAY: aPara = "W"; break;
+ case FILL_MONTH: aPara = "M"; break;
+ case FILL_YEAR: aPara = "Y"; break;
+ default: break;
+ }
+ rReq.AppendItem( SfxStringItem( FN_PARAM_2, aPara ) );
+
+ sal_uInt32 nFormatKey = pFormatter->GetStandardFormat(SvNumFormatType::NUMBER,
+ ScGlobal::eLnge );
+
+ pFormatter->GetOutputString( fIncVal, nFormatKey, aPara, &pColor );
+ rReq.AppendItem( SfxStringItem( FN_PARAM_3, aPara ) );
+
+ pFormatter->GetOutputString( fStartVal, nFormatKey, aPara, &pColor );
+ rReq.AppendItem( SfxStringItem( FN_PARAM_4, aPara ) );
+
+ pFormatter->GetOutputString( fMaxVal, nFormatKey, aPara, &pColor );
+ rReq.AppendItem( SfxStringItem( FN_PARAM_5, aPara ) );
+
+ rReq.Done();
+ }
+ }
+ }
+ break;
+
+ case FID_FILL_AUTO:
+ {
+ SCCOL nStartCol;
+ SCROW nStartRow;
+ SCCOL nEndCol;
+ SCROW nEndRow;
+
+ GetViewData().GetFillData( nStartCol, nStartRow, nEndCol, nEndRow );
+ SCCOL nFillCol = GetViewData().GetRefEndX();
+ SCROW nFillRow = GetViewData().GetRefEndY();
+ ScDocument& rDoc = GetViewData().GetDocument();
+
+ if( pReqArgs != nullptr )
+ {
+ if( const SfxStringItem* pItem = pReqArgs->GetItemIfSet( FID_FILL_AUTO ) )
+ {
+ ScAddress aScAddress;
+ OUString aArg = pItem->GetValue();
+
+ if( aScAddress.Parse( aArg, rDoc, rDoc.GetAddressConvention() ) & ScRefFlags::VALID )
+ {
+ nFillRow = aScAddress.Row();
+ nFillCol = aScAddress.Col();
+ }
+ }
+
+ SCTAB nStartTab, nEndTab;
+ GetViewData().GetSimpleArea( nStartCol,nStartRow,nStartTab,
+ nEndCol,nEndRow,nEndTab );
+ }
+ else // call via mouse
+ {
+ // not in a merged cell
+
+ if ( nStartCol == nEndCol && nStartRow == nEndRow )
+ {
+ SCCOL nMergeCol = nStartCol;
+ SCROW nMergeRow = nStartRow;
+ if ( GetViewData().GetDocument().ExtendMerge(
+ nStartCol, nStartRow, nMergeCol, nMergeRow,
+ GetViewData().GetTabNo() ) )
+ {
+ if ( nFillCol >= nStartCol && nFillCol <= nMergeCol && nFillRow == nStartRow )
+ nFillCol = nStartCol;
+ if ( nFillRow >= nStartRow && nFillRow <= nMergeRow && nFillCol == nStartCol )
+ nFillRow = nStartRow;
+ }
+ }
+ }
+
+ if ( nFillCol != nEndCol || nFillRow != nEndRow )
+ {
+ if ( nFillCol==nEndCol || nFillRow==nEndRow )
+ {
+ FillDir eDir = FILL_TO_BOTTOM;
+ SCCOLROW nCount = 0;
+
+ if ( nFillCol==nEndCol )
+ {
+ if ( nFillRow > nEndRow )
+ {
+ eDir = FILL_TO_BOTTOM;
+ nCount = nFillRow - nEndRow;
+ }
+ else if ( nFillRow < nStartRow )
+ {
+ eDir = FILL_TO_TOP;
+ nCount = nStartRow - nFillRow;
+ }
+ }
+ else
+ {
+ if ( nFillCol > nEndCol )
+ {
+ eDir = FILL_TO_RIGHT;
+ nCount = nFillCol - nEndCol;
+ }
+ else if ( nFillCol < nStartCol )
+ {
+ eDir = FILL_TO_LEFT;
+ nCount = nStartCol - nFillCol;
+ }
+ }
+
+ if ( nCount != 0)
+ {
+ pTabViewShell->FillAuto( eDir, nStartCol, nStartRow, nEndCol, nEndRow, nCount );
+
+ if( ! rReq.IsAPI() )
+ {
+ ScAddress aAdr( nFillCol, nFillRow, 0 );
+ OUString aAdrStr(aAdr.Format(ScRefFlags::RANGE_ABS, &rDoc, rDoc.GetAddressConvention()));
+
+ rReq.AppendItem( SfxStringItem( FID_FILL_AUTO, aAdrStr ) );
+ rReq.Done();
+ }
+ }
+
+ }
+ else
+ {
+ OSL_FAIL( "Direction not unique for autofill" );
+ }
+ }
+ }
+ break;
+ case FID_FILL_SINGLE_EDIT:
+ ExecuteFillSingleEdit();
+ break;
+ case SID_RANDOM_NUMBER_GENERATOR_DIALOG:
+ {
+ sal_uInt16 nId = ScRandomNumberGeneratorDialogWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+
+ }
+ break;
+ case SID_SAMPLING_DIALOG:
+ {
+ sal_uInt16 nId = ScSamplingDialogWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ }
+ break;
+ case SID_DESCRIPTIVE_STATISTICS_DIALOG:
+ {
+ sal_uInt16 nId = ScDescriptiveStatisticsDialogWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ }
+ break;
+ case SID_ANALYSIS_OF_VARIANCE_DIALOG:
+ {
+ sal_uInt16 nId = ScAnalysisOfVarianceDialogWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ }
+ break;
+ case SID_CORRELATION_DIALOG:
+ {
+ sal_uInt16 nId = ScCorrelationDialogWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ }
+ break;
+ case SID_COVARIANCE_DIALOG:
+ {
+ sal_uInt16 nId = ScCovarianceDialogWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ }
+ break;
+ case SID_EXPONENTIAL_SMOOTHING_DIALOG:
+ {
+ sal_uInt16 nId = ScExponentialSmoothingDialogWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ }
+ break;
+ case SID_MOVING_AVERAGE_DIALOG:
+ {
+ sal_uInt16 nId = ScMovingAverageDialogWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ }
+ break;
+ case SID_REGRESSION_DIALOG:
+ {
+ sal_uInt16 nId = ScRegressionDialogWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ }
+ break;
+ case SID_TTEST_DIALOG:
+ {
+ sal_uInt16 nId = ScTTestDialogWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+
+ }
+ break;
+ case SID_FTEST_DIALOG:
+ {
+ sal_uInt16 nId = ScFTestDialogWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+
+ }
+ break;
+ case SID_ZTEST_DIALOG:
+ {
+ sal_uInt16 nId = ScZTestDialogWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+
+ }
+ break;
+ case SID_CHI_SQUARE_TEST_DIALOG:
+ {
+ sal_uInt16 nId = ScChiSquareTestDialogWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+
+ }
+ break;
+ case SID_FOURIER_ANALYSIS_DIALOG:
+ {
+ sal_uInt16 nId = ScFourierAnalysisDialogWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+
+ }
+ break;
+ case SID_SEARCH_RESULTS_DIALOG:
+ {
+ const SfxPoolItem* pItem = nullptr;
+ if (pReqArgs && pReqArgs->HasItem(SID_SEARCH_RESULTS_DIALOG, &pItem))
+ {
+ bool bVisible = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ // The window ID should equal the slot ID, but not a biggie if it wasn't.
+ sal_uInt16 nId = sc::SearchResultsDlgWrapper::GetChildWindowId();
+ rViewFrm.SetChildWindow(nId, bVisible, false);
+ }
+ rReq.Done();
+ }
+ break;
+
+ case SID_INSERT_SPARKLINE:
+ case SID_EDIT_SPARKLINE_GROUP:
+ {
+ sal_uInt16 nId = sc::SparklineDialogWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrame = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWindow = rViewFrame.GetChildWindow(nId);
+ pScMod->SetRefDialog(nId, pWindow == nullptr);
+ rReq.Done();
+ }
+ break;
+
+ case SID_EDIT_SPARKLINE:
+ {
+ sal_uInt16 nId = sc::SparklineDataRangeDialogWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrame = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWindow = rViewFrame.GetChildWindow(nId);
+ pScMod->SetRefDialog(nId, pWindow == nullptr);
+ rReq.Done();
+ }
+ break;
+
+ case SID_DELETE_SPARKLINE:
+ {
+ pTabViewShell->DeleteContents(InsertDeleteFlags::SPARKLINES);
+
+ rReq.Done();
+ }
+ break;
+
+ case SID_DELETE_SPARKLINE_GROUP:
+ {
+ ScRange aMarkRange;
+ ScMarkType eMarkType = GetViewData().GetSimpleArea(aMarkRange);
+ if (eMarkType == SC_MARK_SIMPLE)
+ {
+ std::shared_ptr<sc::SparklineGroup> pSparklineGroup;
+ if (GetViewData().GetDocument().GetSparklineGroupInRange(aMarkRange, pSparklineGroup) && pSparklineGroup)
+ {
+ GetViewData().GetDocShell()->GetDocFunc().DeleteSparklineGroup(pSparklineGroup, GetViewData().GetTabNo());
+ }
+ }
+ rReq.Done();
+ }
+ break;
+
+ case SID_GROUP_SPARKLINES:
+ {
+ ScRange aRange;
+ if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
+ {
+ ScAddress aCursorAddress(GetViewData().GetCurX(), GetViewData().GetCurY(), GetViewData().GetTabNo());
+ auto pSparkline = GetViewData().GetDocument().GetSparkline(aCursorAddress);
+ if (pSparkline)
+ {
+ auto const& rpSparklineGroup = pSparkline->getSparklineGroup();
+ GetViewData().GetDocShell()->GetDocFunc().GroupSparklines(aRange, rpSparklineGroup);
+ }
+ }
+ rReq.Done();
+ }
+ break;
+
+ case SID_UNGROUP_SPARKLINES:
+ {
+ ScRange aRange;
+ if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
+ {
+ GetViewData().GetDocShell()->GetDocFunc().UngroupSparklines(aRange);
+ }
+ rReq.Done();
+ }
+ break;
+
+ // disposal (Outlines)
+ // SID_AUTO_OUTLINE, SID_OUTLINE_DELETEALL in Execute (in docsh.idl)
+
+ case SID_OUTLINE_HIDE:
+ if ( GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
+ GetViewData().GetCurY(), GetViewData().GetTabNo() ) )
+ pTabViewShell->SetDataPilotDetails( false );
+ else
+ pTabViewShell->HideMarkedOutlines();
+ rReq.Done();
+ break;
+
+ case SID_OUTLINE_SHOW:
+ {
+ ScDPObject* pDPObj = GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
+ GetViewData().GetCurY(), GetViewData().GetTabNo() );
+ if ( pDPObj )
+ {
+ Sequence<sheet::DataPilotFieldFilter> aFilters;
+ css::sheet::DataPilotFieldOrientation nOrientation;
+ if ( pTabViewShell->HasSelectionForDrillDown( nOrientation ) )
+ {
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractScDPShowDetailDlg> pDlg( pFact->CreateScDPShowDetailDlg(
+ pTabViewShell->GetFrameWeld(), *pDPObj, nOrientation ) );
+ if ( pDlg->Execute() == RET_OK )
+ {
+ OUString aNewDimName( pDlg->GetDimensionName() );
+ pTabViewShell->SetDataPilotDetails( true, &aNewDimName );
+ }
+ }
+ else if ( !pDPObj->IsServiceData() &&
+ pDPObj->GetDataFieldPositionData(
+ ScAddress( GetViewData().GetCurX(), GetViewData().GetCurY(), GetViewData().GetTabNo() ),
+ aFilters ) )
+ pTabViewShell->ShowDataPilotSourceData( *pDPObj, aFilters );
+ else
+ pTabViewShell->SetDataPilotDetails(true);
+ }
+ else
+ pTabViewShell->ShowMarkedOutlines();
+ rReq.Done();
+ }
+ break;
+
+ case SID_OUTLINE_MAKE:
+ {
+ bool bColumns = false;
+ bool bOk = true;
+
+ if ( GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
+ GetViewData().GetCurY(), GetViewData().GetTabNo() ) )
+ {
+ ScDPNumGroupInfo aNumInfo;
+ aNumInfo.mbEnable = true;
+ aNumInfo.mbAutoStart = true;
+ aNumInfo.mbAutoEnd = true;
+ sal_Int32 nParts = 0;
+ if ( pTabViewShell->HasSelectionForDateGroup( aNumInfo, nParts ) )
+ {
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+ const Date& rNullDate( GetViewData().GetDocument().GetFormatTable()->GetNullDate() );
+ ScopedVclPtr<AbstractScDPDateGroupDlg> pDlg( pFact->CreateScDPDateGroupDlg(
+ pTabViewShell->GetFrameWeld(),
+ aNumInfo, nParts, rNullDate ) );
+ if( pDlg->Execute() == RET_OK )
+ {
+ aNumInfo = pDlg->GetGroupInfo();
+ pTabViewShell->DateGroupDataPilot( aNumInfo, pDlg->GetDatePart() );
+ }
+ }
+ else if ( pTabViewShell->HasSelectionForNumGroup( aNumInfo ) )
+ {
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractScDPNumGroupDlg> pDlg( pFact->CreateScDPNumGroupDlg(
+ pTabViewShell->GetFrameWeld(), aNumInfo ) );
+ if( pDlg->Execute() == RET_OK )
+ pTabViewShell->NumGroupDataPilot( pDlg->GetGroupInfo() );
+ }
+ else
+ pTabViewShell->GroupDataPilot();
+
+ bOk = false;
+ }
+ else if( pReqArgs != nullptr )
+ {
+ const SfxPoolItem* pItem;
+ bOk = false;
+
+ if( pReqArgs->HasItem( SID_OUTLINE_MAKE, &pItem ) )
+ {
+ OUString aCol = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ aCol = aCol.toAsciiUpperCase();
+
+ switch( aCol[0] )
+ {
+ case 'R': bColumns=false; bOk = true;break;
+ case 'C': bColumns=true; bOk = true;break;
+ }
+ }
+ }
+ else // Dialog, when not whole rows/columns are marked
+ {
+ if ( GetViewData().SimpleColMarked() && !GetViewData().SimpleRowMarked() )
+ bColumns = true;
+ else if ( !GetViewData().SimpleColMarked() && GetViewData().SimpleRowMarked() )
+ bColumns = false;
+ else
+ {
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ VclPtr<AbstractScGroupDlg> pDlg(pFact->CreateAbstractScGroupDlg(pTabViewShell->GetFrameWeld()));
+
+ pDlg->StartExecuteAsync(
+ [pDlg, pTabViewShell] (sal_Int32 nResult) {
+ if( RET_OK == nResult )
+ {
+ bool bColumn = pDlg->GetColsChecked();
+ pTabViewShell->MakeOutline( bColumn );
+ }
+ pDlg->disposeOnce();
+ }
+ );
+
+ bOk = false;
+ }
+ }
+ if (bOk)
+ {
+ pTabViewShell->MakeOutline( bColumns );
+
+ if( ! rReq.IsAPI() )
+ {
+ OUString aCol = bColumns ? OUString('C') : OUString('R');
+ rReq.AppendItem( SfxStringItem( SID_OUTLINE_MAKE, aCol ) );
+ rReq.Done();
+ }
+ }
+ }
+ break;
+
+ case SID_OUTLINE_REMOVE:
+ {
+ bool bColumns = false;
+ bool bOk = true;
+
+ if ( GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
+ GetViewData().GetCurY(), GetViewData().GetTabNo() ) )
+ {
+ pTabViewShell->UngroupDataPilot();
+ bOk = false;
+ }
+ else if( pReqArgs != nullptr )
+ {
+ const SfxPoolItem* pItem;
+ bOk = false;
+
+ if( pReqArgs->HasItem( SID_OUTLINE_REMOVE, &pItem ) )
+ {
+ OUString aCol = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ aCol = aCol.toAsciiUpperCase();
+
+ switch (aCol[0])
+ {
+ case 'R': bColumns=false; bOk = true;break;
+ case 'C': bColumns=true; bOk = true;break;
+ }
+ }
+ }
+ else // Dialog only when removal for rows and columns is possible
+ {
+ bool bColPoss, bRowPoss;
+ pTabViewShell->TestRemoveOutline( bColPoss, bRowPoss );
+ // TODO: handle this case in LOK too
+ if ( bColPoss && bRowPoss && !comphelper::LibreOfficeKit::isActive() )
+ {
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ VclPtr<AbstractScGroupDlg> pDlg(pFact->CreateAbstractScGroupDlg(pTabViewShell->GetFrameWeld(), true));
+
+ pDlg->StartExecuteAsync(
+ [pDlg, pTabViewShell] (sal_Int32 nResult) {
+ if( RET_OK == nResult )
+ {
+ bool bColumn = pDlg->GetColsChecked();
+ pTabViewShell->RemoveOutline( bColumn );
+ }
+ pDlg->disposeOnce();
+ }
+ );
+
+ bOk = false;
+ }
+ else if ( bColPoss )
+ bColumns = true;
+ else if ( bRowPoss )
+ bColumns = false;
+ else
+ bOk = false;
+ }
+ if (bOk)
+ {
+ pTabViewShell->RemoveOutline( bColumns );
+
+ if( ! rReq.IsAPI() )
+ {
+ OUString aCol = bColumns ? OUString('C') : OUString('R');
+ rReq.AppendItem( SfxStringItem( SID_OUTLINE_REMOVE, aCol ) );
+ rReq.Done();
+ }
+ }
+ }
+ break;
+
+ // Clipboard
+
+ case SID_COPY: // for graphs in DrawShell
+ {
+ weld::WaitObject aWait( GetViewData().GetDialogParent() );
+ pTabViewShell->CopyToClip( nullptr, false, false, true );
+ rReq.Done();
+ GetViewData().SetPasteMode( ScPasteFlags::Mode | ScPasteFlags::Border );
+ pTabViewShell->ShowCursor();
+ pTabViewShell->UpdateCopySourceOverlay();
+ }
+ break;
+
+ case SID_CUT: // for graphs in DrawShell
+ {
+ weld::WaitObject aWait( GetViewData().GetDialogParent() );
+ pTabViewShell->CutToClip();
+ rReq.Done();
+ GetViewData().SetPasteMode( ScPasteFlags::Mode | ScPasteFlags::Border );
+ pTabViewShell->ShowCursor();
+ pTabViewShell->UpdateCopySourceOverlay();
+ }
+ break;
+
+ case SID_PASTE:
+ {
+ ScClipUtil::PasteFromClipboard( GetViewData(), pTabViewShell, true );
+ rReq.Done();
+ }
+ break;
+
+ case SID_CLIPBOARD_FORMAT_ITEMS:
+ {
+ weld::WaitObject aWait( GetViewData().GetDialogParent() );
+
+ SotClipboardFormatId nFormat = SotClipboardFormatId::NONE;
+ const SfxPoolItem* pItem;
+ if ( pReqArgs && pReqArgs->GetItemState(nSlot, true, &pItem) == SfxItemState::SET )
+ if (auto pIntItem = dynamic_cast<const SfxUInt32Item*>(pItem) )
+ nFormat = static_cast<SotClipboardFormatId>(pIntItem->GetValue());
+
+ if ( nFormat != SotClipboardFormatId::NONE )
+ {
+ css::uno::Reference<css::datatransfer::XTransferable2> xTransferable(ScTabViewShell::GetClipData(GetViewData().GetActiveWin()));
+ bool bCells = ( ScTransferObj::GetOwnClipboard(xTransferable) != nullptr );
+ bool bDraw = ( ScDrawTransferObj::GetOwnClipboard(xTransferable) != nullptr );
+ bool bOle = ( nFormat == SotClipboardFormatId::EMBED_SOURCE );
+
+ if ( bCells && bOle )
+ pTabViewShell->PasteFromSystem();
+ else if ( bDraw && bOle )
+ pTabViewShell->PasteDraw();
+ else
+ pTabViewShell->PasteFromSystem(nFormat);
+ }
+ //?else
+ //? pTabViewShell->PasteFromSystem();
+
+ rReq.Done();
+ }
+ pTabViewShell->CellContentChanged();
+ break;
+
+ case FID_INS_CELL_CONTENTS:
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+ bool bOtherDoc = !rDoc.IsClipboardSource();
+ // keep a reference in case the clipboard is changed during dialog or PasteFromClip
+ const ScTransferObj* pOwnClip = ScTransferObj::GetOwnClipboard(ScTabViewShell::GetClipData(GetViewData().GetActiveWin()));
+ if ( pOwnClip )
+ {
+ InsertDeleteFlags nFlags = InsertDeleteFlags::NONE;
+ ScPasteFunc nFunction = ScPasteFunc::NONE;
+ InsCellCmd eMoveMode = INS_NONE;
+ bool bSkipEmpty = false;
+ bool bTranspose = false;
+ bool bAsLink = false;
+
+ if ( pReqArgs!=nullptr && pTabViewShell->SelectionEditable() )
+ {
+ const SfxPoolItem* pItem;
+ OUString aFlags('A');
+
+ if( pReqArgs->HasItem( FID_INS_CELL_CONTENTS, &pItem ) )
+ aFlags = static_cast<const SfxStringItem*>(pItem)->GetValue();
+
+ nFlags |= FlagsFromString(aFlags);
+
+ const SfxUInt16Item* pFuncItem = rReq.GetArg<SfxUInt16Item>(FN_PARAM_1);
+ const SfxBoolItem* pSkipItem = rReq.GetArg<SfxBoolItem>(FN_PARAM_2);
+ const SfxBoolItem* pTransposeItem = rReq.GetArg<SfxBoolItem>(FN_PARAM_3);
+ const SfxBoolItem* pLinkItem = rReq.GetArg<SfxBoolItem>(FN_PARAM_4);
+ const SfxInt16Item* pMoveItem = rReq.GetArg<SfxInt16Item>(FN_PARAM_5);
+ if ( pFuncItem )
+ nFunction = static_cast<ScPasteFunc>(pFuncItem->GetValue());
+ if ( pSkipItem )
+ bSkipEmpty = pSkipItem->GetValue();
+ if ( pTransposeItem )
+ bTranspose = pTransposeItem->GetValue();
+ if ( pLinkItem )
+ bAsLink = pLinkItem->GetValue();
+ if ( pMoveItem )
+ eMoveMode = static_cast<InsCellCmd>(pMoveItem->GetValue());
+ }
+ else
+ {
+ ScEditableTester aTester( pTabViewShell );
+ if (aTester.IsEditable())
+ {
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScInsertContentsDlg> pDlg(pFact->CreateScInsertContentsDlg(pTabViewShell->GetFrameWeld()));
+ pDlg->SetOtherDoc( bOtherDoc );
+ // if ChangeTrack MoveMode disable
+ pDlg->SetChangeTrack( rDoc.GetChangeTrack() != nullptr );
+ // fdo#56098 disable shift if necessary
+ if (!bOtherDoc)
+ {
+ ScViewData& rData = GetViewData();
+ if ( rData.GetMarkData().GetTableSelect( rData.GetTabNo() ) )
+ {
+ SCCOL nStartX, nEndX, nClipStartX, nClipSizeX, nRangeSizeX;
+ SCROW nStartY, nEndY, nClipStartY, nClipSizeY, nRangeSizeY;
+ SCTAB nStartTab, nEndTab;
+ pOwnClip->GetDocument()->GetClipStart( nClipStartX, nClipStartY );
+ pOwnClip->GetDocument()->GetClipArea( nClipSizeX, nClipSizeY, true );
+
+ if ( rData.GetSimpleArea( nStartX, nStartY, nStartTab,
+ nEndX, nEndY, nEndTab ) != SC_MARK_SIMPLE ||
+ nStartTab != nEndTab )
+ {
+ // the destination is not a simple range,
+ // assume the destination as the current cell
+ nStartX = nEndX = rData.GetCurX();
+ nStartY = nEndY = rData.GetCurY();
+ nStartTab = rData.GetTabNo();
+ }
+ // we now have clip- and range dimensions
+ // the size of the destination area is the larger of the two
+ nRangeSizeX = nClipSizeX >= nEndX - nStartX ? nClipSizeX : nEndX - nStartX;
+ nRangeSizeY = nClipSizeY >= nEndY - nStartY ? nClipSizeY : nEndY - nStartY;
+ // When the source and destination areas intersect things may go wrong,
+ // especially if the area contains references. This may produce data loss
+ // (e.g. formulas that get wrong references), this scenario _must_ be avoided.
+ ScRange aSource( nClipStartX, nClipStartY, nStartTab,
+ nClipStartX + nClipSizeX, nClipStartY + nClipSizeY, nStartTab );
+ ScRange aDest( nStartX, nStartY, nStartTab,
+ nStartX + nRangeSizeX, nStartY + nRangeSizeY, nStartTab );
+ if ( pOwnClip->GetDocument()->IsCutMode() && aSource.Intersects( aDest ) )
+ pDlg->SetCellShiftDisabled( CellShiftDisabledFlags::Down | CellShiftDisabledFlags::Right );
+ else
+ {
+ //no conflict with intersecting ranges,
+ //check if paste plus shift will fit on sheet
+ //and disable shift-option if no fit
+ CellShiftDisabledFlags nDisableShiftX = CellShiftDisabledFlags::NONE;
+ CellShiftDisabledFlags nDisableShiftY = CellShiftDisabledFlags::NONE;
+
+ //check if horizontal shift will fit
+ if ( !rData.GetDocument().IsBlockEmpty(
+ rDoc.MaxCol() - nRangeSizeX, nStartY,
+ rDoc.MaxCol(), nStartY + nRangeSizeY,
+ nStartTab ) )
+ nDisableShiftX = CellShiftDisabledFlags::Right;
+
+ //check if vertical shift will fit
+ if ( !rData.GetDocument().IsBlockEmpty(
+ nStartX, rDoc.MaxRow() - nRangeSizeY,
+ nStartX + nRangeSizeX, rDoc.MaxRow(),
+ nStartTab ) )
+ nDisableShiftY = CellShiftDisabledFlags::Down;
+
+ if ( nDisableShiftX != CellShiftDisabledFlags::NONE || nDisableShiftY != CellShiftDisabledFlags::NONE)
+ pDlg->SetCellShiftDisabled( nDisableShiftX | nDisableShiftY );
+ }
+ }
+ }
+ if (pDlg->Execute() == RET_OK)
+ {
+ nFlags = pDlg->GetInsContentsCmdBits();
+ nFunction = pDlg->GetFormulaCmdBits();
+ bSkipEmpty = pDlg->IsSkipEmptyCells();
+ bTranspose = pDlg->IsTranspose();
+ bAsLink = pDlg->IsLink();
+ eMoveMode = pDlg->GetMoveMode();
+ }
+ }
+ else
+ pTabViewShell->ErrorMessage(aTester.GetMessageId());
+ }
+
+ if( nFlags != InsertDeleteFlags::NONE )
+ {
+ {
+ weld::WaitObject aWait( GetViewData().GetDialogParent() );
+ if ( bAsLink && bOtherDoc )
+ pTabViewShell->PasteFromSystem(SotClipboardFormatId::LINK); // DDE insert
+ else
+ {
+ pTabViewShell->PasteFromClip( nFlags, pOwnClip->GetDocument(),
+ nFunction, bSkipEmpty, bTranspose, bAsLink,
+ eMoveMode, InsertDeleteFlags::NONE, true ); // allow warning dialog
+ }
+ }
+
+ if( !pReqArgs )
+ {
+ OUString aFlags = FlagsToString( nFlags );
+
+ rReq.AppendItem( SfxStringItem( FID_INS_CELL_CONTENTS, aFlags ) );
+ rReq.AppendItem( SfxBoolItem( FN_PARAM_2, bSkipEmpty ) );
+ rReq.AppendItem( SfxBoolItem( FN_PARAM_3, bTranspose ) );
+ rReq.AppendItem( SfxBoolItem( FN_PARAM_4, bAsLink ) );
+ rReq.AppendItem( SfxUInt16Item( FN_PARAM_1, static_cast<sal_uInt16>(nFunction) ) );
+ rReq.AppendItem( SfxInt16Item( FN_PARAM_5, static_cast<sal_Int16>(eMoveMode) ) );
+ rReq.Done();
+ }
+ }
+ }
+ }
+ pTabViewShell->CellContentChanged(); // => PasteFromXXX ???
+ break;
+ case SID_PASTE_ONLY_VALUE:
+ case SID_PASTE_ONLY_TEXT:
+ case SID_PASTE_ONLY_FORMULA:
+ {
+ if ( ScTransferObj::GetOwnClipboard(ScTabViewShell::GetClipData(GetViewData().GetActiveWin())) ) // own cell data
+ {
+ rReq.SetSlot( FID_INS_CELL_CONTENTS );
+ OUString aFlags;
+ if ( nSlot == SID_PASTE_ONLY_VALUE )
+ aFlags = "V";
+ else if ( nSlot == SID_PASTE_ONLY_TEXT )
+ aFlags = "S";
+ else
+ aFlags = "F";
+ rReq.AppendItem( SfxStringItem( FID_INS_CELL_CONTENTS, aFlags ) );
+ ExecuteSlot( rReq, GetInterface() );
+ rReq.SetReturnValue(SfxInt16Item(nSlot, 1)); // 1 = success
+ pTabViewShell->CellContentChanged();
+ }
+ else
+ rReq.SetReturnValue(SfxInt16Item(nSlot, 0)); // 0 = fail
+ break;
+ }
+ case SID_PASTE_TRANSPOSED:
+ {
+ if (ScTransferObj::GetOwnClipboard(
+ ScTabViewShell::GetClipData(GetViewData().GetActiveWin()))) // own cell data
+ {
+ rReq.SetSlot(FID_INS_CELL_CONTENTS);
+ // By default content (values/numbers, strings, formulas and dates),
+ // attributes and notes are pasted
+ rReq.AppendItem(SfxBoolItem(FN_PARAM_3, true)); // transpose
+ ExecuteSlot(rReq, GetInterface());
+ rReq.SetReturnValue(SfxInt16Item(nSlot, 1)); // 1 = success
+ pTabViewShell->CellContentChanged();
+ }
+ else
+ rReq.SetReturnValue(SfxInt16Item(nSlot, 0)); // 0 = fail
+ break;
+ }
+ case SID_PASTE_AS_LINK:
+ {
+ if (ScTransferObj::GetOwnClipboard(
+ ScTabViewShell::GetClipData(GetViewData().GetActiveWin()))) // own cell data
+ {
+ rReq.SetSlot(FID_INS_CELL_CONTENTS);
+ // paste links to values/numbers, strings, formulas and dates
+ // do not paste attributes, notes and objects
+ rReq.AppendItem(SfxStringItem(FID_INS_CELL_CONTENTS, "VSFD"));
+ rReq.AppendItem(SfxBoolItem(FN_PARAM_4, true)); // as link
+ ExecuteSlot(rReq, GetInterface());
+ rReq.SetReturnValue(SfxInt16Item(nSlot, 1)); // 1 = success
+ pTabViewShell->CellContentChanged();
+ }
+ else
+ rReq.SetReturnValue(SfxInt16Item(nSlot, 0)); // 0 = fail
+ break;
+ }
+ case SID_PASTE_TEXTIMPORT_DIALOG:
+ {
+ vcl::Window* pWin = GetViewData().GetActiveWin();
+ TransferableDataHelper aDataHelper(
+ TransferableDataHelper::CreateFromSystemClipboard(pWin));
+ const uno::Reference<datatransfer::XTransferable>& xTransferable
+ = aDataHelper.GetTransferable();
+ SotClipboardFormatId format = SotClipboardFormatId::STRING;
+ bool bSuccess = false;
+ if (xTransferable.is() && HasClipboardFormat(format))
+ {
+ OUString sStrBuffer;
+ bSuccess = aDataHelper.GetString(format, sStrBuffer);
+ if (bSuccess)
+ {
+ auto pStrm = std::make_shared<ScImportStringStream>(sStrBuffer);
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+ VclPtr<AbstractScImportAsciiDlg> pDlg(pFact->CreateScImportAsciiDlg(
+ pWin ? pWin->GetFrameWeld() : nullptr, OUString(), pStrm.get(), SC_PASTETEXT));
+ ScRange aRange;
+ SCCOL nPosX = 0;
+ SCROW nPosY = 0;
+ if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
+ {
+ nPosX = aRange.aStart.Col();
+ nPosY = aRange.aStart.Row();
+ }
+ else
+ {
+ nPosX = GetViewData().GetCurX();
+ nPosY = GetViewData().GetCurY();
+ }
+ ScAddress aCellPos(nPosX, nPosY, GetViewData().GetTabNo());
+ auto pObj = std::make_shared<ScImportExport>(GetViewData().GetDocument(), aCellPos);
+ pObj->SetOverwriting(true);
+ if (pDlg->Execute()) {
+ ScAsciiOptions aOptions;
+ pDlg->GetOptions(aOptions);
+ pDlg->SaveParameters();
+ pObj->SetExtOptions(aOptions);
+ pObj->ImportString(sStrBuffer, format);
+ }
+ pDlg->disposeOnce();
+ rReq.SetReturnValue(SfxInt16Item(nSlot, 1)); // 1 = success, 0 = fail
+ rReq.Done();
+ }
+ }
+ if (!bSuccess)
+ {
+ rReq.SetReturnValue(SfxInt16Item(nSlot, 0)); // 0 = fail
+ rReq.Ignore();
+ }
+ }
+ break;
+ case SID_PASTE_SPECIAL:
+ // differentiate between own cell data and draw objects/external data
+ // this makes FID_INS_CELL_CONTENTS superfluous
+ {
+ vcl::Window* pWin = GetViewData().GetActiveWin();
+ css::uno::Reference<css::datatransfer::XTransferable2> xTransferable(ScTabViewShell::GetClipData(pWin));
+
+ // Clipboard-ID given as parameter? Basic "PasteSpecial(Format)"
+ const SfxPoolItem* pItem=nullptr;
+ if ( pReqArgs &&
+ pReqArgs->GetItemState(nSlot, true, &pItem) == SfxItemState::SET &&
+ dynamic_cast<const SfxUInt32Item*>( pItem) != nullptr )
+ {
+ SotClipboardFormatId nFormat = static_cast<SotClipboardFormatId>(static_cast<const SfxUInt32Item*>(pItem)->GetValue());
+ bool bRet=true;
+ {
+ weld::WaitObject aWait( GetViewData().GetDialogParent() );
+ bool bDraw = ( ScDrawTransferObj::GetOwnClipboard(xTransferable) != nullptr );
+ if ( bDraw && nFormat == SotClipboardFormatId::EMBED_SOURCE )
+ pTabViewShell->PasteDraw();
+ else
+ bRet = pTabViewShell->PasteFromSystem(nFormat, true); // TRUE: no error messages
+ }
+
+ if ( bRet )
+ {
+ rReq.SetReturnValue(SfxInt16Item(nSlot, 1)); // 1 = success, 0 = fail
+ rReq.Done();
+ }
+ else
+ // if format is not available -> fallback to request without parameters
+ pItem = nullptr;
+ }
+
+ if ( !pItem )
+ {
+ if ( ScTransferObj::GetOwnClipboard(xTransferable) ) // own cell data
+ {
+ rReq.SetSlot( FID_INS_CELL_CONTENTS );
+ ExecuteSlot( rReq, GetInterface() );
+ rReq.SetReturnValue(SfxInt16Item(nSlot, 1)); // 1 = success
+ }
+ else // draw objects or external data
+ {
+ bool bDraw = ( ScDrawTransferObj::GetOwnClipboard(xTransferable) != nullptr );
+
+ SvxClipboardFormatItem aFormats( SID_CLIPBOARD_FORMAT_ITEMS );
+ GetPossibleClipboardFormats( aFormats );
+
+ sal_uInt16 nFormatCount = aFormats.Count();
+ if ( nFormatCount )
+ {
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ ScopedVclPtr<SfxAbstractPasteDialog> pDlg(pFact->CreatePasteDialog(pTabViewShell->GetFrameWeld()));
+ for (sal_uInt16 i=0; i<nFormatCount; i++)
+ {
+ SotClipboardFormatId nFormatId = aFormats.GetClipbrdFormatId( i );
+ OUString aName = aFormats.GetClipbrdFormatName( i );
+ // special case for paste dialog: '*' is replaced by object type
+ if ( nFormatId == SotClipboardFormatId::EMBED_SOURCE )
+ aName = "*";
+ pDlg->Insert( nFormatId, aName );
+ }
+
+ SfxViewFrame& rViewFrame = pTabViewShell->GetViewFrame();
+ auto xFrame = rViewFrame.GetFrame().GetFrameInterface();
+ const OUString aModuleName(vcl::CommandInfoProvider::GetModuleIdentifier(xFrame));
+ auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(".uno:PasteTextImportDialog", aModuleName);
+ OUString sLabel(vcl::CommandInfoProvider::GetTooltipLabelForCommand(aProperties));
+ pDlg->InsertUno(".uno:PasteTextImportDialog", sLabel);
+
+ TransferableDataHelper aDataHelper(
+ TransferableDataHelper::CreateFromSystemClipboard( pWin ) );
+ SotClipboardFormatId nFormat = pDlg->GetFormat( aDataHelper.GetTransferable() );
+ if (nFormat != SotClipboardFormatId::NONE)
+ {
+ {
+ weld::WaitObject aWait( GetViewData().GetDialogParent() );
+ if ( bDraw && nFormat == SotClipboardFormatId::EMBED_SOURCE )
+ pTabViewShell->PasteDraw();
+ else
+ pTabViewShell->PasteFromSystem(nFormat);
+ }
+ rReq.SetReturnValue(SfxInt16Item(nSlot, 1)); // 1 = success
+ rReq.AppendItem( SfxUInt32Item( nSlot, static_cast<sal_uInt32>(nFormat) ) );
+ rReq.Done();
+ }
+ else
+ {
+ rReq.SetReturnValue(SfxInt16Item(nSlot, 0)); // 0 = fail
+ rReq.Ignore();
+ }
+ }
+ else
+ rReq.SetReturnValue(SfxInt16Item(nSlot, 0)); // 0 = fail
+ }
+ }
+ }
+ pTabViewShell->CellContentChanged(); // => PasteFromSystem() ???
+ break;
+
+ case SID_PASTE_UNFORMATTED:
+ // differentiate between own cell data and draw objects/external data
+ // this makes FID_INS_CELL_CONTENTS superfluous
+ {
+ weld::WaitObject aWait( GetViewData().GetDialogParent() );
+
+ // we should differentiate between SotClipboardFormatId::STRING and SotClipboardFormatId::STRING_TSVC,
+ // and paste the SotClipboardFormatId::STRING_TSVC if it is available.
+ // Which makes a difference if the clipboard contains cells with embedded line breaks.
+
+ SotClipboardFormatId nFormat = HasClipboardFormat( SotClipboardFormatId::STRING_TSVC) ?
+ SotClipboardFormatId::STRING_TSVC : SotClipboardFormatId::STRING;
+
+ const bool bRet = pTabViewShell->PasteFromSystem(nFormat, true); // TRUE: no error messages
+ if ( bRet )
+ {
+ rReq.SetReturnValue(SfxInt16Item(nSlot, 1)); // 1 = success
+ rReq.Done();
+ }
+ else
+ {
+ rReq.SetReturnValue(SfxInt16Item(nSlot, 0)); // 0 = fail
+ }
+
+ pTabViewShell->CellContentChanged(); // => PasteFromSystem() ???
+ }
+ break;
+
+ // other
+
+ case FID_INS_ROWBRK:
+ pTabViewShell->InsertPageBreak( false );
+ rReq.Done();
+ break;
+
+ case FID_INS_COLBRK:
+ pTabViewShell->InsertPageBreak( true );
+ rReq.Done();
+ break;
+
+ case FID_DEL_ROWBRK:
+ pTabViewShell->DeletePageBreak( false );
+ rReq.Done();
+ break;
+
+ case FID_DEL_COLBRK:
+ pTabViewShell->DeletePageBreak( true );
+ rReq.Done();
+ break;
+
+ case SID_DETECTIVE_ADD_PRED:
+ pTabViewShell->DetectiveAddPred();
+ rReq.Done();
+ break;
+
+ case SID_DETECTIVE_DEL_PRED:
+ pTabViewShell->DetectiveDelPred();
+ rReq.Done();
+ break;
+
+ case SID_DETECTIVE_ADD_SUCC:
+ pTabViewShell->DetectiveAddSucc();
+ rReq.Done();
+ break;
+
+ case SID_DETECTIVE_DEL_SUCC:
+ pTabViewShell->DetectiveDelSucc();
+ rReq.Done();
+ break;
+
+ case SID_DETECTIVE_ADD_ERR:
+ pTabViewShell->DetectiveAddError();
+ rReq.Done();
+ break;
+
+ case SID_DETECTIVE_INVALID:
+ pTabViewShell->DetectiveMarkInvalid();
+ rReq.Done();
+ break;
+
+ case SID_DETECTIVE_REFRESH:
+ pTabViewShell->DetectiveRefresh();
+ rReq.Done();
+ break;
+
+ case SID_DETECTIVE_MARK_PRED:
+ pTabViewShell->DetectiveMarkPred();
+ break;
+ case SID_DETECTIVE_MARK_SUCC:
+ pTabViewShell->DetectiveMarkSucc();
+ break;
+ case SID_INSERT_CURRENT_DATE:
+ pTabViewShell->InsertCurrentTime(
+ SvNumFormatType::DATE, ScResId(STR_UNDO_INSERT_CURRENT_DATE));
+ break;
+ case SID_INSERT_CURRENT_TIME:
+ pTabViewShell->InsertCurrentTime(
+ SvNumFormatType::TIME, ScResId(STR_UNDO_INSERT_CURRENT_TIME));
+ break;
+
+ case SID_SPELL_DIALOG:
+ {
+ SfxViewFrame& rViewFrame = pTabViewShell->GetViewFrame();
+ if( rReq.GetArgs() )
+ rViewFrame.SetChildWindow( SID_SPELL_DIALOG,
+ static_cast< const SfxBoolItem& >( rReq.GetArgs()->
+ Get( SID_SPELL_DIALOG ) ).GetValue() );
+ else
+ rViewFrame.ToggleChildWindow( SID_SPELL_DIALOG );
+
+ rViewFrame.GetBindings().Invalidate( SID_SPELL_DIALOG );
+ rReq.Ignore();
+ }
+ break;
+
+ case SID_HANGUL_HANJA_CONVERSION:
+ pTabViewShell->DoHangulHanjaConversion();
+ break;
+
+ case SID_CHINESE_CONVERSION:
+ {
+ //open ChineseTranslationDialog
+ Reference< XComponentContext > xContext(
+ ::cppu::defaultBootstrap_InitialComponentContext() ); //@todo get context from calc if that has one
+ if(xContext.is())
+ {
+ Reference< lang::XMultiComponentFactory > xMCF( xContext->getServiceManager() );
+ if(xMCF.is())
+ {
+ Reference< ui::dialogs::XExecutableDialog > xDialog(
+ xMCF->createInstanceWithContext(
+ "com.sun.star.linguistic2.ChineseTranslationDialog"
+ , xContext),
+ UNO_QUERY);
+ Reference< lang::XInitialization > xInit( xDialog, UNO_QUERY );
+ if( xInit.is() )
+ {
+ // initialize dialog
+ uno::Sequence<uno::Any> aSeq(comphelper::InitAnyPropertySequence(
+ {
+ {"ParentWindow", uno::Any(Reference< awt::XWindow >())}
+ }));
+ xInit->initialize( aSeq );
+
+ //execute dialog
+ sal_Int16 nDialogRet = xDialog->execute();
+ if( RET_OK == nDialogRet )
+ {
+ //get some parameters from the dialog
+ bool bToSimplified = true;
+ bool bUseVariants = true;
+ bool bCommonTerms = true;
+ Reference< beans::XPropertySet > xProp( xDialog, UNO_QUERY );
+ if( xProp.is() )
+ {
+ try
+ {
+ xProp->getPropertyValue("IsDirectionToSimplified") >>= bToSimplified;
+ xProp->getPropertyValue("IsUseCharacterVariants") >>= bUseVariants;
+ xProp->getPropertyValue("IsTranslateCommonTerms") >>= bCommonTerms;
+ }
+ catch( Exception& )
+ {
+ }
+ }
+
+ //execute translation
+ LanguageType eSourceLang = bToSimplified ? LANGUAGE_CHINESE_TRADITIONAL : LANGUAGE_CHINESE_SIMPLIFIED;
+ LanguageType eTargetLang = bToSimplified ? LANGUAGE_CHINESE_SIMPLIFIED : LANGUAGE_CHINESE_TRADITIONAL;
+ sal_Int32 nOptions = bUseVariants ? i18n::TextConversionOption::USE_CHARACTER_VARIANTS : 0;
+ if( !bCommonTerms )
+ nOptions |= i18n::TextConversionOption::CHARACTER_BY_CHARACTER;
+
+ vcl::Font aTargetFont = OutputDevice::GetDefaultFont(
+ DefaultFontType::CJK_SPREADSHEET,
+ eTargetLang, GetDefaultFontFlags::OnlyOne );
+ ScConversionParam aConvParam( SC_CONVERSION_CHINESE_TRANSL,
+ eSourceLang, eTargetLang, std::move(aTargetFont), nOptions, false );
+ pTabViewShell->DoSheetConversion( aConvParam );
+ }
+ }
+ Reference< lang::XComponent > xComponent( xDialog, UNO_QUERY );
+ if( xComponent.is() )
+ xComponent->dispose();
+ }
+ }
+ }
+ break;
+
+ case SID_CONVERT_FORMULA_TO_VALUE:
+ {
+ pTabViewShell->ConvertFormulaToValue();
+ }
+ break;
+ case SID_THESAURUS:
+ pTabViewShell->DoThesaurus();
+ break;
+
+ case SID_TOGGLE_REL:
+ pTabViewShell->DoRefConversion();
+ break;
+
+ case SID_DEC_INDENT:
+ pTabViewShell->ChangeIndent( false );
+ break;
+ case SID_INC_INDENT:
+ pTabViewShell->ChangeIndent( true );
+ break;
+
+ case FID_USE_NAME:
+ {
+ CreateNameFlags nFlags = pTabViewShell->GetCreateNameFlags();
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScNameCreateDlg> pDlg(pFact->CreateScNameCreateDlg(pTabViewShell->GetFrameWeld(), nFlags));
+
+ if( pDlg->Execute() )
+ {
+ pTabViewShell->CreateNames(pDlg->GetFlags());
+ rReq.Done();
+ }
+ }
+ break;
+
+ case SID_CONSOLIDATE:
+ {
+ const ScConsolidateItem* pItem;
+ if ( pReqArgs && (pItem =
+ pReqArgs->GetItemIfSet( SCITEM_CONSOLIDATEDATA )) )
+ {
+ const ScConsolidateParam& rParam = pItem->GetData();
+
+ pTabViewShell->Consolidate( rParam );
+ GetViewData().GetDocument().SetConsolidateDlgData( std::unique_ptr<ScConsolidateParam>(new ScConsolidateParam(rParam)) );
+
+ rReq.Done();
+ }
+#if HAVE_FEATURE_SCRIPTING
+ else if (rReq.IsAPI())
+ SbxBase::SetError(ERRCODE_BASIC_BAD_PARAMETER);
+#endif
+ }
+ break;
+
+ case SID_INS_FUNCTION:
+ {
+ const SfxBoolItem* pOkItem = static_cast<const SfxBoolItem*>(&pReqArgs->Get( SID_DLG_RETOK ));
+
+ if ( pOkItem->GetValue() ) // OK
+ {
+ OUString aFormula;
+ const SfxStringItem* pSItem = &pReqArgs->Get( SCITEM_STRING );
+ const SfxBoolItem* pMatrixItem = static_cast<const SfxBoolItem*>(&pReqArgs->Get( SID_DLG_MATRIX ));
+
+ aFormula += pSItem->GetValue();
+ pScMod->ActivateInputWindow( &aFormula, pMatrixItem->GetValue() );
+ }
+ else // CANCEL
+ {
+ pScMod->ActivateInputWindow();
+ }
+ rReq.Ignore(); // only SID_ENTER_STRING is recorded
+ }
+ break;
+
+ case FID_DEFINE_NAME:
+ case FID_DEFINE_CURRENT_NAME:
+ if ( pReqArgs )
+ {
+ const SfxPoolItem* pItem;
+ OUString aName, aSymbol, aAttrib;
+
+ if( pReqArgs->HasItem( FID_DEFINE_NAME, &pItem ) )
+ aName = static_cast<const SfxStringItem*>(pItem)->GetValue();
+
+ if( pReqArgs->HasItem( FN_PARAM_1, &pItem ) )
+ aSymbol = static_cast<const SfxStringItem*>(pItem)->GetValue();
+
+ if( pReqArgs->HasItem( FN_PARAM_2, &pItem ) )
+ aAttrib = static_cast<const SfxStringItem*>(pItem)->GetValue();
+
+ if ( !aName.isEmpty() && !aSymbol.isEmpty() )
+ {
+ if (pTabViewShell->InsertName( aName, aSymbol, aAttrib ))
+ rReq.Done();
+#if HAVE_FEATURE_SCRIPTING
+ else
+ SbxBase::SetError( ERRCODE_BASIC_BAD_PARAMETER ); // Basic-error
+#endif
+ }
+ }
+ else
+ {
+ sal_uInt16 nId = ScNameDlgWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ }
+ break;
+ case FID_ADD_NAME:
+ {
+ sal_uInt16 nId = ScNameDefDlgWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ }
+ break;
+
+ case SID_OPENDLG_CONDFRMT:
+ case SID_OPENDLG_CURRENTCONDFRMT:
+ case SID_OPENDLG_COLORSCALE:
+ case SID_OPENDLG_DATABAR:
+ case SID_OPENDLG_ICONSET:
+ case SID_OPENDLG_CONDDATE:
+ {
+ sal_uInt32 nIndex = sal_uInt32(-1);
+ bool bManaged = false;
+
+ // Get the pool item stored by Conditional Format Manager Dialog.
+ auto itemsRange = pTabViewShell->GetPool().GetItemSurrogates(SCITEM_CONDFORMATDLGDATA);
+ if (itemsRange.begin() != itemsRange.end())
+ {
+ const ScCondFormatDlgItem* pDlgItem = static_cast<const ScCondFormatDlgItem*>(*itemsRange.begin());
+ nIndex = pDlgItem->GetIndex();
+ bManaged = true;
+ }
+
+ // Check if the Conditional Manager Dialog is editing or adding
+ // conditional format item.
+ if ( bManaged )
+ {
+ sal_uInt16 nId = ScCondFormatDlgWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ break;
+ }
+
+ ScRangeList aRangeList;
+ ScViewData& rData = GetViewData();
+ rData.GetMarkData().FillRangeListWithMarks(&aRangeList, false);
+
+ ScDocument& rDoc = GetViewData().GetDocument();
+ if(rDoc.IsTabProtected(rData.GetTabNo()))
+ {
+ pTabViewShell->ErrorMessage( STR_ERR_CONDFORMAT_PROTECTED );
+ break;
+ }
+
+ ScAddress aPos(rData.GetCurX(), rData.GetCurY(), rData.GetTabNo());
+ if(aRangeList.empty())
+ {
+ aRangeList.push_back(ScRange(aPos));
+ }
+
+ // try to find an existing conditional format
+ const ScConditionalFormat* pCondFormat = nullptr;
+ const ScPatternAttr* pPattern = rDoc.GetPattern(aPos.Col(), aPos.Row(), aPos.Tab());
+ ScConditionalFormatList* pList = rDoc.GetCondFormList(aPos.Tab());
+ const ScCondFormatIndexes& rCondFormats = pPattern->GetItem(ATTR_CONDITIONAL).GetCondFormatData();
+ bool bContainsCondFormat = !rCondFormats.empty();
+ bool bCondFormatDlg = false;
+ bool bContainsExistingCondFormat = false;
+ if(bContainsCondFormat)
+ {
+ for (const auto& rCondFormat : rCondFormats)
+ {
+ // check if at least one existing conditional format has the same range
+ pCondFormat = pList->GetFormat(rCondFormat);
+ if(!pCondFormat)
+ continue;
+
+ bContainsExistingCondFormat = true;
+ const ScRangeList& rCondFormatRange = pCondFormat->GetRange();
+ if(rCondFormatRange == aRangeList)
+ {
+ // found a matching range, edit this conditional format
+ bCondFormatDlg = true;
+ nIndex = pCondFormat->GetKey();
+ break;
+ }
+ }
+ }
+
+ // do we have a parameter with the conditional formatting type?
+ const SfxInt16Item* pParam = rReq.GetArg<SfxInt16Item>(FN_PARAM_1);
+ if (pParam)
+ {
+ auto pFormat = std::make_unique<ScConditionalFormat>(0, &rDoc);
+ pFormat->SetRange(aRangeList);
+
+ if (nSlot == SID_OPENDLG_ICONSET)
+ {
+ ScIconSetType eIconSetType = limit_cast<ScIconSetType>(pParam->GetValue(), IconSet_3Arrows, IconSet_5Boxes);
+ const int nSteps = ScIconSetFormat::getIconSetElements(eIconSetType);
+
+ ScIconSetFormat* pEntry = new ScIconSetFormat(&rDoc);
+ ScIconSetFormatData* pIconSetFormatData = new ScIconSetFormatData(eIconSetType);
+
+ pIconSetFormatData->m_Entries.emplace_back(new ScColorScaleEntry(0, COL_RED, COLORSCALE_PERCENT));
+ pIconSetFormatData->m_Entries.emplace_back(new ScColorScaleEntry(round(100. / nSteps), COL_BROWN, COLORSCALE_PERCENT));
+ pIconSetFormatData->m_Entries.emplace_back(new ScColorScaleEntry(round(200. / nSteps), COL_YELLOW, COLORSCALE_PERCENT));
+ if (nSteps > 3)
+ pIconSetFormatData->m_Entries.emplace_back(new ScColorScaleEntry(round(300. / nSteps), COL_WHITE, COLORSCALE_PERCENT));
+ if (nSteps > 4)
+ pIconSetFormatData->m_Entries.emplace_back(new ScColorScaleEntry(round(400. / nSteps), COL_GREEN, COLORSCALE_PERCENT));
+
+ pEntry->SetIconSetData(pIconSetFormatData);
+ pFormat->AddEntry(pEntry);
+ }
+ else if (nSlot == SID_OPENDLG_COLORSCALE)
+ {
+ typedef std::tuple<double, Color, ScColorScaleEntryType> ScaleEntry;
+ static std::vector<std::vector<ScaleEntry>> aScaleThemes =
+ {
+ {
+ { 0, Color(0xF8696B), COLORSCALE_MIN },
+ { 0, Color(0x63BE7B), COLORSCALE_MAX },
+ { 50, Color(0xFFEB84), COLORSCALE_PERCENTILE }
+ },
+ {
+ { 0, Color(0x63BE7B), COLORSCALE_MIN },
+ { 0, Color(0xF8696B), COLORSCALE_MAX },
+ { 50, Color(0xFFEB84), COLORSCALE_PERCENTILE }
+ },
+ {
+ { 0, Color(0xF8696B), COLORSCALE_MIN },
+ { 0, Color(0x63BE7B), COLORSCALE_MAX },
+ { 50, Color(0xFCFCFF), COLORSCALE_PERCENTILE }
+ },
+ {
+ { 0, Color(0x63BE7B), COLORSCALE_MIN },
+ { 0, Color(0xF8696B), COLORSCALE_MAX },
+ { 50, Color(0xFCFCFF), COLORSCALE_PERCENTILE }
+ },
+ {
+ { 0, Color(0xF8696B), COLORSCALE_MIN },
+ { 0, Color(0x5A8AC6), COLORSCALE_MAX },
+ { 50, Color(0xFCFCFF), COLORSCALE_PERCENTILE }
+ },
+ {
+ { 0, Color(0x5A8AC6), COLORSCALE_MIN },
+ { 0, Color(0xF8696B), COLORSCALE_MAX },
+ { 50, Color(0xFCFCFF), COLORSCALE_PERCENTILE }
+ },
+ {
+ { 0, Color(0xF8696B), COLORSCALE_MIN },
+ { 0, Color(0xFCFCFF), COLORSCALE_MAX }
+ },
+ {
+ { 0, Color(0xFCFCFF), COLORSCALE_MIN },
+ { 0, Color(0xF8696B), COLORSCALE_MAX }
+ },
+ {
+ { 0, Color(0x63BE7B), COLORSCALE_MIN },
+ { 0, Color(0xFCFCFF), COLORSCALE_MAX }
+ },
+ {
+ { 0, Color(0xFCFCFF), COLORSCALE_MIN },
+ { 0, Color(0x63BE7B), COLORSCALE_MAX }
+ },
+ {
+ { 0, Color(0x63BE7B), COLORSCALE_MIN },
+ { 0, Color(0xFFEF9C), COLORSCALE_MAX }
+ },
+ {
+ { 0, Color(0xFFEF9C), COLORSCALE_MIN },
+ { 0, Color(0x63BE7B), COLORSCALE_MAX }
+ }
+ };
+
+ sal_uInt16 nTheme = pParam->GetValue();
+ if (nTheme < aScaleThemes.size())
+ {
+ ScColorScaleFormat* pFormatEntry = new ScColorScaleFormat(&rDoc);
+
+ auto& aTheme = aScaleThemes[nTheme];
+
+ ScColorScaleEntry* pMin = new ScColorScaleEntry(std::get<0>(aTheme[0]), std::get<1>(aTheme[0]), std::get<2>(aTheme[0]));
+ ScColorScaleEntry* pMax = new ScColorScaleEntry(std::get<0>(aTheme[1]), std::get<1>(aTheme[1]), std::get<2>(aTheme[1]));
+
+ pFormatEntry->AddEntry(pMin);
+
+ // COLORSCALE_PERCENTILE has to be in the middle
+ if (aTheme.size() > 2)
+ {
+ ScColorScaleEntry* pPer = new ScColorScaleEntry(std::get<0>(aTheme[2]), std::get<1>(aTheme[2]), std::get<2>(aTheme[2]));
+ pFormatEntry->AddEntry(pPer);
+ }
+
+ pFormatEntry->AddEntry(pMax);
+
+ pFormat->AddEntry(pFormatEntry);
+ }
+
+ }
+ else if (nSlot == SID_OPENDLG_DATABAR)
+ {
+ typedef std::tuple<Color, bool> DatabarEntry;
+ static std::vector<DatabarEntry> aDatabarThemes =
+ {
+ { Color(0x638EC6), true },
+ { Color(0x63C384), true },
+ { Color(0xFF555A), true },
+ { Color(0xFFB628), true },
+ { Color(0x008AEF), true },
+ { Color(0xD6007B), true },
+ { Color(0x638EC6), false },
+ { Color(0x63C384), false },
+ { Color(0xFF555A), false },
+ { Color(0xFFB628), false },
+ { Color(0x008AEF), false },
+ { Color(0xD6007B), false }
+ };
+
+ sal_uInt16 nTheme = pParam->GetValue();
+ if (nTheme < aDatabarThemes.size())
+ {
+ ScDataBarFormat* pFormatEntry = new ScDataBarFormat(&rDoc);
+
+ auto& aTheme = aDatabarThemes[nTheme];
+
+ ScDataBarFormatData* pData = new ScDataBarFormatData();
+ pData->maPositiveColor = std::get<0>(aTheme);
+ pData->mbGradient = std::get<1>(aTheme);
+ pData->mxNegativeColor = Color(0xFF0000);
+ pData->mpLowerLimit.reset(new ScColorScaleEntry(0, 0, COLORSCALE_AUTO));
+ pData->mpUpperLimit.reset(new ScColorScaleEntry(0, 0, COLORSCALE_AUTO));
+
+ pFormatEntry->SetDataBarData(pData);
+
+ pFormat->AddEntry(pFormatEntry);
+ }
+ }
+
+ // use the new conditional formatting
+ GetViewData().GetDocShell()->GetDocFunc().ReplaceConditionalFormat(nIndex, std::move(pFormat), aPos.Tab(), aRangeList);
+
+ break;
+ }
+
+ // if not found a conditional format ask whether we should edit one of the existing
+ // or should create a new overlapping conditional format
+ if(bContainsCondFormat && !bCondFormatDlg && bContainsExistingCondFormat)
+ {
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(pTabViewShell->GetFrameWeld(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ ScResId(STR_EDIT_EXISTING_COND_FORMATS)));
+ xQueryBox->set_default_response(RET_YES);
+ bool bEditExisting = xQueryBox->run() == RET_YES;
+ if (bEditExisting)
+ {
+ // differentiate between ranges where one conditional format is defined
+ // and several formats are defined
+ // if we have only one => open the cond format dlg to edit it
+ // otherwise open the manage cond format dlg
+ if (rCondFormats.size() == 1)
+ {
+ pCondFormat = pList->GetFormat(rCondFormats[0]);
+ assert(pCondFormat);
+ nIndex = pCondFormat->GetKey();
+ bCondFormatDlg = true;
+ }
+ else
+ {
+ // Queue message to open Conditional Format Manager Dialog.
+ GetViewData().GetDispatcher().Execute( SID_OPENDLG_CONDFRMT_MANAGER, SfxCallMode::ASYNCHRON );
+ break;
+ }
+ }
+ else
+ {
+ // define an overlapping conditional format
+ pCondFormat = pList->GetFormat(rCondFormats[0]);
+ assert(pCondFormat);
+ bCondFormatDlg = true;
+ }
+ }
+
+ condformat::dialog::ScCondFormatDialogType eType = condformat::dialog::NONE;
+ switch(nSlot)
+ {
+ case SID_OPENDLG_CONDFRMT:
+ case SID_OPENDLG_CURRENTCONDFRMT:
+ eType = condformat::dialog::CONDITION;
+ break;
+ case SID_OPENDLG_COLORSCALE:
+ eType = condformat::dialog::COLORSCALE;
+ break;
+ case SID_OPENDLG_DATABAR:
+ eType = condformat::dialog::DATABAR;
+ break;
+ case SID_OPENDLG_ICONSET:
+ eType = condformat::dialog::ICONSET;
+ break;
+ case SID_OPENDLG_CONDDATE:
+ eType = condformat::dialog::DATE;
+ break;
+ default:
+ assert(false);
+ break;
+ }
+
+
+ if(bCondFormatDlg || !bContainsCondFormat)
+ {
+ // Put the xml string parameter to initialize the
+ // Conditional Format Dialog.
+ ScCondFormatDlgItem aDlgItem(nullptr, nIndex, false);
+ aDlgItem.SetDialogType(eType);
+ pTabViewShell->GetPool().DirectPutItemInPool(aDlgItem);
+
+ sal_uInt16 nId = ScCondFormatDlgWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ }
+ }
+ break;
+
+ case SID_DEFINE_COLROWNAMERANGES:
+ {
+
+ sal_uInt16 nId = ScColRowNameRangesDlgWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+
+ }
+ break;
+
+ case SID_UPDATECHART:
+ {
+ bool bAll = false;
+
+ if( pReqArgs )
+ {
+ const SfxPoolItem* pItem;
+
+ if( pReqArgs->HasItem( SID_UPDATECHART, &pItem ) )
+ bAll = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+ }
+
+ pTabViewShell->UpdateCharts( bAll );
+
+ if( ! rReq.IsAPI() )
+ {
+ rReq.AppendItem( SfxBoolItem( SID_UPDATECHART, bAll ) );
+ rReq.Done();
+ }
+ }
+ break;
+
+ case SID_TABOP:
+ if (pReqArgs)
+ {
+ const ScTabOpItem& rItem =
+ static_cast<const ScTabOpItem&>(
+ pReqArgs->Get( SID_TABOP ));
+
+ pTabViewShell->TabOp( rItem.GetData() );
+
+ rReq.Done( *pReqArgs );
+ }
+ break;
+
+ case SID_SOLVE:
+ if (pReqArgs)
+ {
+ const ScSolveItem& rItem =
+ pReqArgs->Get( SCITEM_SOLVEDATA );
+
+ pTabViewShell->Solve( rItem.GetData() );
+
+ rReq.Done( *pReqArgs );
+ }
+ break;
+
+ case FID_INSERT_NAME:
+ {
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScNamePasteDlg> pDlg(pFact->CreateScNamePasteDlg(pTabViewShell->GetFrameWeld(), GetViewData().GetDocShell()));
+ switch( pDlg->Execute() )
+ {
+ case BTN_PASTE_LIST:
+ pTabViewShell->InsertNameList();
+ break;
+ case BTN_PASTE_NAME:
+ {
+ ScInputHandler* pHdl = pScMod->GetInputHdl( pTabViewShell );
+ if (pHdl)
+ {
+ // "=" in KeyEvent, switches to input-mode
+ (void)pScMod->InputKeyEvent( KeyEvent('=', vcl::KeyCode()) );
+
+ std::vector<OUString> aNames = pDlg->GetSelectedNames();
+ if (!aNames.empty())
+ {
+ OUStringBuffer aBuffer;
+ for (const auto& rName : aNames)
+ {
+ aBuffer.append(rName + " ");
+ }
+ pHdl->InsertFunction( aBuffer.makeStringAndClear(), false ); // without "()"
+ }
+ }
+ }
+ break;
+ }
+ }
+ break;
+
+ case SID_RANGE_NOTETEXT:
+ if (pReqArgs)
+ {
+ const SfxStringItem& rTextItem = pReqArgs->Get( SID_RANGE_NOTETEXT );
+
+ // always cursor position
+ ScAddress aPos( GetViewData().GetCurX(), GetViewData().GetCurY(), GetViewData().GetTabNo() );
+ pTabViewShell->SetNoteText( aPos, rTextItem.GetValue() );
+ rReq.Done();
+ }
+ break;
+
+ case SID_INSERT_POSTIT:
+ case SID_EDIT_POSTIT:
+ {
+ const SvxPostItTextItem* pTextItem;
+ if ( pReqArgs && (pTextItem = pReqArgs->GetItemIfSet( SID_ATTR_POSTIT_TEXT )) )
+ {
+ OUString aCellId;
+ // SID_ATTR_POSTIT_ID only argument for SID_EDIT_POSTIT
+ if (const SvxPostItIdItem* pCellId = pReqArgs->GetItemIfSet( SID_ATTR_POSTIT_ID ))
+ aCellId = pCellId->GetValue();
+
+ const SvxPostItAuthorItem* pAuthorItem = pReqArgs->GetItem( SID_ATTR_POSTIT_AUTHOR );
+ const SvxPostItDateItem* pDateItem = pReqArgs->GetItem( SID_ATTR_POSTIT_DATE );
+
+ if (!aCellId.isEmpty())
+ {
+ SetTabNoAndCursor( GetViewData(), aCellId );
+ }
+
+ ScAddress aPos( GetViewData().GetCurX(), GetViewData().GetCurY(), GetViewData().GetTabNo() );
+ pTabViewShell->ReplaceNote( aPos, pTextItem->GetValue(),
+ pAuthorItem ? &pAuthorItem->GetValue() : nullptr,
+ pDateItem ? &pDateItem->GetValue() : nullptr );
+ }
+ else if (!comphelper::LibreOfficeKit::isActive() || comphelper::LibreOfficeKit::isTiledAnnotations())
+ {
+ pTabViewShell->EditNote(); // note object to edit
+ }
+ rReq.Done();
+ }
+ break;
+
+ case FID_NOTE_VISIBLE:
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScAddress aPos( GetViewData().GetCurX(), GetViewData().GetCurY(), GetViewData().GetTabNo() );
+ if( ScPostIt* pNote = rDoc.GetNote(aPos) )
+ {
+ bool bShow;
+ const SfxPoolItem* pItem;
+ if ( pReqArgs && (pReqArgs->GetItemState( FID_NOTE_VISIBLE, true, &pItem ) == SfxItemState::SET) )
+ bShow = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+ else
+ bShow = !pNote->IsCaptionShown();
+
+ pTabViewShell->ShowNote( bShow );
+
+ if (!pReqArgs)
+ rReq.AppendItem( SfxBoolItem( FID_NOTE_VISIBLE, bShow ) );
+
+ rReq.Done();
+ rBindings.Invalidate( FID_NOTE_VISIBLE );
+ }
+ else
+ rReq.Ignore();
+ }
+ break;
+
+ case FID_HIDE_NOTE:
+ case FID_SHOW_NOTE:
+ {
+ bool bShowNote = nSlot == FID_SHOW_NOTE;
+ ScViewData& rData = GetViewData();
+ ScDocument& rDoc = rData.GetDocument();
+ ScMarkData& rMark = rData.GetMarkData();
+
+ if (!rMark.IsMarked() && !rMark.IsMultiMarked())
+ {
+ // Check current cell
+ ScAddress aPos( rData.GetCurX(), rData.GetCurY(), rData.GetTabNo() );
+ if( rDoc.GetNote(aPos) )
+ {
+ rData.GetDocShell()->GetDocFunc().ShowNote( aPos, bShowNote );
+ }
+ }
+ else
+ {
+ // Check selection range
+ bool bDone = false;
+ ScRangeListRef aRangesRef;
+ rData.GetMultiArea(aRangesRef);
+ const ScRangeList aRanges = *aRangesRef;
+
+ OUString aUndo = ScResId( bShowNote ? STR_UNDO_SHOWNOTE : STR_UNDO_HIDENOTE );
+ rData.GetDocShell()->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, rData.GetViewShell()->GetViewShellId() );
+
+ for (auto const& rTab : rMark.GetSelectedTabs())
+ {
+ // get notes
+ std::vector<sc::NoteEntry> aNotes;
+ rDoc.GetAllNoteEntries(rTab, aNotes);
+
+ for (const sc::NoteEntry& rNote : aNotes)
+ {
+ // check if note is in our selection range
+ const ScAddress& rAdr = rNote.maPos;
+ const ScRange* rRange = aRanges.Find(rAdr);
+ if (! rRange)
+ continue;
+
+ // check if cell is editable
+ const SCTAB nRangeTab = rRange->aStart.Tab();
+ if (rDoc.IsBlockEditable( nRangeTab, rAdr.Col(), rAdr.Row(), rAdr.Col(), rAdr.Row() ))
+ {
+ rData.GetDocShell()->GetDocFunc().ShowNote( rAdr, bShowNote );
+ bDone = true;
+ }
+ }
+ }
+
+ rData.GetDocShell()->GetUndoManager()->LeaveListAction();
+
+ if ( bDone )
+ {
+ rReq.Done();
+ rBindings.Invalidate( nSlot );
+ }
+ else
+ rReq.Ignore();
+ }
+
+ }
+ break;
+
+ case FID_SHOW_ALL_NOTES:
+ case FID_HIDE_ALL_NOTES:
+ {
+ bool bShowNote = nSlot == FID_SHOW_ALL_NOTES;
+ ScViewData& rData = GetViewData();
+ ScMarkData& rMark = rData.GetMarkData();
+ ScDocument& rDoc = rData.GetDocument();
+ std::vector<sc::NoteEntry> aNotes;
+
+ OUString aUndo = ScResId( bShowNote ? STR_UNDO_SHOWALLNOTES : STR_UNDO_HIDEALLNOTES );
+ rData.GetDocShell()->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, rData.GetViewShell()->GetViewShellId() );
+
+ for (auto const& rTab : rMark.GetSelectedTabs())
+ {
+ rDoc.GetAllNoteEntries(rTab, aNotes);
+ }
+
+ for (const sc::NoteEntry& rNote : aNotes)
+ {
+ const ScAddress& rAdr = rNote.maPos;
+ rData.GetDocShell()->GetDocFunc().ShowNote( rAdr, bShowNote );
+ }
+
+ rData.GetDocShell()->GetUndoManager()->LeaveListAction();
+ }
+ break;
+
+ case SID_TOGGLE_NOTES:
+ {
+ ScViewData& rData = GetViewData();
+ ScMarkData& rMark = rData.GetMarkData();
+ ScDocument& rDoc = rData.GetDocument();
+ ScRangeList aRanges;
+ std::vector<sc::NoteEntry> aNotes;
+
+ for (auto const& rTab : rMark.GetSelectedTabs())
+ aRanges.push_back(ScRange(0,0,rTab,rDoc.MaxCol(),rDoc.MaxRow(),rTab));
+
+ CommentCaptionState eState = rDoc.GetAllNoteCaptionsState( aRanges );
+ rDoc.GetNotesInRange(aRanges, aNotes);
+ bool bShowNote = (eState == ALLHIDDEN || eState == MIXED);
+
+ OUString aUndo = ScResId( bShowNote ? STR_UNDO_SHOWALLNOTES : STR_UNDO_HIDEALLNOTES );
+ rData.GetDocShell()->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, rData.GetViewShell()->GetViewShellId() );
+
+ for(const auto& rNote : aNotes)
+ {
+ const ScAddress& rAdr = rNote.maPos;
+ rData.GetDocShell()->GetDocFunc().ShowNote( rAdr, bShowNote );
+ }
+
+ rData.GetDocShell()->GetUndoManager()->LeaveListAction();
+
+ if (!pReqArgs)
+ rReq.AppendItem( SfxBoolItem( SID_TOGGLE_NOTES, bShowNote ) );
+
+ rReq.Done();
+ rBindings.Invalidate( SID_TOGGLE_NOTES );
+ }
+ break;
+
+ case SID_DELETE_NOTE:
+ {
+ const SvxPostItIdItem* pIdItem;
+ // If Id is mentioned, select the appropriate cell first
+ if ( pReqArgs && (pIdItem = pReqArgs->GetItemIfSet( SID_ATTR_POSTIT_ID )) )
+ {
+ const OUString& aCellId = pIdItem->GetValue();
+ if (!aCellId.isEmpty())
+ {
+ SetTabNoAndCursor( GetViewData(), aCellId );
+ }
+ }
+
+ pTabViewShell->DeleteContents( InsertDeleteFlags::NOTE ); // delete all notes in selection
+ rReq.Done();
+ }
+ break;
+
+ case FID_DELETE_ALL_NOTES:
+ {
+ ScViewData& rData = GetViewData();
+ ScMarkData& rMark = rData.GetMarkData();
+ ScDocument& rDoc = rData.GetDocument();
+ ScMarkData aNewMark(rDoc.GetSheetLimits());
+ ScRangeList aRangeList;
+
+ for (auto const& rTab : rMark.GetSelectedTabs())
+ {
+ aRangeList.push_back(ScRange(0,0,rTab,rDoc.MaxCol(),rDoc.MaxRow(),rTab));
+ }
+
+ aNewMark.MarkFromRangeList( aRangeList, true );
+ rData.GetDocShell()->GetDocFunc().DeleteContents(aNewMark, InsertDeleteFlags::NOTE, true, false );
+ }
+ break;
+
+ case SID_CHARMAP:
+ if( pReqArgs != nullptr )
+ {
+ OUString aChars, aFontName;
+ const SfxItemSet *pArgs = rReq.GetArgs();
+ const SfxPoolItem* pItem = nullptr;
+ if ( pArgs )
+ pArgs->GetItemState(SID_CHARMAP, false, &pItem);
+ if ( pItem )
+ {
+ const SfxStringItem* pStringItem = dynamic_cast<const SfxStringItem*>( pItem );
+ if ( pStringItem )
+ aChars = pStringItem->GetValue();
+ const SfxStringItem* pFontItem =
+ pArgs->GetItemIfSet( SID_ATTR_SPECIALCHAR, false);
+ if ( pFontItem )
+ aFontName = pFontItem->GetValue();
+ }
+
+ if ( !aChars.isEmpty() )
+ {
+ vcl::Font aFont;
+ pTabViewShell->GetSelectionPattern()->fillFontOnly(aFont, nullptr, nullptr, nullptr,
+ pTabViewShell->GetSelectionScriptType() );
+ if ( !aFontName.isEmpty() )
+ aFont = vcl::Font( aFontName, Size(1,1) );
+ pTabViewShell->InsertSpecialChar( aChars, aFont );
+ if( ! rReq.IsAPI() )
+ rReq.Done();
+ }
+ }
+ else
+ {
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+
+ // font color doesn't matter here
+ vcl::Font aCurFont;
+ pTabViewShell->GetSelectionPattern()->fillFontOnly(aCurFont, nullptr, nullptr, nullptr,
+ pTabViewShell->GetSelectionScriptType());
+
+ SfxAllItemSet aSet( GetPool() );
+ aSet.Put( SfxBoolItem( FN_PARAM_1, false ) );
+ aSet.Put( SvxFontItem( aCurFont.GetFamilyType(), aCurFont.GetFamilyName(), aCurFont.GetStyleName(), aCurFont.GetPitch(), aCurFont.GetCharSet(), GetPool().GetWhich(SID_ATTR_CHAR_FONT) ) );
+ SfxViewFrame& rViewFrame = pTabViewShell->GetViewFrame();
+ auto xFrame = rViewFrame.GetFrame().GetFrameInterface();
+ ScopedVclPtr<SfxAbstractDialog> pDlg(pFact->CreateCharMapDialog(pTabViewShell->GetFrameWeld(), aSet, xFrame));
+ pDlg->Execute();
+ }
+ break;
+
+ case SID_SELECT_SCENARIO:
+ {
+ // Testing
+
+ if ( pReqArgs )
+ {
+ const SfxStringItem& rItem = pReqArgs->Get(SID_SELECT_SCENARIO);
+ pTabViewShell->UseScenario(rItem.GetValue());
+ //! why should the return value be valid?!?!
+ rReq.SetReturnValue(SfxStringItem(SID_SELECT_SCENARIO, rItem.GetValue()));
+ rReq.Done();
+ }
+ }
+ break;
+
+ case SID_HYPERLINK_SETLINK:
+ if( pReqArgs )
+ {
+ const SfxPoolItem* pItem;
+ if( pReqArgs->HasItem( SID_HYPERLINK_SETLINK, &pItem ) )
+ {
+ const SvxHyperlinkItem* pHyper = static_cast<const SvxHyperlinkItem*>(pItem);
+ const OUString& rName = pHyper->GetName();
+ const OUString& rURL = pHyper->GetURL();
+ const OUString& rTarget = pHyper->GetTargetFrame();
+ sal_uInt16 nType = static_cast<sal_uInt16>(pHyper->GetInsertMode());
+
+ pTabViewShell->InsertURL( rName, rURL, rTarget, nType );
+ rReq.Done();
+ }
+ else
+ rReq.Ignore();
+ }
+ break;
+
+ case SID_OPENDLG_CONDFRMT_MANAGER:
+ case SID_OPENDLG_CURRENTCONDFRMT_MANAGER:
+ {
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScViewData& rData = GetViewData();
+ ScDocument& rDoc = rData.GetDocument();
+
+ if (rDoc.IsTabProtected(rData.GetTabNo()))
+ {
+ pTabViewShell->ErrorMessage( STR_ERR_CONDFORMAT_PROTECTED );
+ break;
+ }
+
+ ScAddress aPos(rData.GetCurX(), rData.GetCurY(), rData.GetTabNo());
+
+ ScConditionalFormatList* pList = nullptr;
+
+ const ScCondFormatDlgItem* pDlgItem = nullptr;
+ auto itemsRange = pTabViewShell->GetPool().GetItemSurrogates(SCITEM_CONDFORMATDLGDATA);
+ if (itemsRange.begin() != itemsRange.end())
+ {
+ pDlgItem= static_cast<const ScCondFormatDlgItem*>(*itemsRange.begin());
+ pList = const_cast<ScCondFormatDlgItem*>(pDlgItem)->GetConditionalFormatList();
+ }
+
+ if (!pList)
+ pList = rDoc.GetCondFormList( aPos.Tab() );
+
+ VclPtr<AbstractScCondFormatManagerDlg> pDlg(pFact->CreateScCondFormatMgrDlg(
+ pTabViewShell->GetFrameWeld(), rDoc, pList));
+
+ if (pDlgItem)
+ pDlg->SetModified();
+
+ pDlg->StartExecuteAsync([this, pDlg, &rData, pTabViewShell, pDlgItem, aPos](sal_Int32 nRet){
+ std::unique_ptr<ScConditionalFormatList> pCondFormatList = pDlg->GetConditionalFormatList();
+ if(nRet == RET_OK && pDlg->CondFormatsChanged())
+ {
+ rData.GetDocShell()->GetDocFunc().SetConditionalFormatList(pCondFormatList.release(), aPos.Tab());
+ }
+ else if(nRet == DLG_RET_ADD)
+ {
+ // Put the xml string parameter to initialize the
+ // Conditional Format Dialog. ( add new )
+ pTabViewShell->GetPool().DirectPutItemInPool(ScCondFormatDlgItem(
+ std::shared_ptr<ScConditionalFormatList>(pCondFormatList.release()), -1, true));
+ // Queue message to open Conditional Format Dialog
+ GetViewData().GetDispatcher().Execute( SID_OPENDLG_CONDFRMT, SfxCallMode::ASYNCHRON );
+ }
+ else if (nRet == DLG_RET_EDIT)
+ {
+ ScConditionalFormat* pFormat = pDlg->GetCondFormatSelected();
+ sal_Int32 nIndex = pFormat ? pFormat->GetKey() : -1;
+ // Put the xml string parameter to initialize the
+ // Conditional Format Dialog. ( edit selected conditional format )
+ pTabViewShell->GetPool().DirectPutItemInPool(ScCondFormatDlgItem(
+ std::shared_ptr<ScConditionalFormatList>(pCondFormatList.release()), nIndex, true));
+
+ // Queue message to open Conditional Format Dialog
+ GetViewData().GetDispatcher().Execute( SID_OPENDLG_CONDFRMT, SfxCallMode::ASYNCHRON );
+ }
+ else
+ pCondFormatList.reset();
+
+ if (pDlgItem)
+ pTabViewShell->GetPool().DirectRemoveItemFromPool(*pDlgItem);
+
+ pDlg->disposeOnce();
+ });
+ }
+ break;
+
+ case SID_EXTERNAL_SOURCE:
+ {
+ const SfxStringItem* pFile = rReq.GetArg<SfxStringItem>(SID_FILE_NAME);
+ const SfxStringItem* pSource = rReq.GetArg<SfxStringItem>(FN_PARAM_1);
+ if ( pFile && pSource )
+ {
+ OUString aFile;
+ OUString aFilter;
+ OUString aOptions;
+ OUString aSource;
+ sal_Int32 nRefreshDelaySeconds=0;
+
+ aFile = pFile->GetValue();
+ aSource = pSource->GetValue();
+ const SfxStringItem* pFilter = rReq.GetArg<SfxStringItem>(SID_FILTER_NAME);
+ if ( pFilter )
+ aFilter = pFilter->GetValue();
+ const SfxStringItem* pOptions = rReq.GetArg<SfxStringItem>(SID_FILE_FILTEROPTIONS);
+ if ( pOptions )
+ aOptions = pOptions->GetValue();
+ const SfxUInt32Item* pRefresh = rReq.GetArg<SfxUInt32Item>(FN_PARAM_2);
+ if ( pRefresh )
+ nRefreshDelaySeconds = pRefresh->GetValue();
+
+ ExecuteExternalSource( aFile, aFilter, aOptions, aSource, nRefreshDelaySeconds, rReq );
+ }
+ else
+ {
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ pImpl->m_pLinkedDlg.disposeAndClear();
+ pImpl->m_pLinkedDlg =
+ pFact->CreateScLinkedAreaDlg(pTabViewShell->GetFrameWeld());
+ delete pImpl->m_pRequest;
+ pImpl->m_pRequest = new SfxRequest( rReq );
+ OUString sFile, sFilter, sOptions, sSource;
+ sal_Int32 nRefreshDelaySeconds = 0;
+ if (pImpl->m_pLinkedDlg->Execute() == RET_OK)
+ {
+ sFile = pImpl->m_pLinkedDlg->GetURL();
+ sFilter = pImpl->m_pLinkedDlg->GetFilter();
+ sOptions = pImpl->m_pLinkedDlg->GetOptions();
+ sSource = pImpl->m_pLinkedDlg->GetSource();
+ nRefreshDelaySeconds = pImpl->m_pLinkedDlg->GetRefreshDelaySeconds();
+ if ( !sFile.isEmpty() )
+ pImpl->m_pRequest->AppendItem( SfxStringItem( SID_FILE_NAME, sFile ) );
+ if ( !sFilter.isEmpty() )
+ pImpl->m_pRequest->AppendItem( SfxStringItem( SID_FILTER_NAME, sFilter ) );
+ if ( !sOptions.isEmpty() )
+ pImpl->m_pRequest->AppendItem( SfxStringItem( SID_FILE_FILTEROPTIONS, sOptions ) );
+ if ( !sSource.isEmpty() )
+ pImpl->m_pRequest->AppendItem( SfxStringItem( FN_PARAM_1, sSource ) );
+ if ( nRefreshDelaySeconds )
+ pImpl->m_pRequest->AppendItem( SfxUInt32Item( FN_PARAM_2, nRefreshDelaySeconds ) );
+ }
+
+ ExecuteExternalSource( sFile, sFilter, sOptions, sSource, nRefreshDelaySeconds, *(pImpl->m_pRequest) );
+ }
+ }
+ break;
+
+ case SID_AUTO_SUM:
+ {
+ const SfxItemSet *pArgs = rReq.GetArgs();
+ const OUString sFunction = pArgs ?
+ static_cast<const SfxStringItem&>( pArgs->Get( SID_AUTO_SUM ) ).GetValue()
+ : "";
+
+ OpCode eFunction = ocSum;
+ if (sFunction == "average")
+ eFunction = ocAverage;
+ else if (sFunction == "count")
+ eFunction = ocCount;
+ else if (sFunction == "min")
+ eFunction = ocMin;
+ if (sFunction == "max")
+ eFunction = ocMax;
+
+ bool bSubTotal = false;
+ bool bRangeFinder = false;
+ const OUString aFormula = pTabViewShell->DoAutoSum( bRangeFinder, bSubTotal , eFunction );
+ if ( !aFormula.isEmpty() )
+ {
+ const sal_Int32 nPar = aFormula.indexOf( '(' );
+ const sal_Int32 nLen = aFormula.getLength();
+ ScInputHandler* pHdl = pScMod->GetInputHdl( pTabViewShell );
+
+ if ( pHdl && nPar != -1 )
+ {
+ if ( !pScMod->IsEditMode() )
+ {
+ pScMod->SetInputMode( SC_INPUT_TABLE );
+ }
+
+ EditView *pEditView=pHdl->GetActiveView();
+ if ( pEditView )
+ {
+ ESelection aTextSel = pEditView->GetSelection();
+ aTextSel.nStartPos = 0;
+ aTextSel.nEndPos = EE_TEXTPOS_ALL;
+ pHdl->DataChanging();
+ pEditView->SetSelection(aTextSel);
+ pEditView->InsertText(aFormula);
+ pEditView->SetSelection( bRangeFinder ? ESelection( 0, nPar + ( bSubTotal ? 3 : 1 ), 0, nLen - 1 ) : ESelection( 0, nLen - 1, 0, nLen - 1 ) );
+ pHdl->DataChanged();
+
+ if ( bRangeFinder )
+ {
+ pHdl->InitRangeFinder( aFormula );
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case SID_SELECT_UNPROTECTED_CELLS:
+ {
+ ScViewData& rData = GetViewData();
+ SCTAB aTab = rData.GetTabNo();
+ ScMarkData& rMark = rData.GetMarkData();
+ ScDocument& rDoc = rData.GetDocument();
+ ScRangeList rRangeList;
+
+ rDoc.GetUnprotectedCells(rRangeList, aTab);
+ rMark.MarkFromRangeList(rRangeList, true);
+ pTabViewShell->SetMarkData(rMark);
+ }
+ break;
+
+ case SID_SELECT_VISIBLE_ROWS:
+ {
+ ScViewData& rData = GetViewData();
+ ScMarkData& rMark = rData.GetMarkData();
+ ScDocument& rDoc = rData.GetDocument();
+
+ rMark.MarkToMulti();
+
+ const ScRange& aMultiArea = rMark.GetMultiMarkArea();
+ SCCOL nStartCol = aMultiArea.aStart.Col();
+ SCROW nStartRow = aMultiArea.aStart.Row();
+ SCCOL nEndCol = aMultiArea.aEnd.Col();
+ SCROW nEndRow = aMultiArea.aEnd.Row();
+
+ bool bChanged = false;
+ for (const SCTAB& nTab : rMark)
+ {
+ for (SCROW nRow = nStartRow; nRow <= nEndRow; ++nRow)
+ {
+ SCROW nLastRow = nRow;
+ if (rDoc.RowHidden(nRow, nTab, nullptr, &nLastRow))
+ {
+ rMark.SetMultiMarkArea(
+ ScRange(nStartCol, nRow, nTab, nEndCol, nLastRow, nTab), false);
+ bChanged = true;
+ nRow = nLastRow;
+ }
+ }
+ }
+
+ if (bChanged && !rMark.HasAnyMultiMarks())
+ rMark.ResetMark();
+
+ rMark.MarkToSimple();
+
+ pTabViewShell->SelectionChanged();
+ }
+ break;
+
+ case SID_SELECT_VISIBLE_COLUMNS:
+ {
+ ScViewData& rData = GetViewData();
+ ScMarkData& rMark = rData.GetMarkData();
+ ScDocument& rDoc = rData.GetDocument();
+
+ rMark.MarkToMulti();
+
+ const ScRange& aMultiArea = rMark.GetMultiMarkArea();
+ SCCOL nStartCol = aMultiArea.aStart.Col();
+ SCROW nStartRow = aMultiArea.aStart.Row();
+ SCCOL nEndCol = aMultiArea.aEnd.Col();
+ SCROW nEndRow = aMultiArea.aEnd.Row();
+
+ bool bChanged = false;
+ for (const SCTAB& nTab : rMark)
+ {
+ for (SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol)
+ {
+ SCCOL nLastCol = nCol;
+ if (rDoc.ColHidden(nCol, nTab, nullptr, &nLastCol))
+ {
+ rMark.SetMultiMarkArea(
+ ScRange(nCol, nStartRow, nTab, nLastCol, nEndRow, nTab), false);
+ bChanged = true;
+ nCol = nLastCol;
+ }
+ }
+ }
+
+ if (bChanged && !rMark.HasAnyMultiMarks())
+ rMark.ResetMark();
+
+ rMark.MarkToSimple();
+
+ pTabViewShell->SelectionChanged();
+ }
+ break;
+
+ case SID_CURRENT_FORMULA_RANGE:
+ {
+ const SfxInt32Item* param1 = rReq.GetArg<SfxInt32Item>(FN_PARAM_1);
+ SCCOL colStart = param1 ? param1->GetValue() : 0;
+
+ const SfxInt32Item* param2 = rReq.GetArg<SfxInt32Item>(FN_PARAM_2);
+ SCROW rowStart = param2 ? param2->GetValue() : 0;
+
+ const SfxInt32Item* param3 = rReq.GetArg<SfxInt32Item>(FN_PARAM_3);
+ SCCOL colEnd = param3 ? param3->GetValue() : 0;
+
+ const SfxInt32Item* param4 = rReq.GetArg<SfxInt32Item>(FN_PARAM_4);
+ SCROW rowEnd = param4 ? param4->GetValue() : 0;
+
+ const SfxInt32Item* param5 = rReq.GetArg<SfxInt32Item>(FN_PARAM_5);
+ SCROW table = param5 ? param5->GetValue() : 0;
+
+ ScInputHandler* pInputHdl = SC_MOD()->GetInputHdl();
+
+ if (param3 && param4 && pInputHdl)
+ {
+ ScViewData& rData = pTabViewShell->GetViewData();
+ ScTabView* pTabView = rData.GetView();
+
+ if (param1 && param2)
+ rData.SetRefStart(colStart, rowStart, table);
+
+ pTabView->UpdateRef( colEnd, rowEnd, table ); // setup the end & refresh formula
+
+ ScRange aRef(
+ colStart, rowStart, rData.GetRefStartZ(),
+ colEnd, rowEnd, rData.GetRefEndZ() );
+ SC_MOD()->SetReference( aRef, rData.GetDocument(), &rData.GetMarkData() );
+
+ pInputHdl->UpdateLokReferenceMarks();
+ }
+ }
+ break;
+
+ default:
+ OSL_FAIL("incorrect slot in ExecuteEdit");
+ break;
+ }
+}
+
+void ScCellShell::ExecuteTrans( SfxRequest& rReq )
+{
+ TransliterationFlags nType = ScViewUtil::GetTransliterationType( rReq.GetSlot() );
+ if ( nType != TransliterationFlags::NONE )
+ {
+ GetViewData().GetView()->TransliterateText( nType );
+ rReq.Done();
+ }
+}
+
+void ScCellShell::ExecuteRotateTrans( const SfxRequest& rReq )
+{
+ if( rReq.GetSlot() == SID_TRANSLITERATE_ROTATE_CASE )
+ GetViewData().GetView()->TransliterateText( m_aRotateCase.getNextMode() );
+}
+
+void ScCellShell::ExecuteExternalSource(
+ const OUString& _rFile, const OUString& _rFilter, const OUString& _rOptions,
+ const OUString& _rSource, sal_Int32 _nRefreshDelaySeconds, SfxRequest& _rRequest )
+{
+ if ( !_rFile.isEmpty() && !_rSource.isEmpty() ) // filter may be empty
+ {
+ ScRange aLinkRange;
+ bool bMove = false;
+
+ ScViewData& rData = GetViewData();
+ ScMarkData& rMark = rData.GetMarkData();
+ rMark.MarkToSimple();
+ if ( rMark.IsMarked() )
+ {
+ aLinkRange = rMark.GetMarkArea();
+ bMove = true; // insert/delete cells to fit range
+ }
+ else
+ aLinkRange = ScRange( rData.GetCurX(), rData.GetCurY(), rData.GetTabNo() );
+
+ rData.GetDocFunc().InsertAreaLink( _rFile, _rFilter, _rOptions, _rSource,
+ aLinkRange, _nRefreshDelaySeconds, bMove, false );
+ _rRequest.Done();
+ }
+ else
+ _rRequest.Ignore();
+}
+
+namespace {
+
+bool isDPSourceValid(const ScDPObject& rDPObj)
+{
+ if (rDPObj.IsImportData())
+ {
+ // If the data type is database, check if the database is still valid.
+ const ScImportSourceDesc* pDesc = rDPObj.GetImportSourceDesc();
+ if (!pDesc)
+ return false;
+
+ const ScDPSaveData* pSaveData = rDPObj.GetSaveData();
+ const ScDPDimensionSaveData* pDimData = nullptr;
+ if (pSaveData)
+ pDimData = pSaveData->GetExistingDimensionData();
+
+ const ScDPCache* pCache = pDesc->CreateCache(pDimData);
+ if (!pCache)
+ // cache creation failed, probably due to invalid connection.
+ return false;
+ }
+ return true;
+}
+
+void RunPivotLayoutDialog(ScModule* pScMod,
+ ScTabViewShell* pTabViewShell,
+ std::unique_ptr<ScDPObject>& pNewDPObject)
+{
+ bool bHadNewDPObject = pNewDPObject != nullptr;
+ pTabViewShell->SetDialogDPObject( std::move(pNewDPObject) );
+ if ( bHadNewDPObject )
+ {
+ // start layout dialog
+
+ sal_uInt16 nId = ScPivotLayoutWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ }
+}
+
+void SetupRangeForPivotTableDialog(const ScRange& rRange,
+ ScAddress& rDestPos,
+ ScDocument* pDoc,
+ TranslateId pSrcErrorId,
+ std::unique_ptr<ScDPObject>& pNewDPObject)
+{
+ ScSheetSourceDesc aShtDesc(pDoc);
+ aShtDesc.SetSourceRange(rRange);
+ pSrcErrorId = aShtDesc.CheckSourceRange();
+ if (!pSrcErrorId)
+ {
+ pNewDPObject.reset(new ScDPObject(pDoc));
+ pNewDPObject->SetSheetDesc( aShtDesc );
+ }
+
+ // output below source data
+ if ( rRange.aEnd.Row()+2 <= pDoc->MaxRow() - 4 )
+ rDestPos = ScAddress( rRange.aStart.Col(),
+ rRange.aEnd.Row()+2,
+ rRange.aStart.Tab() );
+}
+
+void ErrorOrRunPivotLayoutDialog(TranslateId pSrcErrorId,
+ const ScAddress& rDestPos,
+ ScModule* pScMod,
+ ScTabViewShell* pTabViewShell,
+ std::unique_ptr<ScDPObject>& pNewDPObject)
+{
+ if (pSrcErrorId)
+ {
+ // Error occurred during data creation. Launch an error and bail out.
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(pTabViewShell->GetFrameWeld(),
+ VclMessageType::Info, VclButtonsType::Ok,
+ ScResId(pSrcErrorId)));
+ xInfoBox->run();
+ return;
+ }
+
+ if ( pNewDPObject )
+ pNewDPObject->SetOutRange( rDestPos );
+
+ RunPivotLayoutDialog(pScMod, pTabViewShell, pNewDPObject);
+}
+
+}
+
+void ScCellShell::ExecuteDataPilotDialog()
+{
+ ScModule* pScMod = SC_MOD();
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ ScViewData& rData = GetViewData();
+ ScDocument& rDoc = rData.GetDocument();
+
+ // ScPivot is no longer used...
+ ScDPObject* pDPObj = rDoc.GetDPAtCursor(
+ rData.GetCurX(), rData.GetCurY(),
+ rData.GetTabNo() );
+ if ( pDPObj ) // on an existing table?
+ {
+ std::unique_ptr<ScDPObject> pNewDPObject;
+
+ if (isDPSourceValid(*pDPObj))
+ pNewDPObject.reset(new ScDPObject(*pDPObj));
+
+ RunPivotLayoutDialog(pScMod, pTabViewShell, pNewDPObject);
+ }
+ else // create new table
+ {
+ // select database range or data
+ pTabViewShell->GetDBData( true, SC_DB_OLD );
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ if ( !rMark.IsMarked() && !rMark.IsMultiMarked() )
+ pTabViewShell->MarkDataArea( false );
+
+ // output to cursor position for non-sheet data
+ ScAddress aDestPos( rData.GetCurX(), rData.GetCurY(),
+ rData.GetTabNo() );
+
+ // first select type of source data
+
+ bool bEnableExt = ScDPObject::HasRegisteredSources();
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ VclPtr<AbstractScDataPilotSourceTypeDlg> pTypeDlg(
+ pFact->CreateScDataPilotSourceTypeDlg(
+ pTabViewShell->GetFrameWeld(), bEnableExt));
+
+ // Populate named ranges (if any).
+ ScRangeName* pRangeName = rDoc.GetRangeName();
+ if (pRangeName)
+ {
+ ScRangeName::const_iterator itr = pRangeName->begin(), itrEnd = pRangeName->end();
+ for (; itr != itrEnd; ++itr)
+ pTypeDlg->AppendNamedRange(itr->second->GetName());
+ }
+
+ pTypeDlg->StartExecuteAsync([this, pTypeDlg, pTabViewShell,
+ pScMod, pFact, &rDoc, &rMark, aDestPos](int nResult) mutable {
+
+ if (nResult == RET_OK )
+ {
+ if ( pTypeDlg->IsExternal() )
+ {
+ std::vector<OUString> aSources = ScDPObject::GetRegisteredSources();
+ VclPtr<AbstractScDataPilotServiceDlg> pServDlg(
+ pFact->CreateScDataPilotServiceDlg(
+ pTabViewShell->GetFrameWeld(), aSources));
+
+ pServDlg->StartExecuteAsync([pServDlg, pScMod, pTabViewShell,
+ aDestPos, &rDoc](int nResult2) mutable {
+ if ( nResult2 == RET_OK )
+ {
+ ScDPServiceDesc aServDesc(
+ pServDlg->GetServiceName(),
+ pServDlg->GetParSource(),
+ pServDlg->GetParName(),
+ pServDlg->GetParUser(),
+ pServDlg->GetParPass() );
+ std::unique_ptr<ScDPObject> pNewDPObject(new ScDPObject(&rDoc));
+ pNewDPObject->SetServiceData( aServDesc );
+ pNewDPObject->SetOutRange(aDestPos);
+
+ RunPivotLayoutDialog(pScMod, pTabViewShell, pNewDPObject);
+ }
+
+ pServDlg->disposeOnce();
+ });
+ }
+ else if ( pTypeDlg->IsDatabase() )
+ {
+ assert(pFact && "ScAbstractFactory create fail!");
+ VclPtr<AbstractScDataPilotDatabaseDlg> pDataDlg(
+ pFact->CreateScDataPilotDatabaseDlg(pTabViewShell->GetFrameWeld()));
+ assert(pDataDlg && "Dialog create fail!");
+
+ pDataDlg->StartExecuteAsync([pDataDlg, pScMod, pTabViewShell,
+ aDestPos, &rDoc](int nResult2) mutable {
+ if ( nResult2 == RET_OK )
+ {
+ ScImportSourceDesc aImpDesc(&rDoc);
+ pDataDlg->GetValues( aImpDesc );
+ std::unique_ptr<ScDPObject> pNewDPObject(new ScDPObject(&rDoc));
+ pNewDPObject->SetImportDesc( aImpDesc );
+ pNewDPObject->SetOutRange(aDestPos);
+
+ RunPivotLayoutDialog(pScMod, pTabViewShell, pNewDPObject);
+ }
+
+ pDataDlg->disposeOnce();
+ });
+ }
+ else
+ {
+ TranslateId pSrcErrorId;
+
+ if (pTypeDlg->IsNamedRange())
+ {
+ std::unique_ptr<ScDPObject> pNewDPObject;
+ OUString aName = pTypeDlg->GetSelectedNamedRange();
+ ScSheetSourceDesc aShtDesc(&rDoc);
+ aShtDesc.SetRangeName(aName);
+ pSrcErrorId = aShtDesc.CheckSourceRange();
+ if (!pSrcErrorId)
+ {
+ pNewDPObject.reset(new ScDPObject(&rDoc));
+ pNewDPObject->SetSheetDesc(aShtDesc);
+ }
+
+ ErrorOrRunPivotLayoutDialog(pSrcErrorId, aDestPos, pScMod, pTabViewShell, pNewDPObject);
+ }
+ else // selection
+ {
+ //! use database ranges (select before type dialog?)
+ ScRange aRange;
+ ScMarkType eType = GetViewData().GetSimpleArea(aRange);
+ if ( (eType & SC_MARK_SIMPLE) == SC_MARK_SIMPLE )
+ {
+ ScDocument* pDoc = &rDoc;
+
+ // Shrink the range to the data area.
+ SCCOL nStartCol = aRange.aStart.Col(), nEndCol = aRange.aEnd.Col();
+ SCROW nStartRow = aRange.aStart.Row(), nEndRow = aRange.aEnd.Row();
+ if (rDoc.ShrinkToDataArea(aRange.aStart.Tab(), nStartCol, nStartRow, nEndCol, nEndRow))
+ {
+ aRange.aStart.SetCol(nStartCol);
+ aRange.aStart.SetRow(nStartRow);
+ aRange.aEnd.SetCol(nEndCol);
+ aRange.aEnd.SetRow(nEndRow);
+ rMark.SetMarkArea(aRange);
+ pTabViewShell->MarkRange(aRange);
+ }
+
+ if ( rDoc.HasSubTotalCells( aRange ) )
+ {
+ // confirm selection if it contains SubTotal cells
+ std::shared_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(pTabViewShell->GetFrameWeld(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ ScResId(STR_DATAPILOT_SUBTOTAL)));
+ xQueryBox->set_default_response(RET_YES);
+ xQueryBox->runAsync(xQueryBox, [aRange, pDoc, pTypeDlg, aDestPos,
+ pScMod, pTabViewShell, pSrcErrorId] (int nResult2) mutable {
+ if (nResult2 == RET_NO)
+ return;
+
+ std::unique_ptr<ScDPObject> pNewDPObject;
+ SetupRangeForPivotTableDialog(aRange, aDestPos, pDoc, pSrcErrorId, pNewDPObject);
+ ErrorOrRunPivotLayoutDialog(pSrcErrorId, aDestPos, pScMod, pTabViewShell, pNewDPObject);
+ });
+
+ pTypeDlg->disposeOnce();
+ return;
+ }
+
+ std::unique_ptr<ScDPObject> pNewDPObject;
+ SetupRangeForPivotTableDialog(aRange, aDestPos, pDoc, pSrcErrorId, pNewDPObject);
+ ErrorOrRunPivotLayoutDialog(pSrcErrorId, aDestPos, pScMod, pTabViewShell, pNewDPObject);
+ }
+ }
+ }
+ }
+
+ pTypeDlg->disposeOnce();
+ });
+ }
+}
+
+void ScCellShell::ExecuteXMLSourceDialog()
+{
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ if (!pTabViewShell)
+ return;
+
+ ScModule* pScMod = SC_MOD();
+
+ sal_uInt16 nId = ScXMLSourceDlgWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrame = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrame.GetChildWindow(nId);
+ pScMod->SetRefDialog(nId, pWnd == nullptr);
+}
+
+void ScCellShell::ExecuteSubtotals(SfxRequest& rReq)
+{
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ const SfxItemSet* pArgs = rReq.GetArgs();
+ if ( pArgs )
+ {
+ pTabViewShell->DoSubTotals( pArgs->Get( SCITEM_SUBTDATA ).
+ GetSubTotalData() );
+ rReq.Done();
+ return;
+ }
+
+ ScopedVclPtr<SfxAbstractTabDialog> pDlg;
+ ScSubTotalParam aSubTotalParam;
+ SfxItemSetFixed<SCITEM_SUBTDATA, SCITEM_SUBTDATA> aArgSet( GetPool() );
+
+ bool bAnonymous;
+
+ // Only get existing named database range.
+ ScDBData* pDBData = pTabViewShell->GetDBData(true, SC_DB_OLD);
+ if (pDBData)
+ bAnonymous = false;
+ else
+ {
+ // No existing DB data at this position. Create an
+ // anonymous DB.
+ bAnonymous = true;
+ pDBData = pTabViewShell->GetAnonymousDBData();
+ ScRange aDataRange;
+ pDBData->GetArea(aDataRange);
+ pTabViewShell->MarkRange(aDataRange, false);
+ }
+
+ pDBData->GetSubTotalParam( aSubTotalParam );
+ aSubTotalParam.bRemoveOnly = false;
+ if (bAnonymous)
+ {
+ // Preset sort formatting along with values and also create formula
+ // cells with "needs formatting". Subtotals on data of different types
+ // doesn't make much sense anyway.
+ aSubTotalParam.bIncludePattern = true;
+ }
+
+ aArgSet.Put( ScSubTotalItem( SCITEM_SUBTDATA, &GetViewData(), &aSubTotalParam ) );
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+ pDlg.disposeAndReset(pFact->CreateScSubTotalDlg(pTabViewShell->GetFrameWeld(), aArgSet));
+ pDlg->SetCurPageId("1stgroup");
+
+ short bResult = pDlg->Execute();
+
+ if ( (bResult == RET_OK) || (bResult == SCRET_REMOVE) )
+ {
+ const SfxItemSet* pOutSet = nullptr;
+
+ if ( bResult == RET_OK )
+ {
+ pOutSet = pDlg->GetOutputItemSet();
+ aSubTotalParam =
+ pOutSet->Get( SCITEM_SUBTDATA ).GetSubTotalData();
+ }
+ else // if (bResult == SCRET_REMOVE)
+ {
+ pOutSet = &aArgSet;
+ aSubTotalParam.bRemoveOnly = true;
+ aSubTotalParam.bReplace = true;
+ aArgSet.Put( ScSubTotalItem( SCITEM_SUBTDATA,
+ &GetViewData(),
+ &aSubTotalParam ) );
+ }
+
+ pTabViewShell->DoSubTotals( aSubTotalParam );
+ rReq.Done( *pOutSet );
+ }
+ else
+ GetViewData().GetDocShell()->CancelAutoDBRange();
+}
+
+void ScCellShell::ExecuteFillSingleEdit()
+{
+ ScAddress aCurPos = GetViewData().GetCurPos();
+
+ OUString aInit;
+
+ if (aCurPos.Row() > 0)
+ {
+ // Get the initial text value from the above cell.
+
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScAddress aPrevPos = aCurPos;
+ aPrevPos.IncRow(-1);
+ ScRefCellValue aCell(rDoc, aPrevPos);
+
+ if (aCell.getType() == CELLTYPE_FORMULA)
+ {
+ aInit = "=";
+ const ScTokenArray* pCode = aCell.getFormula()->GetCode();
+ sc::TokenStringContext aCxt(rDoc, rDoc.GetGrammar());
+ aInit += pCode->CreateString(aCxt, aCurPos);
+ }
+ else
+ aInit = aCell.getString(&rDoc);
+ }
+
+ SC_MOD()->SetInputMode(SC_INPUT_TABLE, &aInit);
+}
+
+CellShell_Impl::CellShell_Impl() :
+ m_pRequest( nullptr ) {}
+
+CellShell_Impl::~CellShell_Impl()
+{
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/cellsh2.cxx b/sc/source/ui/view/cellsh2.cxx
new file mode 100644
index 0000000000..71bcd6cac3
--- /dev/null
+++ b/sc/source/ui/view/cellsh2.cxx
@@ -0,0 +1,1256 @@
+/* -*- 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 .
+ */
+
+#include <config_features.h>
+
+#include <basic/sberrors.hxx>
+#include <scitems.hxx>
+#include <comphelper/lok.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/request.hxx>
+#include <basic/sbxcore.hxx>
+#include <svl/whiter.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <svl/stritem.hxx>
+#include <svl/visitem.hxx>
+#include <unotools/moduleoptions.hxx>
+
+#include <com/sun/star/frame/FrameSearchFlag.hpp>
+#include <com/sun/star/sheet/TableValidationVisibility.hpp>
+
+#include <cellsh.hxx>
+#include <dbdata.hxx>
+#include <queryparam.hxx>
+#include <tabvwsh.hxx>
+#include <sc.hrc>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <global.hxx>
+#include <scmod.hxx>
+#include <docsh.hxx>
+#include <document.hxx>
+#include <uiitems.hxx>
+#include <dbdocfun.hxx>
+#include <reffact.hxx>
+#include <utility>
+#include <validat.hxx>
+#include <validate.hxx>
+#include <datamapper.hxx>
+
+#include <scui_def.hxx>
+#include <scabstdlg.hxx>
+#include <impex.hxx>
+#include <asciiopt.hxx>
+#include <datastream.hxx>
+#include <datastreamdlg.hxx>
+#include <dataproviderdlg.hxx>
+#include <queryentry.hxx>
+#include <markdata.hxx>
+#include <documentlinkmgr.hxx>
+#include <officecfg/Office/Common.hxx>
+
+#include <o3tl/make_shared.hxx>
+#include <memory>
+
+using namespace com::sun::star;
+
+static bool lcl_GetTextToColumnsRange( const ScViewData& rData, ScRange& rRange, bool bDoEmptyCheckOnly )
+{
+ bool bRet = false;
+ const ScMarkData& rMark = rData.GetMarkData();
+
+ if ( rMark.IsMarked() )
+ {
+ if ( !rMark.IsMultiMarked() )
+ {
+ rRange = rMark.GetMarkArea();
+ if ( rRange.aStart.Col() == rRange.aEnd.Col() )
+ {
+ bRet = true;
+ }
+ }
+ }
+ else
+ {
+ const SCCOL nCol = rData.GetCurX();
+ const SCROW nRow = rData.GetCurY();
+ const SCTAB nTab = rData.GetTabNo();
+ rRange = ScRange( nCol, nRow, nTab, nCol, nRow, nTab );
+ bRet = true;
+ }
+
+ const ScDocument& rDoc = rData.GetDocument();
+
+ if ( bDoEmptyCheckOnly )
+ {
+ if ( bRet && rDoc.IsBlockEmpty( rRange.aStart.Col(), rRange.aStart.Row(),
+ rRange.aEnd.Col(), rRange.aEnd.Row(),
+ rRange.aStart.Tab() ) )
+ {
+ bRet = false;
+ }
+ }
+ else if ( bRet )
+ {
+ rRange.PutInOrder();
+ SCCOL nStartCol = rRange.aStart.Col(), nEndCol = rRange.aEnd.Col();
+ SCROW nStartRow = rRange.aStart.Row(), nEndRow = rRange.aEnd.Row();
+ bool bShrunk = false;
+ rDoc.ShrinkToUsedDataArea( bShrunk, rRange.aStart.Tab(), nStartCol, nStartRow,
+ nEndCol, nEndRow, false, false, true );
+ if ( bShrunk )
+ {
+ rRange.aStart.SetRow( nStartRow );
+ rRange.aEnd.SetRow( nEndRow );
+ }
+ }
+
+ return bRet;
+}
+
+static bool lcl_GetSortParam( const ScViewData& rData, const ScSortParam& rSortParam )
+{
+ ScTabViewShell* pTabViewShell = rData.GetViewShell();
+ ScDBData* pDBData = pTabViewShell->GetDBData();
+ ScDocument& rDoc = rData.GetDocument();
+ SCTAB nTab = rData.GetTabNo();
+ ScDirection eFillDir = DIR_TOP;
+ bool bSort = true;
+ ScRange aExternalRange;
+
+ if( rSortParam.nCol1 != rSortParam.nCol2 )
+ eFillDir = DIR_LEFT;
+ if( rSortParam.nRow1 != rSortParam.nRow2 )
+ eFillDir = DIR_TOP;
+
+ if( rSortParam.nRow2 == rDoc.MaxRow() )
+ {
+ // Assume that user selected entire column(s), but cater for the
+ // possibility that the start row is not the first row.
+ SCSIZE nCount = rDoc.GetEmptyLinesInBlock( rSortParam.nCol1, rSortParam.nRow1, nTab,
+ rSortParam.nCol2, rSortParam.nRow2, nTab, eFillDir );
+ aExternalRange = ScRange( rSortParam.nCol1,
+ ::std::min( rSortParam.nRow1 + sal::static_int_cast<SCROW>( nCount ), rDoc.MaxRow()), nTab,
+ rSortParam.nCol2, rSortParam.nRow2, nTab);
+ aExternalRange.PutInOrder();
+ }
+ else if (rSortParam.nCol1 != rSortParam.nCol2 || rSortParam.nRow1 != rSortParam.nRow2)
+ {
+ // Preserve a preselected area.
+ aExternalRange = ScRange( rSortParam.nCol1, rSortParam.nRow1, nTab, rSortParam.nCol2, rSortParam.nRow2, nTab);
+ aExternalRange.PutInOrder();
+ }
+ else
+ aExternalRange = ScRange( rData.GetCurX(), rData.GetCurY(), nTab );
+
+ SCROW nStartRow = aExternalRange.aStart.Row();
+ SCCOL nStartCol = aExternalRange.aStart.Col();
+ SCROW nEndRow = aExternalRange.aEnd.Row();
+ SCCOL nEndCol = aExternalRange.aEnd.Col();
+ rDoc.GetDataArea( aExternalRange.aStart.Tab(), nStartCol, nStartRow, nEndCol, nEndRow, false, false );
+ aExternalRange.aStart.SetRow( nStartRow );
+ aExternalRange.aStart.SetCol( nStartCol );
+ aExternalRange.aEnd.SetRow( nEndRow );
+ aExternalRange.aEnd.SetCol( nEndCol );
+
+ // with LibreOfficeKit, don't try to interact with the user
+ if (!comphelper::LibreOfficeKit::isActive() &&
+ ((rSortParam.nCol1 == rSortParam.nCol2 && aExternalRange.aStart.Col() != aExternalRange.aEnd.Col()) ||
+ (rSortParam.nRow1 == rSortParam.nRow2 && aExternalRange.aStart.Row() != aExternalRange.aEnd.Row())))
+ {
+ pTabViewShell->AddHighlightRange( aExternalRange,COL_LIGHTBLUE );
+ OUString aExtendStr( aExternalRange.Format(rDoc, ScRefFlags::VALID));
+
+ ScRange aCurrentRange( rSortParam.nCol1, rSortParam.nRow1, nTab, rSortParam.nCol2, rSortParam.nRow2, nTab );
+ OUString aCurrentStr(aCurrentRange.Format(rDoc, ScRefFlags::VALID));
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScSortWarningDlg> pWarningDlg(pFact->CreateScSortWarningDlg(pTabViewShell->GetFrameWeld(), aExtendStr, aCurrentStr));
+ short bResult = pWarningDlg->Execute();
+ if( bResult == BTN_EXTEND_RANGE || bResult == BTN_CURRENT_SELECTION )
+ {
+ if( bResult == BTN_EXTEND_RANGE )
+ {
+ pTabViewShell->MarkRange( aExternalRange, false );
+ pDBData->SetArea( nTab, aExternalRange.aStart.Col(), aExternalRange.aStart.Row(), aExternalRange.aEnd.Col(), aExternalRange.aEnd.Row() );
+ }
+ }
+ else
+ {
+ bSort = false;
+ rData.GetDocShell()->CancelAutoDBRange();
+ }
+
+ pTabViewShell->ClearHighlightRanges();
+ }
+ return bSort;
+}
+
+namespace
+{
+ // this registers the dialog which Find1RefWindow search for
+ class ScValidationRegisteredDlg
+ {
+ std::shared_ptr<SfxDialogController> m_xDlg;
+ public:
+ ScValidationRegisteredDlg(weld::Window* pParent, std::shared_ptr<SfxDialogController> xDlg)
+ : m_xDlg(std::move(xDlg))
+ {
+ SC_MOD()->RegisterRefController(static_cast<sal_uInt16>(ScValidationDlg::SLOTID), m_xDlg, pParent);
+ }
+ ~ScValidationRegisteredDlg()
+ {
+ m_xDlg->Close();
+ SC_MOD()->UnregisterRefController(static_cast<sal_uInt16>(ScValidationDlg::SLOTID), m_xDlg);
+ }
+ };
+}
+
+void ScCellShell::ExecuteDB( SfxRequest& rReq )
+{
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ sal_uInt16 nSlotId = rReq.GetSlot();
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+ ScModule* pScMod = SC_MOD();
+
+ pTabViewShell->HideListBox(); // Autofilter-DropDown-Listbox
+
+ if ( GetViewData().HasEditView( GetViewData().GetActivePart() ) )
+ {
+ pScMod->InputEnterHandler();
+ pTabViewShell->UpdateInputHandler();
+ }
+
+ switch ( nSlotId )
+ {
+ case SID_VIEW_DATA_SOURCE_BROWSER:
+ {
+ // check if database beamer is open
+
+ SfxViewFrame& rViewFrame = pTabViewShell->GetViewFrame();
+ bool bWasOpen = false;
+ {
+ uno::Reference<frame::XFrame> xFrame = rViewFrame.GetFrame().GetFrameInterface();
+ uno::Reference<frame::XFrame> xBeamerFrame = xFrame->findFrame(
+ "_beamer",
+ frame::FrameSearchFlag::CHILDREN);
+ if ( xBeamerFrame.is() )
+ bWasOpen = true;
+ }
+
+ if ( bWasOpen )
+ {
+ // close database beamer: just forward to SfxViewFrame
+
+ rViewFrame.ExecuteSlot( rReq );
+ }
+ else
+ {
+ // show database beamer: SfxViewFrame call must be synchronous
+
+ rViewFrame.ExecuteSlot( rReq, false ); // false = synchronous
+
+ // select current database in database beamer
+
+ ScImportParam aImportParam;
+ ScDBData* pDBData = pTabViewShell->GetDBData(true,SC_DB_OLD); // don't create if none found
+ if (pDBData)
+ pDBData->GetImportParam( aImportParam );
+
+ ScDBDocFunc::ShowInBeamer( aImportParam, &pTabViewShell->GetViewFrame() );
+ }
+ rReq.Done(); // needed because it's a toggle slot
+ }
+ break;
+
+ case SID_REIMPORT_DATA:
+ {
+ bool bOk = false;
+ ScDBData* pDBData = pTabViewShell->GetDBData(true,SC_DB_OLD);
+ if (pDBData)
+ {
+ ScImportParam aImportParam;
+ pDBData->GetImportParam( aImportParam );
+ if (aImportParam.bImport && !pDBData->HasImportSelection())
+ {
+ pTabViewShell->ImportData( aImportParam );
+ pDBData->SetImportParam( aImportParam ); //! Undo ??
+ bOk = true;
+ }
+ }
+
+ if (!bOk && ! rReq.IsAPI() )
+ pTabViewShell->ErrorMessage(STR_REIMPORT_EMPTY);
+
+ if( bOk )
+ rReq.Done();
+ }
+ break;
+
+ case SID_REFRESH_DBAREA:
+ {
+ ScDBData* pDBData = pTabViewShell->GetDBData(true,SC_DB_OLD);
+ if (pDBData)
+ {
+ // repeat import like SID_REIMPORT_DATA
+
+ bool bContinue = true;
+ ScImportParam aImportParam;
+ pDBData->GetImportParam( aImportParam );
+ if (aImportParam.bImport && !pDBData->HasImportSelection())
+ {
+ bContinue = pTabViewShell->ImportData( aImportParam );
+ pDBData->SetImportParam( aImportParam ); //! Undo ??
+
+ // mark (size may have been changed)
+ ScRange aNewRange;
+ pDBData->GetArea(aNewRange);
+ pTabViewShell->MarkRange(aNewRange);
+ }
+
+ if ( bContinue ) // fail at import -> break
+ {
+ // internal operations, when any stored
+
+ if ( pDBData->HasQueryParam() || pDBData->HasSortParam() ||
+ pDBData->HasSubTotalParam() )
+ pTabViewShell->RepeatDB();
+
+ // pivot tables that have the range as data source
+
+ ScRange aRange;
+ pDBData->GetArea(aRange);
+ GetViewData().GetDocShell()->RefreshPivotTables(aRange);
+ }
+ }
+ rReq.Done();
+ }
+ break;
+
+ case SID_SBA_BRW_INSERT:
+ {
+ OSL_FAIL( "Deprecated Slot" );
+ }
+ break;
+
+ case SID_DATA_FORM:
+ {
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScDataFormDlg> pDlg(pFact->CreateScDataFormDlg(
+ pTabViewShell->GetFrameWeld(), pTabViewShell));
+
+ pDlg->Execute();
+
+ rReq.Done();
+ }
+ break;
+
+ case SID_SUBTOTALS:
+ ExecuteSubtotals(rReq);
+ break;
+
+ case SID_SORT_DESCENDING:
+ case SID_SORT_ASCENDING:
+ {
+ //#i60401 ux-ctest: Calc does not support all users' strategies regarding sorting data
+ //the patch comes from maoyg
+ ScSortParam aSortParam;
+ ScDBData* pDBData = pTabViewShell->GetDBData();
+ ScViewData& rData = GetViewData();
+
+ pDBData->GetSortParam( aSortParam );
+
+ if( lcl_GetSortParam( rData, aSortParam ) )
+ {
+ SCCOL nCol = GetViewData().GetCurX();
+ SCCOL nTab = GetViewData().GetTabNo();
+ ScDocument& rDoc = GetViewData().GetDocument();
+
+ pDBData->GetSortParam( aSortParam );
+ bool bHasHeader = rDoc.HasColHeader( aSortParam.nCol1, aSortParam.nRow1, aSortParam.nCol2, aSortParam.nRow2, nTab );
+
+ if( nCol < aSortParam.nCol1 )
+ nCol = aSortParam.nCol1;
+ else if( nCol > aSortParam.nCol2 )
+ nCol = aSortParam.nCol2;
+
+ aSortParam.bHasHeader = bHasHeader;
+ aSortParam.bByRow = true;
+ aSortParam.bCaseSens = false;
+ aSortParam.bNaturalSort = false;
+ aSortParam.aDataAreaExtras.mbCellNotes = false;
+ aSortParam.aDataAreaExtras.mbCellDrawObjects = true;
+ aSortParam.aDataAreaExtras.mbCellFormats = true;
+ aSortParam.bInplace = true;
+ aSortParam.maKeyState[0].bDoSort = true;
+ aSortParam.maKeyState[0].nField = nCol;
+ aSortParam.maKeyState[0].bAscending = ( nSlotId == SID_SORT_ASCENDING );
+
+ for ( sal_uInt16 i=1; i<aSortParam.GetSortKeyCount(); i++ )
+ aSortParam.maKeyState[i].bDoSort = false;
+
+ pTabViewShell->UISort( aSortParam ); // subtotal when needed new
+
+ rReq.Done();
+ }
+ }
+ break;
+
+ case SID_SORT:
+ {
+ const SfxItemSet* pArgs = rReq.GetArgs();
+
+ //#i60401 ux-ctest: Calc does not support all users' strategies regarding sorting data
+ //the patch comes from maoyg
+
+ if ( pArgs ) // Basic
+ {
+ ScSortParam aSortParam;
+ ScDBData* pDBData = pTabViewShell->GetDBData();
+ ScViewData& rData = GetViewData();
+
+ pDBData->GetSortParam( aSortParam );
+
+ if( lcl_GetSortParam( rData, aSortParam ) )
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+
+ pDBData->GetSortParam( aSortParam );
+ bool bHasHeader = rDoc.HasColHeader( aSortParam.nCol1, aSortParam.nRow1, aSortParam.nCol2, aSortParam.nRow2, rData.GetTabNo() );
+ if( bHasHeader )
+ aSortParam.bHasHeader = bHasHeader;
+
+ aSortParam.bInplace = true; // from Basic always
+
+ if ( const SfxBoolItem* pItem = pArgs->GetItemIfSet( SID_SORT_BYROW ) )
+ aSortParam.bByRow = pItem->GetValue();
+ if ( const SfxBoolItem* pItem = pArgs->GetItemIfSet( SID_SORT_HASHEADER ) )
+ aSortParam.bHasHeader = pItem->GetValue();
+ if ( const SfxBoolItem* pItem = pArgs->GetItemIfSet( SID_SORT_CASESENS ) )
+ aSortParam.bCaseSens = pItem->GetValue();
+ if ( const SfxBoolItem* pItem = pArgs->GetItemIfSet( SID_SORT_NATURALSORT ) )
+ aSortParam.bNaturalSort = pItem->GetValue();
+ if ( const SfxBoolItem* pItem = pArgs->GetItemIfSet( SID_SORT_INCCOMMENTS ) )
+ aSortParam.aDataAreaExtras.mbCellNotes = pItem->GetValue();
+ if ( const SfxBoolItem* pItem = pArgs->GetItemIfSet( SID_SORT_INCIMAGES ) )
+ aSortParam.aDataAreaExtras.mbCellDrawObjects = pItem->GetValue();
+ if ( const SfxBoolItem* pItem = pArgs->GetItemIfSet( SID_SORT_ATTRIBS ) )
+ aSortParam.aDataAreaExtras.mbCellFormats = pItem->GetValue();
+ if ( const SfxUInt16Item* pItem = pArgs->GetItemIfSet( SID_SORT_USERDEF ) )
+ {
+ sal_uInt16 nUserIndex = pItem->GetValue();
+ aSortParam.bUserDef = ( nUserIndex != 0 );
+ if ( nUserIndex )
+ aSortParam.nUserIndex = nUserIndex - 1; // Basic: 1-based
+ }
+
+ SCCOLROW nField0 = 0;
+ const SfxPoolItem* pItem = nullptr;
+ if ( pArgs->GetItemState( FN_PARAM_1, true, &pItem ) == SfxItemState::SET )
+ nField0 = static_cast<const SfxInt32Item*>(pItem)->GetValue();
+ aSortParam.maKeyState[0].bDoSort = ( nField0 != 0 );
+ aSortParam.maKeyState[0].nField = nField0 > 0 ? (nField0-1) : 0;
+ if ( pArgs->GetItemState( FN_PARAM_2, true, &pItem ) == SfxItemState::SET )
+ aSortParam.maKeyState[0].bAscending = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+ SCCOLROW nField1 = 0;
+ if ( pArgs->GetItemState( FN_PARAM_3, true, &pItem ) == SfxItemState::SET )
+ nField1 = static_cast<const SfxInt32Item*>(pItem)->GetValue();
+ aSortParam.maKeyState[1].bDoSort = ( nField1 != 0 );
+ aSortParam.maKeyState[1].nField = nField1 > 0 ? (nField1-1) : 0;
+ if ( pArgs->GetItemState( FN_PARAM_4, true, &pItem ) == SfxItemState::SET )
+ aSortParam.maKeyState[1].bAscending = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+ SCCOLROW nField2 = 0;
+ if ( pArgs->GetItemState( FN_PARAM_5, true, &pItem ) == SfxItemState::SET )
+ nField2 = static_cast<const SfxInt32Item*>(pItem)->GetValue();
+ aSortParam.maKeyState[2].bDoSort = ( nField2 != 0 );
+ aSortParam.maKeyState[2].nField = nField2 > 0 ? (nField2-1) : 0;
+ if ( pArgs->GetItemState( FN_PARAM_6, true, &pItem ) == SfxItemState::SET )
+ aSortParam.maKeyState[2].bAscending = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+
+ // subtotal when needed new
+ pTabViewShell->UISort( aSortParam );
+ rReq.Done();
+ }
+ }
+ else
+ {
+ ScSortParam aSortParam;
+ ScDBData* pDBData = pTabViewShell->GetDBData();
+ ScViewData& rData = GetViewData();
+
+ pDBData->GetSortParam( aSortParam );
+
+ if( lcl_GetSortParam( rData, aSortParam ) )
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+ SfxItemSetFixed<SCITEM_SORTDATA, SCITEM_SORTDATA> aArgSet( GetPool() );
+
+ pDBData->GetSortParam( aSortParam );
+ bool bHasHeader = rDoc.HasColHeader( aSortParam.nCol1, aSortParam.nRow1, aSortParam.nCol2, aSortParam.nRow2, rData.GetTabNo() );
+ if( bHasHeader )
+ aSortParam.bHasHeader = bHasHeader;
+
+ aArgSet.Put( ScSortItem( SCITEM_SORTDATA, &GetViewData(), &aSortParam ) );
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+ std::shared_ptr<ScAsyncTabController> pDlg(pFact->CreateScSortDlg(pTabViewShell->GetFrameWeld(), &aArgSet));
+ pDlg->SetCurPageId("criteria"); // 1=sort field tab 2=sort options tab
+
+ VclAbstractDialog::AsyncContext aContext;
+ aContext.maEndDialogFn = [pDlg, &rData, pTabViewShell](sal_Int32 nResult)
+ {
+ if ( nResult == RET_OK )
+ {
+ const SfxItemSet* pOutSet = pDlg->GetOutputItemSet();
+ const ScSortParam& rOutParam =
+ pOutSet->Get( SCITEM_SORTDATA ).GetSortData();
+
+ // subtotal when needed new
+
+ pTabViewShell->UISort( rOutParam );
+
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxRequest aRequest(rViewFrm, SID_SORT);
+
+ if ( rOutParam.bInplace )
+ {
+ aRequest.AppendItem( SfxBoolItem( SID_SORT_BYROW,
+ rOutParam.bByRow ) );
+ aRequest.AppendItem( SfxBoolItem( SID_SORT_HASHEADER,
+ rOutParam.bHasHeader ) );
+ aRequest.AppendItem( SfxBoolItem( SID_SORT_CASESENS,
+ rOutParam.bCaseSens ) );
+ aRequest.AppendItem( SfxBoolItem( SID_SORT_NATURALSORT,
+ rOutParam.bNaturalSort ) );
+ aRequest.AppendItem( SfxBoolItem( SID_SORT_INCCOMMENTS,
+ rOutParam.aDataAreaExtras.mbCellNotes ) );
+ aRequest.AppendItem( SfxBoolItem( SID_SORT_INCIMAGES,
+ rOutParam.aDataAreaExtras.mbCellDrawObjects ) );
+ aRequest.AppendItem( SfxBoolItem( SID_SORT_ATTRIBS,
+ rOutParam.aDataAreaExtras.mbCellFormats ) );
+ sal_uInt16 nUser = rOutParam.bUserDef ? ( rOutParam.nUserIndex + 1 ) : 0;
+ aRequest.AppendItem( SfxUInt16Item( SID_SORT_USERDEF, nUser ) );
+ if ( rOutParam.maKeyState[0].bDoSort )
+ {
+ aRequest.AppendItem( SfxInt32Item( TypedWhichId<SfxInt32Item>(FN_PARAM_1),
+ rOutParam.maKeyState[0].nField + 1 ) );
+ aRequest.AppendItem( SfxBoolItem( FN_PARAM_2,
+ rOutParam.maKeyState[0].bAscending ) );
+ }
+ if ( rOutParam.maKeyState[1].bDoSort )
+ {
+ aRequest.AppendItem( SfxInt32Item( TypedWhichId<SfxInt32Item>(FN_PARAM_3),
+ rOutParam.maKeyState[1].nField + 1 ) );
+ aRequest.AppendItem( SfxBoolItem( FN_PARAM_4,
+ rOutParam.maKeyState[1].bAscending ) );
+ }
+ if ( rOutParam.maKeyState[2].bDoSort )
+ {
+ aRequest.AppendItem( SfxInt32Item( TypedWhichId<SfxInt32Item>(FN_PARAM_5),
+ rOutParam.maKeyState[2].nField + 1 ) );
+ aRequest.AppendItem( SfxBoolItem( FN_PARAM_6,
+ rOutParam.maKeyState[2].bAscending ) );
+ }
+ }
+
+ aRequest.Done();
+ }
+ else
+ {
+ rData.GetDocShell()->CancelAutoDBRange();
+ }
+ };
+
+ pDlg->StartExecuteAsync(aContext);
+ }
+ }
+ }
+ break;
+
+ case SID_FILTER:
+ {
+ const SfxItemSet* pArgs = rReq.GetArgs();
+ if ( pArgs )
+ {
+ OSL_FAIL("SID_FILTER with arguments?");
+ pTabViewShell->Query(
+ pArgs->Get( SCITEM_QUERYDATA ).GetQueryData(), nullptr, true );
+ rReq.Done();
+ }
+ else
+ {
+ sal_uInt16 nId = ScFilterDlgWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ }
+ }
+ break;
+
+ case SID_SPECIAL_FILTER:
+ {
+ const SfxItemSet* pArgs = rReq.GetArgs();
+ if ( pArgs )
+ {
+ OSL_FAIL("SID_SPECIAL_FILTER with arguments?");
+ pTabViewShell->Query(
+ pArgs->Get( SCITEM_QUERYDATA ).GetQueryData(), nullptr, true );
+ rReq.Done();
+ }
+ else
+ {
+ sal_uInt16 nId = ScSpecialFilterDlgWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ }
+ }
+ break;
+
+ case FID_FILTER_OK:
+ {
+ const ScQueryItem* pQueryItem;
+ if ( pReqArgs && (pQueryItem =
+ pReqArgs->GetItemIfSet( SCITEM_QUERYDATA )) )
+ {
+ SCTAB nCurTab = GetViewData().GetTabNo();
+ SCTAB nRefTab = GetViewData().GetRefTabNo();
+
+ // If RefInput switched to a different sheet from the data sheet,
+ // switch back:
+
+ if ( nCurTab != nRefTab )
+ {
+ pTabViewShell->SetTabNo( nRefTab );
+ pTabViewShell->PaintExtras();
+ }
+
+ ScRange aAdvSource;
+ if (pQueryItem->GetAdvancedQuerySource(aAdvSource))
+ pTabViewShell->Query( pQueryItem->GetQueryData(), &aAdvSource, true );
+ else
+ pTabViewShell->Query( pQueryItem->GetQueryData(), nullptr, true );
+ rReq.Done( *pReqArgs );
+ }
+ }
+ break;
+
+ case SID_UNFILTER:
+ {
+ ScQueryParam aParam;
+ ScDBData* pDBData = pTabViewShell->GetDBData();
+
+ pDBData->GetQueryParam( aParam );
+ SCSIZE nEC = aParam.GetEntryCount();
+ for (SCSIZE i=0; i<nEC; i++)
+ aParam.GetEntry(i).bDoQuery = false;
+ aParam.bDuplicate = true;
+ pTabViewShell->Query( aParam, nullptr, true );
+ rReq.Done();
+ }
+ break;
+
+ case SID_AUTO_FILTER:
+ pTabViewShell->ToggleAutoFilter();
+ rReq.Done();
+ break;
+
+ case SID_AUTOFILTER_HIDE:
+ pTabViewShell->HideAutoFilter();
+ rReq.Done();
+ break;
+
+ case SID_PIVOT_TABLE:
+ {
+ const ScPivotItem* pPItem;
+ if ( pReqArgs && (pPItem =
+ pReqArgs->GetItemIfSet( SCITEM_PIVOTDATA )) )
+ {
+ SCTAB nCurTab = GetViewData().GetTabNo();
+ SCTAB nRefTab = GetViewData().GetRefTabNo();
+
+ // If RefInput switched to a different sheet from the data sheet,
+ // switch back:
+
+ if ( nCurTab != nRefTab )
+ {
+ pTabViewShell->SetTabNo( nRefTab );
+ pTabViewShell->PaintExtras();
+ }
+
+ const ScDPObject* pDPObject = pTabViewShell->GetDialogDPObject();
+ if ( pDPObject )
+ {
+ bool bSuccess = pTabViewShell->MakePivotTable(
+ pPItem->GetData(), pPItem->GetDestRange(), pPItem->IsNewSheet(), *pDPObject );
+ SfxBoolItem aRet(0, bSuccess);
+ rReq.SetReturnValue(aRet);
+ }
+ rReq.Done();
+ }
+#if HAVE_FEATURE_SCRIPTING
+ else if (rReq.IsAPI())
+ SbxBase::SetError(ERRCODE_BASIC_BAD_PARAMETER);
+#endif
+ }
+ break;
+
+ case SID_OPENDLG_PIVOTTABLE:
+ ExecuteDataPilotDialog();
+ break;
+ case SID_DEFINE_DBNAME:
+ {
+
+ sal_uInt16 nId = ScDbNameDlgWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+
+ }
+ break;
+
+ case SID_SELECT_DB:
+ {
+ if ( pReqArgs )
+ {
+ const SfxStringItem& rItem = pReqArgs->Get(SID_SELECT_DB);
+ pTabViewShell->GotoDBArea(rItem.GetValue());
+ rReq.Done();
+ }
+ else
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScDBCollection* pDBCol = rDoc.GetDBCollection();
+
+ if ( pDBCol )
+ {
+ std::vector<OUString> aList;
+ const ScDBCollection::NamedDBs& rDBs = pDBCol->getNamedDBs();
+ for (const auto& rxDB : rDBs)
+ aList.push_back(rxDB->GetName());
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScSelEntryDlg> pDlg(pFact->CreateScSelEntryDlg(pTabViewShell->GetFrameWeld(), aList));
+ if ( pDlg->Execute() == RET_OK )
+ {
+ OUString aName = pDlg->GetSelectedEntry();
+ pTabViewShell->GotoDBArea( aName );
+ rReq.AppendItem( SfxStringItem( SID_SELECT_DB, aName ) );
+ rReq.Done();
+ }
+ }
+ }
+ }
+ break;
+ case SID_DATA_STREAMS:
+ {
+ sc::DataStreamDlg aDialog(GetViewData().GetDocShell(), pTabViewShell->GetFrameWeld());
+ ScDocument& rDoc = GetViewData().GetDocument();
+ sc::DocumentLinkManager& rMgr = rDoc.GetDocLinkManager();
+ sc::DataStream* pStrm = rMgr.getDataStream();
+ if (pStrm)
+ aDialog.Init(*pStrm);
+
+ if (aDialog.run() == RET_OK)
+ aDialog.StartStream();
+ }
+ break;
+ case SID_DATA_STREAMS_PLAY:
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+ sc::DocumentLinkManager& rMgr = rDoc.GetDocLinkManager();
+ sc::DataStream* pStrm = rMgr.getDataStream();
+ if (pStrm)
+ pStrm->StartImport();
+ }
+ break;
+ case SID_DATA_STREAMS_STOP:
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+ sc::DocumentLinkManager& rMgr = rDoc.GetDocLinkManager();
+ sc::DataStream* pStrm = rMgr.getDataStream();
+ if (pStrm)
+ pStrm->StopImport();
+ }
+ break;
+ case SID_DATA_PROVIDER:
+ {
+ auto xDoc = o3tl::make_shared<ScDocument>();
+ xDoc->InsertTab(0, "test");
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScDataProviderDlg aDialog(pTabViewShell->GetDialogParent(), xDoc, &rDoc);
+ if (aDialog.run() == RET_OK)
+ {
+ aDialog.import(rDoc);
+ }
+ }
+ break;
+ case SID_DATA_PROVIDER_REFRESH:
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+ auto& rDataMapper = rDoc.GetExternalDataMapper();
+ for (auto& rDataSource : rDataMapper.getDataSources())
+ {
+ rDataSource.refresh(&rDoc, false);
+ }
+ }
+ break;
+ case SID_MANAGE_XML_SOURCE:
+ ExecuteXMLSourceDialog();
+ break;
+ case FID_VALIDATION:
+ case FID_CURRENTVALIDATION:
+ {
+ const SfxItemSet* pArgs = rReq.GetArgs();
+ if ( pArgs )
+ {
+ OSL_FAIL("later...");
+ }
+ else
+ {
+ SfxItemSet aArgSet( GetPool(), ScTPValidationValue::GetRanges() );
+ ScValidationMode eMode = SC_VALID_ANY;
+ ScConditionMode eOper = ScConditionMode::Equal;
+ OUString aExpr1, aExpr2;
+ bool bBlank = true;
+ sal_Int16 nListType = css::sheet::TableValidationVisibility::UNSORTED;
+ bool bShowHelp = false;
+ OUString aHelpTitle, aHelpText;
+ bool bShowError = false;
+ ScValidErrorStyle eErrStyle = SC_VALERR_STOP;
+ OUString aErrTitle, aErrText;
+
+ ScDocument& rDoc = GetViewData().GetDocument();
+ SCCOL nCurX = GetViewData().GetCurX();
+ SCROW nCurY = GetViewData().GetCurY();
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScAddress aCursorPos( nCurX, nCurY, nTab );
+ sal_uInt32 nIndex = rDoc.GetAttr(
+ nCurX, nCurY, nTab, ATTR_VALIDDATA )->GetValue();
+ if ( nIndex )
+ {
+ const ScValidationData* pOldData = rDoc.GetValidationEntry( nIndex );
+ if ( pOldData )
+ {
+ eMode = pOldData->GetDataMode();
+ eOper = pOldData->GetOperation();
+ sal_uInt32 nNumFmt = 0;
+ if ( eMode == SC_VALID_DATE || eMode == SC_VALID_TIME )
+ {
+ SvNumFormatType nType = ( eMode == SC_VALID_DATE ) ? SvNumFormatType::DATE
+ : SvNumFormatType::TIME;
+ nNumFmt = rDoc.GetFormatTable()->GetStandardFormat(
+ nType, ScGlobal::eLnge );
+ }
+ aExpr1 = pOldData->GetExpression( aCursorPos, 0, nNumFmt );
+ aExpr2 = pOldData->GetExpression( aCursorPos, 1, nNumFmt );
+ bBlank = pOldData->IsIgnoreBlank();
+ nListType = pOldData->GetListType();
+
+ bShowHelp = pOldData->GetInput( aHelpTitle, aHelpText );
+ bShowError = pOldData->GetErrMsg( aErrTitle, aErrText, eErrStyle );
+
+ aArgSet.Put( SfxUInt16Item( FID_VALID_MODE, sal::static_int_cast<sal_uInt16>(eMode) ) );
+ aArgSet.Put( SfxUInt16Item( FID_VALID_CONDMODE, sal::static_int_cast<sal_uInt16>(eOper) ) );
+ aArgSet.Put( SfxStringItem( FID_VALID_VALUE1, aExpr1 ) );
+ aArgSet.Put( SfxStringItem( FID_VALID_VALUE2, aExpr2 ) );
+ aArgSet.Put( SfxBoolItem( FID_VALID_BLANK, bBlank ) );
+ aArgSet.Put( SfxInt16Item( FID_VALID_LISTTYPE, nListType ) );
+ aArgSet.Put( SfxBoolItem( FID_VALID_SHOWHELP, bShowHelp ) );
+ aArgSet.Put( SfxStringItem( FID_VALID_HELPTITLE, aHelpTitle ) );
+ aArgSet.Put( SfxStringItem( FID_VALID_HELPTEXT, aHelpText ) );
+ aArgSet.Put( SfxBoolItem( FID_VALID_SHOWERR, bShowError ) );
+ aArgSet.Put( SfxUInt16Item( FID_VALID_ERRSTYLE, sal::static_int_cast<sal_uInt16>(eErrStyle) ) );
+ aArgSet.Put( SfxStringItem( FID_VALID_ERRTITLE, aErrTitle ) );
+ aArgSet.Put( SfxStringItem( FID_VALID_ERRTEXT, aErrText ) );
+ }
+ }
+
+ // cell range picker
+ vcl::Window* pWin = GetViewData().GetActiveWin();
+ weld::Window* pParentWin = pWin ? pWin->GetFrameWeld() : nullptr;
+ auto xDlg = std::make_shared<ScValidationDlg>(pParentWin, &aArgSet, pTabViewShell);
+ ScValidationRegisteredDlg aRegisterThatDlgExists(pParentWin, xDlg);
+
+ short nResult = xDlg->run();
+ if ( nResult == RET_OK )
+ {
+ const SfxItemSet* pOutSet = xDlg->GetOutputItemSet();
+
+ if ( const SfxUInt16Item* pItem = pOutSet->GetItemIfSet( FID_VALID_MODE ) )
+ eMode = static_cast<ScValidationMode>(pItem->GetValue());
+ if ( const SfxUInt16Item* pItem = pOutSet->GetItemIfSet( FID_VALID_CONDMODE ) )
+ eOper = static_cast<ScConditionMode>(pItem->GetValue());
+ if ( const SfxStringItem* pItem = pOutSet->GetItemIfSet( FID_VALID_VALUE1 ) )
+ {
+ OUString aTemp1 = pItem->GetValue();
+ if (eMode == SC_VALID_DATE || eMode == SC_VALID_TIME)
+ {
+ sal_uInt32 nNumIndex = 0;
+ double nVal;
+ if (rDoc.GetFormatTable()->IsNumberFormat(aTemp1, nNumIndex, nVal))
+ aExpr1 = ::rtl::math::doubleToUString( nVal,
+ rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max,
+ ScGlobal::getLocaleData().getNumDecimalSep()[0], true);
+ else
+ aExpr1 = aTemp1;
+ }
+ else
+ aExpr1 = aTemp1;
+ }
+ if ( const SfxStringItem* pItem = pOutSet->GetItemIfSet( FID_VALID_VALUE2 ) )
+ {
+ OUString aTemp2 = pItem->GetValue();
+ if (eMode == SC_VALID_DATE || eMode == SC_VALID_TIME)
+ {
+ sal_uInt32 nNumIndex = 0;
+ double nVal;
+ if (rDoc.GetFormatTable()->IsNumberFormat(aTemp2, nNumIndex, nVal))
+ aExpr2 = ::rtl::math::doubleToUString( nVal,
+ rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max,
+ ScGlobal::getLocaleData().getNumDecimalSep()[0], true);
+ else
+ aExpr2 = aTemp2;
+ if ( eMode == SC_VALID_TIME ) {
+ sal_Int32 wraparound = aExpr1.compareTo(aExpr2);
+ if (wraparound > 0) {
+ if (eOper == ScConditionMode::Between) {
+ eOper = ScConditionMode::NotBetween;
+ std::swap( aExpr1, aExpr2 );
+ }
+ else if (eOper == ScConditionMode::NotBetween) {
+ eOper = ScConditionMode::Between;
+ std::swap( aExpr1, aExpr2 );
+ }
+ }
+ }
+ }
+ else
+ aExpr2 = aTemp2;
+ }
+ if ( const SfxBoolItem* pItem = pOutSet->GetItemIfSet( FID_VALID_BLANK ) )
+ bBlank = pItem->GetValue();
+ if ( const SfxInt16Item* pItem = pOutSet->GetItemIfSet( FID_VALID_LISTTYPE ) )
+ nListType = pItem->GetValue();
+
+ if ( const SfxBoolItem* pItem = pOutSet->GetItemIfSet( FID_VALID_SHOWHELP ) )
+ bShowHelp = pItem->GetValue();
+ if ( const SfxStringItem* pItem = pOutSet->GetItemIfSet( FID_VALID_HELPTITLE ) )
+ aHelpTitle = pItem->GetValue();
+ if ( const SfxStringItem* pItem = pOutSet->GetItemIfSet( FID_VALID_HELPTEXT ) )
+ aHelpText = pItem->GetValue();
+
+ if ( const SfxBoolItem* pItem = pOutSet->GetItemIfSet( FID_VALID_SHOWERR ) )
+ bShowError = pItem->GetValue();
+ if ( const SfxUInt16Item* pItem = pOutSet->GetItemIfSet( FID_VALID_ERRSTYLE ) )
+ eErrStyle = static_cast<ScValidErrorStyle>(pItem->GetValue());
+ if ( const SfxStringItem* pItem = pOutSet->GetItemIfSet( FID_VALID_ERRTITLE ) )
+ aErrTitle = pItem->GetValue();
+ if ( const SfxStringItem* pItem = pOutSet->GetItemIfSet( FID_VALID_ERRTEXT ) )
+ aErrText = pItem->GetValue();
+
+ ScValidationData aData( eMode, eOper, aExpr1, aExpr2, rDoc, aCursorPos );
+ aData.SetIgnoreBlank( bBlank );
+ aData.SetListType( nListType );
+
+ aData.SetInput(aHelpTitle, aHelpText); // sets bShowInput to TRUE
+ if (!bShowHelp)
+ aData.ResetInput(); // reset only bShowInput
+
+ aData.SetError(aErrTitle, aErrText, eErrStyle); // sets bShowError to TRUE
+ if (!bShowError)
+ aData.ResetError(); // reset only bShowError
+
+ pTabViewShell->SetValidation( aData );
+ pTabViewShell->TestHintWindow();
+ rReq.Done( *pOutSet );
+ }
+ }
+ }
+ break;
+
+ case SID_TEXT_TO_COLUMNS:
+ {
+ ScViewData& rData = GetViewData();
+ ScRange aRange;
+
+ if ( lcl_GetTextToColumnsRange( rData, aRange, false ) )
+ {
+ ScDocument& rDoc = rData.GetDocument();
+
+ ScImportExport aExport( rDoc, aRange );
+ aExport.SetExportTextOptions( ScExportTextOptions( ScExportTextOptions::None, 0, false ) );
+
+ // #i87703# text to columns fails with tab separator
+ aExport.SetDelimiter( u'\0' );
+
+ SvMemoryStream aStream;
+ aStream.SetStreamCharSet( RTL_TEXTENCODING_UNICODE );
+ ScImportExport::SetNoEndianSwap( aStream );
+ aExport.ExportStream( aStream, OUString(), SotClipboardFormatId::STRING );
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractScImportAsciiDlg> pDlg(pFact->CreateScImportAsciiDlg(
+ pTabViewShell->GetFrameWeld(), OUString(), &aStream, SC_TEXTTOCOLUMNS));
+
+ if ( pDlg->Execute() == RET_OK )
+ {
+ ScDocShell* pDocSh = rData.GetDocShell();
+ OSL_ENSURE( pDocSh, "ScCellShell::ExecuteDB: SID_TEXT_TO_COLUMNS - pDocSh is null!" );
+
+ OUString aUndo = ScResId( STR_UNDO_TEXTTOCOLUMNS );
+ pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, rData.GetViewShell()->GetViewShellId() );
+
+ ScImportExport aImport( rDoc, aRange.aStart );
+ ScAsciiOptions aOptions;
+ pDlg->GetOptions( aOptions );
+ pDlg->SaveParameters();
+ aImport.SetExtOptions( aOptions );
+ aImport.SetApi( false );
+ aImport.SetImportBroadcast( true );
+ aImport.SetOverwriting( true );
+ aStream.Seek( 0 );
+ aImport.ImportStream( aStream, OUString(), SotClipboardFormatId::STRING );
+
+ pDocSh->GetUndoManager()->LeaveListAction();
+ }
+ }
+ }
+ break;
+ }
+}
+
+void ScCellShell::GetDBState( SfxItemSet& rSet )
+{
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ ScViewData& rData = GetViewData();
+ ScDocShell* pDocSh = rData.GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCCOL nPosX = rData.GetCurX();
+ SCROW nPosY = rData.GetCurY();
+ SCTAB nTab = rData.GetTabNo();
+
+ bool bAutoFilter = false;
+ bool bAutoFilterTested = false;
+
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while (nWhich)
+ {
+ switch (nWhich)
+ {
+ case SID_REFRESH_DBAREA:
+ {
+ // imported data without selection
+ // or filter,sort,subtotal (also without import)
+ bool bOk = false;
+ ScDBData* pDBData = pTabViewShell->GetDBData(false,SC_DB_OLD);
+ if (pDBData && rDoc.GetChangeTrack() == nullptr)
+ {
+ if ( pDBData->HasImportParam() )
+ bOk = !pDBData->HasImportSelection();
+ else
+ {
+ bOk = pDBData->HasQueryParam() ||
+ pDBData->HasSortParam() ||
+ pDBData->HasSubTotalParam();
+ }
+ }
+ if (!bOk)
+ rSet.DisableItem( nWhich );
+ }
+ break;
+
+ case SID_FILTER:
+ case SID_SPECIAL_FILTER:
+ {
+ ScRange aDummy;
+ ScMarkType eMarkType = GetViewData().GetSimpleArea( aDummy);
+ if (eMarkType != SC_MARK_SIMPLE && eMarkType != SC_MARK_SIMPLE_FILTERED)
+ {
+ rSet.DisableItem( nWhich );
+ }
+ }
+ break;
+
+ //in case of Redlining and multiselection disable
+ case SID_SORT_ASCENDING:
+ case SID_SORT_DESCENDING:
+ case SCITEM_SORTDATA:
+ case SCITEM_SUBTDATA:
+ case SID_OPENDLG_PIVOTTABLE:
+ {
+ //! move ReadOnly check to idl flags
+
+ if ( pDocSh->IsReadOnly() || rDoc.GetChangeTrack()!=nullptr ||
+ GetViewData().IsMultiMarked() )
+ {
+ rSet.DisableItem( nWhich );
+ }
+ }
+ break;
+
+ case SID_REIMPORT_DATA:
+ {
+ // only imported data without selection
+ ScDBData* pDBData = pTabViewShell->GetDBData(false,SC_DB_OLD);
+ if (!pDBData || !pDBData->HasImportParam() || pDBData->HasImportSelection() ||
+ rDoc.GetChangeTrack()!=nullptr)
+ {
+ rSet.DisableItem( nWhich );
+ }
+ }
+ break;
+
+ case SID_VIEW_DATA_SOURCE_BROWSER:
+ {
+ if (!SvtModuleOptions().IsModuleInstalled(SvtModuleOptions::EModule::DATABASE))
+ rSet.Put(SfxVisibilityItem(nWhich, false));
+ else
+ // get state (BoolItem) from SfxViewFrame
+ pTabViewShell->GetViewFrame().GetSlotState( nWhich, nullptr, &rSet );
+ }
+ break;
+ case SID_SBA_BRW_INSERT:
+ {
+ // SBA wants a sal_Bool-item, enabled
+
+ rSet.Put(SfxBoolItem(nWhich, true));
+ }
+ break;
+
+ case SID_AUTO_FILTER:
+ case SID_AUTOFILTER_HIDE:
+ {
+ if (!bAutoFilterTested)
+ {
+ bAutoFilter = rDoc.HasAutoFilter( nPosX, nPosY, nTab );
+ bAutoFilterTested = true;
+ }
+ if ( nWhich == SID_AUTO_FILTER )
+ {
+ ScRange aDummy;
+ ScMarkType eMarkType = GetViewData().GetSimpleArea( aDummy);
+ if (eMarkType != SC_MARK_SIMPLE && eMarkType != SC_MARK_SIMPLE_FILTERED)
+ {
+ rSet.DisableItem( nWhich );
+ }
+ else if (rDoc.GetDPAtBlock(aDummy))
+ {
+ rSet.DisableItem( nWhich );
+ }
+ else
+ rSet.Put( SfxBoolItem( nWhich, bAutoFilter ) );
+ }
+ else
+ if (!bAutoFilter)
+ rSet.DisableItem( nWhich );
+ }
+ break;
+
+ case SID_UNFILTER:
+ {
+ SCCOL nStartCol, nEndCol;
+ SCROW nStartRow, nEndRow;
+ SCTAB nStartTab, nEndTab;
+ bool bAnyQuery = false;
+
+ bool bSelected = (GetViewData().GetSimpleArea(
+ nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab )
+ == SC_MARK_SIMPLE);
+
+ if ( bSelected )
+ {
+ if (nStartCol==nEndCol && nStartRow==nEndRow)
+ bSelected = false;
+ }
+ else
+ {
+ nStartCol = GetViewData().GetCurX();
+ nStartRow = GetViewData().GetCurY();
+ nStartTab = GetViewData().GetTabNo();
+ }
+
+ ScDBData* pDBData = bSelected
+ ? rDoc.GetDBAtArea( nStartTab, nStartCol, nStartRow, nEndCol, nEndRow )
+ : rDoc.GetDBAtCursor( nStartCol, nStartRow, nStartTab, ScDBDataPortion::AREA );
+
+ if ( pDBData )
+ {
+ ScQueryParam aParam;
+ pDBData->GetQueryParam( aParam );
+ if ( aParam.GetEntry(0).bDoQuery )
+ bAnyQuery = true;
+ }
+
+ if ( !bAnyQuery )
+ rSet.DisableItem( nWhich );
+ }
+ break;
+
+ case SID_DEFINE_DBNAME:
+ {
+ if ( pDocSh->IsDocShared() )
+ {
+ rSet.DisableItem( nWhich );
+ }
+ }
+ break;
+ case SID_DATA_PROVIDER:
+ break;
+ case SID_DATA_PROVIDER_REFRESH:
+ {
+ ScDocument& rViewDoc = GetViewData().GetDocument();
+ auto& rDataMapper = rViewDoc.GetExternalDataMapper();
+ if (rDataMapper.getDataSources().empty())
+ rSet.DisableItem(nWhich);
+ }
+ break;
+ case SID_DATA_STREAMS:
+ case SID_DATA_STREAMS_PLAY:
+ case SID_DATA_STREAMS_STOP:
+ {
+ if ( !officecfg::Office::Common::Misc::ExperimentalMode::get() )
+ rSet.DisableItem( nWhich );
+ }
+ break;
+ case SID_TEXT_TO_COLUMNS:
+ {
+ ScRange aRange;
+ if ( !lcl_GetTextToColumnsRange( rData, aRange, true ) )
+ {
+ rSet.DisableItem( nWhich );
+ }
+ }
+ break;
+ case SID_MANAGE_XML_SOURCE:
+ break;
+ }
+ nWhich = aIter.NextWhich();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/cellsh3.cxx b/sc/source/ui/view/cellsh3.cxx
new file mode 100644
index 0000000000..e6c89b6a2b
--- /dev/null
+++ b/sc/source/ui/view/cellsh3.cxx
@@ -0,0 +1,1096 @@
+/* -*- 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 .
+ */
+
+#include <scitems.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/editeng.hxx>
+#include <formula/formulahelper.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/request.hxx>
+#include <svl/stritem.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <scmod.hxx>
+#include <appoptio.hxx>
+#include <tabvwsh.hxx>
+#include <document.hxx>
+#include <sc.hrc>
+#include <reffact.hxx>
+#include <uiitems.hxx>
+#include <autoform.hxx>
+#include <cellsh.hxx>
+#include <inputhdl.hxx>
+#include <editable.hxx>
+#include <funcdesc.hxx>
+#include <markdata.hxx>
+#include <scabstdlg.hxx>
+#include <condformateasydlg.hxx>
+#include <columnspanset.hxx>
+#include <comphelper/lok.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <inputwin.hxx>
+
+#include <memory>
+
+using sc::TwipsToEvenHMM;
+
+namespace
+{
+/// Rid ourselves of unwanted " quoted json characters.
+OString escapeJSON(const OUString &aStr)
+{
+ OUString aEscaped = aStr;
+ aEscaped = aEscaped.replaceAll("\n", " ");
+ aEscaped = aEscaped.replaceAll("\"", "'");
+ return OUStringToOString(aEscaped, RTL_TEXTENCODING_UTF8);
+}
+
+void lcl_lokGetWholeFunctionList()
+{
+ const SfxViewShell* pViewShell = SfxViewShell::Current();
+ if (!(comphelper::LibreOfficeKit::isActive()
+ && pViewShell && pViewShell->isLOKMobilePhone()))
+ return;
+
+ const ScFunctionList* pFuncList = ScGlobal::GetStarCalcFunctionList();
+ sal_uInt32 nListCount = pFuncList->GetCount();
+ std::set<OUString> aFuncNameOrderedSet;
+ for(sal_uInt32 i = 0; i < nListCount; ++i)
+ {
+ const ScFuncDesc* pDesc = pFuncList->GetFunction( i );
+ if ( pDesc->mxFuncName )
+ {
+ aFuncNameOrderedSet.insert(*pDesc->mxFuncName);
+ }
+ }
+ ScFunctionMgr* pFuncManager = ScGlobal::GetStarCalcFunctionMgr();
+ if (!(pFuncManager && aFuncNameOrderedSet.size()))
+ return;
+
+ OStringBuffer aPayload(
+ "{ \"wholeList\": true, "
+ "\"categories\": [ ");
+
+ formula::FormulaHelper aHelper(pFuncManager);
+ sal_uInt32 nCategoryCount = pFuncManager->getCount();
+ for (sal_uInt32 i = 0; i < nCategoryCount; ++i)
+ {
+ OUString sCategoryName = ScFunctionMgr::GetCategoryName(i);
+ aPayload.append("{"
+ "\"name\": \""
+ + escapeJSON(sCategoryName)
+ + "\"}, ");
+ }
+ sal_Int32 nLen = aPayload.getLength();
+ aPayload[nLen - 2] = ' ';
+ aPayload[nLen - 1] = ']';
+ aPayload.append(", ");
+
+ OUString aDescFuncNameStr;
+ aPayload.append("\"functions\": [ ");
+ sal_uInt32 nCurIndex = 0;
+ for (const OUString& aFuncNameStr : aFuncNameOrderedSet)
+ {
+ aDescFuncNameStr = aFuncNameStr + "()";
+ sal_Int32 nNextFStart = 0;
+ const formula::IFunctionDescription* ppFDesc;
+ ::std::vector< OUString > aArgs;
+ OUString eqPlusFuncName = "=" + aDescFuncNameStr;
+ if ( aHelper.GetNextFunc( eqPlusFuncName, false, nNextFStart, nullptr, &ppFDesc, &aArgs ) )
+ {
+ if ( ppFDesc && !ppFDesc->getFunctionName().isEmpty() )
+ {
+ if (ppFDesc->getCategory())
+ {
+ aPayload.append("{"
+ "\"index\": "
+ + OString::number(static_cast<sal_Int64>(nCurIndex))
+ + ", "
+ "\"category\": "
+ + OString::number(static_cast<sal_Int64>(ppFDesc->getCategory()->getNumber()))
+ + ", "
+ "\"signature\": \""
+ + escapeJSON(ppFDesc->getSignature())
+ + "\", "
+ "\"description\": \""
+ + escapeJSON(ppFDesc->getDescription())
+ + "\"}, ");
+ }
+ }
+ }
+ ++nCurIndex;
+ }
+ nLen = aPayload.getLength();
+ aPayload[nLen - 2] = ' ';
+ aPayload[nLen - 1] = ']';
+ aPayload.append(" }");
+
+ OString s = aPayload.makeStringAndClear();
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CALC_FUNCTION_LIST, s);
+}
+
+} // end namespace
+
+void ScCellShell::Execute( SfxRequest& rReq )
+{
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ SfxBindings& rBindings = pTabViewShell->GetViewFrame().GetBindings();
+ ScModule* pScMod = SC_MOD();
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+ sal_uInt16 nSlot = rReq.GetSlot();
+
+ if (nSlot != SID_CURRENTCELL) // this comes with MouseButtonUp
+ pTabViewShell->HideListBox(); // Autofilter-DropDown-Listbox
+
+ if ( GetViewData().HasEditView( GetViewData().GetActivePart() ) )
+ {
+ switch ( nSlot )
+ {
+ // when opening a reference-dialog the subshell may not be switched
+ // (on closing the dialog StopEditShell is called)
+ case SID_OPENDLG_FUNCTION:
+ // inplace leads to trouble with EditShell ...
+ //! cannot always be switched ????
+ if (!pTabViewShell->GetViewFrame().GetFrame().IsInPlace())
+ pTabViewShell->SetDontSwitch(true); // do not switch off EditShell
+ [[fallthrough]];
+
+ case FID_CELL_FORMAT:
+ case SID_ENABLE_HYPHENATION:
+ case SID_DATA_SELECT:
+ case SID_OPENDLG_CONSOLIDATE:
+ case SID_OPENDLG_SOLVE:
+ case SID_OPENDLG_OPTSOLVER:
+
+ pScMod->InputEnterHandler();
+ pTabViewShell->UpdateInputHandler();
+
+ pTabViewShell->SetDontSwitch(false);
+
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ switch ( nSlot )
+ {
+ case SID_STATUS_SELMODE:
+ if ( pReqArgs )
+ {
+ /* 0: STD Click cancels selection
+ * 1: ER Click extends selection
+ * 2: ERG Click defines further selection
+ */
+ sal_uInt16 nMode = static_cast<const SfxUInt16Item&>(pReqArgs->Get( nSlot )).GetValue();
+
+ switch ( nMode )
+ {
+ case 1: nMode = KEY_SHIFT; break;
+ case 2: nMode = KEY_MOD1; break; // control-key
+ case 0:
+ default:
+ nMode = 0;
+ }
+
+ pTabViewShell->LockModifiers( nMode );
+ }
+ else
+ {
+ // no arguments (also executed by double click on the status bar controller):
+ // advance to next selection mode
+
+ sal_uInt16 nModifiers = pTabViewShell->GetLockedModifiers();
+ switch ( nModifiers )
+ {
+ case KEY_SHIFT: nModifiers = KEY_MOD1; break; // EXT -> ADD
+ case KEY_MOD1: nModifiers = 0; break; // ADD -> STD
+ default: nModifiers = KEY_SHIFT; break; // STD -> EXT
+ }
+ pTabViewShell->LockModifiers( nModifiers );
+ }
+
+ rBindings.Invalidate( SID_STATUS_SELMODE );
+ rReq.Done();
+ break;
+
+ // SID_STATUS_SELMODE_NORM is not used ???
+
+ case SID_STATUS_SELMODE_NORM:
+ pTabViewShell->LockModifiers( 0 );
+ rBindings.Invalidate( SID_STATUS_SELMODE );
+ break;
+
+ // SID_STATUS_SELMODE_ERG / SID_STATUS_SELMODE_ERW as toggles:
+
+ case SID_STATUS_SELMODE_ERG:
+ if ( pTabViewShell->GetLockedModifiers() & KEY_MOD1 )
+ pTabViewShell->LockModifiers( 0 );
+ else
+ pTabViewShell->LockModifiers( KEY_MOD1 );
+ rBindings.Invalidate( SID_STATUS_SELMODE );
+ break;
+
+ case SID_STATUS_SELMODE_ERW:
+ if ( pTabViewShell->GetLockedModifiers() & KEY_SHIFT )
+ pTabViewShell->LockModifiers( 0 );
+ else
+ pTabViewShell->LockModifiers( KEY_SHIFT );
+ rBindings.Invalidate( SID_STATUS_SELMODE );
+ break;
+
+ case SID_ENTER_STRING:
+ {
+ if ( pReqArgs )
+ {
+ // In the LOK case, we want to set the document modified state
+ // right away at the start of the edit, so that the content is
+ // saved even when the user leaves the document before hitting
+ // Enter
+ // NOTE: This also means we want to set the modified state
+ // regardless of the DontCommit parameter's value.
+ if (comphelper::LibreOfficeKit::isActive() && !GetViewData().GetDocShell()->IsModified())
+ {
+ GetViewData().GetDocShell()->SetModified();
+ rBindings.Invalidate(SID_SAVEDOC);
+ rBindings.Invalidate(SID_DOC_MODIFIED);
+ }
+
+ OUString aStr( pReqArgs->Get( SID_ENTER_STRING ).GetValue() );
+ const SfxPoolItem* pDontCommitItem;
+ bool bCommit = true;
+ if (pReqArgs->HasItem(FN_PARAM_1, &pDontCommitItem))
+ bCommit = !(static_cast<const SfxBoolItem*>(pDontCommitItem)->GetValue());
+
+ ScInputHandler* pHdl = SC_MOD()->GetInputHdl( pTabViewShell );
+ if (bCommit)
+ {
+ pTabViewShell->EnterData( GetViewData().GetCurX(),
+ GetViewData().GetCurY(),
+ GetViewData().GetTabNo(),
+ aStr, nullptr,
+ true /*bMatrixExpand*/);
+ }
+ else if (pHdl)
+ {
+ SC_MOD()->SetInputMode(SC_INPUT_TABLE);
+
+ EditView* pTableView = pHdl->GetActiveView();
+ pHdl->DataChanging();
+ if (pTableView)
+ pTableView->GetEditEngine()->SetText(aStr);
+ pHdl->DataChanged();
+
+ SC_MOD()->SetInputMode(SC_INPUT_NONE);
+ }
+
+ if ( !pHdl || !pHdl->IsInEnterHandler() )
+ {
+ // UpdateInputHandler is needed after the cell content
+ // has changed, but if called from EnterHandler, UpdateInputHandler
+ // will be called later when moving the cursor.
+ pTabViewShell->UpdateInputHandler();
+ }
+
+ rReq.Done();
+
+ // no GrabFocus here, as otherwise on a Mac the tab jumps before the
+ // sideview, when the input was not finished
+ // (GrabFocus is called in KillEditView)
+ }
+ }
+ break;
+
+ case SID_INSERT_MATRIX:
+ {
+ if ( pReqArgs )
+ {
+ OUString aStr = static_cast<const SfxStringItem&>(pReqArgs->
+ Get( SID_INSERT_MATRIX )).GetValue();
+ ScDocument& rDoc = GetViewData().GetDocument();
+ pTabViewShell->EnterMatrix( aStr, rDoc.GetGrammar() );
+ rReq.Done();
+ }
+ }
+ break;
+
+ case FID_INPUTLINE_ENTER:
+ case FID_INPUTLINE_BLOCK:
+ case FID_INPUTLINE_MATRIX:
+ {
+ if( pReqArgs == nullptr ) //XXX temporary HACK to avoid GPF
+ break;
+
+ const ScInputStatusItem* pStatusItem
+ = static_cast<const ScInputStatusItem*>(&pReqArgs->
+ Get( FID_INPUTLINE_STATUS ));
+
+ const ScAddress& aCursorPos = pStatusItem->GetPos();
+ const OUString& aString = pStatusItem->GetString();
+ const EditTextObject* pData = pStatusItem->GetEditData();
+
+ if (pData)
+ {
+ if (nSlot == FID_INPUTLINE_BLOCK)
+ {
+ pTabViewShell->EnterBlock( aString, pData );
+ }
+ else if ( !aString.isEmpty() && ( aString[0] == '=' || aString[0] == '+' || aString[0] == '-' ) )
+ {
+ pTabViewShell->EnterData( aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab(),
+ aString, pData, true /*bMatrixExpand*/);
+ }
+ else
+ {
+ pTabViewShell->EnterData(aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab(), *pData);
+ }
+ }
+ else
+ {
+ if (nSlot == FID_INPUTLINE_ENTER)
+ {
+ if (
+ aCursorPos.Col() == GetViewData().GetCurX() &&
+ aCursorPos.Row() == GetViewData().GetCurY() &&
+ aCursorPos.Tab() == GetViewData().GetTabNo()
+ )
+ {
+ SfxStringItem aItem( SID_ENTER_STRING, aString );
+
+ const SfxPoolItem* aArgs[2];
+ aArgs[0] = &aItem;
+ aArgs[1] = nullptr;
+ rBindings.Execute( SID_ENTER_STRING, aArgs );
+ }
+ else
+ {
+ pTabViewShell->EnterData( aCursorPos.Col(),
+ aCursorPos.Row(),
+ aCursorPos.Tab(),
+ aString, nullptr,
+ true /*bMatrixExpand*/);
+ rReq.Done();
+ }
+ }
+ else if (nSlot == FID_INPUTLINE_BLOCK)
+ {
+ pTabViewShell->EnterBlock( aString, nullptr );
+ rReq.Done();
+ }
+ else
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+ pTabViewShell->EnterMatrix( aString, rDoc.GetGrammar() );
+ rReq.Done();
+ }
+ }
+
+ pTabViewShell->SetAutoSpellData(
+ aCursorPos.Col(), aCursorPos.Row(), pStatusItem->GetMisspellRanges());
+
+ // no GrabFocus here, as otherwise on a Mac the tab jumps before the
+ // sideview, when the input was not finished
+ // (GrabFocus is called in KillEditView)
+ }
+ break;
+
+ case SID_OPENDLG_FUNCTION:
+ {
+ const SfxViewShell* pViewShell = SfxViewShell::Current();
+ if (comphelper::LibreOfficeKit::isActive()
+ && pViewShell && pViewShell->isLOKMobilePhone())
+ {
+ // not set the dialog id in the mobile case or we would
+ // not be able to get cell address pasted in the edit view
+ // by just tapping on them
+ lcl_lokGetWholeFunctionList();
+ }
+ else
+ {
+ sal_uInt16 nId = SID_OPENDLG_FUNCTION;
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+ bool bVis = comphelper::LibreOfficeKit::isActive() || pWnd == nullptr;
+ pScMod->SetRefDialog( nId, bVis );
+ }
+ rReq.Ignore();
+ }
+ break;
+
+ case SID_OPENDLG_CONSOLIDATE:
+ {
+ sal_uInt16 nId = ScConsolidateDlgWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ }
+ break;
+
+ case SID_EASY_CONDITIONAL_FORMAT_DIALOG:
+ {
+ if (pReqArgs != nullptr)
+ {
+ const SfxPoolItem* pFormat;
+ if (pReqArgs->HasItem( FN_PARAM_1, &pFormat))
+ {
+ sal_Int16 nFormat = static_cast<const SfxInt16Item*>(pFormat)->GetValue();
+ sal_uInt16 nId = sc::ConditionalFormatEasyDialogWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrame = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWindow = rViewFrame.GetChildWindow( nId );
+ GetViewData().GetDocument().SetEasyConditionalFormatDialogData(std::make_unique<ScConditionMode>(static_cast<ScConditionMode>(nFormat)));
+
+ pScMod->SetRefDialog( nId, pWindow == nullptr );
+ }
+ }
+ }
+ break;
+
+ case FID_CELL_FORMAT:
+ {
+ if ( pReqArgs != nullptr )
+ {
+
+ // set cell attribute without dialog:
+
+ SfxItemSetFixed<ATTR_PATTERN_START, ATTR_PATTERN_END> aEmptySet( *pReqArgs->GetPool() );
+
+ SfxItemSetFixed<ATTR_PATTERN_START, ATTR_PATTERN_END> aNewSet( *pReqArgs->GetPool() );
+
+ const SfxPoolItem* pAttr = nullptr;
+ sal_uInt16 nWhich = 0;
+
+ for ( nWhich=ATTR_PATTERN_START; nWhich<=ATTR_PATTERN_END; nWhich++ )
+ if ( pReqArgs->GetItemState( nWhich, true, &pAttr ) == SfxItemState::SET )
+ aNewSet.Put( *pAttr );
+
+ pTabViewShell->ApplyAttributes( aNewSet, aEmptySet );
+
+ rReq.Done();
+ }
+ else
+ {
+ pTabViewShell->ExecuteCellFormatDlg( rReq, "" );
+ }
+ }
+ break;
+
+ case SID_ENABLE_HYPHENATION:
+ pTabViewShell->ExecuteCellFormatDlg(rReq, "alignment");
+ break;
+
+ case SID_PROPERTY_PANEL_CELLTEXT_DLG:
+ pTabViewShell->ExecuteCellFormatDlg( rReq, "font" );
+ break;
+
+ case SID_CELL_FORMAT_BORDER:
+ pTabViewShell->ExecuteCellFormatDlg( rReq, "borders" );
+ break;
+
+ case SID_CHAR_DLG_EFFECT:
+ pTabViewShell->ExecuteCellFormatDlg( rReq, "fonteffects" );
+ break;
+
+ case SID_OPENDLG_SOLVE:
+ {
+ sal_uInt16 nId = ScSolverDlgWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ }
+ break;
+
+ case SID_OPENDLG_OPTSOLVER:
+ {
+ sal_uInt16 nId = ScOptSolverDlgWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ }
+ break;
+
+ case SID_OPENDLG_TABOP:
+ {
+ sal_uInt16 nId = ScTabOpDlgWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ }
+ break;
+
+ case SID_SCENARIOS:
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ SCTAB nTab = GetViewData().GetTabNo();
+
+ if ( rDoc.IsScenario(nTab) )
+ {
+ rMark.MarkToMulti();
+ if ( rMark.IsMultiMarked() )
+ {
+
+ bool bExtend = rReq.IsAPI();
+ if (!bExtend)
+ {
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(pTabViewShell->GetFrameWeld(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ ScResId(STR_UPDATE_SCENARIO)));
+ xQueryBox->set_default_response(RET_YES);
+ bExtend = xQueryBox->run() == RET_YES;
+ }
+
+ if (bExtend)
+ {
+ pTabViewShell->ExtendScenario();
+ rReq.Done();
+ }
+ }
+ else if( ! rReq.IsAPI() )
+ {
+ std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(pTabViewShell->GetFrameWeld(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ ScResId(STR_NOAREASELECTED)));
+ xErrorBox->run();
+ }
+ }
+ else
+ {
+ rMark.MarkToMulti();
+ if ( rMark.IsMultiMarked() )
+ {
+ SCTAB i=1;
+ OUString aBaseName;
+ OUString aName;
+ Color aColor;
+ ScScenarioFlags nFlags;
+
+ OUString aTmp;
+ rDoc.GetName(nTab, aTmp);
+ aBaseName = aTmp + "_" + ScResId(STR_SCENARIO) + "_";
+
+ // first test, if the prefix is recognised as valid,
+ // else avoid only doubles
+ bool bPrefix = ScDocument::ValidTabName( aBaseName );
+ OSL_ENSURE(bPrefix, "invalid sheet name");
+
+ while ( rDoc.IsScenario(nTab+i) )
+ i++;
+
+ bool bValid;
+ SCTAB nDummy;
+ do
+ {
+ aName = aBaseName + OUString::number( i );
+ if (bPrefix)
+ bValid = rDoc.ValidNewTabName( aName );
+ else
+ bValid = !rDoc.GetTable( aName, nDummy );
+ ++i;
+ }
+ while ( !bValid && i <= MAXTAB + 2 );
+
+ if ( pReqArgs != nullptr )
+ {
+ OUString aArgName;
+ OUString aArgComment;
+ if ( const SfxStringItem* pItem = pReqArgs->GetItemIfSet( SID_SCENARIOS ) )
+ aArgName = pItem->GetValue();
+ if ( const SfxStringItem* pItem = pReqArgs->GetItemIfSet( SID_NEW_TABLENAME ) )
+ aArgComment = pItem->GetValue();
+
+ aColor = COL_LIGHTGRAY; // Default
+ nFlags = ScScenarioFlags::NONE; // not TwoWay
+
+ pTabViewShell->MakeScenario( aArgName, aArgComment, aColor, nFlags );
+ if( ! rReq.IsAPI() )
+ rReq.Done();
+ }
+ else
+ {
+ bool bSheetProtected = rDoc.IsTabProtected(nTab);
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScNewScenarioDlg> pNewDlg(pFact->CreateScNewScenarioDlg(pTabViewShell->GetFrameWeld(), aName, false, bSheetProtected));
+ if ( pNewDlg->Execute() == RET_OK )
+ {
+ OUString aComment;
+ pNewDlg->GetScenarioData( aName, aComment, aColor, nFlags );
+ pTabViewShell->MakeScenario( aName, aComment, aColor, nFlags );
+
+ rReq.AppendItem( SfxStringItem( SID_SCENARIOS, aName ) );
+ rReq.AppendItem( SfxStringItem( SID_NEW_TABLENAME, aComment ) );
+ rReq.Done();
+ }
+ }
+ }
+ else if( ! rReq.IsAPI() )
+ {
+ pTabViewShell->ErrorMessage(STR_ERR_NEWSCENARIO);
+ }
+ }
+ }
+ break;
+
+ case SID_SELECTALL:
+ {
+ pTabViewShell->SelectAll();
+ rReq.Done();
+ }
+ break;
+
+ case FID_ROW_HEIGHT:
+ {
+ const SfxPoolItem* pRow;
+ const SfxUInt16Item* pHeight;
+ sal_uInt16 nHeight;
+
+ if ( pReqArgs && (pHeight = pReqArgs->GetItemIfSet( FID_ROW_HEIGHT )) &&
+ pReqArgs->HasItem( FN_PARAM_1, &pRow ) )
+ {
+ std::vector<sc::ColRowSpan> aRanges;
+ SCCOLROW nRow = static_cast<const SfxInt32Item*>(pRow)->GetValue() - 1;
+ nHeight = pHeight->GetValue();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+
+ if ( rMark.IsRowMarked( static_cast<SCROW>(nRow) ) )
+ {
+ aRanges = rMark.GetMarkedRowSpans();
+ }
+ else
+ {
+ aRanges.emplace_back(nRow, nRow);
+ }
+
+ pTabViewShell->SetWidthOrHeight(false, aRanges, SC_SIZE_DIRECT, o3tl::toTwips(nHeight, o3tl::Length::mm100));
+ }
+ else if ( pReqArgs && (pHeight = pReqArgs->GetItemIfSet( FID_ROW_HEIGHT )) )
+ {
+ nHeight = pHeight->GetValue();
+
+ // #101390#; the value of the macro is in HMM so use convertMm100ToTwip to convert
+ pTabViewShell->SetMarkedWidthOrHeight( false, SC_SIZE_DIRECT,
+ o3tl::toTwips(nHeight, o3tl::Length::mm100));
+ if( ! rReq.IsAPI() )
+ rReq.Done();
+ }
+ else
+ {
+ ScViewData& rData = GetViewData();
+ FieldUnit eMetric = SC_MOD()->GetAppOptions().GetAppMetric();
+ sal_uInt16 nCurHeight = rData.GetDocument().
+ GetRowHeight( rData.GetCurY(),
+ rData.GetTabNo() );
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+ VclPtr<AbstractScMetricInputDlg> pDlg(pFact->CreateScMetricInputDlg(
+ pTabViewShell->GetFrameWeld(), "RowHeightDialog", nCurHeight,
+ rData.GetDocument().GetSheetOptimalMinRowHeight(rData.GetTabNo()),
+ eMetric, 2, MAX_ROW_HEIGHT));
+
+ pDlg->StartExecuteAsync([pDlg, pTabViewShell](sal_Int32 nResult){
+ if (nResult == RET_OK)
+ {
+ SfxRequest pRequest(pTabViewShell->GetViewFrame(), FID_ROW_HEIGHT);
+ tools::Long nVal = pDlg->GetInputValue();
+ pTabViewShell->SetMarkedWidthOrHeight( false, SC_SIZE_DIRECT, static_cast<sal_uInt16>(nVal) );
+
+ // #101390#; the value of the macro should be in HMM so use TwipsToEvenHMM to convert
+ pRequest.AppendItem( SfxUInt16Item( FID_ROW_HEIGHT, static_cast<sal_uInt16>(TwipsToEvenHMM(nVal)) ) );
+ pRequest.Done();
+ }
+ pDlg->disposeOnce();
+ });
+ }
+ }
+ break;
+
+ case FID_ROW_OPT_HEIGHT:
+ {
+ if ( pReqArgs )
+ {
+ const SfxUInt16Item& rUInt16Item = pReqArgs->Get( FID_ROW_OPT_HEIGHT );
+
+ // #101390#; the value of the macro is in HMM so use convertMm100ToTwip to convert
+ pTabViewShell->SetMarkedWidthOrHeight( false, SC_SIZE_OPTIMAL,
+ o3tl::toTwips(rUInt16Item.GetValue(), o3tl::Length::mm100) );
+ ScGlobal::nLastRowHeightExtra = rUInt16Item.GetValue();
+
+ if( ! rReq.IsAPI() )
+ rReq.Done();
+ }
+ else
+ {
+ FieldUnit eMetric = SC_MOD()->GetAppOptions().GetAppMetric();
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+ VclPtr<AbstractScMetricInputDlg> pDlg(pFact->CreateScMetricInputDlg(
+ pTabViewShell->GetFrameWeld(), "OptimalRowHeightDialog",
+ ScGlobal::nLastRowHeightExtra, 0, eMetric, 2, MAX_EXTRA_HEIGHT));
+
+ pDlg->StartExecuteAsync([pDlg, pTabViewShell](sal_Int32 nResult){
+ if ( nResult == RET_OK )
+ {
+ SfxRequest pRequest(pTabViewShell->GetViewFrame(), FID_ROW_OPT_HEIGHT);
+ tools::Long nVal = pDlg->GetInputValue();
+ pTabViewShell->SetMarkedWidthOrHeight( false, SC_SIZE_OPTIMAL, static_cast<sal_uInt16>(nVal) );
+ ScGlobal::nLastRowHeightExtra = nVal;
+
+ // #101390#; the value of the macro should be in HMM so use TwipsToEvenHMM to convert
+ pRequest.AppendItem( SfxUInt16Item( FID_ROW_OPT_HEIGHT, static_cast<sal_uInt16>(TwipsToEvenHMM(nVal)) ) );
+ pRequest.Done();
+ }
+ pDlg->disposeOnce();
+ });
+ }
+ }
+ break;
+
+ case FID_COL_WIDTH:
+ {
+ const SfxPoolItem* pColumn;
+ const SfxUInt16Item* pWidth;
+ sal_uInt16 nWidth;
+
+ if ( pReqArgs && (pWidth = pReqArgs->GetItemIfSet( FID_COL_WIDTH )) &&
+ pReqArgs->HasItem( FN_PARAM_1, &pColumn ) )
+ {
+ std::vector<sc::ColRowSpan> aRanges;
+ SCCOLROW nColumn = static_cast<const SfxUInt16Item*>(pColumn)->GetValue() - 1;
+ nWidth = pWidth->GetValue();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+
+ if ( rMark.IsColumnMarked( static_cast<SCCOL>(nColumn) ) )
+ {
+ aRanges = rMark.GetMarkedColSpans();
+ }
+ else
+ {
+ aRanges.emplace_back(nColumn, nColumn);
+ }
+
+ pTabViewShell->SetWidthOrHeight(true, aRanges, SC_SIZE_DIRECT, o3tl::toTwips(nWidth, o3tl::Length::mm100));
+ }
+ else if ( pReqArgs && (pWidth = pReqArgs->GetItemIfSet( FID_COL_WIDTH )) )
+ {
+ nWidth = pWidth->GetValue();
+
+ // #101390#; the value of the macro is in HMM so use convertMm100ToTwip to convert
+ pTabViewShell->SetMarkedWidthOrHeight( true, SC_SIZE_DIRECT,
+ o3tl::toTwips(nWidth, o3tl::Length::mm100));
+ if( ! rReq.IsAPI() )
+ rReq.Done();
+ }
+ else
+ {
+ FieldUnit eMetric = SC_MOD()->GetAppOptions().GetAppMetric();
+ ScViewData& rData = GetViewData();
+ sal_uInt16 nCurHeight = rData.GetDocument().
+ GetColWidth( rData.GetCurX(),
+ rData.GetTabNo() );
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+ VclPtr<AbstractScMetricInputDlg> pDlg(pFact->CreateScMetricInputDlg(
+ pTabViewShell->GetFrameWeld(), "ColWidthDialog", nCurHeight,
+ STD_COL_WIDTH, eMetric, 2, MAX_COL_WIDTH));
+
+ pDlg->StartExecuteAsync([pDlg, pTabViewShell](sal_Int32 nResult){
+ if ( nResult == RET_OK )
+ {
+ SfxRequest pRequest(pTabViewShell->GetViewFrame(), FID_COL_WIDTH);
+ tools::Long nVal = pDlg->GetInputValue();
+ pTabViewShell->SetMarkedWidthOrHeight( true, SC_SIZE_DIRECT, static_cast<sal_uInt16>(nVal) );
+
+ // #101390#; the value of the macro should be in HMM so use TwipsToEvenHMM to convert
+ pRequest.AppendItem( SfxUInt16Item( FID_COL_WIDTH, static_cast<sal_uInt16>(TwipsToEvenHMM(nVal))) );
+ pRequest.Done();
+ }
+ pDlg->disposeOnce();
+ });
+ }
+ }
+ break;
+
+ case FID_COL_OPT_WIDTH:
+ {
+ if ( pReqArgs )
+ {
+ const SfxUInt16Item& rUInt16Item = pReqArgs->Get( FID_COL_OPT_WIDTH );
+
+ // #101390#; the value of the macro is in HMM so use convertMm100ToTwip to convert
+ pTabViewShell->SetMarkedWidthOrHeight( true, SC_SIZE_OPTIMAL,
+ o3tl::toTwips(rUInt16Item.GetValue(), o3tl::Length::mm100) );
+ ScGlobal::nLastColWidthExtra = rUInt16Item.GetValue();
+
+ if( ! rReq.IsAPI() )
+ rReq.Done();
+ }
+ else
+ {
+ FieldUnit eMetric = SC_MOD()->GetAppOptions().GetAppMetric();
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+ VclPtr<AbstractScMetricInputDlg> pDlg(pFact->CreateScMetricInputDlg(
+ pTabViewShell->GetFrameWeld(), "OptimalColWidthDialog",
+ ScGlobal::nLastColWidthExtra, STD_EXTRA_WIDTH, eMetric, 2, MAX_EXTRA_WIDTH));
+
+ pDlg->StartExecuteAsync([pDlg, pTabViewShell](sal_Int32 nResult){
+ SfxRequest pRequest(pTabViewShell->GetViewFrame(), FID_COL_OPT_WIDTH);
+ if ( nResult == RET_OK )
+ {
+ tools::Long nVal = pDlg->GetInputValue();
+ pTabViewShell->SetMarkedWidthOrHeight( true, SC_SIZE_OPTIMAL, static_cast<sal_uInt16>(nVal) );
+ ScGlobal::nLastColWidthExtra = nVal;
+
+ // #101390#; the value of the macro should be in HMM so use TwipsToEvenHMM to convert
+ pRequest.AppendItem( SfxUInt16Item( FID_COL_OPT_WIDTH, static_cast<sal_uInt16>(TwipsToEvenHMM(nVal)) ) );
+ pRequest.Done();
+ }
+ pDlg->disposeOnce();
+ });
+ }
+ }
+ break;
+
+ case FID_COL_OPT_DIRECT:
+ pTabViewShell->SetMarkedWidthOrHeight( true, SC_SIZE_OPTIMAL, STD_EXTRA_WIDTH );
+ rReq.Done();
+ break;
+
+ case FID_ROW_HIDE:
+ pTabViewShell->SetMarkedWidthOrHeight( false, SC_SIZE_DIRECT, 0 );
+ rReq.Done();
+ break;
+ case FID_ROW_SHOW:
+ pTabViewShell->SetMarkedWidthOrHeight( false, SC_SIZE_SHOW, 0 );
+ rReq.Done();
+ break;
+ case FID_COL_HIDE:
+ pTabViewShell->SetMarkedWidthOrHeight( true, SC_SIZE_DIRECT, 0 );
+ rReq.Done();
+ break;
+ case FID_COL_SHOW:
+ pTabViewShell->SetMarkedWidthOrHeight( true, SC_SIZE_SHOW, 0 );
+ rReq.Done();
+ break;
+
+ case SID_CELL_FORMAT_RESET:
+ {
+ pTabViewShell->DeleteContents( InsertDeleteFlags::HARDATTR | InsertDeleteFlags::EDITATTR );
+ rReq.Done();
+ }
+ break;
+
+ case FID_MERGE_ON:
+ case FID_MERGE_OFF:
+ case FID_MERGE_TOGGLE:
+ {
+ if ( !GetViewData().GetDocument().GetChangeTrack() )
+ {
+ // test whether to merge or to split
+ bool bMerge = false;
+ bool bCenter = false;
+ switch( nSlot )
+ {
+ case FID_MERGE_ON:
+ bMerge = true;
+ break;
+ case FID_MERGE_OFF:
+ bMerge = false;
+ break;
+ case FID_MERGE_TOGGLE:
+ {
+ bCenter = true;
+ std::unique_ptr<SfxPoolItem> pItem;
+ if( rBindings.QueryState( nSlot, pItem ) >= SfxItemState::DEFAULT )
+ bMerge = !static_cast< SfxBoolItem* >( pItem.get() )->GetValue();
+ }
+ break;
+ }
+
+ if( bMerge )
+ {
+ // merge - check if to move contents of covered cells
+ bool bMoveContents = false;
+ bool bApi = rReq.IsAPI();
+ const SfxPoolItem* pItem;
+ if ( pReqArgs &&
+ pReqArgs->GetItemState(nSlot, true, &pItem) == SfxItemState::SET )
+ {
+ assert(dynamic_cast<const SfxBoolItem*>( pItem) && "wrong item");
+ bMoveContents = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+ }
+
+ pTabViewShell->MergeCells( bApi, bMoveContents, bCenter, nSlot );
+ }
+ else
+ {
+ // split cells
+ if (pTabViewShell->RemoveMerge())
+ {
+ rBindings.Invalidate( nSlot );
+ rReq.Done();
+ }
+ }
+ break;
+ }
+ }
+ break;
+
+ case SID_AUTOFORMAT:
+ {
+ weld::Window* pDlgParent = pTabViewShell->GetFrameWeld();
+ SCCOL nStartCol;
+ SCROW nStartRow;
+ SCTAB nStartTab;
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ SCTAB nEndTab;
+
+ const ScMarkData& rMark = GetViewData().GetMarkData();
+ if ( !rMark.IsMarked() && !rMark.IsMultiMarked() )
+ pTabViewShell->MarkDataArea();
+
+ GetViewData().GetSimpleArea( nStartCol,nStartRow,nStartTab,
+ nEndCol,nEndRow,nEndTab );
+
+ if ( ( std::abs(nEndCol-nStartCol) > 1 )
+ && ( std::abs(nEndRow-nStartRow) > 1 ) )
+ {
+ if ( pReqArgs )
+ {
+ const SfxStringItem& rNameItem = pReqArgs->Get( SID_AUTOFORMAT );
+ ScAutoFormat* pFormat = ScGlobal::GetOrCreateAutoFormat();
+ ScAutoFormat::const_iterator it = pFormat->find(rNameItem.GetValue());
+ ScAutoFormat::const_iterator itBeg = pFormat->begin();
+ size_t nIndex = std::distance(itBeg, it);
+
+ pTabViewShell->AutoFormat( nIndex );
+
+ if( ! rReq.IsAPI() )
+ rReq.Done();
+ }
+ else
+ {
+ ScGlobal::ClearAutoFormat();
+ std::unique_ptr<ScAutoFormatData> pNewEntry(pTabViewShell->CreateAutoFormatData());
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScAutoFormatDlg> pDlg(pFact->CreateScAutoFormatDlg(pDlgParent, ScGlobal::GetOrCreateAutoFormat(), pNewEntry.get(), GetViewData()));
+
+ if ( pDlg->Execute() == RET_OK )
+ {
+ ScEditableTester aTester( pTabViewShell );
+ if ( !aTester.IsEditable() )
+ {
+ pTabViewShell->ErrorMessage(aTester.GetMessageId());
+ }
+ else
+ {
+ pTabViewShell->AutoFormat( pDlg->GetIndex() );
+
+ rReq.AppendItem( SfxStringItem( SID_AUTOFORMAT, pDlg->GetCurrFormatName() ) );
+ rReq.Done();
+ }
+ }
+ }
+ }
+ else
+ {
+ std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(pDlgParent,
+ VclMessageType::Warning, VclButtonsType::Ok,
+ ScResId(STR_INVALID_AFAREA)));
+ xErrorBox->run();
+ }
+ }
+ break;
+
+ case SID_CANCEL:
+ {
+ if (GetViewData().HasEditView(GetViewData().GetActivePart()))
+ pScMod->InputCancelHandler();
+ else if (pTabViewShell->HasPaintBrush())
+ pTabViewShell->ResetBrushDocument(); // abort format paint brush
+ else if (pTabViewShell->HasHintWindow())
+ pTabViewShell->RemoveHintWindow();
+ else if( ScViewUtil::IsFullScreen( *pTabViewShell ) )
+ ScViewUtil::SetFullScreen( *pTabViewShell, false );
+ else
+ {
+ // TODO/LATER: when is this code executed?
+ pTabViewShell->Escape();
+ }
+ }
+ break;
+
+ case SID_ACCEPT_FORMULA:
+ {
+ if (GetViewData().HasEditView(GetViewData().GetActivePart()))
+ pScMod->InputEnterHandler();
+ }
+ break;
+
+ case SID_START_FORMULA:
+ {
+ ScInputHandler* pInputHandler = pScMod->GetInputHdl();
+ if (pInputHandler && pInputHandler->GetInputWindow())
+ pInputHandler->GetInputWindow()->StartFormula();
+ }
+ break;
+
+ case SID_DATA_SELECT:
+ pTabViewShell->StartDataSelect();
+ break;
+
+ case SID_DETECTIVE_FILLMODE:
+ {
+ bool bOldMode = pTabViewShell->IsAuditShell();
+ pTabViewShell->SetAuditShell( !bOldMode );
+ pTabViewShell->Invalidate( nSlot );
+ }
+ break;
+
+ case FID_INPUTLINE_STATUS:
+ OSL_FAIL("Execute from InputLine status");
+ break;
+
+ case SID_STATUS_DOCPOS:
+ // Launch navigator.
+ GetViewData().GetDispatcher().Execute(
+ SID_NAVIGATOR, SfxCallMode::SYNCHRON|SfxCallMode::RECORD );
+ break;
+
+ case SID_MARKAREA:
+ // called from Basic at the hidden view to select a range in the visible view
+ OSL_FAIL("old slot SID_MARKAREA");
+ break;
+
+ default:
+ OSL_FAIL("ScCellShell::Execute: unknown slot");
+ break;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/cellsh4.cxx b/sc/source/ui/view/cellsh4.cxx
new file mode 100644
index 0000000000..bacbf2b98f
--- /dev/null
+++ b/sc/source/ui/view/cellsh4.cxx
@@ -0,0 +1,522 @@
+/* -*- 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 .
+ */
+
+#include <sfx2/request.hxx>
+#include <osl/diagnose.h>
+
+#include <cellsh.hxx>
+#include <tabvwsh.hxx>
+#include <global.hxx>
+#include <scmod.hxx>
+#include <inputhdl.hxx>
+#include <inputwin.hxx>
+#include <document.hxx>
+#include <officecfg/Office/Calc.hxx>
+#include <sc.hrc>
+
+void ScCellShell::ExecuteCursor( SfxRequest& rReq )
+{
+ ScViewData& rData = GetViewData();
+ ScTabViewShell* pTabViewShell = rData.GetViewShell();
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+ sal_uInt16 nSlotId = rReq.GetSlot();
+ SCCOLROW nRepeat = 1;
+ bool bSel = false;
+ bool bKeep = false;
+
+ if ( pReqArgs != nullptr )
+ {
+ const SfxPoolItem* pItem;
+ if (pReqArgs->HasItem(FN_PARAM_1, &pItem))
+ nRepeat = static_cast<SCCOLROW>(static_cast<const SfxInt16Item*>(pItem)->GetValue());
+ if (pReqArgs->HasItem(FN_PARAM_2, &pItem))
+ bSel = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+ }
+ else
+ {
+ // evaluate locked selection mode
+
+ sal_uInt16 nLocked = pTabViewShell->GetLockedModifiers();
+ if ( nLocked & KEY_SHIFT )
+ bSel = true; // EXT
+ else if ( nLocked & KEY_MOD1 )
+ {
+ // ADD mode: keep the selection, start a new block when marking with shift again
+ bKeep = true;
+ }
+ }
+
+ if (bSel)
+ {
+ switch (nSlotId)
+ {
+ case SID_CURSORDOWN:
+ rReq.SetSlot(SID_CURSORDOWN_SEL);
+ break;
+ case SID_CURSORUP:
+ rReq.SetSlot(SID_CURSORUP_SEL);
+ break;
+ case SID_CURSORRIGHT:
+ rReq.SetSlot(SID_CURSORRIGHT_SEL);
+ break;
+ case SID_CURSORLEFT:
+ rReq.SetSlot(SID_CURSORLEFT_SEL);
+ break;
+ case SID_CURSORPAGEDOWN:
+ rReq.SetSlot(SID_CURSORPAGEDOWN_SEL);
+ break;
+ case SID_CURSORPAGEUP:
+ rReq.SetSlot(SID_CURSORPAGEUP_SEL);
+ break;
+ case SID_CURSORBLKDOWN:
+ rReq.SetSlot(SID_CURSORBLKDOWN_SEL);
+ break;
+ case SID_CURSORBLKUP:
+ rReq.SetSlot(SID_CURSORBLKUP_SEL);
+ break;
+ case SID_CURSORBLKRIGHT:
+ rReq.SetSlot(SID_CURSORBLKRIGHT_SEL);
+ break;
+ case SID_CURSORBLKLEFT:
+ rReq.SetSlot(SID_CURSORBLKLEFT_SEL);
+ break;
+ default:
+ ;
+ }
+ ExecuteCursorSel(rReq);
+ return;
+ }
+
+ SCCOLROW nRTLSign = 1;
+ if ( rData.GetDocument().IsLayoutRTL( rData.GetTabNo() ) )
+ {
+ //! evaluate cursor movement option?
+ nRTLSign = -1;
+ }
+
+ // once extra, so that the cursor will not be painted too often with ExecuteInputDirect:
+ pTabViewShell->HideAllCursors();
+
+ // #i123629#
+ if( pTabViewShell->GetCurObjectSelectionType() == OST_Editing )
+ pTabViewShell->SetForceFocusOnCurCell(true);
+ else
+ pTabViewShell->SetForceFocusOnCurCell(false);
+
+ // If ScrollLock key is active, cell cursor stays on the current cell while
+ // scrolling the grid.
+ bool bScrollLock = false;
+ // tdf#112876 - allow to disable for special keyboards
+ if (officecfg::Office::Calc::Input::UseScrollLock::get())
+ {
+ KeyIndicatorState eState = pFrameWin->GetIndicatorState();
+ if (eState & KeyIndicatorState::SCROLLLOCK)
+ bScrollLock = true;
+ }
+ //OS: once for all should do, however!
+ pTabViewShell->ExecuteInputDirect();
+ switch ( nSlotId )
+ {
+ case SID_CURSORDOWN:
+ if (bScrollLock)
+ pTabViewShell->ScrollY( nRepeat, SC_SPLIT_BOTTOM );
+ else
+ pTabViewShell->MoveCursorRel( 0, nRepeat, SC_FOLLOW_LINE, bSel, bKeep );
+ break;
+
+ case SID_CURSORBLKDOWN:
+ pTabViewShell->MoveCursorArea( 0, nRepeat, SC_FOLLOW_JUMP, bSel, bKeep, !rReq.IsAPI() );
+ break;
+
+ case SID_CURSORUP:
+ if (bScrollLock)
+ pTabViewShell->ScrollY( -nRepeat, SC_SPLIT_BOTTOM);
+ else
+ pTabViewShell->MoveCursorRel( 0, -nRepeat, SC_FOLLOW_LINE, bSel, bKeep );
+ break;
+
+ case SID_CURSORBLKUP:
+ pTabViewShell->MoveCursorArea( 0, -nRepeat, SC_FOLLOW_JUMP, bSel, bKeep, !rReq.IsAPI() );
+ break;
+
+ case SID_CURSORLEFT:
+ if (bScrollLock)
+ pTabViewShell->ScrollX( static_cast<SCCOL>(-nRepeat * nRTLSign), SC_SPLIT_LEFT);
+ else
+ pTabViewShell->MoveCursorRel( static_cast<SCCOL>(-nRepeat * nRTLSign), 0, SC_FOLLOW_LINE, bSel, bKeep );
+ break;
+
+ case SID_CURSORBLKLEFT:
+ pTabViewShell->MoveCursorArea( static_cast<SCCOL>(-nRepeat * nRTLSign), 0, SC_FOLLOW_JUMP, bSel, bKeep, !rReq.IsAPI() );
+ break;
+
+ case SID_CURSORRIGHT:
+ if (bScrollLock)
+ pTabViewShell->ScrollX( static_cast<SCCOL>(nRepeat * nRTLSign), SC_SPLIT_LEFT);
+ else
+ pTabViewShell->MoveCursorRel( static_cast<SCCOL>(nRepeat * nRTLSign), 0, SC_FOLLOW_LINE, bSel, bKeep );
+ break;
+
+ case SID_CURSORBLKRIGHT:
+ pTabViewShell->MoveCursorArea( static_cast<SCCOL>(nRepeat * nRTLSign), 0, SC_FOLLOW_JUMP, bSel, bKeep, !rReq.IsAPI() );
+ break;
+
+ case SID_CURSORPAGEDOWN:
+ if (bScrollLock)
+ {
+ SCCOL nPageX;
+ SCROW nPageY;
+ pTabViewShell->GetPageMoveEndPosition( 0, nRepeat, nPageX, nPageY);
+ pTabViewShell->ScrollY( nPageY, SC_SPLIT_BOTTOM);
+ }
+ else
+ pTabViewShell->MoveCursorPage( 0, nRepeat, SC_FOLLOW_FIX, bSel, bKeep );
+ break;
+
+ case SID_CURSORPAGEUP:
+ if (bScrollLock)
+ {
+ SCCOL nPageX;
+ SCROW nPageY;
+ pTabViewShell->GetPageMoveEndPosition( 0, nRepeat, nPageX, nPageY);
+ pTabViewShell->ScrollY( -nPageY, SC_SPLIT_BOTTOM);
+ }
+ else
+ pTabViewShell->MoveCursorPage( 0, -nRepeat, SC_FOLLOW_FIX, bSel, bKeep );
+ break;
+
+ case SID_CURSORPAGERIGHT_: //XXX !!!
+ if (bScrollLock)
+ {
+ SCCOL nPageX;
+ SCROW nPageY;
+ pTabViewShell->GetPageMoveEndPosition( static_cast<SCCOL>(nRepeat), 0, nPageX, nPageY);
+ pTabViewShell->ScrollX( nPageX, SC_SPLIT_LEFT);
+ }
+ else
+ pTabViewShell->MoveCursorPage( static_cast<SCCOL>(nRepeat), 0, SC_FOLLOW_FIX, bSel, bKeep );
+ break;
+
+ case SID_CURSORPAGELEFT_: //XXX !!!
+ if (bScrollLock)
+ {
+ SCCOL nPageX;
+ SCROW nPageY;
+ pTabViewShell->GetPageMoveEndPosition( static_cast<SCCOL>(nRepeat), 0, nPageX, nPageY);
+ pTabViewShell->ScrollX( -nPageX, SC_SPLIT_LEFT);
+ }
+ else
+ pTabViewShell->MoveCursorPage( static_cast<SCCOL>(-nRepeat), 0, SC_FOLLOW_FIX, bSel, bKeep );
+ break;
+
+ default:
+ OSL_FAIL("Unknown message in ViewShell (Cursor)");
+ return;
+ }
+
+ pTabViewShell->ShowAllCursors();
+
+ rReq.AppendItem( SfxInt16Item(FN_PARAM_1, static_cast<sal_Int16>(nRepeat)) );
+ rReq.AppendItem( SfxBoolItem(FN_PARAM_2, bSel) );
+ rReq.Done();
+}
+
+void ScCellShell::GetStateCursor( SAL_UNUSED_PARAMETER SfxItemSet& /* rSet */ )
+{
+}
+
+void ScCellShell::ExecuteCursorSel( SfxRequest& rReq )
+{
+ sal_uInt16 nSlotId = rReq.GetSlot();
+ ScTabViewShell* pViewShell = GetViewData().GetViewShell();
+ ScInputHandler* pInputHdl = pViewShell->GetInputHandler();
+ pViewShell->HideAllCursors();
+ if (pInputHdl && pInputHdl->IsInputMode())
+ {
+ // the current cell is in edit mode. Commit the text before moving on.
+ pViewShell->ExecuteInputDirect();
+ }
+
+ SCCOLROW nRepeat = 1;
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+ // get repetition
+ if ( pReqArgs != nullptr )
+ {
+ const SfxPoolItem* pItem;
+ if (pReqArgs->HasItem(FN_PARAM_1, &pItem))
+ nRepeat = static_cast<SCCOLROW>(static_cast<const SfxInt16Item*>(pItem)->GetValue());
+ }
+
+ SCROW nMovY = nRepeat;
+ // Horizontal direction depends on whether or not the UI language is RTL.
+ SCCOL nMovX = nRepeat;
+ if (GetViewData().GetDocument().IsLayoutRTL(GetViewData().GetTabNo()))
+ {
+ // mirror horizontal movement for right-to-left mode.
+ nMovX = -nRepeat;
+ }
+
+ switch (nSlotId)
+ {
+ case SID_CURSORDOWN_SEL:
+ pViewShell->ExpandBlock(0, nMovY, SC_FOLLOW_LINE);
+ break;
+ case SID_CURSORUP_SEL:
+ pViewShell->ExpandBlock(0, -nMovY, SC_FOLLOW_LINE);
+ break;
+ case SID_CURSORRIGHT_SEL:
+ pViewShell->ExpandBlock(nMovX, 0, SC_FOLLOW_LINE);
+ break;
+ case SID_CURSORLEFT_SEL:
+ pViewShell->ExpandBlock(-nMovX, 0, SC_FOLLOW_LINE);
+ break;
+ case SID_CURSORPAGEUP_SEL:
+ pViewShell->ExpandBlockPage(0, -nMovY);
+ break;
+ case SID_CURSORPAGEDOWN_SEL:
+ pViewShell->ExpandBlockPage(0, nMovY);
+ break;
+ case SID_CURSORPAGERIGHT_SEL:
+ pViewShell->ExpandBlockPage(nMovX, 0);
+ break;
+ case SID_CURSORPAGELEFT_SEL:
+ pViewShell->ExpandBlockPage(-nMovX, 0);
+ break;
+ case SID_CURSORBLKDOWN_SEL:
+ pViewShell->ExpandBlockArea(0, nMovY);
+ break;
+ case SID_CURSORBLKUP_SEL:
+ pViewShell->ExpandBlockArea(0, -nMovY);
+ break;
+ case SID_CURSORBLKRIGHT_SEL:
+ pViewShell->ExpandBlockArea(nMovX , 0);
+ break;
+ case SID_CURSORBLKLEFT_SEL:
+ pViewShell->ExpandBlockArea(-nMovX, 0);
+ break;
+ default:
+ ;
+ }
+ pViewShell->ShowAllCursors();
+
+ rReq.AppendItem( SfxInt16Item(FN_PARAM_1,static_cast<sal_Int16>(nRepeat)) );
+ rReq.Done();
+}
+
+void ScCellShell::ExecuteMove( SfxRequest& rReq )
+{
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ sal_uInt16 nSlotId = rReq.GetSlot();
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+
+ if(nSlotId != SID_CURSORTOPOFSCREEN && nSlotId != SID_CURSORENDOFSCREEN)
+ pTabViewShell->ExecuteInputDirect();
+ switch ( nSlotId )
+ {
+ case SID_NEXT_TABLE:
+ case SID_NEXT_TABLE_SEL:
+ pTabViewShell->SelectNextTab( 1, (nSlotId == SID_NEXT_TABLE_SEL) );
+ break;
+
+ case SID_PREV_TABLE:
+ case SID_PREV_TABLE_SEL:
+ pTabViewShell->SelectNextTab( -1, (nSlotId == SID_PREV_TABLE_SEL) );
+ break;
+
+ // cursor movements in range do not originate from Basic,
+ // because the ScSbxRange-object changes the marking at input
+
+ case SID_NEXT_UNPROTECT:
+ pTabViewShell->FindNextUnprot( false, !rReq.IsAPI() );
+ break;
+
+ case SID_PREV_UNPROTECT:
+ pTabViewShell->FindNextUnprot( true, !rReq.IsAPI() );
+ break;
+
+ case SID_CURSORENTERUP:
+ if (rReq.IsAPI())
+ pTabViewShell->MoveCursorRel( 0, -1, SC_FOLLOW_LINE, false );
+ else
+ pTabViewShell->MoveCursorEnter( true );
+ break;
+
+ case SID_CURSORENTERDOWN:
+ if (rReq.IsAPI())
+ pTabViewShell->MoveCursorRel( 0, 1, SC_FOLLOW_LINE, false );
+ else
+ pTabViewShell->MoveCursorEnter( false );
+ break;
+
+ case SID_SELECT_COL:
+ {
+ const SfxPoolItem* pColItem;
+ const SfxPoolItem* pModifierItem;
+ if ( pReqArgs && pReqArgs->HasItem( FN_PARAM_1, &pColItem ) &&
+ pReqArgs->HasItem( FN_PARAM_2, &pModifierItem ) )
+ {
+ SCCOL nCol = static_cast<SCCOL>(static_cast<const SfxInt32Item*>(pColItem)->GetValue());
+ sal_Int16 nModifier = static_cast<const SfxInt16Item*>(pModifierItem)->GetValue();
+
+ pTabViewShell->MarkColumns( nCol, nModifier );
+ }
+ else
+ pTabViewShell->MarkColumns();
+ }
+ break;
+
+ case SID_SELECT_ROW:
+ {
+ const SfxPoolItem* pRowItem;
+ const SfxPoolItem* pModifierItem;
+ if ( pReqArgs && pReqArgs->HasItem( FN_PARAM_1, &pRowItem ) &&
+ pReqArgs->HasItem( FN_PARAM_2, &pModifierItem ) )
+ {
+ SCROW nRow = static_cast<SCROW>(static_cast<const SfxInt32Item*>(pRowItem)->GetValue());
+ sal_Int16 nModifier = static_cast<const SfxInt16Item*>(pModifierItem)->GetValue();
+
+ pTabViewShell->MarkRows( nRow, nModifier );
+ }
+ else
+ pTabViewShell->MarkRows();
+ }
+ break;
+
+ case SID_SELECT_NONE:
+ pTabViewShell->Unmark();
+ break;
+
+ case SID_ALIGNCURSOR:
+ pTabViewShell->AlignToCursor( GetViewData().GetCurX(), GetViewData().GetCurY(), SC_FOLLOW_JUMP );
+ break;
+
+ case SID_MARKDATAAREA:
+ pTabViewShell->MarkDataArea();
+ break;
+
+ case SID_MARKARRAYFORMULA:
+ pTabViewShell->MarkMatrixFormula();
+ break;
+
+ case SID_SETINPUTMODE:
+ SC_MOD()->SetInputMode( SC_INPUT_TABLE );
+ break;
+
+ case SID_FOCUS_INPUTLINE:
+ {
+ ScInputHandler* pHdl = SC_MOD()->GetInputHdl( pTabViewShell );
+ if (pHdl)
+ {
+ ScInputWindow* pWin = pHdl->GetInputWindow();
+ if (pWin)
+ pWin->SwitchToTextWin();
+ }
+ }
+ break;
+
+ case SID_CURSORTOPOFSCREEN:
+ pTabViewShell->MoveCursorScreen( 0, -1, SC_FOLLOW_LINE, false );
+ break;
+
+ case SID_CURSORENDOFSCREEN:
+ pTabViewShell->MoveCursorScreen( 0, 1, SC_FOLLOW_LINE, false );
+ break;
+
+ default:
+ OSL_FAIL("Unknown message in ViewShell (Cursor)");
+ return;
+ }
+
+ rReq.Done();
+}
+
+void ScCellShell::ExecutePageSel( SfxRequest& rReq )
+{
+ sal_uInt16 nSlotId = rReq.GetSlot();
+ switch ( nSlotId )
+ {
+ case SID_CURSORHOME_SEL: rReq.SetSlot( SID_CURSORHOME ); break;
+ case SID_CURSOREND_SEL: rReq.SetSlot( SID_CURSOREND ); break;
+ case SID_CURSORTOPOFFILE_SEL: rReq.SetSlot( SID_CURSORTOPOFFILE ); break;
+ case SID_CURSORENDOFFILE_SEL: rReq.SetSlot( SID_CURSORENDOFFILE ); break;
+ default:
+ OSL_FAIL("Unknown message in ViewShell (ExecutePageSel)");
+ return;
+ }
+ rReq.AppendItem( SfxBoolItem(FN_PARAM_2, true) );
+ ExecuteSlot( rReq, GetInterface() );
+}
+
+void ScCellShell::ExecutePage( SfxRequest& rReq )
+{
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+ sal_uInt16 nSlotId = rReq.GetSlot();
+ bool bSel = false;
+ bool bKeep = false;
+
+ if ( pReqArgs != nullptr )
+ {
+ const SfxPoolItem* pItem;
+ if (pReqArgs->HasItem(FN_PARAM_2, &pItem))
+ bSel = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+ }
+ else
+ {
+ // evaluate locked selection mode
+
+ sal_uInt16 nLocked = pTabViewShell->GetLockedModifiers();
+ if ( nLocked & KEY_SHIFT )
+ bSel = true; // EXT
+ else if ( nLocked & KEY_MOD1 )
+ {
+ // ADD mode: keep the selection, start a new block when marking with shift again
+ bKeep = true;
+ }
+ }
+
+ pTabViewShell->ExecuteInputDirect();
+ switch ( nSlotId )
+ {
+ case SID_CURSORHOME:
+ pTabViewShell->MoveCursorEnd( -1, 0, SC_FOLLOW_LINE, bSel, bKeep );
+ break;
+
+ case SID_CURSOREND:
+ pTabViewShell->MoveCursorEnd( 1, 0, SC_FOLLOW_JUMP, bSel, bKeep );
+ break;
+
+ case SID_CURSORTOPOFFILE:
+ pTabViewShell->MoveCursorEnd( -1, -1, SC_FOLLOW_LINE, bSel, bKeep );
+ break;
+
+ case SID_CURSORENDOFFILE:
+ pTabViewShell->MoveCursorEnd( 1, 1, SC_FOLLOW_JUMP_END, bSel, bKeep );
+ break;
+
+ default:
+ OSL_FAIL("Unknown message in ViewShell (ExecutePage)");
+ return;
+ }
+
+ rReq.AppendItem( SfxBoolItem(FN_PARAM_2, bSel) );
+ rReq.Done();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/cliputil.cxx b/sc/source/ui/view/cliputil.cxx
new file mode 100644
index 0000000000..9c7d25db10
--- /dev/null
+++ b/sc/source/ui/view/cliputil.cxx
@@ -0,0 +1,169 @@
+/* -*- 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/.
+ */
+
+#include <cliputil.hxx>
+#include <attrib.hxx>
+#include <viewdata.hxx>
+#include <tabvwsh.hxx>
+#include <transobj.hxx>
+#include <document.hxx>
+#include <dpobject.hxx>
+#include <globstr.hrc>
+#include <clipparam.hxx>
+#include <clipoptions.hxx>
+#include <rangelst.hxx>
+#include <viewutil.hxx>
+#include <markdata.hxx>
+#include <gridwin.hxx>
+#include <scitems.hxx>
+
+#include <sfx2/classificationhelper.hxx>
+#include <comphelper/lok.hxx>
+
+namespace
+{
+
+/// Paste only if SfxClassificationHelper recommends so.
+bool lcl_checkClassification(ScDocument* pSourceDoc, const ScDocument& rDestinationDoc)
+{
+ if (!pSourceDoc)
+ return true;
+
+ ScClipOptions* pSourceOptions = pSourceDoc->GetClipOptions();
+ ScDocShell* pDestinationShell = rDestinationDoc.GetDocumentShell();
+ if (!pSourceOptions || !pDestinationShell)
+ return true;
+
+ SfxClassificationCheckPasteResult eResult = SfxClassificationHelper::CheckPaste(pSourceOptions->m_xDocumentProperties, pDestinationShell->getDocProperties());
+ return SfxClassificationHelper::ShowPasteInfo(eResult);
+}
+
+}
+
+void ScClipUtil::PasteFromClipboard( ScViewData& rViewData, ScTabViewShell* pTabViewShell, bool bShowDialog )
+{
+ const ScTransferObj* pOwnClip = ScTransferObj::GetOwnClipboard(ScTabViewShell::GetClipData(rViewData.GetActiveWin()));
+ ScDocument& rThisDoc = rViewData.GetDocument();
+ SCCOL nThisCol = rViewData.GetCurX();
+ SCROW nThisRow = rViewData.GetCurY();
+ SCTAB nThisTab = rViewData.GetTabNo();
+ ScDPObject* pDPObj = rThisDoc.GetDPAtCursor( nThisCol, nThisRow, nThisTab );
+
+ if ( pOwnClip && pDPObj )
+ {
+ // paste from Calc into DataPilot table: sort (similar to drag & drop)
+
+ ScDocument* pClipDoc = pOwnClip->GetDocument();
+ SCTAB nSourceTab = pOwnClip->GetVisibleTab();
+
+ SCCOL nClipStartX;
+ SCROW nClipStartY;
+ SCCOL nClipEndX;
+ SCROW nClipEndY;
+ pClipDoc->GetClipStart( nClipStartX, nClipStartY );
+ pClipDoc->GetClipArea( nClipEndX, nClipEndY, true );
+ nClipEndX = nClipEndX + nClipStartX;
+ nClipEndY = nClipEndY + nClipStartY; // GetClipArea returns the difference
+
+ ScRange aSource( nClipStartX, nClipStartY, nSourceTab, nClipEndX, nClipEndY, nSourceTab );
+ bool bDone = pTabViewShell->DataPilotMove( aSource, rViewData.GetCurPos() );
+ if ( !bDone )
+ pTabViewShell->ErrorMessage( STR_ERR_DATAPILOT_INPUT );
+ }
+ else
+ {
+ // normal paste
+ weld::WaitObject aWait( rViewData.GetDialogParent() );
+ if (!pOwnClip)
+ {
+ pTabViewShell->PasteFromSystem();
+ // Anchor To Cell rather than To Page
+ ScDrawView* pDrawView = pTabViewShell->GetScDrawView();
+ if(pDrawView && 1 == pDrawView->GetMarkedObjectCount())
+ {
+ SdrObject* pPickObj = pDrawView->GetMarkedObjectByIndex(0);
+ if(pPickObj)
+ {
+ ScDrawLayer::SetCellAnchoredFromPosition( *pPickObj, rThisDoc, nThisTab, false );
+ }
+ }
+ }
+ else
+ {
+ ScDocument* pClipDoc = pOwnClip->GetDocument();
+ InsertDeleteFlags nFlags = InsertDeleteFlags::ALL;
+ if (pClipDoc->GetClipParam().isMultiRange())
+ // For multi-range paste, we paste values by default.
+ nFlags &= ~InsertDeleteFlags::FORMULA;
+
+ if (lcl_checkClassification(pClipDoc, rThisDoc))
+ pTabViewShell->PasteFromClip( nFlags, pClipDoc,
+ ScPasteFunc::NONE, false, false, false, INS_NONE, InsertDeleteFlags::NONE,
+ bShowDialog ); // allow warning dialog
+ }
+ }
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ bool entireColumnOrRowSelected = false;
+ if (pOwnClip)
+ {
+ ScClipParam clipParam = pOwnClip->GetDocument()->GetClipParam();
+ if (clipParam.maRanges.size() > 0)
+ {
+ if (clipParam.maRanges[0].aEnd.Col() == pOwnClip->GetDocument()->MaxCol()
+ || clipParam.maRanges[0].aEnd.Row() == pOwnClip->GetDocument()->MaxRow())
+ {
+ entireColumnOrRowSelected = true;
+ }
+ }
+ }
+ const SfxBoolItem* pItem = rThisDoc.GetAttr(nThisCol, nThisRow, nThisTab, ATTR_LINEBREAK);
+ if (pItem->GetValue() || entireColumnOrRowSelected)
+ {
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
+ pTabViewShell, true /* bColumns */, true /* bRows */, true /* bSizes*/,
+ true /* bHidden */, true /* bFiltered */, true /* bGroups */, nThisTab);
+ }
+ }
+ pTabViewShell->CellContentChanged(); // => PasteFromSystem() ???
+}
+
+bool ScClipUtil::CheckDestRanges(
+ const ScDocument& rDoc, SCCOL nSrcCols, SCROW nSrcRows, const ScMarkData& rMark, const ScRangeList& rDest)
+{
+ for (size_t i = 0, n = rDest.size(); i < n; ++i)
+ {
+ ScRange aTest = rDest[i];
+ // Check for filtered rows in all selected sheets.
+ for (const auto& rTab : rMark)
+ {
+ aTest.aStart.SetTab(rTab);
+ aTest.aEnd.SetTab(rTab);
+ if (ScViewUtil::HasFiltered(aTest, rDoc))
+ {
+ // I don't know how to handle pasting into filtered rows yet.
+ return false;
+ }
+ }
+
+ // Destination range must be an exact multiple of the source range.
+ SCROW nRows = aTest.aEnd.Row() - aTest.aStart.Row() + 1;
+ SCCOL nCols = aTest.aEnd.Col() - aTest.aStart.Col() + 1;
+ SCROW nRowTest = (nRows / nSrcRows) * nSrcRows;
+ SCCOL nColTest = (nCols / nSrcCols) * nSrcCols;
+ if ( rDest.size() > 1 && ( nRows != nRowTest || nCols != nColTest ) )
+ {
+ // Destination range is not a multiple of the source range. Bail out.
+ return false;
+ }
+ }
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/colrowba.cxx b/sc/source/ui/view/colrowba.cxx
new file mode 100644
index 0000000000..ab9e82282e
--- /dev/null
+++ b/sc/source/ui/view/colrowba.cxx
@@ -0,0 +1,383 @@
+/* -*- 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 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <unotools/localedatawrapper.hxx>
+#include <vcl/fieldvalues.hxx>
+
+#include <colrowba.hxx>
+#include <document.hxx>
+#include <scmod.hxx>
+#include <tabvwsh.hxx>
+#include <appoptio.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <markdata.hxx>
+#include <tabview.hxx>
+#include <columnspanset.hxx>
+
+static OUString lcl_MetricString( tools::Long nTwips, std::u16string_view rText )
+{
+ if ( nTwips <= 0 )
+ return ScResId(STR_TIP_HIDE);
+ else
+ {
+ FieldUnit eUserMet = SC_MOD()->GetAppOptions().GetAppMetric();
+
+ sal_Int64 nUserVal = vcl::ConvertValue( nTwips*100, 1, 2, FieldUnit::TWIP, eUserMet );
+
+ OUString aStr = OUString::Concat(rText) + " "
+ + ScGlobal::getLocaleData().getNum( nUserVal, 2 )
+ + " " + SdrFormatter::GetUnitStr(eUserMet);
+ return aStr;
+ }
+}
+
+ScColBar::ScColBar( vcl::Window* pParent, ScHSplitPos eWhich,
+ ScHeaderFunctionSet* pFuncSet, ScHeaderSelectionEngine* pEng,
+ ScTabView* pTab ) :
+ ScHeaderControl( pParent, pEng, pTab->GetViewData().GetDocument().MaxCol()+1, false, pTab ),
+ meWhich( eWhich ),
+ mpFuncSet( pFuncSet )
+{
+ Show();
+}
+
+ScColBar::~ScColBar()
+{
+}
+
+SCCOLROW ScColBar::GetPos() const
+{
+ return pTabView->GetViewData().GetPosX(meWhich);
+}
+
+sal_uInt16 ScColBar::GetEntrySize( SCCOLROW nEntryNo ) const
+{
+ const ScViewData& rViewData = pTabView->GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ SCTAB nTab = rViewData.GetTabNo();
+ if (rDoc.ColHidden(static_cast<SCCOL>(nEntryNo), nTab))
+ return 0;
+ else
+ return static_cast<sal_uInt16>(ScViewData::ToPixel( rDoc.GetColWidth( static_cast<SCCOL>(nEntryNo), nTab ), rViewData.GetPPTX() ));
+}
+
+OUString ScColBar::GetEntryText( SCCOLROW nEntryNo ) const
+{
+ return pTabView->GetViewData().GetDocument().GetAddressConvention() == formula::FormulaGrammar::CONV_XL_R1C1
+ ? OUString::number(nEntryNo + 1)
+ : ScColToAlpha( static_cast<SCCOL>(nEntryNo) );
+}
+
+void ScColBar::SetEntrySize( SCCOLROW nPos, sal_uInt16 nNewSize )
+{
+ const ScViewData& rViewData = pTabView->GetViewData();
+ sal_uInt16 nSizeTwips;
+ ScSizeMode eMode = SC_SIZE_DIRECT;
+ if (nNewSize < 10) nNewSize = 10; // pixels
+
+ if ( nNewSize == HDR_SIZE_OPTIMUM )
+ {
+ nSizeTwips = STD_EXTRA_WIDTH;
+ eMode = SC_SIZE_OPTIMAL;
+ }
+ else
+ nSizeTwips = static_cast<sal_uInt16>( nNewSize / rViewData.GetPPTX() );
+
+ const ScMarkData& rMark = rViewData.GetMarkData();
+
+ std::vector<sc::ColRowSpan> aRanges;
+ if ( rMark.IsColumnMarked( static_cast<SCCOL>(nPos) ) )
+ {
+ ScDocument& rDoc = rViewData.GetDocument();
+ SCCOL nStart = 0;
+ while (nStart<=rDoc.MaxCol())
+ {
+ while (nStart<rDoc.MaxCol() && !rMark.IsColumnMarked(nStart))
+ ++nStart;
+ if (rMark.IsColumnMarked(nStart))
+ {
+ SCCOL nEnd = nStart;
+ while (nEnd<rDoc.MaxCol() && rMark.IsColumnMarked(nEnd))
+ ++nEnd;
+ if (!rMark.IsColumnMarked(nEnd))
+ --nEnd;
+ aRanges.emplace_back(nStart,nEnd);
+ nStart = nEnd+1;
+ }
+ else
+ nStart = rDoc.MaxCol()+1;
+ }
+ }
+ else
+ {
+ aRanges.emplace_back(nPos,nPos);
+ }
+
+ rViewData.GetView()->SetWidthOrHeight(true, aRanges, eMode, nSizeTwips);
+}
+
+void ScColBar::HideEntries( SCCOLROW nStart, SCCOLROW nEnd )
+{
+ std::vector<sc::ColRowSpan> aRanges(1, sc::ColRowSpan(nStart,nEnd));
+ pTabView->GetViewData().GetView()->SetWidthOrHeight(true, aRanges, SC_SIZE_DIRECT, 0);
+}
+
+void ScColBar::SetMarking( bool bSet )
+{
+ pTabView->GetViewData().GetMarkData().SetMarking( bSet );
+ if (!bSet)
+ {
+ pTabView->GetViewData().GetView()->UpdateAutoFillMark();
+ }
+}
+
+void ScColBar::SelectWindow()
+{
+ const ScViewData& rViewData = pTabView->GetViewData();
+ ScTabViewShell* pViewSh = rViewData.GetViewShell();
+
+ pViewSh->SetActive(); // Appear and SetViewFrame
+ pViewSh->DrawDeselectAll();
+
+ ScSplitPos eActive = rViewData.GetActivePart();
+ if (meWhich==SC_SPLIT_LEFT)
+ {
+ if (eActive==SC_SPLIT_TOPRIGHT) eActive=SC_SPLIT_TOPLEFT;
+ if (eActive==SC_SPLIT_BOTTOMRIGHT) eActive=SC_SPLIT_BOTTOMLEFT;
+ }
+ else
+ {
+ if (eActive==SC_SPLIT_TOPLEFT) eActive=SC_SPLIT_TOPRIGHT;
+ if (eActive==SC_SPLIT_BOTTOMLEFT) eActive=SC_SPLIT_BOTTOMRIGHT;
+ }
+ pViewSh->ActivatePart( eActive );
+
+ mpFuncSet->SetColumn( true );
+ mpFuncSet->SetWhich( eActive );
+
+ pViewSh->ActiveGrabFocus();
+}
+
+bool ScColBar::IsDisabled() const
+{
+ ScModule* pScMod = SC_MOD();
+ return pScMod->IsModalMode();
+}
+
+bool ScColBar::ResizeAllowed() const
+{
+ const ScViewData& rViewData = pTabView->GetViewData();
+ return !rViewData.HasEditView( rViewData.GetActivePart() );
+}
+
+void ScColBar::DrawInvert( tools::Long nDragPosP )
+{
+ tools::Rectangle aRect( nDragPosP,0, nDragPosP+HDR_SLIDERSIZE-1,GetOutputSizePixel().Width()-1 );
+ PaintImmediately();
+ GetOutDev()->Invert(aRect);
+
+ pTabView->GetViewData().GetView()->InvertVertical(meWhich,nDragPosP);
+}
+
+OUString ScColBar::GetDragHelp( tools::Long nVal )
+{
+ tools::Long nTwips = static_cast<tools::Long>( nVal / pTabView->GetViewData().GetPPTX() );
+ return lcl_MetricString( nTwips, ScResId(STR_TIP_WIDTH) );
+}
+
+bool ScColBar::IsLayoutRTL() const // override only for columns
+{
+ const ScViewData& rViewData = pTabView->GetViewData();
+ return rViewData.GetDocument().IsLayoutRTL( rViewData.GetTabNo() );
+}
+
+ScRowBar::ScRowBar( vcl::Window* pParent, ScVSplitPos eWhich,
+ ScHeaderFunctionSet* pFuncSet, ScHeaderSelectionEngine* pEng,
+ ScTabView* pTab ) :
+ ScHeaderControl( pParent, pEng, pTab->GetViewData().GetDocument().MaxRow()+1, true, pTab ),
+ meWhich( eWhich ),
+ mpFuncSet( pFuncSet )
+{
+ Show();
+}
+
+ScRowBar::~ScRowBar()
+{
+}
+
+SCCOLROW ScRowBar::GetPos() const
+{
+ return pTabView->GetViewData().GetPosY(meWhich);
+}
+
+sal_uInt16 ScRowBar::GetEntrySize( SCCOLROW nEntryNo ) const
+{
+ const ScViewData& rViewData = pTabView->GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ SCTAB nTab = rViewData.GetTabNo();
+ SCROW nLastRow = -1;
+ if (rDoc.RowHidden(nEntryNo, nTab, nullptr, &nLastRow))
+ return 0;
+ else
+ return static_cast<sal_uInt16>(ScViewData::ToPixel( rDoc.GetOriginalHeight( nEntryNo,
+ nTab ), rViewData.GetPPTY() ));
+}
+
+OUString ScRowBar::GetEntryText( SCCOLROW nEntryNo ) const
+{
+ return OUString::number( nEntryNo + 1 );
+}
+
+void ScRowBar::SetEntrySize( SCCOLROW nPos, sal_uInt16 nNewSize )
+{
+ const ScViewData& rViewData = pTabView->GetViewData();
+ sal_uInt16 nSizeTwips;
+ ScSizeMode eMode = SC_SIZE_DIRECT;
+ if (nNewSize < 10) nNewSize = 10; // pixels
+
+ if ( nNewSize == HDR_SIZE_OPTIMUM )
+ {
+ nSizeTwips = 0;
+ eMode = SC_SIZE_OPTIMAL;
+ }
+ else
+ nSizeTwips = static_cast<sal_uInt16>( nNewSize / rViewData.GetPPTY() );
+
+ const ScMarkData& rMark = rViewData.GetMarkData();
+
+ std::vector<sc::ColRowSpan> aRanges;
+ if ( rMark.IsRowMarked( nPos ) )
+ {
+ ScDocument& rDoc = rViewData.GetDocument();
+ SCROW nStart = 0;
+ while (nStart<=rDoc.MaxRow())
+ {
+ while (nStart<rDoc.MaxRow() && !rMark.IsRowMarked(nStart))
+ ++nStart;
+ if (rMark.IsRowMarked(nStart))
+ {
+ SCROW nEnd = nStart;
+ while (nEnd<rDoc.MaxRow() && rMark.IsRowMarked(nEnd))
+ ++nEnd;
+ if (!rMark.IsRowMarked(nEnd))
+ --nEnd;
+ aRanges.emplace_back(nStart,nEnd);
+ nStart = nEnd+1;
+ }
+ else
+ nStart = rDoc.MaxRow()+1;
+ }
+ }
+ else
+ {
+ aRanges.emplace_back(nPos,nPos);
+ }
+
+ rViewData.GetView()->SetWidthOrHeight(false, aRanges, eMode, nSizeTwips);
+}
+
+void ScRowBar::HideEntries( SCCOLROW nStart, SCCOLROW nEnd )
+{
+ std::vector<sc::ColRowSpan> aRange(1, sc::ColRowSpan(nStart,nEnd));
+ pTabView->GetViewData().GetView()->SetWidthOrHeight(false, aRange, SC_SIZE_DIRECT, 0);
+}
+
+void ScRowBar::SetMarking( bool bSet )
+{
+ pTabView->GetViewData().GetMarkData().SetMarking( bSet );
+ if (!bSet)
+ {
+ pTabView->GetViewData().GetView()->UpdateAutoFillMark();
+ }
+}
+
+void ScRowBar::SelectWindow()
+{
+ const ScViewData& rViewData = pTabView->GetViewData();
+ ScTabViewShell* pViewSh = rViewData.GetViewShell();
+
+ pViewSh->SetActive(); // Appear and SetViewFrame
+ pViewSh->DrawDeselectAll();
+
+ ScSplitPos eActive = rViewData.GetActivePart();
+ if (meWhich==SC_SPLIT_TOP)
+ {
+ if (eActive==SC_SPLIT_BOTTOMLEFT) eActive=SC_SPLIT_TOPLEFT;
+ if (eActive==SC_SPLIT_BOTTOMRIGHT) eActive=SC_SPLIT_TOPRIGHT;
+ }
+ else
+ {
+ if (eActive==SC_SPLIT_TOPLEFT) eActive=SC_SPLIT_BOTTOMLEFT;
+ if (eActive==SC_SPLIT_TOPRIGHT) eActive=SC_SPLIT_BOTTOMRIGHT;
+ }
+ pViewSh->ActivatePart( eActive );
+
+ mpFuncSet->SetColumn( false );
+ mpFuncSet->SetWhich( eActive );
+
+ pViewSh->ActiveGrabFocus();
+}
+
+bool ScRowBar::IsDisabled() const
+{
+ ScModule* pScMod = SC_MOD();
+ return pScMod->IsModalMode();
+}
+
+bool ScRowBar::ResizeAllowed() const
+{
+ const ScViewData& rViewData = pTabView->GetViewData();
+ return !rViewData.HasEditView( rViewData.GetActivePart() );
+}
+
+void ScRowBar::DrawInvert( tools::Long nDragPosP )
+{
+ tools::Rectangle aRect( 0,nDragPosP, GetOutputSizePixel().Width()-1,nDragPosP+HDR_SLIDERSIZE-1 );
+ PaintImmediately();
+ GetOutDev()->Invert(aRect);
+
+ pTabView->GetViewData().GetView()->InvertHorizontal(meWhich,nDragPosP);
+}
+
+OUString ScRowBar::GetDragHelp( tools::Long nVal )
+{
+ tools::Long nTwips = static_cast<tools::Long>( nVal / pTabView->GetViewData().GetPPTY() );
+ return lcl_MetricString( nTwips, ScResId(STR_TIP_HEIGHT) );
+}
+
+SCCOLROW ScRowBar::GetHiddenCount( SCCOLROW nEntryNo ) const // override only for rows
+{
+ const ScViewData& rViewData = pTabView->GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ SCTAB nTab = rViewData.GetTabNo();
+ return rDoc.GetHiddenRowCount( nEntryNo, nTab );
+}
+
+bool ScRowBar::IsMirrored() const // override only for rows
+{
+ const ScViewData& rViewData = pTabView->GetViewData();
+ return rViewData.GetDocument().IsLayoutRTL( rViewData.GetTabNo() );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/dbfunc.cxx b/sc/source/ui/view/dbfunc.cxx
new file mode 100644
index 0000000000..8f1b9e8fc5
--- /dev/null
+++ b/sc/source/ui/view/dbfunc.cxx
@@ -0,0 +1,466 @@
+/* -*- 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 .
+ */
+
+#include <scitems.hxx>
+#include <sfx2/bindings.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <unotools/charclass.hxx>
+#include <osl/diagnose.h>
+
+#include <dbfunc.hxx>
+#include <docsh.hxx>
+#include <attrib.hxx>
+#include <sc.hrc>
+#include <undodat.hxx>
+#include <dbdata.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <global.hxx>
+#include <dbdocfun.hxx>
+#include <editable.hxx>
+#include <queryentry.hxx>
+#include <markdata.hxx>
+#include <tabvwsh.hxx>
+#include <sortparam.hxx>
+
+ScDBFunc::ScDBFunc( vcl::Window* pParent, ScDocShell& rDocSh, ScTabViewShell* pViewShell ) :
+ ScViewFunc( pParent, rDocSh, pViewShell )
+{
+}
+
+ScDBFunc::~ScDBFunc()
+{
+}
+
+// auxiliary functions
+
+void ScDBFunc::GotoDBArea( const OUString& rDBName )
+{
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScDBCollection* pDBCol = rDoc.GetDBCollection();
+ ScDBData* pData = pDBCol->getNamedDBs().findByUpperName(ScGlobal::getCharClass().uppercase(rDBName));
+ if (!pData)
+ return;
+
+ SCTAB nTab = 0;
+ SCCOL nStartCol = 0;
+ SCROW nStartRow = 0;
+ SCCOL nEndCol = 0;
+ SCROW nEndRow = 0;
+
+ pData->GetArea( nTab, nStartCol, nStartRow, nEndCol, nEndRow );
+ SetTabNo( nTab );
+
+ MoveCursorAbs( nStartCol, nStartRow, SC_FOLLOW_JUMP,
+ false, false ); // bShift,bControl
+ DoneBlockMode();
+ InitBlockMode( nStartCol, nStartRow, nTab );
+ MarkCursor( nEndCol, nEndRow, nTab );
+ SelectionChanged();
+}
+
+// search current datarange for sort / filter
+
+ScDBData* ScDBFunc::GetDBData( bool bMark, ScGetDBMode eMode, ScGetDBSelection eSel )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDBData* pData = nullptr;
+ ScRange aRange;
+ ScMarkType eMarkType = GetViewData().GetSimpleArea(aRange);
+ if ( eMarkType == SC_MARK_SIMPLE || eMarkType == SC_MARK_SIMPLE_FILTERED )
+ {
+ bool bShrinkColumnsOnly = false;
+ if (eSel == ScGetDBSelection::RowDown)
+ {
+ // Don't alter row range, additional rows may have been selected on
+ // purpose to append data, or to have a fake header row.
+ bShrinkColumnsOnly = true;
+ // Select further rows only if only one row or a portion thereof is
+ // selected.
+ if (aRange.aStart.Row() != aRange.aEnd.Row())
+ {
+ // If an area is selected shrink that to the actual used
+ // columns, don't draw filter buttons for empty columns.
+ eSel = ScGetDBSelection::ShrinkToUsedData;
+ }
+ else if (aRange.aStart.Col() == aRange.aEnd.Col())
+ {
+ // One cell only, if it is not marked obtain entire used data
+ // area.
+ const ScMarkData& rMarkData = GetViewData().GetMarkData();
+ if (!(rMarkData.IsMarked() || rMarkData.IsMultiMarked()))
+ eSel = ScGetDBSelection::Keep;
+ }
+ }
+ switch (eSel)
+ {
+ case ScGetDBSelection::ShrinkToUsedData:
+ case ScGetDBSelection::RowDown:
+ {
+ // Shrink the selection to actual used area.
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCCOL nCol1 = aRange.aStart.Col(), nCol2 = aRange.aEnd.Col();
+ SCROW nRow1 = aRange.aStart.Row(), nRow2 = aRange.aEnd.Row();
+ bool bShrunk;
+ rDoc.ShrinkToUsedDataArea( bShrunk, aRange.aStart.Tab(),
+ nCol1, nRow1, nCol2, nRow2, bShrinkColumnsOnly);
+ if (bShrunk)
+ {
+ aRange.aStart.SetCol(nCol1);
+ aRange.aEnd.SetCol(nCol2);
+ aRange.aStart.SetRow(nRow1);
+ aRange.aEnd.SetRow(nRow2);
+ }
+ }
+ break;
+ default:
+ ; // nothing
+ }
+ pData = pDocSh->GetDBData( aRange, eMode, eSel );
+ }
+ else if ( eMode != SC_DB_OLD )
+ pData = pDocSh->GetDBData(
+ ScRange( GetViewData().GetCurX(), GetViewData().GetCurY(),
+ GetViewData().GetTabNo() ),
+ eMode, ScGetDBSelection::Keep );
+
+ if (!pData)
+ return nullptr;
+
+ if (bMark)
+ {
+ ScRange aFound;
+ pData->GetArea(aFound);
+ MarkRange( aFound, false );
+ }
+ return pData;
+}
+
+ScDBData* ScDBFunc::GetAnonymousDBData()
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScRange aRange;
+ ScMarkType eMarkType = GetViewData().GetSimpleArea(aRange);
+ if (eMarkType != SC_MARK_SIMPLE && eMarkType != SC_MARK_SIMPLE_FILTERED)
+ return nullptr;
+
+ // Expand to used data area if not explicitly marked.
+ const ScMarkData& rMarkData = GetViewData().GetMarkData();
+ if (!rMarkData.IsMarked() && !rMarkData.IsMultiMarked())
+ {
+ SCCOL nCol1 = aRange.aStart.Col();
+ SCCOL nCol2 = aRange.aEnd.Col();
+ SCROW nRow1 = aRange.aStart.Row();
+ SCROW nRow2 = aRange.aEnd.Row();
+ pDocSh->GetDocument().GetDataArea(aRange.aStart.Tab(), nCol1, nRow1, nCol2, nRow2, false, false);
+ aRange.aStart.SetCol(nCol1);
+ aRange.aStart.SetRow(nRow1);
+ aRange.aEnd.SetCol(nCol2);
+ aRange.aEnd.SetRow(nRow2);
+ }
+
+ return pDocSh->GetAnonymousDBData(aRange);
+}
+
+// main functions
+
+// Sort
+
+void ScDBFunc::UISort( const ScSortParam& rSortParam )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScDBData* pDBData = rDoc.GetDBAtArea( nTab, rSortParam.nCol1, rSortParam.nRow1,
+ rSortParam.nCol2, rSortParam.nRow2 );
+ if (!pDBData)
+ {
+ OSL_FAIL( "Sort: no DBData" );
+ return;
+ }
+
+ ScSubTotalParam aSubTotalParam;
+ pDBData->GetSubTotalParam( aSubTotalParam );
+ if (aSubTotalParam.bGroupActive[0] && !aSubTotalParam.bRemoveOnly)
+ {
+ // repeat subtotals, with new sortorder
+
+ DoSubTotals( aSubTotalParam, true/*bRecord*/, &rSortParam );
+ }
+ else
+ {
+ Sort( rSortParam ); // just sort
+ }
+}
+
+void ScDBFunc::Sort( const ScSortParam& rSortParam, bool bRecord, bool bPaint )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScDBDocFunc aDBDocFunc( *pDocSh );
+ bool bSuccess = aDBDocFunc.Sort( nTab, rSortParam, bRecord, bPaint, false );
+ if ( bSuccess && !rSortParam.bInplace )
+ {
+ // mark target
+ ScRange aDestRange( rSortParam.nDestCol, rSortParam.nDestRow, rSortParam.nDestTab,
+ rSortParam.nDestCol + rSortParam.nCol2 - rSortParam.nCol1,
+ rSortParam.nDestRow + rSortParam.nRow2 - rSortParam.nRow1,
+ rSortParam.nDestTab );
+ MarkRange( aDestRange );
+ }
+
+ ResetAutoSpellForContentChange();
+}
+
+// filters
+
+void ScDBFunc::Query( const ScQueryParam& rQueryParam, const ScRange* pAdvSource, bool bRecord )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScDBDocFunc aDBDocFunc( *pDocSh );
+ bool bSuccess = aDBDocFunc.Query( nTab, rQueryParam, pAdvSource, bRecord, false );
+
+ if (!bSuccess)
+ return;
+
+ bool bCopy = !rQueryParam.bInplace;
+ if (bCopy)
+ {
+ // mark target range (data base range has been set up if applicable)
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScDBData* pDestData = rDoc.GetDBAtCursor(
+ rQueryParam.nDestCol, rQueryParam.nDestRow,
+ rQueryParam.nDestTab, ScDBDataPortion::TOP_LEFT );
+ if (pDestData)
+ {
+ ScRange aDestRange;
+ pDestData->GetArea(aDestRange);
+ MarkRange( aDestRange );
+ }
+ }
+
+ if (!bCopy)
+ {
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
+ GetViewData().GetViewShell(),
+ false /* bColumns */, true /* bRows */,
+ false /* bSizes*/, true /* bHidden */, true /* bFiltered */,
+ false /* bGroups */, nTab);
+ UpdateScrollBars(ROW_HEADER);
+ SelectionChanged(); // for attribute states (filtered rows are ignored)
+ }
+
+ GetViewData().GetBindings().Invalidate( SID_UNFILTER );
+}
+
+// autofilter-buttons show / hide
+
+void ScDBFunc::ToggleAutoFilter()
+{
+ ScViewData* pViewData = &GetViewData();
+ ScDocShell* pDocSh = pViewData->GetDocShell();
+
+ ScQueryParam aParam;
+ ScDocument& rDoc = pViewData->GetDocument();
+ ScDBData* pDBData = GetDBData(false, SC_DB_AUTOFILTER, ScGetDBSelection::RowDown);
+
+ pDBData->SetByRow( true ); //! undo, retrieve beforehand ??
+ pDBData->GetQueryParam( aParam );
+
+ SCCOL nCol;
+ SCROW nRow = aParam.nRow1;
+ SCTAB nTab = pViewData->GetTabNo();
+ ScMF nFlag;
+ bool bHasAuto = true;
+ bool bHeader = pDBData->HasHeader();
+
+ //! instead retrieve from DB-range?
+
+ for (nCol=aParam.nCol1; nCol<=aParam.nCol2 && bHasAuto; nCol++)
+ {
+ nFlag = rDoc.GetAttr( nCol, nRow, nTab, ATTR_MERGE_FLAG )->GetValue();
+
+ if ( !(nFlag & ScMF::Auto) )
+ bHasAuto = false;
+ }
+
+ if (bHasAuto) // remove
+ {
+ // hide filter buttons
+
+ for (nCol=aParam.nCol1; nCol<=aParam.nCol2; nCol++)
+ {
+ nFlag = rDoc.GetAttr( nCol, nRow, nTab, ATTR_MERGE_FLAG )->GetValue();
+ rDoc.ApplyAttr( nCol, nRow, nTab, ScMergeFlagAttr( nFlag & ~ScMF::Auto ) );
+ }
+
+ // use a list action for the AutoFilter buttons (ScUndoAutoFilter) and the filter operation
+
+ OUString aUndo = ScResId( STR_UNDO_QUERY );
+ pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, pViewData->GetViewShell()->GetViewShellId() );
+
+ ScRange aRange;
+ pDBData->GetArea( aRange );
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoAutoFilter>( pDocSh, aRange, pDBData->GetName(), false ) );
+
+ pDBData->SetAutoFilter(false);
+
+ // remove filter (incl. Paint / Undo)
+
+ SCSIZE nEC = aParam.GetEntryCount();
+ for (SCSIZE i=0; i<nEC; i++)
+ aParam.GetEntry(i).bDoQuery = false;
+ aParam.bDuplicate = true;
+ Query( aParam, nullptr, true );
+
+ pDocSh->GetUndoManager()->LeaveListAction();
+
+ ScDBFunc::ModifiedAutoFilter(pDocSh);
+ }
+ else // show filter buttons
+ {
+ if ( !rDoc.IsBlockEmpty( aParam.nCol1, aParam.nRow1,
+ aParam.nCol2, aParam.nRow2, nTab ) )
+ {
+ if (!bHeader)
+ {
+ std::shared_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pViewData->GetDialogParent(),
+ VclMessageType::Question,
+ VclButtonsType::YesNo, ScResId(STR_MSSG_MAKEAUTOFILTER_0))); // header from first row?
+ xBox->set_title(ScResId(STR_MSSG_DOSUBTOTALS_0)); // "StarCalc"
+ xBox->set_default_response(RET_YES);
+ xBox->SetInstallLOKNotifierHdl(LINK(this, ScDBFunc, InstallLOKNotifierHdl));
+ xBox->runAsync(xBox, [pDocSh, pViewData, pDBData, nCol, nRow, nTab, aParam] (sal_Int32 nResult) {
+ if (nResult == RET_YES)
+ {
+ pDBData->SetHeader( true ); //! Undo ??
+ }
+
+ ApplyAutoFilter(pDocSh, pViewData, pDBData, nCol, nRow, nTab, aParam);
+ });
+ }
+ else
+ ApplyAutoFilter(pDocSh, pViewData, pDBData, nCol, nRow, nTab, aParam);
+ }
+ else
+ {
+ std::shared_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(pViewData->GetDialogParent(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ ScResId(STR_ERR_AUTOFILTER)));
+ xErrorBox->SetInstallLOKNotifierHdl(LINK(this, ScDBFunc, InstallLOKNotifierHdl));
+ xErrorBox->runAsync(xErrorBox, [] (sal_Int32) {});
+ }
+ }
+}
+
+IMPL_STATIC_LINK_NOARG(ScDBFunc, InstallLOKNotifierHdl, void*, vcl::ILibreOfficeKitNotifier*)
+{
+ return GetpApp();
+}
+
+void ScDBFunc::ApplyAutoFilter(ScDocShell* pDocSh, ScViewData* pViewData, ScDBData* pDBData,
+ SCCOL nCol, SCROW nRow, SCTAB nTab, ScQueryParam aParam)
+{
+ ScDocument& rDoc = pViewData->GetDocument();
+ ScRange aRange;
+ pDBData->GetArea(aRange);
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoAutoFilter>(pDocSh, aRange, pDBData->GetName(), true));
+
+ pDBData->SetAutoFilter(true);
+
+ for (nCol=aParam.nCol1; nCol<=aParam.nCol2; nCol++)
+ {
+ ScMF nFlag = rDoc.GetAttr(nCol, nRow, nTab, ATTR_MERGE_FLAG)->GetValue();
+ rDoc.ApplyAttr(nCol, nRow, nTab, ScMergeFlagAttr(nFlag | ScMF::Auto));
+ }
+ pDocSh->PostPaint(ScRange(aParam.nCol1, nRow, nTab, aParam.nCol2, nRow, nTab),
+ PaintPartFlags::Grid);
+
+ ScDBFunc::ModifiedAutoFilter(pDocSh);
+}
+
+void ScDBFunc::ModifiedAutoFilter(ScDocShell* pDocSh)
+{
+ ScDocShellModificator aModificator(*pDocSh);
+ aModificator.SetDocumentModified();
+
+ SfxBindings* pBindings = pDocSh->GetViewBindings();
+ pBindings->Invalidate(SID_AUTO_FILTER);
+ pBindings->Invalidate(SID_AUTOFILTER_HIDE);
+}
+
+// just hide, no data change
+
+void ScDBFunc::HideAutoFilter()
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocShellModificator aModificator( *pDocSh );
+
+ ScDocument& rDoc = pDocSh->GetDocument();
+
+ ScDBData* pDBData = GetDBData( false );
+
+ SCTAB nTab;
+ SCCOL nCol1, nCol2;
+ SCROW nRow1, nRow2;
+ pDBData->GetArea(nTab, nCol1, nRow1, nCol2, nRow2);
+
+ for (SCCOL nCol=nCol1; nCol<=nCol2; nCol++)
+ {
+ ScMF nFlag = rDoc.GetAttr( nCol, nRow1, nTab, ATTR_MERGE_FLAG )->GetValue();
+ rDoc.ApplyAttr( nCol, nRow1, nTab, ScMergeFlagAttr( nFlag & ~ScMF::Auto ) );
+ }
+
+ ScRange aRange;
+ pDBData->GetArea( aRange );
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoAutoFilter>( pDocSh, aRange, pDBData->GetName(), false ) );
+
+ pDBData->SetAutoFilter(false);
+
+ pDocSh->PostPaint(ScRange(nCol1, nRow1, nTab, nCol2, nRow1, nTab), PaintPartFlags::Grid );
+ aModificator.SetDocumentModified();
+
+ SfxBindings& rBindings = GetViewData().GetBindings();
+ rBindings.Invalidate( SID_AUTO_FILTER );
+ rBindings.Invalidate( SID_AUTOFILTER_HIDE );
+}
+
+// Re-Import
+
+bool ScDBFunc::ImportData( const ScImportParam& rParam )
+{
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScEditableTester aTester( rDoc, GetViewData().GetTabNo(), rParam.nCol1,rParam.nRow1,
+ rParam.nCol2,rParam.nRow2 );
+ if ( !aTester.IsEditable() )
+ {
+ ErrorMessage(aTester.GetMessageId());
+ return false;
+ }
+
+ ScDBDocFunc aDBDocFunc( *GetViewData().GetDocShell() );
+ return aDBDocFunc.DoImport( GetViewData().GetTabNo(), rParam, nullptr );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/dbfunc2.cxx b/sc/source/ui/view/dbfunc2.cxx
new file mode 100644
index 0000000000..fffa16909f
--- /dev/null
+++ b/sc/source/ui/view/dbfunc2.cxx
@@ -0,0 +1,41 @@
+/* -*- 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 .
+ */
+
+#include <dbfunc.hxx>
+#include <document.hxx>
+#include <globstr.hrc>
+
+void ScDBFunc::UpdateCharts( bool bAllCharts )
+{
+ sal_uInt16 nFound = 0;
+ ScViewData& rViewData = GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+
+ if ( rDoc.GetDrawLayer() )
+ nFound = DoUpdateCharts( ScAddress( rViewData.GetCurX(),
+ rViewData.GetCurY(),
+ rViewData.GetTabNo()),
+ rDoc,
+ bAllCharts );
+
+ if ( !nFound && !bAllCharts )
+ ErrorMessage(STR_NOCHARTATCURSOR);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/dbfunc3.cxx b/sc/source/ui/view/dbfunc3.cxx
new file mode 100644
index 0000000000..9720bf7f4f
--- /dev/null
+++ b/sc/source/ui/view/dbfunc3.cxx
@@ -0,0 +1,2312 @@
+/* -*- 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 .
+ */
+
+#include <dbfunc.hxx>
+#include <scitems.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <sfx2/app.hxx>
+#include <unotools/collatorwrapper.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
+#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
+#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
+#include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
+#include <com/sun/star/sheet/DataPilotTableHeaderData.hpp>
+#include <com/sun/star/sheet/MemberResultFlags.hpp>
+#include <com/sun/star/sheet/XDimensionsSupplier.hpp>
+#include <com/sun/star/sheet/XDrillDownDataSupplier.hpp>
+
+#include <global.hxx>
+#include <scresid.hxx>
+#include <globstr.hrc>
+#include <undotab.hxx>
+#include <undodat.hxx>
+#include <dbdata.hxx>
+#include <rangenam.hxx>
+#include <docsh.hxx>
+#include <olinetab.hxx>
+#include <olinefun.hxx>
+#include <dpobject.hxx>
+#include <dpsave.hxx>
+#include <dpdimsave.hxx>
+#include <dbdocfun.hxx>
+#include <dpoutput.hxx>
+#include <editable.hxx>
+#include <docpool.hxx>
+#include <patattr.hxx>
+#include <unonames.hxx>
+#include <userlist.hxx>
+#include <queryentry.hxx>
+#include <markdata.hxx>
+#include <tabvwsh.hxx>
+#include <generalfunction.hxx>
+#include <sortparam.hxx>
+
+#include <comphelper/lok.hxx>
+#include <osl/diagnose.h>
+
+#include <memory>
+#include <string_view>
+#include <unordered_set>
+#include <unordered_map>
+#include <vector>
+#include <algorithm>
+
+using namespace com::sun::star;
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::UNO_QUERY;
+using ::com::sun::star::beans::XPropertySet;
+using ::com::sun::star::container::XNameAccess;
+using ::com::sun::star::sheet::XDimensionsSupplier;
+using ::std::vector;
+
+// outliner
+
+// create outline grouping
+
+void ScDBFunc::MakeOutline( bool bColumns, bool bRecord )
+{
+ ScRange aRange;
+ if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
+ {
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScOutlineDocFunc aFunc(*pDocSh);
+ aFunc.MakeOutline( aRange, bColumns, bRecord, false );
+
+ ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), bColumns ? COLUMN_HEADER : ROW_HEADER, GetViewData().GetTabNo());
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
+ bColumns, !bColumns, false /* bSizes*/,
+ false /* bHidden */, false /* bFiltered */,
+ true /* bGroups */, GetViewData().GetTabNo());
+ }
+ else
+ ErrorMessage(STR_NOMULTISELECT);
+}
+
+// delete outline grouping
+
+void ScDBFunc::RemoveOutline( bool bColumns, bool bRecord )
+{
+ ScRange aRange;
+ if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
+ {
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScOutlineDocFunc aFunc(*pDocSh);
+ aFunc.RemoveOutline( aRange, bColumns, bRecord, false );
+
+ ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), bColumns ? COLUMN_HEADER : ROW_HEADER, GetViewData().GetTabNo());
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
+ bColumns, !bColumns, false /* bSizes*/,
+ true /* bHidden */, true /* bFiltered */,
+ true /* bGroups */, GetViewData().GetTabNo());
+ }
+ else
+ ErrorMessage(STR_NOMULTISELECT);
+}
+
+// menu status: delete outlines
+
+void ScDBFunc::TestRemoveOutline( bool& rCol, bool& rRow )
+{
+ bool bColFound = false;
+ bool bRowFound = false;
+
+ SCCOL nStartCol, nEndCol;
+ SCROW nStartRow, nEndRow;
+ SCTAB nStartTab, nEndTab;
+ if (GetViewData().GetSimpleArea(nStartCol,nStartRow,nStartTab,nEndCol,nEndRow,nEndTab) == SC_MARK_SIMPLE)
+ {
+ SCTAB nTab = nStartTab;
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab );
+ if (pTable)
+ {
+ ScOutlineEntry* pEntry;
+ SCCOLROW nStart;
+ SCCOLROW nEnd;
+ bool bColMarked = ( nStartRow == 0 && nEndRow == rDoc.MaxRow() );
+ bool bRowMarked = ( nStartCol == 0 && nEndCol == rDoc.MaxCol() );
+
+ // columns
+
+ if ( !bRowMarked || bColMarked ) // not when entire rows are marked
+ {
+ ScOutlineArray& rArray = pTable->GetColArray();
+ ScSubOutlineIterator aColIter( &rArray );
+ while (!bColFound)
+ {
+ pEntry=aColIter.GetNext();
+ if (!pEntry)
+ break;
+ nStart = pEntry->GetStart();
+ nEnd = pEntry->GetEnd();
+ if ( nStartCol<=static_cast<SCCOL>(nEnd) && nEndCol>=static_cast<SCCOL>(nStart) )
+ bColFound = true;
+ }
+ }
+
+ // rows
+
+ if ( !bColMarked || bRowMarked ) // not when entire columns are marked
+ {
+ ScOutlineArray& rArray = pTable->GetRowArray();
+ ScSubOutlineIterator aRowIter( &rArray );
+ while (!bRowFound)
+ {
+ pEntry=aRowIter.GetNext();
+ if (!pEntry)
+ break;
+ nStart = pEntry->GetStart();
+ nEnd = pEntry->GetEnd();
+ if ( nStartRow<=nEnd && nEndRow>=nStart )
+ bRowFound = true;
+ }
+ }
+ }
+ }
+
+ rCol = bColFound;
+ rRow = bRowFound;
+}
+
+void ScDBFunc::RemoveAllOutlines( bool bRecord )
+{
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScOutlineDocFunc aFunc(*pDocSh);
+
+ bool bOk = aFunc.RemoveAllOutlines( nTab, bRecord );
+
+ if (bOk)
+ {
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
+ true /* bColumns */, true /* bRows */, false /* bSizes*/,
+ true /* bHidden */, true /* bFiltered */,
+ true /* bGroups */, nTab);
+ UpdateScrollBars(BOTH_HEADERS);
+ }
+}
+
+// auto outlines
+
+void ScDBFunc::AutoOutline( )
+{
+ ScDocument& rDoc = GetViewData().GetDocument();
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScRange aRange( 0,0,nTab, rDoc.MaxCol(),rDoc.MaxRow(),nTab ); // the complete sheet, if nothing is marked
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ if ( rMark.IsMarked() || rMark.IsMultiMarked() )
+ {
+ rMark.MarkToMulti();
+ aRange = rMark.GetMultiMarkArea();
+ }
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScOutlineDocFunc aFunc(*pDocSh);
+ aFunc.AutoOutline( aRange, true );
+}
+
+// select outline level
+
+void ScDBFunc::SelectLevel( bool bColumns, sal_uInt16 nLevel, bool bRecord )
+{
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScOutlineDocFunc aFunc(*pDocSh);
+
+ bool bOk = aFunc.SelectLevel( nTab, bColumns, nLevel, bRecord, true/*bPaint*/ );
+
+ if (bOk)
+ {
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
+ bColumns, !bColumns, false /* bSizes*/,
+ true /* bHidden */, true /* bFiltered */,
+ true /* bGroups */, nTab);
+ UpdateScrollBars(bColumns ? COLUMN_HEADER : ROW_HEADER);
+ }
+}
+
+// show individual outline groups
+
+void ScDBFunc::SetOutlineState( bool bColumns, sal_uInt16 nLevel, sal_uInt16 nEntry, bool bHidden)
+{
+ const sal_uInt16 nHeadEntry = static_cast< sal_uInt16 >( -1 );
+ if ( nEntry == nHeadEntry)
+ SelectLevel( bColumns, sal::static_int_cast<sal_uInt16>(nLevel) );
+ else
+ {
+ if ( !bHidden )
+ ShowOutline( bColumns, sal::static_int_cast<sal_uInt16>(nLevel), sal::static_int_cast<sal_uInt16>(nEntry) );
+ else
+ HideOutline( bColumns, sal::static_int_cast<sal_uInt16>(nLevel), sal::static_int_cast<sal_uInt16>(nEntry) );
+ }
+}
+
+void ScDBFunc::ShowOutline( bool bColumns, sal_uInt16 nLevel, sal_uInt16 nEntry, bool bRecord, bool bPaint )
+{
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScOutlineDocFunc aFunc(*pDocSh);
+
+ aFunc.ShowOutline( nTab, bColumns, nLevel, nEntry, bRecord, bPaint );
+
+ if ( bPaint )
+ {
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
+ bColumns, !bColumns, false /* bSizes*/,
+ true /* bHidden */, true /* bFiltered */,
+ true /* bGroups */, nTab);
+ UpdateScrollBars(bColumns ? COLUMN_HEADER : ROW_HEADER);
+ }
+}
+
+// hide individual outline groups
+
+void ScDBFunc::HideOutline( bool bColumns, sal_uInt16 nLevel, sal_uInt16 nEntry, bool bRecord, bool bPaint )
+{
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScOutlineDocFunc aFunc(*pDocSh);
+
+ bool bOk = aFunc.HideOutline( nTab, bColumns, nLevel, nEntry, bRecord, bPaint );
+
+ if ( bOk && bPaint )
+ {
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
+ bColumns, !bColumns, false /* bSizes*/,
+ true /* bHidden */, true /* bFiltered */,
+ true /* bGroups */, nTab);
+ UpdateScrollBars(bColumns ? COLUMN_HEADER : ROW_HEADER);
+ }
+}
+
+// menu status: show/hide marked range
+
+bool ScDBFunc::OutlinePossible(bool bHide)
+{
+ bool bEnable = false;
+
+ SCCOL nStartCol;
+ SCROW nStartRow;
+ SCTAB nStartTab;
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ SCTAB nEndTab;
+
+ if (GetViewData().GetSimpleArea(nStartCol,nStartRow,nStartTab,nEndCol,nEndRow,nEndTab) == SC_MARK_SIMPLE)
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab );
+ if (pTable)
+ {
+ SCCOLROW nStart;
+ SCCOLROW nEnd;
+
+ // columns
+
+ ScOutlineArray& rColArray = pTable->GetColArray();
+ ScSubOutlineIterator aColIter( &rColArray );
+ while (!bEnable)
+ {
+ ScOutlineEntry* pEntry = aColIter.GetNext();
+ if (!pEntry)
+ break;
+ nStart = pEntry->GetStart();
+ nEnd = pEntry->GetEnd();
+ if ( bHide )
+ {
+ if ( nStartCol<=static_cast<SCCOL>(nEnd) && nEndCol>=static_cast<SCCOL>(nStart) )
+ if (!pEntry->IsHidden())
+ bEnable = true;
+ }
+ else
+ {
+ if ( nStart>=nStartCol && nEnd<=nEndCol )
+ if (pEntry->IsHidden())
+ bEnable = true;
+ }
+ }
+
+ // rows
+
+ ScOutlineArray& rRowArray = pTable->GetRowArray();
+ ScSubOutlineIterator aRowIter( &rRowArray );
+ for (;;)
+ {
+ ScOutlineEntry* pEntry = aRowIter.GetNext();
+ if (!pEntry)
+ break;
+ nStart = pEntry->GetStart();
+ nEnd = pEntry->GetEnd();
+ if ( bHide )
+ {
+ if ( nStartRow<=nEnd && nEndRow>=nStart )
+ if (!pEntry->IsHidden())
+ bEnable = true;
+ }
+ else
+ {
+ if ( nStart>=nStartRow && nEnd<=nEndRow )
+ if (pEntry->IsHidden())
+ bEnable = true;
+ }
+ }
+ }
+ }
+
+ return bEnable;
+}
+
+// show marked range
+
+void ScDBFunc::ShowMarkedOutlines( bool bRecord )
+{
+ ScRange aRange;
+ if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
+ {
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScOutlineDocFunc aFunc(*pDocSh);
+ bool bDone = aFunc.ShowMarkedOutlines( aRange, bRecord );
+ if (bDone)
+ {
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
+ GetViewData().GetViewShell(), true, true,
+ false /* bSizes*/, true /* bHidden */, true /* bFiltered */,
+ true /* bGroups */, GetViewData().GetTabNo());
+ UpdateScrollBars();
+ }
+ }
+ else
+ ErrorMessage(STR_NOMULTISELECT);
+}
+
+// hide marked range
+
+void ScDBFunc::HideMarkedOutlines( bool bRecord )
+{
+ ScRange aRange;
+ if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
+ {
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScOutlineDocFunc aFunc(*pDocSh);
+ bool bDone = aFunc.HideMarkedOutlines( aRange, bRecord );
+ if (bDone)
+ {
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
+ GetViewData().GetViewShell(), true, true,
+ false /* bSizes*/, true /* bHidden */, true /* bFiltered */,
+ true /* bGroups */, GetViewData().GetTabNo());
+ UpdateScrollBars();
+ }
+ }
+ else
+ ErrorMessage(STR_NOMULTISELECT);
+}
+
+// sub totals
+
+void ScDBFunc::DoSubTotals( const ScSubTotalParam& rParam, bool bRecord,
+ const ScSortParam* pForceNewSort )
+{
+ bool bDo = !rParam.bRemoveOnly; // sal_False = only delete
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ SCTAB nTab = GetViewData().GetTabNo();
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ ScDBData* pDBData = rDoc.GetDBAtArea( nTab, rParam.nCol1, rParam.nRow1,
+ rParam.nCol2, rParam.nRow2 );
+ if (!pDBData)
+ {
+ OSL_FAIL( "SubTotals: no DBData" );
+ return;
+ }
+
+ ScEditableTester aTester( rDoc, nTab, 0,rParam.nRow1+1, rDoc.MaxCol(),rDoc.MaxRow() );
+ if (!aTester.IsEditable())
+ {
+ ErrorMessage(aTester.GetMessageId());
+ return;
+ }
+
+ if (rDoc.HasAttrib( rParam.nCol1, rParam.nRow1+1, nTab,
+ rParam.nCol2, rParam.nRow2, nTab, HasAttrFlags::Merged | HasAttrFlags::Overlapped ))
+ {
+ ErrorMessage(STR_MSSG_INSERTCELLS_0); // do not insert into merged
+ return;
+ }
+
+ weld::WaitObject aWait(GetViewData().GetDialogParent());
+ bool bOk = true;
+ if (rParam.bReplace)
+ {
+ if (rDoc.TestRemoveSubTotals( nTab, rParam ))
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetViewData().GetDialogParent(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ ScResId(STR_MSSG_DOSUBTOTALS_1))); // "delete data?"
+ xBox->set_title(ScResId(STR_MSSG_DOSUBTOTALS_0)); // "StarCalc"
+ xBox->set_default_response(RET_YES);
+ bOk = xBox->run() == RET_YES;
+ }
+ }
+
+ if (!bOk)
+ return;
+
+ ScDocShellModificator aModificator( *pDocSh );
+
+ ScSubTotalParam aNewParam( rParam ); // change end of range
+ ScDocumentUniquePtr pUndoDoc;
+ std::unique_ptr<ScOutlineTable> pUndoTab;
+ std::unique_ptr<ScRangeName> pUndoRange;
+ std::unique_ptr<ScDBCollection> pUndoDB;
+
+ if (bRecord) // record old data
+ {
+ bool bOldFilter = bDo && rParam.bDoSort;
+ SCTAB nTabCount = rDoc.GetTableCount();
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab );
+ if (pTable)
+ {
+ pUndoTab.reset(new ScOutlineTable( *pTable ));
+
+ SCCOLROW nOutStartCol; // row/column status
+ SCCOLROW nOutStartRow;
+ SCCOLROW nOutEndCol;
+ SCCOLROW nOutEndRow;
+ pTable->GetColArray().GetRange( nOutStartCol, nOutEndCol );
+ pTable->GetRowArray().GetRange( nOutStartRow, nOutEndRow );
+
+ pUndoDoc->InitUndo( rDoc, nTab, nTab, true, true );
+ rDoc.CopyToDocument( static_cast<SCCOL>(nOutStartCol), 0, nTab, static_cast<SCCOL>(nOutEndCol), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE, false, *pUndoDoc );
+ rDoc.CopyToDocument( 0, nOutStartRow, nTab, rDoc.MaxCol(), nOutEndRow, nTab, InsertDeleteFlags::NONE, false, *pUndoDoc );
+ }
+ else
+ pUndoDoc->InitUndo( rDoc, nTab, nTab, false, bOldFilter );
+
+ // record data range - including filter results
+ rDoc.CopyToDocument( 0,rParam.nRow1+1,nTab, rDoc.MaxCol(),rParam.nRow2,nTab,
+ InsertDeleteFlags::ALL, false, *pUndoDoc );
+
+ // all formulas for reference
+ rDoc.CopyToDocument( 0,0,0, rDoc.MaxCol(),rDoc.MaxRow(),nTabCount-1,
+ InsertDeleteFlags::FORMULA, false, *pUndoDoc );
+
+ // database and other ranges
+ ScRangeName* pDocRange = rDoc.GetRangeName();
+ if (!pDocRange->empty())
+ pUndoRange.reset(new ScRangeName( *pDocRange ));
+ ScDBCollection* pDocDB = rDoc.GetDBCollection();
+ if (!pDocDB->empty())
+ pUndoDB.reset(new ScDBCollection( *pDocDB ));
+ }
+
+ ScOutlineTable* pOut = rDoc.GetOutlineTable( nTab );
+ if (pOut)
+ {
+ // Remove all existing outlines in the specified range.
+ ScOutlineArray& rRowArray = pOut->GetRowArray();
+ sal_uInt16 nDepth = rRowArray.GetDepth();
+ for (sal_uInt16 i = 0; i < nDepth; ++i)
+ {
+ bool bSize;
+ rRowArray.Remove(aNewParam.nRow1, aNewParam.nRow2, bSize);
+ }
+ }
+
+ if (rParam.bReplace)
+ rDoc.RemoveSubTotals( nTab, aNewParam );
+ bool bSuccess = true;
+ if (bDo)
+ {
+ // Sort
+ if ( rParam.bDoSort || pForceNewSort )
+ {
+ pDBData->SetArea( nTab, aNewParam.nCol1,aNewParam.nRow1, aNewParam.nCol2,aNewParam.nRow2 );
+
+ // set subtotal fields before sorting
+ // (duplicate values are dropped, so that they can be called again)
+
+ ScSortParam aOldSort;
+ pDBData->GetSortParam( aOldSort );
+ ScSortParam aSortParam( aNewParam, pForceNewSort ? *pForceNewSort : aOldSort );
+ Sort( aSortParam, false, false );
+ }
+
+ bSuccess = rDoc.DoSubTotals( nTab, aNewParam );
+ }
+ ScRange aDirtyRange( aNewParam.nCol1, aNewParam.nRow1, nTab,
+ aNewParam.nCol2, aNewParam.nRow2, nTab );
+ rDoc.SetDirty( aDirtyRange, true );
+
+ if (bRecord)
+ {
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoSubTotals>( pDocSh, nTab,
+ rParam, aNewParam.nRow2,
+ std::move(pUndoDoc), std::move(pUndoTab), // pUndoDBData,
+ std::move(pUndoRange), std::move(pUndoDB) ) );
+ }
+
+ if (!bSuccess)
+ {
+ // "Can not insert any rows"
+ ErrorMessage(STR_MSSG_DOSUBTOTALS_2);
+ }
+
+ // store
+ pDBData->SetSubTotalParam( aNewParam );
+ pDBData->SetArea( nTab, aNewParam.nCol1,aNewParam.nRow1, aNewParam.nCol2,aNewParam.nRow2 );
+ rDoc.CompileDBFormula();
+
+ const ScRange aMarkRange( aNewParam.nCol1, aNewParam.nRow1, nTab, aNewParam.nCol2, aNewParam.nRow2, nTab);
+ DoneBlockMode();
+ InitOwnBlockMode( aMarkRange );
+ rMark.SetMarkArea( aMarkRange );
+ MarkDataChanged();
+
+ pDocSh->PostPaint(ScRange(0, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab),
+ PaintPartFlags::Grid | PaintPartFlags::Left | PaintPartFlags::Top | PaintPartFlags::Size);
+
+ aModificator.SetDocumentModified();
+
+ SelectionChanged();
+}
+
+// consolidate
+
+void ScDBFunc::Consolidate( const ScConsolidateParam& rParam )
+{
+ ScDocShell* pDocShell = GetViewData().GetDocShell();
+ pDocShell->DoConsolidate( rParam );
+ SetTabNo( rParam.nTab, true );
+}
+
+// pivot
+
+static OUString lcl_MakePivotTabName( std::u16string_view rPrefix, SCTAB nNumber )
+{
+ OUString aName = rPrefix + OUString::number( nNumber );
+ return aName;
+}
+
+bool ScDBFunc::MakePivotTable(
+ const ScDPSaveData& rData, const ScRange& rDest, bool bNewTable,
+ const ScDPObject& rSource )
+{
+ // error message if no fields are set
+ // this must be removed when drag&drop of fields from a toolbox is available
+
+ if ( rData.IsEmpty() )
+ {
+ ErrorMessage(STR_PIVOT_NODATA);
+ return false;
+ }
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = GetViewData().GetDocument();
+ bool bUndo = rDoc.IsUndoEnabled();
+
+ ScRange aDestRange = rDest;
+ if ( bNewTable )
+ {
+ SCTAB nSrcTab = GetViewData().GetTabNo();
+
+ OUString aName( ScResId(STR_PIVOT_TABLE) );
+ OUString aStr;
+
+ rDoc.GetName( nSrcTab, aStr );
+ aName += "_" + aStr + "_";
+
+ SCTAB nNewTab = nSrcTab+1;
+
+ SCTAB i=1;
+ while ( !rDoc.InsertTab( nNewTab, lcl_MakePivotTabName( aName, i ) ) && i <= MAXTAB )
+ i++;
+
+ bool bAppend = ( nNewTab+1 == rDoc.GetTableCount() );
+ if (bUndo)
+ {
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoInsertTab>( pDocSh, nNewTab, bAppend, lcl_MakePivotTabName( aName, i ) ));
+ }
+
+ GetViewData().InsertTab( nNewTab );
+ SetTabNo(nNewTab, true);
+
+ aDestRange = ScRange( 0, 0, nNewTab );
+ }
+
+ ScDPObject* pDPObj = rDoc.GetDPAtCursor(
+ aDestRange.aStart.Col(), aDestRange.aStart.Row(), aDestRange.aStart.Tab() );
+
+ ScDPObject aObj( rSource );
+ aObj.SetOutRange( aDestRange );
+ if ( pDPObj && !rData.GetExistingDimensionData() )
+ {
+ // copy dimension data from old object - lost in the dialog
+ //! change the dialog to keep the dimension data
+
+ ScDPSaveData aNewData( rData );
+ const ScDPSaveData* pOldData = pDPObj->GetSaveData();
+ if ( pOldData )
+ {
+ const ScDPDimensionSaveData* pDimSave = pOldData->GetExistingDimensionData();
+ aNewData.SetDimensionData( pDimSave );
+ }
+ aObj.SetSaveData( aNewData );
+ }
+ else
+ aObj.SetSaveData( rData );
+
+ bool bAllowMove = (pDPObj != nullptr); // allow re-positioning when editing existing table
+
+ ScDBDocFunc aFunc( *pDocSh );
+ bool bSuccess = aFunc.DataPilotUpdate(pDPObj, &aObj, true, false, bAllowMove);
+
+ CursorPosChanged(); // shells may be switched
+
+ if ( bNewTable )
+ {
+ pDocSh->PostPaintExtras();
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
+ }
+
+ return bSuccess;
+}
+
+void ScDBFunc::DeletePivotTable()
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScDPObject* pDPObj = rDoc.GetDPAtCursor( GetViewData().GetCurX(),
+ GetViewData().GetCurY(),
+ GetViewData().GetTabNo() );
+ if ( pDPObj )
+ {
+ ScDBDocFunc aFunc( *pDocSh );
+ aFunc.RemovePivotTable(*pDPObj, true, false);
+ CursorPosChanged(); // shells may be switched
+ }
+ else
+ ErrorMessage(STR_PIVOT_NOTFOUND);
+}
+
+void ScDBFunc::RecalcPivotTable()
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = GetViewData().GetDocument();
+
+ ScDPObject* pDPObj = rDoc.GetDPAtCursor( GetViewData().GetCurX(),
+ GetViewData().GetCurY(),
+ GetViewData().GetTabNo() );
+ if (pDPObj)
+ {
+ // Remove existing data cache for the data that this datapilot uses,
+ // to force re-build data cache.
+ ScDBDocFunc aFunc(*pDocSh);
+ aFunc.RefreshPivotTables(pDPObj, false);
+
+ CursorPosChanged(); // shells may be switched
+ }
+ else
+ ErrorMessage(STR_PIVOT_NOTFOUND);
+}
+
+void ScDBFunc::GetSelectedMemberList(ScDPUniqueStringSet& rEntries, tools::Long& rDimension)
+{
+ ScDPObject* pDPObj = GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
+ GetViewData().GetCurY(), GetViewData().GetTabNo() );
+ if ( !pDPObj )
+ return;
+
+ tools::Long nStartDimension = -1;
+ tools::Long nStartHierarchy = -1;
+ tools::Long nStartLevel = -1;
+
+ ScRangeListRef xRanges;
+ GetViewData().GetMultiArea( xRanges ); // incl. cursor if nothing is selected
+ size_t nRangeCount = xRanges->size();
+ bool bContinue = true;
+
+ for (size_t nRangePos=0; nRangePos < nRangeCount && bContinue; nRangePos++)
+ {
+ ScRange const & rRange = (*xRanges)[nRangePos];
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nEndRow = rRange.aEnd.Row();
+ SCTAB nTab = rRange.aStart.Tab();
+
+ for (SCROW nRow=nStartRow; nRow<=nEndRow && bContinue; nRow++)
+ for (SCCOL nCol=nStartCol; nCol<=nEndCol && bContinue; nCol++)
+ {
+ sheet::DataPilotTableHeaderData aData;
+ pDPObj->GetHeaderPositionData(ScAddress(nCol, nRow, nTab), aData);
+ if ( aData.Dimension < 0 )
+ bContinue = false; // not part of any dimension
+ else
+ {
+ if ( nStartDimension < 0 ) // first member?
+ {
+ nStartDimension = aData.Dimension;
+ nStartHierarchy = aData.Hierarchy;
+ nStartLevel = aData.Level;
+ }
+ if ( aData.Dimension != nStartDimension ||
+ aData.Hierarchy != nStartHierarchy ||
+ aData.Level != nStartLevel )
+ {
+ bContinue = false; // cannot mix dimensions
+ }
+ }
+ if ( bContinue )
+ {
+ // accept any part of a member description, also subtotals,
+ // but don't stop if empty parts are contained
+ if ( aData.Flags & sheet::MemberResultFlags::HASMEMBER )
+ rEntries.insert(aData.MemberName);
+ }
+ }
+ }
+
+ rDimension = nStartDimension; // dimension from which the found members came
+ if (!bContinue)
+ rEntries.clear(); // remove all if not valid
+}
+
+bool ScDBFunc::HasSelectionForDateGroup( ScDPNumGroupInfo& rOldInfo, sal_Int32& rParts )
+{
+ // determine if the date group dialog has to be shown for the current selection
+
+ bool bFound = false;
+
+ SCCOL nCurX = GetViewData().GetCurX();
+ SCROW nCurY = GetViewData().GetCurY();
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScDocument& rDoc = GetViewData().GetDocument();
+
+ ScDPObject* pDPObj = rDoc.GetDPAtCursor( nCurX, nCurY, nTab );
+ if ( pDPObj )
+ {
+ ScDPUniqueStringSet aEntries;
+ tools::Long nSelectDimension = -1;
+ GetSelectedMemberList( aEntries, nSelectDimension );
+
+ if (!aEntries.empty())
+ {
+ bool bIsDataLayout;
+ OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
+ OUString aBaseDimName( aDimName );
+
+ bool bInGroupDim = false;
+ bool bFoundParts = false;
+
+ ScDPDimensionSaveData* pDimData =
+ const_cast<ScDPDimensionSaveData*>( pDPObj->GetSaveData()->GetExistingDimensionData() );
+ if ( pDimData )
+ {
+ const ScDPSaveNumGroupDimension* pNumGroupDim = pDimData->GetNumGroupDim( aDimName );
+ const ScDPSaveGroupDimension* pGroupDim = pDimData->GetNamedGroupDim( aDimName );
+ if ( pNumGroupDim )
+ {
+ // existing num group dimension
+
+ if ( pNumGroupDim->GetDatePart() != 0 )
+ {
+ // dimension has date info -> edit settings of this dimension
+ // (parts are collected below)
+
+ rOldInfo = pNumGroupDim->GetDateInfo();
+ bFound = true;
+ }
+ else if ( pNumGroupDim->GetInfo().mbDateValues )
+ {
+ // Numerical grouping with DateValues flag is used for grouping
+ // of days with a "Number of days" value.
+
+ rOldInfo = pNumGroupDim->GetInfo();
+ rParts = css::sheet::DataPilotFieldGroupBy::DAYS; // not found in CollectDateParts
+ bFoundParts = true;
+ bFound = true;
+ }
+ bInGroupDim = true;
+ }
+ else if ( pGroupDim )
+ {
+ // existing additional group dimension
+
+ if ( pGroupDim->GetDatePart() != 0 )
+ {
+ // dimension has date info -> edit settings of this dimension
+ // (parts are collected below)
+
+ rOldInfo = pGroupDim->GetDateInfo();
+ aBaseDimName = pGroupDim->GetSourceDimName();
+ bFound = true;
+ }
+ bInGroupDim = true;
+ }
+ }
+ if ( bFound && !bFoundParts )
+ {
+ // collect date parts from all group dimensions
+ rParts = pDimData->CollectDateParts( aBaseDimName );
+ }
+ if ( !bFound && !bInGroupDim )
+ {
+ // create new date group dimensions if the selection is a single cell
+ // in a normal dimension with date content
+
+ ScRange aSelRange;
+ if ( (GetViewData().GetSimpleArea( aSelRange ) == SC_MARK_SIMPLE) &&
+ aSelRange.aStart == aSelRange.aEnd )
+ {
+ SCCOL nSelCol = aSelRange.aStart.Col();
+ SCROW nSelRow = aSelRange.aStart.Row();
+ SCTAB nSelTab = aSelRange.aStart.Tab();
+ if ( rDoc.HasValueData( nSelCol, nSelRow, nSelTab ) )
+ {
+ sal_uLong nIndex = rDoc.GetAttr(
+ nSelCol, nSelRow, nSelTab, ATTR_VALUE_FORMAT)->GetValue();
+ SvNumFormatType nType = rDoc.GetFormatTable()->GetType(nIndex);
+ if ( nType == SvNumFormatType::DATE || nType == SvNumFormatType::TIME || nType == SvNumFormatType::DATETIME )
+ {
+ bFound = true;
+ // use currently selected value for automatic limits
+ if( rOldInfo.mbAutoStart )
+ rOldInfo.mfStart = rDoc.GetValue( aSelRange.aStart );
+ if( rOldInfo.mbAutoEnd )
+ rOldInfo.mfEnd = rDoc.GetValue( aSelRange.aStart );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return bFound;
+}
+
+bool ScDBFunc::HasSelectionForNumGroup( ScDPNumGroupInfo& rOldInfo )
+{
+ // determine if the numeric group dialog has to be shown for the current selection
+
+ bool bFound = false;
+
+ SCCOL nCurX = GetViewData().GetCurX();
+ SCROW nCurY = GetViewData().GetCurY();
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScDocument& rDoc = GetViewData().GetDocument();
+
+ ScDPObject* pDPObj = rDoc.GetDPAtCursor( nCurX, nCurY, nTab );
+ if ( pDPObj )
+ {
+ ScDPUniqueStringSet aEntries;
+ tools::Long nSelectDimension = -1;
+ GetSelectedMemberList( aEntries, nSelectDimension );
+
+ if (!aEntries.empty())
+ {
+ bool bIsDataLayout;
+ OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
+
+ bool bInGroupDim = false;
+
+ ScDPDimensionSaveData* pDimData =
+ const_cast<ScDPDimensionSaveData*>( pDPObj->GetSaveData()->GetExistingDimensionData() );
+ if ( pDimData )
+ {
+ const ScDPSaveNumGroupDimension* pNumGroupDim = pDimData->GetNumGroupDim( aDimName );
+ if ( pNumGroupDim )
+ {
+ // existing num group dimension
+ // -> edit settings of this dimension
+
+ rOldInfo = pNumGroupDim->GetInfo();
+ bFound = true;
+ }
+ else if ( pDimData->GetNamedGroupDim( aDimName ) )
+ bInGroupDim = true; // in a group dimension
+ }
+ if ( !bFound && !bInGroupDim )
+ {
+ // create a new num group dimension if the selection is a single cell
+ // in a normal dimension with numeric content
+
+ ScRange aSelRange;
+ if ( (GetViewData().GetSimpleArea( aSelRange ) == SC_MARK_SIMPLE) &&
+ aSelRange.aStart == aSelRange.aEnd )
+ {
+ if ( rDoc.HasValueData( aSelRange.aStart.Col(), aSelRange.aStart.Row(),
+ aSelRange.aStart.Tab() ) )
+ {
+ bFound = true;
+ // use currently selected value for automatic limits
+ if( rOldInfo.mbAutoStart )
+ rOldInfo.mfStart = rDoc.GetValue( aSelRange.aStart );
+ if( rOldInfo.mbAutoEnd )
+ rOldInfo.mfEnd = rDoc.GetValue( aSelRange.aStart );
+ }
+ }
+ }
+ }
+ }
+
+ return bFound;
+}
+
+void ScDBFunc::DateGroupDataPilot( const ScDPNumGroupInfo& rInfo, sal_Int32 nParts )
+{
+ ScDPObject* pDPObj = GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
+ GetViewData().GetCurY(), GetViewData().GetTabNo() );
+ if (!pDPObj)
+ return;
+
+ ScDPUniqueStringSet aEntries;
+ tools::Long nSelectDimension = -1;
+ GetSelectedMemberList( aEntries, nSelectDimension );
+
+ if (aEntries.empty())
+ return;
+
+ std::vector<OUString> aDeletedNames;
+ bool bIsDataLayout;
+ OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
+
+ ScDPSaveData aData( *pDPObj->GetSaveData() );
+ ScDPDimensionSaveData* pDimData = aData.GetDimensionData(); // created if not there
+
+ // find the source dimension name.
+ OUString aBaseDimName = aDimName;
+ if( const ScDPSaveGroupDimension* pBaseGroupDim = pDimData->GetNamedGroupDim( aDimName ) )
+ aBaseDimName = pBaseGroupDim->GetSourceDimName();
+
+ // Remove all group dimensions associated with this source dimension. For
+ // date grouping, we need to remove all existing groups for the affected
+ // source dimension and build new one(s) from scratch. Keep the deleted
+ // names so that they can be reused during re-construction.
+ aData.RemoveAllGroupDimensions(aBaseDimName, &aDeletedNames);
+
+ if ( nParts )
+ {
+ // create date group dimensions
+
+ bool bFirst = true;
+ sal_Int32 nMask = 1;
+ for (sal_uInt16 nBit=0; nBit<32; nBit++)
+ {
+ if ( nParts & nMask )
+ {
+ if ( bFirst )
+ {
+ // innermost part: create NumGroupDimension (replacing original values)
+ // Dimension name is left unchanged
+
+ if ( (nParts == sheet::DataPilotFieldGroupBy::DAYS) && (rInfo.mfStep >= 1.0) )
+ {
+ // only days, and a step value specified: use numerical grouping
+ // with DateValues flag, not date grouping
+
+ ScDPNumGroupInfo aNumInfo( rInfo );
+ aNumInfo.mbDateValues = true;
+
+ ScDPSaveNumGroupDimension aNumGroupDim( aBaseDimName, aNumInfo );
+ pDimData->AddNumGroupDimension( aNumGroupDim );
+ }
+ else
+ {
+ ScDPSaveNumGroupDimension aNumGroupDim( aBaseDimName, rInfo, nMask );
+ pDimData->AddNumGroupDimension( aNumGroupDim );
+ }
+
+ bFirst = false;
+ }
+ else
+ {
+ // additional parts: create GroupDimension (shown as additional dimensions)
+ OUString aGroupDimName =
+ pDimData->CreateDateGroupDimName(nMask, *pDPObj, true, &aDeletedNames);
+ ScDPSaveGroupDimension aGroupDim( aBaseDimName, aGroupDimName );
+ aGroupDim.SetDateInfo( rInfo, nMask );
+ pDimData->AddGroupDimension( aGroupDim );
+
+ // set orientation
+ ScDPSaveDimension* pSaveDimension = aData.GetDimensionByName( aGroupDimName );
+ if ( pSaveDimension->GetOrientation() == sheet::DataPilotFieldOrientation_HIDDEN )
+ {
+ ScDPSaveDimension* pOldDimension = aData.GetDimensionByName( aBaseDimName );
+ pSaveDimension->SetOrientation( pOldDimension->GetOrientation() );
+ aData.SetPosition( pSaveDimension, 0 ); //! before (immediate) base
+ }
+ }
+ }
+ nMask *= 2;
+ }
+ }
+
+ // apply changes
+ ScDBDocFunc aFunc( *GetViewData().GetDocShell() );
+ pDPObj->SetSaveData( aData );
+ aFunc.RefreshPivotTableGroups(pDPObj);
+
+ // unmark cell selection
+ Unmark();
+}
+
+void ScDBFunc::NumGroupDataPilot( const ScDPNumGroupInfo& rInfo )
+{
+ ScDPObject* pDPObj = GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
+ GetViewData().GetCurY(), GetViewData().GetTabNo() );
+ if (!pDPObj)
+ return;
+
+ ScDPUniqueStringSet aEntries;
+ tools::Long nSelectDimension = -1;
+ GetSelectedMemberList( aEntries, nSelectDimension );
+
+ if (aEntries.empty())
+ return;
+
+ bool bIsDataLayout;
+ OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
+
+ ScDPSaveData aData( *pDPObj->GetSaveData() );
+ ScDPDimensionSaveData* pDimData = aData.GetDimensionData(); // created if not there
+
+ ScDPSaveNumGroupDimension* pExisting = pDimData->GetNumGroupDimAcc( aDimName );
+ if ( pExisting )
+ {
+ // modify existing group dimension
+ pExisting->SetGroupInfo( rInfo );
+ }
+ else
+ {
+ // create new group dimension
+ ScDPSaveNumGroupDimension aNumGroupDim( aDimName, rInfo );
+ pDimData->AddNumGroupDimension( aNumGroupDim );
+ }
+
+ // apply changes
+ ScDBDocFunc aFunc( *GetViewData().GetDocShell() );
+ pDPObj->SetSaveData( aData );
+ aFunc.RefreshPivotTableGroups(pDPObj);
+
+ // unmark cell selection
+ Unmark();
+}
+
+void ScDBFunc::GroupDataPilot()
+{
+ ScDPObject* pDPObj = GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
+ GetViewData().GetCurY(), GetViewData().GetTabNo() );
+ if (!pDPObj)
+ return;
+
+ ScDPUniqueStringSet aEntries;
+ tools::Long nSelectDimension = -1;
+ GetSelectedMemberList( aEntries, nSelectDimension );
+
+ if (aEntries.empty())
+ return;
+
+ bool bIsDataLayout;
+ OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
+
+ ScDPSaveData aData( *pDPObj->GetSaveData() );
+ ScDPDimensionSaveData* pDimData = aData.GetDimensionData(); // created if not there
+
+ // find original base
+ OUString aBaseDimName = aDimName;
+ const ScDPSaveGroupDimension* pBaseGroupDim = pDimData->GetNamedGroupDim( aDimName );
+ if ( pBaseGroupDim )
+ {
+ // any entry's SourceDimName is the original base
+ aBaseDimName = pBaseGroupDim->GetSourceDimName();
+ }
+
+ // find existing group dimension
+ // (using the selected dim, can be intermediate group dim)
+ ScDPSaveGroupDimension* pGroupDimension = pDimData->GetGroupDimAccForBase( aDimName );
+
+ // remove the selected items from their groups
+ // (empty groups are removed, too)
+ if ( pGroupDimension )
+ {
+ for (const OUString& aEntryName : aEntries)
+ {
+ if ( pBaseGroupDim )
+ {
+ // for each selected (intermediate) group, remove all its items
+ // (same logic as for adding, below)
+ const ScDPSaveGroupItem* pBaseGroup = pBaseGroupDim->GetNamedGroup( aEntryName );
+ if ( pBaseGroup )
+ pBaseGroup->RemoveElementsFromGroups( *pGroupDimension ); // remove all elements
+ else
+ pGroupDimension->RemoveFromGroups( aEntryName );
+ }
+ else
+ pGroupDimension->RemoveFromGroups( aEntryName );
+ }
+ }
+
+ std::unique_ptr<ScDPSaveGroupDimension> pNewGroupDim;
+ if ( !pGroupDimension )
+ {
+ // create a new group dimension
+ OUString aGroupDimName =
+ pDimData->CreateGroupDimName(aBaseDimName, *pDPObj, false, nullptr);
+ pNewGroupDim.reset(new ScDPSaveGroupDimension( aBaseDimName, aGroupDimName ));
+
+ pGroupDimension = pNewGroupDim.get(); // make changes to the new dim if none existed
+
+ if ( pBaseGroupDim )
+ {
+ // If it's a higher-order group dimension, pre-allocate groups for all
+ // non-selected original groups, so the individual base members aren't
+ // used for automatic groups (this would make the original groups hard
+ // to find).
+ //! Also do this when removing groups?
+ //! Handle this case dynamically with automatic groups?
+
+ tools::Long nGroupCount = pBaseGroupDim->GetGroupCount();
+ for ( tools::Long nGroup = 0; nGroup < nGroupCount; nGroup++ )
+ {
+ const ScDPSaveGroupItem& rBaseGroup = pBaseGroupDim->GetGroupByIndex( nGroup );
+
+ if (!aEntries.count(rBaseGroup.GetGroupName()))
+ {
+ // add an additional group for each item that is not in the selection
+ ScDPSaveGroupItem aGroup( rBaseGroup.GetGroupName() );
+ aGroup.AddElementsFromGroup( rBaseGroup );
+ pGroupDimension->AddGroupItem( aGroup );
+ }
+ }
+ }
+ }
+ OUString aGroupDimName = pGroupDimension->GetGroupDimName();
+
+ OUString aGroupName = pGroupDimension->CreateGroupName(ScResId(STR_PIVOT_GROUP));
+ ScDPSaveGroupItem aGroup( aGroupName );
+ for (const OUString& aEntryName : aEntries)
+ {
+ if ( pBaseGroupDim )
+ {
+ // for each selected (intermediate) group, add all its items
+ const ScDPSaveGroupItem* pBaseGroup = pBaseGroupDim->GetNamedGroup( aEntryName );
+ if ( pBaseGroup )
+ aGroup.AddElementsFromGroup( *pBaseGroup );
+ else
+ aGroup.AddElement( aEntryName ); // no group found -> automatic group, add the item itself
+ }
+ else
+ aGroup.AddElement( aEntryName ); // no group dimension, add all items directly
+ }
+
+ pGroupDimension->AddGroupItem( aGroup );
+
+ if ( pNewGroupDim )
+ {
+ pDimData->AddGroupDimension( *pNewGroupDim );
+ pNewGroupDim.reset(); // AddGroupDimension copies the object
+ // don't access pGroupDimension after here
+ }
+ pGroupDimension = nullptr;
+
+ // set orientation
+ ScDPSaveDimension* pSaveDimension = aData.GetDimensionByName( aGroupDimName );
+ if ( pSaveDimension->GetOrientation() == sheet::DataPilotFieldOrientation_HIDDEN )
+ {
+ ScDPSaveDimension* pOldDimension = aData.GetDimensionByName( aDimName );
+ pSaveDimension->SetOrientation( pOldDimension->GetOrientation() );
+ aData.SetPosition( pSaveDimension, 0 ); //! before (immediate) base
+ }
+
+ // apply changes
+ ScDBDocFunc aFunc( *GetViewData().GetDocShell() );
+ pDPObj->SetSaveData( aData );
+ aFunc.RefreshPivotTableGroups(pDPObj);
+
+ // unmark cell selection
+ Unmark();
+}
+
+void ScDBFunc::UngroupDataPilot()
+{
+ ScDPObject* pDPObj = GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
+ GetViewData().GetCurY(), GetViewData().GetTabNo() );
+ if (!pDPObj)
+ return;
+
+ ScDPUniqueStringSet aEntries;
+ tools::Long nSelectDimension = -1;
+ GetSelectedMemberList( aEntries, nSelectDimension );
+
+ if (aEntries.empty())
+ return;
+
+ bool bIsDataLayout;
+ OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
+
+ ScDPSaveData aData( *pDPObj->GetSaveData() );
+ if (!aData.GetExistingDimensionData())
+ // There is nothing to ungroup.
+ return;
+
+ ScDPDimensionSaveData* pDimData = aData.GetDimensionData();
+
+ ScDPSaveGroupDimension* pGroupDim = pDimData->GetNamedGroupDimAcc( aDimName );
+ const ScDPSaveNumGroupDimension* pNumGroupDim = pDimData->GetNumGroupDim( aDimName );
+ if ( ( pGroupDim && pGroupDim->GetDatePart() != 0 ) ||
+ ( pNumGroupDim && pNumGroupDim->GetDatePart() != 0 ) )
+ {
+ // Date grouping: need to remove all affected group dimensions.
+ // This is done using DateGroupDataPilot with nParts=0.
+
+ DateGroupDataPilot( ScDPNumGroupInfo(), 0 );
+ return;
+ }
+
+ if ( pGroupDim )
+ {
+ for (const auto& rEntry : aEntries)
+ pGroupDim->RemoveGroup(rEntry);
+
+ // remove group dimension if empty
+ bool bEmptyDim = pGroupDim->IsEmpty();
+ if ( !bEmptyDim )
+ {
+ // If all remaining groups in the dimension aren't shown, remove
+ // the dimension too, as if it was completely empty.
+ ScDPUniqueStringSet aVisibleEntries;
+ pDPObj->GetMemberResultNames( aVisibleEntries, nSelectDimension );
+ bEmptyDim = pGroupDim->HasOnlyHidden( aVisibleEntries );
+ }
+ if ( bEmptyDim )
+ {
+ pDimData->RemoveGroupDimension( aDimName ); // pGroupDim is deleted
+
+ // also remove SaveData settings for the dimension that no longer exists
+ aData.RemoveDimensionByName( aDimName );
+ }
+ }
+ else if ( pNumGroupDim )
+ {
+ // remove the numerical grouping
+ pDimData->RemoveNumGroupDimension( aDimName );
+ // SaveData settings can remain unchanged - the same dimension still exists
+ }
+
+ // apply changes
+ ScDBDocFunc aFunc( *GetViewData().GetDocShell() );
+ pDPObj->SetSaveData( aData );
+ aFunc.RefreshPivotTableGroups(pDPObj);
+
+ // unmark cell selection
+ Unmark();
+}
+
+static OUString lcl_replaceMemberNameInSubtotal(const OUString& rSubtotal, std::u16string_view rMemberName)
+{
+ sal_Int32 n = rSubtotal.getLength();
+ const sal_Unicode* p = rSubtotal.getStr();
+ OUStringBuffer aBuf, aWordBuf;
+ for (sal_Int32 i = 0; i < n; ++i)
+ {
+ sal_Unicode c = p[i];
+ if (c == ' ')
+ {
+ OUString aWord = aWordBuf.makeStringAndClear();
+ if (aWord == rMemberName)
+ aBuf.append('?');
+ else
+ aBuf.append(aWord);
+ aBuf.append(c);
+ }
+ else if (c == '\\')
+ {
+ // Escape a backslash character.
+ aWordBuf.append(OUStringChar(c) + OUStringChar(c));
+ }
+ else if (c == '?')
+ {
+ // A literal '?' must be escaped with a backslash ('\');
+ aWordBuf.append("\\" + OUStringChar(c));
+ }
+ else
+ aWordBuf.append(c);
+ }
+
+ if (!aWordBuf.isEmpty())
+ {
+ OUString aWord = aWordBuf.makeStringAndClear();
+ if (aWord == rMemberName)
+ aBuf.append('?');
+ else
+ aBuf.append(aWord);
+ }
+
+ return aBuf.makeStringAndClear();
+}
+
+void ScDBFunc::DataPilotInput( const ScAddress& rPos, const OUString& rString )
+{
+ using namespace ::com::sun::star::sheet;
+
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScDPObject* pDPObj = rDoc.GetDPAtCursor( rPos.Col(), rPos.Row(), rPos.Tab() );
+ if (!pDPObj)
+ return;
+
+ OUString aOldText = rDoc.GetString(rPos.Col(), rPos.Row(), rPos.Tab());
+
+ if ( aOldText == rString )
+ {
+ // nothing to do: silently exit
+ return;
+ }
+
+ TranslateId pErrorId;
+
+ pDPObj->BuildAllDimensionMembers();
+ ScDPSaveData aData( *pDPObj->GetSaveData() );
+ bool bChange = false;
+ bool bNeedReloadGroups = false;
+
+ DataPilotFieldOrientation nOrient = DataPilotFieldOrientation_HIDDEN;
+ tools::Long nField = pDPObj->GetHeaderDim( rPos, nOrient );
+ if ( nField >= 0 )
+ {
+ // changing a field title
+ if ( aData.GetExistingDimensionData() )
+ {
+ // only group dimensions can be renamed
+
+ ScDPDimensionSaveData* pDimData = aData.GetDimensionData();
+ ScDPSaveGroupDimension* pGroupDim = pDimData->GetNamedGroupDimAcc( aOldText );
+ if ( pGroupDim )
+ {
+ // valid name: not empty, no existing dimension (group or other)
+ if (!rString.isEmpty() && !pDPObj->IsDimNameInUse(rString))
+ {
+ pGroupDim->Rename( rString );
+
+ // also rename in SaveData to preserve the field settings
+ ScDPSaveDimension* pSaveDim = aData.GetDimensionByName( aOldText );
+ pSaveDim->SetName( rString );
+
+ bChange = true;
+ }
+ else
+ pErrorId = STR_INVALIDNAME;
+ }
+ }
+ else if (nOrient == DataPilotFieldOrientation_COLUMN || nOrient == DataPilotFieldOrientation_ROW)
+ {
+ bool bDataLayout = false;
+ OUString aDimName = pDPObj->GetDimName(nField, bDataLayout);
+ ScDPSaveDimension* pDim = bDataLayout ? aData.GetDataLayoutDimension() : aData.GetDimensionByName(aDimName);
+ if (pDim)
+ {
+ if (!rString.isEmpty())
+ {
+ if (rString.equalsIgnoreAsciiCase(aDimName))
+ {
+ pDim->RemoveLayoutName();
+ bChange = true;
+ }
+ else if (!pDPObj->IsDimNameInUse(rString))
+ {
+ pDim->SetLayoutName(rString);
+ bChange = true;
+ }
+ else
+ pErrorId = STR_INVALIDNAME;
+ }
+ else
+ pErrorId = STR_INVALIDNAME;
+ }
+ }
+ }
+ else if (pDPObj->IsDataDescriptionCell(rPos))
+ {
+ // There is only one data dimension.
+ ScDPSaveDimension* pDim = aData.GetFirstDimension(sheet::DataPilotFieldOrientation_DATA);
+ if (pDim)
+ {
+ if (!rString.isEmpty())
+ {
+ if (pDim->GetName().equalsIgnoreAsciiCase(rString))
+ {
+ pDim->RemoveLayoutName();
+ bChange = true;
+ }
+ else if (!pDPObj->IsDimNameInUse(rString))
+ {
+ pDim->SetLayoutName(rString);
+ bChange = true;
+ }
+ else
+ pErrorId = STR_INVALIDNAME;
+ }
+ else
+ pErrorId = STR_INVALIDNAME;
+ }
+ }
+ else
+ {
+ // This is not a field header.
+ sheet::DataPilotTableHeaderData aPosData;
+ pDPObj->GetHeaderPositionData(rPos, aPosData);
+
+ if ((aPosData.Flags & MemberResultFlags::HASMEMBER) && !aOldText.isEmpty())
+ {
+ if ( aData.GetExistingDimensionData() && !(aPosData.Flags & MemberResultFlags::SUBTOTAL))
+ {
+ bool bIsDataLayout;
+ OUString aDimName = pDPObj->GetDimName( aPosData.Dimension, bIsDataLayout );
+
+ ScDPDimensionSaveData* pDimData = aData.GetDimensionData();
+ ScDPSaveGroupDimension* pGroupDim = pDimData->GetNamedGroupDimAcc( aDimName );
+ if ( pGroupDim )
+ {
+ // valid name: not empty, no existing group in this dimension
+ //! ignore case?
+ if (!rString.isEmpty() && !pGroupDim->GetNamedGroup(rString))
+ {
+ ScDPSaveGroupItem* pGroup = pGroupDim->GetNamedGroupAcc( aOldText );
+ if ( pGroup )
+ pGroup->Rename( rString ); // rename the existing group
+ else
+ {
+ // create a new group to replace the automatic group
+ ScDPSaveGroupItem aGroup( rString );
+ aGroup.AddElement( aOldText );
+ pGroupDim->AddGroupItem( aGroup );
+ }
+
+ // in both cases also adjust savedata, to preserve member settings (show details)
+ ScDPSaveDimension* pSaveDim = aData.GetDimensionByName( aDimName );
+ ScDPSaveMember* pSaveMember = pSaveDim->GetExistingMemberByName( aOldText );
+ if ( pSaveMember )
+ pSaveMember->SetName( rString );
+
+ bChange = true;
+ bNeedReloadGroups = true;
+ }
+ else
+ pErrorId = STR_INVALIDNAME;
+ }
+ }
+ else if (aPosData.Flags & MemberResultFlags::GRANDTOTAL)
+ {
+ aData.SetGrandTotalName(rString);
+ bChange = true;
+ }
+ else if (aPosData.Dimension >= 0 && !aPosData.MemberName.isEmpty())
+ {
+ bool bDataLayout = false;
+ OUString aDimName = pDPObj->GetDimName(static_cast<tools::Long>(aPosData.Dimension), bDataLayout);
+ if (bDataLayout)
+ {
+ // data dimension
+ do
+ {
+ if (aPosData.Flags & MemberResultFlags::SUBTOTAL)
+ break;
+
+ ScDPSaveDimension* pDim = aData.GetDimensionByName(aPosData.MemberName);
+ if (!pDim)
+ break;
+
+ if (rString.isEmpty())
+ {
+ pErrorId = STR_INVALIDNAME;
+ break;
+ }
+
+ if (aPosData.MemberName.equalsIgnoreAsciiCase(rString))
+ {
+ pDim->RemoveLayoutName();
+ bChange = true;
+ }
+ else if (!pDPObj->IsDimNameInUse(rString))
+ {
+ pDim->SetLayoutName(rString);
+ bChange = true;
+ }
+ else
+ pErrorId = STR_INVALIDNAME;
+ }
+ while (false);
+ }
+ else
+ {
+ // field member
+ do
+ {
+ ScDPSaveDimension* pDim = aData.GetDimensionByName(aDimName);
+ if (!pDim)
+ break;
+
+ ScDPSaveMember* pMem = pDim->GetExistingMemberByName(aPosData.MemberName);
+ if (!pMem)
+ break;
+
+ if (aPosData.Flags & MemberResultFlags::SUBTOTAL)
+ {
+ // Change subtotal only when the table has one data dimension.
+ if (aData.GetDataDimensionCount() > 1)
+ break;
+
+ // display name for subtotal is allowed only if the subtotal type is 'Automatic'.
+ if (pDim->GetSubTotalsCount() != 1)
+ break;
+
+ if (pDim->GetSubTotalFunc(0) != ScGeneralFunction::AUTO)
+ break;
+
+ const std::optional<OUString> & pLayoutName = pMem->GetLayoutName();
+ OUString aMemberName;
+ if (pLayoutName)
+ aMemberName = *pLayoutName;
+ else
+ aMemberName = aPosData.MemberName;
+
+ OUString aNew = lcl_replaceMemberNameInSubtotal(rString, aMemberName);
+ pDim->SetSubtotalName(aNew);
+ bChange = true;
+ }
+ else
+ {
+ // Check to make sure the member name isn't
+ // already used.
+ if (!rString.isEmpty())
+ {
+ if (rString.equalsIgnoreAsciiCase(pMem->GetName()))
+ {
+ pMem->RemoveLayoutName();
+ bChange = true;
+ }
+ else if (!pDim->IsMemberNameInUse(rString))
+ {
+ pMem->SetLayoutName(rString);
+ bChange = true;
+ }
+ else
+ pErrorId = STR_INVALIDNAME;
+ }
+ else
+ pErrorId = STR_INVALIDNAME;
+ }
+ }
+ while (false);
+ }
+ }
+ }
+ }
+
+ if ( bChange )
+ {
+ // apply changes
+ ScDBDocFunc aFunc( *GetViewData().GetDocShell() );
+ pDPObj->SetSaveData( aData );
+ if (bNeedReloadGroups)
+ {
+ ScDPCollection* pDPs = rDoc.GetDPCollection();
+ if (pDPs)
+ {
+ o3tl::sorted_vector<ScDPObject*> aRefs;
+ // tdf#111305: Reload groups in cache after modifications.
+ pDPs->ReloadGroupsInCache(pDPObj, aRefs);
+ } // pDPs
+ } // bNeedReloadGroups
+ aFunc.UpdatePivotTable(*pDPObj, true, false);
+ }
+ else
+ {
+ if (!pErrorId)
+ pErrorId = STR_ERR_DATAPILOT_INPUT;
+ ErrorMessage(pErrorId);
+ }
+}
+
+static void lcl_MoveToEnd( ScDPSaveDimension& rDim, const OUString& rItemName )
+{
+ std::unique_ptr<ScDPSaveMember> pNewMember;
+ const ScDPSaveMember* pOldMember = rDim.GetExistingMemberByName( rItemName );
+ if ( pOldMember )
+ pNewMember.reset(new ScDPSaveMember( *pOldMember ));
+ else
+ pNewMember.reset(new ScDPSaveMember( rItemName ));
+ rDim.AddMember( std::move(pNewMember) );
+ // AddMember takes ownership of the new pointer,
+ // puts it to the end of the list even if it was in the list before.
+}
+
+namespace {
+
+struct ScOUStringCollate
+{
+ CollatorWrapper* mpCollator;
+
+ explicit ScOUStringCollate(CollatorWrapper* pColl) : mpCollator(pColl) {}
+
+ bool operator()(const OUString& rStr1, const OUString& rStr2) const
+ {
+ return ( mpCollator->compareString(rStr1, rStr2) < 0 );
+ }
+};
+
+}
+
+void ScDBFunc::DataPilotSort(ScDPObject* pDPObj, tools::Long nDimIndex, bool bAscending, const sal_uInt16* pUserListId)
+{
+ if (!pDPObj)
+ return;
+
+ // We need to run this to get all members later.
+ if ( pUserListId )
+ pDPObj->BuildAllDimensionMembers();
+
+ if (nDimIndex < 0)
+ // Invalid dimension index. Bail out.
+ return;
+
+ ScDPSaveData* pSaveData = pDPObj->GetSaveData();
+ if (!pSaveData)
+ return;
+
+ ScDPSaveData aNewSaveData(*pSaveData);
+ bool bDataLayout;
+ OUString aDimName = pDPObj->GetDimName(nDimIndex, bDataLayout);
+ ScDPSaveDimension* pSaveDim = aNewSaveData.GetDimensionByName(aDimName);
+ if (!pSaveDim)
+ return;
+
+ // manual evaluation of sort order is only needed if a user list id is given
+ if ( pUserListId )
+ {
+ typedef ScDPSaveDimension::MemberList MemList;
+ const MemList& rDimMembers = pSaveDim->GetMembers();
+ vector<OUString> aMembers;
+ std::unordered_set<OUString> aMemberSet;
+ size_t nMemberCount = 0;
+ for (ScDPSaveMember* pMem : rDimMembers)
+ {
+ aMembers.push_back(pMem->GetName());
+ aMemberSet.insert(pMem->GetName());
+ ++nMemberCount;
+ }
+
+ // Sort the member list in ascending order.
+ ScOUStringCollate aCollate( &ScGlobal::GetCollator() );
+ std::stable_sort(aMembers.begin(), aMembers.end(), aCollate);
+
+ // Collect and rank those custom sort strings that also exist in the member name list.
+
+ typedef std::unordered_map<OUString, sal_uInt16> UserSortMap;
+ UserSortMap aSubStrs;
+ sal_uInt16 nSubCount = 0;
+ ScUserList* pUserList = ScGlobal::GetUserList();
+ if (!pUserList)
+ return;
+
+ {
+ size_t n = pUserList->size();
+ if (!n || *pUserListId >= static_cast<sal_uInt16>(n))
+ return;
+ }
+
+ const ScUserListData& rData = (*pUserList)[*pUserListId];
+ sal_uInt16 n = rData.GetSubCount();
+ for (sal_uInt16 i = 0; i < n; ++i)
+ {
+ OUString aSub = rData.GetSubStr(i);
+ if (!aMemberSet.count(aSub))
+ // This string doesn't exist in the member name set. Don't add this.
+ continue;
+
+ aSubStrs.emplace(aSub, nSubCount++);
+ }
+
+ // Rank all members.
+
+ vector<OUString> aRankedNames(nMemberCount);
+ sal_uInt16 nCurStrId = 0;
+ for (auto const& aMemberName : aMembers)
+ {
+ sal_uInt16 nRank = 0;
+ UserSortMap::const_iterator itrSub = aSubStrs.find(aMemberName);
+ if (itrSub == aSubStrs.end())
+ nRank = nSubCount + nCurStrId++;
+ else
+ nRank = itrSub->second;
+
+ if (!bAscending)
+ nRank = static_cast< sal_uInt16 >( nMemberCount - nRank - 1 );
+
+ aRankedNames[nRank] = aMemberName;
+ }
+
+ // Re-order ScDPSaveMember instances with the new ranks.
+ for (auto const& aRankedName : aRankedNames)
+ {
+ const ScDPSaveMember* pOldMem = pSaveDim->GetExistingMemberByName(aRankedName);
+ if (!pOldMem)
+ // All members are supposed to be present.
+ continue;
+
+ pSaveDim->AddMember(std::unique_ptr<ScDPSaveMember>(new ScDPSaveMember(*pOldMem)));
+ }
+
+ // Set the sorting mode to manual for now. We may introduce a new sorting
+ // mode later on.
+
+ sheet::DataPilotFieldSortInfo aSortInfo;
+ aSortInfo.Mode = sheet::DataPilotFieldSortMode::MANUAL;
+ pSaveDim->SetSortInfo(&aSortInfo);
+ }
+ else
+ {
+ // without user list id, just apply sorting mode
+
+ sheet::DataPilotFieldSortInfo aSortInfo;
+ aSortInfo.Mode = sheet::DataPilotFieldSortMode::NAME;
+ aSortInfo.IsAscending = bAscending;
+ pSaveDim->SetSortInfo(&aSortInfo);
+ }
+
+ // Update the datapilot with the newly sorted field members.
+
+ std::unique_ptr<ScDPObject> pNewObj(new ScDPObject(*pDPObj));
+ pNewObj->SetSaveData(aNewSaveData);
+ ScDBDocFunc aFunc(*GetViewData().GetDocShell());
+
+ aFunc.DataPilotUpdate(pDPObj, pNewObj.get(), true, false);
+}
+
+bool ScDBFunc::DataPilotMove( const ScRange& rSource, const ScAddress& rDest )
+{
+ bool bRet = false;
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScDPObject* pDPObj = rDoc.GetDPAtCursor( rSource.aStart.Col(), rSource.aStart.Row(), rSource.aStart.Tab() );
+ if ( pDPObj && pDPObj == rDoc.GetDPAtCursor( rDest.Col(), rDest.Row(), rDest.Tab() ) )
+ {
+ sheet::DataPilotTableHeaderData aDestData;
+ pDPObj->GetHeaderPositionData( rDest, aDestData );
+ bool bValid = ( aDestData.Dimension >= 0 ); // dropping onto a field
+
+ // look through the source range
+ std::unordered_set< OUString > aMembersSet; // for lookup
+ std::vector< OUString > aMembersVector; // members in original order, for inserting
+ aMembersVector.reserve( std::max( static_cast<SCSIZE>( rSource.aEnd.Col() - rSource.aStart.Col() + 1 ),
+ static_cast<SCSIZE>( rSource.aEnd.Row() - rSource.aStart.Row() + 1 ) ) );
+ for (SCROW nRow = rSource.aStart.Row(); bValid && nRow <= rSource.aEnd.Row(); ++nRow )
+ for (SCCOL nCol = rSource.aStart.Col(); bValid && nCol <= rSource.aEnd.Col(); ++nCol )
+ {
+ sheet::DataPilotTableHeaderData aSourceData;
+ pDPObj->GetHeaderPositionData( ScAddress( nCol, nRow, rSource.aStart.Tab() ), aSourceData );
+ if ( aSourceData.Dimension == aDestData.Dimension && !aSourceData.MemberName.isEmpty() )
+ {
+ if ( aMembersSet.insert( aSourceData.MemberName ).second )
+ {
+ aMembersVector.push_back( aSourceData.MemberName );
+ }
+ // duplicates are ignored
+ }
+ else
+ bValid = false; // empty (subtotal) or different field
+ }
+
+ if ( bValid )
+ {
+ bool bIsDataLayout;
+ OUString aDimName = pDPObj->GetDimName( aDestData.Dimension, bIsDataLayout );
+ if ( !bIsDataLayout )
+ {
+ ScDPSaveData aData( *pDPObj->GetSaveData() );
+ ScDPSaveDimension* pDim = aData.GetDimensionByName( aDimName );
+
+ // get all member names in source order
+ uno::Sequence<OUString> aMemberNames;
+ pDPObj->GetMemberNames( aDestData.Dimension, aMemberNames );
+
+ bool bInserted = false;
+
+ for (const OUString& aMemberStr : std::as_const(aMemberNames))
+ {
+ if ( !bInserted && aMemberStr == aDestData.MemberName )
+ {
+ // insert dragged items before this item
+ for ( const auto& rMember : aMembersVector )
+ lcl_MoveToEnd( *pDim, rMember );
+ bInserted = true;
+ }
+
+ if ( aMembersSet.find( aMemberStr ) == aMembersSet.end() ) // skip dragged items
+ lcl_MoveToEnd( *pDim, aMemberStr );
+ }
+ // insert dragged item at end if dest wasn't found (for example, empty)
+ if ( !bInserted )
+ for ( const auto& rMember : aMembersVector )
+ lcl_MoveToEnd( *pDim, rMember );
+
+ // Items that were in SaveData, but not in the source, end up at the start of the list.
+
+ // set flag for manual sorting
+ sheet::DataPilotFieldSortInfo aSortInfo;
+ aSortInfo.Mode = sheet::DataPilotFieldSortMode::MANUAL;
+ pDim->SetSortInfo( &aSortInfo );
+
+ // apply changes
+ ScDBDocFunc aFunc( *GetViewData().GetDocShell() );
+ std::unique_ptr<ScDPObject> pNewObj(new ScDPObject( *pDPObj ));
+ pNewObj->SetSaveData( aData );
+ aFunc.DataPilotUpdate( pDPObj, pNewObj.get(), true, false ); //! bApi for drag&drop?
+ pNewObj.reset();
+
+ Unmark(); // entry was moved - no use in leaving the old cell selected
+
+ bRet = true;
+ }
+ }
+ }
+
+ return bRet;
+}
+
+bool ScDBFunc::HasSelectionForDrillDown( css::sheet::DataPilotFieldOrientation& rOrientation )
+{
+ bool bRet = false;
+
+ ScDPObject* pDPObj = GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
+ GetViewData().GetCurY(), GetViewData().GetTabNo() );
+ if ( pDPObj )
+ {
+ ScDPUniqueStringSet aEntries;
+ tools::Long nSelectDimension = -1;
+ GetSelectedMemberList( aEntries, nSelectDimension );
+
+ if (!aEntries.empty())
+ {
+ bool bIsDataLayout;
+ OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
+ if ( !bIsDataLayout )
+ {
+ ScDPSaveData* pSaveData = pDPObj->GetSaveData();
+ ScDPSaveDimension* pDim = pSaveData->GetExistingDimensionByName( aDimName );
+ if ( pDim )
+ {
+ css::sheet::DataPilotFieldOrientation nDimOrient = pDim->GetOrientation();
+ ScDPSaveDimension* pInner = pSaveData->GetInnermostDimension( nDimOrient );
+ if ( pDim == pInner )
+ {
+ rOrientation = nDimOrient;
+ bRet = true;
+ }
+ }
+ }
+ }
+ }
+
+ return bRet;
+}
+
+void ScDBFunc::SetDataPilotDetails(bool bShow, const OUString* pNewDimensionName)
+{
+ ScDPObject* pDPObj = GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
+ GetViewData().GetCurY(), GetViewData().GetTabNo() );
+ if ( !pDPObj )
+ return;
+
+ ScDPUniqueStringSet aEntries;
+ tools::Long nSelectDimension = -1;
+ GetSelectedMemberList( aEntries, nSelectDimension );
+
+ if (aEntries.empty())
+ return;
+
+ bool bIsDataLayout;
+ OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
+ if ( bIsDataLayout )
+ return;
+
+ ScDPSaveData aData( *pDPObj->GetSaveData() );
+ ScDPSaveDimension* pDim = aData.GetDimensionByName( aDimName );
+
+ if ( bShow && pNewDimensionName )
+ {
+ // add the new dimension with the same orientation, at the end
+
+ ScDPSaveDimension* pNewDim = aData.GetDimensionByName( *pNewDimensionName );
+ ScDPSaveDimension* pDuplicated = nullptr;
+ if ( pNewDim->GetOrientation() == sheet::DataPilotFieldOrientation_DATA )
+ {
+ // Need to duplicate the dimension, create column/row in addition to data:
+ // The duplicated dimension inherits the existing settings, pNewDim is modified below.
+ pDuplicated = aData.DuplicateDimension( *pNewDimensionName );
+ }
+
+ css::sheet::DataPilotFieldOrientation nOrientation = pDim->GetOrientation();
+ pNewDim->SetOrientation( nOrientation );
+
+ tools::Long nPosition = LONG_MAX;
+ aData.SetPosition( pNewDim, nPosition );
+
+ ScDPSaveDimension* pDataLayout = aData.GetDataLayoutDimension();
+ if ( pDataLayout->GetOrientation() == nOrientation &&
+ aData.GetDataDimensionCount() <= 1 )
+ {
+ // If there is only one data dimension, the data layout dimension
+ // must still be the last one in its orientation.
+ aData.SetPosition( pDataLayout, nPosition );
+ }
+
+ if ( pDuplicated )
+ {
+ // The duplicated (data) dimension needs to be behind the original dimension
+ aData.SetPosition( pDuplicated, nPosition );
+ }
+
+ // Hide details for all visible members (selected are changed below).
+ //! Use all members from source level instead (including non-visible)?
+
+ ScDPUniqueStringSet aVisibleEntries;
+ pDPObj->GetMemberResultNames( aVisibleEntries, nSelectDimension );
+
+ for (const OUString& aVisName : aVisibleEntries)
+ {
+ ScDPSaveMember* pMember = pDim->GetMemberByName( aVisName );
+ pMember->SetShowDetails( false );
+ }
+ }
+
+ for (const auto& rEntry : aEntries)
+ {
+ ScDPSaveMember* pMember = pDim->GetMemberByName(rEntry);
+ pMember->SetShowDetails( bShow );
+ }
+
+ // apply changes
+ ScDBDocFunc aFunc( *GetViewData().GetDocShell() );
+ std::unique_ptr<ScDPObject> pNewObj(new ScDPObject( *pDPObj ));
+ pNewObj->SetSaveData( aData );
+ aFunc.DataPilotUpdate( pDPObj, pNewObj.get(), true, false );
+ pNewObj.reset();
+
+ // unmark cell selection
+ Unmark();
+}
+
+void ScDBFunc::ShowDataPilotSourceData( ScDPObject& rDPObj, const Sequence<sheet::DataPilotFieldFilter>& rFilters )
+{
+ ScDocument& rDoc = GetViewData().GetDocument();
+ if (rDoc.GetDocumentShell()->IsReadOnly())
+ {
+ ErrorMessage(STR_READONLYERR);
+ return;
+ }
+
+ Reference<sheet::XDimensionsSupplier> xDimSupplier = rDPObj.GetSource();
+ Reference<container::XNameAccess> xDims = xDimSupplier->getDimensions();
+ Reference<sheet::XDrillDownDataSupplier> xDDSupplier(xDimSupplier, UNO_QUERY);
+ if (!xDDSupplier.is())
+ return;
+
+ Sequence< Sequence<Any> > aTabData = xDDSupplier->getDrillDownData(rFilters);
+ sal_Int32 nRowSize = aTabData.getLength();
+ if (nRowSize <= 1)
+ // There is no data to show. Bail out.
+ return;
+
+ SCCOL nColSize = aTabData[0].getLength();
+
+ SCTAB nNewTab = GetViewData().GetTabNo();
+
+ ScDocumentUniquePtr pInsDoc(new ScDocument(SCDOCMODE_CLIP));
+ pInsDoc->ResetClip( &rDoc, nNewTab );
+ for (SCROW nRow = 0; nRow < nRowSize; ++nRow)
+ {
+ for (SCCOL nCol = 0; nCol < nColSize; ++nCol)
+ {
+ const Any& rAny = aTabData[nRow][nCol];
+ OUString aStr;
+ double fVal;
+ if (rAny >>= aStr)
+ {
+ pInsDoc->SetString(ScAddress(nCol,nRow,nNewTab), aStr);
+ }
+ else if (rAny >>= fVal)
+ pInsDoc->SetValue(nCol, nRow, nNewTab, fVal);
+ }
+ }
+
+ // set number format (important for dates)
+ for (SCCOL nCol = 0; nCol < nColSize; ++nCol)
+ {
+ OUString aStr;
+ if (!(aTabData[0][nCol] >>= aStr))
+ continue;
+
+ Reference<XPropertySet> xPropSet(xDims->getByName(aStr), UNO_QUERY);
+ if (!xPropSet.is())
+ continue;
+
+ Any any = xPropSet->getPropertyValue( SC_UNO_DP_NUMBERFO );
+ sal_Int32 nNumFmt = 0;
+ if (!(any >>= nNumFmt))
+ continue;
+
+ ScPatternAttr aPattern( pInsDoc->GetPool() );
+ aPattern.GetItemSet().Put( SfxUInt32Item(ATTR_VALUE_FORMAT, static_cast<sal_uInt32>(nNumFmt)) );
+ pInsDoc->ApplyPatternAreaTab(nCol, 1, nCol, nRowSize-1, nNewTab, aPattern);
+ }
+
+ SCCOL nEndCol = 0;
+ SCROW nEndRow = 0;
+ pInsDoc->GetCellArea( nNewTab, nEndCol, nEndRow );
+ pInsDoc->SetClipArea( ScRange( 0, 0, nNewTab, nEndCol, nEndRow, nNewTab ) );
+
+ SfxUndoManager* pMgr = GetViewData().GetDocShell()->GetUndoManager();
+ OUString aUndo = ScResId( STR_UNDO_DOOUTLINE );
+ pMgr->EnterListAction( aUndo, aUndo, 0, GetViewData().GetViewShell()->GetViewShellId() );
+
+ OUString aNewTabName;
+ rDoc.CreateValidTabName(aNewTabName);
+ if ( InsertTable(aNewTabName, nNewTab) )
+ PasteFromClip( InsertDeleteFlags::ALL, pInsDoc.get() );
+
+ pMgr->LeaveListAction();
+}
+
+// repeat data base operations (sorting, filtering, subtotals)
+
+void ScDBFunc::RepeatDB( bool bRecord )
+{
+ SCCOL nCurX = GetViewData().GetCurX();
+ SCROW nCurY = GetViewData().GetCurY();
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScDBData* pDBData = GetDBData();
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ ScQueryParam aQueryParam;
+ pDBData->GetQueryParam( aQueryParam );
+ bool bQuery = aQueryParam.GetEntry(0).bDoQuery;
+
+ ScSortParam aSortParam;
+ pDBData->GetSortParam( aSortParam );
+ bool bSort = aSortParam.maKeyState[0].bDoSort;
+
+ ScSubTotalParam aSubTotalParam;
+ pDBData->GetSubTotalParam( aSubTotalParam );
+ bool bSubTotal = aSubTotalParam.bGroupActive[0] && !aSubTotalParam.bRemoveOnly;
+
+ if ( bQuery || bSort || bSubTotal )
+ {
+ bool bQuerySize = false;
+ ScRange aOldQuery;
+ ScRange aNewQuery;
+ if (bQuery && !aQueryParam.bInplace)
+ {
+ ScDBData* pDest = rDoc.GetDBAtCursor( aQueryParam.nDestCol, aQueryParam.nDestRow,
+ aQueryParam.nDestTab, ScDBDataPortion::TOP_LEFT );
+ if (pDest && pDest->IsDoSize())
+ {
+ pDest->GetArea( aOldQuery );
+ bQuerySize = true;
+ }
+ }
+
+ SCTAB nDummy;
+ SCCOL nStartCol;
+ SCROW nStartRow;
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ pDBData->GetArea( nDummy, nStartCol, nStartRow, nEndCol, nEndRow );
+
+ //! undo only needed data ?
+
+ ScDocumentUniquePtr pUndoDoc;
+ std::unique_ptr<ScOutlineTable> pUndoTab;
+ std::unique_ptr<ScRangeName> pUndoRange;
+ std::unique_ptr<ScDBCollection> pUndoDB;
+
+ if (bRecord)
+ {
+ SCTAB nTabCount = rDoc.GetTableCount();
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab );
+ if (pTable)
+ {
+ pUndoTab.reset(new ScOutlineTable( *pTable ));
+
+ SCCOLROW nOutStartCol; // row/column status
+ SCCOLROW nOutStartRow;
+ SCCOLROW nOutEndCol;
+ SCCOLROW nOutEndRow;
+ pTable->GetColArray().GetRange( nOutStartCol, nOutEndCol );
+ pTable->GetRowArray().GetRange( nOutStartRow, nOutEndRow );
+
+ pUndoDoc->InitUndo( rDoc, nTab, nTab, true, true );
+ rDoc.CopyToDocument( static_cast<SCCOL>(nOutStartCol), 0, nTab, static_cast<SCCOL>(nOutEndCol), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE, false, *pUndoDoc );
+ rDoc.CopyToDocument( 0, nOutStartRow, nTab, rDoc.MaxCol(), nOutEndRow, nTab, InsertDeleteFlags::NONE, false, *pUndoDoc );
+ }
+ else
+ pUndoDoc->InitUndo( rDoc, nTab, nTab, false, true );
+
+ // Record data range - including filter results
+ rDoc.CopyToDocument( 0,nStartRow,nTab, rDoc.MaxCol(),nEndRow,nTab, InsertDeleteFlags::ALL, false, *pUndoDoc );
+
+ // all formulas for reference
+ rDoc.CopyToDocument( 0,0,0, rDoc.MaxCol(),rDoc.MaxRow(),nTabCount-1, InsertDeleteFlags::FORMULA, false, *pUndoDoc );
+
+ // data base and other ranges
+ ScRangeName* pDocRange = rDoc.GetRangeName();
+ if (!pDocRange->empty())
+ pUndoRange.reset(new ScRangeName( *pDocRange ));
+ ScDBCollection* pDocDB = rDoc.GetDBCollection();
+ if (!pDocDB->empty())
+ pUndoDB.reset(new ScDBCollection( *pDocDB ));
+ }
+
+ if (bSort && bSubTotal)
+ {
+ // sort without subtotals
+
+ aSubTotalParam.bRemoveOnly = true; // is reset below
+ DoSubTotals( aSubTotalParam, false );
+ }
+
+ if (bSort)
+ {
+ pDBData->GetSortParam( aSortParam ); // range may have changed
+ Sort( aSortParam, false, false);
+ }
+ if (bQuery)
+ {
+ pDBData->GetQueryParam( aQueryParam ); // range may have changed
+ ScRange aAdvSource;
+ if (pDBData->GetAdvancedQuerySource(aAdvSource))
+ {
+ rDoc.CreateQueryParam(aAdvSource, aQueryParam);
+ Query( aQueryParam, &aAdvSource, false );
+ }
+ else
+ Query( aQueryParam, nullptr, false );
+
+ // if not inplace the sheet may have changed
+ if ( !aQueryParam.bInplace && aQueryParam.nDestTab != nTab )
+ SetTabNo( nTab );
+ }
+ if (bSubTotal)
+ {
+ pDBData->GetSubTotalParam( aSubTotalParam ); // range may have changed
+ aSubTotalParam.bRemoveOnly = false;
+ DoSubTotals( aSubTotalParam, false );
+ }
+
+ if (bRecord)
+ {
+ SCTAB nDummyTab;
+ SCCOL nDummyCol;
+ SCROW nDummyRow, nNewEndRow;
+ pDBData->GetArea( nDummyTab, nDummyCol,nDummyRow, nDummyCol,nNewEndRow );
+
+ const ScRange* pOld = nullptr;
+ const ScRange* pNew = nullptr;
+ if (bQuerySize)
+ {
+ ScDBData* pDest = rDoc.GetDBAtCursor( aQueryParam.nDestCol, aQueryParam.nDestRow,
+ aQueryParam.nDestTab, ScDBDataPortion::TOP_LEFT );
+ if (pDest)
+ {
+ pDest->GetArea( aNewQuery );
+ pOld = &aOldQuery;
+ pNew = &aNewQuery;
+ }
+ }
+
+ GetViewData().GetDocShell()->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoRepeatDB>( GetViewData().GetDocShell(), nTab,
+ nStartCol, nStartRow, nEndCol, nEndRow,
+ nNewEndRow,
+ nCurX, nCurY,
+ std::move(pUndoDoc), std::move(pUndoTab),
+ std::move(pUndoRange), std::move(pUndoDB),
+ pOld, pNew ) );
+ }
+
+ GetViewData().GetDocShell()->PostPaint(
+ ScRange(0, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab),
+ PaintPartFlags::Grid | PaintPartFlags::Left | PaintPartFlags::Top | PaintPartFlags::Size);
+ }
+ else // "no not execute any operations"
+ ErrorMessage(STR_MSSG_REPEATDB_0);
+}
+
+void ScDBFunc::OnLOKShowHideColRow(bool bColumns, SCCOLROW nStart)
+{
+ if (!comphelper::LibreOfficeKit::isActive())
+ return;
+
+ SCTAB nCurrentTabIndex = GetViewData().GetTabNo();
+ SfxViewShell* pThisViewShell = GetViewData().GetViewShell();
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
+ if (pTabViewShell && pTabViewShell->GetDocId() == pThisViewShell->GetDocId())
+ {
+ if (bColumns)
+ {
+ if (ScPositionHelper* pPosHelper = pTabViewShell->GetViewData().GetLOKWidthHelper(nCurrentTabIndex))
+ pPosHelper->invalidateByIndex(nStart);
+ }
+ else
+ {
+ if (ScPositionHelper* pPosHelper = pTabViewShell->GetViewData().GetLOKHeightHelper(nCurrentTabIndex))
+ pPosHelper->invalidateByIndex(nStart);
+ }
+
+ if (pTabViewShell->getPart() == nCurrentTabIndex)
+ {
+ pTabViewShell->ShowCursor();
+ pTabViewShell->MarkDataChanged();
+ }
+ }
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/dbfunc4.cxx b/sc/source/ui/view/dbfunc4.cxx
new file mode 100644
index 0000000000..f13035b291
--- /dev/null
+++ b/sc/source/ui/view/dbfunc4.cxx
@@ -0,0 +1,73 @@
+/* -*- 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 .
+ */
+
+#include <svx/svditer.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdpage.hxx>
+#include <osl/diagnose.h>
+
+#include <dbfunc.hxx>
+#include <drwlayer.hxx>
+#include <document.hxx>
+
+using namespace com::sun::star;
+
+sal_uInt16 ScDBFunc::DoUpdateCharts(const ScAddress& rPos, ScDocument& rDoc, bool bAllCharts)
+{
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ if (!pModel)
+ return 0;
+
+ sal_uInt16 nFound = 0;
+
+ sal_uInt16 nPageCount = pModel->GetPageCount();
+ for (sal_uInt16 nPageNo = 0; nPageNo < nPageCount; nPageNo++)
+ {
+ SdrPage* pPage = pModel->GetPage(nPageNo);
+ OSL_ENSURE(pPage, "Page ?");
+
+ SdrObjListIter aIter(pPage, SdrIterMode::DeepNoGroups);
+ SdrObject* pObject = aIter.Next();
+ while (pObject)
+ {
+ if (pObject->GetObjIdentifier() == SdrObjKind::OLE2 && ScDocument::IsChart(pObject))
+ {
+ OUString aName = static_cast<SdrOle2Obj*>(pObject)->GetPersistName();
+ bool bHit = true;
+ if (!bAllCharts)
+ {
+ ScRangeList aRanges;
+ bool bColHeaders = false;
+ bool bRowHeaders = false;
+ rDoc.GetOldChartParameters(aName, aRanges, bColHeaders, bRowHeaders);
+ bHit = aRanges.Contains(rPos);
+ }
+ if (bHit)
+ {
+ rDoc.UpdateChart(aName);
+ ++nFound;
+ }
+ }
+ pObject = aIter.Next();
+ }
+ }
+ return nFound;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/drawutil.cxx b/sc/source/ui/view/drawutil.cxx
new file mode 100644
index 0000000000..9658fa7ff2
--- /dev/null
+++ b/sc/source/ui/view/drawutil.cxx
@@ -0,0 +1,89 @@
+/* -*- 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 .
+ */
+
+#include <sal/config.h>
+
+#include <o3tl/unit_conversion.hxx>
+#include <vcl/outdev.hxx>
+
+#include <drawutil.hxx>
+#include <document.hxx>
+#include <viewdata.hxx>
+
+void ScDrawUtil::CalcScale( const ScDocument& rDoc, SCTAB nTab,
+ SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
+ const OutputDevice* pDev,
+ const Fraction& rZoomX, const Fraction& rZoomY,
+ double nPPTX, double nPPTY,
+ Fraction& rScaleX, Fraction& rScaleY )
+{
+ tools::Long nPixelX = 0;
+ tools::Long nTwipsX = 0;
+ tools::Long nPixelY = 0;
+ tools::Long nTwipsY = 0;
+ for (SCCOL i=nStartCol; i<nEndCol; i++)
+ {
+ sal_uInt16 nWidth = rDoc.GetColWidth(i,nTab);
+ nTwipsX += static_cast<tools::Long>(nWidth);
+ nPixelX += ScViewData::ToPixel( nWidth, nPPTX );
+ }
+
+ for (SCROW nRow = nStartRow; nRow <= nEndRow-1; ++nRow)
+ {
+ SCROW nLastRow = nRow;
+ if (rDoc.RowHidden(nRow, nTab, nullptr, &nLastRow))
+ {
+ nRow = nLastRow;
+ continue;
+ }
+
+ sal_uInt16 nHeight = rDoc.GetRowHeight(nRow, nTab);
+ nTwipsY += static_cast<tools::Long>(nHeight);
+ nPixelY += ScViewData::ToPixel(nHeight, nPPTY);
+ }
+
+ MapMode aHMMMode( MapUnit::Map100thMM, Point(), rZoomX, rZoomY );
+ Point aPixelLog = pDev->PixelToLogic( Point( nPixelX,nPixelY ), aHMMMode );
+
+ // Fraction(double) ctor can be used here (and avoid overflows of PixelLog * Zoom)
+ // because ReduceInaccurate is called later anyway.
+
+ if ( aPixelLog.X() && nTwipsX )
+ rScaleX = Fraction( static_cast<double>(aPixelLog.X()) *
+ static_cast<double>(rZoomX.GetNumerator()) /
+ o3tl::convert<double>(nTwipsX, o3tl::Length::twip, o3tl::Length::mm100) /
+ static_cast<double>(rZoomX.GetDenominator()) );
+ else
+ rScaleX = Fraction( 1, 1 );
+
+ if ( aPixelLog.Y() && nTwipsY )
+ rScaleY = Fraction( static_cast<double>(aPixelLog.Y()) *
+ static_cast<double>(rZoomY.GetNumerator()) /
+ o3tl::convert<double>(nTwipsY, o3tl::Length::twip, o3tl::Length::mm100) /
+ static_cast<double>(rZoomY.GetDenominator()) );
+ else
+ rScaleY = Fraction( 1, 1 );
+
+ // 25 bits of accuracy are needed to always hit the right part of
+ // cells in the last rows (was 17 before 1M rows).
+ rScaleX.ReduceInaccurate( 25 );
+ rScaleY.ReduceInaccurate( 25 );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/drawvie3.cxx b/sc/source/ui/view/drawvie3.cxx
new file mode 100644
index 0000000000..6561423ab2
--- /dev/null
+++ b/sc/source/ui/view/drawvie3.cxx
@@ -0,0 +1,259 @@
+/* -*- 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 .
+ */
+
+#include <sal/config.h>
+
+#include <cstdlib>
+
+#include <svx/svdograf.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/ImageMapInfo.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <comphelper/lok.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+
+#include <strings.hrc>
+#include <scresid.hxx>
+#include <drawview.hxx>
+#include <drwlayer.hxx>
+#include "imapwrap.hxx"
+#include <viewdata.hxx>
+#include <document.hxx>
+#include <userdat.hxx>
+#include <tabvwsh.hxx>
+#include <docsh.hxx>
+
+ScDrawView::ScDrawView(
+ OutputDevice* pOut,
+ ScViewData* pData )
+: FmFormView(*pData->GetDocument().GetDrawLayer(), pOut),
+ pViewData( pData ),
+ pDev( pOut ),
+ rDoc( pData->GetDocument() ),
+ nTab( pData->GetTabNo() ),
+ pDropMarkObj( nullptr ),
+ bInConstruct( true )
+{
+ SetNegativeX(comphelper::LibreOfficeKit::isActive() && rDoc.IsLayoutRTL(nTab));
+ // #i73602# Use default from the configuration
+ SetBufferedOverlayAllowed(SvtOptionsDrawinglayer::IsOverlayBuffer_Calc());
+
+ // #i74769#, #i75172# Use default from the configuration
+ SetBufferedOutputAllowed(SvtOptionsDrawinglayer::IsPaintBuffer_Calc());
+
+ Construct();
+}
+
+// set anchor
+
+void ScDrawView::SetPageAnchored()
+{
+ if( !AreObjectsMarked() )
+ return;
+
+ const SdrMarkList* pMark = &GetMarkedObjectList();
+ const size_t nCount = pMark->GetMarkCount();
+
+ BegUndo(ScResId(SCSTR_UNDO_PAGE_ANCHOR));
+ for( size_t i=0; i<nCount; ++i )
+ {
+ SdrObject* pObj = pMark->GetMark(i)->GetMarkedSdrObj();
+ AddUndo (std::make_unique<ScUndoAnchorData>( pObj, &rDoc, nTab ));
+ ScDrawLayer::SetPageAnchored( *pObj );
+ }
+ EndUndo();
+
+ if ( pViewData )
+ pViewData->GetDocShell()->SetDrawModified();
+
+ // Remove the anchor object.
+ maHdlList.RemoveAllByKind(SdrHdlKind::Anchor);
+ maHdlList.RemoveAllByKind(SdrHdlKind::Anchor_TR);
+}
+
+void ScDrawView::SetCellAnchored(bool bResizeWithCell)
+{
+ if( !AreObjectsMarked() )
+ return;
+
+ const SdrMarkList* pMark = &GetMarkedObjectList();
+ const size_t nCount = pMark->GetMarkCount();
+
+ BegUndo(ScResId(SCSTR_UNDO_CELL_ANCHOR));
+ for( size_t i=0; i<nCount; ++i )
+ {
+ SdrObject* pObj = pMark->GetMark(i)->GetMarkedSdrObj();
+ AddUndo (std::make_unique<ScUndoAnchorData>( pObj, &rDoc, nTab ));
+ ScDrawLayer::SetCellAnchoredFromPosition(*pObj, rDoc, nTab, bResizeWithCell);
+ }
+ EndUndo();
+
+ if ( pViewData )
+ {
+ pViewData->GetDocShell()->SetDrawModified();
+
+ // Set the anchor object.
+ AddCustomHdl();
+ }
+}
+
+ScAnchorType ScDrawView::GetAnchorType() const
+{
+ bool bPage = false;
+ bool bCell = false;
+ bool bCellResize = false;
+ if( AreObjectsMarked() )
+ {
+ const SdrMarkList* pMark = &GetMarkedObjectList();
+ const size_t nCount = pMark->GetMarkCount();
+ for( size_t i=0; i<nCount; ++i )
+ {
+ const SdrObject* pObj = pMark->GetMark(i)->GetMarkedSdrObj();
+ const ScAnchorType aAnchorType = ScDrawLayer::GetAnchorType( *pObj );
+ if( aAnchorType == SCA_CELL )
+ bCell =true;
+ else if (aAnchorType == SCA_CELL_RESIZE)
+ bCellResize = true;
+ else
+ bPage = true;
+ }
+ }
+ if( bPage && !bCell && !bCellResize )
+ return SCA_PAGE;
+ if( !bPage && bCell && !bCellResize )
+ return SCA_CELL;
+ if( !bPage && !bCell && bCellResize )
+ return SCA_CELL_RESIZE;
+ return SCA_DONTKNOW;
+}
+
+namespace {
+
+bool lcl_AreRectanglesApproxEqual(const tools::Rectangle& rRectA, const tools::Rectangle& rRectB)
+{
+ // Twips <-> Hmm conversions introduce +-1 differences although the rectangles should actually
+ // be equal. Therefore test with == is not appropriate in some cases.
+ if (std::abs(rRectA.Left() - rRectB.Left()) > 1)
+ return false;
+ if (std::abs(rRectA.Top() - rRectB.Top()) > 1)
+ return false;
+ if (std::abs(rRectA.Right() - rRectB.Right()) > 1)
+ return false;
+ if (std::abs(rRectA.Bottom() - rRectB.Bottom()) > 1)
+ return false;
+ return true;
+}
+
+/**
+ * Updated the anchors of any non-note object that is cell anchored which
+ * has been moved since the last anchors for its position was calculated.
+ */
+void adjustAnchoredPosition(const SdrHint& rHint, const ScDocument& rDoc, SCTAB nTab)
+{
+ if (rHint.GetKind() != SdrHintKind::ObjectChange && rHint.GetKind() != SdrHintKind::ObjectInserted)
+ return;
+
+ SdrObject* pObj = const_cast<SdrObject*>(rHint.GetObject());
+ if (!pObj)
+ return;
+
+ ScDrawObjData *pAnchor = ScDrawLayer::GetObjData(pObj);
+ if (!pAnchor)
+ return;
+
+ if (pAnchor->meType == ScDrawObjData::CellNote)
+ return;
+
+ // SetCellAnchoredFromPosition has to be called only if shape geometry has been changed, and not
+ // if only shape visibility has been changed. It is not enough to test shape rect, because e.g. a
+ // 180deg rotation changes only the logic rect (tdf#139583).
+ ScDrawObjData& rNoRotatedAnchor = *ScDrawLayer::GetNonRotatedObjData(pObj, true /*bCreate*/);
+ if (lcl_AreRectanglesApproxEqual(pAnchor->getShapeRect(), pObj->GetSnapRect())
+ && lcl_AreRectanglesApproxEqual(rNoRotatedAnchor.getShapeRect(), pObj->GetLogicRect()))
+ return;
+
+ if (pAnchor->maStart.Tab() != nTab)
+ // The object is not anchored on the current sheet. Skip it.
+ // TODO: In the future, we may want to adjust objects that are
+ // anchored on all selected sheets.
+ return;
+
+ ScDrawLayer::SetCellAnchoredFromPosition(*pObj, rDoc, pAnchor->maStart.Tab(), pAnchor->mbResizeWithCell);
+}
+
+}
+
+void ScDrawView::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint)
+ {
+ const SdrHint* pSdrHint = static_cast<const SdrHint*>( &rHint );
+ adjustAnchoredPosition(*pSdrHint, rDoc, nTab);
+ FmFormView::Notify( rBC,rHint );
+ }
+ else if (auto pDeletedHint = dynamic_cast<const ScTabDeletedHint*>(&rHint)) // Sheet has been deleted
+ {
+ SCTAB nDelTab = pDeletedHint->GetTab();
+ if (ValidTab(nDelTab))
+ {
+ // used to be: HidePagePgNum(nDelTab) - hide only if the deleted sheet is shown here
+ if ( nDelTab == nTab )
+ HideSdrPage();
+ }
+ }
+ else if (auto pChangedHint = dynamic_cast<const ScTabSizeChangedHint*>(&rHint)) // Size has been changed
+ {
+ if ( nTab == pChangedHint->GetTab() )
+ UpdateWorkArea();
+ }
+ else
+ FmFormView::Notify( rBC,rHint );
+}
+
+void ScDrawView::UpdateIMap( SdrObject* pObj )
+{
+ if ( !(pViewData &&
+ pViewData->GetViewShell()->GetViewFrame().HasChildWindow( ScIMapChildWindowId() ) &&
+ pObj && ( dynamic_cast<const SdrGrafObj*>( pObj) != nullptr || dynamic_cast<const SdrOle2Obj*>( pObj) != nullptr )) )
+ return;
+
+ Graphic aGraphic;
+ TargetList aTargetList;
+ SvxIMapInfo* pIMapInfo = SvxIMapInfo::GetIMapInfo( pObj );
+ const ImageMap* pImageMap = nullptr;
+ if ( pIMapInfo )
+ pImageMap = &pIMapInfo->GetImageMap();
+
+ // handle target list
+ SfxViewFrame::GetTargetList( aTargetList );
+
+ // handle graphics from object
+ if ( auto pGrafObj = dynamic_cast<SdrGrafObj*>( pObj) )
+ aGraphic = pGrafObj->GetGraphic();
+ else
+ {
+ const Graphic* pGraphic = static_cast<const SdrOle2Obj*>(pObj)->GetGraphic();
+ if ( pGraphic )
+ aGraphic = *pGraphic;
+ }
+
+ ScIMapDlgSet( aGraphic, pImageMap, &aTargetList, pObj ); // from imapwrap
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/drawvie4.cxx b/sc/source/ui/view/drawvie4.cxx
new file mode 100644
index 0000000000..2bd3290982
--- /dev/null
+++ b/sc/source/ui/view/drawvie4.cxx
@@ -0,0 +1,572 @@
+/* -*- 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 .
+ */
+
+#include <svx/svditer.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdundo.hxx>
+#include <sfx2/docfile.hxx>
+#include <tools/urlobj.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+#include <drawview.hxx>
+#include <global.hxx>
+#include <drwlayer.hxx>
+#include <viewdata.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <drwtrans.hxx>
+#include <transobj.hxx>
+#include <drawutil.hxx>
+#include <scmod.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <gridwin.hxx>
+#include <userdat.hxx>
+
+#include <com/sun/star/embed/NoVisualAreaSizeException.hpp>
+#include <com/sun/star/embed/Aspects.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/chart2/XChartTypeContainer.hpp>
+#include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
+#include <com/sun/star/chart2/XDataSeriesContainer.hpp>
+#include <com/sun/star/chart2/XChartDocument.hpp>
+#include <comphelper/diagnose_ex.hxx>
+
+using namespace com::sun::star;
+
+Point aDragStartDiff;
+
+void ScDrawView::BeginDrag( vcl::Window* pWindow, const Point& rStartPos )
+{
+ if ( !AreObjectsMarked() )
+ return;
+
+ BrkAction();
+
+ tools::Rectangle aMarkedRect = GetAllMarkedRect();
+
+ aDragStartDiff = rStartPos - aMarkedRect.TopLeft();
+
+ bool bAnyOle, bOneOle;
+ const SdrMarkList& rMarkList = GetMarkedObjectList();
+ CheckOle( rMarkList, bAnyOle, bOneOle );
+
+ ScDocShellRef aDragShellRef;
+ if (bAnyOle)
+ {
+ aDragShellRef = new ScDocShell; // DocShell needs a Ref immediately
+ aDragShellRef->DoInitNew();
+ }
+ ScDrawLayer::SetGlobalDrawPersist( aDragShellRef.get() );
+ std::unique_ptr<SdrModel> pModel(CreateMarkedObjModel());
+ ScDrawLayer::SetGlobalDrawPersist(nullptr);
+
+ // Charts now always copy their data in addition to the source reference, so
+ // there's no need to call SchDLL::Update for the charts in the clipboard doc.
+ // Update with the data (including NumberFormatter) from the live document would
+ // also store the NumberFormatter in the clipboard chart (#88749#)
+
+ ScDocShell* pDocSh = pViewData->GetDocShell();
+
+ TransferableObjectDescriptor aObjDesc;
+ pDocSh->FillTransferableObjectDescriptor( aObjDesc );
+ aObjDesc.maDisplayName = pDocSh->GetMedium()->GetURLObject().GetURLNoPass();
+ // maSize is set in ScDrawTransferObj ctor
+
+ rtl::Reference<ScDrawTransferObj> pTransferObj = new ScDrawTransferObj( std::move(pModel), pDocSh, std::move(aObjDesc) );
+
+ pTransferObj->SetDrawPersist( aDragShellRef.get() ); // keep persist for ole objects alive
+ pTransferObj->SetDragSource( this ); // copies selection
+
+ SC_MOD()->SetDragObject( nullptr, pTransferObj.get() ); // for internal D&D
+ pTransferObj->StartDrag( pWindow, DND_ACTION_COPYMOVE | DND_ACTION_LINK );
+}
+
+namespace {
+
+void getRangeFromDataSource( uno::Reference< chart2::data::XDataSource > const & xDataSource, std::vector<OUString>& rRangeRep)
+{
+ const uno::Sequence<uno::Reference<chart2::data::XLabeledDataSequence> > xSeqs = xDataSource->getDataSequences();
+ for (const uno::Reference<chart2::data::XLabeledDataSequence>& xLS : xSeqs)
+ {
+ uno::Reference<chart2::data::XDataSequence> xSeq = xLS->getValues();
+ if (xSeq.is())
+ {
+ OUString aRep = xSeq->getSourceRangeRepresentation();
+ rRangeRep.push_back(aRep);
+ }
+ xSeq = xLS->getLabel();
+ if (xSeq.is())
+ {
+ OUString aRep = xSeq->getSourceRangeRepresentation();
+ rRangeRep.push_back(aRep);
+ }
+ }
+}
+
+void getRangeFromErrorBar(const uno::Reference< chart2::XChartDocument >& rChartDoc, std::vector<OUString>& rRangeRep)
+{
+ uno::Reference <chart2::XDiagram > xDiagram = rChartDoc->getFirstDiagram();
+ if(!xDiagram.is())
+ return;
+
+ uno::Reference< chart2::XCoordinateSystemContainer > xCooSysContainer( xDiagram, uno::UNO_QUERY);
+ if(!xCooSysContainer.is())
+ return;
+
+ const uno::Sequence< uno::Reference< chart2::XCoordinateSystem > > xCooSysSequence( xCooSysContainer->getCoordinateSystems());
+ for(const auto& rCooSys : xCooSysSequence)
+ {
+ uno::Reference< chart2::XChartTypeContainer > xChartTypeContainer( rCooSys, uno::UNO_QUERY);
+ if(!xChartTypeContainer.is())
+ continue;
+
+ const uno::Sequence< uno::Reference< chart2::XChartType > > xChartTypeSequence( xChartTypeContainer->getChartTypes() );
+ for(const auto& rChartType : xChartTypeSequence)
+ {
+ uno::Reference< chart2::XDataSeriesContainer > xDataSequenceContainer( rChartType, uno::UNO_QUERY);
+ if(!xDataSequenceContainer.is())
+ continue;
+
+ const uno::Sequence< uno::Reference< chart2::XDataSeries > > xSeriesSequence( xDataSequenceContainer->getDataSeries() );
+ for(const uno::Reference<chart2::XDataSeries>& xSeries : xSeriesSequence)
+ {
+ uno::Reference< beans::XPropertySet > xPropSet( xSeries, uno::UNO_QUERY);
+ uno::Reference< chart2::data::XDataSource > xErrorBarY;
+ xPropSet->getPropertyValue("ErrorBarY") >>= xErrorBarY;
+ if(xErrorBarY.is())
+ getRangeFromDataSource(xErrorBarY, rRangeRep);
+ uno::Reference< chart2::data::XDataSource > xErrorBarX;
+ xPropSet->getPropertyValue("ErrorBarX") >>= xErrorBarX;
+ if(xErrorBarX.is())
+ getRangeFromDataSource(xErrorBarX, rRangeRep);
+ }
+ }
+ }
+}
+
+void getRangeFromOle2Object(const SdrOle2Obj& rObj, std::vector<OUString>& rRangeRep)
+{
+ if (!rObj.IsChart())
+ // not a chart object.
+ return;
+
+ const uno::Reference<embed::XEmbeddedObject>& xObj = rObj.GetObjRef();
+ if (!xObj.is())
+ return;
+
+ uno::Reference<chart2::XChartDocument> xChartDoc(xObj->getComponent(), uno::UNO_QUERY);
+ if (!xChartDoc.is())
+ return;
+
+ if(xChartDoc->hasInternalDataProvider())
+ return;
+
+ getRangeFromErrorBar(xChartDoc, rRangeRep);
+
+ uno::Reference<chart2::data::XDataSource> xDataSource(xChartDoc, uno::UNO_QUERY);
+ if (!xDataSource.is())
+ return;
+
+ // Get all data sources used in this chart.
+ getRangeFromDataSource(xDataSource, rRangeRep);
+
+ return;
+}
+
+// Get all cell ranges that are referenced by the selected chart objects.
+void getOleSourceRanges(const SdrMarkList& rMarkList, bool& rAnyOle, bool& rOneOle, std::vector<ScRange>* pRanges = nullptr, const ScDocument* pDoc = nullptr )
+{
+ bool bCalcSourceRanges = pRanges && pDoc;
+ std::vector<OUString> aRangeReps;
+ rAnyOle = rOneOle = false;
+ const size_t nCount = rMarkList.GetMarkCount();
+ for (size_t i=0; i<nCount; ++i)
+ {
+ SdrMark* pMark = rMarkList.GetMark(i);
+ if ( !pMark )
+ continue;
+
+ SdrObject* pObj = pMark->GetMarkedSdrObj();
+ if ( !pObj )
+ continue;
+
+ SdrObjKind nSdrObjKind = pObj->GetObjIdentifier();
+ if (nSdrObjKind == SdrObjKind::OLE2)
+ {
+ rAnyOle = true;
+ rOneOle = (nCount == 1);
+ if ( bCalcSourceRanges )
+ getRangeFromOle2Object( static_cast<const SdrOle2Obj&>( *pObj ), aRangeReps );
+ else
+ break;
+ }
+ else if ( dynamic_cast<const SdrObjGroup*>( pObj) != nullptr )
+ {
+ SdrObjListIter aIter( *pObj, SdrIterMode::DeepNoGroups );
+ SdrObject* pSubObj = aIter.Next();
+ while (pSubObj)
+ {
+ if ( pSubObj->GetObjIdentifier() == SdrObjKind::OLE2 )
+ {
+ rAnyOle = true;
+ // rOneOle remains false - a group isn't treated like a single OLE object
+ if ( !bCalcSourceRanges )
+ return;
+
+ getRangeFromOle2Object( static_cast<const SdrOle2Obj&>( *pSubObj ), aRangeReps );
+ }
+ pSubObj = aIter.Next();
+ }
+ }
+ }
+
+ if (!bCalcSourceRanges)
+ return;
+
+ // Compile all range representation strings into ranges.
+ for (const auto& rRangeRep : aRangeReps)
+ {
+ ScRangeList aRange;
+ ScAddress aAddr;
+ if (aRange.Parse(rRangeRep, *pDoc, pDoc->GetAddressConvention()) & ScRefFlags::VALID)
+ {
+ pRanges->insert(pRanges->end(), aRange.begin(), aRange.end());
+ }
+ else if (aAddr.Parse(rRangeRep, *pDoc, pDoc->GetAddressConvention()) & ScRefFlags::VALID)
+ pRanges->push_back(aAddr);
+ }
+
+ return;
+}
+
+class InsertTabIndex
+{
+ std::vector<SCTAB>& mrTabs;
+public:
+ explicit InsertTabIndex(std::vector<SCTAB>& rTabs) : mrTabs(rTabs) {}
+ void operator() (const ScRange& rRange)
+ {
+ mrTabs.push_back(rRange.aStart.Tab());
+ }
+};
+
+class CopyRangeData
+{
+ ScDocument& mrSrc;
+ ScDocument& mrDest;
+public:
+ CopyRangeData(ScDocument& rSrc, ScDocument& rDest) : mrSrc(rSrc), mrDest(rDest) {}
+
+ void operator() (const ScRange& rRange)
+ {
+ OUString aTabName;
+ mrSrc.GetName(rRange.aStart.Tab(), aTabName);
+
+ SCTAB nTab;
+ if (!mrDest.GetTable(aTabName, nTab))
+ // Sheet by this name doesn't exist.
+ return;
+
+ mrSrc.CopyStaticToDocument(rRange, nTab, mrDest);
+ }
+};
+
+void copyChartRefDataToClipDoc(ScDocument& rSrcDoc, ScDocument& rClipDoc, const std::vector<ScRange>& rRanges)
+{
+ // Get a list of referenced table indices.
+ std::vector<SCTAB> aTabs;
+ std::for_each(rRanges.begin(), rRanges.end(), InsertTabIndex(aTabs));
+ std::sort(aTabs.begin(), aTabs.end());
+ aTabs.erase(std::unique(aTabs.begin(), aTabs.end()), aTabs.end());
+
+ // Get table names.
+ if (aTabs.empty())
+ return;
+
+ // Create sheets only for referenced source sheets.
+ OUString aName;
+ std::vector<SCTAB>::const_iterator it = aTabs.begin(), itEnd = aTabs.end();
+ if (!rSrcDoc.GetName(*it, aName))
+ return;
+
+ rClipDoc.SetTabNameOnLoad(0, aName); // document initially has one sheet.
+
+ for (++it; it != itEnd; ++it)
+ {
+ if (!rSrcDoc.GetName(*it, aName))
+ return;
+
+ rClipDoc.AppendTabOnLoad(aName);
+ }
+
+ std::for_each(rRanges.begin(), rRanges.end(), CopyRangeData(rSrcDoc, rClipDoc));
+}
+
+}
+
+void ScDrawView::CheckOle( const SdrMarkList& rMarkList, bool& rAnyOle, bool& rOneOle )
+{
+ getOleSourceRanges( rMarkList, rAnyOle, rOneOle );
+}
+
+void ScDrawView::DoCopy()
+{
+ const SdrMarkList& rMarkList = GetMarkedObjectList();
+ std::vector<ScRange> aRanges;
+ bool bAnyOle = false, bOneOle = false;
+ getOleSourceRanges( rMarkList, bAnyOle, bOneOle, &aRanges, &rDoc );
+
+ // update ScGlobal::xDrawClipDocShellRef
+ ScDrawLayer::SetGlobalDrawPersist( ScTransferObj::SetDrawClipDoc( bAnyOle ) );
+ if (ScGlobal::xDrawClipDocShellRef.is() && !aRanges.empty())
+ {
+ // Copy data referenced by the chart objects to the draw clip
+ // document. We need to do this before CreateMarkedObjModel() below.
+ ScDocShellRef xDocSh = ScGlobal::xDrawClipDocShellRef;
+ ScDocument& rClipDoc = xDocSh->GetDocument();
+ copyChartRefDataToClipDoc(rDoc, rClipDoc, aRanges);
+ }
+ std::unique_ptr<SdrModel> pModel(CreateMarkedObjModel());
+ ScDrawLayer::SetGlobalDrawPersist(nullptr);
+
+ // Charts now always copy their data in addition to the source reference, so
+ // there's no need to call SchDLL::Update for the charts in the clipboard doc.
+ // Update with the data (including NumberFormatter) from the live document would
+ // also store the NumberFormatter in the clipboard chart (#88749#)
+
+ ScDocShell* pDocSh = pViewData->GetDocShell();
+
+ TransferableObjectDescriptor aObjDesc;
+ pDocSh->FillTransferableObjectDescriptor( aObjDesc );
+ aObjDesc.maDisplayName = pDocSh->GetMedium()->GetURLObject().GetURLNoPass();
+ // maSize is set in ScDrawTransferObj ctor
+
+ rtl::Reference<ScDrawTransferObj> pTransferObj(new ScDrawTransferObj( std::move(pModel), pDocSh, std::move(aObjDesc) ));
+
+ if ( ScGlobal::xDrawClipDocShellRef.is() )
+ {
+ pTransferObj->SetDrawPersist( ScGlobal::xDrawClipDocShellRef.get() ); // keep persist for ole objects alive
+ }
+
+ pTransferObj->CopyToClipboard( pViewData->GetActiveWin() ); // system clipboard
+}
+
+uno::Reference<datatransfer::XTransferable> ScDrawView::CopyToTransferable()
+{
+ bool bAnyOle, bOneOle;
+ const SdrMarkList& rMarkList = GetMarkedObjectList();
+ CheckOle( rMarkList, bAnyOle, bOneOle );
+
+ // update ScGlobal::xDrawClipDocShellRef
+ ScDrawLayer::SetGlobalDrawPersist( ScTransferObj::SetDrawClipDoc( bAnyOle ) );
+ std::unique_ptr<SdrModel> pModel( CreateMarkedObjModel() );
+ ScDrawLayer::SetGlobalDrawPersist(nullptr);
+
+ // Charts now always copy their data in addition to the source reference, so
+ // there's no need to call SchDLL::Update for the charts in the clipboard doc.
+ // Update with the data (including NumberFormatter) from the live document would
+ // also store the NumberFormatter in the clipboard chart (#88749#)
+ // lcl_RefreshChartData( pModel, pViewData->GetDocument() );
+
+ ScDocShell* pDocSh = pViewData->GetDocShell();
+
+ TransferableObjectDescriptor aObjDesc;
+ pDocSh->FillTransferableObjectDescriptor( aObjDesc );
+ aObjDesc.maDisplayName = pDocSh->GetMedium()->GetURLObject().GetURLNoPass();
+ // maSize is set in ScDrawTransferObj ctor
+
+ rtl::Reference<ScDrawTransferObj> pTransferObj = new ScDrawTransferObj( std::move(pModel), pDocSh, std::move(aObjDesc) );
+
+ if ( ScGlobal::xDrawClipDocShellRef.is() )
+ {
+ pTransferObj->SetDrawPersist( ScGlobal::xDrawClipDocShellRef.get() ); // keep persist for ole objects alive
+ }
+
+ return pTransferObj;
+}
+
+// Calculate correction for 100%, regardless of current settings
+
+void ScDrawView::CalcNormScale( Fraction& rFractX, Fraction& rFractY ) const
+{
+ double nPPTX = ScGlobal::nScreenPPTX;
+ double nPPTY = ScGlobal::nScreenPPTY;
+
+ if (pViewData)
+ nPPTX /= pViewData->GetDocShell()->GetOutputFactor();
+
+ SCCOL nEndCol = 0;
+ SCROW nEndRow = 0;
+ rDoc.GetTableArea( nTab, nEndCol, nEndRow );
+ if (nEndCol<20)
+ nEndCol = 20;
+ if (nEndRow<20)
+ nEndRow = 1000;
+
+ Fraction aZoom(1,1);
+ ScDrawUtil::CalcScale( rDoc, nTab, 0,0, nEndCol,nEndRow, pDev, aZoom,aZoom,
+ nPPTX, nPPTY, rFractX,rFractY );
+}
+
+void ScDrawView::SetMarkedOriginalSize()
+{
+ std::unique_ptr<SdrUndoGroup> pUndoGroup(new SdrUndoGroup(GetModel()));
+
+ const SdrMarkList& rMarkList = GetMarkedObjectList();
+ tools::Long nDone = 0;
+ const size_t nCount = rMarkList.GetMarkCount();
+ for (size_t i=0; i<nCount; ++i)
+ {
+ SdrObject* pObj = rMarkList.GetMark(i)->GetMarkedSdrObj();
+ SdrObjKind nIdent = pObj->GetObjIdentifier();
+ bool bDo = false;
+ Size aOriginalSize;
+ if (nIdent == SdrObjKind::OLE2)
+ {
+ // TODO/LEAN: working with visual area can switch object to running state
+ uno::Reference < embed::XEmbeddedObject > xObj = static_cast<SdrOle2Obj*>(pObj)->GetObjRef();
+ if ( xObj.is() ) // NULL for an invalid object that couldn't be loaded
+ {
+ sal_Int64 nAspect = static_cast<SdrOle2Obj*>(pObj)->GetAspect();
+
+ if ( nAspect == embed::Aspects::MSOLE_ICON )
+ {
+ MapMode aMapMode( MapUnit::Map100thMM );
+ aOriginalSize = static_cast<SdrOle2Obj*>(pObj)->GetOrigObjSize( &aMapMode );
+ bDo = true;
+ }
+ else
+ {
+ MapUnit aUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( static_cast<SdrOle2Obj*>(pObj)->GetAspect() ) );
+ try
+ {
+ awt::Size aSz = xObj->getVisualAreaSize( static_cast<SdrOle2Obj*>(pObj)->GetAspect() );
+ aOriginalSize = OutputDevice::LogicToLogic(
+ Size( aSz.Width, aSz.Height ),
+ MapMode(aUnit),
+ MapMode(MapUnit::Map100thMM));
+ bDo = true;
+ } catch( embed::NoVisualAreaSizeException& )
+ {
+ TOOLS_WARN_EXCEPTION("sc.ui", "Can't get the original size of the object!" );
+ }
+ }
+ }
+ }
+ else if (nIdent == SdrObjKind::Graphic)
+ {
+ const SdrGrafObj* pSdrGrafObj = static_cast<const SdrGrafObj*>(pObj);
+
+ MapMode aSourceMap = pSdrGrafObj->GetGraphic().GetPrefMapMode();
+ MapMode aDestMap( MapUnit::Map100thMM );
+ if (aSourceMap.GetMapUnit() == MapUnit::MapPixel)
+ {
+ // consider pixel correction, so that the bitmap is correct on the screen
+ Fraction aNormScaleX, aNormScaleY;
+ CalcNormScale( aNormScaleX, aNormScaleY );
+ aDestMap.SetScaleX(aNormScaleX);
+ aDestMap.SetScaleY(aNormScaleY);
+ }
+ aOriginalSize = pSdrGrafObj->getOriginalSize();
+ bDo = true;
+ }
+
+ if ( bDo )
+ {
+ tools::Rectangle aDrawRect = pObj->GetLogicRect();
+
+ pUndoGroup->AddAction( std::make_unique<SdrUndoGeoObj>( *pObj ) );
+ pObj->Resize( aDrawRect.TopLeft(), Fraction( aOriginalSize.Width(), aDrawRect.GetWidth() ),
+ Fraction( aOriginalSize.Height(), aDrawRect.GetHeight() ) );
+ ++nDone;
+ }
+ }
+
+ if (nDone && pViewData)
+ {
+ pUndoGroup->SetComment(ScResId( STR_UNDO_ORIGINALSIZE ));
+ ScDocShell* pDocSh = pViewData->GetDocShell();
+ pDocSh->GetUndoManager()->AddUndoAction(std::move(pUndoGroup));
+ pDocSh->SetDrawModified();
+ }
+}
+
+void ScDrawView::FitToCellSize()
+{
+ const SdrMarkList& rMarkList = GetMarkedObjectList();
+
+ if (rMarkList.GetMarkCount() != 1)
+ {
+ SAL_WARN("sc.ui", "Fit to cell only works with one graphic!");
+ return;
+ }
+
+ SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
+
+ ScAnchorType aAnchorType = ScDrawLayer::GetAnchorType(*pObj);
+ if (aAnchorType != SCA_CELL && aAnchorType != SCA_CELL_RESIZE)
+ {
+ SAL_WARN("sc.ui", "Fit to cell only works with cell anchored graphics!");
+ return;
+ }
+
+ ScDrawObjData* pObjData = ScDrawLayer::GetObjData(pObj);
+ if (!pObjData)
+ {
+ SAL_WARN("sc.ui", "Missing ScDrawObjData!");
+ return;
+ }
+
+ std::unique_ptr<SdrUndoGroup> pUndoGroup(new SdrUndoGroup(GetModel()));
+ tools::Rectangle aGraphicRect = pObj->GetSnapRect();
+ tools::Rectangle aCellRect = ScDrawLayer::GetCellRect( rDoc, pObjData->maStart, true);
+
+ // For graphic objects, we want to keep the aspect ratio
+ if (pObj->shouldKeepAspectRatio())
+ {
+ tools::Long nWidth = aGraphicRect.GetWidth();
+ assert(nWidth && "div-by-zero");
+ double fScaleX = static_cast<double>(aCellRect.GetWidth()) / static_cast<double>(nWidth);
+ tools::Long nHeight = aGraphicRect.GetHeight();
+ assert(nHeight && "div-by-zero");
+ double fScaleY = static_cast<double>(aCellRect.GetHeight()) / static_cast<double>(nHeight);
+ double fScaleMin = std::min(fScaleX, fScaleY);
+
+ aCellRect.setWidth(static_cast<double>(aGraphicRect.GetWidth()) * fScaleMin);
+ aCellRect.setHeight(static_cast<double>(aGraphicRect.GetHeight()) * fScaleMin);
+ }
+
+ pUndoGroup->AddAction( std::make_unique<SdrUndoGeoObj>( *pObj ) );
+ if (pObj->GetObjIdentifier() == SdrObjKind::CustomShape)
+ pObj->AdjustToMaxRect(aCellRect);
+ else
+ pObj->SetSnapRect(aCellRect);
+
+ pUndoGroup->SetComment(ScResId( STR_UNDO_FITCELLSIZE ));
+ ScDocShell* pDocSh = pViewData->GetDocShell();
+ pDocSh->GetUndoManager()->AddUndoAction(std::move(pUndoGroup));
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/drawview.cxx b/sc/source/ui/view/drawview.cxx
new file mode 100644
index 0000000000..c1a48dc6df
--- /dev/null
+++ b/sc/source/ui/view/drawview.cxx
@@ -0,0 +1,1251 @@
+/* -*- 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 .
+ */
+
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+
+#include <svx/svditer.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdouno.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdundo.hxx>
+#include <svx/svdocapt.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <svx/sdrundomanager.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xbtmpit.hxx>
+#include <comphelper/lok.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <svx/sdr/contact/objectcontactofpageview.hxx>
+#include <svx/sdr/contact/viewobjectcontact.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+#include <svx/sdrpagewindow.hxx>
+#include <tools/UnitConversion.hxx>
+#include <osl/diagnose.h>
+
+#include <drawview.hxx>
+#include <global.hxx>
+#include <viewdata.hxx>
+#include <document.hxx>
+#include <drawutil.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <tabvwsh.hxx>
+#include <client.hxx>
+#include <scmod.hxx>
+#include <drwlayer.hxx>
+#include <docsh.hxx>
+#include <viewuno.hxx>
+#include <userdat.hxx>
+#include <postit.hxx>
+#include <undocell.hxx>
+#include <gridwin.hxx>
+
+#include <sc.hrc>
+
+using namespace com::sun::star;
+
+#define SC_HANDLESIZE_BIG 9
+
+void ScDrawView::Construct()
+{
+ EnableExtendedKeyInputDispatcher(false);
+ EnableExtendedMouseEventDispatcher(false);
+
+ SetFrameDragSingles();
+
+ SetMinMoveDistancePixel( 2 );
+ SetHitTolerancePixel( 2 );
+
+ if (pViewData)
+ {
+ SCTAB nViewTab = pViewData->GetTabNo();
+ ShowSdrPage(GetModel().GetPage(nViewTab));
+
+ bool bEx = pViewData->GetViewShell()->IsDrawSelMode();
+ bool bProt = rDoc.IsTabProtected( nViewTab ) ||
+ pViewData->GetSfxDocShell()->IsReadOnly();
+
+ SdrLayer* pLayer;
+ SdrLayerAdmin& rAdmin = GetModel().GetLayerAdmin();
+ pLayer = rAdmin.GetLayerPerID(SC_LAYER_BACK);
+ if (pLayer)
+ SetLayerLocked( pLayer->GetName(), bProt || !bEx );
+ pLayer = rAdmin.GetLayerPerID(SC_LAYER_INTERN);
+ if (pLayer)
+ SetLayerLocked( pLayer->GetName() );
+ pLayer = rAdmin.GetLayerPerID(SC_LAYER_FRONT);
+ if (pLayer)
+ {
+ SetLayerLocked( pLayer->GetName(), bProt );
+ SetActiveLayer( pLayer->GetName() ); // set active layer to FRONT
+ }
+ pLayer = rAdmin.GetLayerPerID(SC_LAYER_CONTROLS);
+ if (pLayer)
+ SetLayerLocked( pLayer->GetName(), bProt );
+ pLayer = rAdmin.GetLayerPerID(SC_LAYER_HIDDEN);
+ if (pLayer)
+ {
+ SetLayerLocked( pLayer->GetName(), bProt );
+ SetLayerVisible( pLayer->GetName(), false);
+ }
+
+ SetSwapAsynchron();
+ }
+ else
+ {
+ ShowSdrPage(GetModel().GetPage(nTab));
+ }
+
+ UpdateUserViewOptions();
+ RecalcScale();
+ UpdateWorkArea();
+
+ bInConstruct = false;
+}
+
+void ScDrawView::ImplClearCalcDropMarker()
+{
+ pDropMarker.reset();
+}
+
+ScDrawView::~ScDrawView()
+{
+ ImplClearCalcDropMarker();
+}
+
+void ScDrawView::AddCustomHdl()
+{
+ const SdrMarkList &rMrkList = GetMarkedObjectList();
+ const size_t nCount = rMrkList.GetMarkCount();
+ for(size_t nPos=0; nPos<nCount; ++nPos )
+ {
+ SdrObject* pObj = rMrkList.GetMark(nPos)->GetMarkedSdrObj();
+ if (ScDrawObjData *pAnchor = ScDrawLayer::GetObjDataTab(pObj, nTab))
+ {
+ if (ScTabView* pView = pViewData->GetView())
+ pView->CreateAnchorHandles(maHdlList, pAnchor->maStart);
+ }
+ }
+}
+
+void ScDrawView::InvalidateAttribs()
+{
+ if (!pViewData) return;
+ SfxBindings& rBindings = pViewData->GetBindings();
+
+ // true status values:
+ rBindings.InvalidateAll( true );
+}
+
+void ScDrawView::InvalidateDrawTextAttrs()
+{
+ if (!pViewData) return;
+ SfxBindings& rBindings = pViewData->GetBindings();
+
+ // cjk/ctl font items have no configured slots,
+ // need no invalidate
+
+ rBindings.Invalidate( SID_ATTR_CHAR_FONT );
+ rBindings.Invalidate( SID_ATTR_CHAR_FONTHEIGHT );
+ rBindings.Invalidate( SID_ATTR_CHAR_WEIGHT );
+ rBindings.Invalidate( SID_ATTR_CHAR_POSTURE );
+ rBindings.Invalidate( SID_ATTR_CHAR_UNDERLINE );
+ rBindings.Invalidate( SID_ULINE_VAL_NONE );
+ rBindings.Invalidate( SID_ULINE_VAL_SINGLE );
+ rBindings.Invalidate( SID_ULINE_VAL_DOUBLE );
+ rBindings.Invalidate( SID_ULINE_VAL_DOTTED );
+ rBindings.Invalidate( SID_ATTR_CHAR_OVERLINE );
+ rBindings.Invalidate( SID_ATTR_CHAR_COLOR );
+ rBindings.Invalidate( SID_ATTR_CHAR_BACK_COLOR );
+ rBindings.Invalidate( SID_ATTR_PARA_ADJUST_LEFT );
+ rBindings.Invalidate( SID_ATTR_PARA_ADJUST_RIGHT );
+ rBindings.Invalidate( SID_ATTR_PARA_ADJUST_BLOCK );
+ rBindings.Invalidate( SID_ATTR_PARA_ADJUST_CENTER);
+ rBindings.Invalidate( SID_ALIGNLEFT );
+ rBindings.Invalidate( SID_ALIGNCENTERHOR );
+ rBindings.Invalidate( SID_ALIGNRIGHT );
+ rBindings.Invalidate( SID_ALIGNBLOCK );
+ rBindings.Invalidate( SID_ATTR_PARA_LINESPACE_10 );
+ rBindings.Invalidate( SID_ATTR_PARA_LINESPACE_15 );
+ rBindings.Invalidate( SID_ATTR_PARA_LINESPACE_20 );
+ rBindings.Invalidate( SID_SET_SUPER_SCRIPT );
+ rBindings.Invalidate( SID_SET_SUB_SCRIPT );
+ rBindings.Invalidate( SID_ATTR_CHAR_KERNING );
+ rBindings.Invalidate( SID_ATTR_CHAR_STRIKEOUT );
+ rBindings.Invalidate( SID_ATTR_CHAR_SHADOWED );
+ rBindings.Invalidate( SID_TEXTDIRECTION_LEFT_TO_RIGHT );
+ rBindings.Invalidate( SID_TEXTDIRECTION_TOP_TO_BOTTOM );
+ rBindings.Invalidate( SID_ATTR_PARA_LEFT_TO_RIGHT );
+ rBindings.Invalidate( SID_ATTR_PARA_RIGHT_TO_LEFT );
+ rBindings.Invalidate( SID_TABLE_VERT_NONE );
+ rBindings.Invalidate( SID_TABLE_VERT_CENTER );
+ rBindings.Invalidate( SID_TABLE_VERT_BOTTOM );
+ // pseudo slots for Format menu
+ rBindings.Invalidate( SID_ALIGN_ANY_LEFT );
+ rBindings.Invalidate( SID_ALIGN_ANY_HCENTER );
+ rBindings.Invalidate( SID_ALIGN_ANY_RIGHT );
+ rBindings.Invalidate( SID_ALIGN_ANY_JUSTIFIED );
+}
+
+void ScDrawView::SetMarkedToLayer( SdrLayerID nLayerNo )
+{
+ if (!AreObjectsMarked())
+ return;
+
+ // #i11702# use SdrUndoObjectLayerChange for undo
+ // STR_UNDO_SELATTR is "Attributes" - should use a different text later
+ BegUndo( ScResId( STR_UNDO_SELATTR ) );
+
+ const SdrMarkList& rMark = GetMarkedObjectList();
+ const size_t nCount = rMark.GetMarkCount();
+ for (size_t i=0; i<nCount; ++i)
+ {
+ SdrObject* pObj = rMark.GetMark(i)->GetMarkedSdrObj();
+ if ( dynamic_cast<const SdrUnoObj*>( pObj) == nullptr && (pObj->GetLayer() != SC_LAYER_INTERN) )
+ {
+ AddUndo( std::make_unique<SdrUndoObjectLayerChange>( *pObj, pObj->GetLayer(), nLayerNo) );
+ pObj->SetLayer( nLayerNo );
+ }
+ }
+
+ EndUndo();
+
+ // repaint is done in SetLayer
+
+ pViewData->GetDocShell()->SetDrawModified();
+
+ // check mark list now instead of later in a timer
+ CheckMarked();
+ MarkListHasChanged();
+}
+
+bool ScDrawView::HasMarkedControl() const
+{
+ SdrObjListIter aIter( GetMarkedObjectList() );
+ for( SdrObject* pObj = aIter.Next(); pObj; pObj = aIter.Next() )
+ if( dynamic_cast<const SdrUnoObj*>( pObj) != nullptr )
+ return true;
+ return false;
+}
+
+bool ScDrawView::HasMarkedInternal() const
+{
+ // internal objects should not be inside a group, but who knows...
+ SdrObjListIter aIter( GetMarkedObjectList() );
+ for( SdrObject* pObj = aIter.Next(); pObj; pObj = aIter.Next() )
+ if( pObj->GetLayer() == SC_LAYER_INTERN )
+ return true;
+ return false;
+}
+
+void ScDrawView::UpdateWorkArea()
+{
+ SdrPage* pPage = GetModel().GetPage(static_cast<sal_uInt16>(nTab));
+ if (pPage)
+ {
+ Size aPageSize( pPage->GetSize() );
+ tools::Rectangle aNewArea( Point(), aPageSize );
+ if ( aPageSize.Width() < 0 )
+ {
+ // RTL: from max.negative (left) to zero (right)
+ aNewArea.SetRight( 0 );
+ aNewArea.SetLeft( aPageSize.Width() + 1 );
+ }
+ SetWorkArea( aNewArea );
+ }
+ else
+ {
+ OSL_FAIL("Page not found");
+ }
+}
+
+void ScDrawView::DoCut()
+{
+ DoCopy();
+ BegUndo( ScResId( STR_UNDO_CUT ) );
+ DeleteMarked(); // In this View - not affected by 505f change
+ EndUndo();
+}
+
+void ScDrawView::GetScale( Fraction& rFractX, Fraction& rFractY ) const
+{
+ rFractX = aScaleX;
+ rFractY = aScaleY;
+}
+
+void ScDrawView::RecalcScale()
+{
+ double nPPTX;
+ double nPPTY;
+ Fraction aZoomX(1,1);
+ Fraction aZoomY(1,1);
+
+ if (pViewData)
+ {
+ nTab = pViewData->GetTabNo();
+ nPPTX = pViewData->GetPPTX();
+ nPPTY = pViewData->GetPPTY();
+ aZoomX = pViewData->GetZoomX();
+ aZoomY = pViewData->GetZoomY();
+ }
+ else
+ {
+ Point aLogic = pDev->LogicToPixel(Point(1000,1000), MapMode(MapUnit::MapTwip));
+ nPPTX = aLogic.X() / 1000.0;
+ nPPTY = aLogic.Y() / 1000.0;
+ //! Zoom, handed over ???
+ }
+
+ SCCOL nEndCol = 0;
+ SCROW nEndRow = 0;
+ rDoc.GetTableArea( nTab, nEndCol, nEndRow );
+ if (nEndCol<20)
+ nEndCol = 20;
+ if (nEndRow<20)
+ nEndRow = 20;
+
+ ScDrawUtil::CalcScale(
+ rDoc, nTab, 0, 0, nEndCol, nEndRow, pDev, aZoomX, aZoomY, nPPTX, nPPTY,
+ aScaleX, aScaleY);
+
+ // clear all evtl existing GridOffset vectors
+ resetGridOffsetsForAllSdrPageViews();
+
+ SdrPageView* pPV = GetSdrPageView();
+ if ( !(pViewData && pPV) )
+ return;
+
+ if ( SdrPage* pPage = pPV->GetPage() )
+ {
+ for (const rtl::Reference<SdrObject>& pObj : *pPage)
+ // Align objects to nearest grid position
+ SyncForGrid( pObj.get() );
+ }
+}
+
+void ScDrawView::DoConnect(SdrOle2Obj* pOleObj)
+{
+ if ( pViewData )
+ pViewData->GetViewShell()->ConnectObject( pOleObj );
+}
+
+void ScDrawView::MarkListHasChanged()
+{
+ FmFormView::MarkListHasChanged();
+
+ ScTabViewShell* pViewSh = pViewData->GetViewShell();
+
+ // #i110829# remove the cell selection only if drawing objects are selected
+ if ( !bInConstruct && GetMarkedObjectList().GetMarkCount() )
+ {
+ pViewSh->Unmark(); // remove cell selection
+
+ // end cell edit mode if drawing objects are selected
+ SC_MOD()->InputEnterHandler();
+ }
+
+ // deactivate IP
+
+ ScModule* pScMod = SC_MOD();
+ bool bUnoRefDialog = pScMod->IsRefDialogOpen() && pScMod->GetCurRefDlgId() == WID_SIMPLE_REF;
+
+ ScClient* pClient = static_cast<ScClient*>( pViewSh->GetIPClient() );
+ if ( pClient && pClient->IsObjectInPlaceActive() && !bUnoRefDialog )
+ {
+ // do not display the handles for ViewShell::Activate from the Reset2Open
+ pClient->DeactivateObject();
+ // replacing image ole graphics is now done in ScClient::UIActivate
+ }
+
+ // Select Ole object?
+
+ SdrOle2Obj* pOle2Obj = nullptr;
+ SdrGrafObj* pGrafObj = nullptr;
+
+ const SdrMarkList& rMarkList = GetMarkedObjectList();
+ const size_t nMarkCount = rMarkList.GetMarkCount();
+
+ if ( nMarkCount == 0 && !pViewData->GetViewShell()->IsDrawSelMode() && !bInConstruct )
+ {
+ // relock layers that may have been unlocked before
+ LockBackgroundLayer(true);
+ LockInternalLayer();
+ }
+
+ bool bSubShellSet = false;
+ if (nMarkCount == 1)
+ {
+ SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
+ if (pObj->GetObjIdentifier() == SdrObjKind::OLE2)
+ {
+ pOle2Obj = static_cast<SdrOle2Obj*>(pObj);
+ if (!ScDocument::IsChart(pObj) )
+ pViewSh->SetOleObjectShell(true);
+ else
+ pViewSh->SetChartShell(true);
+ bSubShellSet = true;
+ }
+ else if (pObj->GetObjIdentifier() == SdrObjKind::Graphic)
+ {
+ pGrafObj = static_cast<SdrGrafObj*>(pObj);
+ pViewSh->SetGraphicShell(true);
+ bSubShellSet = true;
+ }
+ else if (pObj->GetObjIdentifier() == SdrObjKind::Media)
+ {
+ pViewSh->SetMediaShell(true);
+ bSubShellSet = true;
+ }
+ else if (pObj->GetObjIdentifier() != SdrObjKind::Text // prevent switching to the drawing shell
+ || !pViewSh->IsDrawTextShell()) // when creating a text object @#70206#
+ {
+ pViewSh->SetDrawShell(true);
+ }
+ }
+
+ if ( nMarkCount && !bSubShellSet )
+ {
+ bool bOnlyControls = true;
+ bool bOnlyGraf = true;
+ for (size_t i=0; i<nMarkCount; ++i)
+ {
+ SdrObject* pObj = rMarkList.GetMark(i)->GetMarkedSdrObj();
+ if ( auto pObjGroup = dynamic_cast<const SdrObjGroup*>( pObj) )
+ {
+ const SdrObjList *pLst = pObjGroup->GetSubList();
+ const size_t nListCount = pLst->GetObjCount();
+ if ( nListCount == 0 )
+ {
+ // An empty group (may occur during Undo) is no control or graphics object.
+ // Creating the form shell during undo would lead to problems with the undo manager.
+ bOnlyControls = false;
+ bOnlyGraf = false;
+ }
+ for ( size_t j = 0; j < nListCount; ++j )
+ {
+ SdrObject *pSubObj = pLst->GetObj( j );
+
+ if (dynamic_cast<const SdrUnoObj*>( pSubObj) == nullptr)
+ bOnlyControls = false;
+ if (pSubObj->GetObjIdentifier() != SdrObjKind::Graphic)
+ bOnlyGraf = false;
+
+ if ( !bOnlyControls && !bOnlyGraf ) break;
+ }
+ }
+ else
+ {
+ if (dynamic_cast<const SdrUnoObj*>( pObj) == nullptr)
+ bOnlyControls = false;
+ if (pObj->GetObjIdentifier() != SdrObjKind::Graphic)
+ bOnlyGraf = false;
+ }
+
+ if ( !bOnlyControls && !bOnlyGraf ) break;
+ }
+
+ if(bOnlyControls)
+ {
+ pViewSh->SetDrawFormShell(true); // now UNO controls
+ }
+ else if(bOnlyGraf)
+ {
+ pViewSh->SetGraphicShell(true);
+ }
+ else if(nMarkCount>1)
+ {
+ pViewSh->SetDrawShell(true);
+ }
+ }
+
+ // adjust verbs
+
+ SfxViewFrame& rViewFrame = pViewSh->GetViewFrame();
+ bool bOle = pViewSh->GetViewFrame().GetFrame().IsInPlace();
+ uno::Sequence< embed::VerbDescriptor > aVerbs;
+ if ( pOle2Obj && !bOle )
+ {
+ const uno::Reference < embed::XEmbeddedObject >& xObj = pOle2Obj->GetObjRef();
+ OSL_ENSURE( xObj.is(), "SdrOle2Obj without ObjRef" );
+ if (xObj.is())
+ aVerbs = xObj->getSupportedVerbs();
+ }
+ pViewSh->SetVerbs( aVerbs );
+
+ // image map editor
+
+ if ( pOle2Obj )
+ UpdateIMap( pOle2Obj );
+ else if ( pGrafObj )
+ UpdateIMap( pGrafObj );
+
+ InvalidateAttribs(); // after the image map editor update
+ InvalidateDrawTextAttrs();
+
+ for(sal_uInt32 a(0); a < PaintWindowCount(); a++)
+ {
+ SdrPaintWindow* pPaintWindow = GetPaintWindow(a);
+ OutputDevice& rOutDev = pPaintWindow->GetOutputDevice();
+
+ if(OUTDEV_WINDOW == rOutDev.GetOutDevType())
+ {
+ rOutDev.GetOwnerWindow()->PaintImmediately();
+ }
+ }
+
+ // uno object for view returns drawing objects as selection,
+ // so it must notify its SelectionChangeListeners
+
+ SfxFrame& rFrame = rViewFrame.GetFrame();
+ uno::Reference<frame::XController> xController = rFrame.GetController();
+ if (xController.is())
+ {
+ ScTabViewObj* pImp = dynamic_cast<ScTabViewObj*>( xController.get() );
+ if (pImp)
+ pImp->SelectionChanged();
+ }
+
+ // update selection transfer object
+
+ pViewSh->CheckSelectionTransfer();
+
+}
+
+bool ScDrawView::SdrBeginTextEdit(
+ SdrObject* pObj,
+ SdrPageView* pPV,
+ vcl::Window* pWinL,
+ bool bIsNewObj,
+ SdrOutliner* pGivenOutliner,
+ OutlinerView* pGivenOutlinerView,
+ bool bDontDeleteOutliner,
+ bool bOnlyOneView,
+ bool bGrabFocus )
+{
+ const bool bRet = FmFormView::SdrBeginTextEdit(
+ pObj, pPV, pWinL, bIsNewObj,
+ pGivenOutliner, pGivenOutlinerView, bDontDeleteOutliner,
+ bOnlyOneView, bGrabFocus );
+
+ ScTabViewShell* pViewSh = pViewData->GetViewShell();
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ if (OutlinerView* pView = GetTextEditOutlinerView())
+ {
+ tools::Rectangle aRectangle = pView->GetOutputArea();
+ if (pWinL && pWinL->GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
+ {
+ aRectangle = o3tl::convert(aRectangle, o3tl::Length::mm100, o3tl::Length::twip);
+ }
+ OString sRectangle = aRectangle.toString();
+ SfxLokHelper::notifyOtherViews(pViewSh, LOK_CALLBACK_VIEW_LOCK, "rectangle", sRectangle);
+ }
+ }
+
+ SfxFrame& rFrame = pViewSh->GetViewFrame().GetFrame();
+ uno::Reference< frame::XController > xController = rFrame.GetController();
+ if (xController.is())
+ {
+ ScTabViewObj* pImp = dynamic_cast<ScTabViewObj*>( xController.get() );
+ if (pImp)
+ pImp->SelectionChanged();
+ }
+
+ return bRet;
+}
+
+SdrEndTextEditKind ScDrawView::SdrEndTextEdit( bool bDontDeleteReally )
+{
+ const SdrEndTextEditKind eRet = FmFormView::SdrEndTextEdit( bDontDeleteReally );
+
+ ScTabViewShell* pViewSh = pViewData->GetViewShell();
+
+ if (comphelper::LibreOfficeKit::isActive())
+ SfxLokHelper::notifyOtherViews(pViewSh, LOK_CALLBACK_VIEW_LOCK, "rectangle", "EMPTY"_ostr);
+
+ SfxFrame& rFrame = pViewSh->GetViewFrame().GetFrame();
+ uno::Reference< frame::XController > xController = rFrame.GetController();
+ if (xController.is())
+ {
+ ScTabViewObj* pImp = dynamic_cast<ScTabViewObj*>( xController.get() );
+ if (pImp)
+ pImp->SelectionChanged();
+ }
+
+ return eRet;
+}
+
+void ScDrawView::ModelHasChanged()
+{
+ SdrObject* pEditObj = GetTextEditObject();
+ if ( pEditObj && !pEditObj->IsInserted() && pViewData )
+ {
+ // SdrObjEditView::ModelHasChanged will end text edit in this case,
+ // so make sure the EditEngine's undo manager is no longer used.
+ pViewData->GetViewShell()->SetDrawTextUndo(nullptr);
+ SetCreateMode(); // don't leave FuText in a funny state
+ }
+
+ FmFormView::ModelHasChanged();
+}
+
+void ScDrawView::UpdateUserViewOptions()
+{
+ if (!pViewData)
+ return;
+
+ const ScViewOptions& rOpt = pViewData->GetOptions();
+ const ScGridOptions& rGrid = rOpt.GetGridOptions();
+
+ SetDragStripes( rOpt.GetOption( VOPT_HELPLINES ) );
+ SetMarkHdlSizePixel( SC_HANDLESIZE_BIG );
+
+ SetGridVisible( rGrid.GetGridVisible() );
+ SetSnapEnabled( rGrid.GetUseGridSnap() );
+ SetGridSnap( rGrid.GetUseGridSnap() );
+
+ Fraction aFractX( rGrid.GetFieldDrawX(), rGrid.GetFieldDivisionX() + 1 );
+ Fraction aFractY( rGrid.GetFieldDrawY(), rGrid.GetFieldDivisionY() + 1 );
+ SetSnapGridWidth( aFractX, aFractY );
+
+ SetGridCoarse( Size( rGrid.GetFieldDrawX(), rGrid.GetFieldDrawY() ) );
+ SetGridFine( Size( rGrid.GetFieldDrawX() / (rGrid.GetFieldDivisionX() + 1),
+ rGrid.GetFieldDrawY() / (rGrid.GetFieldDivisionY() + 1) ) );
+}
+
+SdrObject* ScDrawView::GetObjectByName(std::u16string_view rName)
+{
+ ScDocShell* pShell = rDoc.GetDocumentShell();
+ if (pShell)
+ {
+ SdrModel& rDrawLayer = GetModel();
+ sal_uInt16 nTabCount = rDoc.GetTableCount();
+ for (sal_uInt16 i=0; i<nTabCount; i++)
+ {
+ SdrPage* pPage = rDrawLayer.GetPage(i);
+ DBG_ASSERT(pPage,"Page ?");
+ if (pPage)
+ {
+ SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups );
+ SdrObject* pObject = aIter.Next();
+ while (pObject)
+ {
+ if ( ScDrawLayer::GetVisibleName( pObject ) == rName )
+ {
+ return pObject;
+ }
+ pObject = aIter.Next();
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
+//realize multi-selection of objects
+
+void ScDrawView::SelectCurrentViewObject( std::u16string_view rName )
+{
+ sal_uInt16 nObjectTab = 0;
+ SdrObject* pFound = nullptr;
+ ScDocShell* pShell = rDoc.GetDocumentShell();
+ if (pShell)
+ {
+ SdrModel& rDrawLayer = GetModel();
+ sal_uInt16 nTabCount = rDoc.GetTableCount();
+ for (sal_uInt16 i=0; i<nTabCount && !pFound; i++)
+ {
+ SdrPage* pPage = rDrawLayer.GetPage(i);
+ DBG_ASSERT(pPage,"Page ?");
+ if (pPage)
+ {
+ SdrObjListIter aIter( pPage, SdrIterMode::DeepWithGroups );
+ SdrObject* pObject = aIter.Next();
+ while (pObject && !pFound)
+ {
+ if ( ScDrawLayer::GetVisibleName( pObject ) == rName )
+ {
+ pFound = pObject;
+ nObjectTab = i;
+ }
+ pObject = aIter.Next();
+ }
+ }
+ }
+ }
+ if ( !pFound )
+ return;
+
+ ScTabView* pView = pViewData->GetView();
+ if ( nObjectTab != nTab ) // switch sheet
+ pView->SetTabNo( nObjectTab );
+ DBG_ASSERT( nTab == nObjectTab, "Switching sheets did not work" );
+ pView->ScrollToObject( pFound );
+ if ( pFound->GetLayer() == SC_LAYER_BACK &&
+ !pViewData->GetViewShell()->IsDrawSelMode() &&
+ !rDoc.IsTabProtected( nTab ) &&
+ !pViewData->GetSfxDocShell()->IsReadOnly() )
+ {
+ SdrLayer* pLayer = GetModel().GetLayerAdmin().GetLayerPerID(SC_LAYER_BACK);
+ if (pLayer)
+ SetLayerLocked( pLayer->GetName(), false );
+ }
+ SdrPageView* pPV = GetSdrPageView();
+ const bool bUnMark = IsObjMarked(pFound);
+ MarkObj( pFound, pPV, bUnMark);
+}
+
+bool ScDrawView::SelectObject( std::u16string_view rName )
+{
+ UnmarkAll();
+
+ SCTAB nObjectTab = 0;
+ SdrObject* pFound = nullptr;
+
+ ScDocShell* pShell = rDoc.GetDocumentShell();
+ if (pShell)
+ {
+ SdrModel& rDrawLayer = GetModel();
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (SCTAB i=0; i<nTabCount && !pFound; i++)
+ {
+ SdrPage* pPage = rDrawLayer.GetPage(static_cast<sal_uInt16>(i));
+ OSL_ENSURE(pPage,"Page ?");
+ if (pPage)
+ {
+ SdrObjListIter aIter( pPage, SdrIterMode::DeepWithGroups );
+ SdrObject* pObject = aIter.Next();
+ while (pObject && !pFound)
+ {
+ if ( ScDrawLayer::GetVisibleName( pObject ) == rName )
+ {
+ pFound = pObject;
+ nObjectTab = i;
+ }
+ pObject = aIter.Next();
+ }
+ }
+ }
+ }
+
+ if ( pFound )
+ {
+ ScTabView* pView = pViewData->GetView();
+ if ( nObjectTab != nTab ) // switch sheet
+ pView->SetTabNo( nObjectTab );
+
+ OSL_ENSURE( nTab == nObjectTab, "Switching sheets did not work" );
+
+ pView->ScrollToObject( pFound );
+
+ /* To select an object on the background layer, the layer has to
+ be unlocked even if exclusive drawing selection mode is not active
+ (this is reversed in MarkListHasChanged when nothing is selected) */
+ if ( pFound->GetLayer() == SC_LAYER_BACK &&
+ !pViewData->GetViewShell()->IsDrawSelMode() &&
+ !rDoc.IsTabProtected( nTab ) &&
+ !pViewData->GetSfxDocShell()->IsReadOnly() )
+ {
+ LockBackgroundLayer(false);
+ }
+
+ SdrPageView* pPV = GetSdrPageView();
+ MarkObj( pFound, pPV );
+ }
+
+ return ( pFound != nullptr );
+}
+
+//If object is marked , return true , else return false .
+bool ScDrawView::GetObjectIsMarked( const SdrObject* pObject )
+{
+ bool bisMarked = false;
+ if (pObject )
+ {
+ bisMarked = IsObjMarked(pObject);
+ }
+ return bisMarked;
+}
+
+bool ScDrawView::InsertObjectSafe(SdrObject* pObj, SdrPageView& rPV)
+{
+ SdrInsertFlags nOptions=SdrInsertFlags::NONE;
+ // Do not change marks when the ole object is active
+ // (for Drop from ole object would otherwise be deactivated in the middle of ExecuteDrag!)
+
+ if (pViewData)
+ {
+ SfxInPlaceClient* pClient = pViewData->GetViewShell()->GetIPClient();
+ if ( pClient && pClient->IsObjectInPlaceActive() )
+ nOptions |= SdrInsertFlags::DONTMARK;
+ }
+
+ return InsertObjectAtView(pObj, rPV, nOptions);
+}
+
+SdrObject* ScDrawView::GetMarkedNoteCaption( ScDrawObjData** ppCaptData )
+{
+ const SdrMarkList& rMarkList = GetMarkedObjectList();
+ if( pViewData && (rMarkList.GetMarkCount() == 1) )
+ {
+ SdrObject* pObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj();
+ if( ScDrawObjData* pCaptData = ScDrawLayer::GetNoteCaptionData( pObj, pViewData->GetTabNo() ) )
+ {
+ if( ppCaptData ) *ppCaptData = pCaptData;
+ return pObj;
+ }
+ }
+ return nullptr;
+}
+
+void ScDrawView::LockCalcLayer( SdrLayerID nLayer, bool bLock )
+{
+ SdrLayer* pLockLayer = GetModel().GetLayerAdmin().GetLayerPerID( nLayer );
+ if( pLockLayer && (IsLayerLocked( pLockLayer->GetName() ) != bLock) )
+ SetLayerLocked( pLockLayer->GetName(), bLock );
+}
+
+void ScDrawView::MakeVisible( const tools::Rectangle& rRect, vcl::Window& rWin )
+{
+ //! Evaluate rWin properly
+ //! change zoom if necessary
+
+ if ( pViewData && pViewData->GetActiveWin() == &rWin )
+ pViewData->GetView()->MakeVisible( rRect );
+}
+
+SfxViewShell* ScDrawView::GetSfxViewShell() const
+{
+ return pViewData->GetViewShell();
+}
+
+void ScDrawView::DeleteMarked()
+{
+ // try to delete a note caption object with its cell note in the Calc document
+ ScDrawObjData* pCaptData = nullptr;
+ if( SdrObject* pCaptObj = GetMarkedNoteCaption( &pCaptData ) )
+ {
+ ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer();
+ ScDocShell* pDocShell = pViewData ? pViewData->GetDocShell() : nullptr;
+ SfxUndoManager* pUndoMgr = pDocShell ? pDocShell->GetUndoManager() : nullptr;
+ bool bUndo = pDrawLayer && pDocShell && pUndoMgr && rDoc.IsUndoEnabled();
+
+ // remove the cell note from document, we are its owner now
+ std::unique_ptr<ScPostIt> pNote = rDoc.ReleaseNote( pCaptData->maStart );
+ OSL_ENSURE( pNote, "ScDrawView::DeleteMarked - cell note missing in document" );
+ if( pNote )
+ {
+ // rescue note data for undo (with pointer to caption object)
+ ScNoteData aNoteData = pNote->GetNoteData();
+ OSL_ENSURE( aNoteData.mxCaption.get() == pCaptObj, "ScDrawView::DeleteMarked - caption object does not match" );
+ // collect the drawing undo action created while deleting the note
+ if( bUndo )
+ pDrawLayer->BeginCalcUndo(false);
+ // delete the note (already removed from document above)
+ pNote.reset();
+ // add the undo action for the note
+ if( bUndo )
+ pUndoMgr->AddUndoAction( std::make_unique<ScUndoReplaceNote>( *pDocShell, pCaptData->maStart, aNoteData, false, pDrawLayer->GetCalcUndo() ) );
+ // repaint the cell to get rid of the note marker
+ if( pDocShell )
+ pDocShell->PostPaintCell( pCaptData->maStart );
+ // done, return now to skip call of FmFormView::DeleteMarked()
+ return;
+ }
+ }
+
+ FmFormView::DeleteMarked();
+}
+
+SdrEndTextEditKind ScDrawView::ScEndTextEdit()
+{
+ bool bIsTextEdit = IsTextEdit();
+ SdrEndTextEditKind eKind = SdrEndTextEdit();
+
+ if (bIsTextEdit)
+ pViewData->GetViewShell()->SetDrawTextUndo(nullptr); // the "normal" undo manager
+
+ return eKind;
+}
+
+void ScDrawView::MarkDropObj( SdrObject* pObj )
+{
+ if ( pDropMarkObj != pObj )
+ {
+ pDropMarkObj = pObj;
+ ImplClearCalcDropMarker();
+
+ if(pDropMarkObj)
+ {
+ pDropMarker.reset( new SdrDropMarkerOverlay(*this, *pDropMarkObj) );
+ }
+ }
+}
+
+// In order to counteract the effects of rounding due to the nature of how the
+// grid positions are calculated and drawn we calculate the offset needed at the
+// current zoom to be applied to an SrdObject when it is drawn in order to make
+// sure that it's position relative to the nearest cell anchor doesn't change.
+// Of course not all shape(s)/control(s) are cell anchored, if the
+// object doesn't have a cell anchor we synthesise a temporary anchor.
+void ScDrawView::SyncForGrid( SdrObject* pObj )
+{
+ // process members of a group shape separately
+ if ( auto pObjGroup = dynamic_cast<const SdrObjGroup*>( pObj) )
+ {
+ SdrObjList *pLst = pObjGroup->GetSubList();
+ for (const rtl::Reference<SdrObject>& pChild : *pLst)
+ SyncForGrid( pChild.get() );
+ }
+
+ ScSplitPos eWhich = pViewData->GetActivePart();
+ ScGridWindow* pGridWin = pViewData->GetActiveWin();
+ ScDrawObjData* pData = ScDrawLayer::GetObjData( pObj );
+ if ( !pGridWin )
+ return;
+
+ ScAddress aOldStt;
+ if( pData && pData->maStart.IsValid())
+ {
+ aOldStt = pData->maStart;
+ }
+ else
+ {
+ // Page anchored object so...
+ // synthesise an anchor ( but don't attach it to
+ // the object as we want to maintain page anchoring )
+ ScDrawObjData aAnchor;
+ const tools::Rectangle aObjRect(pObj->GetLogicRect());
+ ScDrawLayer::GetCellAnchorFromPosition(
+ aObjRect,
+ aAnchor,
+ rDoc,
+ GetTab());
+ aOldStt = aAnchor.maStart;
+ }
+ MapMode aDrawMode = pGridWin->GetDrawMapMode();
+ // find pos anchor position
+ Point aOldPos( rDoc.GetColOffset( aOldStt.Col(), aOldStt.Tab() ), rDoc.GetRowOffset( aOldStt.Row(), aOldStt.Tab() ) );
+ aOldPos.setX(convertTwipToMm100(aOldPos.X()));
+ aOldPos.setY(convertTwipToMm100(aOldPos.Y()));
+ // find position of same point on the screen ( e.g. grid )
+ Point aCurPos = pViewData->GetScrPos( aOldStt.Col(), aOldStt.Row(), eWhich, true );
+ Point aCurPosHmm = pGridWin->PixelToLogic(aCurPos, aDrawMode );
+ Point aGridOff = aCurPosHmm - aOldPos;
+ // fdo#63878 Fix the X position for RTL Sheet
+ if( rDoc.IsNegativePage( GetTab() ) && !comphelper::LibreOfficeKit::isActive() )
+ aGridOff.setX( aCurPosHmm.getX() + aOldPos.getX() );
+}
+
+void ScDrawView::resetGridOffsetsForAllSdrPageViews()
+{
+ SdrPageView* pPageView(GetSdrPageView());
+
+ if(nullptr == pPageView)
+ return;
+
+ for(sal_uInt32 a(0); a < pPageView->PageWindowCount(); a++)
+ {
+ SdrPageWindow* pPageWindow(pPageView->GetPageWindow(a));
+ assert(pPageWindow && "SdrView::SetMasterPagePaintCaching: Corrupt SdrPageWindow list (!)");
+
+ if(nullptr != pPageWindow)
+ {
+ sdr::contact::ObjectContact& rObjectContact(pPageWindow->GetObjectContact());
+
+ if(rObjectContact.supportsGridOffsets())
+ {
+ rObjectContact.resetAllGridOffsets();
+ }
+ }
+ }
+}
+
+bool ScDrawView::calculateGridOffsetForSdrObject(
+ SdrObject& rSdrObject,
+ basegfx::B2DVector& rTarget) const
+{
+ if (comphelper::LibreOfficeKit::isActive() &&
+ !comphelper::LibreOfficeKit::isCompatFlagSet(
+ comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
+ return false;
+
+ ScGridWindow* pGridWin(pViewData->GetActiveWin());
+
+ if(nullptr == pGridWin)
+ {
+ return false;
+ }
+
+ ScDrawObjData* pData(ScDrawLayer::GetObjData(&rSdrObject));
+ ScAddress aOldStt;
+
+ if(nullptr != pData && pData->maStart.IsValid())
+ {
+ aOldStt = pData->maStart;
+ }
+ else
+ {
+ // Page anchored object so...
+ // synthesise an anchor ( but don't attach it to
+ // the object as we want to maintain page anchoring )
+ ScDrawObjData aAnchor;
+ const tools::Rectangle aObjRect(rSdrObject.GetLogicRect());
+ ScDrawLayer::GetCellAnchorFromPosition(
+ aObjRect,
+ aAnchor,
+ rDoc,
+ GetTab());
+ aOldStt = aAnchor.maStart;
+ }
+
+ MapMode aDrawMode = pGridWin->GetDrawMapMode();
+
+ // find pos anchor position
+ Point aOldPos(rDoc.GetColOffset(aOldStt.Col(), aOldStt.Tab()), rDoc.GetRowOffset(aOldStt.Row(), aOldStt.Tab()));
+ aOldPos.setX(convertTwipToMm100(aOldPos.X()));
+ aOldPos.setY(convertTwipToMm100(aOldPos.Y()));
+
+ // find position of same point on the screen ( e.g. grid )
+ ScSplitPos eWhich(pViewData->GetActivePart());
+ Point aCurPos(pViewData->GetScrPos(aOldStt.Col(), aOldStt.Row(), eWhich, true));
+ Point aCurPosHmm(pGridWin->PixelToLogic(aCurPos, aDrawMode));
+ Point aGridOff(aCurPosHmm - aOldPos);
+
+ bool bLOKActive = comphelper::LibreOfficeKit::isActive();
+ bool bNegativePage = rDoc.IsNegativePage(GetTab());
+
+ // fdo#63878 Fix the X position for RTL Sheet
+ if(bNegativePage && !bLOKActive)
+ {
+ aGridOff.setX(aCurPosHmm.getX() + aOldPos.getX());
+ }
+
+ rTarget.setX(bNegativePage && bLOKActive ? -aGridOff.X() : aGridOff.X());
+ rTarget.setY(aGridOff.Y());
+ return true;
+}
+
+bool ScDrawView::calculateGridOffsetForB2DRange(
+ const basegfx::B2DRange& rB2DRange,
+ basegfx::B2DVector& rTarget) const
+{
+ ScGridWindow* pGridWin(pViewData->GetActiveWin());
+
+ if(nullptr == pGridWin || rB2DRange.isEmpty())
+ {
+ return false;
+ }
+
+ // No SdrObject, so synthesise an anchor ( but don't attach it to
+ // the object as we want to maintain page anchoring )
+ ScDrawObjData aAnchor;
+ const tools::Rectangle aRectangle(
+ basegfx::fround(rB2DRange.getMinX()), basegfx::fround(rB2DRange.getMinY()),
+ basegfx::fround(rB2DRange.getMaxX()), basegfx::fround(rB2DRange.getMaxY()));
+ ScDrawLayer::GetCellAnchorFromPosition(
+ aRectangle,
+ aAnchor,
+ rDoc,
+ GetTab());
+ ScAddress aOldStt(aAnchor.maStart);
+
+ MapMode aDrawMode = pGridWin->GetDrawMapMode();
+
+ // find pos anchor position
+ Point aOldPos(rDoc.GetColOffset(aOldStt.Col(), aOldStt.Tab()), rDoc.GetRowOffset(aOldStt.Row(), aOldStt.Tab()));
+ aOldPos.setX(convertTwipToMm100(aOldPos.X()));
+ aOldPos.setY(convertTwipToMm100(aOldPos.Y()));
+
+ // find position of same point on the screen ( e.g. grid )
+ ScSplitPos eWhich(pViewData->GetActivePart());
+ Point aCurPos(pViewData->GetScrPos(aOldStt.Col(), aOldStt.Row(), eWhich, true));
+ Point aCurPosHmm(pGridWin->PixelToLogic(aCurPos, aDrawMode));
+ Point aGridOff(aCurPosHmm - aOldPos);
+
+ bool bLOKActive = comphelper::LibreOfficeKit::isActive();
+ bool bNegativePage = rDoc.IsNegativePage(GetTab());
+
+ // fdo#63878 Fix the X position for RTL Sheet
+ if(bNegativePage && !bLOKActive)
+ {
+ aGridOff.setX(aCurPosHmm.getX() + aOldPos.getX());
+ }
+
+ rTarget.setX(bLOKActive && bNegativePage ? -aGridOff.X() : aGridOff.X());
+ rTarget.setY(aGridOff.Y());
+ return true;
+}
+
+// Create a new view-local UndoManager manager for Calc
+std::unique_ptr<SdrUndoManager> ScDrawView::createLocalTextUndoManager()
+{
+ std::unique_ptr<SdrUndoManager> pUndoManager(new SdrUndoManager);
+ ScDocShell* pDocShell = pViewData ? pViewData->GetDocShell() : nullptr;
+ pUndoManager->SetDocShell(pDocShell);
+ return pUndoManager;
+}
+
+// #i123922# helper to apply a Graphic to an existing SdrObject
+SdrObject* ScDrawView::ApplyGraphicToObject(
+ SdrObject& rHitObject,
+ const Graphic& rGraphic,
+ const OUString& rBeginUndoText,
+ const OUString& rFile)
+{
+ if(auto pGrafHitObj = dynamic_cast< SdrGrafObj* >(&rHitObject))
+ {
+ rtl::Reference<SdrGrafObj> pNewGrafObj = SdrObject::Clone(*pGrafHitObj, rHitObject.getSdrModelFromSdrObject());
+
+ pNewGrafObj->SetGraphic(rGraphic);
+ BegUndo(rBeginUndoText);
+ ReplaceObjectAtView(&rHitObject, *GetSdrPageView(), pNewGrafObj.get());
+
+ // set in all cases - the Clone() will have copied an existing link (!)
+ pNewGrafObj->SetGraphicLink( rFile );
+
+ EndUndo();
+ return pNewGrafObj.get();
+ }
+ else if(rHitObject.IsClosedObj() && !dynamic_cast< SdrOle2Obj* >(&rHitObject))
+ {
+ AddUndo(std::make_unique<SdrUndoAttrObj>(rHitObject));
+
+ SfxItemSetFixed<XATTR_FILLSTYLE, XATTR_FILLBITMAP> aSet(GetModel().GetItemPool());
+
+ aSet.Put(XFillStyleItem(drawing::FillStyle_BITMAP));
+ aSet.Put(XFillBitmapItem(OUString(), rGraphic));
+ rHitObject.SetMergedItemSetAndBroadcast(aSet);
+ return &rHitObject;
+ }
+
+ return nullptr;
+}
+
+// Own derivation of ObjectContact to allow on-demand calculation of
+// GridOffset for non-linear ViewToDevice transformation (calc)
+namespace sdr::contact
+{
+ namespace {
+
+ class ObjectContactOfScDrawView final : public ObjectContactOfPageView
+ {
+ private:
+ // The ScDrawView to work on
+ const ScDrawView& mrScDrawView;
+
+ public:
+ explicit ObjectContactOfScDrawView(
+ const ScDrawView& rScDrawView,
+ SdrPageWindow& rPageWindow,
+ const char* pDebugName);
+
+ virtual bool supportsGridOffsets() const override;
+ virtual void calculateGridOffsetForViewObjectContact(
+ basegfx::B2DVector& rTarget,
+ const ViewObjectContact& rClient) const override;
+ virtual void calculateGridOffsetForB2DRange(
+ basegfx::B2DVector& rTarget,
+ const basegfx::B2DRange& rB2DRange) const override;
+ };
+
+ }
+
+ ObjectContactOfScDrawView::ObjectContactOfScDrawView(
+ const ScDrawView& rScDrawView,
+ SdrPageWindow& rPageWindow,
+ const char* pDebugName)
+ : ObjectContactOfPageView(rPageWindow, pDebugName),
+ mrScDrawView(rScDrawView)
+ {
+ }
+
+ bool ObjectContactOfScDrawView::supportsGridOffsets() const
+ {
+ // Except when scPrintTwipsMsgs flag is active,
+ // Calc in LOK mode directly sets pixel-aligned logical coordinates for draw-objects.
+ if (comphelper::LibreOfficeKit::isActive() &&
+ !comphelper::LibreOfficeKit::isCompatFlagSet(
+ comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
+ return false;
+
+ // no GridOffset support for printer
+ if(isOutputToPrinter())
+ {
+ return false;
+ }
+
+ // no GridOffset support for PDF export
+ if(isOutputToPDFFile())
+ {
+ return false;
+ }
+
+ // yes - we support it
+ return true;
+ }
+
+ void ObjectContactOfScDrawView::calculateGridOffsetForViewObjectContact(
+ basegfx::B2DVector& rTarget,
+ const ViewObjectContact& rClient) const
+ {
+ // Here the on-demand calculation happens. Try to access the SdrObject involved
+ SdrObject* pTargetSdrObject(rClient.GetViewContact().TryToGetSdrObject());
+
+ if(nullptr != pTargetSdrObject)
+ {
+ mrScDrawView.calculateGridOffsetForSdrObject(
+ *pTargetSdrObject,
+ rTarget);
+ }
+ }
+
+ void ObjectContactOfScDrawView::calculateGridOffsetForB2DRange(
+ basegfx::B2DVector& rTarget,
+ const basegfx::B2DRange& rB2DRange) const
+ {
+ // Here the on-demand calculation happens. Try to access the SdrObject involved
+ if(!rB2DRange.isEmpty())
+ {
+ mrScDrawView.calculateGridOffsetForB2DRange(
+ rB2DRange,
+ rTarget);
+ }
+ }
+}
+
+// Create own derivation of ObjectContact for calc
+sdr::contact::ObjectContact* ScDrawView::createViewSpecificObjectContact(
+ SdrPageWindow& rPageWindow,
+ const char* pDebugName) const
+{
+ return new sdr::contact::ObjectContactOfScDrawView(
+ *this,
+ rPageWindow,
+ pDebugName);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/editsh.cxx b/sc/source/ui/view/editsh.cxx
new file mode 100644
index 0000000000..c392f111e2
--- /dev/null
+++ b/sc/source/ui/view/editsh.cxx
@@ -0,0 +1,1427 @@
+/* -*- 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 .
+ */
+
+#include <comphelper/string.hxx>
+#include <comphelper/lok.hxx>
+#include <scitems.hxx>
+#include <editeng/eeitem.hxx>
+#include <i18nutil/unicode.hxx>
+#include <i18nutil/transliteration.hxx>
+
+#include <svx/clipfmtitem.hxx>
+#include <svx/svxdlg.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/outliner.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/flditem.hxx>
+#include <editeng/flstitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/urlfieldhelper.hxx>
+#include <editeng/editund2.hxx>
+#include <svx/hlnkitem.hxx>
+#include <vcl/EnumContext.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/scripttypeitem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/msg.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <svtools/cliplistener.hxx>
+#include <svl/whiter.hxx>
+#include <sot/formats.hxx>
+#include <vcl/transfer.hxx>
+#include <vcl/unohelp2.hxx>
+#include <svl/stritem.hxx>
+#include <editeng/colritem.hxx>
+
+#include <editsh.hxx>
+#include <global.hxx>
+#include <appoptio.hxx>
+#include <scmod.hxx>
+#include <sc.hrc>
+#include <inputhdl.hxx>
+#include <viewutil.hxx>
+#include <viewdata.hxx>
+#include <document.hxx>
+#include <reffind.hxx>
+#include <tabvwsh.hxx>
+#include <editutil.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <gridwin.hxx>
+
+#define ShellClass_ScEditShell
+#include <scslots.hxx>
+
+#include <scui_def.hxx>
+#include <scabstdlg.hxx>
+#include <memory>
+
+using namespace ::com::sun::star;
+
+
+SFX_IMPL_INTERFACE(ScEditShell, SfxShell)
+
+void ScEditShell::InitInterface_Impl()
+{
+ GetStaticInterface()->RegisterPopupMenu("celledit");
+}
+
+ScEditShell::ScEditShell(EditView* pView, ScViewData& rData) :
+ pEditView (pView),
+ rViewData (rData),
+ bPastePossible (false),
+ bIsInsertMode (true)
+{
+ SetPool( pEditView->GetEditEngine()->GetEmptyItemSet().GetPool() );
+ SetUndoManager( &pEditView->GetEditEngine()->GetUndoManager() );
+ SetName("EditCell");
+ SfxShell::SetContextName(vcl::EnumContext::GetContextName(vcl::EnumContext::Context::EditCell));
+}
+
+ScEditShell::~ScEditShell()
+{
+ if ( mxClipEvtLstnr.is() )
+ {
+ mxClipEvtLstnr->RemoveListener( rViewData.GetActiveWin() );
+
+ // The listener may just now be waiting for the SolarMutex and call the link
+ // afterwards, in spite of RemoveListener. So the link has to be reset, too.
+ mxClipEvtLstnr->ClearCallbackLink();
+ }
+}
+
+ScInputHandler* ScEditShell::GetMyInputHdl()
+{
+ return SC_MOD()->GetInputHdl( rViewData.GetViewShell() );
+}
+
+void ScEditShell::SetEditView(EditView* pView)
+{
+ pEditView = pView;
+ pEditView->SetInsertMode( bIsInsertMode );
+ SetPool( pEditView->GetEditEngine()->GetEmptyItemSet().GetPool() );
+ SetUndoManager( &pEditView->GetEditEngine()->GetUndoManager() );
+}
+
+static void lcl_RemoveAttribs( EditView& rEditView )
+{
+ ScEditEngineDefaulter* pEngine = static_cast<ScEditEngineDefaulter*>(rEditView.GetEditEngine());
+
+ bool bOld = pEngine->SetUpdateLayout(false);
+
+ OUString aName = ScResId( STR_UNDO_DELETECONTENTS );
+ ViewShellId nViewShellId(-1);
+ if (ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell())
+ nViewShellId = pViewSh->GetViewShellId();
+ pEngine->GetUndoManager().EnterListAction( aName, aName, 0, nViewShellId );
+
+ rEditView.RemoveAttribs(true);
+ pEngine->RepeatDefaults(); // paragraph attributes from cell formats must be preserved
+
+ pEngine->GetUndoManager().LeaveListAction();
+
+ pEngine->SetUpdateLayout(bOld);
+}
+
+static void lclInsertCharacter( EditView* pTableView, EditView* pTopView, sal_Unicode cChar )
+{
+ OUString aString( cChar );
+ if( pTableView )
+ pTableView->InsertText( aString );
+ if( pTopView )
+ pTopView->InsertText( aString );
+}
+
+void ScEditShell::Execute( SfxRequest& rReq )
+{
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+ sal_uInt16 nSlot = rReq.GetSlot();
+ SfxBindings& rBindings = rViewData.GetBindings();
+
+ ScInputHandler* pHdl = GetMyInputHdl();
+ OSL_ENSURE(pHdl,"no ScInputHandler");
+
+ EditView* pTopView = pHdl->GetTopView(); // Has thee input cell the focus?
+ EditView* pTableView = pHdl->GetTableView();
+
+ OSL_ENSURE(pTableView,"no EditView :-(");
+ /* #i91683# No EditView if spell-check dialog is active and positioned on
+ * an error and user immediately (without double click or F2) selected a
+ * text portion of that cell with the mouse and wanted to modify it. */
+ /* FIXME: Bailing out only cures the symptom and prevents a crash, no edit
+ * action is possible. A real fix somehow would need to create a valid
+ * EditView from the spell-check view. */
+ if (!pTableView)
+ return;
+
+ EditEngine* pEngine = pTableView->GetEditEngine();
+
+ pHdl->DataChanging();
+ bool bSetSelIsRef = false;
+ bool bSetModified = true;
+
+ switch ( nSlot )
+ {
+ case SID_ATTR_INSERT:
+ case FID_INS_CELL_CONTENTS: // Insert taste, while defined as Acc
+ bIsInsertMode = !pTableView->IsInsertMode();
+ pTableView->SetInsertMode( bIsInsertMode );
+ if (pTopView)
+ pTopView->SetInsertMode( bIsInsertMode );
+ rBindings.Invalidate( SID_ATTR_INSERT );
+ break;
+
+ case SID_THES:
+ {
+ OUString aReplaceText;
+ const SfxStringItem* pItem2 = rReq.GetArg(FN_PARAM_THES_WORD_REPLACE);
+ if (pItem2)
+ aReplaceText = pItem2->GetValue();
+ if (!aReplaceText.isEmpty())
+ ReplaceTextWithSynonym( *pEditView, aReplaceText );
+ }
+ break;
+
+ case SID_COPY:
+ pTableView->Copy();
+ bSetModified = false;
+ break;
+
+ case SID_CUT:
+ pTableView->Cut();
+ if (pTopView)
+ pTopView->DeleteSelected();
+ break;
+
+ case SID_PASTE:
+ {
+ EVControlBits nControl = pTableView->GetControlWord();
+ if (pTopView)
+ {
+ pTopView->Paste();
+ pTableView->SetControlWord(nControl | EVControlBits::SINGLELINEPASTE);
+ }
+
+ pTableView->PasteSpecial();
+ pTableView->SetControlWord(nControl);
+ }
+ break;
+
+ case SID_DELETE:
+ pTableView->DeleteSelected();
+ if (pTopView)
+ pTopView->DeleteSelected();
+ break;
+
+ case SID_CELL_FORMAT_RESET: // "Standard"
+ lcl_RemoveAttribs( *pTableView );
+ if ( pTopView )
+ lcl_RemoveAttribs( *pTopView );
+ break;
+
+ case SID_CLIPBOARD_FORMAT_ITEMS:
+ {
+ SotClipboardFormatId nFormat = SotClipboardFormatId::NONE;
+ const SfxPoolItem* pItem;
+ if ( pReqArgs && pReqArgs->GetItemState(nSlot, true, &pItem) == SfxItemState::SET )
+ if (auto pIntItem = dynamic_cast<const SfxUInt32Item*>( pItem))
+ nFormat = static_cast<SotClipboardFormatId>(pIntItem->GetValue());
+
+ if ( nFormat != SotClipboardFormatId::NONE )
+ {
+ if (SotClipboardFormatId::STRING == nFormat)
+ pTableView->Paste();
+ else
+ pTableView->PasteSpecial();
+
+ if (pTopView)
+ pTopView->Paste();
+ }
+ }
+ break;
+
+ case SID_PASTE_SPECIAL:
+ {
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ ScopedVclPtr<SfxAbstractPasteDialog> pDlg(pFact->CreatePasteDialog(rViewData.GetDialogParent()));
+ SotClipboardFormatId nFormat = SotClipboardFormatId::NONE;
+ pDlg->Insert( SotClipboardFormatId::STRING, OUString() );
+ pDlg->Insert( SotClipboardFormatId::RTF, OUString() );
+ pDlg->Insert( SotClipboardFormatId::RICHTEXT, OUString() );
+ // Do not offer SotClipboardFormatId::STRING_TSVC for
+ // in-cell paste.
+
+ TransferableDataHelper aDataHelper(
+ TransferableDataHelper::CreateFromSystemClipboard( rViewData.GetActiveWin() ) );
+
+ nFormat = pDlg->GetFormat( aDataHelper.GetTransferable() );
+ pDlg.disposeAndClear();
+
+ // while the dialog was open, edit mode may have been stopped
+ if (!SC_MOD()->IsInputMode())
+ return;
+
+ if (nFormat != SotClipboardFormatId::NONE)
+ {
+ if (SotClipboardFormatId::STRING == nFormat)
+ pTableView->Paste();
+ else
+ pTableView->PasteSpecial();
+
+ if (pTopView)
+ pTopView->Paste();
+ }
+
+ if (vcl::Window* pViewWindow = pTopView ? pTopView->GetWindow() : nullptr)
+ pViewWindow->GrabFocus();
+ }
+ break;
+
+ case SID_PASTE_UNFORMATTED:
+ {
+ pTableView->Paste();
+
+ if (pTopView)
+ {
+ pTopView->Paste();
+ if (vcl::Window* pViewWindow = pTopView->GetWindow())
+ pViewWindow->GrabFocus();
+ }
+ }
+ break;
+
+ case SID_SELECTALL:
+ {
+ sal_Int32 nPar = pEngine->GetParagraphCount();
+ if (nPar)
+ {
+ sal_Int32 nLen = pEngine->GetTextLen(nPar-1);
+ pTableView->SetSelection(ESelection(0,0,nPar-1,nLen));
+ if (pTopView)
+ pTopView->SetSelection(ESelection(0,0,nPar-1,nLen));
+ rBindings.Invalidate( SID_ATTR_CHAR_FONT );
+ rBindings.Invalidate( SID_ATTR_CHAR_FONTHEIGHT );
+ rBindings.Invalidate( SID_ATTR_CHAR_WEIGHT );
+ rBindings.Invalidate( SID_ATTR_CHAR_POSTURE );
+ rBindings.Invalidate( SID_ATTR_CHAR_UNDERLINE );
+ rBindings.Invalidate( SID_ATTR_CHAR_STRIKEOUT );
+ rBindings.Invalidate( SID_ATTR_CHAR_SHADOWED );
+ rBindings.Invalidate( SID_ATTR_CHAR_KERNING );
+ rBindings.Invalidate( SID_ATTR_CHAR_COLOR );
+ rBindings.Invalidate( SID_SET_SUPER_SCRIPT );
+ rBindings.Invalidate( SID_SET_SUB_SCRIPT );
+ }
+ }
+ return;
+ case SID_UNICODE_NOTATION_TOGGLE:
+ {
+ EditView* pActiveView = pHdl->GetActiveView();
+ if( pActiveView )
+ {
+ OUString sInput = pEngine->GetText();
+ ESelection aSel( pActiveView->GetSelection() );
+ if( aSel.HasRange() )
+ sInput = pActiveView->GetSelected();
+
+ if( aSel.nStartPos > aSel.nEndPos )
+ aSel.nEndPos = aSel.nStartPos;
+
+ //calculate a valid end-position by reading logical characters
+ sal_Int32 nUtf16Pos=0;
+ while( (nUtf16Pos < sInput.getLength()) && (nUtf16Pos < aSel.nEndPos) )
+ {
+ sInput.iterateCodePoints(&nUtf16Pos);
+ if( nUtf16Pos > aSel.nEndPos )
+ aSel.nEndPos = nUtf16Pos;
+ }
+
+ ToggleUnicodeCodepoint aToggle;
+ while( nUtf16Pos && aToggle.AllowMoreInput( sInput[nUtf16Pos-1]) )
+ --nUtf16Pos;
+ OUString sReplacement = aToggle.ReplacementString();
+ if( !sReplacement.isEmpty() )
+ {
+ aSel.nStartPos = aSel.nEndPos - aToggle.StringToReplace().getLength();
+ pTableView->SetSelection( aSel );
+ pTableView->InsertText(sReplacement, true);
+ if( pTopView )
+ {
+ pTopView->SetSelection( aSel );
+ pTopView->InsertText(sReplacement, true);
+ }
+ }
+ }
+ }
+ break;
+
+ case SID_CHARMAP:
+ {
+ SvtScriptType nScript = pTableView->GetSelectedScriptType();
+ sal_uInt16 nFontWhich = ( nScript == SvtScriptType::ASIAN ) ? EE_CHAR_FONTINFO_CJK :
+ ( ( nScript == SvtScriptType::COMPLEX ) ? EE_CHAR_FONTINFO_CTL :
+ EE_CHAR_FONTINFO );
+ auto const attribs = pTableView->GetAttribs();
+ const SvxFontItem& rItem = static_cast<const SvxFontItem&>(
+ attribs.Get(nFontWhich));
+
+ OUString aString;
+ std::shared_ptr<SvxFontItem> aNewItem(std::make_shared<SvxFontItem>(EE_CHAR_FONTINFO));
+
+ const SfxItemSet *pArgs = rReq.GetArgs();
+ const SfxPoolItem* pItem = nullptr;
+ if( pArgs )
+ pArgs->GetItemState(SID_CHARMAP, false, &pItem);
+
+ if ( pItem )
+ {
+ aString = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ const SfxStringItem* pFontItem = pArgs->GetItemIfSet( SID_ATTR_SPECIALCHAR, false);
+ if ( pFontItem )
+ {
+ const OUString& aFontName(pFontItem->GetValue());
+ vcl::Font aFont(aFontName, Size(1,1)); // Size just because CTOR
+ // tdf#125054 see comment in drtxob.cxx, same ID
+ aNewItem = std::make_shared<SvxFontItem>(
+ aFont.GetFamilyType(), aFont.GetFamilyName(),
+ aFont.GetStyleName(), aFont.GetPitch(),
+ aFont.GetCharSet(), ATTR_FONT);
+ }
+ else
+ {
+ aNewItem.reset(rItem.Clone());
+ }
+
+ // tdf#125054 force Item to correct intended ID
+ aNewItem->SetWhich(EE_CHAR_FONTINFO);
+ }
+ else
+ {
+ ScViewUtil::ExecuteCharMap(rItem, *rViewData.GetViewShell());
+
+ // while the dialog was open, edit mode may have been stopped
+ if (!SC_MOD()->IsInputMode())
+ return;
+ }
+
+ if ( !aString.isEmpty() )
+ {
+ // if string contains WEAK characters, set all fonts
+ SvtScriptType nSetScript;
+ ScDocument& rDoc = rViewData.GetDocument();
+ if ( rDoc.HasStringWeakCharacters( aString ) )
+ nSetScript = SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX;
+ else
+ nSetScript = rDoc.GetStringScriptType( aString );
+
+ SfxItemSet aSet( pTableView->GetEmptyItemSet() );
+ SvxScriptSetItem aSetItem( SID_ATTR_CHAR_FONT, GetPool() );
+ aSetItem.PutItemForScriptType( nSetScript, *aNewItem );
+ aSet.Put( aSetItem.GetItemSet(), false );
+
+ // SetAttribs on the View selects a word, when nothing is selected
+ pTableView->GetEditEngine()->QuickSetAttribs( aSet, pTableView->GetSelection() );
+ pTableView->InsertText(aString);
+ if (pTopView)
+ pTopView->InsertText(aString);
+
+ SfxStringItem aStringItem( SID_CHARMAP, aString );
+ SfxStringItem aFontItem( SID_ATTR_SPECIALCHAR, aNewItem->GetFamilyName() );
+ rReq.AppendItem( aFontItem );
+ rReq.AppendItem( aStringItem );
+ rReq.Done();
+
+ }
+
+ if (vcl::Window* pViewWindow = pTopView ? pTopView->GetWindow() : nullptr)
+ pViewWindow->GrabFocus();
+ }
+ break;
+
+ case FID_INSERT_NAME:
+ {
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScNamePasteDlg> pDlg(pFact->CreateScNamePasteDlg(rViewData.GetDialogParent(), rViewData.GetDocShell()));
+ short nRet = pDlg->Execute();
+ // pDlg is needed below
+
+ // while the dialog was open, edit mode may have been stopped
+ if (!SC_MOD()->IsInputMode())
+ return;
+
+ if ( nRet == BTN_PASTE_NAME )
+ {
+ std::vector<OUString> aNames = pDlg->GetSelectedNames();
+ if (!aNames.empty())
+ {
+ OUStringBuffer aBuffer;
+ for (const auto& rName : aNames)
+ {
+ aBuffer.append(rName + " ");
+ }
+ const OUString s = aBuffer.makeStringAndClear();
+ pTableView->InsertText(s);
+ if (pTopView)
+ pTopView->InsertText(s);
+ }
+ }
+ pDlg.disposeAndClear();
+
+ if (vcl::Window* pViewWindow = pTopView ? pTopView->GetWindow() : nullptr)
+ pViewWindow->GrabFocus();
+ }
+ break;
+
+ case SID_CHAR_DLG_EFFECT:
+ case SID_CHAR_DLG:
+ {
+ SfxItemSet aAttrs( pTableView->GetAttribs() );
+
+ SfxObjectShell* pObjSh = rViewData.GetSfxDocShell();
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateScCharDlg(
+ rViewData.GetDialogParent(), &aAttrs, pObjSh, false));
+ if (nSlot == SID_CHAR_DLG_EFFECT)
+ {
+ pDlg->SetCurPageId("fonteffects");
+ }
+ short nRet = pDlg->Execute();
+ // pDlg is needed below
+
+ // while the dialog was open, edit mode may have been stopped
+ if (!SC_MOD()->IsInputMode())
+ return;
+
+ if ( nRet == RET_OK )
+ {
+ const SfxItemSet* pOut = pDlg->GetOutputItemSet();
+ pTableView->SetAttribs( *pOut );
+ }
+ }
+ break;
+
+ case SID_TOGGLE_REL:
+ {
+ /* TODO: MLFORMULA: this should work also with multi-line formulas. */
+ if (pEngine->GetParagraphCount() == 1)
+ {
+ OUString aText = pEngine->GetText();
+ ESelection aSel = pEditView->GetSelection(); // current View
+
+ ScDocument& rDoc = rViewData.GetDocument();
+ ScRefFinder aFinder(aText, rViewData.GetCurPos(), rDoc, rDoc.GetAddressConvention());
+ aFinder.ToggleRel( aSel.nStartPos, aSel.nEndPos );
+ if (aFinder.GetFound())
+ {
+ const OUString& aNew = aFinder.GetText();
+ ESelection aNewSel( 0,aFinder.GetSelStart(), 0,aFinder.GetSelEnd() );
+ pEngine->SetText( aNew );
+ pTableView->SetSelection( aNewSel );
+ if ( pTopView )
+ {
+ pTopView->GetEditEngine()->SetText( aNew );
+ pTopView->SetSelection( aNewSel );
+ }
+
+ // reference is being selected -> do not overwrite when typing
+ bSetSelIsRef = true;
+ }
+ }
+ }
+ break;
+
+ case SID_HYPERLINK_SETLINK:
+ if( pReqArgs )
+ {
+ const SfxPoolItem* pItem;
+ if ( pReqArgs->GetItemState( SID_HYPERLINK_SETLINK, true, &pItem ) == SfxItemState::SET )
+ {
+ const SvxHyperlinkItem* pHyper = static_cast<const SvxHyperlinkItem*>(pItem);
+ const OUString& rName = pHyper->GetName();
+ const OUString& rURL = pHyper->GetURL();
+ const OUString& rTarget = pHyper->GetTargetFrame();
+ SvxLinkInsertMode eMode = pHyper->GetInsertMode();
+
+ bool bCellLinksOnly
+ = (SC_MOD()->GetAppOptions().GetLinksInsertedLikeMSExcel()
+ && rViewData.GetSfxDocShell()->GetMedium()->GetFilter()->IsMSOFormat())
+ || comphelper::LibreOfficeKit::isActive();
+
+ bool bDone = false;
+ if ( (eMode == HLINK_DEFAULT || eMode == HLINK_FIELD) && !bCellLinksOnly )
+ {
+ std::unique_ptr<const SvxFieldData> aSvxFieldDataPtr(GetURLField());
+ const SvxURLField* pURLField(static_cast<const SvxURLField*>(aSvxFieldDataPtr.get()));
+ if ( pURLField )
+ {
+ // select old field
+
+ ESelection aSel = pTableView->GetSelection();
+ aSel.Adjust();
+ aSel.nEndPara = aSel.nStartPara;
+ aSel.nEndPos = aSel.nStartPos + 1;
+ pTableView->SetSelection( aSel );
+
+ // insert new field
+
+ SvxURLField aURLField( rURL, rName, SvxURLFormat::Repr );
+ aURLField.SetTargetFrame( rTarget );
+ SvxFieldItem aURLItem( aURLField, EE_FEATURE_FIELD );
+ pTableView->InsertField( aURLItem );
+ pTableView->SetSelection( aSel ); // select inserted field
+
+ // now also fields in the Top-View
+
+ if ( pTopView )
+ {
+ aSel = pTopView->GetSelection();
+ aSel.nEndPara = aSel.nStartPara;
+ aSel.nEndPos = aSel.nStartPos + 1;
+ pTopView->SetSelection( aSel );
+ pTopView->InsertField( aURLItem );
+ pTopView->SetSelection( aSel ); // select inserted field
+ }
+
+ bDone = true;
+ }
+ }
+
+ if (!bDone)
+ {
+ if (bCellLinksOnly)
+ {
+ sal_Int32 nPar = pEngine->GetParagraphCount();
+ if (nPar)
+ {
+ sal_Int32 nLen = pEngine->GetTextLen(nPar - 1);
+ pTableView->SetSelection(ESelection(0, 0, nPar - 1, nLen));
+ if (pTopView)
+ pTopView->SetSelection(ESelection(0, 0, nPar - 1, nLen));
+ }
+ }
+ rViewData.GetViewShell()->
+ InsertURL( rName, rURL, rTarget, static_cast<sal_uInt16>(eMode) );
+
+ // when "Button", the InsertURL in ViewShell turns the EditShell off
+ // thus the immediate return statement
+ return;
+ }
+ }
+ }
+ break;
+ case SID_OPEN_HYPERLINK:
+ {
+ const SvxFieldItem* pFieldItem
+ = pEditView->GetFieldAtSelection(/*AlsoCheckBeforeCursor=*/true);
+ const SvxFieldData* pField = pFieldItem ? pFieldItem->GetField() : nullptr;
+ if (const SvxURLField* pURLField = dynamic_cast<const SvxURLField*>(pField))
+ ScGlobal::OpenURL( pURLField->GetURL(), pURLField->GetTargetFrame(), true );
+ return;
+ }
+ case SID_EDIT_HYPERLINK:
+ {
+ // Ensure the field is selected first
+ pEditView->SelectFieldAtCursor();
+ rViewData.GetViewShell()->GetViewFrame().GetDispatcher()->Execute(
+ SID_HYPERLINK_DIALOG);
+ }
+ break;
+ case SID_COPY_HYPERLINK_LOCATION:
+ {
+ const SvxFieldItem* pFieldItem
+ = pEditView->GetFieldAtSelection(/*AlsoCheckBeforeCursor=*/true);
+ const SvxFieldData* pField = pFieldItem ? pFieldItem->GetField() : nullptr;
+ if (const SvxURLField* pURLField = dynamic_cast<const SvxURLField*>(pField))
+ {
+ uno::Reference<datatransfer::clipboard::XClipboard> xClipboard
+ = pEditView->GetClipboard();
+ vcl::unohelper::TextDataObject::CopyStringTo(pURLField->GetURL(), xClipboard, SfxViewShell::Current());
+ }
+ }
+ break;
+ case SID_REMOVE_HYPERLINK:
+ {
+ URLFieldHelper::RemoveURLField(*pEditView);
+ }
+ break;
+
+ case FN_INSERT_SOFT_HYPHEN:
+ lclInsertCharacter( pTableView, pTopView, CHAR_SHY );
+ break;
+ case FN_INSERT_HARDHYPHEN:
+ lclInsertCharacter( pTableView, pTopView, CHAR_NBHY );
+ break;
+ case FN_INSERT_HARD_SPACE:
+ lclInsertCharacter( pTableView, pTopView, CHAR_NBSP );
+ break;
+ case FN_INSERT_NNBSP:
+ lclInsertCharacter( pTableView, pTopView, CHAR_NNBSP );
+ break;
+ case SID_INSERT_RLM:
+ lclInsertCharacter( pTableView, pTopView, CHAR_RLM );
+ break;
+ case SID_INSERT_LRM:
+ lclInsertCharacter( pTableView, pTopView, CHAR_LRM );
+ break;
+ case SID_INSERT_ZWSP:
+ lclInsertCharacter( pTableView, pTopView, CHAR_ZWSP );
+ break;
+ case SID_INSERT_WJ:
+ lclInsertCharacter( pTableView, pTopView, CHAR_WJ );
+ break;
+ case SID_INSERT_FIELD_SHEET:
+ {
+ SvxTableField aField(rViewData.GetTabNo());
+ SvxFieldItem aItem(aField, EE_FEATURE_FIELD);
+ pTableView->InsertField(aItem);
+ }
+ break;
+ case SID_INSERT_FIELD_TITLE:
+ {
+ SvxFileField aField;
+ SvxFieldItem aItem(aField, EE_FEATURE_FIELD);
+ pTableView->InsertField(aItem);
+ }
+ break;
+ case SID_INSERT_FIELD_DATE_VAR:
+ {
+ SvxDateField aField;
+ SvxFieldItem aItem(aField, EE_FEATURE_FIELD);
+ pTableView->InsertField(aItem);
+ }
+ break;
+ }
+
+ pHdl->DataChanged(false, bSetModified);
+ if (bSetSelIsRef)
+ pHdl->SetSelIsRef(true);
+}
+
+static void lcl_DisableAll( SfxItemSet& rSet ) // disable all slots
+{
+ SfxWhichIter aIter( rSet );
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while (nWhich)
+ {
+ rSet.DisableItem( nWhich );
+ nWhich = aIter.NextWhich();
+ }
+}
+
+void ScEditShell::GetState( SfxItemSet& rSet )
+{
+ // When deactivating the view, edit mode is stopped, but the EditShell is left active
+ // (a shell can't be removed from within Deactivate). In that state, the EditView isn't inserted
+ // into the EditEngine, so it can have an invalid selection and must not be used.
+ ScInputHandler* pHdl = GetMyInputHdl();
+ if ( !rViewData.HasEditView( rViewData.GetActivePart() ) )
+ {
+ lcl_DisableAll( rSet );
+
+ // Some items are actually useful and still applicable when in formula building mode: enable
+ if (pHdl && pHdl->IsFormulaMode())
+ {
+ rSet.ClearItem(SID_TOGGLE_REL); // F4 Cycle Cell Reference Types
+ rSet.ClearItem(SID_CHARMAP); // Insert Special Characters
+ }
+ return;
+ }
+
+ EditView* pActiveView = pHdl ? pHdl->GetActiveView() : pEditView;
+
+ SfxWhichIter aIter( rSet );
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while (nWhich)
+ {
+ switch (nWhich)
+ {
+ case SID_ATTR_INSERT: // Status row
+ {
+ if ( pActiveView )
+ rSet.Put( SfxBoolItem( nWhich, pActiveView->IsInsertMode() ) );
+ else
+ {
+ // Here the code used to pass the value 42 and it used
+ // to "work" without warnings because the SfxBoolItem
+ // was based on 'sal_Bool', which is actually 'unsigned
+ // char'. But now it uses actual 'bool', and passing 42
+ // for a 'bool' parameter causes a warning at least with
+ // MSVC. So use 'true'. I really really hope there is
+ // not code somewhere that retrieves this "boolean" item
+ // and checks it value for the magic value 42...
+ rSet.Put( SfxBoolItem( nWhich, true) );
+ }
+ }
+ break;
+
+ case SID_HYPERLINK_GETLINK:
+ {
+ SvxHyperlinkItem aHLinkItem;
+ bool bCellLinksOnly
+ = (SC_MOD()->GetAppOptions().GetLinksInsertedLikeMSExcel()
+ && rViewData.GetSfxDocShell()->GetMedium()->GetFilter()->IsMSOFormat())
+ || comphelper::LibreOfficeKit::isActive();
+ std::unique_ptr<const SvxFieldData> aSvxFieldDataPtr(GetURLField());
+ const SvxURLField* pURLField(static_cast<const SvxURLField*>(aSvxFieldDataPtr.get()));
+ if (!bCellLinksOnly)
+ {
+ if (pURLField)
+ {
+ aHLinkItem.SetName(pURLField->GetRepresentation());
+ aHLinkItem.SetURL(pURLField->GetURL());
+ aHLinkItem.SetTargetFrame(pURLField->GetTargetFrame());
+ }
+ else if (pActiveView)
+ {
+ // use selected text as name for urls
+ OUString sReturn = pActiveView->GetSelected();
+ sReturn = sReturn.copy(
+ 0, std::min(sReturn.getLength(), static_cast<sal_Int32>(255)));
+ aHLinkItem.SetName(comphelper::string::stripEnd(sReturn, ' '));
+ }
+ }
+ else
+ {
+ if (!pURLField)
+ {
+ aSvxFieldDataPtr = GetFirstURLFieldFromCell();
+ pURLField = static_cast<const SvxURLField*>(aSvxFieldDataPtr.get());
+ }
+ if (pURLField)
+ {
+ aHLinkItem.SetURL(pURLField->GetURL());
+ aHLinkItem.SetTargetFrame(pURLField->GetTargetFrame());
+ }
+ ScDocument& rDoc = rViewData.GetDocument();
+ SCCOL nPosX = rViewData.GetCurX();
+ SCROW nPosY = rViewData.GetCurY();
+ SCTAB nTab = rViewData.GetTabNo();
+ aHLinkItem.SetName(rDoc.GetString(nPosX, nPosY, nTab));
+ }
+ rSet.Put(aHLinkItem);
+ }
+ break;
+
+ case SID_OPEN_HYPERLINK:
+ case SID_EDIT_HYPERLINK:
+ case SID_COPY_HYPERLINK_LOCATION:
+ case SID_REMOVE_HYPERLINK:
+ {
+ if (!URLFieldHelper::IsCursorAtURLField(*pEditView,
+ /*AlsoCheckBeforeCursor=*/true))
+ rSet.DisableItem (nWhich);
+ }
+ break;
+
+ case SID_TRANSLITERATE_HALFWIDTH:
+ case SID_TRANSLITERATE_FULLWIDTH:
+ case SID_TRANSLITERATE_HIRAGANA:
+ case SID_TRANSLITERATE_KATAKANA:
+ case SID_INSERT_RLM:
+ case SID_INSERT_LRM:
+ ScViewUtil::HideDisabledSlot( rSet, rViewData.GetBindings(), nWhich );
+ break;
+
+ case SID_THES:
+ {
+ OUString aStatusVal;
+ LanguageType nLang = LANGUAGE_NONE;
+ bool bIsLookUpWord = pActiveView &&
+ GetStatusValueForThesaurusFromContext(aStatusVal, nLang, *pActiveView);
+ rSet.Put( SfxStringItem( SID_THES, aStatusVal ) );
+
+ // disable thesaurus context menu entry if there is nothing to look up
+ bool bCanDoThesaurus = ScModule::HasThesaurusLanguage( nLang );
+ if (!bIsLookUpWord || !bCanDoThesaurus)
+ rSet.DisableItem( SID_THES );
+ }
+ break;
+ case SID_INSERT_FIELD_SHEET:
+ case SID_INSERT_FIELD_TITLE:
+ case SID_INSERT_FIELD_DATE_VAR:
+ break;
+ case SID_COPY:
+ case SID_CUT:
+ if (GetObjectShell() && GetObjectShell()->isContentExtractionLocked())
+ {
+ rSet.DisableItem(SID_COPY);
+ rSet.DisableItem(SID_CUT);
+ }
+ break;
+
+ }
+ nWhich = aIter.NextWhich();
+ }
+}
+
+std::unique_ptr<const SvxFieldData> ScEditShell::GetURLField()
+{
+ ScInputHandler* pHdl = GetMyInputHdl();
+ EditView* pActiveView = pHdl ? pHdl->GetActiveView() : pEditView;
+ if (!pActiveView)
+ return std::unique_ptr<const SvxFieldData>();
+
+ const SvxFieldData* pField = pActiveView->GetFieldUnderMouseOrInSelectionOrAtCursor();
+ if (auto pURLField = dynamic_cast<const SvxURLField*>(pField))
+ return pURLField->Clone();
+
+ return std::unique_ptr<const SvxFieldData>();
+}
+
+std::unique_ptr<const SvxFieldData> ScEditShell::GetFirstURLFieldFromCell()
+{
+ EditEngine* pEE = GetEditView()->GetEditEngine();
+ sal_Int32 nParaCount = pEE->GetParagraphCount();
+ for (sal_Int32 nPara = 0; nPara < nParaCount; ++nPara)
+ {
+ ESelection aSel(nPara, 0);
+ std::vector<sal_Int32> aPosList;
+ pEE->GetPortions(nPara, aPosList);
+ for (const auto& rPos : aPosList)
+ {
+ aSel.nEndPos = rPos;
+
+ SfxItemSet aEditSet(pEE->GetAttribs(aSel));
+ if (aSel.nStartPos + 1 == aSel.nEndPos)
+ {
+ // test if the character is a text field
+ if (const SvxFieldItem* pItem = aEditSet.GetItemIfSet(EE_FEATURE_FIELD, false))
+ {
+ const SvxFieldData* pField = pItem->GetField();
+ if (const SvxURLField* pUrlField = dynamic_cast<const SvxURLField*>(pField))
+ {
+ return pUrlField->Clone();
+ }
+ }
+ }
+ aSel.nStartPos = aSel.nEndPos;
+ }
+ }
+
+ return std::unique_ptr<const SvxFieldData>();
+}
+
+IMPL_LINK( ScEditShell, ClipboardChanged, TransferableDataHelper*, pDataHelper, void )
+{
+ bPastePossible = ( pDataHelper->HasFormat( SotClipboardFormatId::STRING )
+ || pDataHelper->HasFormat( SotClipboardFormatId::RTF )
+ || pDataHelper->HasFormat( SotClipboardFormatId::RICHTEXT ));
+
+ SfxBindings& rBindings = rViewData.GetBindings();
+ rBindings.Invalidate( SID_PASTE );
+ rBindings.Invalidate( SID_PASTE_SPECIAL );
+ rBindings.Invalidate( SID_PASTE_UNFORMATTED );
+ rBindings.Invalidate( SID_CLIPBOARD_FORMAT_ITEMS );
+}
+
+void ScEditShell::GetClipState( SfxItemSet& rSet )
+{
+ // Do not offer SotClipboardFormatId::STRING_TSVC for in-cell paste.
+
+ if ( !mxClipEvtLstnr.is() )
+ {
+ // create listener
+ mxClipEvtLstnr = new TransferableClipboardListener( LINK( this, ScEditShell, ClipboardChanged ) );
+ vcl::Window* pWin = rViewData.GetActiveWin();
+ mxClipEvtLstnr->AddListener( pWin );
+
+ // get initial state
+ TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( rViewData.GetActiveWin() ) );
+ bPastePossible = ( aDataHelper.HasFormat( SotClipboardFormatId::STRING )
+ || aDataHelper.HasFormat( SotClipboardFormatId::RTF )
+ || aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT ) );
+ }
+
+ SfxWhichIter aIter( rSet );
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while (nWhich)
+ {
+ switch (nWhich)
+ {
+ case SID_PASTE:
+ case SID_PASTE_SPECIAL:
+ case SID_PASTE_UNFORMATTED:
+ if( !bPastePossible )
+ rSet.DisableItem( nWhich );
+ break;
+ case SID_CLIPBOARD_FORMAT_ITEMS:
+ if( bPastePossible )
+ {
+ SvxClipboardFormatItem aFormats( SID_CLIPBOARD_FORMAT_ITEMS );
+ TransferableDataHelper aDataHelper(
+ TransferableDataHelper::CreateFromSystemClipboard( rViewData.GetActiveWin() ) );
+
+ if ( aDataHelper.HasFormat( SotClipboardFormatId::STRING ) )
+ aFormats.AddClipbrdFormat( SotClipboardFormatId::STRING );
+ if ( aDataHelper.HasFormat( SotClipboardFormatId::RTF ) )
+ aFormats.AddClipbrdFormat( SotClipboardFormatId::RTF );
+
+ rSet.Put( aFormats );
+ }
+ else
+ rSet.DisableItem( nWhich );
+ break;
+ }
+ nWhich = aIter.NextWhich();
+ }
+}
+
+static void lcl_InvalidateUnder( SfxBindings& rBindings )
+{
+ rBindings.Invalidate( SID_ATTR_CHAR_UNDERLINE );
+ rBindings.Invalidate( SID_ULINE_VAL_NONE );
+ rBindings.Invalidate( SID_ULINE_VAL_SINGLE );
+ rBindings.Invalidate( SID_ULINE_VAL_DOUBLE );
+ rBindings.Invalidate( SID_ULINE_VAL_DOTTED );
+}
+
+void ScEditShell::ExecuteAttr(SfxRequest& rReq)
+{
+ SfxItemSet aSet( pEditView->GetEmptyItemSet() );
+ SfxBindings& rBindings = rViewData.GetBindings();
+ const SfxItemSet* pArgs = rReq.GetArgs();
+ sal_uInt16 nSlot = rReq.GetSlot();
+
+ switch ( nSlot )
+ {
+ case SID_ATTR_CHAR_FONTHEIGHT:
+ case SID_ATTR_CHAR_FONT:
+ {
+ if (pArgs)
+ {
+ // #i78017 establish the same behaviour as in Writer
+ SvtScriptType nScript = SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX;
+ if (nSlot == SID_ATTR_CHAR_FONT)
+ {
+ nScript = pEditView->GetSelectedScriptType();
+ if (nScript == SvtScriptType::NONE) nScript = ScGlobal::GetDefaultScriptType();
+ }
+
+ SfxItemPool& rPool = GetPool();
+ SvxScriptSetItem aSetItem( nSlot, rPool );
+ sal_uInt16 nWhich = rPool.GetWhich( nSlot );
+ aSetItem.PutItemForScriptType( nScript, pArgs->Get( nWhich ) );
+
+ aSet.Put( aSetItem.GetItemSet(), false );
+ }
+ }
+ break;
+
+ case SID_ATTR_CHAR_COLOR:
+ {
+ if (pArgs)
+ {
+ aSet.Put( pArgs->Get( pArgs->GetPool()->GetWhich( nSlot ) ) );
+ rBindings.Invalidate( nSlot );
+ }
+ }
+ break;
+
+ // Toggles
+
+ case SID_ATTR_CHAR_WEIGHT:
+ {
+ // #i78017 establish the same behaviour as in Writer
+ SvtScriptType nScript = SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX;
+
+ SfxItemPool& rPool = GetPool();
+
+ bool bOld = false;
+ SvxScriptSetItem aOldSetItem( nSlot, rPool );
+ aOldSetItem.GetItemSet().Put( pEditView->GetAttribs(), false );
+ const SfxPoolItem* pCore = aOldSetItem.GetItemOfScript( nScript );
+ if ( pCore && static_cast<const SvxWeightItem*>(pCore)->GetWeight() > WEIGHT_NORMAL )
+ bOld = true;
+
+ SvxScriptSetItem aSetItem( nSlot, rPool );
+ aSetItem.PutItemForScriptType( nScript,
+ SvxWeightItem( bOld ? WEIGHT_NORMAL : WEIGHT_BOLD, EE_CHAR_WEIGHT ) );
+ aSet.Put( aSetItem.GetItemSet(), false );
+
+ rBindings.Invalidate( nSlot );
+ }
+ break;
+
+ case SID_ATTR_CHAR_POSTURE:
+ {
+ // #i78017 establish the same behaviour as in Writer
+ SvtScriptType nScript = SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX;
+
+ SfxItemPool& rPool = GetPool();
+
+ bool bOld = false;
+ SvxScriptSetItem aOldSetItem( nSlot, rPool );
+ aOldSetItem.GetItemSet().Put( pEditView->GetAttribs(), false );
+ const SfxPoolItem* pCore = aOldSetItem.GetItemOfScript( nScript );
+ if ( pCore && static_cast<const SvxPostureItem*>(pCore)->GetValue() != ITALIC_NONE )
+ bOld = true;
+
+ SvxScriptSetItem aSetItem( nSlot, rPool );
+ aSetItem.PutItemForScriptType( nScript,
+ SvxPostureItem( bOld ? ITALIC_NONE : ITALIC_NORMAL, EE_CHAR_ITALIC ) );
+ aSet.Put( aSetItem.GetItemSet(), false );
+
+ rBindings.Invalidate( nSlot );
+ }
+ break;
+
+ case SID_ULINE_VAL_NONE:
+ aSet.Put( SvxUnderlineItem( LINESTYLE_NONE, EE_CHAR_UNDERLINE ) );
+ lcl_InvalidateUnder( rBindings );
+ break;
+
+ case SID_ATTR_CHAR_UNDERLINE:
+ case SID_ULINE_VAL_SINGLE:
+ case SID_ULINE_VAL_DOUBLE:
+ case SID_ULINE_VAL_DOTTED:
+ {
+ FontLineStyle eOld = pEditView->GetAttribs().Get(EE_CHAR_UNDERLINE).GetLineStyle();
+ FontLineStyle eNew = eOld;
+ switch (nSlot)
+ {
+ case SID_ATTR_CHAR_UNDERLINE:
+ if ( pArgs )
+ {
+ const SvxTextLineItem& rTextLineItem = static_cast< const SvxTextLineItem& >( pArgs->Get( pArgs->GetPool()->GetWhich(nSlot) ) );
+ eNew = rTextLineItem.GetLineStyle();
+ }
+ else
+ {
+ eNew = ( eOld != LINESTYLE_NONE ) ? LINESTYLE_NONE : LINESTYLE_SINGLE;
+ }
+ break;
+ case SID_ULINE_VAL_SINGLE:
+ eNew = ( eOld == LINESTYLE_SINGLE ) ? LINESTYLE_NONE : LINESTYLE_SINGLE;
+ break;
+ case SID_ULINE_VAL_DOUBLE:
+ eNew = ( eOld == LINESTYLE_DOUBLE ) ? LINESTYLE_NONE : LINESTYLE_DOUBLE;
+ break;
+ case SID_ULINE_VAL_DOTTED:
+ eNew = ( eOld == LINESTYLE_DOTTED ) ? LINESTYLE_NONE : LINESTYLE_DOTTED;
+ break;
+ }
+ aSet.Put( SvxUnderlineItem( eNew, EE_CHAR_UNDERLINE ) );
+ lcl_InvalidateUnder( rBindings );
+ }
+ break;
+
+ case SID_ATTR_CHAR_OVERLINE:
+ {
+ FontLineStyle eOld = pEditView->GetAttribs().Get(EE_CHAR_OVERLINE).GetLineStyle();
+ FontLineStyle eNew = ( eOld != LINESTYLE_NONE ) ? LINESTYLE_NONE : LINESTYLE_SINGLE;
+ aSet.Put( SvxOverlineItem( eNew, EE_CHAR_OVERLINE ) );
+ rBindings.Invalidate( nSlot );
+ }
+ break;
+
+ case SID_ATTR_CHAR_STRIKEOUT:
+ {
+ bool bOld = pEditView->GetAttribs().Get(EE_CHAR_STRIKEOUT).GetValue() != STRIKEOUT_NONE;
+ aSet.Put( SvxCrossedOutItem( bOld ? STRIKEOUT_NONE : STRIKEOUT_SINGLE, EE_CHAR_STRIKEOUT ) );
+ rBindings.Invalidate( nSlot );
+ }
+ break;
+
+ case SID_ATTR_CHAR_SHADOWED:
+ {
+ bool bOld = pEditView->GetAttribs().Get(EE_CHAR_SHADOW).GetValue();
+ aSet.Put( SvxShadowedItem( !bOld, EE_CHAR_SHADOW ) );
+ rBindings.Invalidate( nSlot );
+ }
+ break;
+
+ case SID_ATTR_CHAR_CONTOUR:
+ {
+ bool bOld = pEditView->GetAttribs().Get(EE_CHAR_OUTLINE).GetValue();
+ aSet.Put( SvxContourItem( !bOld, EE_CHAR_OUTLINE ) );
+ rBindings.Invalidate( nSlot );
+ }
+ break;
+
+ case SID_SET_SUPER_SCRIPT:
+ {
+ SvxEscapement eOld = static_cast<SvxEscapement>(pEditView->GetAttribs().Get(EE_CHAR_ESCAPEMENT).GetEnumValue());
+ SvxEscapement eNew = (eOld == SvxEscapement::Superscript) ?
+ SvxEscapement::Off : SvxEscapement::Superscript;
+ aSet.Put( SvxEscapementItem( eNew, EE_CHAR_ESCAPEMENT ) );
+ rBindings.Invalidate( nSlot );
+ }
+ break;
+ case SID_SET_SUB_SCRIPT:
+ {
+ SvxEscapement eOld = static_cast<SvxEscapement>(pEditView->GetAttribs().Get(EE_CHAR_ESCAPEMENT).GetEnumValue());
+ SvxEscapement eNew = (eOld == SvxEscapement::Subscript) ?
+ SvxEscapement::Off : SvxEscapement::Subscript;
+ aSet.Put( SvxEscapementItem( eNew, EE_CHAR_ESCAPEMENT ) );
+ rBindings.Invalidate( nSlot );
+ }
+ break;
+ case SID_ATTR_CHAR_KERNING:
+ {
+ if(pArgs)
+ {
+ aSet.Put ( pArgs->Get(pArgs->GetPool()->GetWhich(nSlot)));
+ rBindings.Invalidate( nSlot );
+ }
+ }
+ break;
+
+ case SID_GROW_FONT_SIZE:
+ case SID_SHRINK_FONT_SIZE:
+ {
+ SfxObjectShell* pObjSh = SfxObjectShell::Current();
+ const SvxFontListItem* pFontListItem = static_cast<const SvxFontListItem*>
+ (pObjSh ? pObjSh->GetItem(SID_ATTR_CHAR_FONTLIST) : nullptr);
+ const FontList* pFontList = pFontListItem ? pFontListItem->GetFontList() : nullptr;
+ pEditView->ChangeFontSize( nSlot == SID_GROW_FONT_SIZE, pFontList );
+ rBindings.Invalidate( SID_ATTR_CHAR_FONTHEIGHT );
+ }
+ break;
+ }
+
+ // apply
+
+ EditEngine* pEngine = pEditView->GetEditEngine();
+ bool bOld = pEngine->SetUpdateLayout(false);
+
+ pEditView->SetAttribs( aSet );
+
+ pEngine->SetUpdateLayout(bOld);
+ pEditView->Invalidate();
+
+ ScInputHandler* pHdl = GetMyInputHdl();
+ pHdl->SetModified();
+
+ rReq.Done();
+}
+
+void ScEditShell::GetAttrState(SfxItemSet &rSet)
+{
+ if ( !rViewData.HasEditView( rViewData.GetActivePart() ) )
+ {
+ lcl_DisableAll( rSet );
+ return;
+ }
+
+ SfxItemSet aAttribs = pEditView->GetAttribs();
+ rSet.Put( aAttribs );
+
+ // choose font info according to selection script type
+
+ SvtScriptType nScript = pEditView->GetSelectedScriptType();
+ if (nScript == SvtScriptType::NONE) nScript = ScGlobal::GetDefaultScriptType();
+
+ // #i55929# input-language-dependent script type (depends on input language if nothing selected)
+ SvtScriptType nInputScript = nScript;
+ if ( !pEditView->GetSelection().HasRange() )
+ {
+ LanguageType nInputLang = rViewData.GetActiveWin()->GetInputLanguage();
+ if (nInputLang != LANGUAGE_DONTKNOW && nInputLang != LANGUAGE_SYSTEM)
+ nInputScript = SvtLanguageOptions::GetScriptTypeOfLanguage( nInputLang );
+ }
+
+ // #i55929# according to spec, nInputScript is used for font and font height only
+ if ( rSet.GetItemState( EE_CHAR_FONTINFO ) != SfxItemState::UNKNOWN )
+ ScViewUtil::PutItemScript( rSet, aAttribs, EE_CHAR_FONTINFO, nInputScript );
+ if ( rSet.GetItemState( EE_CHAR_FONTHEIGHT ) != SfxItemState::UNKNOWN )
+ ScViewUtil::PutItemScript( rSet, aAttribs, EE_CHAR_FONTHEIGHT, nInputScript );
+ if ( rSet.GetItemState( EE_CHAR_WEIGHT ) != SfxItemState::UNKNOWN )
+ ScViewUtil::PutItemScript( rSet, aAttribs, EE_CHAR_WEIGHT, nScript );
+ if ( rSet.GetItemState( EE_CHAR_ITALIC ) != SfxItemState::UNKNOWN )
+ ScViewUtil::PutItemScript( rSet, aAttribs, EE_CHAR_ITALIC, nScript );
+
+ // underline
+ SfxItemState eState = aAttribs.GetItemState( EE_CHAR_UNDERLINE );
+ if ( eState == SfxItemState::DONTCARE )
+ {
+ rSet.InvalidateItem( SID_ULINE_VAL_NONE );
+ rSet.InvalidateItem( SID_ULINE_VAL_SINGLE );
+ rSet.InvalidateItem( SID_ULINE_VAL_DOUBLE );
+ rSet.InvalidateItem( SID_ULINE_VAL_DOTTED );
+ }
+ else
+ {
+ FontLineStyle eUnderline = aAttribs.Get(EE_CHAR_UNDERLINE).GetLineStyle();
+ rSet.Put(SfxBoolItem(SID_ULINE_VAL_SINGLE, eUnderline == LINESTYLE_SINGLE));
+ rSet.Put(SfxBoolItem(SID_ULINE_VAL_DOUBLE, eUnderline == LINESTYLE_DOUBLE));
+ rSet.Put(SfxBoolItem(SID_ULINE_VAL_DOTTED, eUnderline == LINESTYLE_DOTTED));
+ rSet.Put(SfxBoolItem(SID_ULINE_VAL_NONE, eUnderline == LINESTYLE_NONE));
+ }
+
+ //! Testing whether brace highlighting is active !!!!
+ ScInputHandler* pHdl = GetMyInputHdl();
+ if ( pHdl && pHdl->IsFormulaMode() )
+ rSet.ClearItem( EE_CHAR_WEIGHT ); // Highlighted brace not here
+
+ SvxEscapement eEsc = static_cast<SvxEscapement>(aAttribs.Get( EE_CHAR_ESCAPEMENT ).GetEnumValue());
+ rSet.Put(SfxBoolItem(SID_SET_SUPER_SCRIPT, eEsc == SvxEscapement::Superscript));
+ rSet.Put(SfxBoolItem(SID_SET_SUB_SCRIPT, eEsc == SvxEscapement::Subscript));
+ rViewData.GetBindings().Invalidate( SID_SET_SUPER_SCRIPT );
+ rViewData.GetBindings().Invalidate( SID_SET_SUB_SCRIPT );
+
+ eState = aAttribs.GetItemState( EE_CHAR_KERNING );
+ rViewData.GetBindings().Invalidate( SID_ATTR_CHAR_KERNING );
+ if ( eState == SfxItemState::DONTCARE )
+ {
+ rSet.InvalidateItem(EE_CHAR_KERNING);
+ }
+}
+
+OUString ScEditShell::GetSelectionText( bool bWholeWord )
+{
+ OUString aStrSelection;
+
+ if ( rViewData.HasEditView( rViewData.GetActivePart() ) )
+ {
+ if ( bWholeWord )
+ {
+ EditEngine* pEngine = pEditView->GetEditEngine();
+ ESelection aSel = pEditView->GetSelection();
+ OUString aStrCurrentDelimiters = pEngine->GetWordDelimiters();
+
+ pEngine->SetWordDelimiters(" .,;\"'");
+ aStrSelection = pEngine->GetWord( aSel.nEndPara, aSel.nEndPos );
+ pEngine->SetWordDelimiters( aStrCurrentDelimiters );
+ }
+ else
+ {
+ aStrSelection = pEditView->GetSelected();
+ }
+ }
+
+ return aStrSelection;
+}
+
+void ScEditShell::ExecuteUndo(const SfxRequest& rReq)
+{
+ // Undo must be handled here because it's called for both EditViews
+
+ ScInputHandler* pHdl = GetMyInputHdl();
+ OSL_ENSURE(pHdl,"no ScInputHandler");
+ EditView* pTopView = pHdl->GetTopView();
+ EditView* pTableView = pHdl->GetTableView();
+ OSL_ENSURE(pTableView,"no EditView");
+
+ pHdl->DataChanging();
+
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+ sal_uInt16 nSlot = rReq.GetSlot();
+ switch ( nSlot )
+ {
+ case SID_UNDO:
+ case SID_REDO:
+ {
+ bool bIsUndo = ( nSlot == SID_UNDO );
+
+ sal_uInt16 nCount = 1;
+ const SfxPoolItem* pItem;
+ if ( pReqArgs && pReqArgs->GetItemState( nSlot, true, &pItem ) == SfxItemState::SET )
+ nCount = static_cast<const SfxUInt16Item*>(pItem)->GetValue();
+
+ for (sal_uInt16 i=0; i<nCount; i++)
+ {
+ if ( bIsUndo )
+ {
+ pTableView->Undo();
+ if (pTopView)
+ pTopView->Undo();
+ }
+ else
+ {
+ pTableView->Redo();
+ if (pTopView)
+ pTopView->Redo();
+ }
+ }
+ }
+ break;
+ }
+ rViewData.GetBindings().InvalidateAll(false);
+
+ pHdl->DataChanged();
+}
+
+void ScEditShell::GetUndoState(SfxItemSet &rSet)
+{
+ // Undo state is taken from normal ViewFrame state function
+
+ SfxViewFrame& rViewFrm = rViewData.GetViewShell()->GetViewFrame();
+ if ( GetUndoManager() )
+ {
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while( nWhich )
+ {
+ rViewFrm.GetSlotState( nWhich, nullptr, &rSet );
+ nWhich = aIter.NextWhich();
+ }
+ }
+
+ // disable if no action in input line EditView
+
+ ScInputHandler* pHdl = GetMyInputHdl();
+ OSL_ENSURE(pHdl,"no ScInputHandler");
+ EditView* pTopView = pHdl->GetTopView();
+ if (pTopView)
+ {
+ SfxUndoManager& rTopMgr = pTopView->GetEditEngine()->GetUndoManager();
+ if ( rTopMgr.GetUndoActionCount() == 0 )
+ rSet.DisableItem( SID_UNDO );
+ if ( rTopMgr.GetRedoActionCount() == 0 )
+ rSet.DisableItem( SID_REDO );
+ }
+}
+
+void ScEditShell::ExecuteTrans( const SfxRequest& rReq )
+{
+ TransliterationFlags nType = ScViewUtil::GetTransliterationType( rReq.GetSlot() );
+ if ( nType == TransliterationFlags::NONE )
+ return;
+
+ ScInputHandler* pHdl = GetMyInputHdl();
+ assert(pHdl && "no ScInputHandler");
+
+ EditView* pTopView = pHdl->GetTopView();
+ EditView* pTableView = pHdl->GetTableView();
+ assert(pTableView && "no EditView");
+
+ pHdl->DataChanging();
+
+ pTableView->TransliterateText( nType );
+ if (pTopView)
+ pTopView->TransliterateText( nType );
+
+ pHdl->DataChanged();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/formatsh.cxx b/sc/source/ui/view/formatsh.cxx
new file mode 100644
index 0000000000..93a456e46b
--- /dev/null
+++ b/sc/source/ui/view/formatsh.cxx
@@ -0,0 +1,2108 @@
+/* -*- 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 .
+ */
+
+#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+
+#include <scitems.hxx>
+#include <editeng/borderline.hxx>
+
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/sfxdlg.hxx>
+#include <svl/whiter.hxx>
+
+#include <svl/stritem.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zformat.hxx>
+#include <svl/languageoptions.hxx>
+#include <svl/cjkoptions.hxx>
+#include <svl/ctloptions.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/langitem.hxx>
+#include <svx/numinf.hxx>
+#include <editeng/svxenum.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/lineitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <editeng/scripttypeitem.hxx>
+#include <editeng/shaditem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <comphelper/lok.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+
+#include <formatsh.hxx>
+#include <sc.hrc>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <docsh.hxx>
+#include <patattr.hxx>
+#include <scmod.hxx>
+#include <stlpool.hxx>
+#include <stlsheet.hxx>
+#include <printfun.hxx>
+#include <docpool.hxx>
+#include <tabvwsh.hxx>
+#include <attrib.hxx>
+
+#define ShellClass_ScFormatShell
+#define ShellClass_TableFont
+#define ShellClass_FormatForSelection
+#include <scslots.hxx>
+
+#include <editeng/fontitem.hxx>
+#include <sfx2/classificationhelper.hxx>
+
+#include <memory>
+
+using namespace ::com::sun::star;
+
+namespace {
+
+SvxCellHorJustify lclConvertSlotToHAlign( sal_uInt16 nSlot )
+{
+ SvxCellHorJustify eHJustify = SvxCellHorJustify::Standard;
+ switch( nSlot )
+ {
+ case SID_ALIGN_ANY_HDEFAULT: eHJustify = SvxCellHorJustify::Standard; break;
+ case SID_ALIGN_ANY_LEFT: eHJustify = SvxCellHorJustify::Left; break;
+ case SID_ALIGN_ANY_HCENTER: eHJustify = SvxCellHorJustify::Center; break;
+ case SID_ALIGN_ANY_RIGHT: eHJustify = SvxCellHorJustify::Right; break;
+ case SID_ALIGN_ANY_JUSTIFIED: eHJustify = SvxCellHorJustify::Block; break;
+ default: OSL_FAIL( "lclConvertSlotToHAlign - invalid slot" );
+ }
+ return eHJustify;
+}
+
+SvxCellVerJustify lclConvertSlotToVAlign( sal_uInt16 nSlot )
+{
+ SvxCellVerJustify eVJustify = SvxCellVerJustify::Standard;
+ switch( nSlot )
+ {
+ case SID_ALIGN_ANY_VDEFAULT: eVJustify = SvxCellVerJustify::Standard; break;
+ case SID_ALIGN_ANY_TOP: eVJustify = SvxCellVerJustify::Top; break;
+ case SID_ALIGN_ANY_VCENTER: eVJustify = SvxCellVerJustify::Center; break;
+ case SID_ALIGN_ANY_BOTTOM: eVJustify = SvxCellVerJustify::Bottom; break;
+ default: OSL_FAIL( "lclConvertSlotToVAlign - invalid slot" );
+ }
+ return eVJustify;
+}
+
+} // namespace
+
+
+SFX_IMPL_INTERFACE(ScFormatShell, SfxShell)
+
+void ScFormatShell::InitInterface_Impl()
+{
+ GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT,
+ SfxVisibilityFlags::Standard | SfxVisibilityFlags::Server,
+ ToolbarId::Objectbar_Format);
+}
+
+ScFormatShell::ScFormatShell(ScViewData& rData) :
+ SfxShell(rData.GetViewShell()),
+ rViewData(rData)
+{
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+
+ SetPool( &pTabViewShell->GetPool() );
+ SfxUndoManager* pMgr = rViewData.GetSfxDocShell()->GetUndoManager();
+ SetUndoManager( pMgr );
+ if (pMgr && !rViewData.GetDocument().IsUndoEnabled())
+ {
+ pMgr->SetMaxUndoActionCount( 0 );
+ }
+ SetName("Format");
+}
+
+ScFormatShell::~ScFormatShell()
+{
+}
+
+void ScFormatShell::ExecuteStyle( SfxRequest& rReq )
+{
+ const SfxItemSet* pArgs = rReq.GetArgs();
+ const sal_uInt16 nSlotId = rReq.GetSlot();
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScTabViewShell* pTabViewShell= GetViewData().GetViewShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SfxStyleSheetBasePool* pStylePool = rDoc.GetStyleSheetPool();
+
+ if ( (nSlotId == SID_STYLE_PREVIEW)
+ || (nSlotId == SID_STYLE_END_PREVIEW) )
+ {
+ if (nSlotId == SID_STYLE_PREVIEW)
+ {
+ SfxStyleFamily eFamily = SfxStyleFamily::Para;
+ const SfxUInt16Item* pFamItem;
+ if ( pArgs && (pFamItem = pArgs->GetItemIfSet( SID_STYLE_FAMILY )) )
+ eFamily = static_cast<SfxStyleFamily>(pFamItem->GetValue());
+ const SfxPoolItem* pNameItem;
+ OUString aStyleName;
+ if (pArgs && SfxItemState::SET == pArgs->GetItemState( nSlotId, true, &pNameItem ))
+ aStyleName = static_cast<const SfxStringItem*>(pNameItem)->GetValue();
+ if ( eFamily == SfxStyleFamily::Para ) // CellStyles
+ {
+ ScMarkData aFuncMark( rViewData.GetMarkData() );
+ ScViewUtil::UnmarkFiltered( aFuncMark, rDoc );
+ aFuncMark.MarkToMulti();
+
+ if ( !aFuncMark.IsMarked() && !aFuncMark.IsMultiMarked() )
+ {
+ SCCOL nCol = rViewData.GetCurX();
+ SCROW nRow = rViewData.GetCurY();
+ SCTAB nTab = rViewData.GetTabNo();
+ ScRange aRange( nCol, nRow, nTab );
+ aFuncMark.SetMarkArea( aRange );
+ }
+ rDoc.SetPreviewSelection( aFuncMark );
+ ScStyleSheet* pPreviewStyle = static_cast<ScStyleSheet*>( pStylePool->Find( aStyleName, eFamily ) );
+ rDoc.SetPreviewCellStyle( pPreviewStyle );
+ ScPatternAttr aAttr( *rDoc.GetSelectionPattern( aFuncMark ) );
+ aAttr.SetStyleSheet( pPreviewStyle );
+
+ SfxItemSet aItemSet( GetPool() );
+
+ ScPatternAttr aNewAttrs( GetViewData().GetDocument().GetPool() );
+ SfxItemSet& rNewSet = aNewAttrs.GetItemSet();
+ rNewSet.Put( aItemSet, false );
+
+ rDoc.ApplySelectionPattern( aNewAttrs, rDoc.GetPreviewSelection() );
+ pTabViewShell->UpdateSelectionArea( aFuncMark, &aAttr );
+ }
+ }
+ else
+ {
+ // No mark at all happens when creating a new document, in which
+ // case the selection pattern obtained would be empty (created of
+ // GetPool()) anyway and nothing needs to be applied.
+ ScMarkData aPreviewMark( rDoc.GetPreviewSelection());
+ if (aPreviewMark.IsMarked() || aPreviewMark.IsMultiMarked())
+ {
+ ScPatternAttr aAttr( *rDoc.GetSelectionPattern( aPreviewMark ) );
+ if ( ScStyleSheet* pPreviewStyle = rDoc.GetPreviewCellStyle() )
+ aAttr.SetStyleSheet( pPreviewStyle );
+ rDoc.SetPreviewCellStyle(nullptr);
+
+ SfxItemSet aItemSet( GetPool() );
+
+ ScPatternAttr aNewAttrs( GetViewData().GetDocument().GetPool() );
+ SfxItemSet& rNewSet = aNewAttrs.GetItemSet();
+ rNewSet.Put( aItemSet, false );
+ rDoc.ApplySelectionPattern( aNewAttrs, aPreviewMark );
+ pTabViewShell->UpdateSelectionArea( aPreviewMark, &aAttr );
+ }
+ }
+ }
+ else if (nSlotId == SID_CLASSIFICATION_APPLY)
+ {
+ const SfxPoolItem* pItem = nullptr;
+ if (pArgs && pArgs->GetItemState(nSlotId, false, &pItem) == SfxItemState::SET)
+ {
+ const OUString& rName = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ SfxClassificationHelper aHelper(pDocSh->getDocProperties());
+ auto eType = SfxClassificationPolicyType::IntellectualProperty;
+ if (const SfxStringItem* pNameItem = pArgs->GetItemIfSet(SID_TYPE_NAME, false))
+ {
+ const OUString& rType = pNameItem->GetValue();
+ eType = SfxClassificationHelper::stringToPolicyType(rType);
+ }
+ aHelper.SetBACName(rName, eType);
+ }
+ else
+ SAL_WARN("sc.ui", "missing parameter for SID_CLASSIFICATION_APPLY");
+ }
+ else
+ {
+ OSL_FAIL( "Unknown slot (ScViewShell::ExecuteStyle)" );
+ }
+}
+
+void ScFormatShell::ExecuteNumFormat( SfxRequest& rReq )
+{
+ ScModule* pScMod = SC_MOD();
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+ sal_uInt16 nSlot = rReq.GetSlot();
+ SfxBindings& rBindings = pTabViewShell->GetViewFrame().GetBindings();
+
+ pTabViewShell->HideListBox(); // Autofilter-DropDown-Listbox
+
+ // End input
+ if ( GetViewData().HasEditView( GetViewData().GetActivePart() ) )
+ {
+ switch ( nSlot )
+ {
+ case SID_NUMBER_TYPE_FORMAT:
+ case SID_NUMBER_TWODEC:
+ case SID_NUMBER_SCIENTIFIC:
+ case SID_NUMBER_DATE:
+ case SID_NUMBER_CURRENCY:
+ case SID_NUMBER_PERCENT:
+ case SID_NUMBER_STANDARD:
+ case SID_NUMBER_FORMAT:
+ case SID_NUMBER_INCDEC:
+ case SID_NUMBER_DECDEC:
+ case SID_NUMBER_THOUSANDS:
+ case FID_DEFINE_NAME:
+ case FID_ADD_NAME:
+ case FID_USE_NAME:
+ case FID_INSERT_NAME:
+ case SID_SPELL_DIALOG:
+ case SID_HANGUL_HANJA_CONVERSION:
+
+ pScMod->InputEnterHandler();
+ pTabViewShell->UpdateInputHandler();
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ SvNumFormatType nType = GetCurrentNumberFormatType();
+ switch ( nSlot )
+ {
+ case SID_NUMBER_TWODEC:
+ {
+ const SfxItemSet& rAttrSet = pTabViewShell->GetSelectionPattern()->GetItemSet();
+ sal_uInt32 nNumberFormat = rAttrSet.Get(ATTR_VALUE_FORMAT).GetValue();
+
+ if ((nType & SvNumFormatType::NUMBER) && nNumberFormat == 4)
+ pTabViewShell->SetNumberFormat( SvNumFormatType::NUMBER );
+ else
+ pTabViewShell->SetNumberFormat( SvNumFormatType::NUMBER, 4 );
+ rBindings.Invalidate( nSlot );
+ rReq.Done();
+ }
+ break;
+ case SID_NUMBER_SCIENTIFIC:
+ if (nType & SvNumFormatType::SCIENTIFIC)
+ pTabViewShell->SetNumberFormat( SvNumFormatType::NUMBER );
+ else
+ pTabViewShell->SetNumberFormat( SvNumFormatType::SCIENTIFIC );
+ rBindings.Invalidate( nSlot );
+ rReq.Done();
+ break;
+ case SID_NUMBER_DATE:
+ if (nType & SvNumFormatType::DATE)
+ pTabViewShell->SetNumberFormat( SvNumFormatType::NUMBER );
+ else
+ pTabViewShell->SetNumberFormat( SvNumFormatType::DATE );
+ rBindings.Invalidate( nSlot );
+ rReq.Done();
+ break;
+ case SID_NUMBER_TIME:
+ if (nType & SvNumFormatType::TIME)
+ pTabViewShell->SetNumberFormat( SvNumFormatType::NUMBER );
+ else
+ pTabViewShell->SetNumberFormat( SvNumFormatType::TIME );
+ rBindings.Invalidate( nSlot );
+ rReq.Done();
+ break;
+ case SID_NUMBER_CURRENCY:
+ if(pReqArgs)
+ {
+ const SfxPoolItem* pItem;
+ if ( pReqArgs->HasItem( SID_NUMBER_CURRENCY, &pItem ) )
+ {
+ sal_uInt32 nNewFormat = static_cast<const SfxUInt32Item*>(pItem)->GetValue();
+ ScDocument& rDoc = rViewData.GetDocument();
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ const SfxItemSet& rOldSet = pTabViewShell->GetSelectionPattern()->GetItemSet();
+
+ LanguageType eOldLang = rOldSet.Get( ATTR_LANGUAGE_FORMAT ).GetLanguage();
+ sal_uInt32 nOldFormat = rOldSet.Get( ATTR_VALUE_FORMAT ).GetValue();
+
+ if ( nOldFormat != nNewFormat )
+ {
+ const SvNumberformat* pNewEntry = pFormatter->GetEntry( nNewFormat );
+ ScPatternAttr aNewAttrs( rDoc.GetPool() );
+ SfxItemSet& rSet = aNewAttrs.GetItemSet();
+ LanguageType eNewLang = pNewEntry ? pNewEntry->GetLanguage() : LANGUAGE_DONTKNOW;
+ if ( eNewLang != eOldLang && eNewLang != LANGUAGE_DONTKNOW )
+ rSet.Put( SvxLanguageItem( eNewLang, ATTR_LANGUAGE_FORMAT ) );
+ rSet.Put( SfxUInt32Item( ATTR_VALUE_FORMAT, nNewFormat ) );
+ pTabViewShell->ApplySelectionPattern( aNewAttrs );
+ }
+ else
+ pTabViewShell->SetNumberFormat( SvNumFormatType::NUMBER );
+ }
+ }
+ else
+ {
+ if ( nType & SvNumFormatType::CURRENCY )
+ pTabViewShell->SetNumberFormat( SvNumFormatType::NUMBER );
+ else
+ pTabViewShell->SetNumberFormat( SvNumFormatType::CURRENCY );
+ }
+ rBindings.Invalidate( nSlot );
+ rReq.Done();
+ break;
+ case SID_NUMBER_PERCENT:
+ if (nType & SvNumFormatType::PERCENT)
+ pTabViewShell->SetNumberFormat( SvNumFormatType::NUMBER );
+ else
+ pTabViewShell->SetNumberFormat( SvNumFormatType::PERCENT );
+ rBindings.Invalidate( nSlot );
+ rReq.Done();
+ break;
+ case SID_NUMBER_STANDARD:
+ pTabViewShell->SetNumberFormat( SvNumFormatType::NUMBER );
+ rReq.Done();
+ break;
+ case SID_NUMBER_INCDEC:
+ pTabViewShell->ChangeNumFmtDecimals( true );
+ rReq.Done();
+ break;
+ case SID_NUMBER_DECDEC:
+ pTabViewShell->ChangeNumFmtDecimals( false );
+ rReq.Done();
+ break;
+ case SID_NUMBER_THOUSANDS:
+ {
+ ScDocument& rDoc = rViewData.GetDocument();
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ bool bThousand(false);
+ bool bNegRed(false);
+ sal_uInt16 nPrecision(0);
+ sal_uInt16 nLeadZeroes(0);
+ LanguageType eLanguage = ScGlobal::eLnge;
+
+ sal_uInt32 nCurrentNumberFormat = rDoc.GetNumberFormat(
+ rViewData.GetCurX(), rViewData.GetCurY(), rViewData.GetTabNo());
+ const SvNumberformat* pEntry = pFormatter->GetEntry(nCurrentNumberFormat);
+
+ if (pEntry)
+ eLanguage = pEntry->GetLanguage();
+
+ pFormatter->GetFormatSpecialInfo(nCurrentNumberFormat, bThousand, bNegRed, nPrecision, nLeadZeroes);
+ bThousand = !bThousand;
+ OUString aCode = pFormatter->GenerateFormat(
+ nCurrentNumberFormat,
+ eLanguage,
+ bThousand,
+ bNegRed,
+ nPrecision,
+ nLeadZeroes);
+ pTabViewShell->SetNumFmtByStr(aCode);
+
+ rBindings.Invalidate(nSlot);
+ rReq.Done();
+ }
+ break;
+ case SID_NUMBER_FORMAT:
+ // symphony version with format interpretation
+ if(pReqArgs)
+ {
+ const SfxPoolItem* pItem;
+ ScDocument& rDoc = rViewData.GetDocument();
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+
+ sal_uInt32 nCurrentNumberFormat = rDoc.GetNumberFormat(
+ rViewData.GetCurX(), rViewData.GetCurY(), rViewData.GetTabNo());
+ const SvNumberformat* pEntry = pFormatter->GetEntry(nCurrentNumberFormat);
+
+ if(!pEntry)
+ break;
+
+ LanguageType eLanguage = pEntry->GetLanguage();
+ SvNumFormatType eType = pEntry->GetMaskedType();
+
+ //Just use eType to judge whether the command is fired for NUMBER/PERCENT/CURRENCY/SCIENTIFIC/FRACTION/TIME
+ //In sidebar, users can fire SID_NUMBER_FORMAT command by operating the related UI controls before they are disable
+ if(!(eType == SvNumFormatType::ALL
+ || eType == SvNumFormatType::NUMBER
+ || eType == SvNumFormatType::PERCENT
+ || eType == SvNumFormatType::CURRENCY
+ || eType == SvNumFormatType::SCIENTIFIC
+ || eType == SvNumFormatType::TIME
+ || eType == SvNumFormatType::FRACTION))
+ pEntry = nullptr;
+
+ if(SfxItemState::SET == pReqArgs->GetItemState(nSlot, true, &pItem) && pEntry)
+ {
+ OUString aCode = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ sal_uInt16 aLen = aCode.getLength();
+ std::unique_ptr<OUString[]> sFormat( new OUString[4] );
+ OUStringBuffer sTmpStr;
+ sal_uInt16 nCount(0);
+ sal_uInt16 nStrCount(0);
+
+ while(nCount < aLen)
+ {
+ sal_Unicode cChar = aCode[nCount];
+
+ if(cChar == ',')
+ {
+ sFormat[nStrCount] = sTmpStr.makeStringAndClear();
+ nStrCount++;
+ }
+ else
+ {
+ sTmpStr.append(cChar);
+ }
+
+ nCount++;
+
+ if(nStrCount > 3)
+ break;
+ }
+
+ const bool bThousand = static_cast<bool>(sFormat[0].toInt32());
+ const bool bNegRed = static_cast<bool>(sFormat[1].toInt32());
+ const sal_uInt16 nPrecision = static_cast<sal_uInt16>(sFormat[2].toInt32());
+ const sal_uInt16 nLeadZeroes = static_cast<sal_uInt16>(sFormat[3].toInt32());
+
+ aCode = pFormatter->GenerateFormat(
+ nCurrentNumberFormat,//modify
+ eLanguage,
+ bThousand,
+ bNegRed,
+ nPrecision,
+ nLeadZeroes);
+ pTabViewShell->SetNumFmtByStr(aCode);
+ }
+ }
+ break;
+
+ case SID_ATTR_NUMBERFORMAT_VALUE:
+ if ( pReqArgs )
+ {
+ if ( const SfxUInt32Item* pItem = pReqArgs->GetItemIfSet( ATTR_VALUE_FORMAT ) )
+ {
+ // We have to accomplish this using ApplyAttributes()
+ // because we also need the language information to be
+ // considered.
+ const SfxItemSet& rOldSet =
+ pTabViewShell->GetSelectionPattern()->GetItemSet();
+ SfxItemPool* pDocPool = GetViewData().GetDocument().GetPool();
+ SfxItemSetFixed<ATTR_PATTERN_START, ATTR_PATTERN_END> aNewSet( *pDocPool );
+ aNewSet.Put( *pItem );
+ pTabViewShell->ApplyAttributes( aNewSet, rOldSet );
+ }
+ }
+ break;
+
+ case SID_NUMBER_TYPE_FORMAT:
+ if ( pReqArgs )
+ {
+ const SfxPoolItem* pItem;
+ if ( pReqArgs->GetItemState( nSlot, true, &pItem ) == SfxItemState::SET )
+ {
+ sal_uInt16 nFormat = static_cast<const SfxUInt16Item *>(pItem)->GetValue();
+ switch(nFormat)
+ {
+ case 0:
+ pTabViewShell->SetNumberFormat( SvNumFormatType::NUMBER); //Modify
+ break;
+ case 1:
+ pTabViewShell->SetNumberFormat( SvNumFormatType::NUMBER, 2 ); //Modify
+ break;
+ case 2:
+ pTabViewShell->SetNumberFormat( SvNumFormatType::PERCENT );
+ break;
+ case 3:
+ pTabViewShell->SetNumberFormat( SvNumFormatType::CURRENCY );
+ break;
+ case 4:
+ pTabViewShell->SetNumberFormat( SvNumFormatType::DATE );
+ break;
+ case 5:
+ pTabViewShell->SetNumberFormat( SvNumFormatType::TIME );
+ break;
+ case 6:
+ pTabViewShell->SetNumberFormat( SvNumFormatType::SCIENTIFIC );
+ break;
+ case 7:
+ pTabViewShell->SetNumberFormat( SvNumFormatType::FRACTION );
+ break;
+ case 8:
+ pTabViewShell->SetNumberFormat( SvNumFormatType::LOGICAL );
+ break;
+ case 9:
+ pTabViewShell->SetNumberFormat( SvNumFormatType::TEXT );
+ break;
+ default:
+ ;
+ }
+ rReq.Done();
+ }
+ }
+ break;
+
+ default:
+ OSL_FAIL("ExecuteEdit: invalid slot");
+ break;
+ }
+}
+
+void ScFormatShell::ExecuteAlignment( SfxRequest& rReq )
+{
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ SfxBindings& rBindings = rViewData.GetBindings();
+ const SfxItemSet* pSet = rReq.GetArgs();
+ sal_uInt16 nSlot = rReq.GetSlot();
+
+ pTabViewShell->HideListBox(); // Autofilter-DropDown-Listbox
+
+ switch( nSlot )
+ {
+ // pseudo slots for Format menu
+ case SID_ALIGN_ANY_HDEFAULT:
+ case SID_ALIGN_ANY_LEFT:
+ case SID_ALIGN_ANY_HCENTER:
+ case SID_ALIGN_ANY_RIGHT:
+ case SID_ALIGN_ANY_JUSTIFIED:
+ pTabViewShell->ApplyAttr( SvxHorJustifyItem( lclConvertSlotToHAlign( nSlot ), ATTR_HOR_JUSTIFY ) );
+ break;
+ case SID_ALIGN_ANY_VDEFAULT:
+ case SID_ALIGN_ANY_TOP:
+ case SID_ALIGN_ANY_VCENTER:
+ case SID_ALIGN_ANY_BOTTOM:
+ pTabViewShell->ApplyAttr( SvxVerJustifyItem( lclConvertSlotToVAlign( nSlot ), ATTR_VER_JUSTIFY ) );
+ break;
+
+ default:
+ if( pSet )
+ {
+ const SfxPoolItem* pItem = nullptr;
+ if( pSet->GetItemState(GetPool().GetWhich(nSlot), true, &pItem ) == SfxItemState::SET )
+ {
+
+ switch ( nSlot )
+ {
+ case SID_ATTR_ALIGN_HOR_JUSTIFY:
+ case SID_ATTR_ALIGN_VER_JUSTIFY:
+ case SID_ATTR_ALIGN_INDENT:
+ case SID_ATTR_ALIGN_HYPHENATION:
+ case SID_ATTR_ALIGN_DEGREES:
+ case SID_ATTR_ALIGN_LOCKPOS:
+ case SID_ATTR_ALIGN_MARGIN:
+ case SID_ATTR_ALIGN_STACKED:
+ pTabViewShell->ApplyAttr( *pItem );
+ break;
+
+ case SID_H_ALIGNCELL:
+ {
+ SvxCellHorJustify eJust = static_cast<const SvxHorJustifyItem*>(pItem)->GetValue();
+ // #i78476# update alignment of text in cell edit mode
+ pTabViewShell->UpdateInputHandlerCellAdjust( eJust );
+ pTabViewShell->ApplyAttr( SvxHorJustifyItem( eJust, ATTR_HOR_JUSTIFY ) );
+ }
+ break;
+ case SID_V_ALIGNCELL:
+ pTabViewShell->ApplyAttr( SvxVerJustifyItem( static_cast<const SvxVerJustifyItem*>(pItem)->GetValue(), ATTR_VER_JUSTIFY ) );
+ break;
+ default:
+ OSL_FAIL( "ExecuteAlignment: invalid slot" );
+ return;
+ }
+ }
+ }
+ }
+ rBindings.Invalidate( SID_ATTR_PARA_ADJUST_LEFT );
+ rBindings.Invalidate( SID_ATTR_PARA_ADJUST_RIGHT );
+ rBindings.Invalidate( SID_ATTR_PARA_ADJUST_BLOCK );
+ rBindings.Invalidate( SID_ATTR_PARA_ADJUST_CENTER);
+ rBindings.Invalidate( SID_ALIGNLEFT );
+ rBindings.Invalidate( SID_ALIGNRIGHT );
+ rBindings.Invalidate( SID_ALIGNCENTERHOR );
+ rBindings.Invalidate( SID_ALIGNBLOCK );
+ rBindings.Invalidate( SID_ALIGNTOP );
+ rBindings.Invalidate( SID_ALIGNBOTTOM );
+ rBindings.Invalidate( SID_ALIGNCENTERVER );
+ rBindings.Invalidate( SID_V_ALIGNCELL );
+ rBindings.Invalidate( SID_H_ALIGNCELL );
+ // pseudo slots for Format menu
+ rBindings.Invalidate( SID_ALIGN_ANY_HDEFAULT );
+ rBindings.Invalidate( SID_ALIGN_ANY_LEFT );
+ rBindings.Invalidate( SID_ALIGN_ANY_HCENTER );
+ rBindings.Invalidate( SID_ALIGN_ANY_RIGHT );
+ rBindings.Invalidate( SID_ALIGN_ANY_JUSTIFIED );
+ rBindings.Invalidate( SID_ALIGN_ANY_VDEFAULT );
+ rBindings.Invalidate( SID_ALIGN_ANY_TOP );
+ rBindings.Invalidate( SID_ALIGN_ANY_VCENTER );
+ rBindings.Invalidate( SID_ALIGN_ANY_BOTTOM );
+ rBindings.Update();
+
+ if( ! rReq.IsAPI() )
+ rReq.Done();
+}
+
+void ScFormatShell::ExecuteTextAttr( SfxRequest& rReq )
+{
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ SfxBindings& rBindings = rViewData.GetBindings();
+ const ScPatternAttr* pAttrs = pTabViewShell->GetSelectionPattern();
+ const SfxItemSet* pSet = rReq.GetArgs();
+ sal_uInt16 nSlot = rReq.GetSlot();
+ std::optional<SfxAllItemSet> pNewSet;
+
+ pTabViewShell->HideListBox(); // Autofilter-DropDown-Listbox
+
+ if ( (nSlot == SID_ATTR_CHAR_WEIGHT)
+ ||(nSlot == SID_ATTR_CHAR_POSTURE)
+ ||(nSlot == SID_ATTR_CHAR_UNDERLINE)
+ ||(nSlot == SID_ULINE_VAL_NONE)
+ ||(nSlot == SID_ULINE_VAL_SINGLE)
+ ||(nSlot == SID_ULINE_VAL_DOUBLE)
+ ||(nSlot == SID_ULINE_VAL_DOTTED) )
+ {
+ pNewSet.emplace( GetPool() );
+
+ switch ( nSlot )
+ {
+ case SID_ATTR_CHAR_WEIGHT:
+ {
+ // #i78017 establish the same behaviour as in Writer
+ SvtScriptType nScript = SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX;
+
+ SfxItemPool& rPool = GetPool();
+ SvxScriptSetItem aSetItem( nSlot, rPool );
+ if ( pSet )
+ aSetItem.PutItemForScriptType( nScript, pSet->Get( ATTR_FONT_WEIGHT ) );
+ else
+ {
+ // toggle manually
+
+ FontWeight eWeight = WEIGHT_BOLD;
+ SvxScriptSetItem aOldSetItem( nSlot, rPool );
+ aOldSetItem.GetItemSet().Put( pAttrs->GetItemSet(), false );
+ const SfxPoolItem* pCore = aOldSetItem.GetItemOfScript( nScript );
+ if ( pCore && static_cast<const SvxWeightItem*>(pCore)->GetWeight() == WEIGHT_BOLD )
+ eWeight = WEIGHT_NORMAL;
+
+ aSetItem.PutItemForScriptType( nScript, SvxWeightItem( eWeight, ATTR_FONT_WEIGHT ) );
+ }
+ pTabViewShell->ApplyUserItemSet( aSetItem.GetItemSet() );
+ pNewSet->Put( aSetItem.GetItemSet(), false );
+ }
+ break;
+
+ case SID_ATTR_CHAR_POSTURE:
+ {
+ // #i78017 establish the same behaviour as in Writer
+ SvtScriptType nScript = SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX;
+
+ SfxItemPool& rPool = GetPool();
+ SvxScriptSetItem aSetItem( nSlot, rPool );
+ if ( pSet )
+ aSetItem.PutItemForScriptType( nScript, pSet->Get( ATTR_FONT_POSTURE ) );
+ else
+ {
+ // toggle manually
+
+ FontItalic eItalic = ITALIC_NORMAL;
+ SvxScriptSetItem aOldSetItem( nSlot, rPool );
+ aOldSetItem.GetItemSet().Put( pAttrs->GetItemSet(), false );
+ const SfxPoolItem* pCore = aOldSetItem.GetItemOfScript( nScript );
+ if ( pCore && static_cast<const SvxPostureItem*>(pCore)->GetPosture() == ITALIC_NORMAL )
+ eItalic = ITALIC_NONE;
+
+ aSetItem.PutItemForScriptType( nScript, SvxPostureItem( eItalic, ATTR_FONT_POSTURE ) );
+ }
+ pTabViewShell->ApplyUserItemSet( aSetItem.GetItemSet() );
+ pNewSet->Put( aSetItem.GetItemSet(), false );
+ }
+ break;
+
+ case SID_ATTR_CHAR_UNDERLINE:
+ {
+ if( pSet )
+ {
+ const SfxPoolItem& rUnderline = pSet->Get( ATTR_FONT_UNDERLINE );
+
+ if( dynamic_cast<const SvxUnderlineItem*>( &rUnderline) != nullptr )
+ {
+ pTabViewShell->ApplyAttr( rUnderline );
+ pNewSet->Put( rUnderline,rUnderline.Which() );
+ }
+ else if ( auto pTextLineItem = dynamic_cast<const SvxTextLineItem*>( &rUnderline) )
+ {
+ // #i106580# also allow SvxTextLineItem (base class of SvxUnderlineItem)
+ SvxUnderlineItem aNewItem( pTextLineItem->GetLineStyle(), pTextLineItem->Which() );
+ aNewItem.SetColor( pTextLineItem->GetColor() );
+ pTabViewShell->ApplyAttr( aNewItem );
+ pNewSet->Put( aNewItem, aNewItem.Which() );
+ }
+ }
+ else
+ {
+ SvxUnderlineItem aUnderline( pAttrs->GetItem( ATTR_FONT_UNDERLINE ) );
+ FontLineStyle eUnderline = (LINESTYLE_NONE != aUnderline.GetLineStyle())
+ ? LINESTYLE_NONE
+ : LINESTYLE_SINGLE;
+ aUnderline.SetLineStyle( eUnderline );
+ pTabViewShell->ApplyAttr( aUnderline );
+ pNewSet->Put( aUnderline,aUnderline.Which() );
+ }
+ }
+ break;
+
+ case SID_ULINE_VAL_NONE:
+ pTabViewShell->ApplyAttr( SvxUnderlineItem( LINESTYLE_NONE, ATTR_FONT_UNDERLINE ) );
+ break;
+ case SID_ULINE_VAL_SINGLE: // Toggles
+ case SID_ULINE_VAL_DOUBLE:
+ case SID_ULINE_VAL_DOTTED:
+ {
+ FontLineStyle eOld = pAttrs->GetItem(ATTR_FONT_UNDERLINE).GetLineStyle();
+ FontLineStyle eNew = eOld;
+ switch (nSlot)
+ {
+ case SID_ULINE_VAL_SINGLE:
+ eNew = ( eOld == LINESTYLE_SINGLE ) ? LINESTYLE_NONE : LINESTYLE_SINGLE;
+ break;
+ case SID_ULINE_VAL_DOUBLE:
+ eNew = ( eOld == LINESTYLE_DOUBLE ) ? LINESTYLE_NONE : LINESTYLE_DOUBLE;
+ break;
+ case SID_ULINE_VAL_DOTTED:
+ eNew = ( eOld == LINESTYLE_DOTTED ) ? LINESTYLE_NONE : LINESTYLE_DOTTED;
+ break;
+ }
+ pTabViewShell->ApplyAttr( SvxUnderlineItem( eNew, ATTR_FONT_UNDERLINE ) );
+ }
+ break;
+
+ default:
+ break;
+ }
+ rBindings.Invalidate( nSlot );
+ }
+ else
+ {
+ /*
+ * "Self-made" functionality of radio buttons
+ * At the toggle the default state is used, this means
+ * no button was clicked.
+ */
+
+ const SfxItemSet& rAttrSet = pTabViewShell->GetSelectionPattern()->GetItemSet();
+ const SvxHorJustifyItem* pHorJustify = rAttrSet.GetItemIfSet(ATTR_HOR_JUSTIFY);
+ const SvxVerJustifyItem* pVerJustify = rAttrSet.GetItemIfSet(ATTR_VER_JUSTIFY );
+ SvxCellHorJustify eHorJustify = SvxCellHorJustify::Standard;
+ SvxCellVerJustify eVerJustify = SvxCellVerJustify::Standard;
+
+ if (pHorJustify)
+ {
+ eHorJustify = pHorJustify->GetValue();
+ }
+ if (pVerJustify)
+ {
+ eVerJustify = pVerJustify->GetValue();
+ }
+
+ switch ( nSlot )
+ {
+ case SID_ALIGNLEFT:
+ rReq.SetSlot( SID_H_ALIGNCELL );
+ rReq.AppendItem( SvxHorJustifyItem(
+ !pHorJustify || (eHorJustify != SvxCellHorJustify::Left) ?
+ SvxCellHorJustify::Left : SvxCellHorJustify::Standard, SID_H_ALIGNCELL ) );
+ ExecuteSlot( rReq, GetInterface() );
+ return;
+
+ case SID_ALIGNRIGHT:
+ rReq.SetSlot( SID_H_ALIGNCELL );
+ rReq.AppendItem( SvxHorJustifyItem(
+ !pHorJustify || (eHorJustify != SvxCellHorJustify::Right) ?
+ SvxCellHorJustify::Right : SvxCellHorJustify::Standard, SID_H_ALIGNCELL ) );
+ ExecuteSlot( rReq, GetInterface() );
+ return;
+
+ case SID_ALIGNCENTERHOR:
+ rReq.SetSlot( SID_H_ALIGNCELL );
+ rReq.AppendItem( SvxHorJustifyItem(
+ !pHorJustify || (eHorJustify != SvxCellHorJustify::Center) ?
+ SvxCellHorJustify::Center : SvxCellHorJustify::Standard, SID_H_ALIGNCELL ) );
+ ExecuteSlot( rReq, GetInterface() );
+ return;
+
+ case SID_ALIGNBLOCK:
+ rReq.SetSlot( SID_H_ALIGNCELL );
+ rReq.AppendItem( SvxHorJustifyItem(
+ !pHorJustify || (eHorJustify != SvxCellHorJustify::Block) ?
+ SvxCellHorJustify::Block : SvxCellHorJustify::Standard, SID_H_ALIGNCELL ) );
+ ExecuteSlot( rReq, GetInterface() );
+ return;
+
+ case SID_ALIGNTOP:
+ rReq.SetSlot( SID_V_ALIGNCELL );
+ rReq.AppendItem( SvxVerJustifyItem(
+ !pVerJustify || (eVerJustify != SvxCellVerJustify::Top) ?
+ SvxCellVerJustify::Top : SvxCellVerJustify::Standard, SID_V_ALIGNCELL ) );
+ ExecuteSlot( rReq, GetInterface() );
+ return;
+
+ case SID_ALIGNBOTTOM:
+ rReq.SetSlot( SID_V_ALIGNCELL );
+ rReq.AppendItem( SvxVerJustifyItem(
+ !pVerJustify || (eVerJustify != SvxCellVerJustify::Bottom) ?
+ SvxCellVerJustify::Bottom : SvxCellVerJustify::Standard, SID_V_ALIGNCELL ) );
+ ExecuteSlot( rReq, GetInterface() );
+ return;
+
+ case SID_ALIGNCENTERVER:
+ rReq.SetSlot( SID_V_ALIGNCELL );
+ rReq.AppendItem( SvxVerJustifyItem(
+ !pVerJustify || (eVerJustify != SvxCellVerJustify::Center) ?
+ SvxCellVerJustify::Center : SvxCellVerJustify::Standard, SID_V_ALIGNCELL ) );
+ ExecuteSlot( rReq, GetInterface() );
+ return;
+
+ default:
+ break;
+ }
+
+ }
+
+ rBindings.Update();
+
+ if( pNewSet )
+ {
+ rReq.Done( *pNewSet );
+ pNewSet.reset();
+ }
+ else
+ {
+ rReq.Done();
+ }
+
+}
+
+void ScFormatShell::ExecuteAttr( SfxRequest& rReq )
+{
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ SfxBindings& rBindings = rViewData.GetBindings();
+ const SfxItemSet* pNewAttrs = rReq.GetArgs();
+ sal_uInt16 nSlot = rReq.GetSlot();
+
+ pTabViewShell->HideListBox(); // Autofilter-DropDown-Listbox
+ ScDocument& rDoc = GetViewData().GetDocument();
+ if ( !pNewAttrs )
+ {
+ switch ( nSlot )
+ {
+ case SID_GROW_FONT_SIZE:
+ case SID_SHRINK_FONT_SIZE:
+ {
+ SfxItemPool& rPool = GetPool();
+ SvxScriptSetItem aSetItem( SID_ATTR_CHAR_FONTHEIGHT, rPool );
+ aSetItem.GetItemSet().Put( pTabViewShell->GetSelectionPattern()->GetItemSet(), false );
+
+ SvtScriptType nScriptTypes = pTabViewShell->GetSelectionScriptType();
+ const SvxFontHeightItem* pSize( static_cast<const SvxFontHeightItem*>( aSetItem.GetItemOfScript( nScriptTypes ) ) );
+
+ if ( pSize )
+ {
+ SvxFontHeightItem aSize( *pSize );
+ sal_uInt32 nSize = aSize.GetHeight();
+
+ const sal_uInt32 nFontInc = 40; // 2pt
+ const sal_uInt32 nFontMaxSz = 19998; // 999.9pt
+ if ( nSlot == SID_GROW_FONT_SIZE )
+ nSize = std::min< sal_uInt32 >( nSize + nFontInc, nFontMaxSz );
+ else
+ nSize = std::max< sal_Int32 >( nSize - nFontInc, nFontInc );
+
+ aSize.SetHeight( nSize );
+ aSetItem.PutItemForScriptType( nScriptTypes, aSize );
+ pTabViewShell->ApplyUserItemSet( aSetItem.GetItemSet() );
+ }
+ rBindings.Invalidate( SID_ATTR_CHAR_FONTHEIGHT );
+ }
+ break;
+
+ case SID_ATTR_CHAR_ENDPREVIEW_FONT:
+ {
+ rDoc.SetPreviewFont(nullptr);
+ pTabViewShell->UpdateSelectionArea( rDoc.GetPreviewSelection() );
+ break;
+ }
+ case SID_ATTR_CHAR_COLOR:
+ case SID_ATTR_CHAR_FONT:
+ case SID_ATTR_CHAR_FONTHEIGHT:
+ pTabViewShell->ExecuteCellFormatDlg(rReq, "font"); // when ToolBar is vertical
+ break;
+
+ case SID_BACKGROUND_COLOR:
+ {
+ SvxBrushItem aBrushItem(
+ pTabViewShell->GetSelectionPattern()->GetItem( ATTR_BACKGROUND ) );
+ aBrushItem.SetColor( COL_TRANSPARENT );
+ pTabViewShell->ApplyAttr( aBrushItem, false );
+ }
+ break;
+
+ case SID_ATTR_ALIGN_LINEBREAK: // without parameter as toggle
+ {
+ const ScPatternAttr* pAttrs = pTabViewShell->GetSelectionPattern();
+ bool bOld = pAttrs->GetItem(ATTR_LINEBREAK).GetValue();
+ ScLineBreakCell aBreakItem(!bOld);
+ pTabViewShell->ApplyAttr( aBreakItem );
+
+ SfxAllItemSet aNewSet( GetPool() );
+ aNewSet.Put( aBreakItem,aBreakItem.Which() );
+ rReq.Done( aNewSet );
+
+ rBindings.Invalidate( nSlot );
+ }
+ break;
+
+ case SID_SCATTR_CELLPROTECTION: // without parameter as toggle
+ {
+ const ScPatternAttr* pAttrs = pTabViewShell->GetSelectionPattern();
+ bool bProtect = pAttrs->GetItem(ATTR_PROTECTION).GetProtection();
+ bool bHideFormula = pAttrs->GetItem(ATTR_PROTECTION).GetHideFormula();
+ bool bHideCell = pAttrs->GetItem(ATTR_PROTECTION).GetHideCell();
+ bool bHidePrint = pAttrs->GetItem(ATTR_PROTECTION).GetHidePrint();
+
+ ScProtectionAttr aProtectionItem( !bProtect, bHideFormula, bHideCell, bHidePrint );
+ pTabViewShell->ApplyAttr( aProtectionItem );
+
+ SfxAllItemSet aNewSet( GetPool() );
+ aNewSet.Put( aProtectionItem, aProtectionItem.Which());
+ aNewSet.Put( SfxBoolItem( SID_SCATTR_CELLPROTECTION, !bProtect ) );
+ rReq.Done( aNewSet );
+
+ rBindings.Invalidate( nSlot );
+ }
+ break;
+ }
+ }
+ else
+ {
+ switch ( nSlot )
+ {
+ case SID_ATTR_CHAR_PREVIEW_FONT:
+ {
+ SfxItemPool& rPool = GetPool();
+ sal_uInt16 nWhich = rPool.GetWhich( nSlot );
+ const SvxFontItem& rFont = static_cast<const SvxFontItem&>(pNewAttrs->Get( nWhich ));
+ SvxScriptSetItem aSetItem( SID_ATTR_CHAR_FONT, rPool );
+ SvtScriptType nScript = pTabViewShell->GetSelectionScriptType();
+ aSetItem.PutItemForScriptType( nScript, rFont );
+
+ ScMarkData aFuncMark( rViewData.GetMarkData() );
+ ScViewUtil::UnmarkFiltered( aFuncMark, rDoc );
+ rDoc.SetPreviewFont( aSetItem.GetItemSet().Clone() );
+ aFuncMark.MarkToMulti();
+
+ if ( !aFuncMark.IsMarked() && !aFuncMark.IsMultiMarked() )
+ {
+ SCCOL nCol = rViewData.GetCurX();
+ SCROW nRow = rViewData.GetCurY();
+ SCTAB nTab = rViewData.GetTabNo();
+ ScRange aRange( nCol, nRow, nTab );
+ aFuncMark.SetMarkArea( aRange );
+ }
+ rDoc.SetPreviewSelection( aFuncMark );
+ pTabViewShell->UpdateSelectionArea( aFuncMark );
+ break;
+ }
+ case SID_ATTR_CHAR_OVERLINE:
+ case SID_ATTR_CHAR_STRIKEOUT:
+ case SID_ATTR_ALIGN_LINEBREAK:
+ case SID_ATTR_CHAR_CONTOUR:
+ case SID_ATTR_CHAR_SHADOWED:
+ case SID_ATTR_CHAR_RELIEF:
+ pTabViewShell->ApplyAttr( pNewAttrs->Get( pNewAttrs->GetPool()->GetWhich( nSlot ) ) );
+ rBindings.Invalidate( nSlot );
+ rBindings.Update( nSlot );
+ break;
+ case SID_ATTR_CHAR_COLOR:
+ case SID_SCATTR_PROTECTION :
+ {
+ pTabViewShell->ApplyAttr( pNewAttrs->Get( pNewAttrs->GetPool()->GetWhich( nSlot) ), false);
+
+ rBindings.Invalidate( nSlot );
+ rBindings.Update( nSlot );
+ }
+
+ break;
+
+ case SID_ATTR_CHAR_FONT:
+ case SID_ATTR_CHAR_FONTHEIGHT:
+ {
+ // #i78017 establish the same behaviour as in Writer
+ SvtScriptType nScript = SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX;
+ if (nSlot == SID_ATTR_CHAR_FONT)
+ nScript = pTabViewShell->GetSelectionScriptType();
+
+ SfxItemPool& rPool = GetPool();
+ SvxScriptSetItem aSetItem( nSlot, rPool );
+ sal_uInt16 nWhich = rPool.GetWhich( nSlot );
+ aSetItem.PutItemForScriptType( nScript, pNewAttrs->Get( nWhich ) );
+
+ pTabViewShell->ApplyUserItemSet( aSetItem.GetItemSet() );
+
+ rBindings.Invalidate( nSlot );
+ rBindings.Update( nSlot );
+ }
+ break;
+
+ case SID_FRAME_LINESTYLE:
+ {
+ // Update default line
+ const ::editeng::SvxBorderLine* pLine =
+ pNewAttrs->Get( SID_FRAME_LINESTYLE ).
+ GetLine();
+
+ if ( pLine )
+ {
+ ::editeng::SvxBorderLine* pDefLine = pTabViewShell->GetDefaultFrameLine();
+
+ if ( pDefLine )
+ {
+ pDefLine->SetBorderLineStyle(
+ pLine->GetBorderLineStyle());
+ pDefLine->SetWidth( pLine->GetWidth( ) );
+ pTabViewShell->SetSelectionFrameLines( pDefLine, false );
+ }
+ else
+ {
+ pTabViewShell->SetDefaultFrameLine( pLine );
+ pTabViewShell->GetDefaultFrameLine()->SetColor( COL_BLACK );
+ pTabViewShell->SetSelectionFrameLines( pLine, false );
+ }
+ }
+ else
+ {
+ Color aColorBlack( COL_BLACK );
+ ::editeng::SvxBorderLine aDefLine( &aColorBlack, 20,
+ SvxBorderLineStyle::SOLID );
+ pTabViewShell->SetDefaultFrameLine( &aDefLine );
+ pTabViewShell->SetSelectionFrameLines( nullptr, false );
+ }
+ }
+ break;
+
+ case SID_FRAME_LINECOLOR:
+ {
+ ::editeng::SvxBorderLine* pDefLine = pTabViewShell->GetDefaultFrameLine();
+
+ Color aColor = pNewAttrs->Get( SID_FRAME_LINECOLOR ).GetValue();
+
+ // Update default line
+ if ( pDefLine )
+ {
+ pDefLine->SetColor( aColor );
+ pTabViewShell->SetSelectionFrameLines( pDefLine, true );
+ }
+ else
+ {
+ ::editeng::SvxBorderLine aDefLine( &aColor, 20, SvxBorderLineStyle::SOLID );
+ pTabViewShell->SetDefaultFrameLine( &aDefLine );
+ pTabViewShell->SetSelectionFrameLines( &aDefLine, false );
+ }
+ }
+ break;
+
+ case SID_ATTR_BORDER_OUTER:
+ case SID_ATTR_BORDER:
+ {
+ ::editeng::SvxBorderLine* pDefLine = pTabViewShell->GetDefaultFrameLine();
+ const ScPatternAttr* pOldAttrs = pTabViewShell->GetSelectionPattern();
+ SfxItemSetFixed<ATTR_PATTERN_START, ATTR_PATTERN_END> aOldSet( *rDoc.GetPool() );
+ SfxItemSetFixed<ATTR_PATTERN_START, ATTR_PATTERN_END> aNewSet( *rDoc.GetPool() );
+ const SfxPoolItem& rBorderAttr =
+ pOldAttrs->GetItemSet().
+ Get( ATTR_BORDER );
+
+ // Evaluate border items from controller:
+
+ if ( const SvxBoxItem* pBoxItem = pNewAttrs->GetItemIfSet( ATTR_BORDER ) )
+ {
+ // The SvxFrameToolBoxControl toolbox controller uses a default
+ // SvxBorderLine (all widths 0) to mark the lines that should be set.
+ // Macro recording uses a SvxBoxItem with the real values (OutWidth > 0)
+ // or NULL pointers for no lines.
+ // -> Substitute existing lines with pDefLine only if widths are 0.
+ SvxBoxItem aBoxItem ( *pBoxItem );
+ if ( aBoxItem.GetTop() && aBoxItem.GetTop()->GetOutWidth() == 0 )
+ aBoxItem.SetLine( pDefLine, SvxBoxItemLine::TOP );
+ if ( aBoxItem.GetBottom() && aBoxItem.GetBottom()->GetOutWidth() == 0 )
+ aBoxItem.SetLine( pDefLine, SvxBoxItemLine::BOTTOM );
+ if ( aBoxItem.GetLeft() && aBoxItem.GetLeft()->GetOutWidth() == 0 )
+ aBoxItem.SetLine( pDefLine, SvxBoxItemLine::LEFT );
+ if ( aBoxItem.GetRight() && aBoxItem.GetRight()->GetOutWidth() == 0 )
+ aBoxItem.SetLine( pDefLine, SvxBoxItemLine::RIGHT );
+ aNewSet.Put( aBoxItem );
+ rReq.AppendItem( aBoxItem );
+ }
+
+ if ( const SvxBoxInfoItem* pBoxInfoItem = pNewAttrs->GetItemIfSet( ATTR_BORDER_INNER ) )
+ {
+ SvxBoxInfoItem aBoxInfoItem( *pBoxInfoItem );
+ if ( aBoxInfoItem.GetHori() && aBoxInfoItem.GetHori()->GetOutWidth() == 0 )
+ aBoxInfoItem.SetLine( pDefLine, SvxBoxInfoItemLine::HORI );
+ if ( aBoxInfoItem.GetVert() && aBoxInfoItem.GetVert()->GetOutWidth() == 0 )
+ aBoxInfoItem.SetLine( pDefLine, SvxBoxInfoItemLine::VERT );
+ aNewSet.Put( aBoxInfoItem );
+ rReq.AppendItem( aBoxInfoItem );
+ }
+ else
+ {
+ SvxBoxInfoItem aBoxInfoItem( ATTR_BORDER_INNER );
+ aBoxInfoItem.SetLine( nullptr, SvxBoxInfoItemLine::HORI );
+ aBoxInfoItem.SetLine( nullptr, SvxBoxInfoItemLine::VERT );
+ aNewSet.Put( aBoxInfoItem );
+ }
+
+ aOldSet.Put( rBorderAttr );
+ pTabViewShell->ApplyAttributes( aNewSet, aOldSet );
+ }
+ break;
+
+ case SID_ATTR_BORDER_DIAG_TLBR:
+ case SID_ATTR_BORDER_DIAG_BLTR:
+ {
+ const ScPatternAttr* pOldAttrs = pTabViewShell->GetSelectionPattern();
+ SfxItemSet aOldSet(pOldAttrs->GetItemSet());
+ SfxItemSet aNewSet(pOldAttrs->GetItemSet());
+
+ if(SID_ATTR_BORDER_DIAG_TLBR == nSlot)
+ {
+ if(SfxItemState::SET == pNewAttrs->GetItemState(ATTR_BORDER_TLBR))
+ {
+ SvxLineItem aItem(ATTR_BORDER_TLBR);
+ aItem.SetLine(pNewAttrs->Get(ATTR_BORDER_TLBR).GetLine());
+ aNewSet.Put(aItem);
+ rReq.AppendItem(aItem);
+ pTabViewShell->ApplyAttributes(aNewSet, aOldSet);
+ }
+ }
+ else // if( nSlot == SID_ATTR_BORDER_DIAG_BLTR )
+ {
+ if(SfxItemState::SET == pNewAttrs->GetItemState(ATTR_BORDER_BLTR ))
+ {
+ SvxLineItem aItem(ATTR_BORDER_BLTR);
+ aItem.SetLine(pNewAttrs->Get(ATTR_BORDER_BLTR).GetLine());
+ aNewSet.Put(aItem);
+ rReq.AppendItem(aItem);
+ pTabViewShell->ApplyAttributes(aNewSet, aOldSet);
+ }
+ }
+
+ rBindings.Invalidate(nSlot);
+ }
+ break;
+
+ // ATTR_BACKGROUND (=SID_ATTR_BRUSH) has to be set to two IDs:
+ case SID_BACKGROUND_COLOR:
+ {
+ const SvxColorItem& rNewColorItem = pNewAttrs->Get( SID_BACKGROUND_COLOR );
+ Color aColor = rNewColorItem.GetValue();
+
+ SvxBrushItem aBrushItem(
+ pTabViewShell->GetSelectionPattern()->GetItem( ATTR_BACKGROUND ) );
+ aBrushItem.SetColor(aColor);
+ aBrushItem.setComplexColor(rNewColorItem.getComplexColor());
+
+ pTabViewShell->ApplyAttr( aBrushItem, false );
+ }
+ break;
+
+ case SID_ATTR_BRUSH:
+ {
+ SvxBrushItem aBrushItem( pTabViewShell->GetSelectionPattern()->
+ GetItem( ATTR_BACKGROUND ) );
+ const SvxBrushItem& rNewBrushItem = static_cast<const SvxBrushItem&>(
+ pNewAttrs->Get( GetPool().GetWhich(nSlot) ) );
+ aBrushItem.SetColor(rNewBrushItem.GetColor());
+ aBrushItem.setComplexColor(rNewBrushItem.getComplexColor());
+ pTabViewShell->ApplyAttr( aBrushItem );
+ }
+ break;
+
+ case SID_ATTR_BORDER_SHADOW:
+ {
+ const SvxShadowItem& rNewShadowItem =
+ pNewAttrs->Get( ATTR_SHADOW );
+ pTabViewShell->ApplyAttr( rNewShadowItem );
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if( ! rReq.IsAPI() && ! rReq.IsDone() )
+ rReq.Done();
+ }
+}
+
+void ScFormatShell::GetAttrState( SfxItemSet& rSet )
+{
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ const SfxItemSet& rAttrSet = pTabViewShell->GetSelectionPattern()->GetItemSet();
+ const SvxBrushItem& rBrushItem = rAttrSet.Get( ATTR_BACKGROUND );
+ SfxWhichIter aIter( rSet );
+ sal_uInt16 nWhich = aIter.FirstWhich();
+
+ rSet.Put( rAttrSet, false );
+
+ // choose font info according to selection script type
+ SvtScriptType nScript = SvtScriptType::NONE; // GetSelectionScriptType never returns 0
+ if ( rSet.GetItemState( ATTR_FONT ) != SfxItemState::UNKNOWN )
+ {
+ nScript = pTabViewShell->GetSelectionScriptType();
+ ScViewUtil::PutItemScript( rSet, rAttrSet, ATTR_FONT, nScript );
+ }
+ if ( rSet.GetItemState( ATTR_FONT_HEIGHT ) != SfxItemState::UNKNOWN )
+ {
+ if (nScript == SvtScriptType::NONE) nScript = pTabViewShell->GetSelectionScriptType();
+ ScViewUtil::PutItemScript( rSet, rAttrSet, ATTR_FONT_HEIGHT, nScript );
+ }
+
+ while ( nWhich )
+ {
+ switch(nWhich)
+ {
+ case SID_BACKGROUND_COLOR:
+ {
+ rSet.Put( SvxColorItem( rBrushItem.GetColor(), SID_BACKGROUND_COLOR ) );
+ if(SfxItemState::DONTCARE == rAttrSet.GetItemState(ATTR_BACKGROUND))
+ {
+ rSet.InvalidateItem(SID_BACKGROUND_COLOR);
+ }
+ }
+ break;
+ case SID_FRAME_LINESTYLE:
+ case SID_FRAME_LINECOLOR:
+ {
+ // handled together because both need the cell border information for decisions
+ Color aCol;
+ editeng::SvxBorderLine aLine(nullptr,0,SvxBorderLineStyle::SOLID);
+ bool bCol = false;
+ bool bColDisable = false, bStyleDisable = false;
+ std::shared_ptr<SvxBoxItem> aBoxItem(std::make_shared<SvxBoxItem>(ATTR_BORDER));
+ std::shared_ptr<SvxBoxInfoItem> aInfoItem(std::make_shared<SvxBoxInfoItem>(ATTR_BORDER_INNER));
+
+ pTabViewShell->GetSelectionFrame(aBoxItem, aInfoItem);
+
+ if( aBoxItem->GetTop() )
+ {
+ bCol = true;
+ aCol = aBoxItem->GetTop()->GetColor() ;
+ aLine.SetColor(aCol);
+ aLine.SetWidth( aBoxItem->GetTop()->GetWidth());
+ aLine.SetBorderLineStyle( aBoxItem->GetTop()->GetBorderLineStyle());
+ }
+
+ if( aBoxItem->GetBottom() )
+ {
+ if(!bCol)
+ {
+ bCol = true;
+ aCol = aBoxItem->GetBottom()->GetColor() ;
+ aLine.SetColor(aCol);
+ aLine.SetWidth( aBoxItem->GetBottom()->GetWidth());
+ aLine.SetBorderLineStyle( aBoxItem->GetBottom()->GetBorderLineStyle());
+ }
+ else
+ {
+ if(aCol != aBoxItem->GetBottom()->GetColor() )
+ bColDisable = true;
+ if( aLine != *aBoxItem->GetBottom() )
+ bStyleDisable = true;
+ }
+ }
+
+ if( aBoxItem->GetLeft() )
+ {
+ if(!bCol)
+ {
+ bCol = true;
+ aCol = aBoxItem->GetLeft()->GetColor() ;
+ aLine.SetColor(aCol);
+ aLine.SetWidth( aBoxItem->GetLeft()->GetWidth());
+ aLine.SetBorderLineStyle( aBoxItem->GetLeft()->GetBorderLineStyle());
+ }
+ else
+ {
+ if(aCol != aBoxItem->GetLeft()->GetColor() )
+ bColDisable = true;
+ if( aLine != *aBoxItem->GetLeft() )
+ bStyleDisable = true;
+ }
+ }
+
+ if( aBoxItem->GetRight() )
+ {
+ if(!bCol)
+ {
+ bCol = true;
+ aCol = aBoxItem->GetRight()->GetColor() ;
+ aLine.SetColor(aCol);
+ aLine.SetWidth( aBoxItem->GetRight()->GetWidth());
+ aLine.SetBorderLineStyle( aBoxItem->GetRight()->GetBorderLineStyle());
+ }
+ else
+ {
+ if(aCol != aBoxItem->GetRight()->GetColor() )
+ bColDisable = true;
+ if( aLine != *aBoxItem->GetRight() )
+ bStyleDisable = true;
+ }
+ }
+
+ if( aInfoItem->GetVert())
+ {
+ if(!bCol)
+ {
+ bCol = true;
+ aCol = aInfoItem->GetVert()->GetColor() ;
+ aLine.SetColor(aCol);
+ aLine.SetWidth( aInfoItem->GetVert()->GetWidth());
+ aLine.SetBorderLineStyle( aInfoItem->GetVert()->GetBorderLineStyle());
+ }
+ else
+ {
+ if(aCol != aInfoItem->GetVert()->GetColor() )
+ bColDisable = true;
+ if( aLine != *aInfoItem->GetVert() )
+ bStyleDisable = true;
+ }
+ }
+
+ if( aInfoItem->GetHori())
+ {
+ if(!bCol)
+ {
+ bCol = true;
+ aCol = aInfoItem->GetHori()->GetColor() ;
+ aLine.SetColor(aCol);
+ aLine.SetWidth( aInfoItem->GetHori()->GetWidth());
+ aLine.SetBorderLineStyle( aInfoItem->GetHori()->GetBorderLineStyle());
+ }
+ else
+ {
+ if(aCol != aInfoItem->GetHori()->GetColor() )
+ bColDisable = true;
+ if( aLine != *aInfoItem->GetHori() )
+ bStyleDisable = true;
+ }
+ }
+
+ if( !aInfoItem->IsValid( SvxBoxInfoItemValidFlags::VERT )
+ || !aInfoItem->IsValid( SvxBoxInfoItemValidFlags::HORI )
+ || !aInfoItem->IsValid( SvxBoxInfoItemValidFlags::LEFT )
+ || !aInfoItem->IsValid( SvxBoxInfoItemValidFlags::RIGHT )
+ || !aInfoItem->IsValid( SvxBoxInfoItemValidFlags::TOP )
+ || !aInfoItem->IsValid( SvxBoxInfoItemValidFlags::BOTTOM ) )
+ {
+ bColDisable = true;
+ bStyleDisable = true;
+ }
+
+ if(SID_FRAME_LINECOLOR == nWhich)
+ {
+ if(bColDisable) // if different lines have different colors
+ {
+ aCol = COL_TRANSPARENT;
+ rSet.Put( SvxColorItem(aCol, SID_FRAME_LINECOLOR ) );
+ rSet.InvalidateItem(SID_FRAME_LINECOLOR);
+ }
+ else if (!bCol) // if no line available
+ {
+ aCol = COL_AUTO;
+ rSet.Put( SvxColorItem(aCol, SID_FRAME_LINECOLOR ) );
+ }
+ else
+ rSet.Put( SvxColorItem(aCol, SID_FRAME_LINECOLOR ) );
+ }
+ else // if( nWhich == SID_FRAME_LINESTYLE)
+ {
+ if(bStyleDisable) // if have several lines but don't have same style
+ {
+ aLine.SetWidth( 1 );
+ SvxLineItem aItem(SID_FRAME_LINESTYLE);
+ aItem.SetLine(&aLine);
+ rSet.Put( aItem );
+ rSet.InvalidateItem(SID_FRAME_LINESTYLE);
+ }
+ else // all the lines have same style or no line available, use initial value (0,0,0,0)
+ {
+ SvxLineItem aItem(SID_FRAME_LINESTYLE);
+ aItem.SetLine(&aLine);
+ rSet.Put( aItem );
+ }
+ }
+ }
+ break;
+ case SID_ATTR_BRUSH:
+ {
+ rSet.Put( rBrushItem.CloneSetWhich(GetPool().GetWhich(nWhich)) );
+ }
+ break;
+ case SID_SCATTR_CELLPROTECTION:
+ {
+ bool bProtect = rAttrSet.Get( ATTR_PROTECTION ).GetProtection();
+ rSet.Put( SfxBoolItem(SID_SCATTR_CELLPROTECTION, bProtect) );
+ }
+ break;
+ }
+ nWhich = aIter.NextWhich();
+ }
+
+ // stuff for sidebar panels
+ Invalidate(SID_ATTR_ALIGN_DEGREES);
+ Invalidate(SID_ATTR_ALIGN_LOCKPOS);
+ Invalidate(SID_ATTR_ALIGN_STACKED);
+}
+
+void ScFormatShell::GetTextAttrState( SfxItemSet& rSet )
+{
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ const SfxItemSet& rAttrSet = pTabViewShell->GetSelectionPattern()->GetItemSet();
+ rSet.Put( rAttrSet, false ); // Include ItemStates in copy
+
+ // choose font info according to selection script type
+ SvtScriptType nScript = SvtScriptType::NONE; // GetSelectionScriptType never returns 0
+ if ( rSet.GetItemState( ATTR_FONT_WEIGHT ) != SfxItemState::UNKNOWN )
+ {
+ nScript = pTabViewShell->GetSelectionScriptType();
+ ScViewUtil::PutItemScript( rSet, rAttrSet, ATTR_FONT_WEIGHT, nScript );
+ }
+ if ( rSet.GetItemState( ATTR_FONT_POSTURE ) != SfxItemState::UNKNOWN )
+ {
+ if (nScript == SvtScriptType::NONE) nScript = pTabViewShell->GetSelectionScriptType();
+ ScViewUtil::PutItemScript( rSet, rAttrSet, ATTR_FONT_POSTURE, nScript );
+ }
+
+ SfxItemState eState;
+
+ // own control on radio button functionality:
+
+ // underline
+
+ eState = rAttrSet.GetItemState( ATTR_FONT_UNDERLINE );
+ if ( eState == SfxItemState::DONTCARE )
+ {
+ rSet.InvalidateItem( SID_ULINE_VAL_NONE );
+ rSet.InvalidateItem( SID_ULINE_VAL_SINGLE );
+ rSet.InvalidateItem( SID_ULINE_VAL_DOUBLE );
+ rSet.InvalidateItem( SID_ULINE_VAL_DOTTED );
+ }
+ else
+ {
+ FontLineStyle eUnderline =
+ rAttrSet.Get(ATTR_FONT_UNDERLINE).GetLineStyle();
+ rSet.Put(SfxBoolItem(SID_ULINE_VAL_SINGLE, eUnderline == LINESTYLE_SINGLE));
+ rSet.Put(SfxBoolItem(SID_ULINE_VAL_DOUBLE, eUnderline == LINESTYLE_DOUBLE));
+ rSet.Put(SfxBoolItem(SID_ULINE_VAL_DOTTED, eUnderline == LINESTYLE_DOTTED));
+ rSet.Put(SfxBoolItem(SID_ULINE_VAL_NONE, eUnderline == LINESTYLE_NONE));
+ }
+
+ // horizontal alignment
+
+ const SvxHorJustifyItem* pHorJustify = nullptr;
+ const SvxVerJustifyItem* pVerJustify = nullptr;
+ SvxCellVerJustify eVerJustify = SvxCellVerJustify::Standard;
+ sal_uInt16 nWhich = 0;
+ bool bJustifyStd = false;
+ SfxBoolItem aBoolItem ( 0, true );
+
+ eState = rAttrSet.GetItemState( ATTR_HOR_JUSTIFY, true,
+ reinterpret_cast<const SfxPoolItem**>(&pHorJustify) );
+ switch ( eState )
+ {
+ case SfxItemState::SET:
+ {
+ switch ( pHorJustify->GetValue() )
+ {
+ case SvxCellHorJustify::Standard:
+ break;
+
+ case SvxCellHorJustify::Left:
+ nWhich = SID_ALIGNLEFT;
+ break;
+
+ case SvxCellHorJustify::Right:
+ nWhich = SID_ALIGNRIGHT;
+ break;
+
+ case SvxCellHorJustify::Center:
+ nWhich = SID_ALIGNCENTERHOR;
+ break;
+
+ case SvxCellHorJustify::Block:
+ nWhich = SID_ALIGNBLOCK;
+ break;
+
+ case SvxCellHorJustify::Repeat:
+ default:
+ bJustifyStd = true;
+ break;
+ }
+ }
+ break;
+
+ case SfxItemState::DONTCARE:
+ rSet.InvalidateItem( SID_ALIGNLEFT );
+ rSet.InvalidateItem( SID_ALIGNRIGHT );
+ rSet.InvalidateItem( SID_ALIGNCENTERHOR );
+ rSet.InvalidateItem( SID_ALIGNBLOCK );
+ break;
+
+ default:
+ bJustifyStd = true;
+ break;
+ }
+
+ if ( nWhich )
+ {
+ aBoolItem.SetWhich( nWhich );
+ rSet.Put( aBoolItem );
+ }
+ else if ( bJustifyStd )
+ {
+ aBoolItem.SetValue( false );
+ aBoolItem.SetWhich( SID_ALIGNLEFT ); rSet.Put( aBoolItem );
+ aBoolItem.SetWhich( SID_ALIGNRIGHT ); rSet.Put( aBoolItem );
+ aBoolItem.SetWhich( SID_ALIGNCENTERHOR ); rSet.Put( aBoolItem );
+ aBoolItem.SetWhich( SID_ALIGNBLOCK ); rSet.Put( aBoolItem );
+ bJustifyStd = false;
+ }
+
+ // vertical alignment
+
+ nWhich = 0;
+ aBoolItem.SetValue( true );
+
+ eState = rAttrSet.GetItemState( ATTR_VER_JUSTIFY, true,
+ reinterpret_cast<const SfxPoolItem**>(&pVerJustify) );
+
+ switch ( eState )
+ {
+ case SfxItemState::SET:
+ {
+ eVerJustify = pVerJustify->GetValue();
+
+ switch ( eVerJustify )
+ {
+ case SvxCellVerJustify::Top:
+ nWhich = SID_ALIGNTOP;
+ break;
+
+ case SvxCellVerJustify::Bottom:
+ nWhich = SID_ALIGNBOTTOM;
+ break;
+
+ case SvxCellVerJustify::Center:
+ nWhich = SID_ALIGNCENTERVER;
+ break;
+
+ case SvxCellVerJustify::Standard:
+ default:
+ bJustifyStd = true;
+ break;
+ }
+ }
+ break;
+
+ case SfxItemState::DONTCARE:
+ rSet.InvalidateItem( SID_ALIGNTOP );
+ rSet.InvalidateItem( SID_ALIGNBOTTOM );
+ rSet.InvalidateItem( SID_ALIGNCENTERVER );
+ break;
+
+ default:
+ bJustifyStd = true;
+ break;
+ }
+
+ if ( nWhich )
+ {
+ aBoolItem.SetWhich( nWhich );
+ rSet.Put( aBoolItem );
+ }
+ else if ( bJustifyStd )
+ {
+ aBoolItem.SetValue( false );
+ aBoolItem.SetWhich( SID_ALIGNTOP ); rSet.Put( aBoolItem );
+ aBoolItem.SetWhich( SID_ALIGNBOTTOM ); rSet.Put( aBoolItem );
+ aBoolItem.SetWhich( SID_ALIGNCENTERVER ); rSet.Put( aBoolItem );
+ }
+}
+
+void ScFormatShell::GetBorderState( SfxItemSet& rSet )
+{
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ std::shared_ptr<SvxBoxItem> aBoxItem(std::make_shared<SvxBoxItem>(ATTR_BORDER));
+ std::shared_ptr<SvxBoxInfoItem> aInfoItem(std::make_shared<SvxBoxInfoItem>(ATTR_BORDER_INNER));
+
+ pTabViewShell->GetSelectionFrame( aBoxItem, aInfoItem );
+
+ if ( rSet.GetItemState( ATTR_BORDER ) != SfxItemState::UNKNOWN )
+ rSet.Put( *aBoxItem );
+ if ( rSet.GetItemState( ATTR_BORDER_INNER ) != SfxItemState::UNKNOWN )
+ rSet.Put( *aInfoItem );
+}
+
+void ScFormatShell::GetAlignState( SfxItemSet& rSet )
+{
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ const SfxItemSet& rAttrSet = pTabViewShell->GetSelectionPattern()->GetItemSet();
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+
+ SvxCellHorJustify eHAlign = SvxCellHorJustify::Standard;
+ bool bHasHAlign = rAttrSet.GetItemState( ATTR_HOR_JUSTIFY ) != SfxItemState::DONTCARE;
+ if( bHasHAlign )
+ eHAlign = rAttrSet.Get( ATTR_HOR_JUSTIFY ).GetValue();
+
+ SvxCellVerJustify eVAlign = SvxCellVerJustify::Standard;
+ bool bHasVAlign = rAttrSet.GetItemState( ATTR_VER_JUSTIFY ) != SfxItemState::DONTCARE;
+ if( bHasVAlign )
+ eVAlign = rAttrSet.Get( ATTR_VER_JUSTIFY ).GetValue();
+
+ while ( nWhich )
+ {
+ switch ( nWhich )
+ {
+ case SID_H_ALIGNCELL:
+ if ( bHasHAlign )
+ rSet.Put( SvxHorJustifyItem( eHAlign, nWhich ));
+ break;
+ case SID_V_ALIGNCELL:
+ if ( bHasVAlign )
+ rSet.Put( SvxVerJustifyItem( eVAlign, nWhich ));
+ break;
+
+ // pseudo slots for Format menu
+ case SID_ALIGN_ANY_HDEFAULT:
+ case SID_ALIGN_ANY_LEFT:
+ case SID_ALIGN_ANY_HCENTER:
+ case SID_ALIGN_ANY_RIGHT:
+ case SID_ALIGN_ANY_JUSTIFIED:
+ rSet.Put( SfxBoolItem( nWhich, bHasHAlign && (eHAlign == lclConvertSlotToHAlign( nWhich )) ) );
+ break;
+ case SID_ALIGN_ANY_VDEFAULT:
+ case SID_ALIGN_ANY_TOP:
+ case SID_ALIGN_ANY_VCENTER:
+ case SID_ALIGN_ANY_BOTTOM:
+ rSet.Put( SfxBoolItem( nWhich, bHasVAlign && (eVAlign == lclConvertSlotToVAlign( nWhich )) ) );
+ break;
+ }
+ nWhich = aIter.NextWhich();
+ }
+}
+
+void ScFormatShell::GetNumFormatState( SfxItemSet& rSet )
+{
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ ScDocument& rDoc = rViewData.GetDocument();
+ const SfxItemSet& rAttrSet = pTabViewShell->GetSelectionPattern()->GetItemSet();
+ const SfxItemState eItemState = rAttrSet.GetItemState( ATTR_VALUE_FORMAT );
+ sal_uInt32 nNumberFormat = rAttrSet.Get(ATTR_VALUE_FORMAT).GetValue();
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ // If item state is default or set it
+ // indicates one number format so we
+ // don't have to iterate over all
+ // selected cells' attribute ranges to
+ // determine selected types.
+ // Does *NOT* include the
+ // SvNumFormatType::DEFINED bit.
+ const SvNumFormatType nType = (eItemState >= SfxItemState::DEFAULT ? pFormatter->GetType( nNumberFormat) :
+ GetCurrentNumberFormatType());
+ NfIndexTableOffset nOffset = pFormatter->GetIndexTableOffset(nNumberFormat);
+
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+
+ while ( nWhich )
+ {
+ switch ( nWhich )
+ {
+ case SID_NUMBER_THOUSANDS:
+ {
+ bool bEnable = (SfxItemState::DONTCARE != eItemState);
+ if (bEnable)
+ {
+ bEnable = ((nType != SvNumFormatType::ALL) && (nType &
+ (SvNumFormatType::NUMBER |
+ SvNumFormatType::PERCENT |
+ SvNumFormatType::CURRENCY |
+ SvNumFormatType::FRACTION)));
+ if (bEnable)
+ {
+ bool bThousand( false );
+ bool bNegRed( false );
+ sal_uInt16 nPrecision( 0 );
+ sal_uInt16 nLeadZeroes( 0 );
+ pFormatter->GetFormatSpecialInfo( nNumberFormat, bThousand, bNegRed, nPrecision, nLeadZeroes);
+ rSet.Put( SfxBoolItem( nWhich, bThousand));
+ }
+ }
+ if (!bEnable)
+ {
+ rSet.DisableItem( nWhich );
+ }
+ }
+ break;
+ case SID_NUMBER_FORMAT:
+ // symphony version with format interpretation
+ {
+ if(SfxItemState::DONTCARE != eItemState)
+ {
+ bool bThousand(false);
+ bool bNegRed(false);
+ sal_uInt16 nPrecision(0);
+ sal_uInt16 nLeadZeroes(0);
+
+ pFormatter->GetFormatSpecialInfo(nNumberFormat,bThousand, bNegRed, nPrecision, nLeadZeroes);
+
+ const SvNumberformat* pFormatEntry = pFormatter->GetEntry( nNumberFormat );
+ if (pFormatEntry && (pFormatEntry->GetType() & SvNumFormatType::SCIENTIFIC))
+ {
+ // if scientific, bThousand is used for engineering notation
+ const sal_uInt16 nIntegerDigits = pFormatEntry->GetFormatIntegerDigits();
+ bThousand = nIntegerDigits > 0 && ((nIntegerDigits % 3) == 0);
+ }
+ OUString aFormat;
+ static constexpr OUString sBreak = u","_ustr;
+ const OUString sThousand = OUString::number(static_cast<sal_Int32>(bThousand));
+ const OUString sNegRed = OUString::number(static_cast<sal_Int32>(bNegRed));
+ const OUString sPrecision = OUString::number(nPrecision);
+ const OUString sLeadZeroes = OUString::number(nLeadZeroes);
+ const OUString sNatNum12 = OUString::number( static_cast< sal_Int32 >( pFormatter->IsNatNum12( nNumberFormat ) ) );
+
+ aFormat += sThousand +
+ sBreak +
+ sNegRed +
+ sBreak +
+ sPrecision +
+ sBreak +
+ sLeadZeroes +
+ sBreak +
+ sNatNum12 +
+ sBreak;
+
+ rSet.Put(SfxStringItem(nWhich, aFormat));
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ OUString sPayload = ".uno:NumberFormat=" + aFormat;
+ GetViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED,
+ OUStringToOString(sPayload, RTL_TEXTENCODING_ASCII_US));
+ }
+ }
+ else
+ {
+ rSet.InvalidateItem( nWhich );
+ }
+ }
+ break;
+
+ case SID_NUMBER_TYPE_FORMAT:
+ {
+ sal_Int16 nFormatCategory = -1;
+ if ( eItemState >= SfxItemState::DEFAULT ) //Modify for more robust
+ {
+ switch(nType)
+ {
+ case SvNumFormatType::NUMBER:
+ // Determine if General format.
+ if ((nNumberFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0)
+ nFormatCategory = 0;
+ else
+ nFormatCategory = 1;
+ break;
+ case SvNumFormatType::PERCENT:
+ nFormatCategory = 2;
+ break;
+ case SvNumFormatType::CURRENCY:
+ nFormatCategory = 3;
+ break;
+ case SvNumFormatType::DATE:
+ //Add
+ case SvNumFormatType::DATETIME:
+ nFormatCategory = 4;
+ break;
+ case SvNumFormatType::TIME:
+ nFormatCategory = 5;
+ break;
+ case SvNumFormatType::SCIENTIFIC:
+ nFormatCategory = 6;
+ break;
+ case SvNumFormatType::FRACTION:
+ nFormatCategory = 7;
+ break;
+ case SvNumFormatType::LOGICAL:
+ nFormatCategory = 8;
+ break;
+ case SvNumFormatType::TEXT:
+ nFormatCategory = 9;
+ break;
+ default:
+ nFormatCategory = -1; //for more robust
+ }
+ if( nFormatCategory == -1 )
+ rSet.InvalidateItem( nWhich );
+ else
+ rSet.Put( SfxUInt16Item( nWhich, nFormatCategory ) );
+ }
+ else
+ {
+ rSet.InvalidateItem( nWhich );
+ }
+
+ }
+ break;
+ case SID_NUMBER_CURRENCY:
+ rSet.Put( SfxBoolItem(nWhich, bool(nType & SvNumFormatType::CURRENCY)) );
+ break;
+ case SID_NUMBER_SCIENTIFIC:
+ rSet.Put( SfxBoolItem(nWhich, bool(nType & SvNumFormatType::SCIENTIFIC)) );
+ break;
+ case SID_NUMBER_DATE:
+ rSet.Put( SfxBoolItem(nWhich, bool(nType & SvNumFormatType::DATE)) );
+ break;
+ case SID_NUMBER_PERCENT:
+ rSet.Put( SfxBoolItem(nWhich, bool(nType & SvNumFormatType::PERCENT)) );
+ break;
+ case SID_NUMBER_TIME:
+ rSet.Put( SfxBoolItem(nWhich, bool(nType & SvNumFormatType::TIME)) );
+ break;
+ case SID_NUMBER_TWODEC:
+ rSet.Put( SfxBoolItem(nWhich, (nType & SvNumFormatType::NUMBER) && nOffset == NF_NUMBER_1000DEC2 ) );
+ break;
+ case SID_NUMBER_STANDARD:
+ rSet.Put( SfxBoolItem(nWhich, (nType & SvNumFormatType::NUMBER) && (nNumberFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0) );
+ break;
+ }
+ nWhich = aIter.NextWhich();
+ }
+}
+
+void ScFormatShell::ExecuteTextDirection( const SfxRequest& rReq )
+{
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ pTabViewShell->HideListBox(); // Autofilter-DropDown-Listbox
+ bool bEditMode = false;
+ if ( GetViewData().HasEditView( GetViewData().GetActivePart() ) )
+ {
+ bEditMode=true;
+ SC_MOD()->InputEnterHandler();
+ pTabViewShell->UpdateInputHandler();
+ }
+ sal_uInt16 nSlot = rReq.GetSlot();
+ switch( nSlot )
+ {
+ case SID_TEXTDIRECTION_LEFT_TO_RIGHT:
+ case SID_TEXTDIRECTION_TOP_TO_BOTTOM:
+ {
+ bool bVert = (nSlot == SID_TEXTDIRECTION_TOP_TO_BOTTOM);
+ ScPatternAttr aAttr( GetViewData().GetDocument().GetPool() );
+ SfxItemSet& rItemSet = aAttr.GetItemSet();
+ rItemSet.Put( ScVerticalStackCell( bVert ) );
+ rItemSet.Put( SfxBoolItem( ATTR_VERTICAL_ASIAN, bVert ) );
+ pTabViewShell->ApplySelectionPattern( aAttr );
+ pTabViewShell->AdjustBlockHeight();
+ }
+ break;
+
+ case SID_ATTR_PARA_LEFT_TO_RIGHT:
+ case SID_ATTR_PARA_RIGHT_TO_LEFT:
+ {
+ SvxFrameDirection eDirection = ( nSlot == SID_ATTR_PARA_LEFT_TO_RIGHT ) ?
+ SvxFrameDirection::Horizontal_LR_TB : SvxFrameDirection::Horizontal_RL_TB;
+ pTabViewShell->ApplyAttr( SvxFrameDirectionItem( eDirection, ATTR_WRITINGDIR ) );
+ }
+ break;
+ }
+ if (bEditMode)
+ SC_MOD()->SetInputMode( SC_INPUT_TABLE );
+}
+
+void ScFormatShell::GetTextDirectionState( SfxItemSet& rSet )
+{
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ const SfxItemSet& rAttrSet = pTabViewShell->GetSelectionPattern()->GetItemSet();
+
+ bool bVertDontCare =
+ (rAttrSet.GetItemState( ATTR_VERTICAL_ASIAN ) == SfxItemState::DONTCARE) ||
+ (rAttrSet.GetItemState( ATTR_STACKED ) == SfxItemState::DONTCARE);
+ bool bLeftRight = !bVertDontCare &&
+ !rAttrSet.Get( ATTR_STACKED ).GetValue();
+ bool bTopBottom = !bVertDontCare && !bLeftRight &&
+ rAttrSet.Get( ATTR_VERTICAL_ASIAN ).GetValue();
+
+ bool bBidiDontCare = (rAttrSet.GetItemState( ATTR_WRITINGDIR ) == SfxItemState::DONTCARE);
+ EEHorizontalTextDirection eBidiDir = EEHorizontalTextDirection::Default;
+ if ( !bBidiDontCare )
+ {
+ SvxFrameDirection eCellDir = rAttrSet.Get( ATTR_WRITINGDIR ).GetValue();
+ if ( eCellDir == SvxFrameDirection::Environment )
+ eBidiDir = GetViewData().GetDocument().
+ GetEditTextDirection( GetViewData().GetTabNo() );
+ else if ( eCellDir == SvxFrameDirection::Horizontal_RL_TB )
+ eBidiDir = EEHorizontalTextDirection::R2L;
+ else
+ eBidiDir = EEHorizontalTextDirection::L2R;
+ }
+
+ bool bDisableCTLFont = !SvtCTLOptions::IsCTLFontEnabled();
+ bool bDisableVerticalText = !SvtCJKOptions::IsVerticalTextEnabled();
+
+ SfxWhichIter aIter( rSet );
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while( nWhich )
+ {
+ switch( nWhich )
+ {
+ case SID_TEXTDIRECTION_LEFT_TO_RIGHT:
+ case SID_TEXTDIRECTION_TOP_TO_BOTTOM:
+ if ( bDisableVerticalText )
+ rSet.DisableItem( nWhich );
+ else
+ {
+ if( bVertDontCare )
+ rSet.InvalidateItem( nWhich );
+ else if ( nWhich == SID_TEXTDIRECTION_LEFT_TO_RIGHT )
+ rSet.Put( SfxBoolItem( nWhich, bLeftRight ) );
+ else
+ rSet.Put( SfxBoolItem( nWhich, bTopBottom ) );
+ }
+ break;
+
+ case SID_ATTR_PARA_LEFT_TO_RIGHT:
+ case SID_ATTR_PARA_RIGHT_TO_LEFT:
+ if ( bDisableCTLFont )
+ rSet.DisableItem( nWhich );
+ else
+ {
+ if ( bTopBottom )
+ rSet.DisableItem( nWhich );
+ else if ( bBidiDontCare )
+ rSet.InvalidateItem( nWhich );
+ else if ( nWhich == SID_ATTR_PARA_LEFT_TO_RIGHT )
+ rSet.Put( SfxBoolItem( nWhich, eBidiDir == EEHorizontalTextDirection::L2R ) );
+ else
+ rSet.Put( SfxBoolItem( nWhich, eBidiDir == EEHorizontalTextDirection::R2L ) );
+ }
+ }
+ nWhich = aIter.NextWhich();
+ }
+}
+
+void ScFormatShell::ExecFormatPaintbrush( const SfxRequest& rReq )
+{
+ ScViewFunc* pView = rViewData.GetView();
+ if ( pView->HasPaintBrush() )
+ {
+ // cancel paintbrush mode
+ pView->ResetBrushDocument();
+ }
+ else
+ {
+ bool bLock = false;
+ const SfxItemSet *pArgs = rReq.GetArgs();
+ if( pArgs && pArgs->Count() >= 1 )
+ bLock = pArgs->Get(SID_FORMATPAINTBRUSH).GetValue();
+
+ // in case of multi selection, deselect all and use the cursor position
+ ScRange aDummy;
+ if ( rViewData.GetSimpleArea(aDummy) != SC_MARK_SIMPLE )
+ pView->Unmark();
+
+ ScDocumentUniquePtr pBrushDoc(new ScDocument( SCDOCMODE_CLIP ));
+ pView->CopyToClip( pBrushDoc.get(), false, true );
+ pView->SetBrushDocument( std::move(pBrushDoc), bLock );
+ }
+}
+
+void ScFormatShell::StateFormatPaintbrush( SfxItemSet& rSet )
+{
+ if ( rViewData.HasEditView( rViewData.GetActivePart() ) )
+ rSet.DisableItem( SID_FORMATPAINTBRUSH );
+ else
+ rSet.Put( SfxBoolItem( SID_FORMATPAINTBRUSH, rViewData.GetView()->HasPaintBrush() ) );
+}
+
+SvNumFormatType ScFormatShell::GetCurrentNumberFormatType()
+{
+ SvNumFormatType nType = SvNumFormatType::ALL;
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScMarkData aMark(GetViewData().GetMarkData());
+ const SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ if (!pFormatter)
+ return nType;
+
+ // TODO: Find out how to get a selected table range in case multiple tables
+ // are selected. Currently we only check for the current active table.
+
+ if ( aMark.IsMarked() || aMark.IsMultiMarked() )
+ {
+ aMark.MarkToMulti();
+ const ScRange& aRange = aMark.GetMultiMarkArea();
+ const ScMultiSel& rMultiSel = aMark.GetMultiSelData();
+
+ SvNumFormatType nComboType = SvNumFormatType::ALL;
+ bool bFirstItem = true;
+ for (SCCOL nCol = aRange.aStart.Col(); nCol <= aRange.aEnd.Col(); ++nCol)
+ {
+ if (!rMultiSel.HasMarks(nCol))
+ continue;
+
+ SCROW nRow1, nRow2;
+ ScMultiSelIter aMultiIter(rMultiSel, nCol);
+ while (aMultiIter.Next(nRow1, nRow2))
+ {
+ ScRange aColRange(nCol, nRow1, aRange.aStart.Tab());
+ aColRange.aEnd.SetRow(nRow2);
+ sal_uInt32 nNumFmt = rDoc.GetNumberFormat(aColRange);
+ SvNumFormatType nThisType = pFormatter->GetType(nNumFmt);
+ if (bFirstItem)
+ {
+ bFirstItem = false;
+ nComboType = nThisType;
+ }
+ else if (nComboType != nThisType)
+ // mixed number format type.
+ return SvNumFormatType::ALL;
+ }
+ }
+ nType = nComboType;
+ }
+ else
+ {
+ sal_uInt32 nNumFmt = rDoc.GetNumberFormat( rViewData.GetCurX(), rViewData.GetCurY(),
+ rViewData.GetTabNo());
+ nType = pFormatter->GetType( nNumFmt );
+ }
+ return nType;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/gridmerg.cxx b/sc/source/ui/view/gridmerg.cxx
new file mode 100644
index 0000000000..117b3e1ad7
--- /dev/null
+++ b/sc/source/ui/view/gridmerg.cxx
@@ -0,0 +1,225 @@
+/* -*- 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 .
+ */
+
+#include <sal/config.h>
+
+#include <vcl/lineinfo.hxx>
+#include <vcl/outdev.hxx>
+
+#include <gridmerg.hxx>
+
+#define PAGEBREAK_LINE_DISTANCE_PIXEL 5
+#define PAGEBREAK_LINE_DASH_LEN_PIXEL 5
+#define PAGEBREAK_LINE_DASH_COUNT 1
+
+ScGridMerger::ScGridMerger( OutputDevice* pOutDev, tools::Long nOnePixelX, tools::Long nOnePixelY )
+ : pDev(pOutDev)
+ , nOneX(nOnePixelX)
+ , nOneY(nOnePixelY)
+ , nFixStart(0)
+ , nFixEnd(0)
+ , nVarStart(0)
+ , nVarDiff(0)
+ , nCount(0)
+ , bVertical(false)
+{
+ // optimize (DrawGrid) only for pixel MapMode,
+ // to avoid rounding errors
+
+ bOptimize = ( pDev->GetMapMode().GetMapUnit() == MapUnit::MapPixel );
+}
+
+ScGridMerger::~ScGridMerger()
+{
+ Flush();
+}
+
+void ScGridMerger::AddLine( tools::Long nStart, tools::Long nEnd, tools::Long nPos )
+{
+ if ( nCount )
+ {
+ // not first line - test fix position
+ // more than one previous line - test distance
+
+ if ( nStart != nFixStart || nEnd != nFixEnd )
+ {
+ if ( nCount == 1 && nPos == nVarStart &&
+ ( nStart == nFixEnd ||
+ nStart == nFixEnd + ( bVertical ? nOneY : nOneX ) ) )
+ {
+ // additional optimization: extend connected lines
+ // keep nCount at 1
+ nFixEnd = nEnd;
+ }
+ else
+ Flush();
+ }
+ else if ( nCount == 1 )
+ {
+ nVarDiff = nPos - nVarStart;
+ ++nCount;
+ }
+ else if ( nPos != nVarStart + nCount * nVarDiff ) //! keep VarEnd?
+ Flush();
+ else
+ ++nCount;
+ }
+
+ if ( !nCount )
+ {
+ // first line (or flushed above) - just store
+
+ nFixStart = nStart;
+ nFixEnd = nEnd;
+ nVarStart = nPos;
+ nVarDiff = 0;
+ nCount = 1;
+ }
+}
+
+void ScGridMerger::AddHorLine(bool bWorksInPixels, tools::Long nX1, tools::Long nX2, tools::Long nY, bool bDashed)
+{
+ if ( bWorksInPixels )
+ {
+ Point aPoint(pDev->PixelToLogic(Point(nX1, nY)));
+ nX1 = aPoint.X();
+ nY = aPoint.Y();
+ nX2 = pDev->PixelToLogic(Point(nX2, 0)).X();
+ }
+
+ if ( bDashed )
+ {
+ // If there are some unflushed lines they must be flushed since
+ // new line is of different style
+ if (bOptimize) {
+ Flush();
+ bVertical = false;
+ }
+
+ LineInfo aLineInfo(LineStyle::Dash, 1);
+ aLineInfo.SetDashCount( PAGEBREAK_LINE_DASH_COUNT );
+
+ // Calculating logic values of DashLen and Distance from fixed pixel values
+ Size aDashDistanceLen( pDev->PixelToLogic( Size( PAGEBREAK_LINE_DISTANCE_PIXEL,
+ PAGEBREAK_LINE_DASH_LEN_PIXEL )));
+
+ aLineInfo.SetDistance( aDashDistanceLen.Width() );
+ aLineInfo.SetDashLen( aDashDistanceLen.Height() );
+
+ pDev->DrawLine( Point( nX1, nY ), Point( nX2, nY ), aLineInfo );
+ }
+ else if ( bOptimize )
+ {
+ if ( bVertical )
+ {
+ Flush();
+ bVertical = false;
+ }
+ AddLine( nX1, nX2, nY );
+ }
+ else
+ pDev->DrawLine( Point( nX1, nY ), Point( nX2, nY ) );
+}
+
+void ScGridMerger::AddVerLine(bool bWorksInPixels, tools::Long nX, tools::Long nY1, tools::Long nY2, bool bDashed)
+{
+ if (bWorksInPixels)
+ {
+ Point aPoint(pDev->PixelToLogic(Point(nX, nY1)));
+ nX = aPoint.X();
+ nY1 = aPoint.Y();
+ nY2 = pDev->PixelToLogic(Point(0, nY2)).Y();
+ }
+
+ if ( bDashed )
+ {
+ // If there are some unflushed lines they must be flushed since
+ // new line is of different style
+ if (bOptimize) {
+ Flush();
+ bVertical = false;
+ }
+
+ LineInfo aLineInfo(LineStyle::Dash, 1);
+ aLineInfo.SetDashCount( PAGEBREAK_LINE_DASH_COUNT );
+
+ // Calculating logic values of DashLen and Distance from fixed pixel values
+ Size aDashDistanceLen( pDev->PixelToLogic( Size( PAGEBREAK_LINE_DISTANCE_PIXEL,
+ PAGEBREAK_LINE_DASH_LEN_PIXEL )));
+
+ aLineInfo.SetDistance( aDashDistanceLen.Width() );
+ aLineInfo.SetDashLen( aDashDistanceLen.Height() );
+
+ pDev->DrawLine( Point( nX, nY1 ), Point( nX, nY2 ), aLineInfo);
+ }
+ else if ( bOptimize )
+ {
+ if ( !bVertical )
+ {
+ Flush();
+ bVertical = true;
+ }
+ AddLine( nY1, nY2, nX );
+ }
+ else
+ pDev->DrawLine( Point( nX, nY1 ), Point( nX, nY2 ) );
+}
+
+void ScGridMerger::Flush()
+{
+ if (!nCount)
+ return;
+
+ if (bVertical)
+ {
+ if ( nCount == 1 )
+ pDev->DrawLine( Point( nVarStart, nFixStart ), Point( nVarStart, nFixEnd ) );
+ else
+ {
+ tools::Long nVarEnd = nVarStart + ( nCount - 1 ) * nVarDiff;
+ if ( nVarDiff < 0 )
+ {
+ // nVarDiff is negative in RTL layout mode
+ // Change the positions so DrawGrid is called with a positive distance
+ // (nVarStart / nVarDiff can be modified, aren't used after Flush)
+
+ nVarDiff = -nVarDiff;
+ std::swap( nVarStart, nVarEnd );
+ }
+ pDev->DrawGrid( tools::Rectangle( nVarStart, nFixStart, nVarEnd, nFixEnd ),
+ Size( nVarDiff, nFixEnd - nFixStart ),
+ DrawGridFlags::VertLines );
+ }
+ }
+ else
+ {
+ if ( nCount == 1 )
+ pDev->DrawLine( Point( nFixStart, nVarStart ), Point( nFixEnd, nVarStart ) );
+ else
+ {
+ tools::Long nVarEnd = nVarStart + ( nCount - 1 ) * nVarDiff;
+ pDev->DrawGrid( tools::Rectangle( nFixStart, nVarStart, nFixEnd, nVarEnd ),
+ Size( nFixEnd - nFixStart, nVarDiff ),
+ DrawGridFlags::HorzLines );
+ }
+ }
+ nCount = 0;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/gridwin.cxx b/sc/source/ui/view/gridwin.cxx
new file mode 100644
index 0000000000..3f4f6b219c
--- /dev/null
+++ b/sc/source/ui/view/gridwin.cxx
@@ -0,0 +1,7218 @@
+/* -*- 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 .
+ */
+
+#include <scitems.hxx>
+
+#include <cstdlib>
+#include <memory>
+#include <editeng/adjustitem.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <sot/storage.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/editstat.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/flditem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <editeng/outliner.hxx>
+#include <editeng/misspellrange.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/ipclient.hxx>
+#include <svl/stritem.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/cursor.hxx>
+#include <vcl/dialoghelper.hxx>
+#include <vcl/inputctx.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/weldutils.hxx>
+#include <sot/formats.hxx>
+#include <comphelper/classids.hxx>
+#include <comphelper/scopeguard.hxx>
+
+#include <svx/svdview.hxx>
+#include <svx/svdocapt.hxx>
+#include <svx/svdpagv.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+
+#include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
+#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
+#include <com/sun/star/sheet/DataPilotTableHeaderData.hpp>
+#include <com/sun/star/sheet/MemberResultFlags.hpp>
+#include <com/sun/star/sheet/TableValidationVisibility.hpp>
+#include <com/sun/star/awt/KeyModifier.hpp>
+#include <com/sun/star/awt/MouseButton.hpp>
+#include <com/sun/star/awt/XVclWindowPeer.hpp>
+#include <com/sun/star/script/vba/VBAEventId.hpp>
+#include <com/sun/star/script/vba/XVBAEventProcessor.hpp>
+#include <com/sun/star/text/textfield/Type.hpp>
+
+#include <gridwin.hxx>
+#include <tabvwsh.hxx>
+#include <docsh.hxx>
+#include <viewdata.hxx>
+#include <tabview.hxx>
+#include <select.hxx>
+#include <scmod.hxx>
+#include <document.hxx>
+#include <attrib.hxx>
+#include <dbdata.hxx>
+#include <stlpool.hxx>
+#include <printfun.hxx>
+#include <cbutton.hxx>
+#include <sc.hrc>
+#include <helpids.h>
+#include <globstr.hrc>
+#include <strings.hrc>
+#include <editutil.hxx>
+#include <scresid.hxx>
+#include <inputhdl.hxx>
+#include <uiitems.hxx>
+#include <formulacell.hxx>
+#include <patattr.hxx>
+#include <notemark.hxx>
+#include <rfindlst.hxx>
+#include <output.hxx>
+#include <docfunc.hxx>
+#include <dbdocfun.hxx>
+#include <dpobject.hxx>
+#include <transobj.hxx>
+#include <drwtrans.hxx>
+#include <seltrans.hxx>
+#include <sizedev.hxx>
+#include <AccessibilityHints.hxx>
+#include <dpsave.hxx>
+#include <viewuno.hxx>
+#include <compiler.hxx>
+#include <editable.hxx>
+#include <fillinfo.hxx>
+#include <filterentries.hxx>
+#include <drwlayer.hxx>
+#include <validat.hxx>
+#include <tabprotection.hxx>
+#include <postit.hxx>
+#include <dpcontrol.hxx>
+#include <checklistmenu.hxx>
+#include <clipparam.hxx>
+#include <overlayobject.hxx>
+#include <cellsuno.hxx>
+#include <drawview.hxx>
+#include <dragdata.hxx>
+#include <cliputil.hxx>
+#include <queryentry.hxx>
+#include <markdata.hxx>
+#include <externalrefmgr.hxx>
+#include <spellcheckcontext.hxx>
+#include <uiobject.hxx>
+#include <undoblk.hxx>
+#include <datamapper.hxx>
+#include <inputopt.hxx>
+#include <queryparam.hxx>
+#include <SparklineList.hxx>
+
+#include <officecfg/Office/Common.hxx>
+
+#include <svx/PaletteManager.hxx>
+#include <svx/sdrpagewindow.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/uitest/logger.hxx>
+#include <vcl/uitest/eventdescription.hxx>
+#include <svx/sdr/overlay/overlayselection.hxx>
+#include <comphelper/lok.hxx>
+#include <sfx2/lokhelper.hxx>
+
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+
+#include <vector>
+#include <boost/property_tree/json_parser.hpp>
+
+#include <FilterListBox.hxx>
+
+using namespace css;
+using namespace css::uno;
+
+struct ScGridWindow::MouseEventState
+{
+ bool mbActivatePart;
+
+ MouseEventState() :
+ mbActivatePart(false)
+ {}
+};
+
+#define SC_FILTERLISTBOX_LINES 12
+
+ScGridWindow::VisibleRange::VisibleRange(const ScDocument& rDoc)
+ : mnCol1(0)
+ , mnCol2(rDoc.MaxCol())
+ , mnRow1(0)
+ , mnRow2(rDoc.MaxRow())
+{
+}
+
+bool ScGridWindow::VisibleRange::isInside(SCCOL nCol, SCROW nRow) const
+{
+ return mnCol1 <= nCol && nCol <= mnCol2 && mnRow1 <= nRow && nRow <= mnRow2;
+}
+
+bool ScGridWindow::VisibleRange::set(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2)
+{
+ bool bChanged = mnCol1 != nCol1 || mnRow1 != nRow1 || mnCol2 != nCol2 || mnRow2 != nRow2;
+
+ mnCol1 = nCol1;
+ mnRow1 = nRow1;
+ mnCol2 = nCol2;
+ mnRow2 = nRow2;
+
+ return bChanged;
+}
+
+// ListBox in a FloatingWindow (pParent)
+ScFilterListBox::ScFilterListBox(weld::Window* pParent, ScGridWindow* pGrid,
+ SCCOL nNewCol, SCROW nNewRow, ScFilterBoxMode eNewMode)
+ : xBuilder(Application::CreateBuilder(pParent, "modules/scalc/ui/filterlist.ui"))
+ , xPopover(xBuilder->weld_popover("FilterList"))
+ , xTreeView(xBuilder->weld_tree_view("list"))
+ , pGridWin(pGrid)
+ , nCol(nNewCol)
+ , nRow(nNewRow)
+ , bInit(true)
+ , bCancelled(false)
+ , bGridHadMouseCaptured(pGrid->IsMouseCaptured())
+ , nSel(0)
+ , eMode(eNewMode)
+ , nAsyncSelectHdl(nullptr)
+{
+ xTreeView->connect_row_activated(LINK(this, ScFilterListBox, SelectHdl));
+ xTreeView->connect_key_press(LINK(this, ScFilterListBox, KeyInputHdl));
+}
+
+ScFilterListBox::~ScFilterListBox()
+{
+ if (nAsyncSelectHdl)
+ {
+ Application::RemoveUserEvent(nAsyncSelectHdl);
+ nAsyncSelectHdl = nullptr;
+ }
+}
+
+void ScFilterListBox::EndInit()
+{
+ sal_Int32 nPos = xTreeView->get_selected_index();
+ if (nPos == -1)
+ nSel = 0;
+ else
+ nSel = nPos;
+
+ bInit = false;
+}
+
+IMPL_LINK(ScFilterListBox, KeyInputHdl, const KeyEvent&, rKeyEvent, bool)
+{
+ bool bDone = false;
+
+ vcl::KeyCode aCode = rKeyEvent.GetKeyCode();
+ // esc with no modifiers
+ if (!aCode.GetModifier() && aCode.GetCode() == KEY_ESCAPE)
+ {
+ pGridWin->ClickExtern(); // clears the listbox
+ bDone = true;
+ }
+
+ // nowhere to tab to
+ if (aCode.GetCode() == KEY_TAB)
+ bDone = true;
+
+ return bDone;
+}
+
+IMPL_LINK_NOARG(ScFilterListBox, SelectHdl, weld::TreeView&, bool)
+{
+ if (!bInit && !bCancelled && !nAsyncSelectHdl)
+ {
+ int nPos = xTreeView->get_selected_index();
+ if (nPos != -1)
+ {
+ nSel = nPos;
+ // #i81298# launch async so the box isn't deleted from modifications within FilterSelect
+ nAsyncSelectHdl = Application::PostUserEvent(LINK(this, ScFilterListBox, AsyncSelectHdl));
+ }
+ }
+ return true;
+}
+
+IMPL_LINK_NOARG(ScFilterListBox, AsyncSelectHdl, void*, void)
+{
+ nAsyncSelectHdl = nullptr;
+
+ //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();
+}
+
+static bool 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() ) )
+ return false;
+
+ ScRefCellValue aCell(rDoc, rRange.aEnd);
+ ScAddress aPos;
+ return (aCell.getType() == CELLTYPE_FORMULA && aCell.getFormula()->GetMatrixOrigin(rDoc, aPos) && aPos == rRange.aStart);
+}
+
+static void lcl_UnLockComment( ScDrawView* pView, const Point& rPos, const ScViewData& rViewData )
+{
+ if (!pView)
+ return;
+
+ ScDocument& rDoc = rViewData.GetDocument();
+ ScAddress aCellPos( rViewData.GetCurX(), rViewData.GetCurY(), rViewData.GetTabNo() );
+ ScPostIt* pNote = rDoc.GetNote( aCellPos );
+ SdrObject* pObj = pNote ? pNote->GetCaption() : nullptr;
+ if( pObj && pObj->GetLogicRect().Contains( rPos ) && ScDrawLayer::IsNoteCaption( pObj ) )
+ {
+ const ScProtectionAttr* pProtAttr = rDoc.GetAttr( aCellPos, ATTR_PROTECTION );
+ bool bProtectAttr = pProtAttr->GetProtection() || pProtAttr->GetHideCell() ;
+ bool bProtectDoc = rDoc.IsTabProtected( aCellPos.Tab() ) || rViewData.GetSfxDocShell()->IsReadOnly() ;
+ // unlock internal layer (if not protected), will be relocked in ScDrawView::MarkListHasChanged()
+ pView->LockInternalLayer( bProtectDoc && bProtectAttr );
+ }
+}
+
+static bool lcl_GetHyperlinkCell(
+ ScDocument& rDoc, SCCOL& rPosX, SCROW nPosY, SCTAB nTab, ScRefCellValue& rCell, OUString& rURL )
+{
+ bool bFound = false;
+ do
+ {
+ ScAddress aPos(rPosX, nPosY, nTab);
+ rCell.assign(rDoc, aPos);
+ if (rCell.isEmpty())
+ {
+ if ( rPosX <= 0 )
+ return false; // everything empty to the links
+ else
+ --rPosX; // continue search
+ }
+ else
+ {
+ const ScPatternAttr* pPattern = rDoc.GetPattern(aPos);
+ if ( !pPattern->GetItem(ATTR_HYPERLINK).GetValue().isEmpty() )
+ {
+ rURL = pPattern->GetItem(ATTR_HYPERLINK).GetValue();
+ bFound = true;
+ }
+ else if (rCell.getType() == CELLTYPE_EDIT)
+ bFound = true;
+ else if (rCell.getType() == CELLTYPE_FORMULA && rCell.getFormula()->IsHyperLinkCell())
+ bFound = true;
+ else
+ return false; // other cell
+ }
+ }
+ while ( !bFound );
+
+ return bFound;
+}
+
+static void lcl_GetMirror(Point& rPoint, tools::Rectangle& rRect, const tools::Long nWidth)
+{
+ tools::Long nLeft = rRect.Left();
+ tools::Long nRight = rRect.Right();
+ tools::Long nMirrorPX = o3tl::convert(nWidth, o3tl::Length::twip, o3tl::Length::px);
+ tools::Long nMirrorMM = o3tl::convert(nWidth, o3tl::Length::twip, o3tl::Length::mm100);
+
+ rPoint.setX(nMirrorPX - rPoint.X());
+ rRect.SetLeft(nMirrorMM - nRight);
+ rRect.SetRight(nMirrorMM - nLeft);
+}
+
+// WB_DIALOGCONTROL needed for UNO-Controls
+ScGridWindow::ScGridWindow( vcl::Window* pParent, ScViewData& rData, ScSplitPos eWhichPos )
+: DocWindow( pParent, WB_CLIPCHILDREN | WB_DIALOGCONTROL ),
+ DropTargetHelper( this ),
+ DragSourceHelper( this ),
+ maVisibleRange(rData.GetDocument()),
+ mrViewData( rData ),
+ eWhich( eWhichPos ),
+ nCursorHideCount( 0 ),
+ nButtonDown( 0 ),
+ nMouseStatus( SC_GM_NONE ),
+ nNestedButtonState( ScNestedButtonState::NONE ),
+ nDPField( 0 ),
+ pDragDPObj( nullptr ),
+ nRFIndex( 0 ),
+ nRFAddX( 0 ),
+ nRFAddY( 0 ),
+ nPagebreakMouse( SC_PD_NONE ),
+ nPagebreakBreak( 0 ),
+ nPagebreakPrev( 0 ),
+ nPageScript( SvtScriptType::NONE ),
+ nDragStartX( -1 ),
+ nDragStartY( -1 ),
+ nDragEndX( -1 ),
+ nDragEndY( -1 ),
+ meDragInsertMode( INS_NONE ),
+ aComboButton( GetOutDev() ),
+ aCurMousePos( 0,0 ),
+ nPaintCount( 0 ),
+ aRFSelectedCorned( NONE ),
+ maShowPageBreaksTimer("ScGridWindow maShowPageBreaksTimer"),
+ bEEMouse( false ),
+ bDPMouse( false ),
+ bRFMouse( false ),
+ bRFSize( false ),
+ bPagebreakDrawn( false ),
+ bDragRect( false ),
+ bIsInPaint( false ),
+ bNeedsRepaint( false ),
+ bAutoMarkVisible( false ),
+ bListValButton( false ),
+ m_nDownPosX( -1 ),
+ m_nDownPosY( -1 )
+{
+ set_id("grid_window");
+ switch(eWhich)
+ {
+ case SC_SPLIT_TOPLEFT:
+ eHWhich = SC_SPLIT_LEFT;
+ eVWhich = SC_SPLIT_TOP;
+ break;
+ case SC_SPLIT_TOPRIGHT:
+ eHWhich = SC_SPLIT_RIGHT;
+ eVWhich = SC_SPLIT_TOP;
+ break;
+ case SC_SPLIT_BOTTOMLEFT:
+ eHWhich = SC_SPLIT_LEFT;
+ eVWhich = SC_SPLIT_BOTTOM;
+ break;
+ case SC_SPLIT_BOTTOMRIGHT:
+ eHWhich = SC_SPLIT_RIGHT;
+ eVWhich = SC_SPLIT_BOTTOM;
+ break;
+ default:
+ OSL_FAIL("GridWindow: wrong position");
+ }
+
+ SetUseFrameData(comphelper::LibreOfficeKit::isActive());
+ SetBackground();
+
+ SetMapMode(mrViewData.GetLogicMode(eWhich));
+ EnableChildTransparentMode();
+ SetDialogControlFlags( DialogControlFlags::Return | DialogControlFlags::WantFocus );
+
+ SetHelpId( HID_SC_WIN_GRIDWIN );
+
+ GetOutDev()->SetDigitLanguage( ScModule::GetOptDigitLanguage() );
+ EnableRTL( false );
+
+ bInitialPageBreaks = true;
+ maShowPageBreaksTimer.SetInvokeHandler(LINK(this, ScGridWindow, InitiatePageBreaksTimer));
+ maShowPageBreaksTimer.SetTimeout(1);
+}
+
+ScGridWindow::~ScGridWindow()
+{
+ disposeOnce();
+}
+
+void ScGridWindow::dispose()
+{
+ maShowPageBreaksTimer.Stop();
+
+ ImpDestroyOverlayObjects();
+
+ mpFilterBox.reset();
+ mpNoteMarker.reset();
+ mpAutoFilterPopup.reset();
+ mpDPFieldPopup.reset();
+ aComboButton.SetOutputDevice(nullptr);
+
+ 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 );
+ else if (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 = SC_MOD()->GetInputHdl(mrViewData.GetViewShell());
+ if (pHdl)
+ pHdl->EnterHandler();
+ }
+}
+
+namespace {
+
+struct AutoFilterData : public ScCheckListMenuControl::ExtendedData
+{
+ ScAddress maPos;
+ ScDBData* mpData;
+};
+
+class AutoFilterAction : public ScCheckListMenuControl::Action
+{
+protected:
+ VclPtr<ScGridWindow> mpWindow;
+ ScGridWindow::AutoFilterMode meMode;
+public:
+ AutoFilterAction(ScGridWindow* p, ScGridWindow::AutoFilterMode eMode) :
+ mpWindow(p), meMode(eMode) {}
+ virtual bool execute() override
+ {
+ mpWindow->UpdateAutoFilterFromMenu(meMode);
+ // UpdateAutoFilterFromMenu manually closes the popup so return
+ // false to not attempt a second close
+ return false;
+ }
+};
+
+class AutoFilterPopupEndAction : public ScCheckListMenuControl::Action
+{
+ VclPtr<ScGridWindow> mpWindow;
+ ScAddress maPos;
+public:
+ AutoFilterPopupEndAction(ScGridWindow* p, const ScAddress& rPos) :
+ mpWindow(p), maPos(rPos) {}
+ virtual bool execute() override
+ {
+ mpWindow->RefreshAutoFilterButton(maPos);
+ mpWindow->GrabFocus();
+ return false; // this is called after the popup has been closed
+ }
+};
+
+class AutoFilterSubMenuAction : public AutoFilterAction
+{
+protected:
+ ScListSubMenuControl* m_pSubMenu;
+
+public:
+ AutoFilterSubMenuAction(ScGridWindow* p, ScListSubMenuControl* pSubMenu, ScGridWindow::AutoFilterMode eMode)
+ : AutoFilterAction(p, eMode)
+ , m_pSubMenu(pSubMenu)
+ {
+ }
+};
+
+class AutoFilterColorAction : public AutoFilterSubMenuAction
+{
+private:
+ Color m_aColor;
+
+public:
+ AutoFilterColorAction(ScGridWindow* p, ScListSubMenuControl* pSubMenu, ScGridWindow::AutoFilterMode eMode, const Color& rColor)
+ : AutoFilterSubMenuAction(p, pSubMenu, eMode)
+ , m_aColor(rColor)
+ {
+ }
+
+ virtual bool execute() override
+ {
+ const AutoFilterData* pData =
+ static_cast<const AutoFilterData*>(m_pSubMenu->getExtendedData());
+
+ if (!pData)
+ return false;
+
+ ScDBData* pDBData = pData->mpData;
+ if (!pDBData)
+ return false;
+
+ const ScAddress& rPos = pData->maPos;
+
+ ScViewData& rViewData = m_pSubMenu->GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+
+ ScQueryParam aParam;
+ pDBData->GetQueryParam(aParam);
+
+ // 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 false;
+ }
+
+ if (ScTabViewShell::isAnyEditViewInRange(rViewData.GetViewShell(), /*bColumns*/ false, aParam.nRow1, aParam.nRow2))
+ return false;
+
+ pEntry->bDoQuery = true;
+ pEntry->nField = rPos.Col();
+ pEntry->eConnect = SC_AND;
+
+ ScFilterEntries aFilterEntries;
+ rDoc.GetFilterEntries(rPos.Col(), rPos.Row(), rPos.Tab(), aFilterEntries);
+
+ bool bActive = false;
+ auto aItem = pEntry->GetQueryItem();
+ if (aItem.maColor == m_aColor
+ && ((meMode == ScGridWindow::AutoFilterMode::TextColor
+ && aItem.meType == ScQueryEntry::ByTextColor)
+ || (meMode == ScGridWindow::AutoFilterMode::BackgroundColor
+ && aItem.meType == ScQueryEntry::ByBackgroundColor)))
+ {
+ bActive = true;
+ }
+
+ // Disable color filter when active color was selected
+ if (bActive)
+ {
+ aParam.RemoveAllEntriesByField(rPos.Col());
+ pEntry = nullptr; // invalidated by RemoveAllEntriesByField call
+
+ // tdf#46184 reset filter options to default values
+ aParam.eSearchType = utl::SearchParam::SearchType::Normal;
+ aParam.bCaseSens = false;
+ aParam.bDuplicate = true;
+ aParam.bInplace = true;
+ }
+ else
+ {
+ if (meMode == ScGridWindow::AutoFilterMode::TextColor)
+ pEntry->SetQueryByTextColor(m_aColor);
+ else
+ pEntry->SetQueryByBackgroundColor(m_aColor);
+ }
+
+ rViewData.GetView()->Query(aParam, nullptr, true);
+ pDBData->SetQueryParam(aParam);
+
+ return true;
+ }
+};
+
+class AutoFilterSortColorAction : public AutoFilterSubMenuAction
+{
+private:
+ Color m_aColor;
+ ScViewData& m_rViewData;
+
+public:
+ AutoFilterSortColorAction(ScGridWindow* p, ScListSubMenuControl* pSubMenu, ScGridWindow::AutoFilterMode eMode, const Color& rColor, ScViewData& rViewData)
+ : AutoFilterSubMenuAction(p, pSubMenu, eMode)
+ , m_aColor(rColor)
+ , m_rViewData(rViewData)
+ {
+ }
+
+ virtual bool execute() override
+ {
+ const AutoFilterData* pData =
+ static_cast<const AutoFilterData*>(m_pSubMenu->getExtendedData());
+
+ if (!pData)
+ return false;
+
+ ScDBData* pDBData = pData->mpData;
+ if (!pDBData)
+ return false;
+
+ const ScAddress& rPos = pData->maPos;
+ SCCOL nCol = rPos.Col();
+ ScSortParam aSortParam;
+ pDBData->GetSortParam(aSortParam);
+ if (nCol < aSortParam.nCol1 || nCol > aSortParam.nCol2)
+ // out of bound
+ return false;
+
+ bool bHasHeader = pDBData->HasHeader();
+
+ aSortParam.bHasHeader = bHasHeader;
+ aSortParam.bByRow = true;
+ aSortParam.bCaseSens = false;
+ aSortParam.bNaturalSort = false;
+ aSortParam.aDataAreaExtras.mbCellNotes = false;
+ aSortParam.aDataAreaExtras.mbCellDrawObjects = true;
+ aSortParam.aDataAreaExtras.mbCellFormats = true;
+ aSortParam.bInplace = true;
+ aSortParam.maKeyState[0].bDoSort = true;
+ aSortParam.maKeyState[0].nField = nCol;
+ aSortParam.maKeyState[0].bAscending = true;
+ aSortParam.maKeyState[0].aColorSortMode = meMode == ScGridWindow::AutoFilterMode::TextColor
+ ? ScColorSortMode::TextColor
+ : ScColorSortMode::BackgroundColor;
+ aSortParam.maKeyState[0].aColorSortColor = m_aColor;
+
+ for (size_t i = 1; i < aSortParam.GetSortKeyCount(); ++i)
+ aSortParam.maKeyState[i].bDoSort = false;
+
+ m_rViewData.GetViewShell()->UISort(aSortParam);
+
+ return true;
+ }
+};
+
+class AutoFilterColorPopupStartAction : public AutoFilterSubMenuAction
+{
+private:
+ bool mbIsFilter;
+public:
+ AutoFilterColorPopupStartAction(ScGridWindow* p, ScListSubMenuControl* pSubMenu, bool bIsFilter)
+ : AutoFilterSubMenuAction(p, pSubMenu, ScGridWindow::AutoFilterMode::Normal),
+ mbIsFilter(bIsFilter)
+ {
+ }
+
+ virtual bool execute() override
+ {
+ const AutoFilterData* pData =
+ static_cast<const AutoFilterData*>(m_pSubMenu->getExtendedData());
+
+ if (!pData)
+ return false;
+
+ ScDBData* pDBData = pData->mpData;
+ if (!pDBData)
+ return false;
+
+ ScViewData& rViewData = m_pSubMenu->GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ const ScAddress& rPos = pData->maPos;
+
+ ScFilterEntries aFilterEntries;
+ rDoc.GetFilterEntries(rPos.Col(), rPos.Row(), rPos.Tab(), aFilterEntries);
+
+ m_pSubMenu->clearMenuItems();
+
+ XColorListRef xUserColorList;
+
+ OUString aPaletteName(officecfg::Office::Common::UserColors::PaletteName::get());
+ PaletteManager aPaletteManager;
+ std::vector<OUString> aPaletteNames = aPaletteManager.GetPaletteList();
+ for (size_t i = 0, nLen = aPaletteNames.size(); i < nLen; ++i)
+ {
+ if (aPaletteName == aPaletteNames[i])
+ {
+ aPaletteManager.SetPalette(i);
+ xUserColorList = XPropertyList::AsColorList(
+ XPropertyList::CreatePropertyListFromURL(
+ XPropertyListType::Color, aPaletteManager.GetSelectedPalettePath()));
+ if (!xUserColorList->Load())
+ xUserColorList = nullptr;
+ break;
+ }
+ }
+
+ ScQueryParam aParam;
+ pDBData->GetQueryParam(aParam);
+ ScQueryEntry* pEntry = aParam.FindEntryByField(rPos.Col(), true);
+
+ int nMenu = 0;
+ for (auto eMode : {ScGridWindow::AutoFilterMode::BackgroundColor, ScGridWindow::AutoFilterMode::TextColor})
+ {
+ std::set<Color> aColors = eMode == ScGridWindow::AutoFilterMode::TextColor
+ ? aFilterEntries.getTextColors()
+ : aFilterEntries.getBackgroundColors();
+
+ for (auto& rColor : aColors)
+ {
+ bool bActive = false;
+
+ if (pEntry)
+ {
+ auto aItem = pEntry->GetQueryItem();
+ if (aItem.maColor == rColor
+ && ((eMode == ScGridWindow::AutoFilterMode::TextColor
+ && aItem.meType == ScQueryEntry::ByTextColor)
+ || (eMode == ScGridWindow::AutoFilterMode::BackgroundColor
+ && aItem.meType == ScQueryEntry::ByBackgroundColor)))
+ {
+ bActive = true;
+ }
+ }
+
+ const bool bAutoColor = rColor == COL_AUTO;
+
+ // ColorListBox::ShowPreview is similar
+ ScopedVclPtr<VirtualDevice> xDev(m_pSubMenu->create_virtual_device());
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ Size aImageSize(rStyleSettings.GetListBoxPreviewDefaultPixelSize());
+ xDev->SetOutputSize(aImageSize);
+ const tools::Rectangle aRect(Point(0, 0), aImageSize);
+
+ if (bAutoColor)
+ {
+ const Color aW(COL_WHITE);
+ const Color aG(0xef, 0xef, 0xef);
+ int nMinDim = std::min(aImageSize.Width(), aImageSize.Height()) + 1;
+ int nCheckSize = nMinDim / 3;
+ xDev->DrawCheckered(aRect.TopLeft(), aRect.GetSize(), std::min(nCheckSize, 8), aW, aG);
+ xDev->SetFillColor();
+ }
+ else
+ xDev->SetFillColor(rColor);
+
+ xDev->SetLineColor(rStyleSettings.GetDisableColor());
+ xDev->DrawRect(aRect);
+
+ if (bAutoColor)
+ {
+ OUString sText = eMode == ScGridWindow::AutoFilterMode::TextColor
+ ? ScResId(SCSTR_FILTER_AUTOMATIC_COLOR)
+ : ScResId(SCSTR_FILTER_NO_FILL);
+ if (mbIsFilter)
+ {
+ m_pSubMenu->addMenuColorItem(
+ sText, bActive, *xDev, nMenu,
+ new AutoFilterColorAction(mpWindow, m_pSubMenu, eMode, rColor));
+ }
+ else
+ {
+ m_pSubMenu->addMenuColorItem(
+ sText, bActive, *xDev, nMenu,
+ new AutoFilterSortColorAction(mpWindow, m_pSubMenu, eMode, rColor, rViewData));
+ }
+ }
+ else
+ {
+ OUString sName;
+
+ bool bFoundColorName = false;
+ if (xUserColorList)
+ {
+ sal_Int32 nPos = xUserColorList->GetIndexOfColor(rColor);
+ if (nPos != -1)
+ {
+ XColorEntry* pColorEntry = xUserColorList->GetColor(nPos);
+ sName = pColorEntry->GetName();
+ bFoundColorName = true;
+ }
+ }
+ if (!bFoundColorName)
+ sName = "#" + rColor.AsRGBHexString().toAsciiUpperCase();
+
+ if (mbIsFilter)
+ {
+ m_pSubMenu->addMenuColorItem(
+ sName, bActive, *xDev, nMenu,
+ new AutoFilterColorAction(mpWindow, m_pSubMenu, eMode, rColor));
+ }
+ else
+ {
+ m_pSubMenu->addMenuColorItem(
+ sName, bActive, *xDev, nMenu,
+ new AutoFilterSortColorAction(mpWindow, m_pSubMenu, eMode, rColor,
+ rViewData));
+ }
+ }
+ }
+
+ ++nMenu;
+ }
+
+ m_pSubMenu->resizeToFitMenuItems();
+
+ return false;
+ }
+};
+
+class AddItemToEntry
+{
+ ScQueryEntry::QueryItemsType& mrItems;
+ svl::SharedStringPool& mrPool;
+public:
+ AddItemToEntry(ScQueryEntry::QueryItemsType& rItems, svl::SharedStringPool& rPool) :
+ mrItems(rItems), mrPool(rPool) {}
+ void operator() (const ScCheckListMenuControl::ResultEntry& rEntry)
+ {
+ if (rEntry.bValid)
+ {
+ ScQueryEntry::Item aNew;
+ aNew.maString = mrPool.intern(rEntry.aName);
+ // set the filter type to ByValue, if the filter condition is value
+ aNew.meType = rEntry.bDate ? ScQueryEntry::ByDate : rEntry.bValue ? ScQueryEntry::ByValue : ScQueryEntry::ByString;
+ aNew.mfVal = rEntry.nValue;
+ mrItems.push_back(aNew);
+ }
+ }
+};
+
+class AddSelectedItemString
+{
+ std::unordered_set<OUString>& mrSetString;
+ std::unordered_set<double>& mrSetValue;
+public:
+ explicit AddSelectedItemString(std::unordered_set<OUString>& rString, std::unordered_set<double>& rValue) :
+ mrSetString(rString), mrSetValue(rValue) {}
+
+ void operator() (const ScQueryEntry::Item& rItem)
+ {
+ if( rItem.meType == ScQueryEntry::QueryType::ByValue )
+ mrSetValue.insert(rItem.mfVal);
+ else
+ mrSetString.insert(rItem.maString.getString());
+ }
+};
+
+void collectUIInformation(const OUString& aRow, const OUString& aCol , const OUString& aevent)
+{
+ EventDescription aDescription;
+ aDescription.aAction = "LAUNCH";
+ aDescription.aID = "grid_window";
+ aDescription.aParameters = {{aevent, ""},
+ {"ROW", aRow}, {"COL", aCol}};
+ aDescription.aParent = "MainWindow";
+ aDescription.aKeyWord = "ScGridWinUIObject";
+
+ UITestLogger::getInstance().logEvent(aDescription);
+}
+
+}
+
+void ScGridWindow::LaunchAutoFilterMenu(SCCOL nCol, SCROW nRow)
+{
+ SCTAB nTab = mrViewData.GetTabNo();
+ ScDocument& rDoc = mrViewData.GetDocument();
+ bool bLOKActive = comphelper::LibreOfficeKit::isActive();
+
+ mpAutoFilterPopup.reset();
+
+ // Estimate the width (in pixels) of the longest text in the list
+ ScFilterEntries aFilterEntries;
+ rDoc.GetFilterEntries(nCol, nRow, nTab, aFilterEntries);
+
+ weld::Window* pPopupParent = GetFrameWeld();
+ int nColWidth = ScViewData::ToPixel(rDoc.GetColWidth(nCol, nTab), mrViewData.GetPPTX());
+ mpAutoFilterPopup.reset(new ScCheckListMenuControl(pPopupParent, mrViewData,
+ aFilterEntries.mbHasDates, nColWidth));
+
+ int nMaxTextWidth = 0;
+ if (aFilterEntries.size() <= 10)
+ {
+ // do pixel calculation for all elements of short lists
+ for (const auto& 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);
+
+ mpAutoFilterPopup->setOKAction(new AutoFilterAction(this, AutoFilterMode::Normal));
+ mpAutoFilterPopup->setPopupEndAction(
+ new AutoFilterPopupEndAction(this, ScAddress(nCol, nRow, nTab)));
+ std::unique_ptr<AutoFilterData> pData(new AutoFilterData);
+ pData->maPos = ScAddress(nCol, nRow, nTab);
+
+ Point aPos = mrViewData.GetScrPos(nCol, nRow, eWhich);
+ tools::Long nSizeX = 0;
+ tools::Long nSizeY = 0;
+ mrViewData.GetMergeSizePixel(nCol, nRow, nSizeX, nSizeY);
+ if (bLOKActive)
+ {
+ // Reverse the zoom factor from aPos and nSize[X|Y]
+ // before letting the autofilter window convert the to twips
+ // with no zoom information.
+ double fZoomX(mrViewData.GetZoomX());
+ double fZoomY(mrViewData.GetZoomY());
+ aPos.setX(aPos.getX() / fZoomX);
+ aPos.setY(aPos.getY() / fZoomY);
+ nSizeX = nSizeX / fZoomX;
+ nSizeY = nSizeY / fZoomY;
+ }
+ tools::Rectangle aCellRect(bLOKActive ? aPos : OutputToScreenPixel(aPos), Size(nSizeX, nSizeY));
+
+ ScDBData* pDBData = rDoc.GetDBAtCursor(nCol, nRow, nTab, ScDBDataPortion::AREA);
+ if (!pDBData)
+ return;
+
+ pDBData->ExtendBackColorArea(rDoc);
+ pData->mpData = pDBData;
+ mpAutoFilterPopup->setExtendedData(std::move(pData));
+
+ ScQueryParam aParam;
+ pDBData->GetQueryParam(aParam);
+ std::vector<ScQueryEntry*> aEntries = aParam.FindAllEntriesByField(nCol);
+ std::unordered_set<OUString> aSelectedString;
+ std::unordered_set<double> aSelectedValue;
+ bool bQueryByNonEmpty = aEntries.size() == 1 && aEntries[0]->IsQueryByNonEmpty();
+
+ 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 (it->GetString().isEmpty())
+ {
+ const OUString& aStringVal = it->GetString();
+ const double aDoubleVal = it->GetValue();
+ bool bSelected = true;
+ if (!aSelectedValue.empty() || !aSelectedString.empty())
+ bSelected = aSelectedString.count(aStringVal) > 0;
+ else if (bQueryByNonEmpty)
+ bSelected = false;
+ mpAutoFilterPopup->addMember(aStringVal, aDoubleVal, bSelected, it->IsHiddenByFilter());
+ aFilterEntries.maStrData.erase(it);
+ break;
+ }
+ }
+ for (const auto& rEntry : aFilterEntries)
+ {
+ const OUString& aStringVal = rEntry.GetString();
+ const double aDoubleVal = rEntry.GetValue();
+ const double aRDoubleVal = rEntry.GetRoundedValue();
+ bool bSelected = !rEntry.IsHiddenByFilter();
+
+ if (!aSelectedValue.empty() || !aSelectedString.empty())
+ {
+ if (rEntry.GetStringType() == ScTypedStrData::Value)
+ {
+ if (aDoubleVal == aRDoubleVal)
+ bSelected = aSelectedValue.count(aDoubleVal) > 0
+ || aSelectedString.count(aStringVal) > 0;
+ else
+ bSelected = aSelectedValue.count(aDoubleVal) > 0
+ || aSelectedValue.count(aRDoubleVal) > 0
+ || aSelectedString.count(aStringVal) > 0;
+ }
+ else
+ bSelected = aSelectedString.count(aStringVal) > 0;
+ }
+
+ if ( rEntry.IsDate() )
+ mpAutoFilterPopup->addDateMember( aStringVal, rEntry.GetValue(), bSelected, rEntry.IsHiddenByFilter());
+ else
+ mpAutoFilterPopup->addMember( aStringVal, aRDoubleVal, bSelected, rEntry.IsHiddenByFilter(),
+ rEntry.GetStringType() == ScTypedStrData::Value );
+ }
+
+ // Populate the menu.
+ mpAutoFilterPopup->addMenuItem(
+ ScResId(STR_MENU_SORT_ASC),
+ new AutoFilterAction(this, AutoFilterMode::SortAscending));
+ mpAutoFilterPopup->addMenuItem(
+ ScResId(STR_MENU_SORT_DESC),
+ new AutoFilterAction(this, AutoFilterMode::SortDescending));
+ if (ScListSubMenuControl* pSubMenu = mpAutoFilterPopup->addSubMenuItem(ScResId(SCSTR_SORT_COLOR), true, true))
+ pSubMenu->setPopupStartAction(new AutoFilterColorPopupStartAction(this, pSubMenu, false));
+ mpAutoFilterPopup->addSeparator();
+ if (ScListSubMenuControl* pSubMenu = mpAutoFilterPopup->addSubMenuItem(ScResId(SCSTR_FILTER_COLOR), true, true))
+ pSubMenu->setPopupStartAction(new AutoFilterColorPopupStartAction(this, pSubMenu, true));
+ if (ScListSubMenuControl* pSubMenu = mpAutoFilterPopup->addSubMenuItem(ScResId(SCSTR_FILTER_CONDITION), true, false))
+ {
+ pSubMenu->addMenuItem(
+ ScResId(SCSTR_FILTER_EMPTY), new AutoFilterAction(this, AutoFilterMode::Empty));
+ pSubMenu->addMenuItem(
+ ScResId(SCSTR_FILTER_NOTEMPTY), new AutoFilterAction(this, AutoFilterMode::NonEmpty));
+ pSubMenu->addMenuItem(
+ ScResId(SCSTR_TOP10FILTER), new AutoFilterAction(this, AutoFilterMode::Top10));
+ pSubMenu->addMenuItem(
+ ScResId(SCSTR_BOTTOM10FILTER), new AutoFilterAction(this, AutoFilterMode::Bottom10));
+ pSubMenu->addSeparator();
+ pSubMenu->addMenuItem(
+ ScResId(SCSTR_STDFILTER), new AutoFilterAction(this, AutoFilterMode::Custom));
+ pSubMenu->resizeToFitMenuItems();
+ }
+ if (aEntries.size())
+ mpAutoFilterPopup->addMenuItem(
+ ScResId(SCSTR_CLEAR_FILTER), new AutoFilterAction(this, AutoFilterMode::Clear));
+
+ mpAutoFilterPopup->initMembers(nMaxTextWidth + 20); // 20 pixel estimated for the checkbox
+
+ ScCheckListMenuControl::Config aConfig;
+ aConfig.mbAllowEmptySet = false;
+ aConfig.mbRTL = mrViewData.GetDocument().IsLayoutRTL(mrViewData.GetTabNo());
+ mpAutoFilterPopup->setConfig(aConfig);
+ if (IsMouseCaptured())
+ ReleaseMouse();
+ mpAutoFilterPopup->launch(pPopupParent, aCellRect);
+
+ // remember filter rules before modification
+ mpAutoFilterPopup->getResult(aSaveAutoFilterResult);
+
+ collectUIInformation(OUString::number(nRow), OUString::number(nCol),"AUTOFILTER");
+}
+
+void ScGridWindow::RefreshAutoFilterButton(const ScAddress& rPos)
+{
+ if (mpFilterButton)
+ {
+ bool bFilterActive = IsAutoFilterActive(rPos.Col(), rPos.Row(), rPos.Tab());
+ mpFilterButton->setHasHiddenMember(bFilterActive);
+ mpFilterButton->setPopupPressed(false);
+ mpFilterButton->draw();
+ }
+}
+
+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();
+
+ const AutoFilterData* pData =
+ static_cast<const AutoFilterData*>(mpAutoFilterPopup->getExtendedData());
+
+ if (!pData)
+ return;
+
+ const ScAddress& rPos = pData->maPos;
+ ScDBData* pDBData = pData->mpData;
+ if (!pDBData)
+ return;
+
+ ScDocument& rDoc = mrViewData.GetDocument();
+ svl::SharedStringPool& rPool = rDoc.GetSharedStringPool();
+ switch (eMode)
+ {
+ case AutoFilterMode::SortAscending:
+ case AutoFilterMode::SortDescending:
+ {
+ SCCOL nCol = rPos.Col();
+ ScSortParam aSortParam;
+ pDBData->GetSortParam(aSortParam);
+ if (nCol < aSortParam.nCol1 || nCol > aSortParam.nCol2)
+ // out of bound
+ return;
+
+ bool bHasHeader = pDBData->HasHeader();
+
+ aSortParam.bHasHeader = bHasHeader;
+ aSortParam.bByRow = true;
+ aSortParam.bCaseSens = false;
+ aSortParam.bNaturalSort = false;
+ aSortParam.aDataAreaExtras.mbCellNotes = false;
+ aSortParam.aDataAreaExtras.mbCellDrawObjects = true;
+ aSortParam.aDataAreaExtras.mbCellFormats = true;
+ aSortParam.bInplace = true;
+ aSortParam.maKeyState[0].bDoSort = true;
+ aSortParam.maKeyState[0].nField = nCol;
+ aSortParam.maKeyState[0].bAscending = (eMode == AutoFilterMode::SortAscending);
+ aSortParam.maKeyState[0].aColorSortMode = ScColorSortMode::None;
+
+ for (size_t i = 1; i < aSortParam.GetSortKeyCount(); ++i)
+ aSortParam.maKeyState[i].bDoSort = false;
+
+ mrViewData.GetViewShell()->UISort(aSortParam);
+ return;
+ }
+ case AutoFilterMode::Custom:
+ {
+ ScRange aRange;
+ pDBData->GetArea(aRange);
+ mrViewData.GetView()->MarkRange(aRange);
+ mrViewData.GetView()->SetCursor(rPos.Col(), rPos.Row());
+ mrViewData.GetDispatcher().Execute(SID_FILTER, SfxCallMode::SLOT | SfxCallMode::RECORD);
+ return;
+ }
+ default:
+ ;
+ }
+
+ ScQueryParam aParam;
+ pDBData->GetQueryParam(aParam);
+
+ 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());
+
+ // tdf#46184 reset filter options to default values
+ aParam.eSearchType = utl::SearchParam::SearchType::Normal;
+ aParam.bCaseSens = false;
+ aParam.bDuplicate = true;
+ aParam.bInplace = true;
+ }
+
+ 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;
+
+ pEntry->bDoQuery = true;
+ pEntry->nField = rPos.Col();
+ pEntry->eConnect = SC_AND;
+
+ switch (eMode)
+ {
+ case AutoFilterMode::Normal:
+ {
+ pEntry->eOp = SC_EQUAL;
+
+ ScCheckListMenuControl::ResultType aResult;
+ mpAutoFilterPopup->getResult(aResult);
+
+ ScQueryEntry::QueryItemsType& rItems = pEntry->GetQueryItems();
+ rItems.clear();
+ std::for_each(aResult.begin(), aResult.end(), AddItemToEntry(rItems, rPool));
+ }
+ break;
+ case AutoFilterMode::Top10:
+ pEntry->eOp = SC_TOPVAL;
+ pEntry->GetQueryItem().meType = ScQueryEntry::ByString;
+ pEntry->GetQueryItem().maString = rPool.intern("10");
+ break;
+ case AutoFilterMode::Bottom10:
+ pEntry->eOp = SC_BOTVAL;
+ pEntry->GetQueryItem().meType = ScQueryEntry::ByString;
+ pEntry->GetQueryItem().maString = rPool.intern("10");
+ break;
+ case AutoFilterMode::Empty:
+ pEntry->SetQueryByEmpty();
+ break;
+ case AutoFilterMode::NonEmpty:
+ pEntry->SetQueryByNonEmpty();
+ break;
+ case AutoFilterMode::TextColor:
+ case AutoFilterMode::BackgroundColor:
+ assert(false && "should be handled by AutoFilterColorAction::execute");
+ break;
+ break;
+ default:
+ // We don't know how to handle this!
+ return;
+ }
+ }
+
+ mrViewData.GetView()->Query(aParam, nullptr, true);
+ pDBData->SetQueryParam(aParam);
+}
+
+namespace {
+
+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;
+
+ SCTAB nTab = mrViewData.GetTabNo();
+ ScDPObject* pDPObj = mrViewData.GetDocument().GetDPAtCursor(nCol, nRow, nTab);
+ if (!pDPObj)
+ return;
+
+ Point aScrPos;
+ Size aScrSize;
+ getCellGeometry(aScrPos, aScrSize, mrViewData, nCol, nRow, eWhich);
+ bool bLOK = comphelper::LibreOfficeKit::isActive();
+ DPLaunchFieldPopupMenu(bLOK ? aScrPos : OutputToScreenPixel(aScrPos), aScrSize, ScAddress(nCol-1, nRow, nTab), pDPObj);
+}
+
+void ScGridWindow::LaunchDPFieldMenu( SCCOL nCol, SCROW nRow )
+{
+ SCTAB nTab = mrViewData.GetTabNo();
+ ScDPObject* pDPObj = mrViewData.GetDocument().GetDPAtCursor(nCol, nRow, nTab);
+ if (!pDPObj)
+ return;
+
+ Point aScrPos;
+ Size aScrSize;
+ getCellGeometry(aScrPos, aScrSize, mrViewData, nCol, nRow, eWhich);
+ bool bLOK = comphelper::LibreOfficeKit::isActive();
+ DPLaunchFieldPopupMenu(bLOK ? aScrPos : OutputToScreenPixel(aScrPos), aScrSize, ScAddress(nCol, nRow, nTab), pDPObj);
+}
+
+void ScGridWindow::ShowFilterMenu(weld::Window* pParent, const tools::Rectangle& rCellRect, bool bLayoutRTL)
+{
+ auto nSizeX = rCellRect.GetWidth();
+
+ // 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
+
+ if (!bLayoutRTL && aSize.Width() > nSizeX)
+ {
+ // move popup position
+ tools::Long nDiff = aSize.Width() - nSizeX;
+ tools::Long nNewX = aCellRect.Left() - nDiff;
+ if ( nNewX < 0 )
+ nNewX = 0;
+ aCellRect.SetLeft( nNewX );
+ }
+
+ rFilterBox.set_size_request(aSize.Width(), aSize.Height());
+
+ if (IsMouseCaptured())
+ ReleaseMouse();
+ mpFilterBox->popup_at_rect(pParent, aCellRect);
+}
+
+void ScGridWindow::DoScenarioMenu( const ScRange& rScenRange )
+{
+ bool bMenuAtTop = true;
+
+ ScDocument& rDoc = mrViewData.GetDocument();
+ mpFilterBox.reset();
+
+ 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;
+ }
+
+ SCTAB nTab = mrViewData.GetTabNo();
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+
+ tools::Long nSizeX = 0;
+ tools::Long nSizeY = 0;
+ mrViewData.GetMergeSizePixel( nCol, nRow, nSizeX, nSizeY );
+ // The button height should not use the merged cell height, should still use single row height
+ nSizeY = ScViewData::ToPixel(rDoc.GetRowHeight(nRow, nTab), mrViewData.GetPPTY());
+ Point aPos = mrViewData.GetScrPos( nCol, nRow, eWhich );
+ if ( bLayoutRTL )
+ aPos.AdjustX( -nSizeX );
+ tools::Rectangle aCellRect(aPos, Size(nSizeX, nSizeY));
+ aCellRect.AdjustTop( -nSizeY );
+ aCellRect.AdjustBottom( -(nSizeY - 1) );
+ if (!bMenuAtTop)
+ {
+ Size aButSize = mrViewData.GetScenButSize();
+ aCellRect.AdjustBottom(aButSize.Height());
+ }
+
+ // Place the ListBox directly below the black line of the cell grid
+ // (It looks odd if the line gets hidden...)
+
+ weld::Window* pParent = weld::GetPopupParent(*this, aCellRect);
+ mpFilterBox = std::make_shared<ScFilterListBox>(pParent, this, nCol, nRow, ScFilterBoxMode::Scenario);
+ mpFilterBox->connect_closed(LINK(this, ScGridWindow, PopupModeEndHdl));
+ weld::TreeView& rFilterBox = mpFilterBox->get_widget();
+ rFilterBox.set_direction(bLayoutRTL); // Fix for bug fdo#44925 use sheet direction for widget RTL/LTR
+
+ // Listbox fill
+ rFilterBox.freeze();
+ OUString aCurrent;
+ OUString aTabName;
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (SCTAB i=nTab+1; i<nTabCount && rDoc.IsScenario(i); i++)
+ {
+ if (rDoc.HasScenarioRange( i, rScenRange ))
+ if (rDoc.GetName( i, aTabName ))
+ {
+ rFilterBox.append_text(aTabName);
+ if (rDoc.IsActiveScenario(i))
+ aCurrent = aTabName;
+ }
+ }
+ rFilterBox.thaw();
+
+ ShowFilterMenu(pParent, aCellRect, bLayoutRTL);
+
+ rFilterBox.grab_focus();
+
+ sal_Int32 nPos = -1;
+ if (!aCurrent.isEmpty())
+ {
+ nPos = rFilterBox.find_text(aCurrent);
+ }
+ if (nPos == -1 && rFilterBox.n_children() > 0 )
+ {
+ nPos = 0;
+ }
+ if (nPos != -1)
+ {
+ rFilterBox.set_cursor(nPos);
+ rFilterBox.select(nPos);
+ }
+ mpFilterBox->EndInit();
+}
+
+void ScGridWindow::LaunchDataSelectMenu(const SCCOL nCol, const SCROW nRow)
+{
+ mpFilterBox.reset();
+
+ ScDocument& rDoc = mrViewData.GetDocument();
+ const SCTAB nTab = mrViewData.GetTabNo();
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+
+ tools::Long nSizeX = 0;
+ tools::Long nSizeY = 0;
+ mrViewData.GetMergeSizePixel( nCol, nRow, nSizeX, nSizeY );
+ Point aPos = mrViewData.GetScrPos( nCol, nRow, eWhich );
+ bool bLOKActive = comphelper::LibreOfficeKit::isActive();
+
+ 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 ( bLayoutRTL )
+ aPos.AdjustX( -nSizeX );
+ tools::Rectangle aCellRect(aPos, Size(nSizeX, nSizeY));
+
+ weld::Window* pParent = comphelper::LibreOfficeKit::isActive() ? GetFrameWeld() : weld::GetPopupParent(*this, aCellRect);
+ mpFilterBox = std::make_shared<ScFilterListBox>(pParent, this, nCol, nRow, ScFilterBoxMode::DataSelect);
+ mpFilterBox->connect_closed(LINK(this, ScGridWindow, PopupModeEndHdl));
+ weld::TreeView& rFilterBox = mpFilterBox->get_widget();
+ rFilterBox.set_direction(bLayoutRTL); // Fix for bug fdo#44925 use sheet direction for widget RTL/LTR
+
+ // SetSize later
+
+ const sal_uInt32 nIndex = rDoc.GetAttr(nCol, nRow, nTab, ATTR_VALIDDATA)->GetValue();
+ const ScValidationData* pData = nIndex ? rDoc.GetValidationEntry(nIndex) : nullptr;
+
+ bool bEmpty = false;
+ std::vector<ScTypedStrData> aStrings; // case sensitive
+ // Fill List
+ rDoc.GetDataEntries(nCol, nRow, nTab, aStrings, true /* bValidation */);
+
+ // IsIgnoreBlank allows blank values. Don't add empty string unless "Allow Empty Cells"
+ if (pData && !pData->IsIgnoreBlank())
+ {
+ auto lambda = [](const ScTypedStrData& rStr) { return rStr.GetString().isEmpty(); };
+ std::erase_if(aStrings, lambda);
+ }
+
+ if (aStrings.empty())
+ bEmpty = true;
+
+ if (!bEmpty)
+ {
+ rFilterBox.freeze();
+
+ // Fill Listbox
+ bool bWait = aStrings.size() > 100;
+
+ if (bWait)
+ EnterWait();
+
+ for (const auto& rString : aStrings)
+ {
+ const OUString& rFilterString = rString.GetString();
+ rFilterBox.append_text(rFilterString);
+ }
+
+ if (bWait)
+ LeaveWait();
+
+ rFilterBox.thaw();
+
+ ShowFilterMenu(pParent, aCellRect, bLayoutRTL);
+ }
+
+ sal_Int32 nSelPos = -1;
+
+ if ( nIndex )
+ {
+ if (pData)
+ {
+ std::unique_ptr<ScTypedStrData> pNew;
+ OUString aDocStr = rDoc.GetString(nCol, nRow, nTab);
+ if ( rDoc.HasValueData( nCol, nRow, nTab ) )
+ {
+ double fVal = rDoc.GetValue(ScAddress(nCol, nRow, nTab));
+ pNew.reset(new ScTypedStrData(aDocStr, fVal, fVal, ScTypedStrData::Value));
+ }
+ else
+ pNew.reset(new ScTypedStrData(aDocStr, 0.0, 0.0, ScTypedStrData::Standard));
+
+ 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();
+
+ mpFilterBox->EndInit();
+ }
+ collectUIInformation(OUString::number(nRow), OUString::number(nCol),"SELECTMENU");
+}
+
+void ScGridWindow::FilterSelect( sal_uLong nSel )
+{
+ weld::TreeView& rFilterBox = mpFilterBox->get_widget();
+ OUString aString = rFilterBox.get_text(static_cast<sal_Int32>(nSel));
+
+ SCCOL nCol = mpFilterBox->GetCol();
+ SCROW nRow = mpFilterBox->GetRow();
+ switch (mpFilterBox->GetMode())
+ {
+ case ScFilterBoxMode::DataSelect:
+ ExecDataSelect(nCol, nRow, aString);
+ break;
+ case ScFilterBoxMode::Scenario:
+ mrViewData.GetView()->UseScenario(aString);
+ break;
+ }
+
+ // coverity[check_after_deref] - could be destroyed by ExecDataSelect
+ if (mpFilterBox)
+ mpFilterBox->popdown();
+
+ GrabFocus(); // Otherwise the focus would be wrong on OS/2
+}
+
+void ScGridWindow::ExecDataSelect( SCCOL nCol, SCROW nRow, const OUString& rStr )
+{
+ ScModule* pScMod = SC_MOD();
+ ScInputHandler* pViewHdl = pScMod->GetInputHdl(mrViewData.GetViewShell());
+ if (pViewHdl && mrViewData.HasEditView(mrViewData.GetActivePart()))
+ pViewHdl->CancelHandler();
+
+ SCTAB nTab = mrViewData.GetTabNo();
+ ScViewFunc* pView = mrViewData.GetView();
+ pView->EnterData( nCol, nRow, nTab, rStr );
+
+ // #i52307# CellContentChanged is not in EnterData so it isn't called twice
+ // if the cursor is moved afterwards.
+ pView->CellContentChanged();
+}
+
+void ScGridWindow::MoveMouseStatus( ScGridWindow& rDestWin )
+{
+ if (nButtonDown)
+ {
+ rDestWin.nButtonDown = nButtonDown;
+ rDestWin.nMouseStatus = nMouseStatus;
+ }
+
+ if (bRFMouse)
+ {
+ rDestWin.bRFMouse = bRFMouse;
+ rDestWin.bRFSize = bRFSize;
+ rDestWin.nRFIndex = nRFIndex;
+ rDestWin.nRFAddX = nRFAddX;
+ rDestWin.nRFAddY = nRFAddY;
+ bRFMouse = false;
+ }
+
+ if (nPagebreakMouse)
+ {
+ rDestWin.nPagebreakMouse = nPagebreakMouse;
+ rDestWin.nPagebreakBreak = nPagebreakBreak;
+ rDestWin.nPagebreakPrev = nPagebreakPrev;
+ rDestWin.aPagebreakSource = aPagebreakSource;
+ rDestWin.aPagebreakDrag = aPagebreakDrag;
+ nPagebreakMouse = SC_PD_NONE;
+ }
+}
+
+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
+
+ if ( bAction && !rMEvt.IsLeft() )
+ return false;
+
+ bool bNewPointer = false;
+
+ SfxInPlaceClient* pClient = mrViewData.GetViewShell()->GetIPClient();
+ bool bOleActive = ( pClient && pClient->IsObjectInPlaceActive() );
+
+ if ( mrViewData.IsActive() && !bOleActive )
+ {
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCTAB nTab = mrViewData.GetTabNo();
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+
+ // Auto-Fill
+
+ ScRange aMarkRange;
+ if (mrViewData.GetSimpleArea( aMarkRange ) == SC_MARK_SIMPLE)
+ {
+ if (aMarkRange.aStart.Tab() == mrViewData.GetTabNo() && mpAutoFillRect)
+ {
+ Point aMousePos = rMEvt.GetPosPixel();
+ if (mpAutoFillRect->Contains(aMousePos))
+ {
+ SetPointer( PointerStyle::Cross ); //! bold cross ?
+ if (bAction)
+ {
+ SCCOL nX = aMarkRange.aEnd.Col();
+ SCROW nY = aMarkRange.aEnd.Row();
+
+ if ( lcl_IsEditableMatrix( mrViewData.GetDocument(), aMarkRange ) )
+ mrViewData.SetDragMode(
+ aMarkRange.aStart.Col(), aMarkRange.aStart.Row(), nX, nY, ScFillMode::MATRIX );
+ else
+ mrViewData.SetFillMode(
+ aMarkRange.aStart.Col(), aMarkRange.aStart.Row(), nX, nY );
+
+ // 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;
+ }
+ }
+ }
+
+ // Embedded rectangle
+
+ if (rDoc.IsEmbedded())
+ {
+ ScRange aRange;
+ rDoc.GetEmbedded( aRange );
+ if ( mrViewData.GetTabNo() == aRange.aStart.Tab() )
+ {
+ Point aStartPos = mrViewData.GetScrPos( aRange.aStart.Col(), aRange.aStart.Row(), eWhich );
+ Point aEndPos = mrViewData.GetScrPos( aRange.aEnd.Col()+1, aRange.aEnd.Row()+1, eWhich );
+ Point aMousePos = rMEvt.GetPosPixel();
+ if ( bLayoutRTL )
+ {
+ aStartPos.AdjustX(2 );
+ aEndPos.AdjustX(2 );
+ }
+ bool bTop = ( aMousePos.X() >= aStartPos.X()-3 && aMousePos.X() <= aStartPos.X()+1 &&
+ aMousePos.Y() >= aStartPos.Y()-3 && aMousePos.Y() <= aStartPos.Y()+1 );
+ bool bBottom = ( aMousePos.X() >= aEndPos.X()-3 && aMousePos.X() <= aEndPos.X()+1 &&
+ aMousePos.Y() >= aEndPos.Y()-3 && aMousePos.Y() <= aEndPos.Y()+1 );
+ if ( bTop || bBottom )
+ {
+ SetPointer( PointerStyle::Cross );
+ if (bAction)
+ {
+ ScFillMode nMode = bTop ? ScFillMode::EMBED_LT : ScFillMode::EMBED_RB;
+ mrViewData.SetDragMode(
+ aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row(), nMode );
+ }
+ bNewPointer = true;
+ }
+ }
+ }
+ }
+
+ if (!bNewPointer && bAction)
+ {
+ mrViewData.ResetFillMode();
+ }
+
+ return bNewPointer;
+}
+
+void ScGridWindow::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ if (SfxLokHelper::getDeviceFormFactor() == LOKDeviceFormFactor::MOBILE)
+ {
+ ScViewFunc* pView = mrViewData.GetView();
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+ bool bRefMode = pViewShell && pViewShell->IsRefInputMode();
+
+ Point aPos(rMEvt.GetPosPixel());
+ SCCOL nPosX;
+ SCROW nPosY;
+ mrViewData.GetPosFromPixel(aPos.X(), aPos.Y(), eWhich, nPosX, nPosY);
+
+ 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
+
+ HideNoteMarker();
+
+ bEEMouse = false;
+
+ ScModule* pScMod = SC_MOD();
+ if (pScMod->IsModalMode(mrViewData.GetSfxDocShell()))
+ return;
+
+ const bool 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
+
+ bool bDetective = mrViewData.GetViewShell()->IsAuditShell();
+ bool bRefMode = mrViewData.IsRefMode(); // Start reference
+ bool bFormulaMode = pScMod->IsFormulaMode(); // next click -> reference
+ bool bEditMode = mrViewData.HasEditView(eWhich); // also in Mode==SC_INPUT_TYPE
+ bool bDouble = (rMEvt.GetClicks() == 2);
+ ScDocument& rDoc = mrViewData.GetDocument();
+ bool bIsTiledRendering = comphelper::LibreOfficeKit::isActive();
+
+ // 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
+
+ if ( ( bEditMode && mrViewData.GetActivePart() == eWhich ) || !bFormulaMode )
+ GrabFocus();
+
+ // #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 );
+
+ SfxInt16Item aPosXItem( SID_RANGE_COL, nPosX );
+ SfxInt32Item aPosYItem( SID_RANGE_ROW, nPosY );
+ mrViewData.GetDispatcher().ExecuteList(SID_FILL_SELECT,
+ SfxCallMode::SLOT | SfxCallMode::RECORD,
+ { &aPosXItem, &aPosYItem });
+
+ }
+ nButtonDown = 0;
+ nMouseStatus = SC_GM_NONE;
+ return;
+ }
+
+ if (!bDouble)
+ nMouseStatus = SC_GM_NONE;
+
+ rState.mbActivatePart = !bFormulaMode; // Don't activate when in formula mode.
+
+ if (bFormulaMode)
+ {
+ ScViewSelectionEngine* pSelEng = mrViewData.GetView()->GetSelEngine();
+ pSelEng->SetWindow(this);
+ pSelEng->SetWhich(eWhich);
+ pSelEng->SetVisibleArea( tools::Rectangle(Point(), GetOutputSizePixel()) );
+ }
+
+ if (bEditMode && (mrViewData.GetRefTabNo() == mrViewData.GetTabNo()))
+ {
+ Point aPos = rMEvt.GetPosPixel();
+ SCCOL nPosX;
+ SCROW nPosY;
+ mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
+
+ EditView* pEditView;
+ SCCOL nEditCol;
+ SCROW nEditRow;
+ mrViewData.GetEditView( eWhich, pEditView, nEditCol, nEditRow );
+ SCCOL nEndCol = mrViewData.GetEditEndCol();
+ SCROW nEndRow = mrViewData.GetEditEndRow();
+ SCCOL nStartCol = mrViewData.GetEditStartCol();
+
+ 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();
+
+ pScMod->SetInputMode( SC_INPUT_TABLE );
+ bEEMouse = true;
+
+ if (comphelper::LibreOfficeKit::isActive() && rDoc.IsLayoutRTL(mrViewData.GetTabNo()))
+ {
+ Point aMouse = rMEvt.GetPosPixel();
+ tools::Rectangle aOutputArea = pEditView->GetOutputArea();
+ comphelper::ScopeGuard aOutputGuard(
+ [pEditView, aOutputArea] {
+ pEditView->SetOutputArea(aOutputArea);
+ });
+
+ lcl_GetMirror(aMouse, aOutputArea, mrViewData.getLOKVisibleArea().GetWidth());
+ pEditView->SetOutputArea(aOutputArea);
+
+ MouseEvent aEvent(aMouse, rMEvt.GetClicks(), rMEvt.GetMode(),
+ rMEvt.GetButtons(), rMEvt.GetModifier());
+ pEditView->MouseButtonDown( aEvent );
+ }
+ else
+ pEditView->MouseButtonDown( rMEvt );
+ return;
+ }
+ }
+
+ 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
+
+ RfCorner rCorner = NONE;
+ bool bFound = HitRangeFinder(rMEvt.GetPosPixel(), rCorner, &nRFIndex, &nRFAddX, &nRFAddY);
+ bRFSize = (rCorner != NONE);
+ aRFSelectedCorned = rCorner;
+
+ if (bFound)
+ {
+ bRFMouse = true; // the other variables are initialized above
+
+ rState.mbActivatePart = true; // always activate ?
+ StartTracking();
+ return;
+ }
+
+ bool bCrossPointer = TestMouse( rMEvt, true );
+ if ( bCrossPointer )
+ {
+ if ( bDouble )
+ mrViewData.GetView()->FillCrossDblClick();
+ else
+ pScMod->InputEnterHandler(); // Autofill etc.
+ }
+
+ if ( !bCrossPointer )
+ {
+ nPagebreakMouse = HitPageBreak( rMEvt.GetPosPixel(), &aPagebreakSource,
+ &nPagebreakBreak, &nPagebreakPrev );
+ if (nPagebreakMouse)
+ {
+ bPagebreakDrawn = false;
+ StartTracking();
+ PagebreakMove( rMEvt, false );
+ return;
+ }
+ }
+
+ // 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
+
+ // TestMouse has already happened above
+ }
+
+ Point aPos = rMEvt.GetPosPixel();
+ SCCOL nPosX;
+ SCROW nPosY;
+ mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
+ SCTAB nTab = mrViewData.GetTabNo();
+ m_nDownPosX = nPosX;
+ m_nDownPosY = nPosY;
+
+ // 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.
+
+ if ( !bDouble && !bFormulaMode && rMEvt.IsLeft() )
+ {
+ SCCOL nRealPosX;
+ SCROW nRealPosY;
+ mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nRealPosX, nRealPosY, false );//the real row/col
+
+ // 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( pRealPosAttr->HasAutoFilter() )
+ {
+ SC_MOD()->InputEnterHandler();
+ if (DoAutoFilterButton(nPosX, nRealPosY, rMEvt))
+ return;
+ }
+
+ const ScMergeFlagAttr* pAttr = rDoc.GetAttr(nPosX, nPosY, nTab, ATTR_MERGE_FLAG);
+ if (pAttr->HasAutoFilter())
+ {
+ if (DoAutoFilterButton(nPosX, nPosY, rMEvt))
+ {
+ rState.mbActivatePart = false;
+ return;
+ }
+ }
+
+ if (pAttr->HasPivotButton() || pAttr->HasPivotPopupButton() || pAttr->HasPivotMultiFieldPopupButton())
+ {
+ DoPushPivotButton(nPosX, nPosY, rMEvt, pAttr->HasPivotButton(),
+ pAttr->HasPivotPopupButton(), pAttr->HasPivotMultiFieldPopupButton());
+ rState.mbActivatePart = false;
+ return;
+ }
+
+ if (pAttr->HasPivotToggle())
+ {
+ DoPushPivotToggle(nPosX, nPosY, rMEvt);
+ rState.mbActivatePart = false;
+ }
+
+ // List Validity drop-down button
+
+ 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();
+
+ LaunchDataSelectMenu( aListValPos.Col(), aListValPos.Row() );
+
+ 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;
+ }
+
+ // Gridwin - Selection Engine
+
+ if ( !rMEvt.IsLeft() )
+ return;
+
+ ScViewSelectionEngine* pSelEng = mrViewData.GetView()->GetSelEngine();
+ pSelEng->SetWindow(this);
+ pSelEng->SetWhich(eWhich);
+ pSelEng->SetVisibleArea( tools::Rectangle(Point(), GetOutputSizePixel()) );
+
+ // 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
+
+ nButtonDown = 0;
+
+ if (nMouseStatus == SC_GM_IGNORE)
+ {
+ nMouseStatus = SC_GM_NONE;
+ // Selection engine: cancel selection
+ mrViewData.GetView()->GetSelEngine()->Reset();
+ rMark.SetMarking(false);
+ if (mrViewData.IsAnyFillMode())
+ {
+ mrViewData.GetView()->StopRefMode();
+ mrViewData.ResetFillMode();
+ }
+ StopMarking();
+ DrawEndAction(); // cancel selection/moving in drawing layer
+ ReleaseMouse();
+ return;
+ }
+
+ if (nMouseStatus == SC_GM_FILTER)
+ {
+ nMouseStatus = SC_GM_NONE;
+ ReleaseMouse();
+ return; // nothing more should happen here
+ }
+
+ ScModule* pScMod = SC_MOD();
+ if (pScMod->IsModalMode(mrViewData.GetSfxDocShell()))
+ return;
+
+ SfxBindings& rBindings = mrViewData.GetBindings();
+ if (bEEMouse && mrViewData.HasEditView( eWhich ))
+ {
+ EditView* pEditView;
+ SCCOL nEditCol;
+ SCROW nEditRow;
+ mrViewData.GetEditView( eWhich, pEditView, nEditCol, nEditRow );
+
+ if (comphelper::LibreOfficeKit::isActive() && rDoc.IsLayoutRTL(mrViewData.GetTabNo()))
+ {
+ Point aMouse = rMEvt.GetPosPixel();
+ tools::Rectangle aOutputArea = pEditView->GetOutputArea();
+ comphelper::ScopeGuard aOutputGuard(
+ [pEditView, aOutputArea] {
+ pEditView->SetOutputArea(aOutputArea);
+ });
+
+ lcl_GetMirror(aMouse, aOutputArea, mrViewData.getLOKVisibleArea().GetWidth());
+ pEditView->SetOutputArea(aOutputArea);
+
+ MouseEvent aEvent(aMouse, rMEvt.GetClicks(), rMEvt.GetMode(),
+ rMEvt.GetButtons(), rMEvt.GetModifier());
+ pEditView->MouseButtonUp( aEvent );
+ }
+ else
+ pEditView->MouseButtonUp( rMEvt );
+
+ if ( rMEvt.IsMiddle() &&
+ GetSettings().GetMouseSettings().GetMiddleButtonAction() == MouseMiddleButtonAction::PasteSelection )
+ {
+ // EditView may have pasted from selection
+ pScMod->InputChanged( pEditView );
+ }
+ else
+ pScMod->InputSelection( pEditView ); // parentheses etc.
+
+ mrViewData.GetView()->InvalidateAttribs();
+ rBindings.Invalidate( SID_HYPERLINK_GETLINK );
+ bEEMouse = false;
+ return;
+ }
+
+ if (bDPMouse)
+ {
+ DPMouseButtonUp( rMEvt ); // resets bDPMouse
+ return;
+ }
+
+ if (bRFMouse)
+ {
+ RFMouseMove( rMEvt, true ); // Again the proper range
+ bRFMouse = false;
+ SetPointer( PointerStyle::Arrow );
+ ReleaseMouse();
+ return;
+ }
+
+ if (nPagebreakMouse)
+ {
+ PagebreakMove( rMEvt, true );
+ nPagebreakMouse = SC_PD_NONE;
+ SetPointer( PointerStyle::Arrow );
+ ReleaseMouse();
+ return;
+ }
+
+ if (nMouseStatus == SC_GM_WATERUNDO) // Undo in format paintbrush mode
+ {
+ SfxUndoManager* pMgr = mrViewData.GetDocShell()->GetUndoManager();
+ if ( pMgr->GetUndoActionCount() && dynamic_cast<ScUndoSelectionStyle*>(pMgr->GetUndoAction()) )
+ pMgr->Undo();
+ return;
+ }
+
+ if (DrawMouseButtonUp(rMEvt)) // includes format paint brush handling for drawing objects
+ {
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+ SfxBindings& rFrmBindings=pViewShell->GetViewFrame().GetBindings();
+ rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_WIDTH);
+ rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_HEIGHT);
+ rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_POS_X);
+ rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_POS_Y);
+ rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_ANGLE);
+ rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_ROT_X);
+ rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_ROT_Y);
+ rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_AUTOWIDTH);
+ rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_AUTOHEIGHT);
+ return;
+ }
+
+ rMark.SetMarking(false);
+
+ SetPointer( mrViewData.IsThemedCursor() ? PointerStyle::FatCross : PointerStyle::Arrow );
+
+ if (mrViewData.IsFillMode() ||
+ ( mrViewData.GetFillMode() == ScFillMode::MATRIX && rMEvt.IsMod1() ))
+ {
+ nScFillModeMouseModifier = rMEvt.GetModifier();
+ SCCOL nStartCol;
+ SCROW nStartRow;
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ mrViewData.GetFillData( nStartCol, nStartRow, nEndCol, nEndRow );
+ ScRange aDelRange;
+ bool bIsDel = mrViewData.GetDelMark( aDelRange );
+
+ ScViewFunc* pView = mrViewData.GetView();
+ pView->StopRefMode();
+ mrViewData.ResetFillMode();
+ pView->GetFunctionSet().SetAnchorFlag( false ); // #i5819# don't use AutoFill anchor flag for selection
+
+ if ( bIsDel )
+ {
+ pView->MarkRange( aDelRange, false );
+ pView->DeleteContents( InsertDeleteFlags::CONTENTS );
+ SCTAB nTab = mrViewData.GetTabNo();
+ ScRange aBlockRange( nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab );
+ if ( aBlockRange != aDelRange )
+ {
+ if ( aDelRange.aStart.Row() == nStartRow )
+ aBlockRange.aEnd.SetCol( aDelRange.aStart.Col() - 1 );
+ else
+ aBlockRange.aEnd.SetRow( aDelRange.aStart.Row() - 1 );
+ pView->MarkRange( aBlockRange, false );
+ }
+ }
+ else
+ mrViewData.GetDispatcher().Execute( FID_FILL_AUTO, SfxCallMode::SLOT | SfxCallMode::RECORD );
+ }
+ else if (mrViewData.GetFillMode() == ScFillMode::MATRIX)
+ {
+ SCTAB nTab = mrViewData.GetTabNo();
+ SCCOL nStartCol;
+ SCROW nStartRow;
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ mrViewData.GetFillData( nStartCol, nStartRow, nEndCol, nEndRow );
+ ScRange aBlockRange( nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab );
+ SCCOL nFillCol = mrViewData.GetRefEndX();
+ SCROW nFillRow = mrViewData.GetRefEndY();
+ ScAddress aEndPos( nFillCol, nFillRow, nTab );
+
+ ScTabView* pView = mrViewData.GetView();
+ pView->StopRefMode();
+ mrViewData.ResetFillMode();
+ pView->GetFunctionSet().SetAnchorFlag( false );
+
+ if ( aEndPos != aBlockRange.aEnd )
+ {
+ mrViewData.GetDocShell()->GetDocFunc().ResizeMatrix( aBlockRange, aEndPos );
+ mrViewData.GetView()->MarkRange( ScRange( aBlockRange.aStart, aEndPos ) );
+ }
+ }
+ else if (mrViewData.IsAnyFillMode())
+ {
+ // Embedded area has been changed
+ ScTabView* pView = mrViewData.GetView();
+ pView->StopRefMode();
+ mrViewData.ResetFillMode();
+ pView->GetFunctionSet().SetAnchorFlag( false );
+ mrViewData.GetDocShell()->UpdateOle(mrViewData);
+ }
+
+ bool bRefMode = mrViewData.IsRefMode();
+ if (bRefMode)
+ pScMod->EndReference();
+
+ // Format paintbrush mode (Switch)
+
+ if (pScMod->GetIsWaterCan())
+ {
+ // Check on undo already done above
+
+ ScStyleSheetPool* pStylePool = mrViewData.GetDocument().
+ GetStyleSheetPool();
+ if ( pStylePool )
+ {
+ SfxStyleSheet* pStyleSheet = static_cast<SfxStyleSheet*>(
+ pStylePool->GetActualStyleSheet());
+
+ if ( pStyleSheet )
+ {
+ SfxStyleFamily eFamily = pStyleSheet->GetFamily();
+
+ switch ( eFamily )
+ {
+ case SfxStyleFamily::Para:
+ mrViewData.GetView()->SetStyleSheetToMarked( pStyleSheet );
+ mrViewData.GetView()->DoneBlockMode();
+ break;
+
+ case SfxStyleFamily::Page:
+ mrViewData.GetDocument().SetPageStyle( mrViewData.GetTabNo(),
+ pStyleSheet->GetName() );
+
+ ScPrintFunc( mrViewData.GetDocShell(),
+ mrViewData.GetViewShell()->GetPrinter(true),
+ mrViewData.GetTabNo() ).UpdatePages();
+
+ rBindings.Invalidate( SID_STATUS_PAGESTYLE );
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ ScDBFunc* pView = mrViewData.GetView();
+ ScDocument* pBrushDoc = pView->GetBrushDocument();
+ if ( pBrushDoc )
+ {
+ pView->PasteFromClip( InsertDeleteFlags::ATTRIB, pBrushDoc );
+ if ( !pView->IsPaintBrushLocked() )
+ pView->ResetBrushDocument(); // invalidates pBrushDoc pointer
+ }
+
+ Point aPos = rMEvt.GetPosPixel();
+ SCCOL nPosX;
+ SCROW nPosY;
+ SCTAB nTab = mrViewData.GetTabNo();
+ mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
+ ScDPObject* pDPObj = rDoc.GetDPAtCursor( nPosX, nPosY, nTab );
+
+ // double click (only left button)
+
+ bool bIsTiledRendering = comphelper::LibreOfficeKit::isActive();
+ bool bDouble = ( rMEvt.GetClicks() == 2 && rMEvt.IsLeft() );
+ if ( bDouble
+ && !bRefMode
+ && (nMouseStatus == SC_GM_DBLDOWN || (bIsTiledRendering && nMouseStatus != SC_GM_URLDOWN))
+ && !pScMod->IsRefDialogOpen())
+ {
+ // data pilot table
+ if ( pDPObj && pDPObj->GetSaveData()->GetDrillDown() )
+ {
+ ScAddress aCellPos( nPosX, nPosY, mrViewData.GetTabNo() );
+
+ // Check for header drill-down first.
+ sheet::DataPilotTableHeaderData aData;
+ pDPObj->GetHeaderPositionData(aCellPos, aData);
+
+ if ( ( aData.Flags & sheet::MemberResultFlags::HASMEMBER ) &&
+ ! ( aData.Flags & sheet::MemberResultFlags::SUBTOTAL ) )
+ {
+ css::sheet::DataPilotFieldOrientation nDummy;
+ if ( pView->HasSelectionForDrillDown( nDummy ) )
+ {
+ // execute slot to show dialog
+ mrViewData.GetDispatcher().Execute( SID_OUTLINE_SHOW, SfxCallMode::SLOT | SfxCallMode::RECORD );
+ }
+ else
+ {
+ // toggle single entry
+ ScDPObject aNewObj( *pDPObj );
+ pDPObj->ToggleDetails( aData, &aNewObj );
+ ScDBDocFunc aFunc( *mrViewData.GetDocShell() );
+ aFunc.DataPilotUpdate( pDPObj, &aNewObj, true, false );
+ mrViewData.GetView()->CursorPosChanged(); // shells may be switched
+ }
+ }
+ else
+ {
+ // Check if the data area is double-clicked.
+
+ Sequence<sheet::DataPilotFieldFilter> aFilters;
+ if ( pDPObj->GetDataFieldPositionData(aCellPos, aFilters) )
+ mrViewData.GetView()->ShowDataPilotSourceData( *pDPObj, aFilters );
+ }
+
+ return;
+ }
+
+ // Check for cell protection attribute.
+ const ScTableProtection* pProtect = rDoc.GetTabProtection(nTab);
+ bool bEditAllowed = true;
+ if ( pProtect && pProtect->isProtected() )
+ {
+ bool bCellProtected = rDoc.HasAttrib(nPosX, nPosY, nTab, nPosX, nPosY, nTab, HasAttrFlags::Protected);
+ bool bSkipProtected = !pProtect->isOptionEnabled(ScTableProtection::SELECT_LOCKED_CELLS);
+ bool bSkipUnprotected = !pProtect->isOptionEnabled(ScTableProtection::SELECT_UNLOCKED_CELLS);
+
+ if ( bSkipProtected && bSkipUnprotected )
+ bEditAllowed = false;
+ else if ( (bCellProtected && bSkipProtected) || (!bCellProtected && bSkipUnprotected) )
+ bEditAllowed = false;
+ }
+
+ if ( bEditAllowed )
+ {
+ // edit cell contents
+ mrViewData.GetViewShell()->UpdateInputHandler();
+ pScMod->SetInputMode( SC_INPUT_TABLE );
+ if (mrViewData.HasEditView(eWhich))
+ {
+ // Set text cursor where clicked
+ EditView* pEditView = mrViewData.GetEditView( eWhich );
+ MouseEvent aEditEvt( rMEvt.GetPosPixel(), 1, MouseEventModifiers::SYNTHETIC, MOUSE_LEFT, 0 );
+ pEditView->MouseButtonDown( aEditEvt );
+ pEditView->MouseButtonUp( aEditEvt );
+ }
+ }
+
+ if ( bIsTiledRendering && rMEvt.IsLeft() && mrViewData.GetView()->GetSelEngine()->SelMouseButtonUp( rMEvt ) )
+ {
+ mrViewData.GetView()->SelectionChanged();
+ }
+
+ if ( bDouble )
+ return;
+ }
+
+ // Links in edit cells
+
+ bool bAlt = rMEvt.IsMod2();
+ if ( !bAlt && !bRefMode && !bDouble && nMouseStatus == SC_GM_URLDOWN )
+ {
+ // Only execute on ButtonUp, if ButtonDown also was done on a URL
+
+ OUString aName, aUrl, aTarget;
+ if ( GetEditUrl( rMEvt.GetPosPixel(), &aName, &aUrl, &aTarget ) )
+ {
+ 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(nPosX, nPosY);
+ double fPPTX = pViewShell->GetViewData().GetPPTX();
+ int mouseX = aPos.X() / fPPTX;
+ OString aMsg(aUrl.toUtf8() + " coordinates: " + aCursor + ", " + OString::number(mouseX));
+ 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);
+
+ OUString aRefCalcA1(aTempAddr.Format(ScRefFlags::ADDR_ABS, nullptr, formula::FormulaGrammar::CONV_OOO));
+ aBuf.append("#" + aExtInfo.maTabName + "." + aRefCalcA1);
+ ScGlobal::OpenURL(aBuf.makeStringAndClear(), aTarget);
+ }
+ else
+ {
+ // Internal reference.
+ OUString aUrlCalcA1(aTempAddr.Format(ScRefFlags::ADDR_ABS_3D, &rDoc, formula::FormulaGrammar::CONV_OOO));
+ aBuf.append("#" + aUrlCalcA1);
+ ScGlobal::OpenURL(aBuf.makeStringAndClear(), aTarget, isTiledRendering);
+ }
+ }
+
+ // fire worksheet_followhyperlink event
+ uno::Reference< script::vba::XVBAEventProcessor > xVbaEvents = rDoc.GetVbaEventProcessor();
+ if( xVbaEvents.is() ) try
+ {
+ aPos = rMEvt.GetPosPixel();
+ nTab = mrViewData.GetTabNo();
+ mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
+ OUString sURL;
+ ScRefCellValue aCell;
+ if (lcl_GetHyperlinkCell(rDoc, nPosX, nPosY, nTab, aCell, sURL))
+ {
+ ScAddress aCellPos( nPosX, nPosY, nTab );
+ uno::Reference< table::XCell > xCell( new ScCellObj( mrViewData.GetDocShell(), aCellPos ) );
+ uno::Sequence< uno::Any > aArgs{ uno::Any(xCell) };
+ xVbaEvents->processVbaEvent( script::vba::VBAEventId::WORKSHEET_FOLLOWHYPERLINK, aArgs );
+ }
+ }
+ catch( uno::Exception& )
+ {
+ }
+
+ return;
+ }
+ }
+
+ // Gridwin - SelectionEngine
+
+ // 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 });
+
+ mrViewData.GetView()->InvalidateAttribs();
+
+ }
+ mrViewData.GetViewShell()->SelectionChanged();
+
+ if (bIsTiledRendering && !bRefMode && !bDouble)
+ {
+ OUString aName, aUrl, aTarget;
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+ if (pViewShell && nPosX == m_nDownPosX && nPosY == m_nDownPosY
+ && GetEditUrl(aPos, &aName, &aUrl, &aTarget))
+ {
+ OString aMsg(aUrl.toUtf8() + " coordinates: " +
+ pViewShell->GetViewData().describeCellCursorAt(nPosX, nPosY) + ", " +
+ OString::number(aPos.X() / pViewShell->GetViewData().GetPPTX()));
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_HYPERLINK_CLICKED, aMsg);
+ }
+ }
+
+ m_nDownPosX = m_nDownPosY = -1;
+
+ return;
+}
+
+void ScGridWindow::FakeButtonUp()
+{
+ if ( nButtonDown )
+ {
+ MouseEvent aEvent( aCurMousePos ); // nButtons = 0 -> ignore
+ MouseButtonUp( aEvent );
+ }
+}
+
+void ScGridWindow::MouseMove( const MouseEvent& rMEvt )
+{
+ aCurMousePos = rMEvt.GetPosPixel();
+
+ if (rMEvt.IsLeaveWindow() && mpNoteMarker && !mpNoteMarker->IsByKeyboard())
+ HideNoteMarker();
+
+ ScModule* pScMod = SC_MOD();
+ 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 (bEEMouse && mrViewData.HasEditView( eWhich ))
+ {
+ EditView* pEditView;
+ SCCOL nEditCol;
+ SCROW nEditRow;
+ mrViewData.GetEditView( eWhich, pEditView, nEditCol, nEditRow );
+
+ if (comphelper::LibreOfficeKit::isActive() && mrViewData.GetDocument().IsLayoutRTL(mrViewData.GetTabNo()))
+ {
+ Point aMouse = rMEvt.GetPosPixel();
+ tools::Rectangle aOutputArea = pEditView->GetOutputArea();
+ comphelper::ScopeGuard aOutputGuard(
+ [pEditView, aOutputArea] {
+ pEditView->SetOutputArea(aOutputArea);
+ });
+
+ lcl_GetMirror(aMouse, aOutputArea, mrViewData.getLOKVisibleArea().GetWidth());
+ pEditView->SetOutputArea(aOutputArea);
+
+ MouseEvent aEvent(aMouse, rMEvt.GetClicks(), rMEvt.GetMode(),
+ rMEvt.GetButtons(), rMEvt.GetModifier());
+
+ pEditView->MouseMove( aEvent );
+ }
+ else
+ pEditView->MouseMove( rMEvt );
+ return;
+ }
+
+ if (bDPMouse)
+ {
+ DPMouseMove( rMEvt );
+ return;
+ }
+
+ if (bRFMouse)
+ {
+ RFMouseMove( rMEvt, false );
+ return;
+ }
+
+ if (nPagebreakMouse)
+ {
+ PagebreakMove( rMEvt, false );
+ return;
+ }
+
+ // Show other mouse pointer?
+
+ bool bEditMode = mrViewData.HasEditView(eWhich);
+
+ //! Test if refMode dragging !!!
+ if ( bEditMode && (mrViewData.GetRefTabNo() == mrViewData.GetTabNo()) )
+ {
+ Point aPos = rMEvt.GetPosPixel();
+ SCCOL nPosX;
+ SCROW nPosY;
+ mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
+
+ EditView* pEditView;
+ SCCOL nEditCol;
+ SCROW nEditRow;
+ mrViewData.GetEditView( eWhich, pEditView, nEditCol, nEditRow );
+ SCCOL nEndCol = mrViewData.GetEditEndCol();
+ SCROW nEndRow = mrViewData.GetEditEndRow();
+
+ if ( nPosX >= nEditCol && nPosX <= nEndCol &&
+ nPosY >= nEditRow && nPosY <= nEndRow )
+ {
+ if ( !pEditView )
+ {
+ SetPointer( PointerStyle::Text );
+ return;
+ }
+
+ const SvxFieldItem* pFld;
+ if ( comphelper::LibreOfficeKit::isActive() )
+ {
+ Point aLogicClick = pEditView->GetOutputDevice().PixelToLogic(aPos);
+ pFld = pEditView->GetField( aLogicClick );
+ }
+ else
+ {
+ pFld = pEditView->GetFieldUnderMousePointer();
+ }
+ // Field can only be URL field
+ bool bAlt = rMEvt.IsMod2();
+ if ( !bAlt && !nButtonDown && ScGlobal::ShouldOpenURL() && pFld )
+ SetPointer( PointerStyle::RefHand );
+ else if ( pEditView->GetEditEngine()->IsEffectivelyVertical() )
+ SetPointer( PointerStyle::TextVertical );
+ else
+ SetPointer( PointerStyle::Text );
+ return;
+ }
+ }
+
+ bool bWater = SC_MOD()->GetIsWaterCan() || mrViewData.GetView()->HasPaintBrush();
+ if (bWater)
+ SetPointer( PointerStyle::Fill );
+
+ if (!bWater)
+ {
+ bool bCross = false;
+
+ // range finder
+
+ RfCorner rCorner = NONE;
+ if ( HitRangeFinder( rMEvt.GetPosPixel(), rCorner, nullptr, nullptr, nullptr ) )
+ {
+ if (rCorner != NONE)
+ SetPointer( PointerStyle::Cross );
+ else
+ SetPointer( PointerStyle::Hand );
+ bCross = true;
+ }
+
+ // Page-Break-Mode
+
+ 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 );
+ else if ( !bAlt && !nButtonDown && ScGlobal::ShouldOpenURL() &&
+ GetEditUrl(rMEvt.GetPosPixel()) )
+ SetPointer( PointerStyle::RefHand );
+ else if ( 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 );
+}
+
+static void 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;
+
+ rEvent.X = rEvt.GetPosPixel().X();
+ rEvent.Y = rEvt.GetPosPixel().Y();
+ rEvent.ClickCount = rEvt.GetClicks();
+ rEvent.PopupTrigger = false;
+}
+
+bool ScGridWindow::PreNotify( NotifyEvent& rNEvt )
+{
+ bool bDone = false;
+ NotifyEventType nType = rNEvt.GetType();
+ if ( nType == NotifyEventType::MOUSEBUTTONUP || nType == NotifyEventType::MOUSEBUTTONDOWN )
+ {
+ vcl::Window* pWindow = rNEvt.GetWindow();
+ if (pWindow == this)
+ {
+ SfxViewFrame& rViewFrame = mrViewData.GetViewShell()->GetViewFrame();
+ css::uno::Reference<css::frame::XController> xController = rViewFrame.GetFrame().GetController();
+ if (xController.is())
+ {
+ ScTabViewObj* pImp = dynamic_cast<ScTabViewObj*>( xController.get() );
+ if (pImp && pImp->IsMouseListening())
+ {
+ css::awt::MouseEvent aEvent;
+ lcl_InitMouseEvent( aEvent, *rNEvt.GetMouseEvent() );
+ if ( rNEvt.GetWindow() )
+ aEvent.Source = rNEvt.GetWindow()->GetComponentInterface();
+ if ( nType == NotifyEventType::MOUSEBUTTONDOWN)
+ bDone = pImp->MousePressed( aEvent );
+ else
+ bDone = pImp->MouseReleased( aEvent );
+ }
+ }
+ }
+ }
+ if (bDone) // event consumed by a listener
+ {
+ if ( nType == NotifyEventType::MOUSEBUTTONDOWN )
+ {
+ const MouseEvent* pMouseEvent = rNEvt.GetMouseEvent();
+ if ( pMouseEvent->IsRight() && pMouseEvent->GetClicks() == 1 )
+ {
+ // If a listener returned true for a right-click call, also prevent opening the context menu
+ // (this works only if the context menu is opened on mouse-down)
+ nMouseStatus = SC_GM_IGNORE;
+ }
+ }
+
+ return true;
+ }
+ else
+ return Window::PreNotify( rNEvt );
+}
+
+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() && !SC_MOD()->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)
+ SC_MOD()->EndReference(); // Do not let the Dialog remain minimized
+ }
+ }
+ else if ( 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::StartDrag( sal_Int8 /* nAction */, const Point& rPosPixel )
+{
+ if (mpFilterBox || nPagebreakMouse)
+ return;
+
+ HideNoteMarker();
+
+ CommandEvent aDragEvent( rPosPixel, CommandEventId::StartDrag, true );
+
+ if (bEEMouse && mrViewData.HasEditView( eWhich ))
+ {
+ EditView* pEditView;
+ SCCOL nEditCol;
+ SCROW nEditRow;
+ mrViewData.GetEditView( eWhich, pEditView, nEditCol, nEditRow );
+
+ // don't remove the edit view while switching views
+ ScModule* pScMod = SC_MOD();
+ pScMod->SetInEditCommand( true );
+
+ pEditView->Command( aDragEvent );
+
+ ScInputHandler* pHdl = pScMod->GetInputHdl();
+ if (pHdl)
+ pHdl->DataChanged();
+
+ pScMod->SetInEditCommand( false );
+ if (!mrViewData.IsActive()) // dropped to different view?
+ {
+ ScInputHandler* pViewHdl = pScMod->GetInputHdl( mrViewData.GetViewShell() );
+ if ( pViewHdl && mrViewData.HasEditView( eWhich ) )
+ {
+ pViewHdl->CancelHandler();
+ ShowCursor(); // missing from KillEditView
+ }
+ }
+ }
+ else
+ if ( !DrawCommand(aDragEvent) )
+ mrViewData.GetView()->GetSelEngine()->Command( aDragEvent );
+}
+
+static void lcl_SetTextCursorPos( ScViewData& rViewData, ScSplitPos eWhich, vcl::Window* pWin )
+{
+ SCCOL nCol = rViewData.GetCurX();
+ SCROW nRow = rViewData.GetCurY();
+ tools::Rectangle aEditArea = rViewData.GetEditArea( eWhich, nCol, nRow, pWin, nullptr, true );
+ aEditArea.SetRight( aEditArea.Left() );
+ aEditArea = pWin->PixelToLogic( aEditArea );
+ pWin->SetCursorRect( &aEditArea );
+}
+
+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 = SC_MOD();
+ 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;
+ }
+
+ ScInputHandler* pHdl = pScMod->GetInputHdl( mrViewData.GetViewShell() );
+ if ( pHdl )
+ {
+ pHdl->InputCommand( rCEvt );
+ return; // done
+ }
+
+ Window::Command( rCEvt );
+ 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.
+
+ SfxBindings& rBindings = mrViewData.GetBindings();
+ rBindings.Invalidate( SID_ATTR_CHAR_FONT );
+ rBindings.Invalidate( SID_ATTR_CHAR_FONTHEIGHT );
+ return;
+ }
+
+ if ( nCmd == CommandEventId::Wheel || nCmd == CommandEventId::StartAutoScroll || nCmd == CommandEventId::AutoScroll )
+ {
+ bool bDone = mrViewData.GetView()->ScrollCommand( rCEvt, eWhich );
+ 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 || SC_MOD()->GetIsWaterCan())
+ return;
+
+ bool bMouse = rCEvt.IsMouseEvent();
+ if ( bMouse && nMouseStatus == SC_GM_IGNORE )
+ 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 (nCellY >= mrViewData.GetEditViewRow() && nCellY <= mrViewData.GetEditEndRow())
+ nCellY = mrViewData.GetEditViewRow();
+ 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();
+ }
+ }
+
+ bool bSpellError = false;
+ SCCOL nColSpellError = nCellX;
+
+ 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;
+
+ aSpellCheckCell.assign(rDoc, aPos);
+ }
+
+ if (aPos.Col() >= 0 && (aSpellCheckCell.getType() == CELLTYPE_STRING || aSpellCheckCell.getType() == CELLTYPE_EDIT))
+ nColSpellError = aPos.Col();
+
+ // 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);
+ }
+
+ bool bDone = false;
+ bool bEdit = mrViewData.HasEditView(eWhich);
+
+ 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();
+
+ const OUString sOldText = pHdl ? pHdl->GetEditString() : "";
+
+ // 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);
+ }
+ }
+ }
+ else if ( !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);
+
+ SCCOL nCurX = mrViewData.GetCurX();
+ SCROW nCurY = mrViewData.GetCurY();
+ aMenuPos = mrViewData.GetScrPos( nCurX, nCurY, eWhich, true );
+ tools::Long nSizeXPix;
+ tools::Long nSizeYPix;
+ mrViewData.GetMergeSizePixel( nCurX, nCurY, nSizeXPix, nSizeYPix );
+ // fdo#55432 take the correct position for RTL sheet
+ aMenuPos.AdjustX(bLayoutIsRTL ? -nSizeXPix : nSizeXPix );
+ aMenuPos.AdjustY(nSizeYPix );
+
+ ScTabViewShell* pViewSh = mrViewData.GetViewShell();
+ if (pViewSh)
+ {
+ // Is a draw object selected?
+
+ SdrView* pDrawView = pViewSh->GetScDrawView();
+ if (pDrawView && pDrawView->AreObjectsMarked())
+ {
+ // #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)
+
+ ScTabView* pView = mrViewData.GetView();
+ ScDrawView* pDrawView = pView->GetScDrawView();
+
+ // check cell edit mode
+
+ if ( mrViewData.HasEditView(eWhich) )
+ {
+ ScModule* pScMod = SC_MOD();
+ SCCOL nEditStartCol = mrViewData.GetEditViewCol(); //! change to GetEditStartCol after calcrtl is integrated
+ SCROW nEditStartRow = mrViewData.GetEditViewRow();
+ SCCOL nEditEndCol = mrViewData.GetEditEndCol();
+ SCROW nEditEndRow = mrViewData.GetEditEndRow();
+
+ if ( nCellX >= nEditStartCol && nCellX <= nEditEndCol &&
+ nCellY >= nEditStartRow && nCellY <= nEditEndRow )
+ {
+ // handle selection within the EditView
+
+ EditView* pEditView = mrViewData.GetEditView( eWhich ); // not NULL (HasEditView)
+ EditEngine* pEditEngine = pEditView->GetEditEngine();
+ tools::Rectangle aOutputArea = pEditView->GetOutputArea();
+ tools::Rectangle aVisArea = pEditView->GetVisArea();
+
+ Point aTextPos = PixelToLogic( rPosPixel );
+ if ( pEditEngine->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
+
+ EPosition aDocPosition = pEditEngine->FindDocPosition(aTextPos);
+ ESelection aCompare(aDocPosition.nPara, aDocPosition.nIndex);
+ 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
+
+ Outliner* pOutliner = pOlView->GetOutliner();
+ const EditEngine& rEditEngine = pOutliner->GetEditEngine();
+ tools::Rectangle aVisArea = pOlView->GetVisArea();
+
+ Point aTextPos = aLogicPos;
+ if ( pOutliner->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
+
+ EPosition aDocPosition = rEditEngine.FindDocPosition(aTextPos);
+ ESelection aCompare(aDocPosition.nPara, aDocPosition.nIndex);
+ 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();
+ }
+ }
+
+ // look for existing selection
+
+ bool bHitSelected = false;
+ if ( pDrawView && pDrawView->IsMarkedObjHit( aLogicPos ) )
+ {
+ // clicked on selected object -> don't change anything
+ bHitSelected = true;
+ }
+ else if ( mrViewData.GetMarkData().IsCellMarked(nCellX, nCellY) )
+ {
+ // clicked on selected cell -> don't change anything
+ bHitSelected = true;
+ }
+
+ // select drawing object or move cell cursor
+
+ if ( bHitSelected )
+ return;
+
+ bool bWasDraw = ( pDrawView && pDrawView->AreObjectsMarked() );
+ bool bHitDraw = false;
+ if ( pDrawView )
+ {
+ pDrawView->UnmarkAllObj();
+ // Unlock the Internal Layer in order to activate the context menu.
+ // re-lock in ScDrawView::MarkListHasChanged()
+ lcl_UnLockComment(pDrawView, aLogicPos, mrViewData);
+ bHitDraw = pDrawView->MarkObj( aLogicPos );
+ // draw shell is activated in MarkListHasChanged
+ }
+ if ( !bHitDraw )
+ {
+ pView->Unmark();
+ pView->SetCursor(nCellX, nCellY);
+ if ( bWasDraw )
+ mrViewData.GetViewShell()->SetDrawShell( false ); // switch shells
+ }
+}
+
+void ScGridWindow::KeyInput(const KeyEvent& rKEvt)
+{
+ // Cursor control for ref input dialog
+ const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
+
+#ifdef DBG_UTIL
+
+ if (rKeyCode.IsMod1() && rKeyCode.IsShift())
+ {
+ if (rKeyCode.GetCode() == KEY_F12)
+ {
+ dumpColumnInformationPixel();
+ }
+ else if (rKeyCode.GetCode() == KEY_F11)
+ {
+ dumpGraphicInformation();
+ }
+ else if (rKeyCode.GetCode() == KEY_F10)
+ {
+ dumpColumnInformationHmm();
+ }
+ else if (rKeyCode.GetCode() == KEY_F6)
+ {
+ dumpCellProperties();
+ }
+ else if (rKeyCode.GetCode() == KEY_F8)
+ {
+ dumpColumnCellStorage();
+ }
+ else if (rKeyCode.GetCode() == KEY_F7)
+ {
+ ScDocument& rDoc = mrViewData.GetDocument();
+ auto& rMapper = rDoc.GetExternalDataMapper();
+ for (auto& itr : rMapper.getDataSources())
+ {
+ itr.refresh(&rDoc);
+ }
+ return;
+ }
+ }
+
+#endif
+
+ if( SC_MOD()->IsRefDialogOpen() )
+ {
+ if( !rKeyCode.GetModifier() && (rKeyCode.GetCode() == KEY_F2) )
+ {
+ SC_MOD()->EndReference();
+ }
+ else if( mrViewData.GetViewShell()->MoveCursorKeyInput( rKEvt ) )
+ {
+ ScRange aRef(
+ mrViewData.GetRefStartX(), mrViewData.GetRefStartY(), mrViewData.GetRefStartZ(),
+ mrViewData.GetRefEndX(), mrViewData.GetRefEndY(), mrViewData.GetRefEndZ() );
+ SC_MOD()->SetReference( aRef, mrViewData.GetDocument() );
+ }
+ mrViewData.GetViewShell()->SelectionChanged();
+ return ;
+ }
+ else if( rKeyCode.GetCode() == KEY_RETURN && mrViewData.IsPasteMode()
+ && SC_MOD()->GetInputOptions().GetEnterPasteMode() )
+ {
+ ScTabViewShell* pTabViewShell = mrViewData.GetViewShell();
+ ScClipUtil::PasteFromClipboard( mrViewData, pTabViewShell, true );
+
+ // Clear clipboard content.
+ uno::Reference<datatransfer::clipboard::XClipboard> xSystemClipboard =
+ GetClipboard();
+ if (xSystemClipboard.is())
+ {
+ xSystemClipboard->setContents(
+ uno::Reference<datatransfer::XTransferable>(),
+ uno::Reference<datatransfer::clipboard::XClipboardOwner>());
+ }
+
+ // 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:
+ else if( !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 = mpNoteMarker && mpNoteMarker->IsByKeyboard();
+ ScTabViewShell* pViewSh = mrViewData.GetViewShell();
+
+ if (mrViewData.GetDocShell()->GetProgress())
+ return;
+
+ if (DrawKeyInput(rKEvt, this))
+ {
+ const vcl::KeyCode& rLclKeyCode = rKEvt.GetKeyCode();
+ if (rLclKeyCode.GetCode() == KEY_DOWN
+ || rLclKeyCode.GetCode() == KEY_UP
+ || rLclKeyCode.GetCode() == KEY_LEFT
+ || rLclKeyCode.GetCode() == KEY_RIGHT)
+ {
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+ SfxBindings& rBindings = pViewShell->GetViewFrame().GetBindings();
+ rBindings.Invalidate(SID_ATTR_TRANSFORM_POS_X);
+ rBindings.Invalidate(SID_ATTR_TRANSFORM_POS_Y);
+ }
+ 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 )
+ HideNoteMarker();
+ 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)
+
+ if ( bHadKeyMarker )
+ HideNoteMarker(); // hide when previously visible
+ else
+ ShowNoteMarker( mrViewData.GetCurX(), mrViewData.GetCurY(), true );
+ return;
+ }
+ if (aCode.GetCode() == KEY_BRACKETLEFT && aCode.GetModifier() == KEY_MOD1)
+ {
+ pViewSh->DetectiveMarkPred();
+ return;
+ }
+ if (aCode.GetCode() == KEY_BRACKETRIGHT && aCode.GetModifier() == KEY_MOD1)
+ {
+ pViewSh->DetectiveMarkSucc();
+ return;
+ }
+
+ }
+
+ Window::KeyInput(rKEvt);
+}
+
+OUString ScGridWindow::GetSurroundingText() const
+{
+ bool bEditView = mrViewData.HasEditView(eWhich);
+ if (bEditView)
+ {
+ ScModule* pScMod = SC_MOD();
+ ScInputHandler* pHdl = pScMod->GetInputHdl(mrViewData.GetViewShell());
+ if (pHdl)
+ return pHdl->GetSurroundingText();
+ }
+ else if (SdrView* pSdrView = mrViewData.GetView()->GetScDrawView())
+ {
+ // if no cell editview is active, look at drawview
+ OutlinerView* pOlView = pSdrView->GetTextEditOutlinerView();
+ if (pOlView && pOlView->GetWindow() == this)
+ return pOlView->GetSurroundingText();
+ }
+
+ return Window::GetSurroundingText();
+}
+
+Selection ScGridWindow::GetSurroundingTextSelection() const
+{
+ bool bEditView = mrViewData.HasEditView(eWhich);
+ if (bEditView)
+ {
+ ScModule* pScMod = SC_MOD();
+ ScInputHandler* pHdl = pScMod->GetInputHdl(mrViewData.GetViewShell());
+ if (pHdl)
+ return pHdl->GetSurroundingTextSelection();
+ }
+ else if (SdrView* pSdrView = mrViewData.GetView()->GetScDrawView())
+ {
+ // if no cell editview is active, look at drawview
+ OutlinerView* pOlView = pSdrView->GetTextEditOutlinerView();
+ if (pOlView && pOlView->GetWindow() == this)
+ return pOlView->GetSurroundingTextSelection();
+ }
+
+ return Window::GetSurroundingTextSelection();
+}
+
+bool ScGridWindow::DeleteSurroundingText(const Selection& rSelection)
+{
+ bool bEditView = mrViewData.HasEditView(eWhich);
+ if (bEditView)
+ {
+ ScModule* pScMod = SC_MOD();
+ ScInputHandler* pHdl = pScMod->GetInputHdl(mrViewData.GetViewShell());
+ if (pHdl)
+ return pHdl->DeleteSurroundingText(rSelection);
+ }
+ else if (SdrView* pSdrView = mrViewData.GetView()->GetScDrawView())
+ {
+ // if no cell editview is active, look at drawview
+ OutlinerView* pOlView = pSdrView->GetTextEditOutlinerView();
+ if (pOlView && pOlView->GetWindow() == this)
+ return pOlView->DeleteSurroundingText(rSelection);
+ }
+
+ return Window::DeleteSurroundingText(rSelection);
+}
+
+void ScGridWindow::StopMarking()
+{
+ DrawEndAction(); // Cancel Select/move on Drawing-Layer
+
+ if (nButtonDown)
+ {
+ mrViewData.GetMarkData().SetMarking(false);
+ nMouseStatus = SC_GM_IGNORE;
+ }
+}
+
+void ScGridWindow::UpdateInputContext()
+{
+ bool bReadOnly = mrViewData.GetDocShell()->IsReadOnly();
+ InputContextFlags nOptions = bReadOnly ? InputContextFlags::NONE : ( InputContextFlags::Text | InputContextFlags::ExtText );
+
+ // when font from InputContext is used,
+ // it must be taken from the cursor position's cell attributes
+
+ InputContext aContext;
+ aContext.SetOptions( nOptions );
+ SetInputContext( aContext );
+}
+
+ // sensitive range (Pixel)
+#define SCROLL_SENSITIVE 20
+
+void ScGridWindow::DropScroll( const Point& rMousePos )
+{
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCCOL nDx = 0;
+ SCROW nDy = 0;
+ Size aSize = GetOutputSizePixel();
+
+ if (aSize.Width() > SCROLL_SENSITIVE * 3)
+ {
+ if ( rMousePos.X() < SCROLL_SENSITIVE && mrViewData.GetPosX(WhichH(eWhich)) > 0 )
+ nDx = -1;
+ if ( rMousePos.X() >= aSize.Width() - SCROLL_SENSITIVE
+ && mrViewData.GetPosX(WhichH(eWhich)) < rDoc.MaxCol() )
+ nDx = 1;
+ }
+ if (aSize.Height() > SCROLL_SENSITIVE * 3)
+ {
+ if ( rMousePos.Y() < SCROLL_SENSITIVE && mrViewData.GetPosY(WhichV(eWhich)) > 0 )
+ nDy = -1;
+ if ( rMousePos.Y() >= aSize.Height() - SCROLL_SENSITIVE
+ && mrViewData.GetPosY(WhichV(eWhich)) < rDoc.MaxRow() )
+ nDy = 1;
+ }
+
+ if ( nDx != 0 || nDy != 0 )
+ {
+ if ( nDx != 0 )
+ mrViewData.GetView()->ScrollX( nDx, WhichH(eWhich) );
+ if ( nDy != 0 )
+ mrViewData.GetView()->ScrollY( nDy, WhichV(eWhich) );
+ }
+}
+
+static bool 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(pDoc->GetChangeTrack()!=nullptr)
+ {
+ if( pDoc->IsScenario(nTab) && pDoc->HasScenarioRange(nTab, aDragRange))
+ {
+ bReturn = true;
+ }
+ else
+ {
+ for(SCTAB i=nTab+1; i<nTabCount && pDoc->IsScenario(i); i++)
+ {
+ if(pDoc->HasScenarioRange(i, aDragRange))
+ {
+ bReturn = true;
+ break;
+ }
+ }
+ }
+ }
+ return bReturn;
+}
+
+static ScRange lcl_MakeDropRange( const ScDocument& rDoc, SCCOL nPosX, SCROW nPosY, SCTAB nTab, const ScRange& rSource )
+{
+ SCCOL nCol1 = nPosX;
+ SCCOL nCol2 = nCol1 + ( rSource.aEnd.Col() - rSource.aStart.Col() );
+ if ( nCol2 > rDoc.MaxCol() )
+ {
+ nCol1 -= nCol2 - rDoc.MaxCol();
+ nCol2 = rDoc.MaxCol();
+ }
+ SCROW nRow1 = nPosY;
+ SCROW nRow2 = nRow1 + ( rSource.aEnd.Row() - rSource.aStart.Row() );
+ if ( nRow2 > rDoc.MaxRow() )
+ {
+ nRow1 -= nRow2 - rDoc.MaxRow();
+ nRow2 = rDoc.MaxRow();
+ }
+
+ return ScRange( nCol1, nRow1, nTab, nCol2, nRow2, nTab );
+}
+
+sal_Int8 ScGridWindow::AcceptPrivateDrop( const AcceptDropEvent& rEvt, const ScDragData& rData )
+{
+ if ( rEvt.mbLeaving )
+ {
+ bDragRect = false;
+ UpdateDragRectOverlay();
+ return rEvt.mnAction;
+ }
+
+ if ( rData.pCellTransfer )
+ {
+ // Don't move source that would include filtered rows.
+ if ((rEvt.mnAction & DND_ACTION_MOVE) && rData.pCellTransfer->HasFilteredRows())
+ {
+ if (bDragRect)
+ {
+ bDragRect = false;
+ UpdateDragRectOverlay();
+ }
+ return DND_ACTION_NONE;
+ }
+
+ Point aPos = rEvt.maPosPixel;
+
+ ScDocument* pSourceDoc = rData.pCellTransfer->GetSourceDocument();
+ ScDocument& rThisDoc = mrViewData.GetDocument();
+ if (pSourceDoc == &rThisDoc)
+ {
+ OUString aName;
+ if ( rThisDoc.HasChartAtPoint(mrViewData.GetTabNo(), PixelToLogic(aPos), aName ))
+ {
+ if (bDragRect) // Remove rectangle
+ {
+ bDragRect = false;
+ UpdateDragRectOverlay();
+ }
+
+ //! highlight chart? (selection border?)
+
+ sal_Int8 nRet = rEvt.mnAction;
+ return nRet;
+ }
+ }
+
+ if (rData.pCellTransfer->GetDragSourceFlags() & ScDragSrc::Table) // whole sheet?
+ {
+ bool bOk = rThisDoc.IsDocEditable();
+ return bOk ? rEvt.mnAction : 0; // don't draw selection frame
+ }
+
+ SCCOL nPosX;
+ SCROW nPosY;
+ mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
+
+ ScRange aSourceRange = rData.pCellTransfer->GetRange();
+ SCCOL nSourceStartX = aSourceRange.aStart.Col();
+ SCROW nSourceStartY = aSourceRange.aStart.Row();
+ SCCOL nSourceEndX = aSourceRange.aEnd.Col();
+ SCROW nSourceEndY = aSourceRange.aEnd.Row();
+ SCCOL nSizeX = nSourceEndX - nSourceStartX + 1;
+ SCROW nSizeY = nSourceEndY - nSourceStartY + 1;
+
+ if ( rEvt.mnAction != DND_ACTION_MOVE )
+ nSizeY = rData.pCellTransfer->GetNonFilteredRows(); // copy/link: no filtered rows
+
+ SCCOL nNewDragX = nPosX - rData.pCellTransfer->GetDragHandleX();
+ if (nNewDragX<0) nNewDragX=0;
+ if (nNewDragX+(nSizeX-1) > rThisDoc.MaxCol())
+ nNewDragX = rThisDoc.MaxCol()-(nSizeX-1);
+ SCROW nNewDragY = nPosY - rData.pCellTransfer->GetDragHandleY();
+ if (nNewDragY<0) nNewDragY=0;
+ if (nNewDragY+(nSizeY-1) > rThisDoc.MaxRow())
+ nNewDragY = rThisDoc.MaxRow()-(nSizeY-1);
+
+ // don't break scenario ranges, don't drop on filtered
+ SCTAB nTab = mrViewData.GetTabNo();
+ ScRange aDropRange = lcl_MakeDropRange( rThisDoc, nNewDragX, nNewDragY, nTab, aSourceRange );
+ if ( lcl_TestScenarioRedliningDrop( &rThisDoc, aDropRange ) ||
+ lcl_TestScenarioRedliningDrop( pSourceDoc, aSourceRange ) ||
+ ScViewUtil::HasFiltered( aDropRange, rThisDoc) )
+ {
+ if (bDragRect)
+ {
+ bDragRect = false;
+ UpdateDragRectOverlay();
+ }
+ return DND_ACTION_NONE;
+ }
+
+ InsCellCmd eDragInsertMode = INS_NONE;
+ Window::PointerState aState = GetPointerState();
+
+ // check for datapilot item sorting
+ ScDPObject* pDPObj = nullptr;
+ if ( &rThisDoc == pSourceDoc && ( pDPObj = rThisDoc.GetDPAtCursor( nNewDragX, nNewDragY, nTab ) ) != nullptr )
+ {
+ // drop on DataPilot table: sort or nothing
+
+ bool bDPSort = false;
+ if ( rThisDoc.GetDPAtCursor( nSourceStartX, nSourceStartY, aSourceRange.aStart.Tab() ) == pDPObj )
+ {
+ sheet::DataPilotTableHeaderData aDestData;
+ pDPObj->GetHeaderPositionData( ScAddress(nNewDragX, nNewDragY, nTab), aDestData );
+ bool bValid = ( aDestData.Dimension >= 0 ); // dropping onto a field
+
+ // look through the source range
+ for (SCROW nRow = aSourceRange.aStart.Row(); bValid && nRow <= aSourceRange.aEnd.Row(); ++nRow )
+ for (SCCOL nCol = aSourceRange.aStart.Col(); bValid && nCol <= aSourceRange.aEnd.Col(); ++nCol )
+ {
+ sheet::DataPilotTableHeaderData aSourceData;
+ pDPObj->GetHeaderPositionData( ScAddress( nCol, nRow, aSourceRange.aStart.Tab() ), aSourceData );
+ if ( aSourceData.Dimension != aDestData.Dimension || aSourceData.MemberName.isEmpty() )
+ bValid = false; // empty (subtotal) or different field
+ }
+
+ if ( bValid )
+ {
+ bool bIsDataLayout;
+ OUString aDimName = pDPObj->GetDimName( aDestData.Dimension, bIsDataLayout );
+ const ScDPSaveDimension* pDim = pDPObj->GetSaveData()->GetExistingDimensionByName( aDimName );
+ if ( pDim )
+ {
+ ScRange aOutRange = pDPObj->GetOutRange();
+
+ sheet::DataPilotFieldOrientation nOrient = pDim->GetOrientation();
+ if ( nOrient == sheet::DataPilotFieldOrientation_COLUMN )
+ {
+ eDragInsertMode = INS_CELLSRIGHT;
+ nSizeY = aOutRange.aEnd.Row() - nNewDragY + 1;
+ bDPSort = true;
+ }
+ else if ( nOrient == sheet::DataPilotFieldOrientation_ROW )
+ {
+ eDragInsertMode = INS_CELLSDOWN;
+ nSizeX = aOutRange.aEnd.Col() - nNewDragX + 1;
+ bDPSort = true;
+ }
+ }
+ }
+ }
+
+ if ( !bDPSort )
+ {
+ // no valid sorting in a DataPilot table -> disallow
+ if ( bDragRect )
+ {
+ bDragRect = false;
+ UpdateDragRectOverlay();
+ }
+ return DND_ACTION_NONE;
+ }
+ }
+ else if ( aState.mnState & KEY_MOD2 )
+ {
+ if ( &rThisDoc == pSourceDoc && nTab == aSourceRange.aStart.Tab() )
+ {
+ tools::Long nDeltaX = std::abs( static_cast< tools::Long >( nNewDragX - nSourceStartX ) );
+ tools::Long nDeltaY = std::abs( static_cast< tools::Long >( nNewDragY - nSourceStartY ) );
+ if ( nDeltaX <= nDeltaY )
+ {
+ eDragInsertMode = INS_CELLSDOWN;
+ }
+ else
+ {
+ eDragInsertMode = INS_CELLSRIGHT;
+ }
+
+ if ( ( eDragInsertMode == INS_CELLSDOWN && nNewDragY <= nSourceEndY &&
+ ( nNewDragX + nSizeX - 1 ) >= nSourceStartX && nNewDragX <= nSourceEndX &&
+ ( nNewDragX != nSourceStartX || nNewDragY >= nSourceStartY ) ) ||
+ ( eDragInsertMode == INS_CELLSRIGHT && nNewDragX <= nSourceEndX &&
+ ( nNewDragY + nSizeY - 1 ) >= nSourceStartY && nNewDragY <= nSourceEndY &&
+ ( nNewDragY != nSourceStartY || nNewDragX >= nSourceStartX ) ) )
+ {
+ if ( bDragRect )
+ {
+ bDragRect = false;
+ UpdateDragRectOverlay();
+ }
+ return DND_ACTION_NONE;
+ }
+ }
+ else
+ {
+ if ( static_cast< tools::Long >( nSizeX ) >= static_cast< tools::Long >( nSizeY ) )
+ {
+ eDragInsertMode = INS_CELLSDOWN;
+
+ }
+ else
+ {
+ eDragInsertMode = INS_CELLSRIGHT;
+ }
+ }
+ }
+
+ if ( nNewDragX != nDragStartX || nNewDragY != nDragStartY ||
+ nDragStartX+nSizeX-1 != nDragEndX || nDragStartY+nSizeY-1 != nDragEndY ||
+ !bDragRect || eDragInsertMode != meDragInsertMode )
+ {
+ nDragStartX = nNewDragX;
+ nDragStartY = nNewDragY;
+ nDragEndX = nDragStartX+nSizeX-1;
+ nDragEndY = nDragStartY+nSizeY-1;
+ bDragRect = true;
+ meDragInsertMode = eDragInsertMode;
+
+ UpdateDragRectOverlay();
+ }
+ }
+
+ return rEvt.mnAction;
+}
+
+sal_Int8 ScGridWindow::AcceptDrop( const AcceptDropEvent& rEvt )
+{
+ const ScDragData& rData = SC_MOD()->GetDragData();
+ if ( rEvt.mbLeaving )
+ {
+ DrawMarkDropObj( nullptr );
+ if ( rData.pCellTransfer )
+ return AcceptPrivateDrop( rEvt, rData ); // hide drop marker for internal D&D
+ else
+ return rEvt.mnAction;
+ }
+
+ if ( mrViewData.GetDocShell()->IsReadOnly() )
+ return DND_ACTION_NONE;
+
+ ScDocument& rThisDoc = mrViewData.GetDocument();
+ sal_Int8 nRet = DND_ACTION_NONE;
+
+ if (rData.pCellTransfer)
+ {
+ ScRange aSource = rData.pCellTransfer->GetRange();
+ if ( aSource.aStart.Col() != 0 || aSource.aEnd.Col() != rThisDoc.MaxCol() ||
+ aSource.aStart.Row() != 0 || aSource.aEnd.Row() != rThisDoc.MaxRow() )
+ DropScroll( rEvt.maPosPixel );
+
+ nRet = AcceptPrivateDrop( rEvt, rData );
+ }
+ else
+ {
+ if ( !rData.aLinkDoc.isEmpty() )
+ {
+ OUString aThisName;
+ ScDocShell* pDocSh = mrViewData.GetDocShell();
+ if (pDocSh && pDocSh->HasName())
+ aThisName = pDocSh->GetMedium()->GetName();
+
+ if ( rData.aLinkDoc != aThisName )
+ nRet = rEvt.mnAction;
+ }
+ else if (!rData.aJumpTarget.isEmpty())
+ {
+ // internal bookmarks (from Navigator)
+ // local jumps from an unnamed document are possible only within a document
+
+ if ( !rData.pJumpLocalDoc || rData.pJumpLocalDoc == &mrViewData.GetDocument() )
+ nRet = rEvt.mnAction;
+ }
+ else
+ {
+ sal_Int8 nMyAction = rEvt.mnAction;
+
+ // clear DND_ACTION_LINK when other actions are set. The usage below cannot handle
+ // multiple set values
+ if((nMyAction & DND_ACTION_LINK) && (nMyAction & DND_ACTION_COPYMOVE))
+ {
+ nMyAction &= ~DND_ACTION_LINK;
+ }
+
+ if ( !rData.pDrawTransfer ||
+ !IsMyModel(rData.pDrawTransfer->GetDragSourceView()) ) // drawing within the document
+ if ( rEvt.mbDefault && nMyAction == DND_ACTION_MOVE )
+ nMyAction = DND_ACTION_COPY;
+
+ SdrObject* pHitObj = rThisDoc.GetObjectAtPoint(
+ mrViewData.GetTabNo(), PixelToLogic(rEvt.maPosPixel) );
+ if ( pHitObj && nMyAction == DND_ACTION_LINK )
+ {
+ if ( IsDropFormatSupported(SotClipboardFormatId::SVXB)
+ || IsDropFormatSupported(SotClipboardFormatId::GDIMETAFILE)
+ || IsDropFormatSupported(SotClipboardFormatId::PNG)
+ || IsDropFormatSupported(SotClipboardFormatId::BITMAP) )
+ {
+ // graphic dragged onto drawing object
+ DrawMarkDropObj( pHitObj );
+ nRet = nMyAction;
+ }
+ }
+ if (!nRet)
+ {
+ DrawMarkDropObj(nullptr);
+
+ switch ( nMyAction )
+ {
+ case DND_ACTION_COPY:
+ case DND_ACTION_MOVE:
+ case DND_ACTION_COPYMOVE:
+ {
+ bool bMove = ( nMyAction == DND_ACTION_MOVE );
+ if ( IsDropFormatSupported( SotClipboardFormatId::EMBED_SOURCE ) ||
+ IsDropFormatSupported( SotClipboardFormatId::LINK_SOURCE ) ||
+ IsDropFormatSupported( SotClipboardFormatId::EMBED_SOURCE_OLE ) ||
+ IsDropFormatSupported( SotClipboardFormatId::LINK_SOURCE_OLE ) ||
+ IsDropFormatSupported( SotClipboardFormatId::EMBEDDED_OBJ_OLE ) ||
+ IsDropFormatSupported( SotClipboardFormatId::STRING ) ||
+ IsDropFormatSupported( SotClipboardFormatId::STRING_TSVC ) ||
+ IsDropFormatSupported( SotClipboardFormatId::SYLK ) ||
+ IsDropFormatSupported( SotClipboardFormatId::LINK ) ||
+ IsDropFormatSupported( SotClipboardFormatId::HTML ) ||
+ IsDropFormatSupported( SotClipboardFormatId::HTML_SIMPLE ) ||
+ IsDropFormatSupported( SotClipboardFormatId::DIF ) ||
+ IsDropFormatSupported( SotClipboardFormatId::DRAWING ) ||
+ IsDropFormatSupported( SotClipboardFormatId::SVXB ) ||
+ IsDropFormatSupported( SotClipboardFormatId::RTF ) ||
+ IsDropFormatSupported( SotClipboardFormatId::RICHTEXT ) ||
+ IsDropFormatSupported( SotClipboardFormatId::GDIMETAFILE ) ||
+ IsDropFormatSupported( SotClipboardFormatId::PNG ) ||
+ IsDropFormatSupported( SotClipboardFormatId::BITMAP ) ||
+ IsDropFormatSupported( SotClipboardFormatId::SBA_DATAEXCHANGE ) ||
+ IsDropFormatSupported( SotClipboardFormatId::SBA_FIELDDATAEXCHANGE ) ||
+ ( !bMove && (
+ IsDropFormatSupported( SotClipboardFormatId::FILE_LIST ) ||
+ IsDropFormatSupported( SotClipboardFormatId::SIMPLE_FILE ) ||
+ IsDropFormatSupported( SotClipboardFormatId::SOLK ) ||
+ IsDropFormatSupported( SotClipboardFormatId::UNIFORMRESOURCELOCATOR ) ||
+ IsDropFormatSupported( SotClipboardFormatId::NETSCAPE_BOOKMARK ) ||
+ IsDropFormatSupported( SotClipboardFormatId::FILEGRPDESCRIPTOR ) ) ) )
+ {
+ nRet = nMyAction;
+ }
+ }
+ break;
+ case DND_ACTION_LINK:
+ if ( IsDropFormatSupported( SotClipboardFormatId::LINK_SOURCE ) ||
+ IsDropFormatSupported( SotClipboardFormatId::LINK_SOURCE_OLE ) ||
+ IsDropFormatSupported( SotClipboardFormatId::LINK ) ||
+ IsDropFormatSupported( SotClipboardFormatId::FILE_LIST ) ||
+ IsDropFormatSupported( SotClipboardFormatId::SIMPLE_FILE ) ||
+ IsDropFormatSupported( SotClipboardFormatId::SOLK ) ||
+ IsDropFormatSupported( SotClipboardFormatId::UNIFORMRESOURCELOCATOR ) ||
+ IsDropFormatSupported( SotClipboardFormatId::NETSCAPE_BOOKMARK ) ||
+ IsDropFormatSupported( SotClipboardFormatId::FILEGRPDESCRIPTOR ) )
+ {
+ nRet = nMyAction;
+ }
+ break;
+ }
+
+ 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.
+
+ Point aPos = rEvt.maPosPixel;
+ SCCOL nPosX;
+ SCROW nPosY;
+ mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
+ SCTAB nTab = mrViewData.GetTabNo();
+ ScDocument& rDoc = mrViewData.GetDocument();
+
+ ScEditableTester aTester( rDoc, nTab, nPosX,nPosY, nPosX,nPosY );
+ if ( !aTester.IsFormatEditable() )
+ nRet = DND_ACTION_NONE; // forbidden
+ }
+ }
+ }
+
+ // scroll only for accepted formats
+ if (nRet)
+ DropScroll( rEvt.maPosPixel );
+ }
+
+ return nRet;
+}
+
+static SotClipboardFormatId lcl_GetDropFormatId( const uno::Reference<datatransfer::XTransferable>& xTransfer, bool bPreferText )
+{
+ TransferableDataHelper aDataHelper( xTransfer );
+
+ if ( !aDataHelper.HasFormat( SotClipboardFormatId::SBA_DATAEXCHANGE ) )
+ {
+ // use bookmark formats if no sba is present
+
+ if ( aDataHelper.HasFormat( SotClipboardFormatId::SOLK ) )
+ return SotClipboardFormatId::SOLK;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::UNIFORMRESOURCELOCATOR ) )
+ return SotClipboardFormatId::UNIFORMRESOURCELOCATOR;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::NETSCAPE_BOOKMARK ) )
+ return SotClipboardFormatId::NETSCAPE_BOOKMARK;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::FILEGRPDESCRIPTOR ) )
+ return SotClipboardFormatId::FILEGRPDESCRIPTOR;
+ }
+
+ SotClipboardFormatId nFormatId = SotClipboardFormatId::NONE;
+ if ( aDataHelper.HasFormat( SotClipboardFormatId::DRAWING ) )
+ nFormatId = SotClipboardFormatId::DRAWING;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::SVXB ) )
+ nFormatId = SotClipboardFormatId::SVXB;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::EMBED_SOURCE ) )
+ {
+ // If it's a Writer object, insert RTF instead of OLE
+
+ bool bDoRtf = false;
+ tools::SvRef<SotTempStream> xStm;
+ TransferableObjectDescriptor aObjDesc;
+ if( aDataHelper.GetTransferableObjectDescriptor( SotClipboardFormatId::OBJECTDESCRIPTOR, aObjDesc ) &&
+ aDataHelper.GetSotStorageStream( SotClipboardFormatId::EMBED_SOURCE, xStm ) )
+ {
+ bDoRtf = ( ( aObjDesc.maClassName == SvGlobalName( SO3_SW_CLASSID ) ||
+ aObjDesc.maClassName == SvGlobalName( SO3_SWWEB_CLASSID ) )
+ && ( aDataHelper.HasFormat( SotClipboardFormatId::RTF ) || aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT ) ) );
+ }
+ if ( bDoRtf )
+ nFormatId = aDataHelper.HasFormat( SotClipboardFormatId::RTF ) ? SotClipboardFormatId::RTF : SotClipboardFormatId::RICHTEXT;
+ else
+ nFormatId = SotClipboardFormatId::EMBED_SOURCE;
+ }
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::LINK_SOURCE ) )
+ nFormatId = SotClipboardFormatId::LINK_SOURCE;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::SBA_DATAEXCHANGE ) )
+ nFormatId = SotClipboardFormatId::SBA_DATAEXCHANGE;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::SBA_FIELDDATAEXCHANGE ) )
+ nFormatId = SotClipboardFormatId::SBA_FIELDDATAEXCHANGE;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::BIFF_8 ) )
+ nFormatId = SotClipboardFormatId::BIFF_8;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::BIFF_5 ) )
+ nFormatId = SotClipboardFormatId::BIFF_5;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::EMBED_SOURCE_OLE ) )
+ nFormatId = SotClipboardFormatId::EMBED_SOURCE_OLE;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::EMBEDDED_OBJ_OLE ) )
+ nFormatId = SotClipboardFormatId::EMBEDDED_OBJ_OLE;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::LINK_SOURCE_OLE ) )
+ nFormatId = SotClipboardFormatId::LINK_SOURCE_OLE;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::RTF ) )
+ nFormatId = SotClipboardFormatId::RTF;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT ) )
+ nFormatId = SotClipboardFormatId::RICHTEXT;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::HTML ) )
+ nFormatId = SotClipboardFormatId::HTML;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::HTML_SIMPLE ) )
+ nFormatId = SotClipboardFormatId::HTML_SIMPLE;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::SYLK ) )
+ nFormatId = SotClipboardFormatId::SYLK;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::LINK ) )
+ nFormatId = SotClipboardFormatId::LINK;
+ else if ( bPreferText && aDataHelper.HasFormat( SotClipboardFormatId::STRING ) ) // #i86734# the behaviour introduced in #i62773# is wrong when pasting
+ nFormatId = SotClipboardFormatId::STRING;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::FILE_LIST ) )
+ nFormatId = SotClipboardFormatId::FILE_LIST;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::SIMPLE_FILE ) ) // #i62773# FILE_LIST/FILE before STRING (Unix file managers)
+ nFormatId = SotClipboardFormatId::SIMPLE_FILE;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::STRING_TSVC ) )
+ nFormatId = SotClipboardFormatId::STRING_TSVC;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::STRING ) )
+ nFormatId = SotClipboardFormatId::STRING;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::GDIMETAFILE ) )
+ nFormatId = SotClipboardFormatId::GDIMETAFILE;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::PNG ) )
+ nFormatId = SotClipboardFormatId::PNG;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::BITMAP ) )
+ nFormatId = SotClipboardFormatId::BITMAP;
+
+ return nFormatId;
+}
+
+static SotClipboardFormatId lcl_GetDropLinkId( const uno::Reference<datatransfer::XTransferable>& xTransfer )
+{
+ TransferableDataHelper aDataHelper( xTransfer );
+
+ SotClipboardFormatId nFormatId = SotClipboardFormatId::NONE;
+ if ( aDataHelper.HasFormat( SotClipboardFormatId::LINK_SOURCE ) )
+ nFormatId = SotClipboardFormatId::LINK_SOURCE;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::LINK_SOURCE_OLE ) )
+ nFormatId = SotClipboardFormatId::LINK_SOURCE_OLE;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::LINK ) )
+ nFormatId = SotClipboardFormatId::LINK;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::FILE_LIST ) )
+ nFormatId = SotClipboardFormatId::FILE_LIST;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::SIMPLE_FILE ) )
+ nFormatId = SotClipboardFormatId::SIMPLE_FILE;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::SOLK ) )
+ nFormatId = SotClipboardFormatId::SOLK;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::UNIFORMRESOURCELOCATOR ) )
+ nFormatId = SotClipboardFormatId::UNIFORMRESOURCELOCATOR;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::NETSCAPE_BOOKMARK ) )
+ nFormatId = SotClipboardFormatId::NETSCAPE_BOOKMARK;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::FILEGRPDESCRIPTOR ) )
+ nFormatId = SotClipboardFormatId::FILEGRPDESCRIPTOR;
+
+ return nFormatId;
+}
+
+sal_Int8 ScGridWindow::ExecutePrivateDrop( const ExecuteDropEvent& rEvt, const ScDragData& rData )
+{
+ // hide drop marker
+ bDragRect = false;
+ UpdateDragRectOverlay();
+
+ return DropTransferObj( rData.pCellTransfer, nDragStartX, nDragStartY,
+ PixelToLogic(rEvt.maPosPixel), rEvt.mnAction );
+}
+
+sal_Int8 ScGridWindow::DropTransferObj( ScTransferObj* pTransObj, SCCOL nDestPosX, SCROW nDestPosY,
+ const Point& rLogicPos, sal_Int8 nDndAction )
+{
+ if ( !pTransObj )
+ return 0;
+
+ ScDocument* pSourceDoc = pTransObj->GetSourceDocument();
+ ScDocShell* pDocSh = mrViewData.GetDocShell();
+ ScDocument& rThisDoc = mrViewData.GetDocument();
+ ScViewFunc* pView = mrViewData.GetView();
+ SCTAB nThisTab = mrViewData.GetTabNo();
+ ScDragSrc nFlags = pTransObj->GetDragSourceFlags();
+
+ bool bIsNavi = (nFlags & ScDragSrc::Navigator) == ScDragSrc::Navigator;
+ bool bIsMove = ( nDndAction == DND_ACTION_MOVE && !bIsNavi );
+
+ // 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 );
+
+ SCCOL nSizeX = aSource.aEnd.Col() - aSource.aStart.Col() + 1;
+ SCROW nSizeY = (bIsMove ? (aSource.aEnd.Row() - aSource.aStart.Row() + 1) :
+ pTransObj->GetNonFilteredRows()); // copy/link: no filtered rows
+ ScRange aDest( nDestPosX, nDestPosY, nThisTab,
+ nDestPosX + nSizeX - 1, nDestPosY + nSizeY - 1, nThisTab );
+
+ /* 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;
+ }
+ else if ( rThisDoc.GetDPAtCursor( nDestPosX, nDestPosY, nThisTab ) )
+ {
+ // drop on DataPilot table: try to sort, fail if that isn't possible
+
+ ScAddress aDestPos( nDestPosX, nDestPosY, nThisTab );
+ if ( aDestPos != aSource.aStart )
+ bDone = mrViewData.GetView()->DataPilotMove( aSource, aDestPos );
+ else
+ bDone = true; // same position: nothing
+ }
+ else if ( nDestPosX != aSource.aStart.Col() || nDestPosY != aSource.aStart.Row() ||
+ nSourceTab != nThisTab )
+ {
+ OUString aUndo = ScResId( bIsMove ? STR_UNDO_MOVE : STR_UNDO_COPY );
+ pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, mrViewData.GetViewShell()->GetViewShellId() );
+
+ SCCOL nCorrectCursorPosCol = 0;
+ SCROW nCorrectCursorPosRow = 0;
+
+ bDone = true;
+ if ( meDragInsertMode != INS_NONE )
+ {
+ // call with bApi = sal_True to avoid error messages in drop handler
+ bDone = pDocSh->GetDocFunc().InsertCells( aDest, nullptr, meDragInsertMode, true /*bRecord*/, true /*bApi*/, true /*bPartOfPaste*/ );
+ if ( bDone )
+ {
+ if ( nThisTab == nSourceTab )
+ {
+ if ( meDragInsertMode == INS_CELLSDOWN &&
+ nDestPosX == aSource.aStart.Col() && nDestPosY < aSource.aStart.Row() )
+ {
+ ScRange aErrorRange( ScAddress::UNINITIALIZED );
+ bDone = aSource.Move( 0, nSizeY, 0, aErrorRange, *pSourceDoc );
+ nCorrectCursorPosRow = nSizeY;
+ }
+ else if ( meDragInsertMode == INS_CELLSRIGHT &&
+ nDestPosY == aSource.aStart.Row() && nDestPosX < aSource.aStart.Col() )
+ {
+ ScRange aErrorRange( ScAddress::UNINITIALIZED );
+ bDone = aSource.Move( nSizeX, 0, 0, aErrorRange, *pSourceDoc );
+ nCorrectCursorPosCol = nSizeX;
+ }
+ }
+ pDocSh->UpdateOle(mrViewData);
+ pView->CellContentChanged();
+ }
+ }
+
+ if ( bDone )
+ {
+ if ( bIsLink )
+ {
+ bDone = pView->LinkBlock( aSource, aDest.aStart );
+ }
+ else
+ {
+ bDone = pView->MoveBlockTo( aSource, aDest.aStart, bIsMove );
+ }
+ }
+
+ if ( bDone && meDragInsertMode != INS_NONE && bIsMove && nThisTab == nSourceTab )
+ {
+ DelCellCmd eCmd = DelCellCmd::NONE;
+ if ( meDragInsertMode == INS_CELLSDOWN )
+ {
+ eCmd = DelCellCmd::CellsUp;
+ }
+ else if ( meDragInsertMode == INS_CELLSRIGHT )
+ {
+ eCmd = DelCellCmd::CellsLeft;
+ }
+
+ if ( ( eCmd == DelCellCmd::CellsUp && nDestPosX == aSource.aStart.Col() ) ||
+ ( eCmd == DelCellCmd::CellsLeft && nDestPosY == aSource.aStart.Row() ) )
+ {
+ // call with bApi = sal_True to avoid error messages in drop handler
+ bDone = pDocSh->GetDocFunc().DeleteCells( aSource, nullptr, eCmd, true /*bApi*/ );
+ if ( bDone )
+ {
+ if ( eCmd == DelCellCmd::CellsUp && nDestPosY > aSource.aEnd.Row() )
+ {
+ ScRange aErrorRange( ScAddress::UNINITIALIZED );
+ bDone = aDest.Move( 0, -nSizeY, 0, aErrorRange, rThisDoc );
+ }
+ else if ( eCmd == DelCellCmd::CellsLeft && nDestPosX > aSource.aEnd.Col() )
+ {
+ ScRange aErrorRange( ScAddress::UNINITIALIZED );
+ bDone = aDest.Move( -nSizeX, 0, 0, aErrorRange, rThisDoc );
+ }
+ pDocSh->UpdateOle(mrViewData);
+ pView->CellContentChanged();
+ }
+ }
+ }
+
+ if ( bDone )
+ {
+ pView->MarkRange( aDest, false );
+
+ SCCOL nDCol;
+ SCROW nDRow;
+ if (pTransObj->WasSourceCursorInSelection())
+ {
+ nDCol = pTransObj->GetSourceCursorX() - aSource.aStart.Col() + nCorrectCursorPosCol;
+ nDRow = pTransObj->GetSourceCursorY() - aSource.aStart.Row() + nCorrectCursorPosRow;
+ }
+ else
+ {
+ nDCol = 0;
+ nDRow = 0;
+ }
+ pView->SetCursor( aDest.aStart.Col() + nDCol, aDest.aStart.Row() + nDRow );
+ }
+
+ pDocSh->GetUndoManager()->LeaveListAction();
+
+ }
+ else
+ bDone = true; // nothing to do
+ }
+
+ if (bDone)
+ pTransObj->SetDragWasInternal(); // don't delete source in DragFinished
+ }
+ else if ( !bFiltered && pSourceDoc ) // between documents
+ {
+ if (nFlags & ScDragSrc::Table) // copy/link sheets between documents
+ {
+ if ( rThisDoc.IsDocEditable() )
+ {
+ ScDocShell* pSrcShell = pTransObj->GetSourceDocShell();
+
+ std::vector<SCTAB> nTabs;
+
+ ScMarkData aMark = pTransObj->GetSourceMarkData();
+ SCTAB nTabCount = pSourceDoc->GetTableCount();
+
+ for(SCTAB i=0; i<nTabCount; i++)
+ {
+ if(aMark.GetTableSelect(i))
+ {
+ nTabs.push_back(i);
+ for(SCTAB j=i+1;j<nTabCount;j++)
+ {
+ if((!pSourceDoc->IsVisible(j))&&(pSourceDoc->IsScenario(j)))
+ {
+ nTabs.push_back( j );
+ i=j;
+ }
+ else break;
+ }
+ }
+ }
+
+ pView->ImportTables( pSrcShell,static_cast<SCTAB>(nTabs.size()), nTabs.data(), bIsLink, nThisTab );
+ bDone = true;
+ }
+ }
+ else if ( bIsLink )
+ {
+ // as in PasteDDE
+ // (external references might be used instead?)
+
+ ScDocShell* pSourceSh = pSourceDoc->GetDocumentShell();
+ OSL_ENSURE(pSourceSh, "drag document has no shell");
+ if (pSourceSh)
+ {
+ OUString aUndo = ScResId( STR_UNDO_COPY );
+ pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, mrViewData.GetViewShell()->GetViewShellId() );
+
+ bDone = true;
+ if ( meDragInsertMode != INS_NONE )
+ {
+ // call with bApi = sal_True to avoid error messages in drop handler
+ bDone = pDocSh->GetDocFunc().InsertCells( aDest, nullptr, meDragInsertMode, true /*bRecord*/, true /*bApi*/, true /*bPartOfPaste*/ );
+ if ( bDone )
+ {
+ pDocSh->UpdateOle(mrViewData);
+ pView->CellContentChanged();
+ }
+ }
+
+ if ( bDone )
+ {
+ OUString aApp = Application::GetAppName();
+ OUString aTopic = pSourceSh->GetTitle( SFX_TITLE_FULLNAME );
+ OUString aItem(aSource.Format(*pSourceDoc, ScRefFlags::VALID | ScRefFlags::TAB_3D));
+
+ // TODO: we could define ocQuote for "
+ const OUString aQuote('"');
+ const OUString& sSep = ScCompiler::GetNativeSymbol( ocSep);
+ OUString aFormula =
+ "=" +
+ ScCompiler::GetNativeSymbol(ocDde) +
+ ScCompiler::GetNativeSymbol(ocOpen) +
+ aQuote +
+ aApp +
+ aQuote +
+ sSep +
+ aQuote +
+ aTopic +
+ aQuote +
+ sSep +
+ aQuote +
+ aItem +
+ aQuote +
+ ScCompiler::GetNativeSymbol(ocClose);
+
+ pView->DoneBlockMode();
+ pView->InitBlockMode( nDestPosX, nDestPosY, nThisTab );
+ pView->MarkCursor( nDestPosX + nSizeX - 1,
+ nDestPosY + nSizeY - 1, nThisTab );
+
+ pView->EnterMatrix( aFormula, ::formula::FormulaGrammar::GRAM_NATIVE );
+
+ pView->MarkRange( aDest, false );
+ pView->SetCursor( aDest.aStart.Col(), aDest.aStart.Row() );
+ }
+
+ pDocSh->GetUndoManager()->LeaveListAction();
+ }
+ }
+ else
+ {
+ //! HasSelectedBlockMatrixFragment without selected sheet?
+ //! or don't start dragging on a part of a matrix
+
+ OUString aUndo = ScResId( bIsMove ? STR_UNDO_MOVE : STR_UNDO_COPY );
+ pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, mrViewData.GetViewShell()->GetViewShellId() );
+
+ bDone = true;
+ if ( meDragInsertMode != INS_NONE )
+ {
+ // call with bApi = sal_True to avoid error messages in drop handler
+ bDone = pDocSh->GetDocFunc().InsertCells( aDest, nullptr, meDragInsertMode, true /*bRecord*/, true /*bApi*/, true /*bPartOfPaste*/ );
+ if ( bDone )
+ {
+ pDocSh->UpdateOle(mrViewData);
+ pView->CellContentChanged();
+ }
+ }
+
+ if ( bDone )
+ {
+ pView->Unmark(); // before SetCursor, so CheckSelectionTransfer isn't called with a selection
+ pView->SetCursor( nDestPosX, nDestPosY );
+ bDone = pView->PasteFromClip( InsertDeleteFlags::ALL, pTransObj->GetDocument() ); // clip-doc
+ if ( bDone )
+ {
+ pView->MarkRange( aDest, false );
+ pView->SetCursor( aDest.aStart.Col(), aDest.aStart.Row() );
+ }
+ }
+
+ pDocSh->GetUndoManager()->LeaveListAction();
+
+ // no longer call ResetMark here - the inserted block has been selected
+ // and may have been copied to primary selection
+ }
+ }
+
+ sal_Int8 nRet = bDone ? nDndAction : DND_ACTION_NONE;
+ return nRet;
+}
+
+sal_Int8 ScGridWindow::ExecuteDrop( const ExecuteDropEvent& rEvt )
+{
+ DrawMarkDropObj( nullptr ); // drawing layer
+
+ ScModule* pScMod = SC_MOD();
+ const ScDragData& rData = pScMod->GetDragData();
+ if (rData.pCellTransfer)
+ return ExecutePrivateDrop( rEvt, rData );
+
+ Point aPos = rEvt.maPosPixel;
+
+ if ( !rData.aLinkDoc.isEmpty() )
+ {
+ // try to insert a link
+
+ bool bOk = true;
+ OUString aThisName;
+ ScDocShell* pDocSh = mrViewData.GetDocShell();
+ if (pDocSh && pDocSh->HasName())
+ aThisName = pDocSh->GetMedium()->GetName();
+
+ if ( rData.aLinkDoc == aThisName ) // error - no link within a document
+ bOk = false;
+ else
+ {
+ ScViewFunc* pView = mrViewData.GetView();
+ if ( !rData.aLinkTable.isEmpty() )
+ pView->InsertTableLink( rData.aLinkDoc, OUString(), OUString(),
+ rData.aLinkTable );
+ else if ( !rData.aLinkArea.isEmpty() )
+ {
+ SCCOL nPosX;
+ SCROW nPosY;
+ mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
+ pView->MoveCursorAbs( nPosX, nPosY, SC_FOLLOW_NONE, false, false );
+
+ pView->InsertAreaLink( rData.aLinkDoc, OUString(), OUString(),
+ rData.aLinkArea );
+ }
+ else
+ {
+ OSL_FAIL("drop with link: no sheet nor area");
+ bOk = false;
+ }
+ }
+
+ return bOk ? rEvt.mnAction : DND_ACTION_NONE; // don't try anything else
+ }
+
+ Point aLogicPos = PixelToLogic(aPos);
+ bool bIsLink = ( rEvt.mnAction == DND_ACTION_LINK );
+
+ if (!bIsLink && rData.pDrawTransfer)
+ {
+ ScDragSrc nFlags = rData.pDrawTransfer->GetDragSourceFlags();
+
+ bool bIsNavi = (nFlags & ScDragSrc::Navigator) == ScDragSrc::Navigator;
+ bool bIsMove = ( rEvt.mnAction == DND_ACTION_MOVE && !bIsNavi );
+
+ bPasteIsMove = bIsMove;
+
+ mrViewData.GetView()->PasteDraw(
+ aLogicPos, rData.pDrawTransfer->GetModel(), false, u"A", u"B");
+
+ if (bPasteIsMove)
+ rData.pDrawTransfer->SetDragWasInternal();
+ bPasteIsMove = false;
+
+ return rEvt.mnAction;
+ }
+
+ SCCOL nPosX;
+ SCROW nPosY;
+ mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
+
+ if (!rData.aJumpTarget.isEmpty())
+ {
+ // internal bookmark (from Navigator)
+ // bookmark clipboard formats are in PasteScDataObject
+
+ if ( !rData.pJumpLocalDoc || rData.pJumpLocalDoc == &mrViewData.GetDocument() )
+ {
+ mrViewData.GetViewShell()->InsertBookmark( rData.aJumpText, rData.aJumpTarget,
+ nPosX, nPosY );
+ return rEvt.mnAction;
+ }
+ }
+
+ ScDocument& rThisDoc = mrViewData.GetDocument();
+ SdrObject* pHitObj = rThisDoc.GetObjectAtPoint( mrViewData.GetTabNo(), PixelToLogic(aPos) );
+ if ( pHitObj && bIsLink )
+ {
+ // dropped on drawing object
+ // PasteOnDrawObjectLinked checks for valid formats
+ if ( mrViewData.GetView()->PasteOnDrawObjectLinked( rEvt.maDropEvent.Transferable, *pHitObj ) )
+ return rEvt.mnAction;
+ }
+
+ bool bDone = false;
+
+ SotClipboardFormatId nFormatId = bIsLink ?
+ lcl_GetDropLinkId( rEvt.maDropEvent.Transferable ) :
+ lcl_GetDropFormatId( rEvt.maDropEvent.Transferable, false );
+ if ( nFormatId != SotClipboardFormatId::NONE )
+ {
+ pScMod->SetInExecuteDrop( true ); // #i28468# prevent error messages from PasteDataFormat
+ bDone = mrViewData.GetView()->PasteDataFormat(
+ nFormatId, rEvt.maDropEvent.Transferable, nPosX, nPosY, &aLogicPos, bIsLink );
+ pScMod->SetInExecuteDrop( false );
+ }
+
+ sal_Int8 nRet = bDone ? rEvt.mnAction : DND_ACTION_NONE;
+ return nRet;
+}
+
+void ScGridWindow::PasteSelection( const Point& rPosPixel )
+{
+ Point aLogicPos = PixelToLogic( rPosPixel );
+
+ SCCOL nPosX;
+ SCROW nPosY;
+ mrViewData.GetPosFromPixel( rPosPixel.X(), rPosPixel.Y(), eWhich, nPosX, nPosY );
+
+ // 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 size_t nCount = pDrawView->GetMarkedObjectCount();
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ SdrObject* pObj = pDrawView->GetMarkedObjectByIndex(i);
+ if (pObj && pObj->GetLogicRect().Contains(aLogicPos))
+ {
+ // Inside an active drawing object. Bail out.
+ return;
+ }
+ }
+ }
+
+ ScSelectionTransferObj* pOwnSelection = SC_MOD()->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;
+
+ EditView* pView;
+ SCCOL nCol;
+ SCROW nRow;
+ mrViewData.GetEditView( eWhich, pView, nCol, nRow );
+ SCCOL nEndCol = mrViewData.GetEditEndCol();
+ SCROW nEndRow = mrViewData.GetEditEndRow();
+
+ // hide EditView?
+
+ bool bHide = ( nEndCol<mrViewData.GetPosX(eHWhich) || nEndRow<mrViewData.GetPosY(eVWhich) );
+ if ( SC_MOD()->IsFormulaMode() )
+ if ( mrViewData.GetTabNo() != mrViewData.GetRefTabNo() )
+ bHide = true;
+
+ if (bHide)
+ {
+ tools::Rectangle aRect = pView->GetOutputArea();
+ tools::Long nHeight = aRect.Bottom() - aRect.Top();
+ aRect.SetTop( PixelToLogic(GetOutputSizePixel(), mrViewData.GetLogicMode()).
+ Height() * 2 );
+ aRect.SetBottom( aRect.Top() + nHeight );
+ pView->SetOutputArea( aRect );
+ pView->HideCursor();
+ }
+ else
+ {
+ // bForceToTop = sal_True for editing
+ tools::Rectangle aPixRect = mrViewData.GetEditArea( eWhich, nCol, nRow, this, nullptr, true );
+
+ if (comphelper::LibreOfficeKit::isActive() &&
+ comphelper::LibreOfficeKit::isCompatFlagSet(
+ comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
+ {
+ tools::Rectangle aPTwipsRect = mrViewData.GetEditArea(eWhich, nCol, nRow, this, nullptr,
+ true, true /* bInPrintTwips */);
+ tools::Rectangle aOutputAreaPTwips = pView->GetLOKSpecialOutputArea();
+ aOutputAreaPTwips.SetPos(aPTwipsRect.TopLeft());
+ pView->SetLOKSpecialOutputArea(aOutputAreaPTwips);
+ }
+
+ Point aScrPos = PixelToLogic( aPixRect.TopLeft(), mrViewData.GetLogicMode() );
+
+ tools::Rectangle aRect = pView->GetOutputArea();
+ aRect.SetPos( aScrPos );
+ pView->SetOutputArea( aRect );
+ pView->ShowCursor();
+ }
+}
+
+void ScGridWindow::ScrollPixel( tools::Long nDifX, tools::Long nDifY )
+{
+ ClickExtern();
+ HideNoteMarker();
+
+ SetMapMode(MapMode(MapUnit::MapPixel));
+ Scroll( nDifX, nDifY, ScrollFlags::Children );
+ SetMapMode( GetDrawMapMode() ); // generated shifted MapMode
+
+ UpdateEditViewPos();
+
+ DrawAfterScroll();
+}
+
+// Update Formulas ------------------------------------------------------
+
+void ScGridWindow::UpdateFormulas(SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2)
+{
+ if (mrViewData.GetView()->IsMinimized())
+ return;
+
+ if ( nPaintCount )
+ {
+ // Do not start, switched to paint
+ // (then at least the MapMode would no longer be right)
+
+ bNeedsRepaint = true; // -> at end of paint run Invalidate on all
+ aRepaintPixel = tools::Rectangle(); // All
+ return;
+ }
+
+ if ( comphelper::LibreOfficeKit::isActive() )
+ {
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+ if (nX1 < 0)
+ nX1 = pViewShell->GetLOKStartHeaderCol() + 1;
+ if (nY1 < 0)
+ nY1 = pViewShell->GetLOKStartHeaderRow() + 1;
+ if (nX2 < 0)
+ nX2 = pViewShell->GetLOKEndHeaderCol();
+ if (nY2 < 0)
+ nY2 = pViewShell->GetLOKEndHeaderRow();
+
+ if (nX1 < 0 || nY1 < 0) return;
+ }
+ else
+ {
+ nX1 = mrViewData.GetPosX( eHWhich );
+ nY1 = mrViewData.GetPosY( eVWhich );
+ nX2 = nX1 + mrViewData.VisibleCellsX( eHWhich );
+ nY2 = nY1 + mrViewData.VisibleCellsY( eVWhich );
+ }
+
+ if (nX2 < nX1) nX2 = nX1;
+ if (nY2 < nY1) nY2 = nY1;
+
+ ScDocument& rDoc = mrViewData.GetDocument();
+
+ if (nX2 > rDoc.MaxCol()) nX2 = rDoc.MaxCol();
+ if (nY2 > rDoc.MaxRow()) nY2 = rDoc.MaxRow();
+
+ // Draw( nX1, nY1, nX2, nY2, SC_UPDATE_CHANGED );
+
+ // don't draw directly - instead use OutputData to find changed area and invalidate
+
+ SCROW nPosY = nY1;
+
+ SCTAB nTab = mrViewData.GetTabNo();
+
+ if ( !comphelper::LibreOfficeKit::isActive() )
+ {
+ rDoc.ExtendHidden( nX1, nY1, nX2, nY2, nTab );
+ }
+
+ Point aScrPos = mrViewData.GetScrPos( nX1, nY1, eWhich );
+ tools::Long nMirrorWidth = GetSizePixel().Width();
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+ if ( bLayoutRTL )
+ {
+ tools::Long nEndPixel = mrViewData.GetScrPos( nX2+1, nPosY, eWhich ).X();
+ nMirrorWidth = aScrPos.X() - nEndPixel;
+ aScrPos.setX( nEndPixel + 1 );
+ }
+
+ tools::Long nScrX = aScrPos.X();
+ tools::Long nScrY = aScrPos.Y();
+
+ double nPPTX = mrViewData.GetPPTX();
+ double nPPTY = mrViewData.GetPPTY();
+
+ ScTableInfo aTabInfo;
+ rDoc.FillInfo( aTabInfo, nX1, nY1, nX2, nY2, nTab, nPPTX, nPPTY, false, false );
+
+ Fraction aZoomX = mrViewData.GetZoomX();
+ Fraction aZoomY = mrViewData.GetZoomY();
+ ScOutputData aOutputData( GetOutDev(), OUTTYPE_WINDOW, aTabInfo, &rDoc, nTab,
+ nScrX, nScrY, nX1, nY1, nX2, nY2, nPPTX, nPPTY,
+ &aZoomX, &aZoomY );
+ aOutputData.SetMirrorWidth( nMirrorWidth );
+
+ aOutputData.FindChanged();
+
+ // #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::UpdateAutoFillMark(bool bMarked, const ScRange& rMarkRange)
+{
+ if ( bMarked != bAutoMarkVisible || ( bMarked && rMarkRange.aEnd != aAutoMarkPos ) )
+ {
+ bAutoMarkVisible = bMarked;
+ if ( bMarked )
+ aAutoMarkPos = rMarkRange.aEnd;
+
+ UpdateAutoFillOverlay();
+ }
+}
+
+void ScGridWindow::updateLOKInputHelp(const OUString& title, const OUString& content) const
+{
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+
+ boost::property_tree::ptree aTree;
+ aTree.put("title", title);
+ aTree.put("content", content);
+
+ std::stringstream aStream;
+ boost::property_tree::write_json(aStream, aTree);
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_VALIDITY_INPUT_HELP, OString(aStream.str()));
+}
+
+void ScGridWindow::updateLOKValListButton( bool bVisible, const ScAddress& rPos ) const
+{
+ SCCOL nX = rPos.Col();
+ SCROW nY = rPos.Row();
+ std::stringstream ss;
+ ss << nX << ", " << nY << ", " << static_cast<unsigned int>(bVisible);
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_VALIDITY_LIST_BUTTON, OString(ss.str()));
+}
+
+void ScGridWindow::notifyKitCellFollowJump( ) const
+{
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_SC_FOLLOW_JUMP, getCellCursor());
+}
+
+void ScGridWindow::UpdateListValPos( bool bVisible, const ScAddress& rPos )
+{
+ bool bOldButton = bListValButton;
+ ScAddress aOldPos = aListValPos;
+
+ bListValButton = bVisible;
+ aListValPos = rPos;
+
+ if ( bListValButton )
+ {
+ if ( !bOldButton || aListValPos != aOldPos )
+ {
+ // paint area of new button
+ if ( comphelper::LibreOfficeKit::isActive() )
+ {
+ updateLOKValListButton( true, aListValPos );
+ }
+ else
+ {
+ Invalidate( PixelToLogic( GetListValButtonRect( aListValPos ) ) );
+ }
+ }
+ }
+ if ( !bOldButton )
+ return;
+
+ if ( !bListValButton || aListValPos != aOldPos )
+ {
+ // paint area of old button
+ if ( comphelper::LibreOfficeKit::isActive() )
+ {
+ updateLOKValListButton( false, aOldPos );
+ }
+ else
+ {
+ Invalidate( PixelToLogic( GetListValButtonRect( aOldPos ) ) );
+ }
+ }
+}
+
+void ScGridWindow::HideCursor()
+{
+ ++nCursorHideCount;
+}
+
+void ScGridWindow::ShowCursor()
+{
+ --nCursorHideCount;
+}
+
+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 ( !SC_MOD()->IsFormulaMode() )
+ {
+ pViewShell->UpdateInputHandler();
+// StopMarking(); // If Dialog (error), because then no ButtonUp
+ // MO: only when not in RefInput mode
+ // -> GetFocus/MouseButtonDown order on Mac
+ }
+
+ mrViewData.GetDocShell()->CheckConfigOptions();
+ Window::GetFocus();
+}
+
+void ScGridWindow::LoseFocus()
+{
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+
+ if (pViewShell && pViewShell->HasAccessibilityObjects())
+ pViewShell->BroadcastAccessibility(ScAccGridWinFocusLostHint(eWhich));
+
+ Window::LoseFocus();
+}
+
+bool ScGridWindow::HitRangeFinder( const Point& rMouse, RfCorner& rCorner,
+ sal_uInt16* pIndex, SCCOL* pAddX, SCROW* pAddY)
+{
+ bool bFound = false;
+ ScInputHandler* pHdl = SC_MOD()->GetInputHdl( mrViewData.GetViewShell() );
+ if (pHdl)
+ {
+ ScRangeFindList* pRangeFinder = pHdl->GetRangeFindList();
+ if ( pRangeFinder && !pRangeFinder->IsHidden() &&
+ pRangeFinder->GetDocName() == mrViewData.GetDocShell()->GetTitle() )
+ {
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCTAB nTab = mrViewData.GetTabNo();
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ SCCOL nPosX;
+ SCROW nPosY;
+ mrViewData.GetPosFromPixel( rMouse.X(), rMouse.Y(), eWhich, nPosX, nPosY );
+ // merged (single/Range) ???
+ ScAddress aAddr( nPosX, nPosY, nTab );
+
+ Point aCellStart = mrViewData.GetScrPos( nPosX, nPosY, eWhich, true );
+ Point aCellEnd = aCellStart;
+ tools::Long nSizeXPix;
+ tools::Long nSizeYPix;
+ mrViewData.GetMergeSizePixel( nPosX, nPosY, nSizeXPix, nSizeYPix );
+
+ aCellEnd.AdjustX(nSizeXPix * nLayoutSign );
+ aCellEnd.AdjustY(nSizeYPix );
+
+ bool bCornerHorizontalRight;
+ bool bCornerHorizontalLeft;
+ if ( bLayoutRTL )
+ {
+ bCornerHorizontalRight = ( rMouse.X() >= aCellEnd.X() && rMouse.X() <= aCellEnd.X() + 8 );
+ bCornerHorizontalLeft = ( rMouse.X() >= aCellStart.X() - 8 && rMouse.X() <= aCellStart.X() );
+ }
+ else
+ {
+ bCornerHorizontalRight = ( rMouse.X() >= aCellEnd.X() - 8 && rMouse.X() <= aCellEnd.X() );
+ bCornerHorizontalLeft = ( rMouse.X() >= aCellStart.X() && rMouse.X() <= aCellStart.X() + 8 );
+ }
+
+ bool bCornerVerticalDown = rMouse.Y() >= aCellEnd.Y() - 8 && rMouse.Y() <= aCellEnd.Y();
+ bool bCornerVerticalUp = rMouse.Y() >= aCellStart.Y() && rMouse.Y() <= aCellStart.Y() + 8;
+
+ // 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();
+
+ bFound = true;
+
+ rCorner = NONE;
+
+ ScAddress aEnd = rData.aRef.aEnd;
+ ScAddress aStart = rData.aRef.aStart;
+
+ if ( bCornerHorizontalLeft && bCornerVerticalUp &&
+ aAddr == aStart)
+ {
+ rCorner = LEFT_UP;
+ }
+ else if (bCornerHorizontalRight && bCornerVerticalDown &&
+ aAddr == aEnd)
+ {
+ rCorner = RIGHT_DOWN;
+ }
+ else if (bCornerHorizontalRight && bCornerVerticalUp &&
+ aAddr == ScAddress(aEnd.Col(), aStart.Row(), aStart.Tab()))
+ {
+ rCorner = RIGHT_UP;
+ }
+ else if (bCornerHorizontalLeft && bCornerVerticalDown &&
+ aAddr == ScAddress(aStart.Col(), aEnd.Row(), aStart.Tab()))
+ {
+ rCorner = LEFT_DOWN;
+ }
+ break;
+ }
+ }
+ }
+ }
+ return bFound;
+}
+
+#define SCE_TOP 1
+#define SCE_BOTTOM 2
+#define SCE_LEFT 4
+#define SCE_RIGHT 8
+#define SCE_ALL 15
+
+static void lcl_PaintOneRange( ScDocShell* pDocSh, const ScRange& rRange, sal_uInt16 nEdges )
+{
+ // the range is always properly oriented
+
+ SCCOL nCol1 = rRange.aStart.Col();
+ SCROW nRow1 = rRange.aStart.Row();
+ SCTAB nTab1 = rRange.aStart.Tab();
+ SCCOL nCol2 = rRange.aEnd.Col();
+ SCROW nRow2 = rRange.aEnd.Row();
+ SCTAB nTab2 = rRange.aEnd.Tab();
+ bool bHiddenEdge = false;
+ SCROW nTmp;
+
+ ScDocument& rDoc = pDocSh->GetDocument();
+ while ( nCol1 > 0 && rDoc.ColHidden(nCol1, nTab1) )
+ {
+ --nCol1;
+ bHiddenEdge = true;
+ }
+ while ( nCol2 < rDoc.MaxCol() && rDoc.ColHidden(nCol2, nTab1) )
+ {
+ ++nCol2;
+ bHiddenEdge = true;
+ }
+ nTmp = rDoc.FirstVisibleRow(0, nRow1, nTab1);
+ if (!rDoc.ValidRow(nTmp))
+ nTmp = 0;
+ if (nTmp < nRow1)
+ {
+ nRow1 = nTmp;
+ bHiddenEdge = true;
+ }
+ nTmp = rDoc.FirstVisibleRow(nRow2, rDoc.MaxRow(), nTab1);
+ if (!rDoc.ValidRow(nTmp))
+ nTmp = rDoc.MaxRow();
+ if (nTmp > nRow2)
+ {
+ nRow2 = nTmp;
+ bHiddenEdge = true;
+ }
+
+ if ( nCol2 > nCol1 + 1 && nRow2 > nRow1 + 1 && !bHiddenEdge )
+ {
+ // Only along the edges (The corners are hit twice)
+ if ( nEdges & SCE_TOP )
+ pDocSh->PostPaint( nCol1, nRow1, nTab1, nCol2, nRow1, nTab2, PaintPartFlags::Marks );
+ if ( nEdges & SCE_LEFT )
+ pDocSh->PostPaint( nCol1, nRow1, nTab1, nCol1, nRow2, nTab2, PaintPartFlags::Marks );
+ if ( nEdges & SCE_RIGHT )
+ pDocSh->PostPaint( nCol2, nRow1, nTab1, nCol2, nRow2, nTab2, PaintPartFlags::Marks );
+ if ( nEdges & SCE_BOTTOM )
+ pDocSh->PostPaint( nCol1, nRow2, nTab1, nCol2, nRow2, nTab2, PaintPartFlags::Marks );
+ }
+ else // everything in one call
+ pDocSh->PostPaint( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2, PaintPartFlags::Marks );
+}
+
+static void lcl_PaintRefChanged( ScDocShell* pDocSh, const ScRange& rOldUn, const ScRange& rNewUn )
+{
+ // Repaint for the parts of the frame in old, which in are no more in New
+
+ ScRange aOld = rOldUn;
+ ScRange aNew = rNewUn;
+ aOld.PutInOrder();
+ aNew.PutInOrder();
+
+ if ( aOld.aStart == aOld.aEnd ) //! Ignore sheet ?
+ pDocSh->GetDocument().ExtendMerge(aOld);
+ if ( aNew.aStart == aNew.aEnd ) //! Ignore sheet ?
+ pDocSh->GetDocument().ExtendMerge(aNew);
+
+ SCCOL nOldCol1 = aOld.aStart.Col();
+ SCROW nOldRow1 = aOld.aStart.Row();
+ SCCOL nOldCol2 = aOld.aEnd.Col();
+ SCROW nOldRow2 = aOld.aEnd.Row();
+ SCCOL nNewCol1 = aNew.aStart.Col();
+ SCROW nNewRow1 = aNew.aStart.Row();
+ SCCOL nNewCol2 = aNew.aEnd.Col();
+ SCROW nNewRow2 = aNew.aEnd.Row();
+ SCTAB nTab1 = aOld.aStart.Tab(); // sheet is not changed
+ SCTAB nTab2 = aOld.aEnd.Tab();
+
+ 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( pDocSh, aOld, SCE_ALL );
+ }
+ else // Test all four corners separately
+ {
+ // upper part
+ if ( nNewRow1 < nOldRow1 ) // only delete upper line
+ lcl_PaintOneRange( pDocSh, ScRange(
+ nOldCol1, nOldRow1, nTab1, nOldCol2, nOldRow1, nTab2 ), SCE_ALL );
+ else if ( nNewRow1 > nOldRow1 ) // the upper part which is will be removed
+ lcl_PaintOneRange( pDocSh, ScRange(
+ nOldCol1, nOldRow1, nTab1, nOldCol2, nNewRow1-1, nTab2 ),
+ SCE_ALL &~ SCE_BOTTOM );
+
+ // bottom part
+ if ( nNewRow2 > nOldRow2 ) // only delete bottom line
+ lcl_PaintOneRange( pDocSh, ScRange(
+ nOldCol1, nOldRow2, nTab1, nOldCol2, nOldRow2, nTab2 ), SCE_ALL );
+ else if ( nNewRow2 < nOldRow2 ) // the bottom part which is will be removed
+ lcl_PaintOneRange( pDocSh, ScRange(
+ nOldCol1, nNewRow2+1, nTab1, nOldCol2, nOldRow2, nTab2 ),
+ SCE_ALL &~ SCE_TOP );
+
+ // left part
+ if ( nNewCol1 < nOldCol1 ) // only delete left line
+ lcl_PaintOneRange( pDocSh, ScRange(
+ nOldCol1, nOldRow1, nTab1, nOldCol1, nOldRow2, nTab2 ), SCE_ALL );
+ else if ( nNewCol1 > nOldCol1 ) // the left part which is will be removed
+ lcl_PaintOneRange( pDocSh, ScRange(
+ nOldCol1, nOldRow1, nTab1, nNewCol1-1, nOldRow2, nTab2 ),
+ SCE_ALL &~ SCE_RIGHT );
+
+ // right part
+ if ( nNewCol2 > nOldCol2 ) // only delete right line
+ lcl_PaintOneRange( pDocSh, ScRange(
+ nOldCol2, nOldRow1, nTab1, nOldCol2, nOldRow2, nTab2 ), SCE_ALL );
+ else if ( nNewCol2 < nOldCol2 ) // the right part which is will be removed
+ lcl_PaintOneRange( pDocSh, ScRange(
+ nNewCol2+1, nOldRow1, nTab1, nOldCol2, nOldRow2, nTab2 ),
+ SCE_ALL &~ SCE_LEFT );
+ }
+}
+
+void ScGridWindow::RFMouseMove( const MouseEvent& rMEvt, bool bUp )
+{
+ ScInputHandler* pHdl = SC_MOD()->GetInputHdl( mrViewData.GetViewShell() );
+ if (!pHdl)
+ return;
+ ScRangeFindList* pRangeFinder = pHdl->GetRangeFindList();
+ if (!pRangeFinder || nRFIndex >= pRangeFinder->Count())
+ return;
+ ScRangeFindData& rData = pRangeFinder->GetObject( nRFIndex );
+
+ // Mouse pointer
+
+ if (bRFSize)
+ SetPointer( PointerStyle::Cross );
+ else
+ SetPointer( PointerStyle::Hand );
+
+ // Scrolling
+
+ bool bTimer = false;
+ Point aPos = rMEvt.GetPosPixel();
+ SCCOL nDx = 0;
+ SCROW nDy = 0;
+ if ( aPos.X() < 0 ) nDx = -1;
+ if ( aPos.Y() < 0 ) nDy = -1;
+ Size aSize = GetOutputSizePixel();
+ if ( aPos.X() >= aSize.Width() )
+ nDx = 1;
+ if ( aPos.Y() >= aSize.Height() )
+ nDy = 1;
+ if ( nDx != 0 || nDy != 0 )
+ {
+ if ( nDx != 0) mrViewData.GetView()->ScrollX( nDx, WhichH(eWhich) );
+ if ( nDy != 0 ) mrViewData.GetView()->ScrollY( nDy, WhichV(eWhich) );
+ bTimer = true;
+ }
+
+ // Switching when fixating (so Scrolling works)
+
+ if ( eWhich == mrViewData.GetActivePart() ) //??
+ {
+ if ( mrViewData.GetHSplitMode() == SC_SPLIT_FIX )
+ if ( nDx > 0 )
+ {
+ if ( eWhich == SC_SPLIT_TOPLEFT )
+ mrViewData.GetView()->ActivatePart( SC_SPLIT_TOPRIGHT );
+ else if ( eWhich == SC_SPLIT_BOTTOMLEFT )
+ mrViewData.GetView()->ActivatePart( SC_SPLIT_BOTTOMRIGHT );
+ }
+
+ if ( mrViewData.GetVSplitMode() == SC_SPLIT_FIX )
+ if ( nDy > 0 )
+ {
+ if ( eWhich == SC_SPLIT_TOPLEFT )
+ mrViewData.GetView()->ActivatePart( SC_SPLIT_BOTTOMLEFT );
+ else if ( eWhich == SC_SPLIT_TOPRIGHT )
+ mrViewData.GetView()->ActivatePart( SC_SPLIT_BOTTOMRIGHT );
+ }
+ }
+
+ // Move
+
+ SCCOL nPosX;
+ SCROW nPosY;
+ mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
+
+ ScRange aOld = rData.aRef;
+ ScRange aNew = aOld;
+ if ( bRFSize )
+ {
+ switch (aRFSelectedCorned)
+ {
+ case LEFT_UP:
+ aNew.aStart.SetCol(nPosX);
+ aNew.aStart.SetRow(nPosY);
+ break;
+ case LEFT_DOWN:
+ aNew.aStart.SetCol(nPosX);
+ aNew.aEnd.SetRow(nPosY);
+ break;
+ case RIGHT_UP:
+ aNew.aEnd.SetCol(nPosX);
+ aNew.aStart.SetRow(nPosY);
+ break;
+ case RIGHT_DOWN:
+ aNew.aEnd.SetCol(nPosX);
+ aNew.aEnd.SetRow(nPosY);
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ ScDocument& rDoc = mrViewData.GetDocument();
+ tools::Long nStartX = nPosX - nRFAddX;
+ if ( nStartX < 0 ) nStartX = 0;
+ tools::Long nStartY = nPosY - nRFAddY;
+ if ( nStartY < 0 ) nStartY = 0;
+ tools::Long nEndX = nStartX + aOld.aEnd.Col() - aOld.aStart.Col();
+ if ( nEndX > rDoc.MaxCol() )
+ {
+ nStartX -= ( nEndX - rDoc.MaxRow() );
+ nEndX = rDoc.MaxCol();
+ }
+ tools::Long nEndY = nStartY + aOld.aEnd.Row() - aOld.aStart.Row();
+ if ( nEndY > rDoc.MaxRow() )
+ {
+ nStartY -= ( nEndY - rDoc.MaxRow() );
+ nEndY = rDoc.MaxRow();
+ }
+
+ aNew.aStart.SetCol(static_cast<SCCOL>(nStartX));
+ aNew.aStart.SetRow(static_cast<SCROW>(nStartY));
+ aNew.aEnd.SetCol(static_cast<SCCOL>(nEndX));
+ aNew.aEnd.SetRow(static_cast<SCROW>(nEndY));
+ }
+
+ if ( bUp )
+ aNew.PutInOrder(); // For ButtonUp again in the proper order
+
+ if ( aNew != aOld )
+ {
+ pHdl->UpdateRange( nRFIndex, aNew );
+
+ ScDocShell* pDocSh = mrViewData.GetDocShell();
+
+ pHdl->UpdateLokReferenceMarks();
+
+ // only redrawing what has been changed...
+ lcl_PaintRefChanged( pDocSh, aOld, aNew );
+
+ // only redraw new frame (synchronously)
+ pDocSh->Broadcast( ScIndexHint( SfxHintId::ScShowRangeFinder, nRFIndex ) );
+
+ PaintImmediately(); // what you move, will be seen immediately
+ }
+
+ // Timer for Scrolling
+
+ if (bTimer)
+ mrViewData.GetView()->SetTimer( this, rMEvt ); // repeat event
+ else
+ mrViewData.GetView()->ResetTimer();
+}
+
+namespace {
+
+SvxAdjust toSvxAdjust( const ScPatternAttr& rPat )
+{
+ SvxCellHorJustify eHorJust =
+ rPat.GetItem(ATTR_HOR_JUSTIFY).GetValue();
+
+ 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;
+ }
+
+ return eSvxAdjust;
+}
+
+std::shared_ptr<ScFieldEditEngine> createEditEngine( ScDocShell* pDocSh, const ScPatternAttr& rPat )
+{
+ ScDocument& rDoc = pDocSh->GetDocument();
+
+ auto pEngine = std::make_shared<ScFieldEditEngine>(&rDoc, rDoc.GetEditPool());
+ ScSizeDeviceProvider aProv(pDocSh);
+ pEngine->SetRefDevice(aProv.GetDevice());
+ pEngine->SetRefMapMode(MapMode(MapUnit::Map100thMM));
+ SfxItemSet aDefault = pEngine->GetEmptyItemSet();
+ rPat.FillEditItemSet(&aDefault);
+ aDefault.Put( SvxAdjustItem(toSvxAdjust(rPat), EE_PARA_JUST) );
+ pEngine->SetDefaults(aDefault);
+
+ return pEngine;
+}
+
+bool extractURLInfo( const SvxFieldItem* pFieldItem, OUString* pName, OUString* pUrl, OUString* pTarget )
+{
+ if (!pFieldItem)
+ return false;
+
+ const SvxFieldData* pField = pFieldItem->GetField();
+ if (pField->GetClassId() != text::textfield::Type::URL)
+ return false;
+
+ const SvxURLField* pURLField = static_cast<const SvxURLField*>(pField);
+
+ if (pName)
+ *pName = pURLField->GetRepresentation();
+ if (pUrl)
+ *pUrl = pURLField->GetURL();
+ if (pTarget)
+ *pTarget = pURLField->GetTargetFrame();
+
+ return true;
+}
+
+}
+
+bool ScGridWindow::GetEditUrl( const Point& rPos,
+ OUString* pName, OUString* pUrl, OUString* pTarget )
+{
+ ScTabViewShell* pViewSh = mrViewData.GetViewShell();
+ ScInputHandler* pInputHdl = nullptr;
+ if (pViewSh)
+ pInputHdl = pViewSh->GetInputHandler();
+ EditView* pView = (pInputHdl && pInputHdl->IsInputMode()) ? pInputHdl->GetTableView() : nullptr;
+ if (pView)
+ return extractURLInfo(pView->GetFieldUnderMousePointer(), pName, pUrl, pTarget);
+
+ //! Pass on nPosX/Y?
+ SCCOL nPosX;
+ SCROW nPosY;
+ mrViewData.GetPosFromPixel( rPos.X(), rPos.Y(), eWhich, nPosX, nPosY );
+
+ SCTAB nTab = mrViewData.GetTabNo();
+ ScDocShell* pDocSh = mrViewData.GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ OUString sURL;
+ ScRefCellValue aCell;
+ bool bFound = lcl_GetHyperlinkCell(rDoc, nPosX, nPosY, nTab, aCell, sURL);
+ if( !bFound )
+ return false;
+
+ const ScPatternAttr* pPattern = rDoc.GetPattern( nPosX, nPosY, nTab );
+ // bForceToTop = sal_False, use the cell's real position
+ tools::Rectangle aEditRect = mrViewData.GetEditArea( eWhich, nPosX, nPosY, this, pPattern, false );
+ if (rPos.Y() < aEditRect.Top())
+ return false;
+
+ // vertical can not (yet) be clicked:
+
+ if (pPattern->GetCellOrientation() != SvxCellOrientation::Standard)
+ return false;
+
+ bool bBreak = pPattern->GetItem(ATTR_LINEBREAK).GetValue() ||
+ (pPattern->GetItem( ATTR_HOR_JUSTIFY ).GetValue() == SvxCellHorJustify::Block);
+ SvxCellHorJustify eHorJust = pPattern->GetItem(ATTR_HOR_JUSTIFY).GetValue();
+
+ // EditEngine
+
+ std::shared_ptr<ScFieldEditEngine> pEngine = createEditEngine(pDocSh, *pPattern);
+
+ MapMode aEditMode = mrViewData.GetLogicMode(eWhich); // without draw scaling
+ tools::Rectangle aLogicEdit = PixelToLogic( aEditRect, aEditMode );
+ tools::Long nThisColLogic = aLogicEdit.Right() - aLogicEdit.Left() + 1;
+ Size aPaperSize( 1000000, 1000000 );
+ if (aCell.getType() == CELLTYPE_FORMULA)
+ {
+ tools::Long nSizeX = 0;
+ tools::Long nSizeY = 0;
+ mrViewData.GetMergeSizePixel( nPosX, nPosY, nSizeX, nSizeY );
+ aPaperSize = Size(nSizeX, nSizeY );
+ aPaperSize = PixelToLogic(aPaperSize);
+ }
+
+ if (bBreak)
+ aPaperSize.setWidth( nThisColLogic );
+ pEngine->SetPaperSize( aPaperSize );
+
+ std::unique_ptr<EditTextObject> pTextObj;
+ if (aCell.getType() == CELLTYPE_EDIT)
+ {
+ if (aCell.getEditText())
+ pEngine->SetTextCurrentDefaults(*aCell.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 (sURL.isEmpty())
+ pTextObj = aCell.getFormula()->CreateURLObject();
+ else
+ {
+ OUString aRepres = sURL;
+
+ // TODO: text content of formatted numbers can be different
+ if (aCell.hasNumeric())
+ aRepres = OUString::number(aCell.getValue());
+ else if (aCell.getType() == CELLTYPE_FORMULA)
+ aRepres = aCell.getFormula()->GetString().getString();
+
+ pTextObj = ScEditUtil::CreateURLObjectFromURL(rDoc, sURL, aRepres);
+ }
+
+ if (pTextObj)
+ pEngine->SetTextCurrentDefaults(*pTextObj);
+ }
+
+ tools::Long nStartX = aLogicEdit.Left();
+
+ tools::Long nTextWidth = pEngine->CalcTextWidth();
+ tools::Long nTextHeight = pEngine->GetTextHeight();
+ if ( nTextWidth < nThisColLogic )
+ {
+ if (eHorJust == SvxCellHorJustify::Right)
+ nStartX += nThisColLogic - nTextWidth;
+ else if (eHorJust == SvxCellHorJustify::Center)
+ nStartX += (nThisColLogic - nTextWidth) / 2;
+ }
+
+ 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.get(), this);
+ aTempView.SetOutputArea( aLogicEdit );
+
+ bool bRet;
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ bRet = extractURLInfo(aTempView.GetField(aLogicClick), pName, pUrl, pTarget);
+ }
+ else
+ {
+ MapMode aOld = GetMapMode();
+ SetMapMode(aEditMode); // no return anymore
+ bRet = extractURLInfo(aTempView.GetFieldUnderMousePointer(), pName, pUrl, pTarget);
+ SetMapMode(aOld);
+ }
+ return bRet;
+ }
+ return false;
+}
+
+bool ScGridWindow::HasScenarioButton( const Point& rPosPixel, ScRange& rScenRange )
+{
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCTAB nTab = mrViewData.GetTabNo();
+ SCTAB nTabCount = rDoc.GetTableCount();
+ if ( nTab+1<nTabCount && rDoc.IsScenario(nTab+1) && !rDoc.IsScenario(nTab) )
+ {
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+
+ Size aButSize = mrViewData.GetScenButSize();
+ tools::Long nBWidth = aButSize.Width();
+ if (!nBWidth)
+ return false; // No Button drawn yet -> there is none
+ tools::Long nBHeight = aButSize.Height();
+ tools::Long nHSpace = static_cast<tools::Long>( SC_SCENARIO_HSPACE * mrViewData.GetPPTX() );
+
+ //! cache the Ranges in Table!!!!
+
+ ScMarkData aMarks(rDoc.GetSheetLimits());
+ for (SCTAB i=nTab+1; i<nTabCount && rDoc.IsScenario(i); i++)
+ rDoc.MarkScenario( i, nTab, aMarks, false, ScScenarioFlags::ShowFrame );
+ ScRangeList aRanges;
+ aMarks.FillRangeListWithMarks( &aRanges, false );
+
+ size_t nRangeCount = aRanges.size();
+ for (size_t j=0; j< nRangeCount; ++j)
+ {
+ ScRange aRange = aRanges[j];
+ // Always extend scenario frame to merged cells where no new non-covered cells
+ // are framed
+ rDoc.ExtendTotalMerge( aRange );
+
+ bool bTextBelow = ( aRange.aStart.Row() == 0 );
+
+ Point aButtonPos;
+ if ( bTextBelow )
+ {
+ aButtonPos = mrViewData.GetScrPos( aRange.aEnd.Col()+1, aRange.aEnd.Row()+1,
+ eWhich, true );
+ }
+ else
+ {
+ aButtonPos = mrViewData.GetScrPos( aRange.aEnd.Col()+1, aRange.aStart.Row(),
+ eWhich, true );
+ aButtonPos.AdjustY( -nBHeight );
+ }
+ if ( bLayoutRTL )
+ aButtonPos.AdjustX( -(nHSpace - 1) );
+ else
+ aButtonPos.AdjustX( -(nBWidth - nHSpace) ); // same for top or bottom
+
+ tools::Rectangle aButRect( aButtonPos, Size(nBWidth,nBHeight) );
+ if ( aButRect.Contains( rPosPixel ) )
+ {
+ rScenRange = aRange;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+void ScGridWindow::DrawLayerCreated()
+{
+ SetMapMode( GetDrawMapMode() );
+
+ // initially create overlay objects
+ ImpCreateOverlayObjects();
+}
+
+void ScGridWindow::SetAutoSpellContext( const std::shared_ptr<sc::SpellCheckContext> &ctx )
+{
+ mpSpellCheckCxt = ctx;
+}
+
+void ScGridWindow::ResetAutoSpell()
+{
+ if (mpSpellCheckCxt)
+ mpSpellCheckCxt->reset();
+}
+
+void ScGridWindow::ResetAutoSpellForContentChange()
+{
+ if (mpSpellCheckCxt)
+ mpSpellCheckCxt->resetForContentChange();
+}
+
+void ScGridWindow::SetAutoSpellData( SCCOL nPosX, SCROW nPosY, const std::vector<editeng::MisspellRanges>* pRanges )
+{
+ if (!mpSpellCheckCxt)
+ return;
+
+ mpSpellCheckCxt->setMisspellRanges(nPosX, nPosY, pRanges);
+}
+
+const std::vector<editeng::MisspellRanges>* ScGridWindow::GetAutoSpellData( SCCOL nPosX, SCROW nPosY )
+{
+ if (!mpSpellCheckCxt)
+ return nullptr;
+
+ if (!maVisibleRange.isInside(nPosX, nPosY))
+ return nullptr;
+
+ return mpSpellCheckCxt->getMisspellRanges(nPosX, nPosY);
+}
+
+bool ScGridWindow::InsideVisibleRange( SCCOL nPosX, SCROW nPosY )
+{
+ return maVisibleRange.isInside(nPosX, nPosY);
+}
+
+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();
+
+ return mrViewData.describeCellCursor();
+}
+
+void ScGridWindow::notifyKitCellCursor() const
+{
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_CURSOR, getCellCursor());
+ if (bListValButton && aListValPos == mrViewData.GetCurPos())
+ updateLOKValListButton(true, aListValPos);
+ std::vector<tools::Rectangle> aRects;
+ GetSelectionRects(aRects);
+ if (aRects.empty() || !mrViewData.IsActive())
+ {
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, ""_ostr);
+ SfxLokHelper::notifyOtherViews(pViewShell, LOK_CALLBACK_TEXT_VIEW_SELECTION, "selection", "EMPTY"_ostr);
+ }
+}
+
+void ScGridWindow::notifyKitCellViewCursor(const SfxViewShell* pForShell) const
+{
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+
+ if (pViewShell->GetDocId() != pForShell->GetDocId())
+ return;
+
+ OString aCursor("EMPTY"_ostr);
+ if (mpOOCursors) // cf. getCellCursor above
+ {
+ auto pForTabView = dynamic_cast<const ScTabViewShell *>(pForShell);
+ if (!pForTabView)
+ return;
+
+ if (comphelper::LibreOfficeKit::isCompatFlagSet(
+ comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
+ aCursor = mrViewData.describeCellCursorInPrintTwips();
+ else
+ aCursor = pForTabView->GetViewData().describeCellCursorAt(
+ mrViewData.GetCurX(), mrViewData.GetCurY()); // our position.
+ }
+ SfxLokHelper::notifyOtherView(pViewShell, pForShell, LOK_CALLBACK_CELL_VIEW_CURSOR, "rectangle", aCursor);
+}
+
+// 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();
+
+ if (pForShell)
+ {
+ SfxLokHelper::notifyOtherView(pViewShell, pForShell,
+ LOK_CALLBACK_CELL_VIEW_CURSOR, "rectangle", aCursor);
+ }
+ else
+ {
+ notifyKitCellCursor();
+ SfxLokHelper::notifyOtherViews(pViewShell,
+ LOK_CALLBACK_CELL_VIEW_CURSOR, "rectangle", aCursor);
+ }
+
+ return;
+ }
+
+ if (!pForShell)
+ {
+ for (SfxViewShell* it = SfxViewShell::GetFirst(); it;
+ it = SfxViewShell::GetNext(*it))
+ updateKitCellCursor(it);
+ return;
+ }
+
+ if (pForShell == mrViewData.GetViewShell())
+ notifyKitCellCursor();
+ else
+ notifyKitCellViewCursor(pForShell);
+}
+
+void ScGridWindow::updateKitOtherCursors() const
+{
+ for (SfxViewShell* it = SfxViewShell::GetFirst(); it;
+ it = SfxViewShell::GetNext(*it))
+ {
+ auto pOther = dynamic_cast<const ScTabViewShell *>(it);
+ if (!pOther)
+ continue;
+ const ScGridWindow *pGrid = pOther->GetViewData().GetActiveWin();
+ assert(pGrid);
+ if (pGrid == this)
+ notifyKitCellCursor();
+ else
+ pGrid->notifyKitCellViewCursor(mrViewData.GetViewShell());
+ }
+}
+
+void ScGridWindow::CursorChanged()
+{
+ // here the created OverlayObjects may be transformed in later versions. For
+ // now, just re-create them
+
+ UpdateCursorOverlay();
+ UpdateSparklineGroupOverlay();
+}
+
+void ScGridWindow::ImpCreateOverlayObjects()
+{
+ UpdateCursorOverlay();
+ UpdateCopySourceOverlay();
+ UpdateSelectionOverlay();
+ UpdateHighlightOverlay();
+ UpdateAutoFillOverlay();
+ UpdateDragRectOverlay();
+ UpdateHeaderOverlay();
+ UpdateShrinkOverlay();
+ UpdateSparklineGroupOverlay();
+}
+
+void ScGridWindow::ImpDestroyOverlayObjects()
+{
+ DeleteCursorOverlay();
+ DeleteCopySourceOverlay();
+ DeleteSelectionOverlay();
+ mpOOHighlight.reset(); // DeleteHighlightOverlay
+ DeleteAutoFillOverlay();
+ DeleteDragRectOverlay();
+ DeleteHeaderOverlay();
+ DeleteShrinkOverlay();
+ DeleteSparklineGroupOverlay();
+}
+
+void ScGridWindow::UpdateAllOverlays()
+{
+ // delete and re-allocate all overlay objects
+
+ ImpDestroyOverlayObjects();
+ ImpCreateOverlayObjects();
+}
+
+void ScGridWindow::DeleteCursorOverlay()
+{
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_CURSOR, "EMPTY"_ostr);
+ SfxLokHelper::notifyOtherViews(pViewShell, LOK_CALLBACK_CELL_VIEW_CURSOR, "rectangle", "EMPTY"_ostr);
+ mpOOCursors.reset();
+}
+
+void ScGridWindow::DeleteCopySourceOverlay()
+{
+ mpOOSelectionBorder.reset();
+}
+
+void ScGridWindow::UpdateCopySourceOverlay()
+{
+ MapMode aDrawMode = GetDrawMapMode();
+ MapMode aOldMode = GetMapMode();
+ if ( aOldMode != aDrawMode )
+ SetMapMode( aDrawMode );
+
+ DeleteCopySourceOverlay();
+
+ if (comphelper::LibreOfficeKit::isActive())
+ return;
+ if (!mrViewData.ShowPasteSource())
+ return;
+ if (!SC_MOD()->GetInputOptions().GetEnterPasteMode())
+ return;
+ rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager();
+ if (!xOverlayManager.is())
+ return;
+ const ScTransferObj* pTransObj = ScTransferObj::GetOwnClipboard(ScTabViewShell::GetClipData(mrViewData.GetActiveWin()));
+ if (!pTransObj)
+ return;
+ ScDocument* pClipDoc = pTransObj->GetDocument();
+ if (!pClipDoc)
+ return;
+
+ SCTAB nCurTab = mrViewData.GetCurPos().Tab();
+
+ ScClipParam& rClipParam = pClipDoc->GetClipParam();
+ mpOOSelectionBorder.reset(new sdr::overlay::OverlayObjectList);
+ for ( size_t i = 0; i < rClipParam.maRanges.size(); ++i )
+ {
+ ScRange const & r = rClipParam.maRanges[i];
+ if (r.aStart.Tab() != nCurTab)
+ continue;
+
+ SCCOL nClipStartX = r.aStart.Col();
+ SCROW nClipStartY = r.aStart.Row();
+ SCCOL nClipEndX = r.aEnd.Col();
+ SCROW nClipEndY = r.aEnd.Row();
+
+ Point aClipStartScrPos = mrViewData.GetScrPos( nClipStartX, nClipStartY, eWhich );
+ Point aClipEndScrPos = mrViewData.GetScrPos( nClipEndX + 1, nClipEndY + 1, eWhich );
+ aClipStartScrPos -= Point(1, 1);
+ tools::Long nSizeXPix = aClipEndScrPos.X() - aClipStartScrPos.X();
+ tools::Long nSizeYPix = aClipEndScrPos.Y() - aClipStartScrPos.Y();
+
+ tools::Rectangle aRect( aClipStartScrPos, Size(nSizeXPix, nSizeYPix) );
+
+ Color aHighlight = GetSettings().GetStyleSettings().GetHighlightColor();
+
+ tools::Rectangle aLogic = PixelToLogic(aRect, aDrawMode);
+ ::basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(aLogic);
+ std::unique_ptr<ScOverlayDashedBorder> pDashedBorder(new ScOverlayDashedBorder(aRange, aHighlight));
+ xOverlayManager->add(*pDashedBorder);
+ mpOOSelectionBorder->append(std::move(pDashedBorder));
+ }
+
+ if ( aOldMode != aDrawMode )
+ SetMapMode( aOldMode );
+}
+
+static std::vector<tools::Rectangle> convertPixelToLogical(
+ const ScViewData& rViewData,
+ const std::vector<tools::Rectangle>& rRectangles,
+ tools::Rectangle &rBoundingBox)
+{
+ std::vector<tools::Rectangle> aLogicRects;
+
+ double nPPTX = rViewData.GetPPTX();
+ double nPPTY = rViewData.GetPPTY();
+
+ for (const auto& 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 );
+
+ tools::Rectangle aRect(aRectangle.Left() / nPPTX, aRectangle.Top() / nPPTY,
+ aRectangle.Right() / nPPTX, aRectangle.Bottom() / nPPTY);
+
+ rBoundingBox.Union(aRect);
+ aLogicRects.push_back(aRect);
+ }
+ return aLogicRects;
+}
+
+static OString rectanglesToString(const std::vector<tools::Rectangle> &rLogicRects)
+{
+ bool bFirst = true;
+ OStringBuffer aRects;
+ for (const auto &rRect : rLogicRects)
+ {
+ if (!bFirst)
+ aRects.append("; ");
+ bFirst = false;
+ aRects.append(rRect.toString());
+ }
+ return aRects.makeStringAndClear();
+}
+
+/**
+ * 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);
+
+ tools::Rectangle aBoundingBox;
+ std::vector<tools::Rectangle> aConvertedRects;
+
+ if (bInPrintTwips)
+ std::for_each(rRectangles.begin(), rRectangles.end(),
+ [&aBoundingBox](const tools::Rectangle& rRect) { aBoundingBox.Union(rRect); });
+ else
+ aConvertedRects = convertPixelToLogical(mrViewData, rRectangles, aBoundingBox);
+
+ const std::vector<tools::Rectangle>& rLogicRects = bInPrintTwips ? rRectangles : aConvertedRects;
+ if (pLogicRects)
+ {
+ *pLogicRects = rLogicRects;
+ return;
+ }
+
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+ pViewShell->UpdateInputHandler();
+ OString sBoundingBoxString = "EMPTY"_ostr;
+ if (!aBoundingBox.IsEmpty())
+ sBoundingBoxString = aBoundingBox.toString();
+ OString aRectListString = rectanglesToString(rLogicRects);
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_SELECTION_AREA, sBoundingBoxString);
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, aRectListString);
+
+ 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;
+
+ const ScGridWindow *pGrid = pOther->GetViewData().GetActiveWin();
+ assert(pGrid);
+
+ // Fetch pixels & convert for each view separately.
+ tools::Rectangle aDummyBBox;
+ std::vector<tools::Rectangle> aPixelRects;
+ pGrid->GetPixelRectsFor(mrViewData.GetMarkData() /* ours */, aPixelRects);
+ auto aOtherLogicRects = convertPixelToLogical(pOther->GetViewData(), aPixelRects, aDummyBBox);
+ SfxLokHelper::notifyOtherView(pViewShell, pOther, LOK_CALLBACK_TEXT_VIEW_SELECTION,
+ "selection", rectanglesToString(aOtherLogicRects));
+ }
+}
+
+/**
+ * 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)
+ return;
+
+ // Fetch pixels & convert for each view separately.
+ tools::Rectangle aBoundingBox;
+ std::vector<tools::Rectangle> aRects;
+ OString aRectsString;
+ GetRectsAnyFor(pOther->GetViewData().GetMarkData() /* theirs */, aRects, bInPrintTwips);
+ if (bInPrintTwips)
+ {
+ std::for_each(aRects.begin(), aRects.end(),
+ [&aBoundingBox](const tools::Rectangle& rRect) { aBoundingBox.Union(rRect); });
+ aRectsString = rectanglesToString(aRects);
+ }
+ else
+ aRectsString = rectanglesToString(
+ convertPixelToLogical(pViewShell->GetViewData(), aRects, aBoundingBox));
+
+ if (it == pViewShell)
+ {
+ OString sBoundingBoxString = "EMPTY"_ostr;
+ if (!aBoundingBox.IsEmpty())
+ sBoundingBoxString = aBoundingBox.toString();
+
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_SELECTION_AREA, sBoundingBoxString);
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, aRectsString);
+ }
+ else
+ SfxLokHelper::notifyOtherView(it, pViewShell, LOK_CALLBACK_TEXT_VIEW_SELECTION,
+ "selection", aRectsString);
+ }
+}
+
+namespace
+{
+
+void updateLibreOfficeKitAutoFill(const ScViewData& rViewData, tools::Rectangle const & rRectangle)
+{
+ if (!comphelper::LibreOfficeKit::isActive())
+ return;
+
+ double nPPTX = rViewData.GetPPTX();
+ double nPPTY = rViewData.GetPPTY();
+
+ OString sRectangleString = "EMPTY"_ostr;
+ if (!rRectangle.IsEmpty())
+ {
+ // selection start handle
+ tools::Rectangle aLogicRectangle(
+ rRectangle.Left() / nPPTX, rRectangle.Top() / nPPTY,
+ rRectangle.Right() / nPPTX, rRectangle.Bottom() / nPPTY);
+ sRectangleString = aLogicRectangle.toString();
+ }
+
+ ScTabViewShell* pViewShell = rViewData.GetViewShell();
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_AUTO_FILL_AREA, sRectangleString);
+}
+
+} //end anonymous namespace
+
+void ScGridWindow::UpdateCursorOverlay()
+{
+ ScDocument& rDoc = mrViewData.GetDocument();
+
+ MapMode aDrawMode = GetDrawMapMode();
+ MapMode aOldMode = GetMapMode();
+ if ( aOldMode != aDrawMode )
+ SetMapMode( aDrawMode );
+
+ // Existing OverlayObjects may be transformed in later versions.
+ // For now, just re-create them.
+
+ DeleteCursorOverlay();
+
+ std::vector<tools::Rectangle> aPixelRects;
+
+ // determine the cursor rectangles in pixels (moved from ScGridWindow::DrawCursor)
+
+ SCTAB nTab = mrViewData.GetTabNo();
+ SCCOL nX = mrViewData.GetCurX();
+ SCROW nY = mrViewData.GetCurY();
+
+ const ScPatternAttr* pPattern = rDoc.GetPattern(nX,nY,nTab);
+
+ if (!comphelper::LibreOfficeKit::isActive() && !maVisibleRange.isInside(nX, nY))
+ {
+ if (maVisibleRange.mnCol2 < nX || maVisibleRange.mnRow2 < nY)
+ return; // no further check needed, nothing visible
+
+ // 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
+ const ScMergeFlagAttr& rMergeFlag = pPattern->GetItem(ATTR_MERGE_FLAG);
+ bool bOverlapped = rMergeFlag.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;
+ 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 ( bVis && !bOverlapped && !mrViewData.HasEditView(eWhich) && mrViewData.IsActive() )
+ {
+ Point aScrPos = mrViewData.GetScrPos( nX, nY, eWhich, true );
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+
+ // completely right of/below the screen?
+ // (test with logical start position in aScrPos)
+ bool bMaybeVisible;
+ if ( bLayoutRTL )
+ bMaybeVisible = ( aScrPos.X() >= -2 && aScrPos.Y() >= -2 );
+ else
+ {
+ Size aOutSize = GetOutputSizePixel();
+ bMaybeVisible = ( aScrPos.X() <= aOutSize.Width() + 2 && aScrPos.Y() <= aOutSize.Height() + 2 );
+ }
+
+ // in the tiled rendering case, don't limit to the screen size
+ if (bMaybeVisible || comphelper::LibreOfficeKit::isActive())
+ {
+ tools::Long nSizeXPix;
+ tools::Long nSizeYPix;
+ mrViewData.GetMergeSizePixel( nX, nY, nSizeXPix, nSizeYPix );
+
+ if (bLayoutRTL)
+ aScrPos.AdjustX( -(nSizeXPix - 2) ); // move instead of mirroring
+
+ // show the cursor as 4 (thin) rectangles
+ tools::Rectangle aRect(aScrPos, Size(nSizeXPix - 1, nSizeYPix - 1));
+
+ float fScaleFactor = GetDPIScaleFactor();
+
+ tools::Long aCursorWidth = 1 * fScaleFactor;
+
+ tools::Rectangle aLeft = aRect;
+ aLeft.AdjustTop( -aCursorWidth );
+ aLeft.AdjustBottom(aCursorWidth );
+ aLeft.SetRight( aLeft.Left() );
+ aLeft.AdjustLeft( -aCursorWidth );
+
+ tools::Rectangle aRight = aRect;
+ aRight.AdjustTop( -aCursorWidth );
+ aRight.AdjustBottom(aCursorWidth );
+ aRight.SetLeft( aRight.Right() );
+ aRight.AdjustRight(aCursorWidth );
+
+ tools::Rectangle aTop = aRect;
+ aTop.SetBottom( aTop.Top() );
+ aTop.AdjustTop( -aCursorWidth );
+
+ tools::Rectangle aBottom = aRect;
+ aBottom.SetTop( aBottom.Bottom() );
+ aBottom.AdjustBottom(aCursorWidth );
+
+ aPixelRects.push_back(aLeft);
+ aPixelRects.push_back(aRight);
+ aPixelRects.push_back(aTop);
+ aPixelRects.push_back(aBottom);
+ }
+ }
+
+ 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())
+ {
+ Color aCursorColor = GetSettings().GetStyleSettings().GetAccentColor();
+ if (mrViewData.GetActivePart() != eWhich)
+ // non-active pane uses a different color.
+ aCursorColor = SC_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
+ const double MinSize = 0.25 * GetDPIScaleFactor();
+ double fZoom(mrViewData.GetZoomX() * 0.5);
+ for(const tools::Rectangle & rRA : aPixelRects)
+ {
+ basegfx::B2DRange aRB(rRA.Left() - MinSize - fZoom, rRA.Top() - MinSize - fZoom,
+ rRA.Right() + MinSize + fZoom, rRA.Bottom() + MinSize + fZoom);
+ aRB.transform(aTransform);
+ aRanges.push_back(aRB);
+ }
+
+ std::unique_ptr<sdr::overlay::OverlayObject> pOverlay(new sdr::overlay::OverlaySelection(
+ sdr::overlay::OverlayType::Solid,
+ aCursorColor,
+ std::move(aRanges),
+ false));
+
+ xOverlayManager->add(*pOverlay);
+ mpOOCursors.reset(new sdr::overlay::OverlayObjectList);
+ mpOOCursors->append(std::move(pOverlay));
+ }
+ }
+ }
+
+ if ( aOldMode != aDrawMode )
+ SetMapMode( aOldMode );
+}
+
+void ScGridWindow::GetCellSelection(std::vector<tools::Rectangle>& rLogicRects)
+{
+ std::vector<tools::Rectangle> aRects;
+ if (comphelper::LibreOfficeKit::isActive() &&
+ comphelper::LibreOfficeKit::isCompatFlagSet(
+ comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
+ GetSelectionRectsPrintTwips(aRects);
+ else
+ GetSelectionRects(aRects);
+ UpdateKitSelection(aRects, &rLogicRects);
+}
+
+void ScGridWindow::DeleteSelectionOverlay()
+{
+ mpOOSelection.reset();
+}
+
+void ScGridWindow::UpdateSelectionOverlay()
+{
+ MapMode aDrawMode = GetDrawMapMode();
+ MapMode aOldMode = GetMapMode();
+ if ( aOldMode != aDrawMode )
+ SetMapMode( aDrawMode );
+
+ DeleteSelectionOverlay();
+ std::vector<tools::Rectangle> aRects;
+ if (comphelper::LibreOfficeKit::isActive() &&
+ comphelper::LibreOfficeKit::isCompatFlagSet(
+ comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
+ GetSelectionRectsPrintTwips(aRects);
+ else
+ GetSelectionRects(aRects);
+
+ if (!aRects.empty() && mrViewData.IsActive())
+ {
+ // #i70788# get the OverlayManager safely
+ rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager();
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ // notify the LibreOfficeKit too
+ UpdateKitSelection(aRects);
+ }
+ else if (xOverlayManager.is())
+ {
+ std::vector< basegfx::B2DRange > aRanges;
+ const basegfx::B2DHomMatrix aTransform(GetOutDev()->GetInverseViewTransformation());
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCTAB nTab = mrViewData.GetTabNo();
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+
+ for(const tools::Rectangle & rRA : aRects)
+ {
+ if (bLayoutRTL)
+ {
+ basegfx::B2DRange aRB(rRA.Left(), rRA.Top() - 1, rRA.Right() + 1, rRA.Bottom());
+ aRB.transform(aTransform);
+ aRanges.push_back(aRB);
+ }
+ else
+ {
+ basegfx::B2DRange aRB(rRA.Left() - 1, rRA.Top() - 1, rRA.Right(), rRA.Bottom());
+ aRB.transform(aTransform);
+ aRanges.push_back(aRB);
+ }
+ }
+
+ // get the system's highlight color
+ const Color aHighlight(SvtOptionsDrawinglayer::getHilightColor());
+
+ std::unique_ptr<sdr::overlay::OverlayObject> pOverlay(new sdr::overlay::OverlaySelection(
+ sdr::overlay::OverlayType::Transparent,
+ aHighlight,
+ std::move(aRanges),
+ true));
+
+ xOverlayManager->add(*pOverlay);
+ mpOOSelection.reset(new sdr::overlay::OverlayObjectList);
+ mpOOSelection->append(std::move(pOverlay));
+ }
+ }
+ else
+ {
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, "EMPTY"_ostr);
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_SELECTION_AREA, "EMPTY"_ostr);
+ SfxLokHelper::notifyOtherViews(pViewShell, LOK_CALLBACK_TEXT_VIEW_SELECTION, "selection", "EMPTY"_ostr);
+
+ ScInputHandler* pViewHdl = SC_MOD()->GetInputHdl(pViewShell);
+ if (!pViewHdl || !pViewHdl->IsEditMode())
+ {
+ std::vector<ReferenceMark> aReferenceMarks;
+ ScInputHandler::SendReferenceMarks(pViewShell, aReferenceMarks);
+ }
+ }
+
+ if ( aOldMode != aDrawMode )
+ SetMapMode( aOldMode );
+}
+
+void ScGridWindow::UpdateHighlightOverlay()
+{
+ mpOOHighlight.reset(); // DeleteHighlightOverlay
+ std::vector<tools::Rectangle> aRects;
+ if (comphelper::LibreOfficeKit::isActive() &&
+ comphelper::LibreOfficeKit::isCompatFlagSet(
+ comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
+ GetRectsAnyFor(mrViewData.GetHighlightData(), aRects, true);
+ else
+ GetPixelRectsFor(mrViewData.GetHighlightData(), aRects);
+
+ if (!aRects.empty() && mrViewData.IsActive())
+ {
+ // #i70788# get the OverlayManager safely
+ if (rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager())
+ {
+ std::vector< basegfx::B2DRange > aRanges;
+ const basegfx::B2DHomMatrix aTransform(GetOutDev()->GetInverseViewTransformation());
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCTAB nTab = mrViewData.GetTabNo();
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+
+ for(const tools::Rectangle & rRA : aRects)
+ {
+ if (bLayoutRTL)
+ {
+ basegfx::B2DRange aRB(rRA.Left(), rRA.Top() - 1, rRA.Right() + 1, rRA.Bottom());
+ aRB.transform(aTransform);
+ aRanges.push_back(aRB);
+ }
+ else
+ {
+ basegfx::B2DRange aRB(rRA.Left() - 1, rRA.Top() - 1, rRA.Right(), rRA.Bottom());
+ aRB.transform(aTransform);
+ aRanges.push_back(aRB);
+ }
+ }
+
+ const Color aBackgroundColor = SC_MOD()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor;
+ Color aHighlightColor = Application::GetSettings().GetStyleSettings().GetAccentColor();
+ aHighlightColor.Merge(aBackgroundColor, 100);
+
+ std::unique_ptr<sdr::overlay::OverlayObject> pOverlay(new sdr::overlay::OverlaySelection(
+ sdr::overlay::OverlayType::Transparent,
+ aHighlightColor,
+ std::move(aRanges),
+ false));
+
+ xOverlayManager->add(*pOverlay);
+ mpOOHighlight.reset(new sdr::overlay::OverlayObjectList);
+ mpOOHighlight->append(std::move(pOverlay));
+ }
+ }
+}
+
+void ScGridWindow::DeleteAutoFillOverlay()
+{
+ mpOOAutoFill.reset();
+ mpAutoFillRect.reset();
+}
+
+void ScGridWindow::UpdateAutoFillOverlay()
+{
+ MapMode aDrawMode = GetDrawMapMode();
+ MapMode aOldMode = GetMapMode();
+ if ( aOldMode != aDrawMode )
+ SetMapMode( aDrawMode );
+
+ DeleteAutoFillOverlay();
+
+ // get the AutoFill handle rectangle in pixels
+
+ if ( !(bAutoMarkVisible && aAutoMarkPos.Tab() == mrViewData.GetTabNo() &&
+ !mrViewData.HasEditView(eWhich) && mrViewData.IsActive()) )
+ return;
+
+ SCCOL nX = aAutoMarkPos.Col();
+ SCROW nY = aAutoMarkPos.Row();
+
+ if (!maVisibleRange.isInside(nX, nY) && !comphelper::LibreOfficeKit::isActive())
+ {
+ // Autofill mark is not visible. Bail out.
+ return;
+ }
+
+ SCTAB nTab = mrViewData.GetTabNo();
+ ScDocument& rDoc = mrViewData.GetDocument();
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+
+ // tdf#143733 tdf#145080 - improve border visibility
+ // constants picked for maximum consistency at 100%
+ // size = 6 at 100% (as before), 50% = 4.5, 200% = 9, 400% = 15
+ const float fScaleFactor = 3 * GetDPIScaleFactor();
+ const double fZoom(3 * mrViewData.GetZoomX());
+ // Size should be even
+ Size aFillHandleSize(fZoom + fScaleFactor, fZoom + fScaleFactor);
+
+ Point aFillPos = mrViewData.GetScrPos( nX, nY, eWhich, true );
+ tools::Long nSizeXPix;
+ tools::Long nSizeYPix;
+ mrViewData.GetMergeSizePixel( nX, nY, nSizeXPix, nSizeYPix );
+
+ if (bLayoutRTL && !comphelper::LibreOfficeKit::isActive())
+ aFillPos.AdjustX( -(nSizeXPix - 2 + (aFillHandleSize.Width() / 2)) );
+ else
+ aFillPos.AdjustX(nSizeXPix - (aFillHandleSize.Width() / 2) );
+
+ aFillPos.AdjustY(nSizeYPix );
+ aFillPos.AdjustY( -(aFillHandleSize.Height() / 2) );
+
+ tools::Rectangle aFillRect(aFillPos, aFillHandleSize);
+
+ // expand rect to increase hit area
+ mpAutoFillRect = aFillRect;
+ mpAutoFillRect->expand(fScaleFactor);
+
+ // #i70788# get the OverlayManager safely
+ rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager();
+ if (comphelper::LibreOfficeKit::isActive()) // notify the LibreOfficeKit
+ {
+ updateLibreOfficeKitAutoFill(mrViewData, aFillRect);
+ }
+ else if (xOverlayManager.is())
+ {
+ Color aHandleColor = GetSettings().GetStyleSettings().GetHighlightColor();
+ if (mrViewData.GetActivePart() != eWhich)
+ // non-active pane uses a different color.
+ aHandleColor = SC_MOD()->GetColorConfig().GetColorValue(svtools::CALCPAGEBREAKAUTOMATIC).nColor;
+ std::vector< basegfx::B2DRange > aRanges;
+ const basegfx::B2DHomMatrix aTransform(GetOutDev()->GetInverseViewTransformation());
+ basegfx::B2DRange aRB = vcl::unotools::b2DRectangleFromRectangle(aFillRect);
+
+ aRB.transform(aTransform);
+ aRanges.push_back(aRB);
+
+ std::unique_ptr<sdr::overlay::OverlayObject> pOverlay(new sdr::overlay::OverlaySelection(
+ sdr::overlay::OverlayType::Solid,
+ aHandleColor,
+ std::move(aRanges),
+ false));
+
+ xOverlayManager->add(*pOverlay);
+ mpOOAutoFill.reset(new sdr::overlay::OverlayObjectList);
+ mpOOAutoFill->append(std::move(pOverlay));
+ }
+
+ if ( aOldMode != aDrawMode )
+ SetMapMode( aOldMode );
+}
+
+void ScGridWindow::DeleteDragRectOverlay()
+{
+ mpOODragRect.reset();
+}
+
+void ScGridWindow::UpdateDragRectOverlay()
+{
+ bool bInPrintTwips = comphelper::LibreOfficeKit::isCompatFlagSet(
+ comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs);
+
+ MapMode aDrawMode = GetDrawMapMode();
+ MapMode aOldMode = GetMapMode();
+ if ( aOldMode != aDrawMode )
+ SetMapMode( aDrawMode );
+
+ DeleteDragRectOverlay();
+
+ // get the rectangles in pixels (moved from DrawDragRect)
+
+ if ( bDragRect || bPagebreakDrawn )
+ {
+ std::vector<tools::Rectangle> aPixelRects;
+
+ SCCOL nX1 = bDragRect ? nDragStartX : aPagebreakDrag.aStart.Col();
+ SCROW nY1 = bDragRect ? nDragStartY : aPagebreakDrag.aStart.Row();
+ SCCOL nX2 = bDragRect ? nDragEndX : aPagebreakDrag.aEnd.Col();
+ SCROW nY2 = bDragRect ? nDragEndY : aPagebreakDrag.aEnd.Row();
+
+ SCTAB nTab = mrViewData.GetTabNo();
+
+ SCCOL nPosX = mrViewData.GetPosX(WhichH(eWhich));
+ SCROW nPosY = mrViewData.GetPosY(WhichV(eWhich));
+ if (nX1 < nPosX) nX1 = nPosX;
+ if (nX2 < nPosX) nX2 = nPosX;
+ if (nY1 < nPosY) nY1 = nPosY;
+ if (nY2 < nPosY) nY2 = nPosY;
+
+ Point aScrPos(bInPrintTwips ? mrViewData.GetPrintTwipsPos( nX1, nY1 )
+ : mrViewData.GetScrPos( nX1, nY1, eWhich ) );
+
+ tools::Long nSizeXPix=0;
+ tools::Long nSizeYPix=0;
+ ScDocument& rDoc = mrViewData.GetDocument();
+ double nPPTX = mrViewData.GetPPTX();
+ double nPPTY = mrViewData.GetPPTY();
+ SCCOLROW i;
+
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+ tools::Long nAdjust = comphelper::LibreOfficeKit::isActive() ? 0 : 2;
+
+ if (rDoc.ValidCol(nX2) && nX2>=nX1)
+ for (i=nX1; i<=nX2; i++)
+ {
+ tools::Long nWidth = rDoc.GetColWidth( static_cast<SCCOL>(i), nTab );
+ nSizeXPix += bInPrintTwips ? nWidth : ScViewData::ToPixel( nWidth, nPPTX );
+ }
+ else
+ {
+ aScrPos.AdjustX( -nLayoutSign );
+ nSizeXPix += nAdjust;
+ }
+
+ if (rDoc.ValidRow(nY2) && nY2>=nY1)
+ for (i=nY1; i<=nY2; i++)
+ {
+ tools::Long nHeight = rDoc.GetRowHeight( i, nTab );
+ nSizeYPix += bInPrintTwips ? nHeight : ScViewData::ToPixel( nHeight, nPPTY );
+ }
+ else
+ {
+ aScrPos.AdjustY( -1 );
+ nSizeYPix += nAdjust;
+ }
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ nSizeXPix -= 2;
+ nSizeYPix -= 2;
+ }
+
+ aScrPos.AdjustX( -(nAdjust * nLayoutSign) );
+ aScrPos.AdjustY( -1 * nAdjust );
+ tools::Rectangle aRect( aScrPos.X(), aScrPos.Y(),
+ aScrPos.X() + ( nSizeXPix + nAdjust ) * nLayoutSign, aScrPos.Y() + nSizeYPix + nAdjust );
+ if ( bLayoutRTL )
+ {
+ aRect.SetLeft( aRect.Right() ); // end position is left
+ aRect.SetRight( aScrPos.X() );
+ }
+
+ if ( meDragInsertMode == INS_CELLSDOWN )
+ {
+ aPixelRects.emplace_back( aRect.Left()+1, aRect.Top()+3, aRect.Left()+1, aRect.Bottom()-2 );
+ aPixelRects.emplace_back( aRect.Right()-1, aRect.Top()+3, aRect.Right()-1, aRect.Bottom()-2 );
+ aPixelRects.emplace_back( aRect.Left()+1, aRect.Top(), aRect.Right()-1, aRect.Top()+2 );
+ aPixelRects.emplace_back( aRect.Left()+1, aRect.Bottom()-1, aRect.Right()-1, aRect.Bottom()-1 );
+ }
+ else if ( meDragInsertMode == INS_CELLSRIGHT )
+ {
+ aPixelRects.emplace_back( aRect.Left(), aRect.Top()+1, aRect.Left()+2, aRect.Bottom()-1 );
+ aPixelRects.emplace_back( aRect.Right()-1, aRect.Top()+1, aRect.Right()-1, aRect.Bottom()-1 );
+ aPixelRects.emplace_back( aRect.Left()+3, aRect.Top()+1, aRect.Right()-2, aRect.Top()+1 );
+ aPixelRects.emplace_back( aRect.Left()+3, aRect.Bottom()-1, aRect.Right()-2, aRect.Bottom()-1 );
+ }
+ else
+ {
+ aPixelRects.emplace_back( aRect.Left(), aRect.Top(), aRect.Left()+2, aRect.Bottom() );
+ aPixelRects.emplace_back( aRect.Right()-2, aRect.Top(), aRect.Right(), aRect.Bottom() );
+ aPixelRects.emplace_back( aRect.Left()+3, aRect.Top(), aRect.Right()-3, aRect.Top()+2 );
+ aPixelRects.emplace_back( aRect.Left()+3, aRect.Bottom()-2, aRect.Right()-3, aRect.Bottom() );
+ }
+
+ // #i70788# get the OverlayManager safely
+ rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager();
+
+ if (xOverlayManager.is() && !comphelper::LibreOfficeKit::isActive())
+ {
+ std::vector< basegfx::B2DRange > aRanges;
+ const basegfx::B2DHomMatrix aTransform(GetOutDev()->GetInverseViewTransformation());
+
+ for(const tools::Rectangle & rRA : aPixelRects)
+ {
+ basegfx::B2DRange aRB(rRA.Left(), rRA.Top(), rRA.Right() + 1, rRA.Bottom() + 1);
+ aRB.transform(aTransform);
+ aRanges.push_back(aRB);
+ }
+
+ std::unique_ptr<sdr::overlay::OverlayObject> pOverlay(new sdr::overlay::OverlaySelection(
+ sdr::overlay::OverlayType::Invert,
+ COL_BLACK,
+ std::move(aRanges),
+ false));
+
+ xOverlayManager->add(*pOverlay);
+ mpOODragRect.reset(new sdr::overlay::OverlayObjectList);
+ mpOODragRect->append(std::move(pOverlay));
+ }
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (comphelper::LibreOfficeKit::isActive() && pViewShell)
+ {
+ OString aRectsString;
+ tools::Rectangle aBoundingBox;
+
+ std::vector<tools::Rectangle> aRectangles;
+ aRectangles.push_back(aRect);
+
+ if (bInPrintTwips)
+ {
+ aBoundingBox = aRect;
+ aRectsString = rectanglesToString(aRectangles);
+ }
+ else
+ {
+ aRectsString = rectanglesToString(convertPixelToLogical(pViewShell->GetViewData(), aRectangles, aBoundingBox));
+ }
+
+ OString sBoundingBoxString = "EMPTY"_ostr;
+ if (!aBoundingBox.IsEmpty())
+ sBoundingBoxString = aBoundingBox.toString();
+
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_SELECTION_AREA, sBoundingBoxString);
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, aRectsString);
+ }
+ }
+
+ if ( aOldMode != aDrawMode )
+ SetMapMode( aOldMode );
+}
+
+void ScGridWindow::DeleteHeaderOverlay()
+{
+ mpOOHeader.reset();
+}
+
+void ScGridWindow::UpdateHeaderOverlay()
+{
+ MapMode aDrawMode = GetDrawMapMode();
+ MapMode aOldMode = GetMapMode();
+ if ( aOldMode != aDrawMode )
+ SetMapMode( aDrawMode );
+
+ DeleteHeaderOverlay();
+
+ // Pixel rectangle is in aInvertRect
+ if ( !aInvertRect.IsEmpty() )
+ {
+ // #i70788# get the OverlayManager safely
+ rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager();
+
+ if (xOverlayManager.is() && !comphelper::LibreOfficeKit::isActive())
+ {
+ // Color aHighlight = GetSettings().GetStyleSettings().GetHighlightColor();
+ std::vector< basegfx::B2DRange > aRanges;
+ const basegfx::B2DHomMatrix aTransform(GetOutDev()->GetInverseViewTransformation());
+ basegfx::B2DRange aRB(aInvertRect.Left(), aInvertRect.Top(), aInvertRect.Right() + 1, aInvertRect.Bottom() + 1);
+
+ aRB.transform(aTransform);
+ aRanges.push_back(aRB);
+
+ std::unique_ptr<sdr::overlay::OverlayObject> pOverlay(new sdr::overlay::OverlaySelection(
+ sdr::overlay::OverlayType::Invert,
+ COL_BLACK,
+ std::move(aRanges),
+ false));
+
+ xOverlayManager->add(*pOverlay);
+ mpOOHeader.reset(new sdr::overlay::OverlayObjectList);
+ mpOOHeader->append(std::move(pOverlay));
+ }
+ }
+
+ if ( aOldMode != aDrawMode )
+ SetMapMode( aOldMode );
+}
+
+void ScGridWindow::DeleteShrinkOverlay()
+{
+ mpOOShrink.reset();
+}
+
+void ScGridWindow::UpdateShrinkOverlay()
+{
+ MapMode aDrawMode = GetDrawMapMode();
+ MapMode aOldMode = GetMapMode();
+ if ( aOldMode != aDrawMode )
+ SetMapMode( aDrawMode );
+
+ DeleteShrinkOverlay();
+
+ // get the rectangle in pixels
+
+ tools::Rectangle aPixRect;
+ ScRange aRange;
+ SCTAB nTab = mrViewData.GetTabNo();
+ if ( mrViewData.IsRefMode() && nTab >= mrViewData.GetRefStartZ() && nTab <= mrViewData.GetRefEndZ() &&
+ mrViewData.GetDelMark( aRange ) )
+ {
+ //! limit to visible area
+ if ( aRange.aStart.Col() <= aRange.aEnd.Col() &&
+ aRange.aStart.Row() <= aRange.aEnd.Row() )
+ {
+ Point aStart = mrViewData.GetScrPos( aRange.aStart.Col(),
+ aRange.aStart.Row(), eWhich );
+ Point aEnd = mrViewData.GetScrPos( aRange.aEnd.Col()+1,
+ aRange.aEnd.Row()+1, eWhich );
+ aEnd.AdjustX( -1 );
+ aEnd.AdjustY( -1 );
+
+ aPixRect = tools::Rectangle( aStart,aEnd );
+ }
+ }
+
+ if ( !aPixRect.IsEmpty() )
+ {
+ // #i70788# get the OverlayManager safely
+ rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager();
+
+ if (xOverlayManager.is() && !comphelper::LibreOfficeKit::isActive())
+ {
+ std::vector< basegfx::B2DRange > aRanges;
+ const basegfx::B2DHomMatrix aTransform(GetOutDev()->GetInverseViewTransformation());
+ basegfx::B2DRange aRB(aPixRect.Left(), aPixRect.Top(), aPixRect.Right() + 1, aPixRect.Bottom() + 1);
+
+ aRB.transform(aTransform);
+ aRanges.push_back(aRB);
+
+ std::unique_ptr<sdr::overlay::OverlayObject> pOverlay(new sdr::overlay::OverlaySelection(
+ sdr::overlay::OverlayType::Invert,
+ COL_BLACK,
+ std::move(aRanges),
+ false));
+
+ xOverlayManager->add(*pOverlay);
+ mpOOShrink.reset(new sdr::overlay::OverlayObjectList);
+ mpOOShrink->append(std::move(pOverlay));
+ }
+ }
+
+ if ( aOldMode != aDrawMode )
+ SetMapMode( aOldMode );
+}
+
+void ScGridWindow::DeleteSparklineGroupOverlay()
+{
+ mpOOSparklineGroup.reset();
+}
+
+void ScGridWindow::UpdateSparklineGroupOverlay()
+{
+ MapMode aDrawMode = GetDrawMapMode();
+
+ MapMode aOldMode = GetMapMode();
+ if (aOldMode != aDrawMode)
+ SetMapMode(aDrawMode);
+
+ DeleteSparklineGroupOverlay();
+
+ ScAddress aCurrentAddress = mrViewData.GetCurPos();
+
+ ScDocument& rDocument = mrViewData.GetDocument();
+ if (auto pSparkline = rDocument.GetSparkline(aCurrentAddress))
+ {
+ mpOOSparklineGroup.reset(new sdr::overlay::OverlayObjectList);
+
+ rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager();
+ if (xOverlayManager.is())
+ {
+ auto* pList = rDocument.GetSparklineList(aCurrentAddress.Tab());
+ if (pList)
+ {
+ auto const& pSparklines = pList->getSparklinesFor(pSparkline->getSparklineGroup());
+
+ Color aColor = SvtOptionsDrawinglayer::getHilightColor();
+
+ std::vector<basegfx::B2DRange> aRanges;
+ const basegfx::B2DHomMatrix aTransform(GetOutDev()->GetInverseViewTransformation());
+
+ for (auto const& pCurrentSparkline : pSparklines)
+ {
+ SCCOL nColumn = pCurrentSparkline->getColumn();
+ SCROW nRow = pCurrentSparkline->getRow();
+
+ Point aStart = mrViewData.GetScrPos(nColumn, nRow, eWhich);
+ Point aEnd = mrViewData.GetScrPos(nColumn + 1, nRow + 1, eWhich);
+
+ basegfx::B2DRange aRange(aStart.X(), aStart.Y(), aEnd.X(), aEnd.Y());
+
+ aRange.transform(aTransform);
+ aRanges.push_back(aRange);
+ }
+
+ std::unique_ptr<sdr::overlay::OverlayObject> pOverlay(new sdr::overlay::OverlaySelection(
+ sdr::overlay::OverlayType::Transparent,
+ aColor, std::move(aRanges), true));
+
+ xOverlayManager->add(*pOverlay);
+ mpOOSparklineGroup->append(std::move(pOverlay));
+ }
+ }
+ }
+
+ if (aOldMode != aDrawMode)
+ SetMapMode(aOldMode);
+}
+
+// #i70788# central method to get the OverlayManager safely
+rtl::Reference<sdr::overlay::OverlayManager> ScGridWindow::getOverlayManager() const
+{
+ SdrPageView* pPV = mrViewData.GetView()->GetScDrawView()->GetSdrPageView();
+
+ if(pPV)
+ {
+ SdrPageWindow* pPageWin = pPV->FindPageWindow( *GetOutDev() );
+
+ if ( pPageWin )
+ {
+ return pPageWin->GetOverlayManager();
+ }
+ }
+
+ return rtl::Reference<sdr::overlay::OverlayManager>();
+}
+
+void ScGridWindow::flushOverlayManager()
+{
+ // #i70788# get the OverlayManager safely
+ rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager();
+
+ if (xOverlayManager.is())
+ xOverlayManager->flush();
+}
+
+ScViewData& ScGridWindow::getViewData()
+{
+ return mrViewData;
+}
+
+FactoryFunction ScGridWindow::GetUITestFactory() const
+{
+ return ScGridWinUIObject::create;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/gridwin2.cxx b/sc/source/ui/view/gridwin2.cxx
new file mode 100644
index 0000000000..01f5f39dd7
--- /dev/null
+++ b/sc/source/ui/view/gridwin2.cxx
@@ -0,0 +1,1312 @@
+/* -*- 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 .
+ */
+
+#include <scitems.hxx>
+#include <vcl/settings.hxx>
+#include <comphelper/lok.hxx>
+
+#include <attrib.hxx>
+#include <gridwin.hxx>
+#include <tabvwsh.hxx>
+#include <docsh.hxx>
+#include <viewdata.hxx>
+#include <pivot.hxx>
+#include <uiitems.hxx>
+#include <scresid.hxx>
+#include <globstr.hrc>
+#include <strings.hrc>
+#include <pagedata.hxx>
+#include <dpobject.hxx>
+#include <dpsave.hxx>
+#include <dpshttab.hxx>
+#include <dbdocfun.hxx>
+#include <checklistmenu.hxx>
+#include <dpcontrol.hxx>
+#include <userlist.hxx>
+#include <scabstdlg.hxx>
+
+#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
+#include <com/sun/star/sheet/DataPilotTableHeaderData.hpp>
+
+#include <unordered_map>
+#include <memory>
+#include <vector>
+
+using namespace css;
+using namespace css::sheet;
+using css::sheet::DataPilotFieldOrientation;
+using std::vector;
+
+DataPilotFieldOrientation ScGridWindow::GetDPFieldOrientation( SCCOL nCol, SCROW nRow ) const
+{
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCTAB nTab = mrViewData.GetTabNo();
+ ScDPObject* pDPObj = rDoc.GetDPAtCursor(nCol, nRow, nTab);
+ if (!pDPObj)
+ return DataPilotFieldOrientation_HIDDEN;
+
+ DataPilotFieldOrientation nOrient = DataPilotFieldOrientation_HIDDEN;
+
+ // Check for page field first.
+ if (nCol > 0)
+ {
+ // look for the dimension header left of the drop-down arrow
+ tools::Long nField = pDPObj->GetHeaderDim( ScAddress( nCol-1, nRow, nTab ), nOrient );
+ if ( nField >= 0 && nOrient == DataPilotFieldOrientation_PAGE )
+ {
+ bool bIsDataLayout = false;
+ OUString aFieldName = pDPObj->GetDimName( nField, bIsDataLayout );
+ if ( !aFieldName.isEmpty() && !bIsDataLayout )
+ return DataPilotFieldOrientation_PAGE;
+ }
+ }
+
+ nOrient = DataPilotFieldOrientation_HIDDEN;
+
+ // Now, check for row/column field.
+ tools::Long nField = pDPObj->GetHeaderDim(ScAddress(nCol, nRow, nTab), nOrient);
+ if (nField >= 0 && (nOrient == DataPilotFieldOrientation_COLUMN || nOrient == DataPilotFieldOrientation_ROW) )
+ {
+ bool bIsDataLayout = false;
+ OUString aFieldName = pDPObj->GetDimName(nField, bIsDataLayout);
+ if (!aFieldName.isEmpty() && !bIsDataLayout)
+ return nOrient;
+ }
+
+ return DataPilotFieldOrientation_HIDDEN;
+}
+
+// private method for mouse button handling
+bool ScGridWindow::DoPageFieldSelection( SCCOL nCol, SCROW nRow )
+{
+ if (GetDPFieldOrientation( nCol, nRow ) == DataPilotFieldOrientation_PAGE)
+ {
+ LaunchPageFieldMenu( nCol, nRow );
+ return true;
+ }
+ return false;
+}
+
+bool ScGridWindow::DoAutoFilterButton( SCCOL nCol, SCROW nRow, const MouseEvent& rMEvt )
+{
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCTAB nTab = mrViewData.GetTabNo();
+ Point aScrPos = mrViewData.GetScrPos(nCol, nRow, eWhich);
+ Point aDiffPix = rMEvt.GetPosPixel();
+
+ aDiffPix -= aScrPos;
+ bool bLOKActive = comphelper::LibreOfficeKit::isActive();
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+ if ( bLayoutRTL && !bLOKActive )
+ aDiffPix.setX( -aDiffPix.X() );
+
+ tools::Long nSizeX, nSizeY;
+ mrViewData.GetMergeSizePixel( nCol, nRow, nSizeX, nSizeY );
+ // The button height should not use the merged cell height, should still use single row height
+ nSizeY = ScViewData::ToPixel(rDoc.GetRowHeight(nRow, nTab), mrViewData.GetPPTY());
+ Size aScrSize(nSizeX-1, nSizeY-1);
+
+ // Check if the mouse cursor is clicking on the popup arrow box.
+ mpFilterButton.reset(new ScDPFieldButton(GetOutDev(), &GetSettings().GetStyleSettings(), &mrViewData.GetZoomY(), &rDoc));
+ mpFilterButton->setBoundingBox(aScrPos, aScrSize, bLayoutRTL && !bLOKActive);
+ mpFilterButton->setPopupLeft(bLayoutRTL && bLOKActive ? false : bLayoutRTL); // #i114944# AutoFilter button is left-aligned in RTL
+ Point aPopupPos;
+ Size aPopupSize;
+ mpFilterButton->getPopupBoundingBox(aPopupPos, aPopupSize);
+ tools::Rectangle aRect(aPopupPos, aPopupSize);
+ if (aRect.Contains(rMEvt.GetPosPixel()))
+ {
+ if ( DoPageFieldSelection( nCol, nRow ) )
+ return true;
+
+ bool bFilterActive = IsAutoFilterActive(nCol, nRow, nTab);
+ mpFilterButton->setHasHiddenMember(bFilterActive);
+ mpFilterButton->setDrawBaseButton(false);
+ mpFilterButton->setDrawPopupButton(true);
+ mpFilterButton->setPopupPressed(true);
+ mpFilterButton->draw();
+ LaunchAutoFilterMenu(nCol, nRow);
+ return true;
+ }
+
+ return false;
+}
+
+void ScGridWindow::DoPushPivotButton( SCCOL nCol, SCROW nRow, const MouseEvent& rMEvt, bool bButton, bool bPopup, bool bMultiField )
+{
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCTAB nTab = mrViewData.GetTabNo();
+
+ ScDPObject* pDPObj = rDoc.GetDPAtCursor(nCol, nRow, nTab);
+
+ if (pDPObj)
+ {
+ DataPilotFieldOrientation nOrient = DataPilotFieldOrientation_HIDDEN;
+ ScAddress aPos( nCol, nRow, nTab );
+ ScAddress aDimPos = aPos;
+ if (!bButton && bPopup && aDimPos.Col() > 0)
+ // For page field selection cell, the real field position is to the left.
+ aDimPos.IncCol(-1);
+
+ if (bMultiField && DPTestMultiFieldPopupArrow(rMEvt, aPos, pDPObj))
+ {
+ // Multi-field pop up menu has been launched. Don't activate
+ // field move or regular popup.
+ return;
+ }
+
+ tools::Long nField = pDPObj->GetHeaderDim(aDimPos, nOrient);
+ if ( nField >= 0 )
+ {
+ bDPMouse = false;
+ nDPField = nField;
+ pDragDPObj = pDPObj;
+
+ if (bPopup && DPTestFieldPopupArrow(rMEvt, aPos, aDimPos, pDPObj))
+ {
+ // field name pop up menu has been launched. Don't activate
+ // field move.
+ return;
+ }
+
+ if (bButton)
+ {
+ bDPMouse = true;
+ DPTestMouse( rMEvt, true );
+ StartTracking();
+ }
+ }
+ else if ( pDPObj->IsFilterButton(aPos) )
+ {
+ ReleaseMouse(); // may have been captured in ButtonDown
+
+ ScQueryParam aQueryParam;
+ SCTAB nSrcTab = 0;
+ const ScSheetSourceDesc* pDesc = pDPObj->GetSheetDesc();
+ OSL_ENSURE(pDesc, "no sheet source for filter button");
+ if (pDesc)
+ {
+ aQueryParam = pDesc->GetQueryParam();
+ nSrcTab = pDesc->GetSourceRange().aStart.Tab();
+ }
+
+ SfxItemSetFixed<SCITEM_QUERYDATA, SCITEM_QUERYDATA> aArgSet( mrViewData.GetViewShell()->GetPool() );
+ aArgSet.Put( ScQueryItem( SCITEM_QUERYDATA, &mrViewData, &aQueryParam ) );
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScPivotFilterDlg> pDlg(
+ pFact->CreateScPivotFilterDlg(
+ mrViewData.GetViewShell()->GetFrameWeld(), aArgSet, nSrcTab));
+ if ( pDlg->Execute() == RET_OK )
+ {
+ ScSheetSourceDesc aNewDesc(&rDoc);
+ if (pDesc)
+ aNewDesc = *pDesc;
+
+ const ScQueryItem& rQueryItem = pDlg->GetOutputItem();
+ aNewDesc.SetQueryParam(rQueryItem.GetQueryData());
+
+ ScDPObject aNewObj( *pDPObj );
+ aNewObj.SetSheetDesc( aNewDesc );
+ ScDBDocFunc aFunc( *mrViewData.GetDocShell() );
+ aFunc.DataPilotUpdate( pDPObj, &aNewObj, true, false );
+ mrViewData.GetView()->CursorPosChanged(); // shells may be switched
+ }
+ }
+ }
+ else
+ {
+ OSL_FAIL("Nothing here");
+ }
+}
+
+void ScGridWindow::DoPushPivotToggle( SCCOL nCol, SCROW nRow, const MouseEvent& rMEvt )
+{
+ bool bLayoutRTL = mrViewData.GetDocument().IsLayoutRTL( mrViewData.GetTabNo() );
+
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCTAB nTab = mrViewData.GetTabNo();
+
+ ScDPObject* pDPObj = rDoc.GetDPAtCursor(nCol, nRow, nTab);
+ if (!pDPObj)
+ return;
+
+ if (!pDPObj->GetSaveData()->GetDrillDown())
+ return;
+
+ // Get the geometry of the cell.
+ Point aScrPos = mrViewData.GetScrPos(nCol, nRow, eWhich);
+ tools::Long nSizeX, nSizeY;
+ mrViewData.GetMergeSizePixel(nCol, nRow, nSizeX, nSizeY);
+ Size aScrSize(nSizeX - 1, nSizeY - 1);
+
+ sal_uInt16 nIndent = 0;
+ if (const ScIndentItem* pIndentItem = rDoc.GetAttr(nCol, nRow, nTab, ATTR_INDENT))
+ nIndent = pIndentItem->GetValue();
+
+ // Check if the mouse cursor is clicking on the toggle +/- box.
+ ScDPFieldButton aBtn(GetOutDev(), &GetSettings().GetStyleSettings(), &GetMapMode().GetScaleY());
+ aBtn.setBoundingBox(aScrPos, aScrSize, bLayoutRTL);
+ aBtn.setDrawToggleButton(true, true, nIndent);
+ Point aPopupPos;
+ Size aPopupSize;
+ aBtn.getToggleBoundingBox(aPopupPos, aPopupSize);
+ tools::Rectangle aRect(aPopupPos, aPopupSize);
+ if (aRect.Contains(rMEvt.GetPosPixel()))
+ {
+ // Mouse cursor inside the toggle +/- box.
+ sheet::DataPilotTableHeaderData aData;
+ ScAddress aCellPos(nCol, nRow, nTab);
+ pDPObj->GetHeaderPositionData(aCellPos, aData);
+ ScDPObject aNewObj(*pDPObj);
+ pDPObj->ToggleDetails(aData, &aNewObj);
+ ScDBDocFunc aFunc(*mrViewData.GetDocShell());
+ aFunc.DataPilotUpdate(pDPObj, &aNewObj, true, false);
+ }
+}
+
+// Data Pilot interaction
+
+void ScGridWindow::DPTestMouse( const MouseEvent& rMEvt, bool bMove )
+{
+ OSL_ENSURE(pDragDPObj, "pDragDPObj missing");
+
+ // scroll window if at edges
+ //! move this to separate method
+
+ bool bTimer = false;
+ Point aPixel = rMEvt.GetPosPixel();
+
+ SCCOL nDx = 0;
+ SCROW nDy = 0;
+ if ( aPixel.X() < 0 )
+ nDx = -1;
+ if ( aPixel.Y() < 0 )
+ nDy = -1;
+ Size aSize = GetOutputSizePixel();
+ if ( aPixel.X() >= aSize.Width() )
+ nDx = 1;
+ if ( aPixel.Y() >= aSize.Height() )
+ nDy = 1;
+ if ( nDx != 0 || nDy != 0 )
+ {
+ UpdateDragRect( false, tools::Rectangle() );
+
+ if ( nDx != 0)
+ mrViewData.GetView()->ScrollX( nDx, WhichH(eWhich) );
+ if ( nDy != 0 )
+ mrViewData.GetView()->ScrollY( nDy, WhichV(eWhich) );
+
+ bTimer = true;
+ }
+
+ SCCOL nPosX;
+ SCROW nPosY;
+ mrViewData.GetPosFromPixel( aPixel.X(), aPixel.Y(), eWhich, nPosX, nPosY );
+ bool bMouseLeft;
+ bool bMouseTop;
+ mrViewData.GetMouseQuadrant( aPixel, eWhich, nPosX, nPosY, bMouseLeft, bMouseTop );
+
+ ScAddress aPos( nPosX, nPosY, mrViewData.GetTabNo() );
+
+ tools::Rectangle aPosRect;
+ DataPilotFieldOrientation nOrient;
+ tools::Long nDimPos;
+ bool bHasRange = pDragDPObj->GetHeaderDrag( aPos, bMouseLeft, bMouseTop, nDPField,
+ aPosRect, nOrient, nDimPos );
+ UpdateDragRect( bHasRange && bMove, aPosRect );
+
+ bool bIsDataLayout;
+ sal_Int32 nDimFlags = 0;
+ OUString aDimName = pDragDPObj->GetDimName( nDPField, bIsDataLayout, &nDimFlags );
+ bool bAllowed = !bHasRange || ScDPObject::IsOrientationAllowed( nOrient, nDimFlags );
+
+ if (bMove) // set mouse pointer
+ {
+ PointerStyle ePointer = PointerStyle::PivotDelete;
+ if ( !bAllowed )
+ ePointer = PointerStyle::NotAllowed;
+ else if ( bHasRange )
+ switch (nOrient)
+ {
+ case DataPilotFieldOrientation_COLUMN: ePointer = PointerStyle::PivotCol; break;
+ case DataPilotFieldOrientation_ROW: ePointer = PointerStyle::PivotRow; break;
+ case DataPilotFieldOrientation_PAGE:
+ case DataPilotFieldOrientation_DATA: ePointer = PointerStyle::PivotField; break;
+ default: break;
+ }
+ SetPointer( ePointer );
+ }
+ else // execute change
+ {
+ if (!bHasRange)
+ nOrient = DataPilotFieldOrientation_HIDDEN;
+
+ if ( bIsDataLayout && ( nOrient != DataPilotFieldOrientation_COLUMN &&
+ nOrient != DataPilotFieldOrientation_ROW ) )
+ {
+ // removing data layout is not allowed
+ mrViewData.GetView()->ErrorMessage(STR_PIVOT_MOVENOTALLOWED);
+ }
+ else if ( bAllowed )
+ {
+ ScDPSaveData aSaveData( *pDragDPObj->GetSaveData() );
+
+ ScDPSaveDimension* pDim;
+ if ( bIsDataLayout )
+ pDim = aSaveData.GetDataLayoutDimension();
+ else
+ pDim = aSaveData.GetDimensionByName(aDimName);
+ pDim->SetOrientation( nOrient );
+ aSaveData.SetPosition( pDim, nDimPos );
+
+ //! docfunc method with ScDPSaveData as argument?
+
+ ScDPObject aNewObj( *pDragDPObj );
+ aNewObj.SetSaveData( aSaveData );
+ ScDBDocFunc aFunc( *mrViewData.GetDocShell() );
+ // when dragging fields, allow re-positioning (bAllowMove)
+ aFunc.DataPilotUpdate( pDragDPObj, &aNewObj, true, false, true );
+ mrViewData.GetView()->CursorPosChanged(); // shells may be switched
+ }
+ }
+
+ if (bTimer && bMove)
+ mrViewData.GetView()->SetTimer( this, rMEvt ); // repeat event
+ else
+ mrViewData.GetView()->ResetTimer();
+}
+
+bool ScGridWindow::DPTestFieldPopupArrow(
+ const MouseEvent& rMEvt, const ScAddress& rPos, const ScAddress& rDimPos, ScDPObject* pDPObj)
+{
+ bool bLayoutRTL = mrViewData.GetDocument().IsLayoutRTL( mrViewData.GetTabNo() );
+ bool bLOK = comphelper::LibreOfficeKit::isActive();
+
+ // Get the geometry of the cell.
+ Point aScrPos = mrViewData.GetScrPos(rPos.Col(), rPos.Row(), eWhich);
+ tools::Long nSizeX, nSizeY;
+ mrViewData.GetMergeSizePixel(rPos.Col(), rPos.Row(), nSizeX, nSizeY);
+ Size aScrSize(nSizeX-1, nSizeY-1);
+
+ // Check if the mouse cursor is clicking on the popup arrow box.
+ ScDPFieldButton aBtn(GetOutDev(), &GetSettings().GetStyleSettings(), &GetMapMode().GetScaleY());
+ aBtn.setBoundingBox(aScrPos, aScrSize, bLayoutRTL);
+ aBtn.setPopupLeft(false); // DataPilot popup is always right-aligned for now
+ Point aPopupPos;
+ Size aPopupSize;
+ aBtn.getPopupBoundingBox(aPopupPos, aPopupSize);
+ tools::Rectangle aRect(aPopupPos, aPopupSize);
+ if (aRect.Contains(rMEvt.GetPosPixel()))
+ {
+ // Mouse cursor inside the popup arrow box. Launch the field menu.
+ DPLaunchFieldPopupMenu(bLOK ? aScrPos : OutputToScreenPixel(aScrPos), aScrSize, rDimPos, pDPObj);
+ return true;
+ }
+
+ return false;
+}
+
+bool ScGridWindow::DPTestMultiFieldPopupArrow(
+ const MouseEvent& rMEvt, const ScAddress& rPos, ScDPObject* pDPObj)
+{
+ bool bLayoutRTL = mrViewData.GetDocument().IsLayoutRTL( mrViewData.GetTabNo() );
+ bool bLOK = comphelper::LibreOfficeKit::isActive();
+
+ // Get the geometry of the cell.
+ Point aScrPos = mrViewData.GetScrPos(rPos.Col(), rPos.Row(), eWhich);
+ tools::Long nSizeX, nSizeY;
+ mrViewData.GetMergeSizePixel(rPos.Col(), rPos.Row(), nSizeX, nSizeY);
+ Size aScrSize(nSizeX - 1, nSizeY - 1);
+
+ // Check if the mouse cursor is clicking on the popup arrow box.
+ ScDPFieldButton aBtn(GetOutDev(), &GetSettings().GetStyleSettings(), &GetMapMode().GetScaleY());
+ aBtn.setBoundingBox(aScrPos, aScrSize, bLayoutRTL);
+ aBtn.setPopupLeft(false); // DataPilot popup is always right-aligned for now
+ aBtn.setDrawPopupButtonMulti(true);
+ Point aPopupPos;
+ Size aPopupSize;
+ aBtn.getPopupBoundingBox(aPopupPos, aPopupSize);
+ tools::Rectangle aRect(aPopupPos, aPopupSize);
+ if (aRect.Contains(rMEvt.GetPosPixel()))
+ {
+ DataPilotFieldOrientation nOrient;
+ pDPObj->GetHeaderDim(rPos, nOrient);
+ // Mouse cursor inside the popup arrow box. Launch the multi-field menu.
+ DPLaunchMultiFieldPopupMenu(bLOK ? aScrPos : OutputToScreenPixel(aScrPos), aScrSize, pDPObj, nOrient);
+ return true;
+ }
+
+ return false;
+}
+
+namespace {
+
+struct DPFieldPopupData : public ScCheckListMenuControl::ExtendedData
+{
+ ScDPLabelData maLabels;
+ ScDPObject* mpDPObj;
+ tools::Long mnDim;
+};
+
+struct DPMultiFieldPopupData : public DPFieldPopupData
+{
+ std::vector<tools::Long> maFieldIndices;
+ std::vector<OUString> maFieldNames;
+};
+
+class DPFieldPopupOKAction : public ScCheckListMenuControl::Action
+{
+public:
+ explicit DPFieldPopupOKAction(ScGridWindow* p) :
+ mpGridWindow(p) {}
+
+ virtual bool execute() override
+ {
+ mpGridWindow->UpdateDPFromFieldPopupMenu();
+ return true;
+ }
+private:
+ VclPtr<ScGridWindow> mpGridWindow;
+};
+
+class DPFieldChangedAction : public ScCheckListMenuControl::Action
+{
+public:
+ explicit DPFieldChangedAction(ScGridWindow* p) :
+ mpGridWindow(p) {}
+
+ virtual bool execute() override
+ {
+ mpGridWindow->UpdateDPPopupMenuForFieldChange();
+ return true;
+ }
+private:
+ VclPtr<ScGridWindow> mpGridWindow;
+};
+
+class PopupSortAction : public ScCheckListMenuControl::Action
+{
+public:
+ enum SortType { ASCENDING, DESCENDING, CUSTOM };
+
+ explicit PopupSortAction(ScDPObject* pDPObject, tools::Long nDimIndex, SortType eType,
+ sal_uInt16 nUserListIndex, ScTabViewShell* pViewShell)
+ : mpDPObject(pDPObject)
+ , mnDimIndex(nDimIndex)
+ , meType(eType)
+ , mnUserListIndex(nUserListIndex)
+ , mpViewShell(pViewShell)
+ {}
+
+ virtual bool execute() override
+ {
+ switch (meType)
+ {
+ case ASCENDING:
+ mpViewShell->DataPilotSort(mpDPObject, mnDimIndex, true);
+ break;
+ case DESCENDING:
+ mpViewShell->DataPilotSort(mpDPObject, mnDimIndex, false);
+ break;
+ case CUSTOM:
+ mpViewShell->DataPilotSort(mpDPObject, mnDimIndex, true, &mnUserListIndex);
+ break;
+ default:
+ ;
+ }
+ return true;
+ }
+
+private:
+ ScDPObject* mpDPObject;
+ tools::Long mnDimIndex;
+ SortType meType;
+ sal_uInt16 mnUserListIndex;
+ ScTabViewShell* mpViewShell;
+};
+
+}
+
+void ScGridWindow::DPLaunchFieldPopupMenu(const Point& rScreenPosition, const Size& rScreenSize,
+ const ScAddress& rAddress, ScDPObject* pDPObject)
+{
+ DataPilotFieldOrientation nOrient;
+ tools::Long nDimIndex = pDPObject->GetHeaderDim(rAddress, nOrient);
+
+ DPLaunchFieldPopupMenu(rScreenPosition, rScreenSize, nDimIndex, pDPObject);
+}
+
+bool lcl_FillDPFieldPopupData(tools::Long nDimIndex, ScDPObject* pDPObj,
+ DPFieldPopupData& rDPData, bool& bDimOrientNotPage)
+{
+ if (!pDPObj)
+ return false;
+
+ rDPData.mnDim = nDimIndex;
+ pDPObj->GetSource();
+
+ bool bIsDataLayout;
+ OUString aDimName = pDPObj->GetDimName(rDPData.mnDim, bIsDataLayout);
+ pDPObj->BuildAllDimensionMembers();
+ const ScDPSaveData* pSaveData = pDPObj->GetSaveData();
+ const ScDPSaveDimension* pDim = pSaveData->GetExistingDimensionByName(aDimName);
+ if (!pDim)
+ // This should never happen.
+ return false;
+
+ bDimOrientNotPage = pDim->GetOrientation() != DataPilotFieldOrientation_PAGE;
+
+ // We need to get the list of field members.
+ pDPObj->FillLabelData(rDPData.mnDim, rDPData.maLabels);
+ rDPData.mpDPObj = pDPObj;
+
+ return true;
+}
+
+void ScGridWindow::DPLaunchMultiFieldPopupMenu(const Point& rScreenPosition, const Size& rScreenSize,
+ ScDPObject* pDPObj, DataPilotFieldOrientation nOrient)
+{
+ if (!pDPObj)
+ return;
+
+ pDPObj->GetSource();
+
+ std::unique_ptr<DPMultiFieldPopupData> pDPData(new DPMultiFieldPopupData);
+ pDPObj->GetFieldIdsNames(nOrient, pDPData->maFieldIndices, pDPData->maFieldNames);
+
+ if (pDPData->maFieldIndices.empty())
+ return;
+
+ tools::Long nDimIndex = pDPData->maFieldIndices[0];
+
+ bool bDimOrientNotPage = true;
+ if (!lcl_FillDPFieldPopupData(nDimIndex, pDPObj, *pDPData, bDimOrientNotPage))
+ return;
+
+ mpDPFieldPopup.reset();
+
+ weld::Window* pPopupParent = GetFrameWeld();
+ mpDPFieldPopup.reset(new ScCheckListMenuControl(pPopupParent, mrViewData,
+ false, -1, true));
+
+ mpDPFieldPopup->addFields(pDPData->maFieldNames);
+ DPSetupFieldPopup(std::move(pDPData), bDimOrientNotPage, pDPObj, true);
+
+ DPConfigFieldPopup();
+
+ if (IsMouseCaptured())
+ ReleaseMouse();
+
+ tools::Rectangle aCellRect(rScreenPosition, rScreenSize);
+ mpDPFieldPopup->launch(pPopupParent, aCellRect);
+}
+
+void ScGridWindow::DPPopulateFieldMembers(const ScDPLabelData& rLabelData)
+{
+ // Populate field members.
+ size_t n = rLabelData.maMembers.size();
+ mpDPFieldPopup->setMemberSize(n);
+ for (size_t i = 0; i < n; ++i)
+ {
+ const ScDPLabelData::Member& rMem = rLabelData.maMembers[i];
+ OUString aName = rMem.getDisplayName();
+ if (aName.isEmpty())
+ // Use special string for an empty name.
+ mpDPFieldPopup->addMember(ScResId(STR_EMPTYDATA), 0.0, rMem.mbVisible, false);
+ else
+ mpDPFieldPopup->addMember(rMem.getDisplayName(), 0.0, rMem.mbVisible, false);
+ }
+}
+
+void ScGridWindow::DPSetupFieldPopup(std::unique_ptr<ScCheckListMenuControl::ExtendedData> pDPData,
+ bool bDimOrientNotPage, ScDPObject* pDPObj,
+ bool bMultiField)
+{
+ if (!mpDPFieldPopup || !pDPObj)
+ return;
+
+ const ScDPLabelData& rLabelData = static_cast<DPFieldPopupData*>(pDPData.get())->maLabels;
+ const tools::Long nDimIndex = static_cast<DPFieldPopupData*>(pDPData.get())->mnDim;
+ mpDPFieldPopup->setExtendedData(std::move(pDPData));
+
+ if (bMultiField)
+ mpDPFieldPopup->setFieldChangedAction(new DPFieldChangedAction(this));
+
+ mpDPFieldPopup->setOKAction(new DPFieldPopupOKAction(this));
+ DPPopulateFieldMembers(rLabelData);
+
+ if (bDimOrientNotPage)
+ {
+ vector<OUString> aUserSortNames;
+ ScUserList* pUserList = ScGlobal::GetUserList();
+ if (pUserList)
+ {
+ size_t n = pUserList->size();
+ aUserSortNames.reserve(n);
+ for (size_t i = 0; i < n; ++i)
+ {
+ const ScUserListData& rData = (*pUserList)[i];
+ aUserSortNames.push_back(rData.GetString());
+ }
+ }
+
+ // Populate the menus.
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+ mpDPFieldPopup->addMenuItem(
+ ScResId(STR_MENU_SORT_ASC),
+ new PopupSortAction(pDPObj, nDimIndex, PopupSortAction::ASCENDING, 0, pViewShell));
+ mpDPFieldPopup->addMenuItem(
+ ScResId(STR_MENU_SORT_DESC),
+ new PopupSortAction(pDPObj, nDimIndex, PopupSortAction::DESCENDING, 0, pViewShell));
+
+ ScListSubMenuControl* pSubMenu = mpDPFieldPopup->addSubMenuItem(ScResId(STR_MENU_SORT_CUSTOM), !aUserSortNames.empty(), false);
+ if (pSubMenu)
+ {
+ size_t n = aUserSortNames.size();
+ for (size_t i = 0; i < n; ++i)
+ {
+ pSubMenu->addMenuItem(aUserSortNames[i],
+ new PopupSortAction(pDPObj, nDimIndex, PopupSortAction::CUSTOM, sal_uInt16(i), pViewShell));
+ }
+ pSubMenu->resizeToFitMenuItems();
+ }
+ }
+
+ mpDPFieldPopup->initMembers();
+}
+
+void ScGridWindow::DPConfigFieldPopup()
+{
+ if (!mpDPFieldPopup)
+ return;
+
+ ScCheckListMenuControl::Config aConfig;
+ aConfig.mbAllowEmptySet = false;
+ aConfig.mbRTL = mrViewData.GetDocument().IsLayoutRTL(mrViewData.GetTabNo());
+ mpDPFieldPopup->setConfig(aConfig);
+}
+
+void ScGridWindow::DPLaunchFieldPopupMenu(const Point& rScrPos, const Size& rScrSize,
+ tools::Long nDimIndex, ScDPObject* pDPObj)
+{
+ std::unique_ptr<DPFieldPopupData> pDPData(new DPFieldPopupData);
+ bool bDimOrientNotPage = true;
+ if (!lcl_FillDPFieldPopupData(nDimIndex, pDPObj, *pDPData, bDimOrientNotPage))
+ return;
+
+ mpDPFieldPopup.reset();
+
+ vcl::ILibreOfficeKitNotifier* pNotifier = nullptr;
+ if (comphelper::LibreOfficeKit::isActive())
+ pNotifier = SfxViewShell::Current();
+
+ weld::Window* pPopupParent = GetFrameWeld();
+ mpDPFieldPopup.reset(new ScCheckListMenuControl(pPopupParent, mrViewData,
+ false, -1, pNotifier));
+
+ DPSetupFieldPopup(std::move(pDPData), bDimOrientNotPage, pDPObj);
+
+ DPConfigFieldPopup();
+
+ if (IsMouseCaptured())
+ ReleaseMouse();
+
+ tools::Rectangle aCellRect(rScrPos, rScrSize);
+ mpDPFieldPopup->launch(pPopupParent, aCellRect);
+}
+
+void ScGridWindow::UpdateDPPopupMenuForFieldChange()
+{
+ if (!mpDPFieldPopup)
+ return;
+
+ DPMultiFieldPopupData* pDPData = static_cast<DPMultiFieldPopupData*>(mpDPFieldPopup->getExtendedData());
+ if (!pDPData)
+ return;
+
+ if (pDPData->maFieldIndices.empty())
+ return;
+
+ tools::Long nIndex = mpDPFieldPopup->getField();
+ if (nIndex < 0)
+ return;
+
+ tools::Long nDimIndex = pDPData->maFieldIndices[nIndex];
+ if (nDimIndex == pDPData->mnDim)
+ return;
+
+ bool bDimOrientNotPage = true;
+ if (!lcl_FillDPFieldPopupData(nDimIndex, pDPData->mpDPObj, *pDPData, bDimOrientNotPage))
+ return;
+
+ mpDPFieldPopup->clearMembers();
+
+ DPPopulateFieldMembers(pDPData->maLabels);
+
+ mpDPFieldPopup->initMembers();
+}
+
+void ScGridWindow::UpdateDPFromFieldPopupMenu()
+{
+ typedef std::unordered_map<OUString, OUString> MemNameMapType;
+
+ if (!mpDPFieldPopup)
+ return;
+
+ DPFieldPopupData* pDPData = static_cast<DPFieldPopupData*>(mpDPFieldPopup->getExtendedData());
+ if (!pDPData)
+ return;
+
+ ScDPObject* pDPObj = pDPData->mpDPObj;
+ ScDPSaveData* pSaveData = pDPObj->GetSaveData();
+
+ bool bIsDataLayout;
+ OUString aDimName = pDPObj->GetDimName(pDPData->mnDim, bIsDataLayout);
+ ScDPSaveDimension* pDim = pSaveData->GetDimensionByName(aDimName);
+ if (!pDim)
+ return;
+
+ // Build a map of layout names to original names.
+ const ScDPLabelData& rLabelData = pDPData->maLabels;
+ MemNameMapType aMemNameMap;
+ for (const auto& rMember : rLabelData.maMembers)
+ aMemNameMap.emplace(rMember.maLayoutName, rMember.maName);
+
+ // The raw result may contain a mixture of layout names and original names.
+ ScCheckListMenuControl::ResultType aRawResult;
+ mpDPFieldPopup->getResult(aRawResult);
+
+ std::unordered_map<OUString, bool> aResult;
+ for (const auto& rItem : aRawResult)
+ {
+ MemNameMapType::const_iterator itrNameMap = aMemNameMap.find(rItem.aName);
+ if (itrNameMap == aMemNameMap.end())
+ {
+ // This is an original member name. Use it as-is.
+ OUString aName = rItem.aName;
+ if (aName == ScResId(STR_EMPTYDATA))
+ // Translate the special empty name into an empty string.
+ aName.clear();
+
+ aResult.emplace(aName, rItem.bValid);
+ }
+ else
+ {
+ // This is a layout name. Get the original member name and use it.
+ aResult.emplace(itrNameMap->second, rItem.bValid);
+ }
+ }
+ pDim->UpdateMemberVisibility(aResult);
+
+ ScDBDocFunc aFunc(*mrViewData.GetDocShell());
+ aFunc.UpdatePivotTable(*pDPObj, true, false);
+}
+
+namespace {
+
+template <typename T>
+inline
+T lcl_getValidValue(T value, T defvalue)
+{
+ return (value <0) ? defvalue : value;
+}
+
+} // anonymous namespace
+
+bool ScGridWindow::UpdateVisibleRange()
+{
+ ScDocument const& rDoc = mrViewData.GetDocument();
+ SCCOL nPosX = 0;
+ SCROW nPosY = 0;
+ SCCOL nXRight = rDoc.MaxCol();
+ SCROW nYBottom = rDoc.MaxRow();
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+ nPosX = lcl_getValidValue(pViewShell->GetLOKStartHeaderCol(), nPosX);
+ nPosY = lcl_getValidValue(pViewShell->GetLOKStartHeaderRow(), nPosY);
+ nXRight = lcl_getValidValue(pViewShell->GetLOKEndHeaderCol(), nXRight);
+ nYBottom = lcl_getValidValue(pViewShell->GetLOKEndHeaderRow(), nYBottom);
+ }
+ else
+ {
+ nPosX = mrViewData.GetPosX(eHWhich);
+ nPosY = mrViewData.GetPosY(eVWhich);
+ nXRight = nPosX + mrViewData.VisibleCellsX(eHWhich);
+ if (nXRight > rDoc.MaxCol())
+ nXRight = rDoc.MaxCol();
+ nYBottom = nPosY + mrViewData.VisibleCellsY(eVWhich);
+ if (nYBottom > rDoc.MaxRow())
+ nYBottom = rDoc.MaxRow();
+ }
+
+ // Store the current visible range.
+ return maVisibleRange.set(nPosX, nPosY, nXRight, nYBottom);
+}
+
+void ScGridWindow::DPMouseMove( const MouseEvent& rMEvt )
+{
+ DPTestMouse( rMEvt, true );
+}
+
+void ScGridWindow::DPMouseButtonUp( const MouseEvent& rMEvt )
+{
+ bDPMouse = false;
+ ReleaseMouse();
+
+ DPTestMouse( rMEvt, false );
+ SetPointer( PointerStyle::Arrow );
+}
+
+void ScGridWindow::UpdateDragRect( bool bShowRange, const tools::Rectangle& rPosRect )
+{
+ SCCOL nStartX = ( rPosRect.Left() >= 0 ) ? static_cast<SCCOL>(rPosRect.Left()) : SCCOL_MAX;
+ SCROW nStartY = ( rPosRect.Top() >= 0 ) ? static_cast<SCROW>(rPosRect.Top()) : SCROW_MAX;
+ SCCOL nEndX = ( rPosRect.Right() >= 0 ) ? static_cast<SCCOL>(rPosRect.Right()) : SCCOL_MAX;
+ SCROW nEndY = ( rPosRect.Bottom() >= 0 ) ? static_cast<SCROW>(rPosRect.Bottom()) : SCROW_MAX;
+
+ if ( bShowRange == bDragRect && nDragStartX == nStartX && nDragEndX == nEndX &&
+ nDragStartY == nStartY && nDragEndY == nEndY )
+ {
+ return; // everything unchanged
+ }
+
+ if ( bShowRange )
+ {
+ nDragStartX = nStartX;
+ nDragStartY = nStartY;
+ nDragEndX = nEndX;
+ nDragEndY = nEndY;
+ bDragRect = true;
+ }
+ else
+ bDragRect = false;
+
+ UpdateDragRectOverlay();
+}
+
+// Page-Break Mode
+
+sal_uInt16 ScGridWindow::HitPageBreak( const Point& rMouse, ScRange* pSource,
+ SCCOLROW* pBreak, SCCOLROW* pPrev )
+{
+ sal_uInt16 nFound = SC_PD_NONE; // 0
+ ScRange aSource;
+ SCCOLROW nBreak = 0;
+ SCCOLROW nPrev = 0;
+
+ ScPageBreakData* pPageData = mrViewData.GetView()->GetPageBreakData();
+ if ( pPageData )
+ {
+ bool bHori = false;
+ bool bVert = false;
+ SCCOL nHitX = 0;
+ SCROW nHitY = 0;
+
+ tools::Long nMouseX = rMouse.X();
+ tools::Long nMouseY = rMouse.Y();
+ SCCOL nPosX;
+ SCROW nPosY;
+ mrViewData.GetPosFromPixel( nMouseX, nMouseY, eWhich, nPosX, nPosY );
+ Point aTL = mrViewData.GetScrPos( nPosX, nPosY, eWhich );
+ Point aBR = mrViewData.GetScrPos( nPosX+1, nPosY+1, eWhich );
+
+ // Horizontal more tolerances as for vertical, because there is more space
+ if ( nMouseX <= aTL.X() + 4 )
+ {
+ bHori = true;
+ nHitX = nPosX;
+ }
+ else if ( nMouseX >= aBR.X() - 6 )
+ {
+ bHori = true;
+ nHitX = nPosX+1; // left edge of the next cell
+ }
+ if ( nMouseY <= aTL.Y() + 2 )
+ {
+ bVert = true;
+ nHitY = nPosY;
+ }
+ else if ( nMouseY >= aBR.Y() - 4 )
+ {
+ bVert = true;
+ nHitY = nPosY+1; // upper edge of the next cell
+ }
+
+ if ( bHori || bVert )
+ {
+ sal_uInt16 nCount = sal::static_int_cast<sal_uInt16>( pPageData->GetCount() );
+ for (sal_uInt16 nPos=0; nPos<nCount && !nFound; nPos++)
+ {
+ ScPrintRangeData& rData = pPageData->GetData(nPos);
+ ScRange aRange = rData.GetPrintRange();
+ bool bLHit = ( bHori && nHitX == aRange.aStart.Col() );
+ bool bRHit = ( bHori && nHitX == aRange.aEnd.Col() + 1 );
+ bool bTHit = ( bVert && nHitY == aRange.aStart.Row() );
+ bool bBHit = ( bVert && nHitY == aRange.aEnd.Row() + 1 );
+ bool bInsideH = ( nPosX >= aRange.aStart.Col() && nPosX <= aRange.aEnd.Col() );
+ bool bInsideV = ( nPosY >= aRange.aStart.Row() && nPosY <= aRange.aEnd.Row() );
+
+ if ( bLHit )
+ {
+ if ( bTHit )
+ nFound = SC_PD_RANGE_TL;
+ else if ( bBHit )
+ nFound = SC_PD_RANGE_BL;
+ else if ( bInsideV )
+ nFound = SC_PD_RANGE_L;
+ }
+ else if ( bRHit )
+ {
+ if ( bTHit )
+ nFound = SC_PD_RANGE_TR;
+ else if ( bBHit )
+ nFound = SC_PD_RANGE_BR;
+ else if ( bInsideV )
+ nFound = SC_PD_RANGE_R;
+ }
+ else if ( bTHit && bInsideH )
+ nFound = SC_PD_RANGE_T;
+ else if ( bBHit && bInsideH )
+ nFound = SC_PD_RANGE_B;
+ if (nFound)
+ aSource = aRange;
+
+ // breaks
+
+ if ( bVert && bInsideH && !nFound )
+ {
+ size_t nRowCount = rData.GetPagesY();
+ const SCROW* pRowEnd = rData.GetPageEndY();
+ for (size_t nRowPos=0; nRowPos+1<nRowCount; nRowPos++)
+ if ( pRowEnd[nRowPos]+1 == nHitY )
+ {
+ nFound = SC_PD_BREAK_V;
+ aSource = aRange;
+ nBreak = nHitY;
+ if ( nRowPos )
+ nPrev = pRowEnd[nRowPos-1]+1;
+ else
+ nPrev = aRange.aStart.Row();
+ }
+ }
+ if ( bHori && bInsideV && !nFound )
+ {
+ size_t nColCount = rData.GetPagesX();
+ const SCCOL* pColEnd = rData.GetPageEndX();
+ for (size_t nColPos=0; nColPos+1<nColCount; nColPos++)
+ if ( pColEnd[nColPos]+1 == nHitX )
+ {
+ nFound = SC_PD_BREAK_H;
+ aSource = aRange;
+ nBreak = nHitX;
+ if ( nColPos )
+ nPrev = pColEnd[nColPos-1]+1;
+ else
+ nPrev = aRange.aStart.Col();
+ }
+ }
+ }
+ }
+ }
+
+ if (pSource)
+ *pSource = aSource; // print break
+ if (pBreak)
+ *pBreak = nBreak; // X/Y position of the moved page break
+ if (pPrev)
+ *pPrev = nPrev; // X/Y beginning of the page, which is above the break
+ return nFound;
+}
+
+void ScGridWindow::PagebreakMove( const MouseEvent& rMEvt, bool bUp )
+{
+ //! Combine scrolling and switching with RFMouseMove !
+ //! (Inverting before scrolling is different)
+
+ // Scrolling
+
+ bool bTimer = false;
+ Point aPos = rMEvt.GetPosPixel();
+ SCCOL nDx = 0;
+ SCROW nDy = 0;
+ if ( aPos.X() < 0 ) nDx = -1;
+ if ( aPos.Y() < 0 ) nDy = -1;
+ Size aSize = GetOutputSizePixel();
+ if ( aPos.X() >= aSize.Width() )
+ nDx = 1;
+ if ( aPos.Y() >= aSize.Height() )
+ nDy = 1;
+ if ( nDx != 0 || nDy != 0 )
+ {
+ if ( bPagebreakDrawn ) // invert
+ {
+ bPagebreakDrawn = false;
+ UpdateDragRectOverlay();
+ }
+
+ if ( nDx != 0 ) mrViewData.GetView()->ScrollX( nDx, WhichH(eWhich) );
+ if ( nDy != 0 ) mrViewData.GetView()->ScrollY( nDy, WhichV(eWhich) );
+ bTimer = true;
+ }
+
+ // Switching when fixating (so Scrolling works)
+
+ if ( eWhich == mrViewData.GetActivePart() ) //??
+ {
+ if ( mrViewData.GetHSplitMode() == SC_SPLIT_FIX )
+ if ( nDx > 0 )
+ {
+ if ( eWhich == SC_SPLIT_TOPLEFT )
+ mrViewData.GetView()->ActivatePart( SC_SPLIT_TOPRIGHT );
+ else if ( eWhich == SC_SPLIT_BOTTOMLEFT )
+ mrViewData.GetView()->ActivatePart( SC_SPLIT_BOTTOMRIGHT );
+ }
+
+ if ( mrViewData.GetVSplitMode() == SC_SPLIT_FIX )
+ if ( nDy > 0 )
+ {
+ if ( eWhich == SC_SPLIT_TOPLEFT )
+ mrViewData.GetView()->ActivatePart( SC_SPLIT_BOTTOMLEFT );
+ else if ( eWhich == SC_SPLIT_TOPRIGHT )
+ mrViewData.GetView()->ActivatePart( SC_SPLIT_BOTTOMRIGHT );
+ }
+ }
+
+ // from here new
+
+ // Searching for a position between the cells (before nPosX / nPosY)
+ SCCOL nPosX;
+ SCROW nPosY;
+ mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
+ bool bLeft, bTop;
+ mrViewData.GetMouseQuadrant( aPos, eWhich, nPosX, nPosY, bLeft, bTop );
+ if ( !bLeft ) ++nPosX;
+ if ( !bTop ) ++nPosY;
+
+ bool bBreak = ( nPagebreakMouse == SC_PD_BREAK_H || nPagebreakMouse == SC_PD_BREAK_V );
+ bool bHide = false;
+ bool bToEnd = false;
+ ScRange aDrawRange = aPagebreakSource;
+ if ( bBreak )
+ {
+ if ( nPagebreakMouse == SC_PD_BREAK_H )
+ {
+ if ( nPosX > aPagebreakSource.aStart.Col() &&
+ nPosX <= aPagebreakSource.aEnd.Col() + 1 ) // to the end is also allowed
+ {
+ bToEnd = ( nPosX == aPagebreakSource.aEnd.Col() + 1 );
+ aDrawRange.aStart.SetCol( nPosX );
+ aDrawRange.aEnd.SetCol( nPosX - 1 );
+ }
+ else
+ bHide = true;
+ }
+ else
+ {
+ if ( nPosY > aPagebreakSource.aStart.Row() &&
+ nPosY <= aPagebreakSource.aEnd.Row() + 1 ) // to the end is also allowed
+ {
+ bToEnd = ( nPosY == aPagebreakSource.aEnd.Row() + 1 );
+ aDrawRange.aStart.SetRow( nPosY );
+ aDrawRange.aEnd.SetRow( nPosY - 1 );
+ }
+ else
+ bHide = true;
+ }
+ }
+ else
+ {
+ if ( nPagebreakMouse & SC_PD_RANGE_L )
+ aDrawRange.aStart.SetCol( nPosX );
+ if ( nPagebreakMouse & SC_PD_RANGE_T )
+ aDrawRange.aStart.SetRow( nPosY );
+ if ( nPagebreakMouse & SC_PD_RANGE_R )
+ {
+ if ( nPosX > 0 )
+ aDrawRange.aEnd.SetCol( nPosX-1 );
+ else
+ bHide = true;
+ }
+ if ( nPagebreakMouse & SC_PD_RANGE_B )
+ {
+ if ( nPosY > 0 )
+ aDrawRange.aEnd.SetRow( nPosY-1 );
+ else
+ bHide = true;
+ }
+ if ( aDrawRange.aStart.Col() > aDrawRange.aEnd.Col() ||
+ aDrawRange.aStart.Row() > aDrawRange.aEnd.Row() )
+ bHide = true;
+ }
+
+ if ( !bPagebreakDrawn || bUp || aDrawRange != aPagebreakDrag )
+ {
+ // draw...
+
+ if ( bPagebreakDrawn )
+ {
+ // invert
+ bPagebreakDrawn = false;
+ }
+ aPagebreakDrag = aDrawRange;
+ if ( !bUp && !bHide )
+ {
+ // revert
+ bPagebreakDrawn = true;
+ }
+ UpdateDragRectOverlay();
+ }
+
+ // when ButtonUp execute the changes
+
+ if ( bUp )
+ {
+ ScViewFunc* pViewFunc = mrViewData.GetView();
+ ScDocShell* pDocSh = mrViewData.GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTab = mrViewData.GetTabNo();
+ bool bUndo (rDoc.IsUndoEnabled());
+
+ if ( bBreak )
+ {
+ bool bColumn = ( nPagebreakMouse == SC_PD_BREAK_H );
+ SCCOLROW nNew = bColumn ? static_cast<SCCOLROW>(nPosX) : static_cast<SCCOLROW>(nPosY);
+ if ( nNew != nPagebreakBreak )
+ {
+ if (bUndo)
+ {
+ OUString aUndo = ScResId( STR_UNDO_DRAG_BREAK );
+ pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, mrViewData.GetViewShell()->GetViewShellId() );
+ }
+
+ bool bGrow = !bHide && nNew > nPagebreakBreak;
+ if ( bColumn )
+ {
+ if (rDoc.HasColBreak(static_cast<SCCOL>(nPagebreakBreak), nTab) & ScBreakType::Manual)
+ {
+ ScAddress aOldAddr( static_cast<SCCOL>(nPagebreakBreak), nPosY, nTab );
+ pViewFunc->DeletePageBreak( true, true, &aOldAddr, false );
+ }
+ if ( !bHide && !bToEnd ) // not at the end
+ {
+ ScAddress aNewAddr( static_cast<SCCOL>(nNew), nPosY, nTab );
+ pViewFunc->InsertPageBreak( true, true, &aNewAddr, false );
+ }
+ if ( bGrow )
+ {
+ // change last break to hard, and change scaling
+ bool bManualBreak(rDoc.HasColBreak(static_cast<SCCOL>(nPagebreakPrev), nTab) & ScBreakType::Manual);
+ if ( static_cast<SCCOL>(nPagebreakPrev) > aPagebreakSource.aStart.Col() && !bManualBreak )
+ {
+ ScAddress aPrev( static_cast<SCCOL>(nPagebreakPrev), nPosY, nTab );
+ pViewFunc->InsertPageBreak( true, true, &aPrev, false );
+ }
+
+ if (!pDocSh->AdjustPrintZoom( ScRange(
+ static_cast<SCCOL>(nPagebreakPrev),0,nTab, static_cast<SCCOL>(nNew-1),0,nTab ) ))
+ bGrow = false;
+ }
+ }
+ else
+ {
+ if (rDoc.HasRowBreak(nPagebreakBreak, nTab) & ScBreakType::Manual)
+ {
+ ScAddress aOldAddr( nPosX, nPagebreakBreak, nTab );
+ pViewFunc->DeletePageBreak( false, true, &aOldAddr, false );
+ }
+ if ( !bHide && !bToEnd ) // not at the end
+ {
+ ScAddress aNewAddr( nPosX, nNew, nTab );
+ pViewFunc->InsertPageBreak( false, true, &aNewAddr, false );
+ }
+ if ( bGrow )
+ {
+ // change last break to hard, and change scaling
+ bool bManualBreak(rDoc.HasRowBreak(nPagebreakPrev, nTab) & ScBreakType::Manual);
+ if ( nPagebreakPrev > aPagebreakSource.aStart.Row() && !bManualBreak )
+ {
+ ScAddress aPrev( nPosX, nPagebreakPrev, nTab );
+ pViewFunc->InsertPageBreak( false, true, &aPrev, false );
+ }
+
+ if (!pDocSh->AdjustPrintZoom( ScRange(
+ 0,nPagebreakPrev,nTab, 0,nNew-1,nTab ) ))
+ bGrow = false;
+ }
+ }
+
+ if (bUndo)
+ {
+ pDocSh->GetUndoManager()->LeaveListAction();
+ }
+
+ if (!bGrow) // otherwise has already happened in AdjustPrintZoom
+ {
+ pViewFunc->UpdatePageBreakData( true );
+ pDocSh->SetDocumentModified();
+ }
+ }
+ }
+ else if ( bHide || aPagebreakDrag != aPagebreakSource )
+ {
+ // set print range
+
+ OUString aNewRanges;
+ sal_uInt16 nOldCount = rDoc.GetPrintRangeCount( nTab );
+ if ( nOldCount )
+ {
+ for (sal_uInt16 nPos=0; nPos<nOldCount; nPos++)
+ {
+ const ScRange* pOld = rDoc.GetPrintRange( nTab, nPos );
+ if ( pOld )
+ {
+ OUString aTemp;
+ if ( *pOld != aPagebreakSource )
+ aTemp = pOld->Format(rDoc, ScRefFlags::VALID);
+ else if ( !bHide )
+ aTemp = aPagebreakDrag.Format(rDoc, ScRefFlags::VALID);
+ if (!aTemp.isEmpty())
+ {
+ if ( !aNewRanges.isEmpty() )
+ aNewRanges += ";";
+ aNewRanges += aTemp;
+ }
+ }
+ }
+ }
+ else if (!bHide)
+ aNewRanges = aPagebreakDrag.Format(rDoc, ScRefFlags::VALID);
+
+ pViewFunc->SetPrintRanges( rDoc.IsPrintEntireSheet( nTab ), &aNewRanges, nullptr, nullptr, false );
+ }
+ }
+
+ // Timer for Scrolling
+
+ if (bTimer && !bUp)
+ mrViewData.GetView()->SetTimer( this, rMEvt ); // repeat event
+ else
+ mrViewData.GetView()->ResetTimer();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/gridwin3.cxx b/sc/source/ui/view/gridwin3.cxx
new file mode 100644
index 0000000000..e550447da8
--- /dev/null
+++ b/sc/source/ui/view/gridwin3.cxx
@@ -0,0 +1,399 @@
+/* -*- 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 .
+ */
+
+#include <svx/svdpagv.hxx>
+#include <svx/svxids.hrc>
+#include <editeng/sizeitem.hxx>
+#include <sfx2/bindings.hxx>
+#include <svl/ptitem.hxx>
+#include <osl/diagnose.h>
+
+#include <tabvwsh.hxx>
+#include <gridwin.hxx>
+#include <dbfunc.hxx>
+#include <viewdata.hxx>
+#include <output.hxx>
+#include <drawview.hxx>
+#include <fupoor.hxx>
+
+#include <drawutil.hxx>
+#include <document.hxx>
+#include <comphelper/lok.hxx>
+
+bool ScGridWindow::DrawMouseButtonDown(const MouseEvent& rMEvt)
+{
+ bool bRet = false;
+ FuPoor* pDraw = mrViewData.GetView()->GetDrawFuncPtr();
+ if (pDraw && !mrViewData.IsRefMode())
+ {
+ MapMode aDrawMode = GetDrawMapMode();
+ MapMode aOldMode = GetMapMode();
+ if ( comphelper::LibreOfficeKit::isActive() && aOldMode != aDrawMode )
+ SetMapMode( aDrawMode );
+
+ pDraw->SetWindow( this );
+ Point aLogicPos = PixelToLogic(rMEvt.GetPosPixel());
+ if ( pDraw->IsDetectiveHit( aLogicPos ) )
+ {
+ // nothing on detective arrows (double click is evaluated on ButtonUp)
+ bRet = true;
+ }
+ else
+ {
+ bRet = pDraw->MouseButtonDown( rMEvt );
+ if ( bRet )
+ UpdateStatusPosSize();
+ }
+
+ if ( comphelper::LibreOfficeKit::isActive() && aOldMode != aDrawMode )
+ SetMapMode( aOldMode );
+ }
+
+ // cancel draw with right key
+ ScDrawView* pDrView = mrViewData.GetScDrawView();
+ if ( pDrView && !rMEvt.IsLeft() && !bRet )
+ {
+ pDrView->BrkAction();
+ bRet = true;
+ }
+ return bRet;
+}
+
+bool ScGridWindow::DrawMouseButtonUp(const MouseEvent& rMEvt)
+{
+ ScViewFunc* pView = mrViewData.GetView();
+ bool bRet = false;
+ FuPoor* pDraw = pView->GetDrawFuncPtr();
+ if (pDraw && !mrViewData.IsRefMode())
+ {
+ MapMode aDrawMode = GetDrawMapMode();
+ MapMode aOldMode = GetMapMode();
+ if ( comphelper::LibreOfficeKit::isActive() && aOldMode != aDrawMode )
+ SetMapMode( aDrawMode );
+
+ pDraw->SetWindow( this );
+ bRet = pDraw->MouseButtonUp( rMEvt );
+
+ // execute "format paint brush" for drawing objects
+ SfxItemSet* pDrawBrush = pView->GetDrawBrushSet();
+ if ( pDrawBrush )
+ {
+ ScDrawView* pDrView = mrViewData.GetScDrawView();
+ if ( pDrView )
+ {
+ pDrView->SetAttrToMarked(*pDrawBrush, true/*bReplaceAll*/);
+ }
+
+ if ( !pView->IsPaintBrushLocked() )
+ pView->ResetBrushDocument(); // end paint brush mode if not locked
+ }
+
+ if ( comphelper::LibreOfficeKit::isActive() && aOldMode != aDrawMode )
+ SetMapMode( aOldMode );
+ }
+
+ return bRet;
+}
+
+bool ScGridWindow::DrawMouseMove(const MouseEvent& rMEvt)
+{
+ FuPoor* pDraw = mrViewData.GetView()->GetDrawFuncPtr();
+ if (pDraw && !mrViewData.IsRefMode())
+ {
+ MapMode aDrawMode = GetDrawMapMode();
+ MapMode aOldMode = GetMapMode();
+ if ( comphelper::LibreOfficeKit::isActive() && aOldMode != aDrawMode )
+ SetMapMode( aDrawMode );
+
+ pDraw->SetWindow( this );
+ bool bRet = pDraw->MouseMove( rMEvt );
+ if ( bRet )
+ UpdateStatusPosSize();
+
+ if ( comphelper::LibreOfficeKit::isActive() && aOldMode != aDrawMode )
+ SetMapMode( aOldMode );
+
+ return bRet;
+ }
+ else
+ {
+ SetPointer( PointerStyle::Arrow );
+ return false;
+ }
+}
+
+void ScGridWindow::DrawEndAction()
+{
+ ScDrawView* pDrView = mrViewData.GetScDrawView();
+ if ( pDrView && pDrView->IsAction() )
+ pDrView->BrkAction();
+
+ FuPoor* pDraw = mrViewData.GetView()->GetDrawFuncPtr();
+ if (pDraw)
+ pDraw->StopDragTimer();
+
+ // ReleaseMouse on call
+}
+
+bool ScGridWindow::DrawCommand(const CommandEvent& rCEvt)
+{
+ ScDrawView* pDrView = mrViewData.GetScDrawView();
+ FuPoor* pDraw = mrViewData.GetView()->GetDrawFuncPtr();
+ if (pDrView && pDraw && !mrViewData.IsRefMode())
+ {
+ pDraw->SetWindow( this );
+ sal_uInt8 nUsed = pDraw->Command( rCEvt );
+ if( nUsed == SC_CMD_USED )
+ nButtonDown = 0; // MouseButtonUp is swallowed...
+ if( nUsed || pDrView->IsAction() )
+ return true;
+ }
+
+ return false;
+}
+
+bool ScGridWindow::DrawKeyInput(const KeyEvent& rKEvt, vcl::Window* pWin)
+{
+ ScDrawView* pDrView = mrViewData.GetScDrawView();
+ FuPoor* pDraw = mrViewData.GetView()->GetDrawFuncPtr();
+
+
+ if (pDrView && pDrView->KeyInput(rKEvt, pWin))
+ return true;
+
+ if (pDrView && pDraw && !mrViewData.IsRefMode())
+ {
+ pDraw->SetWindow( this );
+ bool bOldMarked = pDrView->AreObjectsMarked();
+ if (pDraw->KeyInput( rKEvt ))
+ {
+ bool bLeaveDraw = false;
+ bool bUsed = true;
+ bool bNewMarked = pDrView->AreObjectsMarked();
+ if ( !mrViewData.GetView()->IsDrawSelMode() )
+ if ( !bNewMarked )
+ {
+ mrViewData.GetViewShell()->SetDrawShell( false );
+ bLeaveDraw = true;
+ if ( !bOldMarked &&
+ rKEvt.GetKeyCode().GetCode() == KEY_DELETE )
+ bUsed = false; // nothing deleted
+ if(bOldMarked)
+ GetFocus();
+ }
+ if (!bLeaveDraw)
+ UpdateStatusPosSize(); // for moving/resizing etc. by keyboard
+ return bUsed;
+ }
+ }
+
+ return false;
+}
+
+void ScGridWindow::DrawRedraw( ScOutputData& rOutputData, SdrLayerID nLayer )
+{
+ const ScViewOptions& rOpts = mrViewData.GetOptions();
+
+ // use new flags at SdrPaintView for hiding objects
+ const bool bDrawOle(VOBJ_MODE_SHOW == rOpts.GetObjMode(VOBJ_TYPE_OLE));
+ const bool bDrawChart(VOBJ_MODE_SHOW == rOpts.GetObjMode(VOBJ_TYPE_CHART));
+ const bool bDrawDraw(VOBJ_MODE_SHOW == rOpts.GetObjMode(VOBJ_TYPE_DRAW));
+
+ if(!(bDrawOle || bDrawChart || bDrawDraw))
+ return;
+
+ ScDrawView* pDrView = mrViewData.GetView()->GetScDrawView();
+
+ if(pDrView)
+ {
+ pDrView->setHideOle(!bDrawOle);
+ pDrView->setHideChart(!bDrawChart);
+ pDrView->setHideDraw(!bDrawDraw);
+ pDrView->setHideFormControl(!bDrawDraw);
+ }
+
+ rOutputData.DrawSelectiveObjects(nLayer);
+}
+
+void ScGridWindow::DrawSdrGrid( const tools::Rectangle& rDrawingRect, OutputDevice* pContentDev )
+{
+ // Draw grid lines
+
+ ScDrawView* pDrView = mrViewData.GetView()->GetScDrawView();
+ if ( pDrView && pDrView->IsGridVisible() )
+ {
+ SdrPageView* pPV = pDrView->GetSdrPageView();
+ OSL_ENSURE(pPV, "PageView not available");
+ if (pPV)
+ {
+ pContentDev->SetLineColor(COL_GRAY);
+
+ pPV->DrawPageViewGrid( *pContentDev, rDrawingRect );
+ }
+ }
+}
+
+MapMode ScGridWindow::GetDrawMapMode( bool bForce )
+{
+ ScDocument& rDoc = mrViewData.GetDocument();
+
+ // FIXME this shouldn't be necessary once we change the entire Calc to
+ // work in the logic coordinates (ideally 100ths of mm - so that it is
+ // the same as editeng and drawinglayer), and get rid of all the
+ // SetMapMode's and other unnecessary fun we have with pixels
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ return mrViewData.GetLogicMode();
+ }
+
+ SCTAB nTab = mrViewData.GetTabNo();
+ bool bNegativePage = rDoc.IsNegativePage( nTab );
+
+ MapMode aDrawMode = mrViewData.GetLogicMode();
+
+ ScDrawView* pDrView = mrViewData.GetView()->GetScDrawView();
+ if ( pDrView || bForce )
+ {
+ Fraction aScaleX;
+ Fraction aScaleY;
+ if (pDrView)
+ pDrView->GetScale( aScaleX, aScaleY );
+ else
+ {
+ SCCOL nEndCol = 0;
+ SCROW nEndRow = 0;
+ rDoc.GetTableArea( nTab, nEndCol, nEndRow );
+ if (nEndCol<20) nEndCol = 20;
+ if (nEndRow<20) nEndRow = 1000;
+ ScDrawUtil::CalcScale( rDoc, nTab, 0,0, nEndCol,nEndRow, GetOutDev(),
+ mrViewData.GetZoomX(),mrViewData.GetZoomY(),
+ mrViewData.GetPPTX(),mrViewData.GetPPTY(),
+ aScaleX,aScaleY );
+ }
+ aDrawMode.SetScaleX(aScaleX);
+ aDrawMode.SetScaleY(aScaleY);
+ }
+ aDrawMode.SetOrigin(Point());
+ Point aStartPos = mrViewData.GetPixPos(eWhich);
+ if ( bNegativePage )
+ {
+ // RTL uses negative positions for drawing objects
+ aStartPos.setX( -aStartPos.X() + GetOutputSizePixel().Width() - 1 );
+ }
+ aDrawMode.SetOrigin( PixelToLogic( aStartPos, aDrawMode ) );
+
+ return aDrawMode;
+}
+
+void ScGridWindow::DrawAfterScroll()
+{
+ PaintImmediately(); // always, so the behaviour with and without DrawingLayer is the same
+
+ ScDrawView* pDrView = mrViewData.GetView()->GetScDrawView();
+ if (pDrView)
+ {
+ OutlinerView* pOlView = pDrView->GetTextEditOutlinerView();
+ if (pOlView && pOlView->GetWindow() == this)
+ pOlView->ShowCursor(false); // was removed at scrolling
+ }
+}
+
+void ScGridWindow::CreateAnchorHandle(SdrHdlList& rHdl, const ScAddress& rAddress)
+{
+ ScDrawView* pDrView = mrViewData.GetView()->GetScDrawView();
+ if (pDrView)
+ {
+ const ScViewOptions& rOpts = mrViewData.GetOptions();
+ if(rOpts.GetOption( VOPT_ANCHOR ))
+ {
+ bool bNegativePage = mrViewData.GetDocument().IsNegativePage( mrViewData.GetTabNo() );
+ Point aPos = mrViewData.GetScrPos( rAddress.Col(), rAddress.Row(), eWhich, true );
+ aPos = PixelToLogic(aPos);
+ rHdl.AddHdl(std::make_unique<SdrHdl>(aPos, bNegativePage ? SdrHdlKind::Anchor_TR : SdrHdlKind::Anchor));
+ }
+ }
+}
+
+void ScGridWindow::UpdateStatusPosSize()
+{
+ ScDrawView* pDrView = mrViewData.GetView()->GetScDrawView();
+ if (!pDrView)
+ return; // shouldn't be called in that case
+
+ SdrPageView* pPV = pDrView->GetSdrPageView();
+ if (!pPV)
+ return; // shouldn't be called in that case either
+
+ SfxItemSetFixed<SID_ATTR_POSITION, SID_ATTR_SIZE> aSet(mrViewData.GetViewShell()->GetPool());
+
+ // Fill items for position and size:
+ // show action rectangle during action,
+ // position and size of selected object(s) if something is selected,
+ // mouse position otherwise
+
+ bool bActionItem = false;
+ if ( pDrView->IsAction() ) // action rectangle
+ {
+ tools::Rectangle aRect;
+ pDrView->TakeActionRect( aRect );
+ if ( !aRect.IsEmpty() )
+ {
+ pPV->LogicToPagePos(aRect);
+ aSet.Put( SfxPointItem( SID_ATTR_POSITION, aRect.TopLeft() ) );
+ aSet.Put( SvxSizeItem( SID_ATTR_SIZE,
+ Size( aRect.Right() - aRect.Left(), aRect.Bottom() - aRect.Top() ) ) );
+ bActionItem = true;
+ }
+ }
+ if ( !bActionItem )
+ {
+ if ( pDrView->AreObjectsMarked() ) // selected objects
+ {
+ tools::Rectangle aRect = pDrView->GetAllMarkedRect();
+ pPV->LogicToPagePos(aRect);
+ aSet.Put( SfxPointItem( SID_ATTR_POSITION, aRect.TopLeft() ) );
+ aSet.Put( SvxSizeItem( SID_ATTR_SIZE,
+ Size( aRect.Right() - aRect.Left(), aRect.Bottom() - aRect.Top() ) ) );
+ }
+ else // mouse position
+ {
+ Point aPos = PixelToLogic(aCurMousePos);
+ pPV->LogicToPagePos(aPos);
+ aSet.Put( SfxPointItem( SID_ATTR_POSITION, aPos ) );
+ aSet.Put( SvxSizeItem( SID_ATTR_SIZE, Size( 0, 0 ) ) );
+ }
+ }
+
+ mrViewData.GetBindings().SetState(aSet);
+}
+
+bool ScGridWindow::DrawHasMarkedObj()
+{
+ ScDrawView* p = mrViewData.GetScDrawView();
+ return p && p->AreObjectsMarked();
+}
+
+void ScGridWindow::DrawMarkDropObj( SdrObject* pObj )
+{
+ ScDrawView* pDrView = mrViewData.GetView()->GetScDrawView();
+ if (pDrView)
+ pDrView->MarkDropObj(pObj);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/gridwin4.cxx b/sc/source/ui/view/gridwin4.cxx
new file mode 100644
index 0000000000..3639e82876
--- /dev/null
+++ b/sc/source/ui/view/gridwin4.cxx
@@ -0,0 +1,2695 @@
+/* -*- 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 .
+ */
+
+#include <memory>
+#include <scitems.hxx>
+#include <editeng/eeitem.hxx>
+
+#include <svtools/colorcfg.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/printer.hxx>
+#include <vcl/cursor.hxx>
+#include <vcl/settings.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <osl/diagnose.h>
+
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <comphelper/lok.hxx>
+#include <comphelper/scopeguard.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <sfx2/lokcomponenthelpers.hxx>
+
+#include <svx/svdview.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/sdrpagewindow.hxx>
+#include <svx/sdr/contact/objectcontactofpageview.hxx>
+#include <svx/sdr/contact/viewobjectcontact.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+#include <tabvwsh.hxx>
+#include <vcl/lineinfo.hxx>
+#include <vcl/sysdata.hxx>
+
+#include <gridwin.hxx>
+#include <viewdata.hxx>
+#include <output.hxx>
+#include <document.hxx>
+#include <attrib.hxx>
+#include <patattr.hxx>
+#include <dbdata.hxx>
+#include <docoptio.hxx>
+#include <notemark.hxx>
+#include <dbfunc.hxx>
+#include <scmod.hxx>
+#include <inputhdl.hxx>
+#include <rfindlst.hxx>
+#include <hiranges.hxx>
+#include <pagedata.hxx>
+#include <docpool.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <docsh.hxx>
+#include <cbutton.hxx>
+#include <invmerge.hxx>
+#include <editutil.hxx>
+#include <inputopt.hxx>
+#include <fillinfo.hxx>
+#include <dpcontrol.hxx>
+#include <queryparam.hxx>
+#include <queryentry.hxx>
+#include <markdata.hxx>
+#include <sc.hrc>
+#include <vcl/virdev.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <drwlayer.hxx>
+
+static void lcl_LimitRect( tools::Rectangle& rRect, const tools::Rectangle& rVisible )
+{
+ if ( rRect.Top() < rVisible.Top()-1 ) rRect.SetTop( rVisible.Top()-1 );
+ if ( rRect.Bottom() > rVisible.Bottom()+1 ) rRect.SetBottom( rVisible.Bottom()+1 );
+
+ // The header row must be drawn also when the inner rectangle is not visible,
+ // that is why there is no return value anymore.
+ // When it is far away, then lcl_DrawOneFrame is not even called.
+}
+
+static void lcl_DrawOneFrame( vcl::RenderContext* pDev, const tools::Rectangle& rInnerPixel,
+ const OUString& rTitle, const Color& rColor, bool bTextBelow,
+ double nPPTX, double nPPTY, const Fraction& rZoomY,
+ ScDocument& rDoc, ScViewData& rButtonViewData, bool bLayoutRTL )
+{
+ // rButtonViewData is only used to set the button size,
+
+ tools::Rectangle aInner = rInnerPixel;
+ if ( bLayoutRTL )
+ {
+ aInner.SetLeft( rInnerPixel.Right() );
+ aInner.SetRight( rInnerPixel.Left() );
+ }
+
+ tools::Rectangle aVisible( Point(0,0), pDev->GetOutputSizePixel() );
+ lcl_LimitRect( aInner, aVisible );
+
+ tools::Rectangle aOuter = aInner;
+ tools::Long nHor = static_cast<tools::Long>( SC_SCENARIO_HSPACE * nPPTX );
+ tools::Long nVer = static_cast<tools::Long>( SC_SCENARIO_VSPACE * nPPTY );
+ aOuter.AdjustLeft( -nHor );
+ aOuter.AdjustRight(nHor );
+ aOuter.AdjustTop( -nVer );
+ aOuter.AdjustBottom(nVer );
+
+ // use ScPatternAttr::GetFont only for font size
+ vcl::Font aAttrFont;
+ rDoc.GetPool()->GetDefaultItem(ATTR_PATTERN).
+ fillFontOnly(aAttrFont, pDev, &rZoomY);
+
+ // everything else from application font
+ vcl::Font aAppFont = pDev->GetSettings().GetStyleSettings().GetAppFont();
+ aAppFont.SetFontSize( aAttrFont.GetFontSize() );
+
+ aAppFont.SetAlignment( ALIGN_TOP );
+ pDev->SetFont( aAppFont );
+
+ Size aTextSize( pDev->GetTextWidth( rTitle ), pDev->GetTextHeight() );
+
+ if ( bTextBelow )
+ aOuter.AdjustBottom(aTextSize.Height() );
+ else
+ aOuter.AdjustTop( -(aTextSize.Height()) );
+
+ pDev->SetLineColor();
+ pDev->SetFillColor( rColor );
+ // left, top, right, bottom
+ pDev->DrawRect( tools::Rectangle( aOuter.Left(), aOuter.Top(), aInner.Left(), aOuter.Bottom() ) );
+ pDev->DrawRect( tools::Rectangle( aOuter.Left(), aOuter.Top(), aOuter.Right(), aInner.Top() ) );
+ pDev->DrawRect( tools::Rectangle( aInner.Right(), aOuter.Top(), aOuter.Right(), aOuter.Bottom() ) );
+ pDev->DrawRect( tools::Rectangle( aOuter.Left(), aInner.Bottom(), aOuter.Right(), aOuter.Bottom() ) );
+
+ tools::Long nButtonY = bTextBelow ? aInner.Bottom() : aOuter.Top();
+
+ ScDDComboBoxButton aComboButton(pDev);
+ aComboButton.SetOptSizePixel();
+ tools::Long nBWidth = tools::Long(aComboButton.GetSizePixel().Width() * rZoomY);
+ tools::Long nBHeight = nVer + aTextSize.Height() + 1;
+ Size aButSize( nBWidth, nBHeight );
+ tools::Long nButtonPos = bLayoutRTL ? aOuter.Left() : aOuter.Right()-nBWidth+1;
+ aComboButton.Draw( Point(nButtonPos, nButtonY), aButSize );
+ rButtonViewData.SetScenButSize( aButSize );
+
+ tools::Long nTextStart = bLayoutRTL ? aInner.Right() - aTextSize.Width() + 1 : aInner.Left();
+
+ bool bWasClip = false;
+ vcl::Region aOldClip;
+ bool bClip = ( aTextSize.Width() > aOuter.Right() - nBWidth - aInner.Left() );
+ if ( bClip )
+ {
+ if (pDev->IsClipRegion())
+ {
+ bWasClip = true;
+ aOldClip = pDev->GetActiveClipRegion();
+ }
+ tools::Long nClipStartX = bLayoutRTL ? aOuter.Left() + nBWidth : aInner.Left();
+ tools::Long nClipEndX = bLayoutRTL ? aInner.Right() : aOuter.Right() - nBWidth;
+ pDev->SetClipRegion( vcl::Region(tools::Rectangle( nClipStartX, nButtonY + nVer/2,
+ nClipEndX, nButtonY + nVer/2 + aTextSize.Height())) );
+ }
+
+ pDev->DrawText( Point( nTextStart, nButtonY + nVer/2 ), rTitle );
+
+ if ( bClip )
+ {
+ if ( bWasClip )
+ pDev->SetClipRegion(aOldClip);
+ else
+ pDev->SetClipRegion();
+ }
+
+ pDev->SetFillColor();
+ pDev->SetLineColor( COL_BLACK );
+ pDev->DrawRect( aInner );
+ pDev->DrawRect( aOuter );
+}
+
+static void lcl_DrawScenarioFrames( OutputDevice* pDev, ScViewData& rViewData, ScSplitPos eWhich,
+ SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2 )
+{
+ ScDocument& rDoc = rViewData.GetDocument();
+ SCTAB nTab = rViewData.GetTabNo();
+ SCTAB nTabCount = rDoc.GetTableCount();
+ if ( nTab+1 >= nTabCount || !rDoc.IsScenario(nTab+1) || rDoc.IsScenario(nTab) )
+ return;
+
+ if ( nX1 > 0 ) --nX1;
+ if ( nY1>=2 ) nY1 -= 2; // Hack: Header row affects two cells
+ else if ( nY1 > 0 ) --nY1;
+ if ( nX2 < rDoc.MaxCol() ) ++nX2;
+ if ( nY2 < rDoc.MaxRow()-1 ) nY2 += 2; // Hack: Header row affects two cells
+ else if ( nY2 < rDoc.MaxRow() ) ++nY2;
+ ScRange aViewRange( nX1,nY1,nTab, nX2,nY2,nTab );
+
+ //! cache the ranges in table!!!!
+
+ ScMarkData aMarks(rDoc.GetSheetLimits());
+ for (SCTAB i=nTab+1; i<nTabCount && rDoc.IsScenario(i); i++)
+ rDoc.MarkScenario( i, nTab, aMarks, false, ScScenarioFlags::ShowFrame );
+ ScRangeListRef xRanges = new ScRangeList;
+ aMarks.FillRangeListWithMarks( xRanges.get(), false );
+
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ for (size_t j = 0, n = xRanges->size(); j < n; ++j)
+ {
+ ScRange aRange = (*xRanges)[j];
+ // Always extend scenario frame to merged cells where no new non-covered cells
+ // are framed
+ rDoc.ExtendTotalMerge( aRange );
+
+ //! -> Extend repaint when merging !!!
+
+ if ( aRange.Intersects( aViewRange ) ) //! Space for Text/Button?
+ {
+ Point aStartPos = rViewData.GetScrPos(
+ aRange.aStart.Col(), aRange.aStart.Row(), eWhich, true );
+ Point aEndPos = rViewData.GetScrPos(
+ aRange.aEnd.Col()+1, aRange.aEnd.Row()+1, eWhich, true );
+ // on the grid:
+ aStartPos.AdjustX( -nLayoutSign );
+ aStartPos.AdjustY( -1 );
+ aEndPos.AdjustX( -nLayoutSign );
+ aEndPos.AdjustY( -1 );
+
+ bool bTextBelow = ( aRange.aStart.Row() == 0 );
+
+ OUString aCurrent;
+ Color aColor( COL_LIGHTGRAY );
+ for (SCTAB nAct=nTab+1; nAct<nTabCount && rDoc.IsScenario(nAct); nAct++)
+ if ( rDoc.IsActiveScenario(nAct) && rDoc.HasScenarioRange(nAct,aRange) )
+ {
+ OUString aDummyComment;
+ ScScenarioFlags nDummyFlags;
+ rDoc.GetName( nAct, aCurrent );
+ rDoc.GetScenarioData( nAct, aDummyComment, aColor, nDummyFlags );
+ }
+
+ if (aCurrent.isEmpty())
+ aCurrent = ScResId( STR_EMPTYDATA );
+
+ //! Own text "(None)" instead of "(Empty)" ???
+
+ lcl_DrawOneFrame( pDev, tools::Rectangle( aStartPos, aEndPos ),
+ aCurrent, aColor, bTextBelow,
+ rViewData.GetPPTX(), rViewData.GetPPTY(), rViewData.GetZoomY(),
+ rDoc, rViewData, bLayoutRTL );
+ }
+ }
+}
+
+static void lcl_DrawHighlight( ScOutputData& rOutputData, const ScViewData& rViewData,
+ const std::vector<ScHighlightEntry>& rHighlightRanges )
+{
+ SCTAB nTab = rViewData.GetTabNo();
+ for ( const auto& rHighlightRange : rHighlightRanges)
+ {
+ ScRange aRange = rHighlightRange.aRef;
+ if ( nTab >= aRange.aStart.Tab() && nTab <= aRange.aEnd.Tab() )
+ {
+ rOutputData.DrawRefMark(
+ aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row(),
+ rHighlightRange.aColor, false );
+ }
+ }
+}
+
+// Calculates top-left offset to be applied based on margins and indent.
+static void lcl_GetEditAreaTLOffset(tools::Long& nOffsetX, tools::Long& nOffsetY, const ScAddress& rAddr,
+ const ScViewData& rViewData, ScDocument& rDoc)
+{
+ tools::Long nLeftMargin = 0;
+ tools::Long nTopMargin = 0;
+ tools::Long nIndent = 0;
+ tools::Long nDummy = 0;
+ ScEditUtil aEUtil(&rDoc, rAddr.Col(), rAddr.Row(), rAddr.Tab(),
+ Point(0, 0), nullptr, rViewData.GetPPTX(),
+ rViewData.GetPPTY(), Fraction(1.0), Fraction(1.0),
+ false /* bPrintTwips */);
+ const ScPatternAttr* pPattern = rDoc.GetPattern(rAddr);
+ if (!rDoc.IsLayoutRTL(rAddr.Tab()))
+ nIndent = aEUtil.GetIndent(pPattern);
+ aEUtil.GetMargins(pPattern, nLeftMargin, nTopMargin, nDummy, nDummy);
+ nOffsetX = nIndent + nLeftMargin;
+ nOffsetY = nTopMargin;
+}
+
+void ScGridWindow::DoInvertRect( const tools::Rectangle& rPixel )
+{
+ if ( rPixel == aInvertRect )
+ aInvertRect = tools::Rectangle(); // Cancel
+ else
+ {
+ OSL_ENSURE( aInvertRect.IsEmpty(), "DoInvertRect no pairs" );
+
+ aInvertRect = rPixel; // Mark new rectangle
+ }
+
+ UpdateHeaderOverlay(); // uses aInvertRect
+}
+
+void ScGridWindow::PrePaint(vcl::RenderContext& /*rRenderContext*/)
+{
+ // forward PrePaint to DrawingLayer
+ ScTabViewShell* pTabViewShell = mrViewData.GetViewShell();
+
+ if(pTabViewShell)
+ {
+ SdrView* pDrawView = pTabViewShell->GetScDrawView();
+
+ if (pDrawView)
+ {
+ pDrawView->PrePaint();
+ }
+ }
+}
+
+bool ScGridWindow::NeedLOKCursorInvalidation(const tools::Rectangle& rCursorRect,
+ const Fraction aScaleX, const Fraction aScaleY)
+{
+ // Don't see the need for a map as there will be only a few zoom levels
+ // and as of now X and Y zooms in online are the same.
+ for (auto& rEntry : maLOKLastCursor)
+ {
+ if (aScaleX == rEntry.aScaleX && aScaleY == rEntry.aScaleY)
+ {
+ if (rCursorRect == rEntry.aRect)
+ return false; // No change
+
+ // Update and allow invalidate.
+ rEntry.aRect = rCursorRect;
+ return true;
+ }
+ }
+
+ maLOKLastCursor.push_back(LOKCursorEntry{aScaleX, aScaleY, rCursorRect});
+ return true;
+}
+
+void ScGridWindow::InvalidateLOKViewCursor(const tools::Rectangle& rCursorRect,
+ const Fraction aScaleX, const Fraction aScaleY)
+{
+ if (!NeedLOKCursorInvalidation(rCursorRect, aScaleX, aScaleY))
+ return;
+
+ ScTabViewShell* pThisViewShell = mrViewData.GetViewShell();
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+
+ while (pViewShell)
+ {
+ if (pViewShell != pThisViewShell && pViewShell->GetDocId() == pThisViewShell->GetDocId())
+ {
+ ScTabViewShell* pOtherViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
+ if (pOtherViewShell)
+ {
+ ScViewData& rOtherViewData = pOtherViewShell->GetViewData();
+ Fraction aZoomX = rOtherViewData.GetZoomX();
+ Fraction aZoomY = rOtherViewData.GetZoomY();
+ if (aZoomX == aScaleX && aZoomY == aScaleY)
+ {
+ SfxLokHelper::notifyOtherView(pThisViewShell, pOtherViewShell,
+ LOK_CALLBACK_INVALIDATE_VIEW_CURSOR, "rectangle", rCursorRect.toString());
+ }
+ }
+ }
+
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+}
+
+void ScGridWindow::Paint( vcl::RenderContext& /*rRenderContext*/, const tools::Rectangle& rRect )
+{
+ ScDocument& rDoc = mrViewData.GetDocument();
+ if ( rDoc.IsInInterpreter() )
+ {
+ // Via Reschedule, interpreted cells do not trigger Invalidate again,
+ // otherwise for instance an error box would never appear (bug 36381).
+ // Later, through bNeedsRepaint everything is painted again.
+ if ( bNeedsRepaint )
+ {
+ //! Merge Rectangle?
+ aRepaintPixel = tools::Rectangle(); // multiple -> paint all
+ }
+ else
+ {
+ bNeedsRepaint = true;
+ aRepaintPixel = LogicToPixel(rRect); // only affected ranges
+ }
+ return;
+ }
+
+ // #i117893# If GetSizePixel needs to call the resize handler, the resulting nested Paint call
+ // (possibly for a larger rectangle) has to be allowed. Call GetSizePixel before setting bIsInPaint.
+ GetSizePixel();
+
+ if (bIsInPaint)
+ return;
+
+ bIsInPaint = true;
+
+ tools::Rectangle aPixRect = LogicToPixel( rRect );
+
+ SCCOL nX1 = mrViewData.GetPosX(eHWhich);
+ SCROW nY1 = mrViewData.GetPosY(eVWhich);
+
+ SCTAB nTab = mrViewData.GetTabNo();
+
+ double nPPTX = mrViewData.GetPPTX();
+ double nPPTY = mrViewData.GetPPTY();
+
+ tools::Rectangle aMirroredPixel = aPixRect;
+ if ( rDoc.IsLayoutRTL( nTab ) )
+ {
+ // mirror and swap
+ tools::Long nWidth = GetSizePixel().Width();
+ aMirroredPixel.SetLeft( nWidth - 1 - aPixRect.Right() );
+ aMirroredPixel.SetRight( nWidth - 1 - aPixRect.Left() );
+ }
+
+ tools::Long nScrX = ScViewData::ToPixel( rDoc.GetColWidth( nX1, nTab ), nPPTX );
+ while ( nScrX <= aMirroredPixel.Left() && nX1 < rDoc.MaxCol() )
+ {
+ ++nX1;
+ nScrX += ScViewData::ToPixel( rDoc.GetColWidth( nX1, nTab ), nPPTX );
+ }
+ SCCOL nX2 = nX1;
+ while ( nScrX <= aMirroredPixel.Right() && nX2 < rDoc.MaxCol() )
+ {
+ ++nX2;
+ nScrX += ScViewData::ToPixel( rDoc.GetColWidth( nX2, nTab ), nPPTX );
+ }
+
+ tools::Long nScrY = 0;
+ ScViewData::AddPixelsWhile( nScrY, aPixRect.Top(), nY1, rDoc.MaxRow(), nPPTY, &rDoc, nTab);
+ SCROW nY2 = nY1;
+ if (nScrY <= aPixRect.Bottom() && nY2 < rDoc.MaxRow())
+ {
+ ++nY2;
+ ScViewData::AddPixelsWhile( nScrY, aPixRect.Bottom(), nY2, rDoc.MaxRow(), nPPTY, &rDoc, nTab);
+ }
+
+ Draw( nX1,nY1,nX2,nY2, ScUpdateMode::Marks ); // don't continue with painting
+
+ bIsInPaint = false;
+}
+
+void ScGridWindow::Draw( SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2, ScUpdateMode eMode )
+{
+ ScDocument& rDoc = mrViewData.GetDocument();
+
+ // let's ignore the normal Draw() attempts when doing the tiled rendering,
+ // all the rendering should go through PaintTile() in that case.
+ // TODO revisit if we can actually turn this into an assert(), and clean
+ // up the callers
+ if (comphelper::LibreOfficeKit::isActive())
+ return;
+
+ ScModule* pScMod = SC_MOD();
+ bool bTextWysiwyg = pScMod->GetInputOptions().GetTextWysiwyg();
+
+ if (mrViewData.IsMinimized())
+ return;
+
+ PutInOrder( nX1, nX2 );
+ PutInOrder( nY1, nY2 );
+
+ OSL_ENSURE( rDoc.ValidCol(nX2) && rDoc.ValidRow(nY2), "GridWin Draw area too big" );
+
+ UpdateVisibleRange();
+
+ if (nX2 < maVisibleRange.mnCol1 || nY2 < maVisibleRange.mnRow1)
+ return;
+ // invisible
+ if (nX1 < maVisibleRange.mnCol1)
+ nX1 = maVisibleRange.mnCol1;
+ if (nY1 < maVisibleRange.mnRow1)
+ nY1 = maVisibleRange.mnRow1;
+
+ if (nX1 > maVisibleRange.mnCol2 || nY1 > maVisibleRange.mnRow2)
+ return;
+
+ if (nX2 > maVisibleRange.mnCol2)
+ nX2 = maVisibleRange.mnCol2;
+ if (nY2 > maVisibleRange.mnRow2)
+ nY2 = maVisibleRange.mnRow2;
+
+ if ( eMode != ScUpdateMode::Marks && nX2 < maVisibleRange.mnCol2)
+ nX2 = maVisibleRange.mnCol2; // to continue painting
+
+ // point of no return
+
+ ++nPaintCount; // mark that painting is in progress
+
+ SCTAB nTab = mrViewData.GetTabNo();
+ rDoc.ExtendHidden( nX1, nY1, nX2, nY2, nTab );
+
+ Point aScrPos = mrViewData.GetScrPos( nX1, nY1, eWhich );
+ tools::Long nMirrorWidth = GetSizePixel().Width();
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+ if ( bLayoutRTL )
+ {
+ tools::Long nEndPixel = mrViewData.GetScrPos( nX2+1, maVisibleRange.mnRow1, eWhich ).X();
+ nMirrorWidth = aScrPos.X() - nEndPixel;
+ aScrPos.setX( nEndPixel + 1 );
+ }
+
+ tools::Long nScrX = aScrPos.X();
+ tools::Long nScrY = aScrPos.Y();
+
+ SCCOL nCurX = mrViewData.GetCurX();
+ SCROW nCurY = mrViewData.GetCurY();
+ SCCOL nCurEndX = nCurX;
+ SCROW nCurEndY = nCurY;
+ rDoc.ExtendMerge( nCurX, nCurY, nCurEndX, nCurEndY, nTab );
+ bool bCurVis = nCursorHideCount==0 &&
+ ( nCurEndX+1 >= nX1 && nCurX <= nX2+1 && nCurEndY+1 >= nY1 && nCurY <= nY2+1 );
+
+ // AutoFill Handles
+ if ( !bCurVis && nCursorHideCount==0 && bAutoMarkVisible && aAutoMarkPos.Tab() == nTab &&
+ ( aAutoMarkPos.Col() != nCurX || aAutoMarkPos.Row() != nCurY ) )
+ {
+ SCCOL nHdlX = aAutoMarkPos.Col();
+ SCROW nHdlY = aAutoMarkPos.Row();
+ rDoc.ExtendMerge( nHdlX, nHdlY, nHdlX, nHdlY, nTab );
+ // left and top is unaffected
+
+ //! Paint AutoFill handles alone (without Cursor) ???
+ }
+
+ double nPPTX = mrViewData.GetPPTX();
+ double nPPTY = mrViewData.GetPPTY();
+
+ const ScViewOptions& rOpts = mrViewData.GetOptions();
+
+ // data block
+
+ ScTableInfo aTabInfo;
+ rDoc.FillInfo( aTabInfo, nX1, nY1, nX2, nY2, nTab,
+ nPPTX, nPPTY, false, rOpts.GetOption(VOPT_FORMULAS),
+ &mrViewData.GetMarkData() );
+
+ Fraction aZoomX = mrViewData.GetZoomX();
+ Fraction aZoomY = mrViewData.GetZoomY();
+ ScOutputData aOutputData( GetOutDev(), OUTTYPE_WINDOW, aTabInfo, &rDoc, nTab,
+ nScrX, nScrY, nX1, nY1, nX2, nY2, nPPTX, nPPTY,
+ &aZoomX, &aZoomY );
+
+ aOutputData.SetMirrorWidth( nMirrorWidth ); // needed for RTL
+ aOutputData.SetSpellCheckContext(mpSpellCheckCxt.get());
+
+ ScopedVclPtr< VirtualDevice > xFmtVirtDev;
+ bool bLogicText = bTextWysiwyg; // call DrawStrings in logic MapMode?
+
+ if ( bTextWysiwyg )
+ {
+ // use printer for text formatting
+
+ OutputDevice* pFmtDev = rDoc.GetPrinter();
+ pFmtDev->SetMapMode( mrViewData.GetLogicMode(eWhich) );
+ aOutputData.SetFmtDevice( pFmtDev );
+ }
+ else if ( aZoomX != aZoomY && mrViewData.IsOle() )
+ {
+ // #i45033# For OLE inplace editing with different zoom factors,
+ // use a virtual device with 1/100th mm as text formatting reference
+
+ xFmtVirtDev.disposeAndReset( VclPtr<VirtualDevice>::Create() );
+ xFmtVirtDev->SetMapMode(MapMode(MapUnit::Map100thMM));
+ aOutputData.SetFmtDevice( xFmtVirtDev.get() );
+
+ bLogicText = true; // use logic MapMode
+ }
+
+ DrawContent(*GetOutDev(), aTabInfo, aOutputData, bLogicText);
+
+ // If something was inverted during the Paint (selection changed from Basic Macro)
+ // then this is now mixed up and has to be repainted
+ OSL_ENSURE(nPaintCount, "Wrong nPaintCount");
+ --nPaintCount;
+ if (!nPaintCount)
+ CheckNeedsRepaint();
+
+ // Flag drawn formula cells "unchanged".
+ rDoc.ResetChanged(ScRange(nX1, nY1, nTab, nX2, nY2, nTab));
+ rDoc.PrepareFormulaCalc();
+}
+
+namespace {
+
+class SuppressEditViewMessagesGuard
+{
+public:
+ SuppressEditViewMessagesGuard(EditView& rEditView) :
+ mrEditView(rEditView),
+ mbOrigSuppressFlag(rEditView.IsSuppressLOKMessages())
+ {
+ if (!mbOrigSuppressFlag)
+ mrEditView.SuppressLOKMessages(true);
+ }
+
+ ~SuppressEditViewMessagesGuard()
+ {
+ if (mrEditView.IsSuppressLOKMessages() != mbOrigSuppressFlag)
+ mrEditView.SuppressLOKMessages(mbOrigSuppressFlag);
+ }
+
+private:
+ EditView& mrEditView;
+ const bool mbOrigSuppressFlag;
+};
+
+}
+
+/**
+ * Used to store the necessary information about the (combined-)tile
+ * area relevant to coordinate transformations in RTL mode.
+ */
+class ScLokRTLContext
+{
+public:
+ ScLokRTLContext(const ScOutputData& rOutputData, const tools::Long nTileDeviceOriginPixelX):
+ mrOutputData(rOutputData),
+ mnTileDevOriginX(nTileDeviceOriginPixelX)
+ {}
+
+ /**
+ * Converts from document x pixel position to the
+ * corresponding pixel position w.r.t the tile device origin.
+ */
+ tools::Long docToTilePos(tools::Long nPosX) const
+ {
+ tools::Long nMirrorX = (-2 * mnTileDevOriginX) + mrOutputData.GetScrW();
+ return nMirrorX - 1 - nPosX;
+ }
+
+
+private:
+ const ScOutputData& mrOutputData;
+ const tools::Long mnTileDevOriginX;
+};
+
+namespace
+{
+int lcl_GetMultiLineHeight(EditEngine* pEditEngine)
+{
+ int nHeight = 0;
+ int nParagraphs = pEditEngine->GetParagraphCount();
+ if (nParagraphs > 1 || (nParagraphs > 0 && pEditEngine->GetLineCount(0) > 1))
+ {
+ for (int nPara = 0; nPara < nParagraphs; nPara++)
+ {
+ nHeight += pEditEngine->GetLineCount(nPara) * pEditEngine->GetLineHeight(nPara);
+ }
+ }
+
+ return nHeight;
+}
+}
+
+void ScGridWindow::DrawContent(OutputDevice &rDevice, const ScTableInfo& rTableInfo, ScOutputData& aOutputData,
+ bool bLogicText)
+{
+ ScModule* pScMod = SC_MOD();
+ ScDocument& rDoc = mrViewData.GetDocument();
+ const ScViewOptions& rOpts = mrViewData.GetOptions();
+ bool bIsTiledRendering = comphelper::LibreOfficeKit::isActive();
+ bool bNoBackgroundAndGrid = bIsTiledRendering
+ && comphelper::LibreOfficeKit::isCompatFlagSet(
+ comphelper::LibreOfficeKit::Compat::scNoGridBackground);
+
+ SCTAB nTab = aOutputData.nTab;
+ SCCOL nX1 = aOutputData.nX1;
+ SCROW nY1 = aOutputData.nY1;
+ SCCOL nX2 = aOutputData.nX2;
+ SCROW nY2 = aOutputData.nY2;
+ tools::Long nScrX = aOutputData.nScrX;
+ tools::Long nScrY = aOutputData.nScrY;
+
+ const svtools::ColorConfig& rColorCfg = pScMod->GetColorConfig();
+ Color aGridColor( rColorCfg.GetColorValue( svtools::CALCGRID ).nColor );
+ if ( aGridColor == COL_TRANSPARENT )
+ {
+ // use view options' grid color only if color config has "automatic" color
+ aGridColor = rOpts.GetGridColor();
+ }
+
+ aOutputData.SetSyntaxMode ( mrViewData.IsSyntaxMode() );
+ aOutputData.SetGridColor ( aGridColor );
+ aOutputData.SetShowNullValues ( rOpts.GetOption( VOPT_NULLVALS ) );
+ aOutputData.SetShowFormulas ( rOpts.GetOption( VOPT_FORMULAS ) );
+ aOutputData.SetShowSpellErrors ( rDoc.GetDocOptions().IsAutoSpell() );
+ aOutputData.SetMarkClipped ( SC_MOD()->GetColorConfig().GetColorValue(svtools::CALCTEXTOVERFLOW).bIsVisible );
+
+ aOutputData.SetUseStyleColor( true ); // always set in table view
+
+ aOutputData.SetViewShell( mrViewData.GetViewShell() );
+
+ bool bGrid = rOpts.GetOption( VOPT_GRID ) && mrViewData.GetShowGrid();
+ bool bGridFirst = !rOpts.GetOption( VOPT_GRID_ONTOP );
+
+ bool bPage = rOpts.GetOption( VOPT_PAGEBREAKS ) && !bIsTiledRendering;
+
+ bool bPageMode = mrViewData.IsPagebreakMode();
+ if (bPageMode) // after FindChanged
+ {
+ // SetPagebreakMode also initializes bPrinted Flags
+ aOutputData.SetPagebreakMode( mrViewData.GetView()->GetPageBreakData() );
+ }
+
+ EditView* pEditView = nullptr;
+ bool bEditMode = mrViewData.HasEditView(eWhich);
+ if ( bEditMode && mrViewData.GetRefTabNo() == nTab )
+ {
+ SCCOL nEditCol;
+ SCROW nEditRow;
+ mrViewData.GetEditView( eWhich, pEditView, nEditCol, nEditRow );
+ SCCOL nEditEndCol = mrViewData.GetEditEndCol();
+ SCROW nEditEndRow = mrViewData.GetEditEndRow();
+
+ if ( nEditEndCol >= nX1 && nEditCol <= nX2 && nEditEndRow >= nY1 && nEditRow <= nY2 )
+ aOutputData.SetEditCell( nEditCol, nEditRow );
+ else
+ bEditMode = false;
+ }
+
+ const MapMode aOriginalMode = rDevice.GetMapMode();
+
+ // define drawing layer map mode and paint rectangle
+ MapMode aDrawMode = GetDrawMapMode();
+ if (bIsTiledRendering)
+ {
+ // FIXME this shouldn't be necessary once we change the entire Calc to
+ // work in the logic coordinates (ideally 100ths of mm - so that it is
+ // the same as editeng and drawinglayer), and get rid of all the
+ // SetMapMode's and other unnecessary fun we have with pixels
+ // See also ScGridWindow::GetDrawMapMode() for the rest of this hack
+ aDrawMode.SetOrigin(PixelToLogic(Point(nScrX, nScrY), aDrawMode));
+ }
+ tools::Rectangle aDrawingRectLogic;
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+ bool bLokRTL = bLayoutRTL && bIsTiledRendering;
+ std::unique_ptr<ScLokRTLContext> pLokRTLCtxt(
+ bLokRTL ?
+ new ScLokRTLContext(aOutputData, o3tl::convert(aOriginalMode.GetOrigin().X(), o3tl::Length::twip, o3tl::Length::px)) :
+ nullptr);
+
+ {
+ // get drawing pixel rect
+ tools::Rectangle aDrawingRectPixel(
+ bLokRTL ? Point(-(nScrX + aOutputData.GetScrW()), nScrY) : Point(nScrX, nScrY),
+ Size(aOutputData.GetScrW(), aOutputData.GetScrH()));
+
+ // correct for border (left/right)
+ if(rDoc.MaxCol() == nX2 && !bLokRTL)
+ {
+ if(bLayoutRTL)
+ {
+ aDrawingRectPixel.SetLeft( 0 );
+ }
+ else
+ {
+ aDrawingRectPixel.SetRight( GetOutputSizePixel().getWidth() );
+ }
+ }
+
+ // correct for border (bottom)
+ if(rDoc.MaxRow() == nY2)
+ {
+ aDrawingRectPixel.SetBottom( GetOutputSizePixel().getHeight() );
+ }
+
+ // get logic positions
+ aDrawingRectLogic = PixelToLogic(aDrawingRectPixel, aDrawMode);
+ }
+
+ bool bInPlaceEditing = bEditMode && (mrViewData.GetRefTabNo() == mrViewData.GetTabNo());
+ vcl::Cursor* pInPlaceCrsr = nullptr;
+ bool bInPlaceVisCursor = false;
+ if (bInPlaceEditing)
+ {
+ // toggle the cursor off if it's on to ensure the cursor invert
+ // background logic remains valid after the background is cleared on
+ // the next cursor flash
+ pInPlaceCrsr = pEditView->GetCursor();
+ bInPlaceVisCursor = pInPlaceCrsr && pInPlaceCrsr->IsVisible();
+ if (bInPlaceVisCursor)
+ pInPlaceCrsr->Hide();
+ }
+
+ OutputDevice* pContentDev = &rDevice; // device for document content, used by overlay manager
+ SdrPaintWindow* pTargetPaintWindow = nullptr; // #i74769# work with SdrPaintWindow directly
+
+ {
+ // init redraw
+ ScTabViewShell* pTabViewShell = mrViewData.GetViewShell();
+
+ if(pTabViewShell)
+ {
+ MapMode aCurrentMapMode(pContentDev->GetMapMode());
+ pContentDev->SetMapMode(aDrawMode);
+ SdrView* pDrawView = pTabViewShell->GetScDrawView();
+
+ if(pDrawView)
+ {
+ // #i74769# Use new BeginDrawLayers() interface
+ vcl::Region aDrawingRegion(aDrawingRectLogic);
+ pTargetPaintWindow = pDrawView->BeginDrawLayers(pContentDev, aDrawingRegion);
+ OSL_ENSURE(pTargetPaintWindow, "BeginDrawLayers: Got no SdrPaintWindow (!)");
+
+ if (!bIsTiledRendering)
+ {
+ // #i74769# get target device from SdrPaintWindow, this may be the prerender
+ // device now, too.
+ pContentDev = &(pTargetPaintWindow->GetTargetOutputDevice());
+ aOutputData.SetContentDevice(pContentDev);
+ }
+ }
+
+ pContentDev->SetMapMode(aCurrentMapMode);
+ }
+ }
+
+ // app-background / document edge (area) (Pixel)
+ if ( !bIsTiledRendering && ( nX2 == rDoc.MaxCol() || nY2 == rDoc.MaxRow() ) )
+ {
+ // save MapMode and set to pixel
+ MapMode aCurrentMapMode(pContentDev->GetMapMode());
+ pContentDev->SetMapMode(MapMode(MapUnit::MapPixel));
+
+ tools::Rectangle aPixRect( Point(), GetOutputSizePixel() );
+ pContentDev->SetFillColor( rColorCfg.GetColorValue(svtools::APPBACKGROUND).nColor );
+ pContentDev->SetLineColor();
+ if ( nX2==rDoc.MaxCol() )
+ {
+ tools::Rectangle aDrawRect( aPixRect );
+ if ( bLayoutRTL )
+ aDrawRect.SetRight( nScrX - 1 );
+ else
+ aDrawRect.SetLeft( nScrX + aOutputData.GetScrW() );
+ if (aDrawRect.Right() >= aDrawRect.Left())
+ pContentDev->DrawRect( aDrawRect );
+ }
+ if ( nY2==rDoc.MaxRow() )
+ {
+ tools::Rectangle aDrawRect( aPixRect );
+ aDrawRect.SetTop( nScrY + aOutputData.GetScrH() );
+ if ( nX2==rDoc.MaxCol() )
+ {
+ // no double painting of the corner
+ if ( bLayoutRTL )
+ aDrawRect.SetLeft( nScrX );
+ else
+ aDrawRect.SetRight( nScrX + aOutputData.GetScrW() - 1 );
+ }
+ if (aDrawRect.Bottom() >= aDrawRect.Top())
+ pContentDev->DrawRect( aDrawRect );
+ }
+
+ // restore MapMode
+ pContentDev->SetMapMode(aCurrentMapMode);
+ }
+
+ if ( rDoc.HasBackgroundDraw( nTab, aDrawingRectLogic ) )
+ {
+ pContentDev->SetMapMode(MapMode(MapUnit::MapPixel));
+ aOutputData.DrawClear();
+
+ // drawing background
+
+ pContentDev->SetMapMode(aDrawMode);
+ DrawRedraw( aOutputData, SC_LAYER_BACK );
+ }
+ else
+ aOutputData.SetSolidBackground(!bNoBackgroundAndGrid);
+
+ aOutputData.DrawDocumentBackground();
+
+ if (bGridFirst && (bGrid || bPage))
+ {
+ // Draw lines in background color cover over lok client grid lines in merged cell areas if bNoBackgroundAndGrid is set.
+ if (bNoBackgroundAndGrid)
+ aOutputData.DrawGrid(*pContentDev, false /* bGrid */, false /* bPage */, true /* bMergeCover */);
+ else
+ aOutputData.DrawGrid(*pContentDev, bGrid, bPage);
+ }
+
+ aOutputData.DrawBackground(*pContentDev);
+
+ if (!bGridFirst && (bGrid || bPage) && !bNoBackgroundAndGrid)
+ aOutputData.DrawGrid(*pContentDev, bGrid, bPage);
+
+ pContentDev->SetMapMode(MapMode(MapUnit::MapPixel));
+
+ //tdf#128258 - draw a dotted line before hidden columns/rows
+ DrawHiddenIndicator(nX1,nY1,nX2,nY2, *pContentDev);
+
+ if ( bPageMode )
+ {
+ // DrawPagePreview draws complete lines/page numbers, must always be clipped
+ if ( aOutputData.SetChangedClip() )
+ {
+ DrawPagePreview(nX1,nY1,nX2,nY2, *pContentDev);
+ pContentDev->SetClipRegion();
+ }
+ }
+
+ aOutputData.DrawShadow();
+ aOutputData.DrawFrame(*pContentDev);
+
+ aOutputData.DrawSparklines(*pContentDev);
+
+ // Show Note Mark
+ if ( rOpts.GetOption( VOPT_NOTES ) )
+ aOutputData.DrawNoteMarks(*pContentDev);
+
+ if ( rOpts.GetOption( VOPT_FORMULAS_MARKS ) )
+ aOutputData.DrawFormulaMarks(*pContentDev);
+
+ if ( !bLogicText )
+ aOutputData.DrawStrings(); // in pixel MapMode
+
+ // edit cells and printer-metrics text must be before the buttons
+ // (DataPilot buttons contain labels in UI font)
+
+ pContentDev->SetMapMode(mrViewData.GetLogicMode(eWhich));
+ if ( bLogicText )
+ aOutputData.DrawStrings(true); // in logic MapMode if bLogicText is set
+ aOutputData.DrawEdit(true);
+
+ // the buttons are painted in absolute coordinates
+ if (bIsTiledRendering)
+ {
+ // Tiled offset nScrX, nScrY
+ MapMode aMap( MapUnit::MapPixel );
+ Point aOrigin = aOriginalMode.GetOrigin();
+ aOrigin.setX(o3tl::convert(aOrigin.getX(), o3tl::Length::twip, o3tl::Length::px) + nScrX);
+ aOrigin.setY(o3tl::convert(aOrigin.getY(), o3tl::Length::twip, o3tl::Length::px) + nScrY);
+ aMap.SetOrigin(aOrigin);
+ pContentDev->SetMapMode(aMap);
+ }
+ else
+ pContentDev->SetMapMode(MapMode(MapUnit::MapPixel));
+
+ // Autofilter- and Pivot-Buttons
+ DrawButtons(nX1, nX2, rTableInfo, pContentDev, pLokRTLCtxt.get()); // Pixel
+
+ pContentDev->SetMapMode(MapMode(MapUnit::MapPixel));
+
+ aOutputData.DrawClipMarks();
+
+ // In any case, Scenario / ChangeTracking must happen after DrawGrid, also for !bGridFirst
+
+ //! test if ChangeTrack display is active
+ //! Disable scenario frame via view option?
+
+ SCTAB nTabCount = rDoc.GetTableCount();
+ const std::vector<ScHighlightEntry> &rHigh = mrViewData.GetView()->GetHighlightRanges();
+ bool bHasScenario = ( nTab+1<nTabCount && rDoc.IsScenario(nTab+1) && !rDoc.IsScenario(nTab) );
+ bool bHasChange = ( rDoc.GetChangeTrack() != nullptr );
+
+ if ( bHasChange || bHasScenario || !rHigh.empty() )
+ {
+ //! Merge SetChangedClip() with DrawMarks() ?? (different MapMode!)
+
+ if ( bHasChange )
+ aOutputData.DrawChangeTrack();
+
+ if ( bHasScenario )
+ lcl_DrawScenarioFrames( pContentDev, mrViewData, eWhich, nX1,nY1,nX2,nY2 );
+
+ lcl_DrawHighlight( aOutputData, mrViewData, rHigh );
+ }
+
+ // Drawing foreground
+
+ pContentDev->SetMapMode(aDrawMode);
+
+ // Bitmaps and buttons are in absolute pixel coordinates.
+ const MapMode aOrig = pContentDev->GetMapMode();
+ if (bIsTiledRendering)
+ {
+ Point aOrigin = aOriginalMode.GetOrigin();
+ tools::Long nXOffset = bLayoutRTL ?
+ (-o3tl::convert(aOrigin.getX(), o3tl::Length::twip, o3tl::Length::px) + aOutputData.GetScrW()) :
+ o3tl::convert(aOrigin.getX(), o3tl::Length::twip, o3tl::Length::px);
+ Size aPixelOffset(nXOffset, o3tl::convert(aOrigin.getY(), o3tl::Length::twip, o3tl::Length::px));
+ pContentDev->SetPixelOffset(aPixelOffset);
+ comphelper::LibreOfficeKit::setLocalRendering();
+ }
+
+ DrawRedraw( aOutputData, SC_LAYER_FRONT );
+ DrawRedraw( aOutputData, SC_LAYER_INTERN );
+ DrawSdrGrid( aDrawingRectLogic, pContentDev );
+
+ if (bIsTiledRendering)
+ {
+ pContentDev->SetPixelOffset(Size());
+ pContentDev->SetMapMode(aOrig);
+ }
+
+ pContentDev->SetMapMode(MapMode(MapUnit::MapPixel));
+
+ if ( mrViewData.IsRefMode() && nTab >= mrViewData.GetRefStartZ() && nTab <= mrViewData.GetRefEndZ() )
+ {
+ Color aRefColor( rColorCfg.GetColorValue(svtools::CALCREFERENCE).nColor );
+ aOutputData.DrawRefMark( mrViewData.GetRefStartX(), mrViewData.GetRefStartY(),
+ mrViewData.GetRefEndX(), mrViewData.GetRefEndY(),
+ aRefColor, false );
+ }
+
+ // range finder
+
+ ScInputHandler* pHdl = pScMod->GetInputHdl( mrViewData.GetViewShell() );
+ if (pHdl)
+ {
+ ScDocShell* pDocSh = mrViewData.GetDocShell();
+ ScRangeFindList* pRangeFinder = pHdl->GetRangeFindList();
+ if ( pRangeFinder && !pRangeFinder->IsHidden() &&
+ pRangeFinder->GetDocName() == pDocSh->GetTitle() )
+ {
+ sal_uInt16 nCount = static_cast<sal_uInt16>(pRangeFinder->Count());
+ for (sal_uInt16 i=0; i<nCount; i++)
+ {
+ ScRangeFindData& rData = pRangeFinder->GetObject(i);
+
+ ScRange aRef = rData.aRef;
+ aRef.PutInOrder();
+ if ( aRef.aStart.Tab() >= nTab && aRef.aEnd.Tab() <= nTab )
+ aOutputData.DrawRefMark( aRef.aStart.Col(), aRef.aStart.Row(),
+ aRef.aEnd.Col(), aRef.aEnd.Row(),
+ rData.nColor,
+ true );
+ }
+ }
+ }
+
+ {
+ // end redraw
+ ScTabViewShell* pTabViewShell = mrViewData.GetViewShell();
+
+ if(pTabViewShell)
+ {
+ MapMode aCurrentMapMode(pContentDev->GetMapMode());
+ pContentDev->SetMapMode(aDrawMode);
+
+ if (bIsTiledRendering)
+ {
+ Point aOrigin = aOriginalMode.GetOrigin();
+ if (bLayoutRTL)
+ aOrigin.setX(-o3tl::convert(aOrigin.getX(), o3tl::Length::twip, o3tl::Length::px)
+ + aOutputData.nScrX + aOutputData.GetScrW());
+ else
+ aOrigin.setX(o3tl::convert(aOrigin.getX(), o3tl::Length::twip, o3tl::Length::px)
+ + aOutputData.nScrX);
+
+ aOrigin.setY(o3tl::convert(aOrigin.getY(), o3tl::Length::twip, o3tl::Length::px)
+ + aOutputData.nScrY);
+ const double twipFactor = 15 * 1.76388889; // 26.45833335
+ aOrigin = Point(aOrigin.getX() * twipFactor,
+ aOrigin.getY() * twipFactor);
+ MapMode aNew = rDevice.GetMapMode();
+ aNew.SetOrigin(aOrigin);
+ rDevice.SetMapMode(aNew);
+ }
+
+ SdrView* pDrawView = pTabViewShell->GetScDrawView();
+
+ if(pDrawView)
+ {
+ // #i74769# work with SdrPaintWindow directly
+ pDrawView->EndDrawLayers(*pTargetPaintWindow, true);
+ }
+
+ pContentDev->SetMapMode(aCurrentMapMode);
+ }
+ }
+
+ // paint in-place editing
+ if (bIsTiledRendering)
+ {
+ ScTabViewShell* pThisViewShell = mrViewData.GetViewShell();
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+
+ while (pViewShell)
+ {
+ bool bEnterLoop = bIsTiledRendering || pViewShell != pThisViewShell;
+ if (bEnterLoop && pViewShell->GetDocId() == pThisViewShell->GetDocId())
+ {
+ ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
+ if (pTabViewShell)
+ {
+ ScViewData& rOtherViewData = pTabViewShell->GetViewData();
+ ScSplitPos eOtherWhich = rOtherViewData.GetEditActivePart();
+
+ bool bOtherEditMode = rOtherViewData.HasEditView(eOtherWhich);
+ SCCOL nCol1 = rOtherViewData.GetEditStartCol();
+ SCROW nRow1 = rOtherViewData.GetEditStartRow();
+ SCCOL nCol2 = rOtherViewData.GetEditEndCol();
+ SCROW nRow2 = rOtherViewData.GetEditEndRow();
+ bOtherEditMode = bOtherEditMode
+ && ( nCol2 >= nX1 && nCol1 <= nX2 && nRow2 >= nY1 && nRow1 <= nY2 );
+ if (bOtherEditMode && rOtherViewData.GetRefTabNo() == nTab)
+ {
+ EditView* pOtherEditView = rOtherViewData.GetEditView(eOtherWhich);
+ if (pOtherEditView)
+ {
+ tools::Long nScreenX = aOutputData.nScrX;
+ tools::Long nScreenY = aOutputData.nScrY;
+
+ rDevice.SetLineColor();
+ SfxViewShell* pSfxViewShell = SfxViewShell::Current();
+ ScTabViewShell* pCurrentViewShell = dynamic_cast<ScTabViewShell*>(pSfxViewShell);
+ if (pCurrentViewShell)
+ {
+ const ScViewData& pViewData = pCurrentViewShell->GetViewData();
+ const ScViewOptions& aViewOptions = pViewData.GetOptions();
+ const ScPatternAttr* pPattern = rDoc.GetPattern( nCol1, nRow1, nTab );
+ Color aCellColor = pPattern->GetItem(ATTR_BACKGROUND).GetColor();
+ if (aCellColor.IsTransparent())
+ {
+ aCellColor = aViewOptions.GetDocColor();
+ }
+ rDevice.SetFillColor(aCellColor);
+ pOtherEditView->SetBackgroundColor(aCellColor);
+ }
+ Point aStart = mrViewData.GetScrPos( nCol1, nRow1, eOtherWhich );
+ Point aEnd = mrViewData.GetScrPos( nCol2+1, nRow2+1, eOtherWhich );
+
+ if (bIsTiledRendering)
+ {
+ EditEngine* pEditEngine = pOtherEditView->GetEditEngine();
+ if (pEditEngine)
+ aEnd.AdjustY(lcl_GetMultiLineHeight(pEditEngine));
+ }
+
+ if (bLokRTL)
+ {
+ // Transform the cell range X coordinates such that the edit cell area is
+ // horizontally mirrored w.r.t the (combined-)tile.
+ aStart.setX(pLokRTLCtxt->docToTilePos(aStart.X()));
+ aEnd.setX(pLokRTLCtxt->docToTilePos(aEnd.X()));
+ }
+
+ // don't overwrite grid
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+ aEnd.AdjustX( -(2 * nLayoutSign) );
+ aEnd.AdjustY( -2 );
+
+ tools::Rectangle aBackground(aStart, aEnd);
+ if (bLokRTL)
+ aBackground.Normalize();
+
+ // Need to draw the background in absolute coords.
+ Point aOrigin = aOriginalMode.GetOrigin();
+ aOrigin.setX(
+ o3tl::convert(aOrigin.getX(), o3tl::Length::twip, o3tl::Length::px)
+ + nScreenX);
+ aOrigin.setY(
+ o3tl::convert(aOrigin.getY(), o3tl::Length::twip, o3tl::Length::px)
+ + nScreenY);
+ aBackground += aOrigin;
+ rDevice.SetMapMode(aDrawMode);
+
+ static const double twipFactor = 15 * 1.76388889; // 26.45833335
+ // keep into account the zoom factor
+ aOrigin = Point((aOrigin.getX() * twipFactor) / static_cast<double>(aDrawMode.GetScaleX()),
+ (aOrigin.getY() * twipFactor) / static_cast<double>(aDrawMode.GetScaleY()));
+
+ MapMode aNew = rDevice.GetMapMode();
+ aNew.SetOrigin(aOrigin);
+ rDevice.SetMapMode(aNew);
+
+ // paint the background
+ rDevice.DrawRect(rDevice.PixelToLogic(aBackground));
+ tools::Rectangle aBGAbs(aBackground);
+
+ tools::Rectangle aEditRect(aBackground);
+ tools::Long nOffsetX = 0, nOffsetY = 0;
+ // Get top-left offset because of margin and indent.
+ lcl_GetEditAreaTLOffset(nOffsetX, nOffsetY, ScAddress(nCol1, nRow1, nTab), mrViewData, rDoc);
+ aEditRect.AdjustLeft(nOffsetX + 1);
+ aEditRect.AdjustRight(1);
+ aEditRect.AdjustTop(nOffsetY + 1);
+ aEditRect.AdjustBottom(1);
+
+ // EditView has an 'output area' which is used to clip the 'paint area' we provide below.
+ // So they need to be in the same coordinates/units. This is tied to the mapmode of the gridwin
+ // attached to the EditView, so we have to change its mapmode too (temporarily). We save the
+ // original mapmode and 'output area' and roll them back when we finish painting to rDevice.
+ OutputDevice& rOtherWin = pOtherEditView->GetOutputDevice();
+ const tools::Rectangle aOrigOutputArea(pOtherEditView->GetOutputArea()); // Not in pixels.
+ const MapMode aOrigMapMode = rOtherWin.GetMapMode();
+ rOtherWin.SetMapMode(rDevice.GetMapMode());
+
+ // Avoid sending wrong cursor/selection messages by the 'other' view, as the output-area is going
+ // to be tweaked temporarily to match the current view's zoom.
+ SuppressEditViewMessagesGuard aGuard(*pOtherEditView);
+ comphelper::ScopeGuard aOutputGuard(
+ [pOtherEditView, aOrigOutputArea, bLokRTL] {
+ if (bLokRTL && aOrigOutputArea != pOtherEditView->GetOutputArea())
+ pOtherEditView->SetOutputArea(aOrigOutputArea);
+ });
+
+ aEditRect = rDevice.PixelToLogic(aEditRect);
+ if (bIsTiledRendering)
+ pOtherEditView->SetOutputArea(aEditRect);
+ else
+ aEditRect.Intersection(pOtherEditView->GetOutputArea());
+ pOtherEditView->Paint(aEditRect, &rDevice);
+
+ // EditView will do the cursor notifications correctly if we're in
+ // print-twips messaging mode.
+ if (bIsTiledRendering && !comphelper::LibreOfficeKit::isCompatFlagSet(
+ comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
+ {
+ // Now we need to get relative cursor position within the editview.
+ // This is for sending the pixel-aligned twips position of the cursor to the specific views with
+ // the same given zoom level.
+ tools::Rectangle aCursorRect = pOtherEditView->GetEditCursor();
+ Point aCursPos = OutputDevice::LogicToLogic(aCursorRect.TopLeft(),
+ MapMode(MapUnit::Map100thMM), MapMode(MapUnit::MapTwip));
+
+ const MapMode& rDevMM = rDevice.GetMapMode();
+ MapMode aMM(MapUnit::MapTwip);
+ aMM.SetScaleX(rDevMM.GetScaleX());
+ aMM.SetScaleY(rDevMM.GetScaleY());
+
+ aBGAbs.AdjustLeft(1);
+ aBGAbs.AdjustTop(1);
+ aCursorRect = GetOutDev()->PixelToLogic(aBGAbs, aMM);
+ aCursorRect.setWidth(0);
+ aCursorRect.Move(aCursPos.getX(), 0);
+ // Sends view cursor position to views of all matching zooms if needed (avoids duplicates).
+ InvalidateLOKViewCursor(aCursorRect, aMM.GetScaleX(), aMM.GetScaleY());
+ }
+
+ // Rollback the mapmode and 'output area'.
+ rOtherWin.SetMapMode(aOrigMapMode);
+ if (!bIsTiledRendering)
+ pOtherEditView->SetOutputArea(aOrigOutputArea);
+ rDevice.SetMapMode(MapMode(MapUnit::MapPixel));
+ }
+ }
+ }
+ }
+
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+
+ }
+
+ // In-place editing - when the user is typing, we need to paint the text
+ // using the editeng.
+ // It's being done after EndDrawLayers() to get it outside the overlay
+ // buffer and on top of everything.
+ if (bInPlaceEditing)
+ {
+ // get the coordinates of the area we need to clear (overpaint by
+ // the background)
+ SCCOL nCol1 = mrViewData.GetEditStartCol();
+ SCROW nRow1 = mrViewData.GetEditStartRow();
+ SCCOL nCol2 = mrViewData.GetEditEndCol();
+ SCROW nRow2 = mrViewData.GetEditEndRow();
+ rDevice.SetLineColor();
+ rDevice.SetFillColor(pEditView->GetBackgroundColor());
+ Point aStart = mrViewData.GetScrPos( nCol1, nRow1, eWhich );
+ Point aEnd = mrViewData.GetScrPos( nCol2+1, nRow2+1, eWhich );
+
+ if (bLokRTL)
+ {
+ // Transform the cell range X coordinates such that the edit cell area is
+ // horizontally mirrored w.r.t the (combined-)tile.
+ aStart.setX(pLokRTLCtxt->docToTilePos(aStart.X()));
+ aEnd.setX(pLokRTLCtxt->docToTilePos(aEnd.X()));
+ }
+
+ // don't overwrite grid
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+ aEnd.AdjustX( -(2 * nLayoutSign) );
+ aEnd.AdjustY( -2 );
+
+ // set the correct mapmode
+ tools::Rectangle aBackground(aStart, aEnd);
+ if (bLokRTL)
+ aBackground.Normalize();
+ tools::Rectangle aBGAbs(aBackground);
+
+ if (bIsTiledRendering)
+ {
+ // Need to draw the background in absolute coords.
+ Point aOrigin = aOriginalMode.GetOrigin();
+ aOrigin.setX(o3tl::convert(aOrigin.getX(), o3tl::Length::twip, o3tl::Length::px)
+ + nScrX);
+ aOrigin.setY(o3tl::convert(aOrigin.getY(), o3tl::Length::twip, o3tl::Length::px)
+ + nScrY);
+ aBackground += aOrigin;
+ rDevice.SetMapMode(aDrawMode);
+ }
+ else
+ rDevice.SetMapMode(mrViewData.GetLogicMode());
+
+ if (bIsTiledRendering)
+ {
+ Point aOrigin = aOriginalMode.GetOrigin();
+ aOrigin.setX(o3tl::convert(aOrigin.getX(), o3tl::Length::twip, o3tl::Length::px)
+ + nScrX);
+ aOrigin.setY(o3tl::convert(aOrigin.getY(), o3tl::Length::twip, o3tl::Length::px)
+ + nScrY);
+ static const double twipFactor = 15 * 1.76388889; // 26.45833335
+ // keep into account the zoom factor
+ aOrigin = Point((aOrigin.getX() * twipFactor) / static_cast<double>(aDrawMode.GetScaleX()),
+ (aOrigin.getY() * twipFactor) / static_cast<double>(aDrawMode.GetScaleY()));
+ MapMode aNew = rDevice.GetMapMode();
+ aNew.SetOrigin(aOrigin);
+ rDevice.SetMapMode(aNew);
+ }
+
+ // paint the editeng text
+ if (bIsTiledRendering)
+ {
+ // EditView has an 'output area' which is used to clip the paint area we provide below.
+ // So they need to be in the same coordinates/units. This is tied to the mapmode of the gridwin
+ // attached to the EditView, so we have to change its mapmode too (temporarily). We save the
+ // original mapmode and 'output area' and roll them back when we finish painting to rDevice.
+ const MapMode aOrigMapMode = GetMapMode();
+ SetMapMode(rDevice.GetMapMode());
+
+ // Avoid sending wrong cursor/selection messages by the current view, as the output-area is going
+ // to be tweaked temporarily to match other view's zoom. (This does not affect the manual
+ // cursor-messaging done in the non print-twips mode)
+ SuppressEditViewMessagesGuard aGuard(*pEditView);
+
+ // EditView will do the cursor notifications correctly if we're in
+ // print-twips messaging mode.
+ if (!comphelper::LibreOfficeKit::isCompatFlagSet(
+ comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
+ {
+ // Now we need to get relative cursor position within the editview.
+ // This is for sending the pixel-aligned twips position of the cursor to the specific views with
+ // the same given zoom level.
+ tools::Rectangle aCursorRect = pEditView->GetEditCursor();
+ Point aCursPos = o3tl::toTwips(aCursorRect.TopLeft(), o3tl::Length::mm100);
+
+ const MapMode& rDevMM = rDevice.GetMapMode();
+ MapMode aMM(MapUnit::MapTwip);
+ aMM.SetScaleX(rDevMM.GetScaleX());
+ aMM.SetScaleY(rDevMM.GetScaleY());
+
+ aBGAbs.AdjustLeft(1);
+ aBGAbs.AdjustTop(1);
+ aCursorRect = GetOutDev()->PixelToLogic(aBGAbs, aMM);
+ aCursorRect.setWidth(0);
+ aCursorRect.Move(aCursPos.getX(), 0);
+ // Sends view cursor position to views of all matching zooms if needed (avoids duplicates).
+ InvalidateLOKViewCursor(aCursorRect, aMM.GetScaleX(), aMM.GetScaleY());
+ }
+
+ // Rollback the mapmode and 'output area'.
+ SetMapMode(aOrigMapMode);
+ }
+ else
+ {
+ // paint the background
+ tools::Rectangle aLogicRect(rDevice.PixelToLogic(aBackground));
+ //tdf#100925, rhbz#1283420, Draw some text here, to get
+ //X11CairoTextRender::getCairoContext called, so that the forced read
+ //from the underlying X Drawable gets it to sync.
+ rDevice.DrawText(aLogicRect.BottomLeft(), " ");
+ rDevice.DrawRect(aLogicRect);
+
+ tools::Rectangle aEditRect(Point(nScrX, nScrY), Size(aOutputData.GetScrW(), aOutputData.GetScrH()));
+ pEditView->Paint(rDevice.PixelToLogic(aEditRect), &rDevice);
+ }
+
+ rDevice.SetMapMode(MapMode(MapUnit::MapPixel));
+
+ // restore the cursor it was originally visible
+ if (bInPlaceVisCursor)
+ pInPlaceCrsr->Show();
+ }
+
+ if (mrViewData.HasEditView(eWhich))
+ {
+ // flush OverlayManager before changing the MapMode
+ flushOverlayManager();
+
+ // set MapMode for text edit
+ rDevice.SetMapMode(mrViewData.GetLogicMode());
+ }
+ else
+ rDevice.SetMapMode(aDrawMode);
+
+ if (mpNoteMarker)
+ mpNoteMarker->Draw(); // Above the cursor, in drawing map mode
+
+ if (bPage && bInitialPageBreaks)
+ SetupInitialPageBreaks(rDoc, nTab);
+}
+
+
+void ScGridWindow::SetupInitialPageBreaks(const ScDocument& rDoc, SCTAB nTab)
+{
+ // tdf#124983, if option LibreOfficeDev Calc/View/Visual Aids/Page breaks
+ // is enabled, breaks should be visible. If the document is opened the first
+ // time, the breaks are not calculated yet, so for this initialization
+ // a timer will be triggered here.
+ std::set<SCCOL> aColBreaks;
+ std::set<SCROW> aRowBreaks;
+ rDoc.GetAllColBreaks(aColBreaks, nTab, true, false);
+ rDoc.GetAllRowBreaks(aRowBreaks, nTab, true, false);
+ if (aColBreaks.size() == 0 || aRowBreaks.size() == 0)
+ {
+ maShowPageBreaksTimer.SetPriority(TaskPriority::DEFAULT_IDLE);
+ maShowPageBreaksTimer.Start();
+ }
+ bInitialPageBreaks = false;
+}
+
+namespace
+{
+ template<typename IndexType>
+ void lcl_getBoundingRowColumnforTile(ScViewData& rViewData,
+ tools::Long nTileStartPosPx, tools::Long nTileEndPosPx,
+ sal_Int32& nTopLeftTileOffset, sal_Int32& nTopLeftTileOrigin,
+ sal_Int32& nTopLeftTileIndex, sal_Int32& nBottomRightTileIndex)
+ {
+ const bool bColumnHeader = std::is_same<IndexType, SCCOL>::value;
+
+ SCTAB nTab = rViewData.GetTabNo();
+
+ IndexType nStartIndex = -1;
+ IndexType nEndIndex = -1;
+ tools::Long nStartPosPx = 0;
+ tools::Long nEndPosPx = 0;
+
+ ScPositionHelper& rPositionHelper =
+ bColumnHeader ? rViewData.GetLOKWidthHelper() : rViewData.GetLOKHeightHelper();
+ const auto& rStartNearest = rPositionHelper.getNearestByPosition(nTileStartPosPx);
+ const auto& rEndNearest = rPositionHelper.getNearestByPosition(nTileEndPosPx);
+
+ ScBoundsProvider aBoundsProvider(rViewData, nTab, bColumnHeader);
+ aBoundsProvider.Compute(rStartNearest, rEndNearest, nTileStartPosPx, nTileEndPosPx);
+ aBoundsProvider.GetStartIndexAndPosition(nStartIndex, nStartPosPx); ++nStartIndex;
+ aBoundsProvider.GetEndIndexAndPosition(nEndIndex, nEndPosPx);
+
+ nTopLeftTileOffset = nTileStartPosPx - nStartPosPx;
+ nTopLeftTileOrigin = nStartPosPx;
+ nTopLeftTileIndex = nStartIndex;
+ nBottomRightTileIndex = nEndIndex;
+ }
+
+ void lcl_RTLAdjustTileColOffset(ScViewData& rViewData, sal_Int32& nTileColOffset,
+ tools::Long nTileEndPx, sal_Int32 nEndCol, SCTAB nTab,
+ const ScDocument& rDoc, double fPPTX)
+ {
+ auto GetColWidthPx = [&rDoc, nTab, fPPTX](SCCOL nCol) {
+ const sal_uInt16 nSize = rDoc.GetColWidth(nCol, nTab);
+ const tools::Long nSizePx = ScViewData::ToPixel(nSize, fPPTX);
+ return nSizePx;
+ };
+
+ ScPositionHelper rHelper = rViewData.GetLOKWidthHelper();
+ tools::Long nEndColPos = rHelper.computePosition(nEndCol, GetColWidthPx);
+
+ nTileColOffset += (nEndColPos - nTileEndPx - nTileColOffset);
+ }
+
+ class ScLOKProxyObjectContact final : public sdr::contact::ObjectContactOfPageView
+ {
+ private:
+ ScDrawView* mpScDrawView;
+
+ public:
+ explicit ScLOKProxyObjectContact(
+ ScDrawView* pDrawView,
+ SdrPageWindow& rPageWindow,
+ const char* pDebugName) :
+ ObjectContactOfPageView(rPageWindow, pDebugName),
+ mpScDrawView(pDrawView)
+ {
+ }
+
+ virtual bool supportsGridOffsets() const override { return true; }
+
+ virtual void calculateGridOffsetForViewObjectContact(
+ basegfx::B2DVector& rTarget,
+ const sdr::contact::ViewObjectContact& rClient) const override
+ {
+ if (!mpScDrawView)
+ return;
+
+ SdrPageView* pPageView(mpScDrawView->GetSdrPageView());
+ if (!pPageView)
+ return;
+
+ SdrPageWindow* pSdrPageWindow = nullptr;
+ if (pPageView->PageWindowCount() > 0)
+ pSdrPageWindow = pPageView->GetPageWindow(0);
+ if (!pSdrPageWindow)
+ return;
+
+ sdr::contact::ObjectContact& rObjContact(pSdrPageWindow->GetObjectContact());
+
+ SdrObject* pTargetSdrObject(rClient.GetViewContact().TryToGetSdrObject());
+ if (pTargetSdrObject)
+ rTarget = pTargetSdrObject->GetViewContact().GetViewObjectContact(rObjContact).getGridOffset();
+ }
+ };
+
+ class ScLOKDrawView : public FmFormView
+ {
+ public:
+ ScLOKDrawView(OutputDevice* pOut, ScViewData& rData) :
+ FmFormView(*rData.GetDocument().GetDrawLayer(), pOut),
+ mpScDrawView(rData.GetScDrawView())
+ {
+ }
+
+ virtual sdr::contact::ObjectContact* createViewSpecificObjectContact(
+ SdrPageWindow& rPageWindow, const char* pDebugName) const override
+ {
+ if (!mpScDrawView)
+ return SdrView::createViewSpecificObjectContact(rPageWindow, pDebugName);
+
+ return new ScLOKProxyObjectContact(mpScDrawView, rPageWindow, pDebugName);
+ }
+
+ private:
+ ScDrawView* mpScDrawView;
+ };
+} // anonymous namespace
+
+void ScGridWindow::PaintTile( VirtualDevice& rDevice,
+ int nOutputWidth, int nOutputHeight,
+ int nTilePosX, int nTilePosY,
+ tools::Long nTileWidth, tools::Long nTileHeight,
+ SCCOL nTiledRenderingAreaEndCol, SCROW nTiledRenderingAreaEndRow )
+{
+ Fraction origZoomX = mrViewData.GetZoomX();
+ Fraction origZoomY = mrViewData.GetZoomY();
+
+ // Output size is in pixels while tile position and size are in logical units (twips).
+
+ // Assumption: always paint the whole sheet i.e. "visible" range is always
+ // from (0,0) to last data position.
+
+ // Tile geometry is independent of the zoom level, but the output size is
+ // dependent of the zoom level. Determine the correct zoom level before
+ // we start.
+
+ // FIXME the painting works using a mixture of drawing with coordinates in
+ // pixels and in logic coordinates; it should be cleaned up to use logic
+ // coords only, and avoid all the SetMapMode()'s.
+ // Similarly to Writer, we should set the mapmode once on the rDevice, and
+ // not care about any zoom settings.
+
+ Fraction aFracX(o3tl::convert(nOutputWidth, o3tl::Length::px, o3tl::Length::twip), nTileWidth);
+ Fraction aFracY(o3tl::convert(nOutputHeight, o3tl::Length::px, o3tl::Length::twip), nTileHeight);
+
+ const bool bChangeZoom = (aFracX != origZoomX || aFracY != origZoomY);
+
+ // page break zoom, and aLogicMode in ScViewData
+ // FIXME: there are issues when SetZoom is called conditionally.
+ mrViewData.SetZoom(aFracX, aFracY, true);
+ if (bChangeZoom)
+ {
+ if (ScDrawView* pDrawView = mrViewData.GetScDrawView())
+ pDrawView->resetGridOffsetsForAllSdrPageViews();
+ }
+
+ const double fTilePosXPixel = static_cast<double>(nTilePosX) * nOutputWidth / nTileWidth;
+ const double fTilePosYPixel = static_cast<double>(nTilePosY) * nOutputHeight / nTileHeight;
+ const double fTileBottomPixel = static_cast<double>(nTilePosY + nTileHeight) * nOutputHeight / nTileHeight;
+ const double fTileRightPixel = static_cast<double>(nTilePosX + nTileWidth) * nOutputWidth / nTileWidth;
+
+ SCTAB nTab = mrViewData.GetTabNo();
+ ScDocument& rDoc = mrViewData.GetDocument();
+
+ const double fPPTX = mrViewData.GetPPTX();
+ const double fPPTY = mrViewData.GetPPTY();
+
+ // find approximate col/row offsets of nearby.
+ sal_Int32 nTopLeftTileRowOffset = 0;
+ sal_Int32 nTopLeftTileColOffset = 0;
+ sal_Int32 nTopLeftTileRowOrigin = 0;
+ sal_Int32 nTopLeftTileColOrigin = 0;
+
+ sal_Int32 nTopLeftTileRow = 0;
+ sal_Int32 nTopLeftTileCol = 0;
+ sal_Int32 nBottomRightTileRow = 0;
+ sal_Int32 nBottomRightTileCol = 0;
+
+ lcl_getBoundingRowColumnforTile<SCROW>(mrViewData,
+ fTilePosYPixel, fTileBottomPixel,
+ nTopLeftTileRowOffset, nTopLeftTileRowOrigin,
+ nTopLeftTileRow, nBottomRightTileRow);
+
+ lcl_getBoundingRowColumnforTile<SCCOL>(mrViewData,
+ fTilePosXPixel, fTileRightPixel,
+ nTopLeftTileColOffset, nTopLeftTileColOrigin,
+ nTopLeftTileCol, nBottomRightTileCol);
+
+ // Enlarge
+ nBottomRightTileCol++;
+ nBottomRightTileRow++;
+
+ if (nBottomRightTileCol > rDoc.MaxCol())
+ nBottomRightTileCol = rDoc.MaxCol();
+
+ if (nBottomRightTileRow > MAXTILEDROW)
+ nBottomRightTileRow = MAXTILEDROW;
+
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+
+ if (bLayoutRTL)
+ {
+ lcl_RTLAdjustTileColOffset(mrViewData, nTopLeftTileColOffset,
+ fTileRightPixel, nBottomRightTileCol, nTab,
+ rDoc, fPPTX);
+ }
+
+ // size of the document including drawings, charts, etc.
+ SCCOL nEndCol = nTiledRenderingAreaEndCol;
+ SCROW nEndRow = nTiledRenderingAreaEndRow;
+
+ if (nEndCol < nBottomRightTileCol)
+ nEndCol = nBottomRightTileCol;
+
+ if (nEndRow < nBottomRightTileRow)
+ nEndRow = nBottomRightTileRow;
+
+ nTopLeftTileCol = std::max<sal_Int32>(nTopLeftTileCol, 0);
+ nTopLeftTileRow = std::max<sal_Int32>(nTopLeftTileRow, 0);
+ nTopLeftTileColOrigin = o3tl::convert(nTopLeftTileColOrigin, o3tl::Length::px, o3tl::Length::twip);
+ nTopLeftTileRowOrigin = o3tl::convert(nTopLeftTileRowOrigin, o3tl::Length::px, o3tl::Length::twip);
+
+ // Checkout -> 'rDoc.ExtendMerge' ... if we miss merged cells.
+
+ // Origin must be the offset of the first col and row
+ // containing our top-left pixel.
+ const MapMode aOriginalMode = rDevice.GetMapMode();
+ MapMode aAbsMode = aOriginalMode;
+ const Point aOrigin(-nTopLeftTileColOrigin, -nTopLeftTileRowOrigin);
+ aAbsMode.SetOrigin(aOrigin);
+ rDevice.SetMapMode(aAbsMode);
+
+ ScTableInfo aTabInfo(nEndRow + 3);
+ rDoc.FillInfo(aTabInfo, nTopLeftTileCol, nTopLeftTileRow,
+ nBottomRightTileCol, nBottomRightTileRow,
+ nTab, fPPTX, fPPTY, false, false);
+
+// FIXME: is this called some
+// Point aScrPos = mrViewData.GetScrPos( nX1, nY1, eWhich );
+
+ ScOutputData aOutputData(&rDevice, OUTTYPE_WINDOW, aTabInfo, &rDoc, nTab,
+ -nTopLeftTileColOffset,
+ -nTopLeftTileRowOffset,
+ nTopLeftTileCol, nTopLeftTileRow,
+ nBottomRightTileCol, nBottomRightTileRow,
+ fPPTX, fPPTY, nullptr, nullptr);
+
+ // setup the SdrPage so that drawinglayer works correctly
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ if (pModel)
+ {
+ bool bPrintTwipsMsgs = comphelper::LibreOfficeKit::isCompatFlagSet(
+ comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs);
+ if (!mpLOKDrawView)
+ {
+ mpLOKDrawView.reset(bPrintTwipsMsgs ?
+ new ScLOKDrawView(
+ &rDevice,
+ mrViewData) :
+ new FmFormView(
+ *pModel,
+ &rDevice));
+ }
+
+ mpLOKDrawView->SetNegativeX(bLayoutRTL);
+ mpLOKDrawView->ShowSdrPage(mpLOKDrawView->GetModel().GetPage(nTab));
+ aOutputData.SetDrawView(mpLOKDrawView.get());
+ aOutputData.SetSpellCheckContext(mpSpellCheckCxt.get());
+ }
+
+ // draw the content
+ DrawContent(rDevice, aTabInfo, aOutputData, true);
+ rDevice.SetMapMode(aOriginalMode);
+
+ // Paint the chart(s) in edit mode.
+ LokChartHelper::PaintAllChartsOnTile(rDevice, nOutputWidth, nOutputHeight,
+ nTilePosX, nTilePosY, nTileWidth, nTileHeight, bLayoutRTL);
+
+ rDevice.SetMapMode(aOriginalMode);
+
+ // Flag drawn formula cells "unchanged".
+ rDoc.ResetChanged(ScRange(nTopLeftTileCol, nTopLeftTileRow, nTab, nBottomRightTileCol, nBottomRightTileRow, nTab));
+ rDoc.PrepareFormulaCalc();
+
+ mrViewData.SetZoom(origZoomX, origZoomY, true);
+ if (bChangeZoom)
+ {
+ if (ScDrawView* pDrawView = mrViewData.GetScDrawView())
+ pDrawView->resetGridOffsetsForAllSdrPageViews();
+ }
+
+ if (bLayoutRTL)
+ {
+ Bitmap aCellBMP = rDevice.GetBitmap(Point(0, 0), Size(nOutputWidth, nOutputHeight));
+ aCellBMP.Mirror(BmpMirrorFlags::Horizontal);
+ rDevice.DrawBitmap(Point(0, 0), Size(nOutputWidth, nOutputHeight), aCellBMP);
+ }
+}
+
+void ScGridWindow::LogicInvalidatePart(const tools::Rectangle* pRectangle, int nPart)
+{
+ tools::Rectangle aRectangle;
+ tools::Rectangle* pResultRectangle;
+ if (!pRectangle)
+ pResultRectangle = nullptr;
+ else
+ {
+ aRectangle = *pRectangle;
+ // When dragging shapes the map mode is disabled.
+ if (IsMapModeEnabled())
+ {
+ if (GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
+ {
+ aRectangle = o3tl::convert(aRectangle, o3tl::Length::mm100, o3tl::Length::twip);
+ }
+ }
+ else
+ aRectangle = PixelToLogic(aRectangle, MapMode(MapUnit::MapTwip));
+ pResultRectangle = &aRectangle;
+ }
+
+ // Trim invalidation rectangle overlapping negative X region in RTL mode.
+ if (pResultRectangle && pResultRectangle->Left() < 0
+ && mrViewData.GetDocument().IsLayoutRTL(mrViewData.GetTabNo()))
+ {
+ pResultRectangle->SetLeft(0);
+ if (pResultRectangle->Right() < 0)
+ pResultRectangle->SetRight(0);
+ }
+
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+ SfxLokHelper::notifyInvalidation(pViewShell, nPart, pResultRectangle);
+}
+
+void ScGridWindow::LogicInvalidate(const tools::Rectangle* pRectangle)
+{
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+ LogicInvalidatePart(pRectangle, pViewShell->getPart());
+}
+
+void ScGridWindow::SetCellSelectionPixel(int nType, int nPixelX, int nPixelY)
+{
+ ScTabView* pTabView = mrViewData.GetView();
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+ ScInputHandler* pInputHandler = SC_MOD()->GetInputHdl(pViewShell);
+
+ if (pInputHandler && pInputHandler->IsInputMode())
+ {
+ // we need to switch off the editeng
+ ScTabView::UpdateInputLine();
+ pViewShell->UpdateInputHandler();
+ }
+
+ if (nType == LOK_SETTEXTSELECTION_RESET)
+ {
+ pTabView->DoneBlockMode();
+ return;
+ }
+
+ // obtain the current selection
+ ScRangeList aRangeList = mrViewData.GetMarkData().GetMarkedRanges();
+
+ SCCOL nCol1, nCol2;
+ SCROW nRow1, nRow2;
+ SCTAB nTab1, nTab2;
+
+ bool bWasEmpty = false;
+ if (aRangeList.empty())
+ {
+ nCol1 = nCol2 = mrViewData.GetCurX();
+ nRow1 = nRow2 = mrViewData.GetCurY();
+ bWasEmpty = true;
+ }
+ else
+ aRangeList.Combine().GetVars(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+
+ // convert the coordinates to column/row
+ SCCOL nNewPosX;
+ SCROW nNewPosY;
+ SCTAB nTab = mrViewData.GetTabNo();
+ mrViewData.GetPosFromPixel(nPixelX, nPixelY, eWhich, nNewPosX, nNewPosY);
+
+ // change the selection
+ switch (nType)
+ {
+ case LOK_SETTEXTSELECTION_START:
+ if (nNewPosX != nCol1 || nNewPosY != nRow1 || bWasEmpty)
+ {
+ pTabView->SetCursor(nNewPosX, nNewPosY);
+ pTabView->DoneBlockMode();
+ pTabView->InitBlockMode(nNewPosX, nNewPosY, nTab, true);
+ pTabView->MarkCursor(nCol2, nRow2, nTab);
+ }
+ break;
+ case LOK_SETTEXTSELECTION_END:
+ if (nNewPosX != nCol2 || nNewPosY != nRow2 || bWasEmpty)
+ {
+ pTabView->SetCursor(nCol1, nRow1);
+ pTabView->DoneBlockMode();
+ pTabView->InitBlockMode(nCol1, nRow1, nTab, true);
+ pTabView->MarkCursor(nNewPosX, nNewPosY, nTab);
+ }
+ break;
+ default:
+ assert(false);
+ break;
+ }
+}
+
+void ScGridWindow::CheckNeedsRepaint()
+{
+ // called at the end of painting, and from timer after background text width calculation
+
+ if (!bNeedsRepaint)
+ return;
+
+ bNeedsRepaint = false;
+ if (aRepaintPixel.IsEmpty())
+ Invalidate();
+ else
+ Invalidate(PixelToLogic(aRepaintPixel));
+ aRepaintPixel = tools::Rectangle();
+
+ // selection function in status bar might also be invalid
+ SfxBindings& rBindings = mrViewData.GetBindings();
+ rBindings.Invalidate( SID_STATUS_SUM );
+ rBindings.Invalidate( SID_ATTR_SIZE );
+ rBindings.Invalidate( SID_TABLE_CELL );
+}
+
+void ScGridWindow::DrawHiddenIndicator( SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2, vcl::RenderContext& rRenderContext)
+{
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCTAB nTab = mrViewData.GetTabNo();
+ const svtools::ColorConfig& rColorCfg = SC_MOD()->GetColorConfig();
+ const svtools::ColorConfigValue aColorValue = rColorCfg.GetColorValue(svtools::CALCHIDDENROWCOL);
+ if (aColorValue.bIsVisible) {
+ rRenderContext.SetLineColor(aColorValue.nColor);
+ LineInfo aLineInfo(LineStyle::Dash, 2);
+ aLineInfo.SetDashCount(0);
+ aLineInfo.SetDotCount(1);
+ aLineInfo.SetDistance(15);
+ // round caps except when running VCL_PLUGIN=gen due to a performance issue
+ // https://bugs.documentfoundation.org/show_bug.cgi?id=128258#c14
+ if (mrViewData.GetActiveWin()->GetSystemData()->toolkit != SystemEnvData::Toolkit::Gen)
+ aLineInfo.SetLineCap(css::drawing::LineCap_ROUND);
+ aLineInfo.SetDotLen(1);
+ for (int i=nX1; i<nX2; i++) {
+ if (rDoc.ColHidden(i,nTab) && (i<rDoc.MaxCol() ? !rDoc.ColHidden(i+1,nTab) : true)) {
+ Point aStart = mrViewData.GetScrPos(i, nY1, eWhich, true );
+ Point aEnd = mrViewData.GetScrPos(i, nY2, eWhich, true );
+ rRenderContext.DrawLine(aStart,aEnd,aLineInfo);
+ }
+ }
+ for (int i=nY1; i<nY2; i++) {
+ if (rDoc.RowHidden(i,nTab) && (i<rDoc.MaxRow() ? !rDoc.RowHidden(i+1,nTab) : true)) {
+ Point aStart = mrViewData.GetScrPos(nX1, i, eWhich, true );
+ Point aEnd = mrViewData.GetScrPos(nX2, i, eWhich, true );
+ rRenderContext.DrawLine(aStart,aEnd,aLineInfo);
+ }
+ }
+ } //visible
+}
+
+void ScGridWindow::DrawPagePreview( SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2, vcl::RenderContext& rRenderContext)
+{
+ ScPageBreakData* pPageData = mrViewData.GetView()->GetPageBreakData();
+ if (!pPageData)
+ return;
+
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCTAB nTab = mrViewData.GetTabNo();
+ Size aWinSize = GetOutputSizePixel();
+ const svtools::ColorConfig& rColorCfg = SC_MOD()->GetColorConfig();
+ Color aManual( rColorCfg.GetColorValue(svtools::CALCPAGEBREAKMANUAL).nColor );
+ Color aAutomatic( rColorCfg.GetColorValue(svtools::CALCPAGEBREAK).nColor );
+
+ OUString aPageStr = ScResId( STR_PGNUM );
+ if ( nPageScript == SvtScriptType::NONE )
+ {
+ // get script type of translated "Page" string only once
+ nPageScript = rDoc.GetStringScriptType( aPageStr );
+ if (nPageScript == SvtScriptType::NONE)
+ nPageScript = ScGlobal::GetDefaultScriptType();
+ }
+
+ vcl::Font aFont;
+ std::unique_ptr<ScEditEngineDefaulter> pEditEng;
+ const ScPatternAttr& rDefPattern = rDoc.GetPool()->GetDefaultItem(ATTR_PATTERN);
+ if ( nPageScript == SvtScriptType::LATIN )
+ {
+ // use single font and call DrawText directly
+ rDefPattern.fillFontOnly(aFont);
+ aFont.SetColor(COL_LIGHTGRAY);
+ // font size is set as needed
+ }
+ else
+ {
+ // use EditEngine to draw mixed-script string
+ pEditEng.reset(new ScEditEngineDefaulter( EditEngine::CreatePool().get(), true ));
+ pEditEng->SetRefMapMode(rRenderContext.GetMapMode());
+ auto pEditDefaults = std::make_unique<SfxItemSet>( pEditEng->GetEmptyItemSet() );
+ rDefPattern.FillEditItemSet( pEditDefaults.get() );
+ pEditDefaults->Put( SvxColorItem( COL_LIGHTGRAY, EE_CHAR_COLOR ) );
+ pEditEng->SetDefaults( std::move(pEditDefaults) );
+ }
+
+ sal_uInt16 nCount = sal::static_int_cast<sal_uInt16>( pPageData->GetCount() );
+ for (sal_uInt16 nPos=0; nPos<nCount; nPos++)
+ {
+ ScPrintRangeData& rData = pPageData->GetData(nPos);
+ ScRange aRange = rData.GetPrintRange();
+ if ( aRange.aStart.Col() <= nX2+1 && aRange.aEnd.Col()+1 >= nX1 &&
+ aRange.aStart.Row() <= nY2+1 && aRange.aEnd.Row()+1 >= nY1 )
+ {
+ // 3 pixel frame around the print area
+ // (middle pixel on the grid lines)
+
+ rRenderContext.SetLineColor();
+ if (rData.IsAutomatic())
+ rRenderContext.SetFillColor( aAutomatic );
+ else
+ rRenderContext.SetFillColor( aManual );
+
+ Point aStart = mrViewData.GetScrPos(
+ aRange.aStart.Col(), aRange.aStart.Row(), eWhich, true );
+ Point aEnd = mrViewData.GetScrPos(
+ aRange.aEnd.Col() + 1, aRange.aEnd.Row() + 1, eWhich, true );
+ aStart.AdjustX( -2 );
+ aStart.AdjustY( -2 );
+
+ // Prevent overflows:
+ if ( aStart.X() < -10 ) aStart.setX( -10 );
+ if ( aStart.Y() < -10 ) aStart.setY( -10 );
+ if ( aEnd.X() > aWinSize.Width() + 10 )
+ aEnd.setX( aWinSize.Width() + 10 );
+ if ( aEnd.Y() > aWinSize.Height() + 10 )
+ aEnd.setY( aWinSize.Height() + 10 );
+
+ rRenderContext.DrawRect( tools::Rectangle( aStart, Point(aEnd.X(),aStart.Y()+2) ) );
+ rRenderContext.DrawRect( tools::Rectangle( aStart, Point(aStart.X()+2,aEnd.Y()) ) );
+ rRenderContext.DrawRect( tools::Rectangle( Point(aStart.X(),aEnd.Y()-2), aEnd ) );
+ rRenderContext.DrawRect( tools::Rectangle( Point(aEnd.X()-2,aStart.Y()), aEnd ) );
+
+ // Page breaks
+ //! Display differently (dashed ????)
+
+ size_t nColBreaks = rData.GetPagesX();
+ const SCCOL* pColEnd = rData.GetPageEndX();
+ size_t nColPos;
+ for (nColPos=0; nColPos+1<nColBreaks; nColPos++)
+ {
+ SCCOL nBreak = pColEnd[nColPos]+1;
+ if ( nBreak >= nX1 && nBreak <= nX2+1 )
+ {
+ //! Search for hidden
+ if (rDoc.HasColBreak(nBreak, nTab) & ScBreakType::Manual)
+ rRenderContext.SetFillColor( aManual );
+ else
+ rRenderContext.SetFillColor( aAutomatic );
+ Point aBreak = mrViewData.GetScrPos(
+ nBreak, aRange.aStart.Row(), eWhich, true );
+ rRenderContext.DrawRect( tools::Rectangle( aBreak.X()-1, aStart.Y(), aBreak.X(), aEnd.Y() ) );
+ }
+ }
+
+ size_t nRowBreaks = rData.GetPagesY();
+ const SCROW* pRowEnd = rData.GetPageEndY();
+ size_t nRowPos;
+ for (nRowPos=0; nRowPos+1<nRowBreaks; nRowPos++)
+ {
+ SCROW nBreak = pRowEnd[nRowPos]+1;
+ if ( nBreak >= nY1 && nBreak <= nY2+1 )
+ {
+ //! Search for hidden
+ if (rDoc.HasRowBreak(nBreak, nTab) & ScBreakType::Manual)
+ rRenderContext.SetFillColor( aManual );
+ else
+ rRenderContext.SetFillColor( aAutomatic );
+ Point aBreak = mrViewData.GetScrPos(
+ aRange.aStart.Col(), nBreak, eWhich, true );
+ rRenderContext.DrawRect( tools::Rectangle( aStart.X(), aBreak.Y()-1, aEnd.X(), aBreak.Y() ) );
+ }
+ }
+
+ // Page numbers
+
+ SCROW nPrStartY = aRange.aStart.Row();
+ for (nRowPos=0; nRowPos<nRowBreaks; nRowPos++)
+ {
+ SCROW nPrEndY = pRowEnd[nRowPos];
+ if ( nPrEndY >= nY1 && nPrStartY <= nY2 )
+ {
+ SCCOL nPrStartX = aRange.aStart.Col();
+ for (nColPos=0; nColPos<nColBreaks; nColPos++)
+ {
+ SCCOL nPrEndX = pColEnd[nColPos];
+ if ( nPrEndX >= nX1 && nPrStartX <= nX2 )
+ {
+ Point aPageStart = mrViewData.GetScrPos(
+ nPrStartX, nPrStartY, eWhich, true );
+ Point aPageEnd = mrViewData.GetScrPos(
+ nPrEndX+1,nPrEndY+1, eWhich, true );
+
+ tools::Long nPageNo = rData.GetFirstPage();
+ if ( rData.IsTopDown() )
+ nPageNo += static_cast<tools::Long>(nColPos)*nRowBreaks+nRowPos;
+ else
+ nPageNo += static_cast<tools::Long>(nRowPos)*nColBreaks+nColPos;
+
+ OUString aThisPageStr = aPageStr.replaceFirst("%1", OUString::number(nPageNo));
+
+ if ( pEditEng )
+ {
+ // find right font size with EditEngine
+ tools::Long nHeight = 100;
+ pEditEng->SetDefaultItem( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT ) );
+ pEditEng->SetDefaultItem( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT_CJK ) );
+ pEditEng->SetDefaultItem( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT_CTL ) );
+ pEditEng->SetTextCurrentDefaults( aThisPageStr );
+ Size aSize100( pEditEng->CalcTextWidth(), pEditEng->GetTextHeight() );
+
+ // 40% of width or 60% of height
+ tools::Long nSizeX = 40 * ( aPageEnd.X() - aPageStart.X() ) / aSize100.Width();
+ tools::Long nSizeY = 60 * ( aPageEnd.Y() - aPageStart.Y() ) / aSize100.Height();
+ nHeight = std::min(nSizeX,nSizeY);
+ pEditEng->SetDefaultItem( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT ) );
+ pEditEng->SetDefaultItem( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT_CJK ) );
+ pEditEng->SetDefaultItem( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT_CTL ) );
+
+ // centered output with EditEngine
+ Size aTextSize( pEditEng->CalcTextWidth(), pEditEng->GetTextHeight() );
+ Point aPos( (aPageStart.X()+aPageEnd.X()-aTextSize.Width())/2,
+ (aPageStart.Y()+aPageEnd.Y()-aTextSize.Height())/2 );
+ pEditEng->Draw(rRenderContext, aPos);
+ }
+ else
+ {
+ // find right font size for DrawText
+ aFont.SetFontSize( Size( 0,100 ) );
+ rRenderContext.SetFont( aFont );
+
+ Size aSize100(rRenderContext.GetTextWidth( aThisPageStr ), rRenderContext.GetTextHeight() );
+ if (aSize100.Width() && aSize100.Height())
+ {
+ // 40% of width or 60% of height
+ tools::Long nSizeX = 40 * ( aPageEnd.X() - aPageStart.X() ) / aSize100.Width();
+ tools::Long nSizeY = 60 * ( aPageEnd.Y() - aPageStart.Y() ) / aSize100.Height();
+ aFont.SetFontSize( Size( 0,std::min(nSizeX,nSizeY) ) );
+ rRenderContext.SetFont( aFont );
+ }
+
+ // centered output with DrawText
+ Size aTextSize(rRenderContext.GetTextWidth( aThisPageStr ), rRenderContext.GetTextHeight() );
+ Point aPos( (aPageStart.X()+aPageEnd.X()-aTextSize.Width())/2,
+ (aPageStart.Y()+aPageEnd.Y()-aTextSize.Height())/2 );
+ rRenderContext.DrawText( aPos, aThisPageStr );
+ }
+ }
+ nPrStartX = nPrEndX + 1;
+ }
+ }
+ nPrStartY = nPrEndY + 1;
+ }
+ }
+ }
+}
+
+void ScGridWindow::DrawButtons(SCCOL nX1, SCCOL nX2, const ScTableInfo& rTabInfo, OutputDevice* pContentDev, const ScLokRTLContext* pLokRTLContext)
+{
+ aComboButton.SetOutputDevice( pContentDev );
+
+ ScDocument& rDoc = mrViewData.GetDocument();
+ ScDPFieldButton aCellBtn(pContentDev, &GetSettings().GetStyleSettings(), &mrViewData.GetZoomY(), &rDoc);
+
+ SCCOL nCol;
+ SCROW nRow;
+ SCSIZE nArrY;
+ SCSIZE nQuery;
+ SCTAB nTab = mrViewData.GetTabNo();
+ ScDBData* pDBData = nullptr;
+ std::unique_ptr<ScQueryParam> pQueryParam;
+
+ RowInfo* pRowInfo = rTabInfo.mpRowInfo.get();
+ sal_uInt16 nArrCount = rTabInfo.mnArrCount;
+
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+
+ Point aOldPos = aComboButton.GetPosPixel(); // store state for MouseDown/Up
+ Size aOldSize = aComboButton.GetSizePixel();
+
+ for (nArrY=1; nArrY+1<nArrCount; nArrY++)
+ {
+ if ( pRowInfo[nArrY].bAutoFilter && pRowInfo[nArrY].bChanged )
+ {
+ RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+
+ nRow = pThisRowInfo->nRowNo;
+
+ for (nCol=nX1; nCol<=nX2; nCol++)
+ {
+ ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nCol);
+ //if several columns merged on a row, there should be only one auto button at the end of the columns.
+ //if several rows merged on a column, the button may be in the middle, so "!pInfo->bVOverlapped" should not be used
+ if ( pInfo->bAutoFilter && !pInfo->bHOverlapped )
+ {
+ if (!pQueryParam)
+ pQueryParam.reset(new ScQueryParam);
+
+ bool bNewData = true;
+ if (pDBData)
+ {
+ SCCOL nStartCol;
+ SCROW nStartRow;
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ SCTAB nAreaTab;
+ pDBData->GetArea( nAreaTab, nStartCol, nStartRow, nEndCol, nEndRow );
+ if ( nCol >= nStartCol && nCol <= nEndCol &&
+ nRow >= nStartRow && nRow <= nEndRow )
+ bNewData = false;
+ }
+ if (bNewData)
+ {
+ pDBData = rDoc.GetDBAtCursor( nCol, nRow, nTab, ScDBDataPortion::AREA );
+ if (pDBData)
+ pDBData->GetQueryParam( *pQueryParam );
+ else
+ {
+ // can also be part of DataPilot table
+ }
+ }
+
+ // pQueryParam can only include MAXQUERY entries
+
+ bool bArrowState = false;
+ if (pQueryParam->bInplace)
+ {
+ SCSIZE nCount = pQueryParam->GetEntryCount();
+ for (nQuery = 0; nQuery < nCount; ++nQuery)
+ {
+ // Do no restrict to EQUAL here
+ // (Column head should become blue also when ">1")
+ const ScQueryEntry& rEntry = pQueryParam->GetEntry(nQuery);
+ if (rEntry.bDoQuery && rEntry.nField == nCol)
+ {
+ bArrowState = true;
+ break;
+ }
+ }
+ }
+
+ tools::Long nSizeX;
+ tools::Long nSizeY;
+ SCCOL nStartCol= nCol;
+ SCROW nStartRow = nRow;
+ //if address(nCol,nRow) is not the start pos of the merge area, the value of the nSizeX will be incorrect, it will be the length of the cell.
+ //should first get the start pos of the merge area, then get the nSizeX through the start pos.
+ rDoc.ExtendOverlapped(nStartCol, nStartRow,nCol, nRow, nTab);//get nStartCol,nStartRow
+ mrViewData.GetMergeSizePixel( nStartCol, nStartRow, nSizeX, nSizeY );//get nSizeX
+ nSizeY = ScViewData::ToPixel(rDoc.GetRowHeight(nRow, nTab), mrViewData.GetPPTY());
+ Point aScrPos = mrViewData.GetScrPos( nCol, nRow, eWhich );
+ if (pLokRTLContext)
+ aScrPos.setX(pLokRTLContext->docToTilePos(aScrPos.X()));
+
+ aCellBtn.setBoundingBox(aScrPos, Size(nSizeX-1, nSizeY-1), bLayoutRTL);
+ aCellBtn.setPopupLeft(bLayoutRTL); // #i114944# AutoFilter button is left-aligned in RTL
+ aCellBtn.setDrawBaseButton(false);
+ aCellBtn.setDrawPopupButton(true);
+ aCellBtn.setHasHiddenMember(bArrowState);
+ aCellBtn.draw();
+ }
+ }
+ }
+
+ if ( (pRowInfo[nArrY].bPivotToggle || pRowInfo[nArrY].bPivotButton) && pRowInfo[nArrY].bChanged )
+ {
+ RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+ nRow = pThisRowInfo->nRowNo;
+ for (nCol=nX1; nCol<=nX2; nCol++)
+ {
+ ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nCol);
+ if (pInfo->bHOverlapped || pInfo->bVOverlapped)
+ continue;
+
+ Point aScrPos = mrViewData.GetScrPos( nCol, nRow, eWhich );
+ tools::Long nSizeX;
+ tools::Long nSizeY;
+ mrViewData.GetMergeSizePixel( nCol, nRow, nSizeX, nSizeY );
+ tools::Long nPosX = aScrPos.X();
+ tools::Long nPosY = aScrPos.Y();
+ // bLayoutRTL is handled in setBoundingBox
+
+ bool bDrawToggle = pInfo->bPivotCollapseButton || pInfo->bPivotExpandButton;
+ if (!bDrawToggle)
+ {
+ OUString aStr = rDoc.GetString(nCol, nRow, nTab);
+ aCellBtn.setText(aStr);
+ }
+
+ sal_uInt16 nIndent = 0;
+ if (const ScIndentItem* pIndentItem = rDoc.GetAttr(nCol, nRow, nTab, ATTR_INDENT))
+ nIndent = pIndentItem->GetValue();
+ aCellBtn.setBoundingBox(Point(nPosX, nPosY), Size(nSizeX-1, nSizeY-1), bLayoutRTL);
+ aCellBtn.setPopupLeft(false); // DataPilot popup is always right-aligned for now
+ aCellBtn.setDrawBaseButton(pInfo->bPivotButton);
+ aCellBtn.setDrawPopupButton(pInfo->bPivotPopupButton);
+ aCellBtn.setDrawPopupButtonMulti(pInfo->bPivotPopupButtonMulti);
+ aCellBtn.setDrawToggleButton(bDrawToggle, pInfo->bPivotCollapseButton, nIndent);
+ aCellBtn.setHasHiddenMember(pInfo->bFilterActive);
+ aCellBtn.draw();
+ }
+ }
+
+ if ( !comphelper::LibreOfficeKit::isActive() && bListValButton && pRowInfo[nArrY].nRowNo == aListValPos.Row() && pRowInfo[nArrY].bChanged )
+ {
+ tools::Rectangle aRect = GetListValButtonRect( aListValPos );
+ aComboButton.SetPosPixel( aRect.TopLeft() );
+ aComboButton.SetSizePixel( aRect.GetSize() );
+ pContentDev->SetClipRegion(vcl::Region(aRect));
+ aComboButton.Draw();
+ pContentDev->SetClipRegion(); // always called from Draw() without clip region
+ aComboButton.SetPosPixel( aOldPos ); // restore old state
+ aComboButton.SetSizePixel( aOldSize ); // for MouseUp/Down (AutoFilter)
+ }
+ }
+
+ pQueryParam.reset();
+ aComboButton.SetOutputDevice( GetOutDev() );
+}
+
+tools::Rectangle ScGridWindow::GetListValButtonRect( const ScAddress& rButtonPos )
+{
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCTAB nTab = mrViewData.GetTabNo();
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ ScDDComboBoxButton aButton( GetOutDev() ); // for optimal size
+ Size aBtnSize = aButton.GetSizePixel();
+
+ SCCOL nCol = rButtonPos.Col();
+ SCROW nRow = rButtonPos.Row();
+
+ tools::Long nCellSizeX; // width of this cell, including merged
+ tools::Long nDummy;
+ mrViewData.GetMergeSizePixel( nCol, nRow, nCellSizeX, nDummy );
+
+ // for height, only the cell's row is used, excluding merged cells
+ tools::Long nCellSizeY = ScViewData::ToPixel( rDoc.GetRowHeight( nRow, nTab ), mrViewData.GetPPTY() );
+ tools::Long nAvailable = nCellSizeX;
+
+ // left edge of next cell if there is a non-hidden next column
+ SCCOL nNextCol = nCol + 1;
+ const ScMergeAttr* pMerge = rDoc.GetAttr( nCol,nRow,nTab, ATTR_MERGE );
+ if ( pMerge->GetColMerge() > 1 )
+ nNextCol = nCol + pMerge->GetColMerge(); // next cell after the merged area
+ while ( nNextCol <= rDoc.MaxCol() && rDoc.ColHidden(nNextCol, nTab) )
+ ++nNextCol;
+ bool bNextCell = ( nNextCol <= rDoc.MaxCol() );
+ if ( bNextCell )
+ nAvailable = ScViewData::ToPixel( rDoc.GetColWidth( nNextCol, nTab ), mrViewData.GetPPTX() );
+
+ if ( nAvailable < aBtnSize.Width() )
+ aBtnSize.setWidth( nAvailable );
+ if ( nCellSizeY < aBtnSize.Height() )
+ aBtnSize.setHeight( nCellSizeY );
+
+ Point aPos = mrViewData.GetScrPos( nCol, nRow, eWhich, true );
+ aPos.AdjustX(nCellSizeX * nLayoutSign ); // start of next cell
+ if (!bNextCell)
+ aPos.AdjustX( -(aBtnSize.Width() * nLayoutSign) ); // right edge of cell if next cell not available
+ aPos.AdjustY(nCellSizeY - aBtnSize.Height() );
+ // X remains at the left edge
+
+ if ( bLayoutRTL )
+ aPos.AdjustX( -(aBtnSize.Width()-1) ); // align right edge of button with cell border
+
+ return tools::Rectangle( aPos, aBtnSize );
+}
+
+bool ScGridWindow::IsAutoFilterActive( SCCOL nCol, SCROW nRow, SCTAB nTab )
+{
+ ScDocument& rDoc = mrViewData.GetDocument();
+ ScDBData* pDBData = rDoc.GetDBAtCursor( nCol, nRow, nTab, ScDBDataPortion::AREA );
+ ScQueryParam aQueryParam;
+
+ if ( pDBData )
+ pDBData->GetQueryParam( aQueryParam );
+ else
+ {
+ OSL_FAIL("Auto filter button without DBData");
+ }
+
+ bool bSimpleQuery = true;
+ bool bColumnFound = false;
+ SCSIZE nQuery;
+
+ if ( !aQueryParam.bInplace )
+ bSimpleQuery = false;
+
+ // aQueryParam can only include MAXQUERY entries
+
+ SCSIZE nCount = aQueryParam.GetEntryCount();
+ for (nQuery = 0; nQuery < nCount && bSimpleQuery; ++nQuery)
+ if ( aQueryParam.GetEntry(nQuery).bDoQuery )
+ {
+ if (aQueryParam.GetEntry(nQuery).nField == nCol)
+ bColumnFound = true;
+
+ if (nQuery > 0)
+ if (aQueryParam.GetEntry(nQuery).eConnect != SC_AND)
+ bSimpleQuery = false;
+ }
+
+ return ( bSimpleQuery && bColumnFound );
+}
+
+void ScGridWindow::GetSelectionRects( ::std::vector< tools::Rectangle >& rPixelRects ) const
+{
+ GetPixelRectsFor( mrViewData.GetMarkData(), rPixelRects );
+}
+
+void ScGridWindow::GetSelectionRectsPrintTwips(::std::vector< tools::Rectangle >& rRects) const
+{
+ GetRectsAnyFor(mrViewData.GetMarkData(), rRects, true);
+}
+
+/// convert rMarkData into pixel rectangles for this view
+void ScGridWindow::GetPixelRectsFor( const ScMarkData &rMarkData,
+ ::std::vector< tools::Rectangle >& rPixelRects ) const
+{
+ GetRectsAnyFor(rMarkData, rPixelRects, false);
+}
+
+void ScGridWindow::GetRectsAnyFor(const ScMarkData &rMarkData,
+ ::std::vector< tools::Rectangle >& rRects,
+ bool bInPrintTwips) const
+{
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCTAB nTab = mrViewData.GetTabNo();
+ double nPPTX = mrViewData.GetPPTX();
+ double nPPTY = mrViewData.GetPPTY();
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+ // LOK clients needs exact document coordinates, so don't horizontally mirror them.
+ tools::Long nLayoutSign = (!comphelper::LibreOfficeKit::isActive() && bLayoutRTL) ? -1 : 1;
+
+ ScMarkData aMultiMark( rMarkData );
+ aMultiMark.SetMarking( false );
+
+ if (!aMultiMark.IsMultiMarked())
+ {
+ // simple range case - simplify calculation
+ const ScRange& aSimpleRange = aMultiMark.GetMarkArea();
+
+ aMultiMark.MarkToMulti();
+ if ( !aMultiMark.IsMultiMarked() )
+ return;
+
+ SCCOL nX1 = aSimpleRange.aStart.Col();
+ SCROW nY1 = aSimpleRange.aStart.Row();
+ SCCOL nX2 = aSimpleRange.aEnd.Col();
+ SCROW nY2 = aSimpleRange.aEnd.Row();
+
+ PutInOrder( nX1, nX2 );
+ PutInOrder( nY1, nY2 );
+
+ SCCOL nPosX = mrViewData.GetPosX( eHWhich );
+ SCROW nPosY = mrViewData.GetPosY( eVWhich );
+ // is the selection visible at all?
+ if (nX2 < nPosX || nY2 < nPosY)
+ return;
+
+ Point aScrStartPos = bInPrintTwips ? mrViewData.GetPrintTwipsPos(nX1, nY1) :
+ mrViewData.GetScrPos(nX1, nY1, eWhich);
+
+ tools::Long nStartX = aScrStartPos.X();
+ tools::Long nStartY = aScrStartPos.Y();
+
+ Point aScrEndPos = bInPrintTwips ? mrViewData.GetPrintTwipsPos(nX2, nY2) :
+ mrViewData.GetScrPos(nX2, nY2, eWhich);
+
+ tools::Long nWidthTwips = rDoc.GetColWidth(nX2, nTab);
+ const tools::Long nWidth = bInPrintTwips ?
+ nWidthTwips : ScViewData::ToPixel(nWidthTwips, nPPTX);
+ tools::Long nEndX = aScrEndPos.X() + (nWidth - 1) * nLayoutSign;
+
+ sal_uInt16 nHeightTwips = rDoc.GetRowHeight( nY2, nTab );
+ const tools::Long nHeight = bInPrintTwips ?
+ nHeightTwips : ScViewData::ToPixel(nHeightTwips, nPPTY);
+ tools::Long nEndY = aScrEndPos.Y() + nHeight - 1;
+
+ ScInvertMerger aInvert( &rRects );
+ aInvert.AddRect( tools::Rectangle( nStartX, nStartY, nEndX, nEndY ) );
+
+ return;
+ }
+
+ aMultiMark.MarkToMulti();
+ if ( !aMultiMark.IsMultiMarked() )
+ return;
+ const ScRange& aMultiRange = aMultiMark.GetMultiMarkArea();
+ SCCOL nX1 = aMultiRange.aStart.Col();
+ SCROW nY1 = aMultiRange.aStart.Row();
+ SCCOL nX2 = aMultiRange.aEnd.Col();
+ SCROW nY2 = aMultiRange.aEnd.Row();
+
+ PutInOrder( nX1, nX2 );
+ PutInOrder( nY1, nY2 );
+
+ SCCOL nTestX2 = nX2;
+ SCROW nTestY2 = nY2;
+
+ rDoc.ExtendMerge( nX1,nY1, nTestX2,nTestY2, nTab );
+
+ SCCOL nPosX = mrViewData.GetPosX( eHWhich );
+ SCROW nPosY = mrViewData.GetPosY( eVWhich );
+ // is the selection visible at all?
+ if (nTestX2 < nPosX || nTestY2 < nPosY)
+ return;
+ SCCOL nRealX1 = nX1;
+ if (nX1 < nPosX)
+ nX1 = nPosX;
+ if (nY1 < nPosY)
+ nY1 = nPosY;
+
+ if (!comphelper::LibreOfficeKit::isActive())
+ {
+ // limit the selection to only what is visible on the screen
+ SCCOL nXRight = nPosX + mrViewData.VisibleCellsX(eHWhich);
+ if (nXRight > rDoc.MaxCol())
+ nXRight = rDoc.MaxCol();
+
+ SCROW nYBottom = nPosY + mrViewData.VisibleCellsY(eVWhich);
+ if (nYBottom > rDoc.MaxRow())
+ nYBottom = rDoc.MaxRow();
+
+ // is the selection visible at all?
+ if (nX1 > nXRight || nY1 > nYBottom)
+ return;
+
+ if (nX2 > nXRight)
+ nX2 = nXRight;
+ if (nY2 > nYBottom)
+ nY2 = nYBottom;
+ }
+ else
+ {
+ SCCOL nMaxTiledCol;
+ SCROW nMaxTiledRow;
+ rDoc.GetTiledRenderingArea(nTab, nMaxTiledCol, nMaxTiledRow);
+
+ if (nX2 > nMaxTiledCol)
+ nX2 = nMaxTiledCol;
+ if (nY2 > nMaxTiledRow)
+ nY2 = nMaxTiledRow;
+ }
+
+ ScInvertMerger aInvert( &rRects );
+
+ Point aScrPos = bInPrintTwips ? mrViewData.GetPrintTwipsPos(nX1, nY1) :
+ mrViewData.GetScrPos(nX1, nY1, eWhich);
+ tools::Long nScrY = aScrPos.Y();
+ bool bWasHidden = false;
+ for (SCROW nY=nY1; nY<=nY2; nY++)
+ {
+ bool bFirstRow = ( nY == nPosY ); // first visible row?
+ bool bDoHidden = false; // repeat hidden ?
+ sal_uInt16 nHeightTwips = rDoc.GetRowHeight( nY,nTab );
+ bool bDoRow = ( nHeightTwips != 0 );
+ if (bDoRow)
+ {
+ if (bWasHidden) // test hidden merge
+ {
+ bDoHidden = true;
+ bDoRow = true;
+ }
+
+ bWasHidden = false;
+ }
+ else
+ {
+ bWasHidden = true;
+ if (nY==nY2)
+ bDoRow = true; // last cell of the block
+ }
+
+ if ( bDoRow )
+ {
+ SCCOL nLoopEndX = nX2;
+ if (nX2 < nX1) // the rest of the merge
+ {
+ SCCOL nStartX = nX1;
+ while ( rDoc.GetAttr(nStartX,nY,nTab,ATTR_MERGE_FLAG)->IsHorOverlapped() )
+ --nStartX;
+ if (nStartX <= nX2)
+ nLoopEndX = nX1;
+ }
+
+ const tools::Long nHeight = bInPrintTwips ?
+ nHeightTwips : ScViewData::ToPixel(nHeightTwips, nPPTY);
+ tools::Long nEndY = nScrY + nHeight - 1;
+ tools::Long nScrX = aScrPos.X();
+ for (SCCOL nX=nX1; nX<=nLoopEndX; nX++)
+ {
+ tools::Long nWidth = rDoc.GetColWidth(nX, nTab);
+ if (!bInPrintTwips)
+ nWidth = ScViewData::ToPixel(nWidth, nPPTX);
+
+ if ( nWidth > 0 )
+ {
+ tools::Long nEndX = nScrX + ( nWidth - 1 ) * nLayoutSign;
+
+ SCROW nThisY = nY;
+ const ScPatternAttr* pPattern = rDoc.GetPattern( nX, nY, nTab );
+ const ScMergeFlagAttr* pMergeFlag = &pPattern->GetItem(ATTR_MERGE_FLAG);
+ if ( pMergeFlag->IsVerOverlapped() && ( bDoHidden || bFirstRow ) )
+ {
+ while ( pMergeFlag->IsVerOverlapped() && nThisY > 0 &&
+ (rDoc.RowHidden(nThisY-1, nTab) || bFirstRow) )
+ {
+ --nThisY;
+ pPattern = rDoc.GetPattern( nX, nThisY, nTab );
+ pMergeFlag = &pPattern->GetItem(ATTR_MERGE_FLAG);
+ }
+ }
+
+ // only the rest of the merged is seen ?
+ SCCOL nThisX = nX;
+ if ( pMergeFlag->IsHorOverlapped() && nX == nPosX && nX > nRealX1 )
+ {
+ while ( pMergeFlag->IsHorOverlapped() )
+ {
+ --nThisX;
+ pPattern = rDoc.GetPattern( nThisX, nThisY, nTab );
+ pMergeFlag = &pPattern->GetItem(ATTR_MERGE_FLAG);
+ }
+ }
+
+ if ( aMultiMark.IsCellMarked( nThisX, nThisY, true ) )
+ {
+ if ( !pMergeFlag->IsOverlapped() )
+ {
+ const ScMergeAttr* pMerge = &pPattern->GetItem(ATTR_MERGE);
+ if (pMerge->GetColMerge() > 0 || pMerge->GetRowMerge() > 0)
+ {
+ const SCCOL nEndColMerge = nThisX + pMerge->GetColMerge();
+ const SCROW nEndRowMerge = nThisY + pMerge->GetRowMerge();
+ Point aEndPos = bInPrintTwips ?
+ mrViewData.GetPrintTwipsPos(nEndColMerge, nEndRowMerge) :
+ mrViewData.GetScrPos(nEndColMerge, nEndRowMerge, eWhich);
+ if ( aEndPos.X() * nLayoutSign > nScrX * nLayoutSign && aEndPos.Y() > nScrY )
+ {
+ aInvert.AddRect( tools::Rectangle( nScrX,nScrY,
+ aEndPos.X()-nLayoutSign,aEndPos.Y()-1 ) );
+ }
+ }
+ else if ( nEndX * nLayoutSign >= nScrX * nLayoutSign && nEndY >= nScrY )
+ {
+ aInvert.AddRect( tools::Rectangle( nScrX,nScrY,nEndX,nEndY ) );
+ }
+ }
+ }
+
+ nScrX = nEndX + nLayoutSign;
+ }
+ }
+ nScrY = nEndY + 1;
+ }
+ }
+}
+
+void ScGridWindow::DataChanged( const DataChangedEvent& rDCEvt )
+{
+ Window::DataChanged(rDCEvt);
+
+ if ( !((rDCEvt.GetType() == DataChangedEventType::PRINTER) ||
+ (rDCEvt.GetType() == DataChangedEventType::DISPLAY) ||
+ (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
+ (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
+ ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
+ (rDCEvt.GetFlags() & AllSettingsFlags::STYLE))) )
+ return;
+
+ if ( rDCEvt.GetType() == DataChangedEventType::FONTS && eWhich == mrViewData.GetActivePart() )
+ mrViewData.GetDocShell()->UpdateFontList();
+
+ if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
+ (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) )
+ {
+ if ( eWhich == mrViewData.GetActivePart() ) // only once for the view
+ {
+ ScTabView* pView = mrViewData.GetView();
+
+ pView->RecalcPPT();
+
+ // RepeatResize in case scroll bar sizes have changed
+ pView->RepeatResize();
+ pView->UpdateAllOverlays();
+
+ // invalidate cell attribs in input handler, in case the
+ // EditEngine BackgroundColor has to be changed
+ if ( mrViewData.IsActive() )
+ {
+ ScInputHandler* pHdl = SC_MOD()->GetInputHdl();
+ if (pHdl)
+ pHdl->ForgetLastPattern();
+ }
+ }
+ }
+
+ Invalidate();
+}
+
+void ScGridWindow::initiatePageBreaks()
+{
+ bInitialPageBreaks = true;
+}
+
+IMPL_LINK(ScGridWindow, InitiatePageBreaksTimer, Timer*, pTimer, void)
+{
+ if (pTimer != &maShowPageBreaksTimer)
+ return;
+
+ const ScViewOptions& rOpts = mrViewData.GetOptions();
+ const bool bPage = rOpts.GetOption(VOPT_PAGEBREAKS);
+ // tdf#124983, if option LibreOfficeDev Calc/View/Visual Aids/Page
+ // breaks is enabled, breaks should be visible. If the document is
+ // opened the first time or a tab is activated the first time, the
+ // breaks are not calculated yet, so this initialization is done here.
+ if (bPage)
+ {
+ const SCTAB nCurrentTab = mrViewData.GetTabNo();
+ ScDocument& rDoc = mrViewData.GetDocument();
+ const Size aPageSize = rDoc.GetPageSize(nCurrentTab);
+ // Do not attempt to calculate a page size here if it is empty if
+ // that involves counting pages.
+ // An earlier implementation did
+ // ScPrintFunc(pDocSh, pDocSh->GetPrinter(), nCurrentTab);
+ // rDoc.SetPageSize(nCurrentTab, rDoc.GetPageSize(nCurrentTab));
+ // which resulted in tremendous waiting times after having loaded
+ // larger documents i.e. imported from CSV, in which UI is entirely
+ // blocked. All time is spent under ScPrintFunc::CountPages() in
+ // ScTable::ExtendPrintArea() in the loop that calls
+ // MaybeAddExtraColumn() to do stuff for each text string content
+ // cell (each row in each column). Maybe that can be optimized, or
+ // obtaining page size without that overhead would be possible, but
+ // as is calling that from here is a no-no so this is a quick
+ // disable things.
+ if (!aPageSize.IsEmpty())
+ {
+ ScDocShell* pDocSh = mrViewData.GetDocShell();
+ const bool bModified = pDocSh->IsModified();
+ // Even setting the same size sets page size valid, so
+ // UpdatePageBreaks() actually does something.
+ rDoc.SetPageSize( nCurrentTab, aPageSize);
+ rDoc.UpdatePageBreaks(nCurrentTab);
+ pDocSh->PostPaint(0, 0, nCurrentTab, rDoc.MaxCol(), rDoc.MaxRow(), nCurrentTab, PaintPartFlags::Grid);
+ pDocSh->SetModified(bModified);
+ }
+ }
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/gridwin5.cxx b/sc/source/ui/view/gridwin5.cxx
new file mode 100644
index 0000000000..206b53843e
--- /dev/null
+++ b/sc/source/ui/view/gridwin5.cxx
@@ -0,0 +1,420 @@
+/* -*- 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 .
+ */
+
+#include <editeng/flditem.hxx>
+
+#include <svx/fmpage.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/ImageMapInfo.hxx>
+#include <vcl/imapobj.hxx>
+#include <vcl/help.hxx>
+#include <tools/urlobj.hxx>
+#include <sfx2/sfxhelp.hxx>
+
+#include <AccessibleDocument.hxx>
+#include <com/sun/star/accessibility/XAccessible.hpp>
+
+#include <gridwin.hxx>
+#include <viewdata.hxx>
+#include <drawview.hxx>
+#include <drwlayer.hxx>
+#include <document.hxx>
+#include <notemark.hxx>
+#include <chgtrack.hxx>
+#include <chgviset.hxx>
+#include <dbfunc.hxx>
+#include <postit.hxx>
+#include <global.hxx>
+
+bool ScGridWindow::ShowNoteMarker( SCCOL nPosX, SCROW nPosY, bool bKeyboard )
+{
+ bool bDone = false;
+
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCTAB nTab = mrViewData.GetTabNo();
+ ScAddress aCellPos( nPosX, nPosY, nTab );
+
+ OUString aTrackText;
+ bool bLeftEdge = false;
+
+ // change tracking
+
+ ScChangeTrack* pTrack = rDoc.GetChangeTrack();
+ ScChangeViewSettings* pSettings = rDoc.GetChangeViewSettings();
+ if ( pTrack && pTrack->GetFirst() && pSettings && pSettings->ShowChanges())
+ {
+ const ScChangeAction* pFound = nullptr;
+ const ScChangeAction* pFoundContent = nullptr;
+ const ScChangeAction* pFoundMove = nullptr;
+ const ScChangeAction* pAction = pTrack->GetFirst();
+ while (pAction)
+ {
+ if ( pAction->IsVisible() &&
+ ScViewUtil::IsActionShown( *pAction, *pSettings, rDoc ) )
+ {
+ ScChangeActionType eType = pAction->GetType();
+ const ScBigRange& rBig = pAction->GetBigRange();
+ if ( rBig.aStart.Tab() == nTab )
+ {
+ ScRange aRange = rBig.MakeRange( rDoc );
+
+ if ( eType == SC_CAT_DELETE_ROWS )
+ aRange.aEnd.SetRow( aRange.aStart.Row() );
+ else if ( eType == SC_CAT_DELETE_COLS )
+ aRange.aEnd.SetCol( aRange.aStart.Col() );
+
+ if ( aRange.Contains( aCellPos ) )
+ {
+ pFound = pAction; // the last one wins
+ switch ( eType )
+ {
+ case SC_CAT_CONTENT :
+ pFoundContent = pAction;
+ break;
+ case SC_CAT_MOVE :
+ pFoundMove = pAction;
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+ }
+ if ( eType == SC_CAT_MOVE )
+ {
+ ScRange aRange =
+ static_cast<const ScChangeActionMove*>(pAction)->
+ GetFromRange().MakeRange( rDoc );
+ if ( aRange.Contains( aCellPos ) )
+ {
+ pFound = pAction;
+ }
+ }
+ }
+ pAction = pAction->GetNext();
+ }
+
+ if ( pFound )
+ {
+ if ( pFoundContent && pFound->GetType() != SC_CAT_CONTENT )
+ pFound = pFoundContent; // content wins
+ if ( pFoundMove && pFound->GetType() != SC_CAT_MOVE &&
+ pFoundMove->GetActionNumber() >
+ pFound->GetActionNumber() )
+ pFound = pFoundMove; // move wins
+
+ // for deleted columns: Arrow on the left side of the cell
+ if ( pFound->GetType() == SC_CAT_DELETE_COLS )
+ bLeftEdge = true;
+
+ DateTime aDT = pFound->GetDateTime();
+ aTrackText = pFound->GetUser()
+ + ", "
+ + ScGlobal::getLocaleData().getDate(aDT)
+ + " "
+ + ScGlobal::getLocaleData().getTime(aDT)
+ + ":\n";
+ OUString aComStr=pFound->GetComment();
+ if(!aComStr.isEmpty())
+ {
+ aTrackText += aComStr + "\n( ";
+ }
+ OUString aTmp = pFound->GetDescription(rDoc);
+ aTrackText += aTmp;
+ if(!aComStr.isEmpty())
+ {
+ aTrackText += ")";
+ }
+ }
+ }
+
+ // Note, only if it is not already displayed on the Drawing Layer:
+ const ScPostIt* pNote = rDoc.GetNote( aCellPos );
+ if ( (!aTrackText.isEmpty()) || (pNote && !pNote->IsCaptionShown()) )
+ {
+ bool bNew = true;
+ bool bFast = false;
+ if (mpNoteMarker) // A note already shown
+ {
+ if (mpNoteMarker->GetDocPos() == aCellPos)
+ bNew = false; // then stop
+ else
+ bFast = true; // otherwise, at once
+
+ // marker which was shown for ctrl-F1 isn't removed by mouse events
+ if (mpNoteMarker->IsByKeyboard() && !bKeyboard)
+ bNew = false;
+ }
+ if (bNew)
+ {
+ if (bKeyboard)
+ bFast = true; // keyboard also shows the marker immediately
+
+ mpNoteMarker.reset();
+
+ bool bHSplit = mrViewData.GetHSplitMode() != SC_SPLIT_NONE;
+ bool bVSplit = mrViewData.GetVSplitMode() != SC_SPLIT_NONE;
+
+ vcl::Window* pLeft = mrViewData.GetView()->GetWindowByPos( bVSplit ? SC_SPLIT_TOPLEFT : SC_SPLIT_BOTTOMLEFT );
+ vcl::Window* pRight = bHSplit ? mrViewData.GetView()->GetWindowByPos( bVSplit ? SC_SPLIT_TOPRIGHT : SC_SPLIT_BOTTOMRIGHT ) : nullptr;
+ vcl::Window* pBottom = bVSplit ? mrViewData.GetView()->GetWindowByPos( SC_SPLIT_BOTTOMLEFT ) : nullptr;
+ vcl::Window* pDiagonal = (bHSplit && bVSplit) ? mrViewData.GetView()->GetWindowByPos( SC_SPLIT_BOTTOMRIGHT ) : nullptr;
+ OSL_ENSURE( pLeft, "ScGridWindow::ShowNoteMarker - missing top-left grid window" );
+
+ /* If caption is shown from right or bottom windows, adjust
+ mapmode to include size of top-left window. */
+ MapMode aMapMode = GetDrawMapMode( true );
+ Size aLeftSize = pLeft->PixelToLogic( pLeft->GetOutputSizePixel(), aMapMode );
+ Point aOrigin = aMapMode.GetOrigin();
+ if( (this == pRight) || (this == pDiagonal) )
+ aOrigin.AdjustX(aLeftSize.Width() );
+ if( (this == pBottom) || (this == pDiagonal) )
+ aOrigin.AdjustY(aLeftSize.Height() );
+ aMapMode.SetOrigin( aOrigin );
+
+ mpNoteMarker.reset(new ScNoteMarker(pLeft, pRight, pBottom, pDiagonal,
+ &rDoc, aCellPos, aTrackText,
+ aMapMode, bLeftEdge, bFast, bKeyboard));
+ }
+
+ bDone = true; // something is shown (old or new)
+ }
+
+ return bDone;
+}
+
+void ScGridWindow::RequestHelp(const HelpEvent& rHEvt)
+{
+ bool bDone = false;
+ OUString aFormulaText;
+ tools::Rectangle aFormulaPixRect;
+ bool bHelpEnabled = bool(rHEvt.GetMode() & ( HelpEventMode::BALLOON | HelpEventMode::QUICK ));
+ SdrView* pDrView = mrViewData.GetScDrawView();
+ bool bDrawTextEdit = false;
+ if (pDrView)
+ bDrawTextEdit = pDrView->IsTextEdit();
+ // notes or change tracking
+ if ( bHelpEnabled && !bDrawTextEdit )
+ {
+ Point aPosPixel = ScreenToOutputPixel( rHEvt.GetMousePosPixel() );
+ SCCOL nPosX;
+ SCROW nPosY;
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCTAB nTab = mrViewData.GetTabNo();
+ const ScViewOptions& rOpts = mrViewData.GetOptions();
+ mrViewData.GetPosFromPixel( aPosPixel.X(), aPosPixel.Y(), eWhich, nPosX, nPosY );
+
+ if ( ShowNoteMarker( nPosX, nPosY, false ) )
+ {
+ Window::RequestHelp( rHEvt ); // turn off old Tip/Balloon
+ bDone = true;
+ }
+
+ if ( rOpts.GetOption( VOPT_FORMULAS_MARKS ) )
+ {
+ aFormulaText = rDoc.GetFormula( nPosX, nPosY, nTab );
+ if ( !aFormulaText.isEmpty() ) {
+ const ScPatternAttr* pPattern = rDoc.GetPattern( nPosX, nPosY, nTab );
+ aFormulaPixRect = mrViewData.GetEditArea( eWhich, nPosX, nPosY, this, pPattern, true );
+ }
+ }
+ }
+
+ if (!bDone && mpNoteMarker)
+ {
+ if (mpNoteMarker->IsByKeyboard())
+ {
+ // marker which was shown for ctrl-F1 isn't removed by mouse events
+ }
+ else
+ {
+ mpNoteMarker.reset();
+ }
+ }
+
+ if ( !aFormulaText.isEmpty() )
+ {
+ tools::Rectangle aScreenRect(OutputToScreenPixel(aFormulaPixRect.TopLeft()),
+ OutputToScreenPixel(aFormulaPixRect.BottomRight()));
+ if ( rHEvt.GetMode() & HelpEventMode::BALLOON )
+ Help::ShowBalloon(this, rHEvt.GetMousePosPixel(), aScreenRect, aFormulaText);
+ else if ( rHEvt.GetMode() & HelpEventMode::QUICK )
+ Help::ShowQuickHelp(this, aScreenRect, aFormulaText);
+ bDone = true;
+ }
+
+ // Image-Map / Text-URL
+
+ if ( bHelpEnabled && !bDone && !nButtonDown ) // only without pressed button
+ {
+ OUString aHelpText;
+ tools::Rectangle aPixRect;
+ Point aPosPixel = ScreenToOutputPixel( rHEvt.GetMousePosPixel() );
+
+ if ( pDrView ) // URL / Image-Map
+ {
+ SdrViewEvent aVEvt;
+ MouseEvent aMEvt( aPosPixel, 1, MouseEventModifiers::NONE, MOUSE_LEFT );
+ SdrHitKind eHit = pDrView->PickAnything( aMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt );
+
+ if ( eHit != SdrHitKind::NONE && aVEvt.mpObj != nullptr )
+ {
+ // URL for IMapObject below Pointer is help text
+ if (SvxIMapInfo::GetIMapInfo(aVEvt.mpObj))
+ {
+ Point aLogicPos = PixelToLogic( aPosPixel );
+ IMapObject* pIMapObj = SvxIMapInfo::GetHitIMapObject(
+ aVEvt.mpObj, aLogicPos, GetOutDev() );
+
+ if ( pIMapObj )
+ {
+ // For image maps show the description, if available
+ aHelpText = pIMapObj->GetAltText();
+ if (aHelpText.isEmpty())
+ aHelpText = SfxHelp::GetURLHelpText(pIMapObj->GetURL());
+ aPixRect = LogicToPixel(aVEvt.mpObj->GetLogicRect());
+ }
+ }
+ // URL in shape text or at shape itself (URL in text overrides object URL)
+ if ( aHelpText.isEmpty() )
+ {
+ if( aVEvt.meEvent == SdrEventKind::ExecuteUrl )
+ {
+ if (aVEvt.mpURLField)
+ {
+ aHelpText = SfxHelp::GetURLHelpText(aVEvt.mpURLField->GetURL());
+ aPixRect = LogicToPixel(aVEvt.mpObj->GetLogicRect());
+ }
+ }
+ else
+ {
+ SdrPageView* pPV = nullptr;
+ Point aMDPos = PixelToLogic( aPosPixel );
+ SdrObject* pObj = pDrView->PickObj(aMDPos, pDrView->getHitTolLog(), pPV, SdrSearchOptions::ALSOONMASTER);
+ if (pObj)
+ {
+ if ( pObj->IsGroupObject() )
+ {
+ SdrObject* pHit = pDrView->PickObj(aMDPos, pDrView->getHitTolLog(), pPV, SdrSearchOptions::DEEP);
+ if (pHit)
+ pObj = pHit;
+ }
+ // Fragments pointing into the current document need no tooltip
+ // describing the ctrl-click functionality.
+ if ( !pObj->getHyperlink().isEmpty() && !pObj->getHyperlink().startsWith("#") )
+ {
+ aPixRect = LogicToPixel(aVEvt.mpObj->GetLogicRect());
+ aHelpText = SfxHelp::GetURLHelpText(pObj->getHyperlink());
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if ( aHelpText.isEmpty() ) // Text-URL
+ {
+ OUString aUrl;
+ if ( GetEditUrl( aPosPixel, nullptr, &aUrl ) )
+ {
+ aHelpText = SfxHelp::GetURLHelpText(
+ INetURLObject::decode(aUrl, INetURLObject::DecodeMechanism::Unambiguous));
+
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCCOL nPosX;
+ SCROW nPosY;
+ SCTAB nTab = mrViewData.GetTabNo();
+ mrViewData.GetPosFromPixel( aPosPixel.X(), aPosPixel.Y(), eWhich, nPosX, nPosY );
+ const ScPatternAttr* pPattern = rDoc.GetPattern( nPosX, nPosY, nTab );
+
+ // bForceToTop = sal_False, use the cell's real position
+ aPixRect = mrViewData.GetEditArea( eWhich, nPosX, nPosY, this, pPattern, false );
+ }
+ }
+
+ if ( !aHelpText.isEmpty() )
+ {
+ tools::Rectangle aScreenRect(OutputToScreenPixel(aPixRect.TopLeft()),
+ OutputToScreenPixel(aPixRect.BottomRight()));
+
+ if ( rHEvt.GetMode() & HelpEventMode::BALLOON )
+ Help::ShowBalloon(this,rHEvt.GetMousePosPixel(), aScreenRect, aHelpText);
+ else if ( rHEvt.GetMode() & HelpEventMode::QUICK )
+ Help::ShowQuickHelp(this,aScreenRect, aHelpText);
+
+ bDone = true;
+ }
+ }
+
+ // basic controls
+
+ if ( pDrView && bHelpEnabled && !bDone )
+ {
+ SdrPageView* pPV = pDrView->GetSdrPageView();
+ OSL_ENSURE( pPV, "SdrPageView* is NULL" );
+ if (pPV)
+ bDone = FmFormPage::RequestHelp( this, pDrView, rHEvt );
+ }
+
+ // If QuickHelp for AutoFill is shown, do not allow it to be removed
+
+ if ( nMouseStatus == SC_GM_TABDOWN && mrViewData.GetRefType() == SC_REFTYPE_FILL &&
+ Help::IsQuickHelpEnabled() )
+ bDone = true;
+
+ if (!bDone)
+ Window::RequestHelp( rHEvt );
+}
+
+bool ScGridWindow::IsMyModel(const SdrEditView* pSdrView)
+{
+ return pSdrView &&
+ &pSdrView->GetModel() == mrViewData.GetDocument().GetDrawLayer();
+}
+
+void ScGridWindow::HideNoteMarker()
+{
+ mpNoteMarker.reset();
+}
+
+css::uno::Reference< css::accessibility::XAccessible >
+ ScGridWindow::CreateAccessible()
+{
+ css::uno::Reference< css::accessibility::XAccessible > xAcc= GetAccessible(false);
+ if (xAcc.is())
+ {
+ return xAcc;
+ }
+
+ rtl::Reference<ScAccessibleDocument> pAccessibleDocument =
+ new ScAccessibleDocument(GetAccessibleParentWindow()->GetAccessible(),
+ mrViewData.GetViewShell(), eWhich);
+ pAccessibleDocument->PreInit();
+
+ xAcc = pAccessibleDocument;
+ SetAccessible(xAcc);
+
+ pAccessibleDocument->Init();
+
+ return xAcc;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/gridwin_dbgutil.cxx b/sc/source/ui/view/gridwin_dbgutil.cxx
new file mode 100644
index 0000000000..b141bddd76
--- /dev/null
+++ b/sc/source/ui/view/gridwin_dbgutil.cxx
@@ -0,0 +1,171 @@
+/* -*- 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/.
+ */
+
+#include <iostream>
+
+#include <gridwin.hxx>
+#include <svx/svdpage.hxx>
+#include <libxml/xmlwriter.h>
+#include <viewdata.hxx>
+#include <document.hxx>
+#include <patattr.hxx>
+#include <userdat.hxx>
+#include <dpobject.hxx>
+
+namespace {
+
+std::ostream& operator<<(std::ostream& rStrm, const ScAddress& rAddr)
+{
+ rStrm << "Col: " << rAddr.Col() << ", Row: " << rAddr.Row() << ", Tab: " << rAddr.Tab();
+ return rStrm;
+}
+
+void dumpScDrawObjData(const ScGridWindow& rWindow, const ScDrawObjData& rData, MapUnit eMapUnit)
+{
+ const Point& rStartOffset = rData.maStartOffset;
+ Point aStartOffsetPixel = rWindow.LogicToPixel(rStartOffset, MapMode(eMapUnit));
+ std::cout << " Start: " << rData.maStart << ", Offset: " << aStartOffsetPixel << std::endl;
+
+ const Point& rEndOffset = rData.maEndOffset;
+ Point aEndOffsetPixel = rWindow.LogicToPixel(rEndOffset, MapMode(eMapUnit));
+ std::cout << " End: : " << rData.maEnd << ", Offset: " << aEndOffsetPixel << std::endl;
+}
+
+}
+
+void ScGridWindow::dumpColumnInformationPixel()
+{
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCTAB nTab = mrViewData.GetTabNo();
+ for (SCCOL nCol = 0; nCol <= 20; ++nCol)
+ {
+ sal_uInt16 nWidth = rDoc.GetColWidth(nCol, nTab);
+ tools::Long nPixel = LogicToPixel(Point(nWidth, 0), MapMode(MapUnit::MapTwip)).getX();
+ std::cout << "Column: " << nCol << ", Width: " << nPixel << "px" << std::endl;
+ }
+}
+
+void ScGridWindow::dumpColumnInformationHmm()
+{
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCTAB nTab = mrViewData.GetTabNo();
+ for (SCCOL nCol = 0; nCol <= 20; ++nCol)
+ {
+ sal_uInt16 nWidth = rDoc.GetColWidth(nCol, nTab);
+ tools::Long nPixel = o3tl::convert(nWidth, o3tl::Length::twip, o3tl::Length::mm100);
+ std::cout << "Column: " << nCol << ", Width: " << nPixel << "hmm" << std::endl;
+ }
+}
+
+void ScGridWindow::dumpCellProperties()
+{
+ ScDocument& rDoc = mrViewData.GetDocument();
+ const ScMarkData& rMark = mrViewData.GetMarkData();
+ SCTAB nTab = mrViewData.GetTabNo();
+
+ ScRangeList aList;
+ if (rMark.IsMultiMarked())
+ {
+ aList = rMark.GetMarkedRangesForTab(nTab);
+ }
+ else if (rMark.IsMarked())
+ {
+ aList.Join(rMark.GetMarkArea());
+ }
+ else
+ {
+ SCCOL nCol = mrViewData.GetCurX();
+ SCROW nRow = mrViewData.GetCurY();
+
+ ScRange aRange(nCol, nRow, nTab);
+ aList.Join(aRange, false);
+ }
+
+ xmlTextWriterPtr writer = xmlNewTextWriterFilename( "dump.xml", 0 );
+ xmlTextWriterSetIndent(writer,1);
+ (void)xmlTextWriterSetIndentString(writer, BAD_CAST(" "));
+
+ (void)xmlTextWriterStartDocument( writer, nullptr, nullptr, nullptr );
+
+ (void)xmlTextWriterStartElement(writer, BAD_CAST("selection"));
+
+ for (size_t i = 0, n = aList.size(); i < n; ++i)
+ {
+ ScRange const & rRange = aList[i];
+
+ for (SCCOL nCol = rRange.aStart.Col(); nCol <= rRange.aEnd.Col(); ++nCol)
+ {
+ for (SCROW nRow = rRange.aStart.Row(); nRow <= rRange.aEnd.Row(); ++nRow)
+ {
+ const ScPatternAttr* pPatternAttr = rDoc.GetPattern(nCol, nRow, nTab);
+ (void)xmlTextWriterStartElement(writer, BAD_CAST("cell"));
+ (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("column"), BAD_CAST(OString::number(nCol).getStr()));
+ (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("row"), BAD_CAST(OString::number(nRow).getStr()));
+ (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("tab"), BAD_CAST(OString::number(nTab).getStr()));
+
+ pPatternAttr->GetItemSet().dumpAsXml(writer);
+
+ (void)xmlTextWriterEndElement(writer);
+ }
+ }
+ }
+
+ (void)xmlTextWriterEndElement(writer);
+
+ (void)xmlTextWriterEndDocument( writer );
+ xmlFreeTextWriter (writer);
+}
+
+void ScGridWindow::dumpGraphicInformation()
+{
+ ScDocument& rDoc = mrViewData.GetDocument();
+ ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer();
+ if (!pDrawLayer)
+ return;
+
+ sal_uInt16 nPageCount = pDrawLayer->GetPageCount();
+ for (sal_uInt16 nPage = 0; nPage < nPageCount; ++nPage)
+ {
+ SdrPage* pPage = pDrawLayer->GetPage(nPage);
+ size_t nObjCount = pPage->GetObjCount();
+ for (size_t nObj = 0; nObj < nObjCount; ++nObj)
+ {
+ SdrObject* pObj = pPage->GetObj(nObj);
+ std::cout << "Graphic Object" << std::endl;
+ ScDrawObjData* pObjData = ScDrawLayer::GetObjData(pObj);
+ if (pObjData)
+ dumpScDrawObjData(*this, *pObjData, pDrawLayer->GetScaleUnit());
+
+ const tools::Rectangle& rRect = pObj->GetSnapRect();
+ tools::Rectangle aRect = LogicToPixel(rRect, MapMode(pDrawLayer->GetScaleUnit()));
+ std::cout << "Snap Rectangle (in pixel): " << aRect << std::endl;
+ }
+ }
+}
+
+void ScGridWindow::dumpColumnCellStorage()
+{
+ // Get the current cursor position.
+ ScAddress aCurPos = mrViewData.GetCurPos();
+
+ ScDocument& rDoc = mrViewData.GetDocument();
+ const ScDPObject* pDP = rDoc.GetDPAtCursor(aCurPos.Col(), aCurPos.Row(), aCurPos.Tab());
+ if (pDP)
+ {
+ // Dump the pivot table info if the cursor is over a pivot table.
+ pDP->Dump();
+ pDP->DumpCache();
+ return;
+ }
+
+ // Dump the column cell storage info.
+ rDoc.DumpColumnStorage(aCurPos.Tab(), aCurPos.Col());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/hdrcont.cxx b/sc/source/ui/view/hdrcont.cxx
new file mode 100644
index 0000000000..c6688ea115
--- /dev/null
+++ b/sc/source/ui/view/hdrcont.cxx
@@ -0,0 +1,1124 @@
+/* -*- 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 .
+ */
+
+#include <sfx2/dispatch.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/help.hxx>
+#include <vcl/settings.hxx>
+#include <svtools/colorcfg.hxx>
+#include <osl/diagnose.h>
+
+#include <tabvwsh.hxx>
+#include <hdrcont.hxx>
+#include <dbdata.hxx>
+#include <scmod.hxx>
+#include <inputopt.hxx>
+#include <gridmerg.hxx>
+#include <document.hxx>
+#include <markdata.hxx>
+#include <tabview.hxx>
+#include <viewdata.hxx>
+#include <columnspanset.hxx>
+
+#define SC_DRAG_MIN 2
+
+// passes in paint
+// (selection left/right must be first because the continuous lines
+// are partly overwritten later)
+
+#define SC_HDRPAINT_SEL_BOTTOM 4
+#define SC_HDRPAINT_BOTTOM 5
+#define SC_HDRPAINT_TEXT 6
+#define SC_HDRPAINT_COUNT 7
+
+ScHeaderControl::ScHeaderControl( vcl::Window* pParent, SelectionEngine* pSelectionEngine,
+ SCCOLROW nNewSize, bool bNewVertical, ScTabView* pTab ) :
+ Window ( pParent ),
+ pSelEngine ( pSelectionEngine ),
+ aShowHelpTimer("sc HeaderControl Popover Timer"),
+ bVertical ( bNewVertical ),
+ nSize ( nNewSize ),
+ nMarkStart ( 0 ),
+ nMarkEnd ( 0 ),
+ bMarkRange ( false ),
+ bDragging ( false ),
+ nDragNo ( 0 ),
+ nDragStart ( 0 ),
+ nDragPos ( 0 ),
+ nTipVisible ( nullptr ),
+ bDragMoved ( false ),
+ bIgnoreMove ( false ),
+ bInRefMode ( false ),
+ pTabView ( pTab )
+{
+ // RTL: no default mirroring for this window, the spreadsheet itself
+ // is also not mirrored
+ // mirror the vertical window for correct border drawing
+ // table layout depends on sheet format, not UI setting, so the
+ // borders of the vertical window have to be handled manually, too.
+ EnableRTL( false );
+
+ aNormFont = GetFont();
+ aNormFont.SetTransparent( true ); //! hard-set WEIGHT_NORMAL ???
+ aBoldFont = aNormFont;
+ aBoldFont.SetWeight( WEIGHT_BOLD );
+ aAutoFilterFont = aNormFont;
+
+ SetFont(aBoldFont);
+ bBoldSet = true;
+ bAutoFilterSet = false;
+
+ Size aSize = LogicToPixel( Size(
+ GetTextWidth("8888"),
+ GetTextHeight() ) );
+ aSize.AdjustWidth(4 ); // place for highlight border
+ aSize.AdjustHeight(3 );
+ SetSizePixel( aSize );
+
+ nWidth = nSmallWidth = aSize.Width();
+ nBigWidth = LogicToPixel( Size( GetTextWidth("8888888"), 0 ) ).Width() + 5;
+
+ aShowHelpTimer.SetInvokeHandler(LINK(this, ScHeaderControl, ShowDragHelpHdl));
+ aShowHelpTimer.SetTimeout(GetSettings().GetMouseSettings().GetDoubleClickTime());
+
+ SetBackground();
+}
+
+void ScHeaderControl::dispose()
+{
+ aShowHelpTimer.Stop();
+ vcl::Window::dispose();
+}
+
+void ScHeaderControl::SetWidth( tools::Long nNew )
+{
+ OSL_ENSURE( bVertical, "SetWidth works only on row headers" );
+ if ( nNew != nWidth )
+ {
+ Size aSize( nNew, GetSizePixel().Height() );
+ SetSizePixel( aSize );
+
+ nWidth = nNew;
+
+ Invalidate();
+ }
+}
+
+ScHeaderControl::~ScHeaderControl()
+{
+}
+
+void ScHeaderControl::DoPaint( SCCOLROW nStart, SCCOLROW nEnd )
+{
+ bool bLayoutRTL = IsLayoutRTL();
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ tools::Rectangle aRect( Point(0,0), GetOutputSizePixel() );
+ if ( bVertical )
+ {
+ aRect.SetTop( GetScrPos( nStart )-nLayoutSign ); // extra pixel for line at top of selection
+ aRect.SetBottom( GetScrPos( nEnd+1 )-nLayoutSign );
+ }
+ else
+ {
+ aRect.SetLeft( GetScrPos( nStart )-nLayoutSign ); // extra pixel for line left of selection
+ aRect.SetRight( GetScrPos( nEnd+1 )-nLayoutSign );
+ }
+ Invalidate(aRect);
+}
+
+void ScHeaderControl::SetMark( bool bNewSet, SCCOLROW nNewStart, SCCOLROW nNewEnd )
+{
+ bool bEnabled = SC_MOD()->GetInputOptions().GetMarkHeader(); //! cache?
+ if (!bEnabled)
+ bNewSet = false;
+
+ bool bOldSet = bMarkRange;
+ SCCOLROW nOldStart = nMarkStart;
+ SCCOLROW nOldEnd = nMarkEnd;
+ PutInOrder( nNewStart, nNewEnd );
+ bMarkRange = bNewSet;
+ nMarkStart = nNewStart;
+ nMarkEnd = nNewEnd;
+
+ // Paint
+
+ if ( bNewSet )
+ {
+ if ( bOldSet )
+ {
+ if ( nNewStart == nOldStart )
+ {
+ if ( nNewEnd != nOldEnd )
+ DoPaint( std::min( nNewEnd, nOldEnd ) + 1, std::max( nNewEnd, nOldEnd ) );
+ }
+ else if ( nNewEnd == nOldEnd )
+ DoPaint( std::min( nNewStart, nOldStart ), std::max( nNewStart, nOldStart ) - 1 );
+ else if ( nNewStart > nOldEnd || nNewEnd < nOldStart )
+ {
+ // two areas
+ DoPaint( nOldStart, nOldEnd );
+ DoPaint( nNewStart, nNewEnd );
+ }
+ else // somehow overlapping... (it is not often)
+ DoPaint( std::min( nNewStart, nOldStart ), std::max( nNewEnd, nOldEnd ) );
+ }
+ else
+ DoPaint( nNewStart, nNewEnd ); // completely new selection
+ }
+ else if ( bOldSet )
+ DoPaint( nOldStart, nOldEnd ); // cancel selection
+}
+
+tools::Long ScHeaderControl::GetScrPos( SCCOLROW nEntryNo ) const
+{
+ tools::Long nScrPos;
+
+ tools::Long nMax = ( bVertical ? GetOutputSizePixel().Height() : GetOutputSizePixel().Width() ) + 1;
+ if (nEntryNo >= nSize)
+ nScrPos = nMax;
+ else
+ {
+ nScrPos = 0;
+ for (SCCOLROW i=GetPos(); i<nEntryNo && nScrPos<nMax; i++)
+ {
+ sal_uInt16 nAdd = GetEntrySize(i);
+ if (nAdd)
+ nScrPos += nAdd;
+ else
+ {
+ SCCOLROW nHidden = GetHiddenCount(i);
+ if (nHidden > 0)
+ i += nHidden - 1;
+ }
+ }
+ }
+
+ if ( IsLayoutRTL() )
+ nScrPos = nMax - nScrPos - 2;
+
+ return nScrPos;
+}
+
+void ScHeaderControl::Paint( vcl::RenderContext& /*rRenderContext*/, const tools::Rectangle& rRect )
+{
+ // It is important for VCL to have few calls, that is why the outer lines are
+ // grouped together
+
+ const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
+ bool bHighContrast = rStyleSettings.GetHighContrastMode();
+ bool bDark = rStyleSettings.GetFaceColor().IsDark();
+ // Use the same distinction for bDark as in Window::DrawSelectionBackground
+
+ Color aTextColor = rStyleSettings.GetButtonTextColor();
+ Color aSelTextColor = rStyleSettings.GetHighlightTextColor();
+ Color aAFilterTextColor = rStyleSettings.GetButtonTextColor();
+ aAFilterTextColor.Merge(COL_LIGHTBLUE, bDark ? 150 : 10); // color of filtered row numbers
+ aNormFont.SetColor( aTextColor );
+ aAutoFilterFont.SetColor(aAFilterTextColor);
+ if ( bHighContrast )
+ aBoldFont.SetColor( aTextColor );
+ else
+ aBoldFont.SetColor( aSelTextColor );
+
+ if (bAutoFilterSet)
+ SetTextColor(aAFilterTextColor);
+ else
+ SetTextColor((bBoldSet && !bHighContrast) ? aSelTextColor : aTextColor);
+
+ Color aSelLineColor = rStyleSettings.GetHighlightColor();
+ aSelLineColor.Merge( COL_BLACK, 0xe0 ); // darken just a little bit
+
+ bool bLayoutRTL = IsLayoutRTL();
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+ bool bMirrored = IsMirrored();
+
+ OUString aString;
+ sal_uInt16 nBarSize;
+ Point aScrPos;
+ Size aTextSize;
+
+ if (bVertical)
+ nBarSize = static_cast<sal_uInt16>(GetSizePixel().Width());
+ else
+ nBarSize = static_cast<sal_uInt16>(GetSizePixel().Height());
+
+ SCCOLROW nPos = GetPos();
+
+ tools::Long nPStart = bVertical ? rRect.Top() : rRect.Left();
+ tools::Long nPEnd = bVertical ? rRect.Bottom() : rRect.Right();
+
+ tools::Long nTransStart = nPEnd + 1;
+ tools::Long nTransEnd = 0;
+
+ tools::Long nInitScrPos = 0;
+ if ( bLayoutRTL )
+ {
+ std::swap(nPStart, nPEnd);
+ std::swap(nTransStart, nTransEnd);
+ if ( bVertical ) // start loops from the end
+ nInitScrPos = GetSizePixel().Height() - 1;
+ else
+ nInitScrPos = GetSizePixel().Width() - 1;
+ }
+
+ // complete the painting of the outer lines
+ // first find the end of the last cell
+
+ tools::Long nLineEnd = nInitScrPos - nLayoutSign;
+
+ for (SCCOLROW i=nPos; i<nSize; i++)
+ {
+ sal_uInt16 nSizePix = GetEntrySize( i );
+ if (nSizePix)
+ {
+ nLineEnd += nSizePix * nLayoutSign;
+
+ if ( bMarkRange && i >= nMarkStart && i <= nMarkEnd )
+ {
+ tools::Long nLineStart = nLineEnd - ( nSizePix - 1 ) * nLayoutSign;
+ if ( nLineStart * nLayoutSign < nTransStart * nLayoutSign )
+ nTransStart = nLineStart;
+ if ( nLineEnd * nLayoutSign > nTransEnd * nLayoutSign )
+ nTransEnd = nLineEnd;
+ }
+
+ if ( nLineEnd * nLayoutSign > nPEnd * nLayoutSign )
+ {
+ nLineEnd = nPEnd;
+ break;
+ }
+ }
+ else
+ {
+ SCCOLROW nHidden = GetHiddenCount(i);
+ if (nHidden > 0)
+ i += nHidden - 1;
+ }
+ }
+
+ // background is different for entry area and behind the entries
+
+ tools::Rectangle aFillRect;
+ GetOutDev()->SetLineColor();
+
+ if ( nLineEnd * nLayoutSign >= nInitScrPos * nLayoutSign )
+ {
+ GetOutDev()->SetFillColor( rStyleSettings.GetFaceColor() );
+ if ( bVertical )
+ aFillRect = tools::Rectangle( 0, nInitScrPos, nBarSize-1, nLineEnd );
+ else
+ aFillRect = tools::Rectangle( nInitScrPos, 0, nLineEnd, nBarSize-1 );
+ GetOutDev()->DrawRect( aFillRect );
+ }
+
+ if ( nLineEnd * nLayoutSign < nPEnd * nLayoutSign )
+ {
+ GetOutDev()->SetFillColor( SC_MOD()->GetColorConfig().GetColorValue(svtools::APPBACKGROUND).nColor );
+ if ( bVertical )
+ aFillRect = tools::Rectangle( 0, nLineEnd+nLayoutSign, nBarSize-1, nPEnd );
+ else
+ aFillRect = tools::Rectangle( nLineEnd+nLayoutSign, 0, nPEnd, nBarSize-1 );
+ GetOutDev()->DrawRect( aFillRect );
+ }
+
+ if ( nLineEnd * nLayoutSign >= nPStart * nLayoutSign )
+ {
+ if ( nTransEnd * nLayoutSign >= nTransStart * nLayoutSign )
+ {
+ if (bVertical)
+ aFillRect = tools::Rectangle( 0, nTransStart, nBarSize-1, nTransEnd );
+ else
+ aFillRect = tools::Rectangle( nTransStart, 0, nTransEnd, nBarSize-1 );
+
+ if ( bHighContrast )
+ {
+ if ( bDark )
+ {
+ // solid grey background for dark face color is drawn before lines
+ GetOutDev()->SetLineColor();
+ GetOutDev()->SetFillColor( COL_LIGHTGRAY );
+ GetOutDev()->DrawRect( aFillRect );
+ }
+ }
+ else
+ {
+ // background for selection
+ GetOutDev()->SetLineColor();
+ Color aColor( rStyleSettings.GetAccentColor() );
+// merging the highlightcolor (which is used if accent does not exist) with the background
+// fails in many cases such as Breeze Dark (highlight is too close to background) and
+// Breeze Light (font color is white and not readable anymore)
+#ifdef MACOSX
+ aColor.Merge( rStyleSettings.GetFaceColor(), 80 );
+#endif
+ GetOutDev()->SetFillColor( aColor );
+ GetOutDev()->DrawRect( aFillRect );
+ }
+ }
+
+ GetOutDev()->SetLineColor( rStyleSettings.GetDarkShadowColor() );
+ if (bVertical)
+ {
+ tools::Long nDarkPos = bMirrored ? 0 : nBarSize-1;
+ GetOutDev()->DrawLine( Point( nDarkPos, nPStart ), Point( nDarkPos, nLineEnd ) );
+ }
+ else
+ GetOutDev()->DrawLine( Point( nPStart, nBarSize-1 ), Point( nLineEnd, nBarSize-1 ) );
+
+ // line in different color for selection
+ if ( nTransEnd * nLayoutSign >= nTransStart * nLayoutSign && !bHighContrast )
+ {
+ GetOutDev()->SetLineColor( aSelLineColor );
+ if (bVertical)
+ {
+ tools::Long nDarkPos = bMirrored ? 0 : nBarSize-1;
+ GetOutDev()->DrawLine( Point( nDarkPos, nTransStart ), Point( nDarkPos, nTransEnd ) );
+ }
+ else
+ GetOutDev()->DrawLine( Point( nTransStart, nBarSize-1 ), Point( nTransEnd, nBarSize-1 ) );
+ }
+ }
+
+ // tdf#89841 Use blue row numbers when Autofilter selected
+ std::vector<sc::ColRowSpan> aSpans;
+ if (bVertical && pTabView)
+ {
+ SCTAB nTab = pTabView->GetViewData().GetTabNo();
+ ScDocument& rDoc = pTabView->GetViewData().GetDocument();
+
+ ScDBData* pDBData = rDoc.GetAnonymousDBData(nTab);
+ if (pDBData && pDBData->HasAutoFilter())
+ {
+ SCSIZE nSelected = 0;
+ SCSIZE nTotal = 0;
+ pDBData->GetFilterSelCount(nSelected, nTotal);
+ if (nTotal > nSelected)
+ {
+ ScRange aRange;
+ pDBData->GetArea(aRange);
+ SCCOLROW nStartRow = static_cast<SCCOLROW>(aRange.aStart.Row());
+ SCCOLROW nEndRow = static_cast<SCCOLROW>(aRange.aEnd.Row());
+ if (pDBData->HasHeader())
+ nStartRow++;
+ aSpans.push_back(sc::ColRowSpan(nStartRow, nEndRow));
+ }
+ }
+
+ ScDBCollection* pDocColl = rDoc.GetDBCollection();
+ if (!pDocColl->empty())
+ {
+ ScDBCollection::NamedDBs& rDBs = pDocColl->getNamedDBs();
+ for (const auto& rxDB : rDBs)
+ {
+ if (rxDB->GetTab() == nTab && rxDB->HasAutoFilter())
+ {
+ SCSIZE nSelected = 0;
+ SCSIZE nTotal = 0;
+ rxDB->GetFilterSelCount(nSelected, nTotal);
+ if (nTotal > nSelected)
+ {
+ ScRange aRange;
+ rxDB->GetArea(aRange);
+ SCCOLROW nStartRow = static_cast<SCCOLROW>(aRange.aStart.Row());
+ SCCOLROW nEndRow = static_cast<SCCOLROW>(aRange.aEnd.Row());
+ if (rxDB->HasHeader())
+ nStartRow++;
+ aSpans.push_back(sc::ColRowSpan(nStartRow, nEndRow));
+ }
+ }
+ }
+ }
+ }
+
+ // loop through entries several times to avoid changing the line color too often
+ // and to allow merging of lines
+
+ ScGridMerger aGrid( GetOutDev(), 1, 1 );
+
+ // start at SC_HDRPAINT_BOTTOM instead of 0 - selection doesn't get different
+ // borders, light border at top isn't used anymore
+ // use SC_HDRPAINT_SEL_BOTTOM for different color
+
+ for (sal_uInt16 nPass = SC_HDRPAINT_SEL_BOTTOM; nPass < SC_HDRPAINT_COUNT; nPass++)
+ {
+ // set line color etc. before entry loop
+ switch ( nPass )
+ {
+ case SC_HDRPAINT_SEL_BOTTOM:
+ // same as non-selected for high contrast
+ GetOutDev()->SetLineColor( bHighContrast ? rStyleSettings.GetDarkShadowColor() : aSelLineColor );
+ break;
+ case SC_HDRPAINT_BOTTOM:
+ GetOutDev()->SetLineColor( rStyleSettings.GetDarkShadowColor() );
+ break;
+ case SC_HDRPAINT_TEXT:
+ // DrawSelectionBackground is used only for high contrast on light background
+ if ( nTransEnd * nLayoutSign >= nTransStart * nLayoutSign && bHighContrast && !bDark )
+ {
+ // Transparent selection background is drawn after lines, before text.
+ // Use DrawSelectionBackground to make sure there is a visible
+ // difference. The case of a dark face color, where DrawSelectionBackground
+ // would just paint over the lines, is handled separately (bDark).
+ // Otherwise, GetHighlightColor is used with 80% transparency.
+ // The window's background color (SetBackground) has to be the background
+ // of the cell area, for the contrast comparison in DrawSelectionBackground.
+
+ tools::Rectangle aTransRect;
+ if (bVertical)
+ aTransRect = tools::Rectangle( 0, nTransStart, nBarSize-1, nTransEnd );
+ else
+ aTransRect = tools::Rectangle( nTransStart, 0, nTransEnd, nBarSize-1 );
+ SetBackground( rStyleSettings.GetFaceColor() );
+ DrawSelectionBackground( aTransRect, 0, true, false );
+ SetBackground();
+ }
+ break;
+ }
+
+ SCCOLROW nCount=0;
+ tools::Long nScrPos=nInitScrPos;
+ do
+ {
+ if (bVertical)
+ aScrPos = Point( 0, nScrPos );
+ else
+ aScrPos = Point( nScrPos, 0 );
+
+ SCCOLROW nEntryNo = nCount + nPos;
+ if ( nEntryNo >= nSize ) // rDoc.MaxCol()/rDoc.MaxRow()
+ nScrPos = nPEnd + nLayoutSign; // beyond nPEnd -> stop
+ else
+ {
+ sal_uInt16 nSizePix = GetEntrySize( nEntryNo );
+
+ if (nSizePix == 0)
+ {
+ SCCOLROW nHidden = GetHiddenCount(nEntryNo);
+ if (nHidden > 0)
+ nCount += nHidden - 1;
+ }
+ else if ((nScrPos+nSizePix*nLayoutSign)*nLayoutSign >= nPStart*nLayoutSign)
+ {
+ Point aEndPos(aScrPos);
+ if (bVertical)
+ aEndPos = Point( aScrPos.X()+nBarSize-1, aScrPos.Y()+(nSizePix-1)*nLayoutSign );
+ else
+ aEndPos = Point( aScrPos.X()+(nSizePix-1)*nLayoutSign, aScrPos.Y()+nBarSize-1 );
+
+ bool bMark = bMarkRange && nEntryNo >= nMarkStart && nEntryNo <= nMarkEnd;
+ bool bNextToMark = bMarkRange && nEntryNo + 1 >= nMarkStart && nEntryNo <= nMarkEnd;
+
+ switch ( nPass )
+ {
+ case SC_HDRPAINT_SEL_BOTTOM:
+ case SC_HDRPAINT_BOTTOM:
+ if ( nPass == ( bNextToMark ? SC_HDRPAINT_SEL_BOTTOM : SC_HDRPAINT_BOTTOM ) )
+ {
+ if (bVertical)
+ aGrid.AddHorLine(/* here we work in pixels */ true, aScrPos.X(), aEndPos.X(), aEndPos.Y());
+ else
+ aGrid.AddVerLine(/* here we work in pixels */ true, aEndPos.X(), aScrPos.Y(), aEndPos.Y());
+
+ // thick bottom for hidden rows
+ // (drawn directly, without aGrid)
+ if ( nEntryNo+1 < nSize )
+ if ( GetEntrySize(nEntryNo+1)==0 )
+ {
+ if (bVertical)
+ GetOutDev()->DrawLine( Point(aScrPos.X(),aEndPos.Y()-nLayoutSign),
+ Point(aEndPos.X(),aEndPos.Y()-nLayoutSign) );
+ else
+ GetOutDev()->DrawLine( Point(aEndPos.X()-nLayoutSign,aScrPos.Y()),
+ Point(aEndPos.X()-nLayoutSign,aEndPos.Y()) );
+ }
+ }
+ break;
+
+ case SC_HDRPAINT_TEXT:
+ if ( nSizePix > 1 ) // minimal check for small columns/rows
+ {
+ if (bVertical)
+ {
+ bool bAutoFilterPos = false;
+ for (const auto& rSpan : aSpans)
+ {
+ if (nEntryNo >= rSpan.mnStart && nEntryNo <= rSpan.mnEnd)
+ {
+ bAutoFilterPos = true;
+ break;
+ }
+ }
+
+ if (bMark != bBoldSet || bAutoFilterPos != bAutoFilterSet)
+ {
+ if (bMark)
+ SetFont(aBoldFont);
+ else if (bAutoFilterPos)
+ SetFont(aAutoFilterFont);
+ else
+ SetFont(aNormFont);
+ bBoldSet = bMark;
+ bAutoFilterSet = bAutoFilterPos && !bMark;
+ }
+ }
+ else
+ {
+ if (bMark != bBoldSet)
+ {
+ if (bMark)
+ SetFont(aBoldFont);
+ else
+ SetFont(aNormFont);
+ bBoldSet = bMark;
+ }
+ }
+
+ aString = GetEntryText( nEntryNo );
+ aTextSize.setWidth( GetTextWidth( aString ) );
+ aTextSize.setHeight( GetTextHeight() );
+
+ Point aTxtPos(aScrPos);
+ if (bVertical)
+ {
+ aTxtPos.AdjustX((nBarSize-aTextSize.Width())/2 );
+ aTxtPos.AdjustY((nSizePix*nLayoutSign-aTextSize.Height())/2 );
+ if ( bMirrored )
+ aTxtPos.AdjustX(1 ); // dark border is left instead of right
+ }
+ else
+ {
+ aTxtPos.AdjustX((nSizePix*nLayoutSign-aTextSize.Width()+1)/2 );
+ aTxtPos.AdjustY((nBarSize-aTextSize.Height())/2 );
+ }
+ GetOutDev()->DrawText( aTxtPos, aString );
+ }
+ break;
+ }
+
+ // when selecting the complete row/column:
+ // InvertRect( Rectangle( aScrPos, aEndPos ) );
+ }
+ nScrPos += nSizePix * nLayoutSign; // also if before the visible area
+ }
+ ++nCount;
+ }
+ while ( nScrPos * nLayoutSign <= nPEnd * nLayoutSign );
+
+ aGrid.Flush();
+ }
+}
+
+SCCOLROW ScHeaderControl::GetMousePos(const Point& rPos, bool& rBorder) const
+{
+ bool bFound = false;
+ SCCOLROW nPos = GetPos();
+ SCCOLROW nHitNo = nPos;
+ SCCOLROW nEntryNo = 1 + nPos;
+ tools::Long nScrPos;
+ tools::Long nMousePos = bVertical ? rPos.Y() : rPos.X();
+ tools::Long nDif;
+ Size aSize = GetOutputSizePixel();
+ tools::Long nWinSize = bVertical ? aSize.Height() : aSize.Width();
+
+ bool bLayoutRTL = IsLayoutRTL();
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+ tools::Long nEndPos = bLayoutRTL ? -1 : nWinSize;
+
+ nScrPos = GetScrPos( nPos ) - nLayoutSign;
+ do
+ {
+ if (nEntryNo > nSize)
+ nScrPos = nEndPos + nLayoutSign;
+ else
+ nScrPos += GetEntrySize( nEntryNo - 1 ) * nLayoutSign; //! GetHiddenCount() ??
+
+ nDif = nMousePos - nScrPos;
+ if (nDif >= -2 && nDif <= 2)
+ {
+ bFound = true;
+ nHitNo=nEntryNo-1;
+ }
+ else if (nDif * nLayoutSign >= 0 && nEntryNo < nSize)
+ nHitNo = nEntryNo;
+ ++nEntryNo;
+ }
+ while ( nScrPos * nLayoutSign < nEndPos * nLayoutSign && nDif * nLayoutSign > 0 );
+
+ rBorder = bFound;
+ return nHitNo;
+}
+
+bool ScHeaderControl::IsSelectionAllowed(SCCOLROW nPos) const
+{
+ ScTabViewShell* pViewSh = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
+ if (!pViewSh)
+ return false;
+
+ ScViewData& rViewData = pViewSh->GetViewData();
+ sal_uInt16 nTab = rViewData.GetTabNo();
+ ScDocument& rDoc = rViewData.GetDocument();
+ 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 bCellsProtected = false;
+ if (bVertical)
+ {
+ // row header
+ SCROW nRPos = static_cast<SCROW>(nPos);
+ bCellsProtected = rDoc.HasAttrib(0, nRPos, nTab, rDoc.MaxCol(), nRPos, nTab, HasAttrFlags::Protected);
+ }
+ else
+ {
+ // column header
+ SCCOL nCPos = static_cast<SCCOL>(nPos);
+ bCellsProtected = rDoc.HasAttrib(nCPos, 0, nTab, nCPos, rDoc.MaxRow(), nTab, HasAttrFlags::Protected);
+ }
+
+ bool bSelProtected = pProtect->isOptionEnabled(ScTableProtection::SELECT_LOCKED_CELLS);
+ bool bSelUnprotected = pProtect->isOptionEnabled(ScTableProtection::SELECT_UNLOCKED_CELLS);
+
+ if (bCellsProtected)
+ bSelectAllowed = bSelProtected;
+ else
+ bSelectAllowed = bSelUnprotected;
+ }
+ return bSelectAllowed;
+}
+
+void ScHeaderControl::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ if (IsDisabled())
+ return;
+
+ bIgnoreMove = false;
+ SelectWindow();
+
+ bool bIsBorder;
+ SCCOLROW nHitNo = GetMousePos(rMEvt.GetPosPixel(), bIsBorder);
+ if (!IsSelectionAllowed(nHitNo))
+ return;
+ if ( ! rMEvt.IsLeft() )
+ return;
+ if ( SC_MOD()->IsFormulaMode() )
+ {
+ if( !pTabView )
+ return;
+ SCTAB nTab = pTabView->GetViewData().GetTabNo();
+ if( !rMEvt.IsShift() )
+ pTabView->DoneRefMode( rMEvt.IsMod1() );
+ ScDocument& rDoc = pTabView->GetViewData().GetDocument();
+ if( !bVertical )
+ {
+ pTabView->InitRefMode( nHitNo, 0, nTab, SC_REFTYPE_REF );
+ pTabView->UpdateRef( nHitNo, rDoc.MaxRow(), nTab );
+ }
+ else
+ {
+ pTabView->InitRefMode( 0, nHitNo, nTab, SC_REFTYPE_REF );
+ pTabView->UpdateRef( rDoc.MaxCol(), nHitNo, nTab );
+ }
+ bInRefMode = true;
+ return;
+ }
+ if ( bIsBorder && ResizeAllowed() )
+ {
+ nDragNo = nHitNo;
+ sal_uInt16 nClicks = rMEvt.GetClicks();
+ if ( nClicks && nClicks%2==0 )
+ {
+ SetEntrySize( nDragNo, HDR_SIZE_OPTIMUM );
+ SetPointer( PointerStyle::Arrow );
+ }
+ else
+ {
+ if (bVertical)
+ nDragStart = rMEvt.GetPosPixel().Y();
+ else
+ nDragStart = rMEvt.GetPosPixel().X();
+ nDragPos = nDragStart;
+ // tdf#140833 launch help tip to show after the double click time has expired
+ // so under gtk the popover isn't active when the double click is processed
+ // by gtk because under load on wayland the double click is getting handled
+ // by something else and getting sent to the window underneath our window
+ aShowHelpTimer.Start();
+ DrawInvert( nDragPos );
+
+ StartTracking();
+ bDragging = true;
+ bDragMoved = false;
+ }
+ }
+ else
+ {
+ pSelEngine->SetWindow( this );
+ tools::Rectangle aVis( Point(), GetOutputSizePixel() );
+ if (bVertical)
+ {
+ aVis.SetLeft( LONG_MIN );
+ aVis.SetRight( LONG_MAX );
+ }
+ else
+ {
+ aVis.SetTop( LONG_MIN );
+ aVis.SetBottom( LONG_MAX );
+ }
+ pSelEngine->SetVisibleArea( aVis );
+
+ SetMarking( true ); // must precede SelMouseButtonDown
+ pSelEngine->SelMouseButtonDown( rMEvt );
+
+ // In column/row headers a simple click already is a selection.
+ // -> Call SelMouseMove to ensure CreateAnchor is called (and DestroyAnchor
+ // if the next click is somewhere else with Control key).
+ pSelEngine->SelMouseMove( rMEvt );
+
+ if (IsMouseCaptured())
+ {
+ // tracking instead of CaptureMouse, so it can be cancelled cleanly
+ //! someday SelectionEngine itself should call StartTracking!?!
+ ReleaseMouse();
+ StartTracking();
+ }
+ }
+}
+
+void ScHeaderControl::MouseButtonUp( const MouseEvent& rMEvt )
+{
+ if ( IsDisabled() )
+ return;
+
+ if ( SC_MOD()->IsFormulaMode() )
+ {
+ SC_MOD()->EndReference();
+ bInRefMode = false;
+ return;
+ }
+
+ SetMarking( false );
+ bIgnoreMove = false;
+
+ if ( bDragging )
+ {
+ DrawInvert( nDragPos );
+ ReleaseMouse();
+ HideDragHelp();
+ bDragging = false;
+
+ tools::Long nScrPos = GetScrPos( nDragNo );
+ tools::Long nMousePos = bVertical ? rMEvt.GetPosPixel().Y() : rMEvt.GetPosPixel().X();
+ bool bLayoutRTL = IsLayoutRTL();
+ tools::Long nNewWidth = bLayoutRTL ? ( nScrPos - nMousePos + 1 )
+ : ( nMousePos + 2 - nScrPos );
+
+ if ( nNewWidth < 0 /* && !IsSelected(nDragNo) */ )
+ {
+ SCCOLROW nStart = 0;
+ SCCOLROW nEnd = nDragNo;
+ while (nNewWidth < 0)
+ {
+ nStart = nDragNo;
+ if (nDragNo>0)
+ {
+ --nDragNo;
+ nNewWidth += GetEntrySize( nDragNo ); //! GetHiddenCount() ???
+ }
+ else
+ nNewWidth = 0;
+ }
+ HideEntries( nStart, nEnd );
+ }
+ else
+ {
+ if (bDragMoved)
+ SetEntrySize( nDragNo, static_cast<sal_uInt16>(nNewWidth) );
+ }
+ }
+ else
+ {
+ pSelEngine->SelMouseButtonUp( rMEvt );
+ ReleaseMouse();
+ }
+}
+
+void ScHeaderControl::MouseMove( const MouseEvent& rMEvt )
+{
+ if ( IsDisabled() )
+ {
+ SetPointer( PointerStyle::Arrow );
+ return;
+ }
+
+ if ( bInRefMode && rMEvt.IsLeft() && SC_MOD()->IsFormulaMode() )
+ {
+ if( !pTabView )
+ return;
+ bool bTmp;
+ SCCOLROW nHitNo = GetMousePos(rMEvt.GetPosPixel(), bTmp);
+ SCTAB nTab = pTabView->GetViewData().GetTabNo();
+ ScDocument& rDoc = pTabView->GetViewData().GetDocument();
+ if( !bVertical )
+ pTabView->UpdateRef( nHitNo, rDoc.MaxRow(), nTab );
+ else
+ pTabView->UpdateRef( rDoc.MaxCol(), nHitNo, nTab );
+
+ return;
+ }
+
+ if ( bDragging )
+ {
+ tools::Long nNewPos = bVertical ? rMEvt.GetPosPixel().Y() : rMEvt.GetPosPixel().X();
+ if ( nNewPos != nDragPos )
+ {
+ DrawInvert( nDragPos );
+ nDragPos = nNewPos;
+ ShowDragHelp();
+ DrawInvert( nDragPos );
+
+ if (nDragPos <= nDragStart-SC_DRAG_MIN || nDragPos >= nDragStart+SC_DRAG_MIN)
+ bDragMoved = true;
+ }
+ }
+ else
+ {
+ bool bIsBorder;
+ (void)GetMousePos(rMEvt.GetPosPixel(), bIsBorder);
+
+ if ( bIsBorder && rMEvt.GetButtons()==0 && ResizeAllowed() )
+ SetPointer( bVertical ? PointerStyle::VSizeBar : PointerStyle::HSizeBar );
+ else
+ SetPointer( PointerStyle::Arrow );
+
+ if (!bIgnoreMove)
+ pSelEngine->SelMouseMove( rMEvt );
+ }
+}
+
+void ScHeaderControl::Tracking( const TrackingEvent& rTEvt )
+{
+ // Distribute the tracking events to the various MouseEvents, because
+ // SelectionEngine does not know anything about Tracking
+
+ if ( rTEvt.IsTrackingCanceled() )
+ StopMarking();
+ else if ( rTEvt.IsTrackingEnded() )
+ MouseButtonUp( rTEvt.GetMouseEvent() );
+ else
+ MouseMove( rTEvt.GetMouseEvent() );
+}
+
+void ScHeaderControl::Command( const CommandEvent& rCEvt )
+{
+ CommandEventId nCmd = rCEvt.GetCommand();
+ if ( nCmd == CommandEventId::ContextMenu )
+ {
+ StopMarking(); // finish selection / dragging
+
+ // execute popup menu
+
+ ScTabViewShell* pViewSh = dynamic_cast< ScTabViewShell *>( SfxViewShell::Current() );
+ if ( pViewSh )
+ {
+ if ( rCEvt.IsMouseEvent() )
+ {
+ // #i18735# select the column/row under the mouse pointer
+ ScViewData& rViewData = pViewSh->GetViewData();
+
+ SelectWindow(); // also deselects drawing objects, stops draw text edit
+ if ( rViewData.HasEditView( rViewData.GetActivePart() ) )
+ SC_MOD()->InputEnterHandler(); // always end edit mode
+
+ bool bBorder;
+ SCCOLROW nPos = GetMousePos(rCEvt.GetMousePosPixel(), bBorder );
+ if (!IsSelectionAllowed(nPos))
+ // Selecting this cell is not allowed, neither is context menu.
+ return;
+
+ SCTAB nTab = rViewData.GetTabNo();
+ ScDocument& rDoc = pViewSh->GetViewData().GetDocument();
+ ScRange aNewRange;
+ if ( bVertical )
+ aNewRange = ScRange( 0, sal::static_int_cast<SCROW>(nPos), nTab,
+ rDoc.MaxCol(), sal::static_int_cast<SCROW>(nPos), nTab );
+ else
+ aNewRange = ScRange( sal::static_int_cast<SCCOL>(nPos), 0, nTab,
+ sal::static_int_cast<SCCOL>(nPos), rDoc.MaxRow(), nTab );
+
+ // see if any part of the range is already selected
+ ScRangeList aRanges;
+ rViewData.GetMarkData().FillRangeListWithMarks( &aRanges, false );
+ bool bSelected = aRanges.Intersects(aNewRange);
+
+ // select the range if no part of it was selected
+ if ( !bSelected )
+ pViewSh->MarkRange( aNewRange );
+ }
+
+ pViewSh->GetDispatcher()->ExecutePopup( bVertical ? OUString( "rowheader" ) : OUString( "colheader" ) );
+ }
+ }
+ else if ( nCmd == CommandEventId::StartDrag )
+ {
+ pSelEngine->Command( rCEvt );
+ }
+}
+
+void ScHeaderControl::StopMarking()
+{
+ if ( bDragging )
+ {
+ DrawInvert( nDragPos );
+ HideDragHelp();
+ bDragging = false;
+ }
+
+ SetMarking( false );
+ bIgnoreMove = true;
+
+ // don't call pSelEngine->Reset, so selection across the parts of
+ // a split/frozen view is possible
+ if (IsMouseCaptured())
+ ReleaseMouse();
+}
+
+IMPL_LINK_NOARG(ScHeaderControl, ShowDragHelpHdl, Timer*, void)
+{
+ ShowDragHelp();
+}
+
+void ScHeaderControl::ShowDragHelp()
+{
+ aShowHelpTimer.Stop();
+ if (!Help::IsQuickHelpEnabled())
+ return;
+
+ tools::Long nScrPos = GetScrPos( nDragNo );
+ bool bLayoutRTL = IsLayoutRTL();
+ tools::Long nVal = bLayoutRTL ? ( nScrPos - nDragPos + 1 )
+ : ( nDragPos + 2 - nScrPos );
+
+ OUString aHelpStr = GetDragHelp( nVal );
+ Point aPos = OutputToScreenPixel( Point(0,0) );
+ Size aSize = GetSizePixel();
+
+ Point aMousePos = OutputToScreenPixel(GetPointerPosPixel());
+
+ tools::Rectangle aRect;
+ QuickHelpFlags nAlign;
+ if (!bVertical)
+ {
+ // above
+ aRect.SetLeft( aMousePos.X() );
+ aRect.SetTop( aPos.Y() - 4 );
+ nAlign = QuickHelpFlags::Bottom|QuickHelpFlags::Center;
+ }
+ else
+ {
+ // top right
+ aRect.SetLeft( aPos.X() + aSize.Width() + 8 );
+ aRect.SetTop( aMousePos.Y() - 2 );
+ nAlign = QuickHelpFlags::Left|QuickHelpFlags::Bottom;
+ }
+
+ aRect.SetRight( aRect.Left() );
+ aRect.SetBottom( aRect.Top() );
+
+ if (nTipVisible)
+ Help::HidePopover(this, nTipVisible);
+ nTipVisible = Help::ShowPopover(this, aRect, aHelpStr, nAlign);
+}
+
+void ScHeaderControl::HideDragHelp()
+{
+ aShowHelpTimer.Stop();
+ if (nTipVisible)
+ {
+ Help::HidePopover(this, nTipVisible);
+ nTipVisible = nullptr;
+ }
+}
+
+void ScHeaderControl::RequestHelp( const HelpEvent& rHEvt )
+{
+ // If the own QuickHelp is displayed, don't let RequestHelp remove it
+
+ bool bOwn = bDragging && Help::IsQuickHelpEnabled();
+ if (!bOwn)
+ Window::RequestHelp(rHEvt);
+}
+
+// dummies for virtual methods
+
+SCCOLROW ScHeaderControl::GetHiddenCount( SCCOLROW nEntryNo ) const
+{
+ SCCOLROW nHidden = 0;
+ while ( nEntryNo < nSize && GetEntrySize( nEntryNo ) == 0 )
+ {
+ ++nEntryNo;
+ ++nHidden;
+ }
+ return nHidden;
+}
+
+bool ScHeaderControl::IsLayoutRTL() const
+{
+ return false;
+}
+
+bool ScHeaderControl::IsMirrored() const
+{
+ return false;
+}
+
+bool ScHeaderControl::IsDisabled() const
+{
+ return false;
+}
+
+bool ScHeaderControl::ResizeAllowed() const
+{
+ return true;
+}
+
+void ScHeaderControl::SelectWindow()
+{
+}
+
+void ScHeaderControl::DrawInvert( tools::Long /* nDragPos */ )
+{
+}
+
+OUString ScHeaderControl::GetDragHelp( tools::Long /* nVal */ )
+{
+ return OUString();
+}
+
+void ScHeaderControl::SetMarking( bool /* bSet */ )
+{
+}
+
+void ScHeaderControl::GetMarkRange(SCCOLROW& rStart, SCCOLROW& rEnd) const
+{
+ rStart = nMarkStart;
+ rEnd = nMarkEnd;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/hintwin.cxx b/sc/source/ui/view/hintwin.cxx
new file mode 100644
index 0000000000..d57d8e0b78
--- /dev/null
+++ b/sc/source/ui/view/hintwin.cxx
@@ -0,0 +1,182 @@
+/* -*- 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 .
+ */
+
+#include <overlayobject.hxx>
+
+#include <drawinglayer/attribute/fontattribute.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
+#include <drawinglayer/primitive2d/textprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <tools/lineend.hxx>
+#include <utility>
+#include <vcl/outdev.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/metric.hxx>
+#include <sal/log.hxx>
+
+#define HINT_LINESPACE 2
+#define HINT_INDENT 3
+#define HINT_MARGIN 4
+
+ScOverlayHint::ScOverlayHint(OUString aTit, const OUString& rMsg, const Color& rColor, vcl::Font aFont)
+ : OverlayObject(rColor)
+ , m_aTitle(std::move(aTit))
+ , m_aMessage(convertLineEnd(rMsg, LINEEND_CR))
+ , m_aTextFont(std::move(aFont))
+ , m_aMapMode(MapUnit::MapPixel)
+ , m_nLeft(0)
+ , m_nTop(0)
+{
+}
+
+drawinglayer::primitive2d::Primitive2DContainer ScOverlayHint::createOverlaySequence(sal_Int32 nLeft, sal_Int32 nTop,
+ const MapMode &rMapMode,
+ basegfx::B2DRange &rRange) const
+{
+ OutputDevice* pDefaultDev = Application::GetDefaultDevice();
+ MapMode aOld = pDefaultDev->GetMapMode();
+ pDefaultDev->SetMapMode(rMapMode);
+
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ const Color& rColor = rStyleSettings.GetLabelTextColor();
+ vcl::Font aTextFont = m_aTextFont;
+ aTextFont.SetFontSize(pDefaultDev->PixelToLogic(aTextFont.GetFontSize(), rMapMode));
+ vcl::Font aHeadFont = aTextFont;
+ aHeadFont.SetWeight(WEIGHT_BOLD);
+
+ // Create the text primitive for the title
+ basegfx::B2DVector aFontSize;
+ drawinglayer::attribute::FontAttribute aFontAttr =
+ drawinglayer::primitive2d::getFontAttributeFromVclFont(aFontSize, aHeadFont, false, false);
+
+ FontMetric aFontMetric = pDefaultDev->GetFontMetric(aHeadFont);
+ Size aHintMargin = pDefaultDev->PixelToLogic(Size(HINT_MARGIN, HINT_MARGIN), rMapMode);
+ Size aIndent = pDefaultDev->PixelToLogic(Size(HINT_INDENT, HINT_LINESPACE), rMapMode);
+ double nTextOffsetY = nTop + aHintMargin.Height() + aFontMetric.GetAscent();
+ Point aTextPos(nLeft + aHintMargin.Width() , nTextOffsetY);
+ rRange = basegfx::B2DRange(nLeft, nTop, nLeft + aHintMargin.Width(), nTop + aHintMargin.Height());
+
+ basegfx::B2DHomMatrix aTextMatrix(basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aFontSize.getX(), aFontSize.getY(),
+ aTextPos.X(), aTextPos.Y()));
+
+ rtl::Reference<drawinglayer::primitive2d::TextSimplePortionPrimitive2D> pTitle =
+ new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
+ aTextMatrix, m_aTitle, 0, m_aTitle.getLength(),
+ std::vector<double>(), {}, std::move(aFontAttr), css::lang::Locale(),
+ rColor.getBColor());
+
+ Point aTextStart(nLeft + aHintMargin.Width() + aIndent.Width(),
+ nTop + aHintMargin.Height() + aFontMetric.GetLineHeight() + aIndent.Height());
+
+ drawinglayer::geometry::ViewInformation2D aDummy;
+ rRange.expand(pTitle->getB2DRange(aDummy));
+
+ drawinglayer::primitive2d::Primitive2DContainer aSeq { pTitle };
+
+ aFontMetric = pDefaultDev->GetFontMetric(aTextFont);
+ pDefaultDev->SetMapMode(aOld);
+
+ nTextOffsetY = aFontMetric.GetAscent();
+ sal_Int32 nLineHeight = aFontMetric.GetLineHeight();
+
+ aFontAttr = drawinglayer::primitive2d::getFontAttributeFromVclFont(aFontSize, aTextFont, false, false);
+
+ sal_Int32 nIndex = 0;
+ Point aLineStart = aTextStart;
+ sal_Int32 nLineCount = 0;
+ while (nIndex != -1)
+ {
+ OUString aLine = m_aMessage.getToken( 0, '\r', nIndex );
+ if (aLine.getLength() > 255)
+ {
+ // prevent silliness higher up from hanging up the program
+ SAL_WARN("sc", "ridiculously long line, truncating, len=" << aLine.getLength());
+ aLine = aLine.copy(0,255);
+ }
+
+ aTextMatrix = basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aFontSize.getX(), aFontSize.getY(),
+ aLineStart.X(), aLineStart.Y() + nTextOffsetY);
+
+ // Create the text primitive for each line of text
+ rtl::Reference<drawinglayer::primitive2d::TextSimplePortionPrimitive2D> pMessage =
+ new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
+ aTextMatrix, aLine, 0, aLine.getLength(),
+ std::vector<double>(), {}, aFontAttr, css::lang::Locale(),
+ rColor.getBColor());
+
+ rRange.expand(pMessage->getB2DRange(aDummy));
+
+ aSeq.push_back(pMessage);
+
+ aLineStart.AdjustY(nLineHeight );
+ nLineCount++;
+ if (nLineCount > 50)
+ {
+ // prevent silliness higher up from hanging up the program
+ SAL_WARN("sc", "ridiculously long message, bailing out");
+ break;
+ }
+ }
+
+ rRange.expand(basegfx::B2DTuple(rRange.getMaxX() + aHintMargin.Width(),
+ rRange.getMaxY() + aHintMargin.Height()));
+
+ basegfx::B2DPolygon aPoly(basegfx::utils::createPolygonFromRect(rRange));
+
+ const drawinglayer::primitive2d::Primitive2DReference aBg(
+ new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(basegfx::B2DPolyPolygon(aPoly), getBaseColor().getBColor()));
+
+ basegfx::BColor aBorderColor(0.5, 0.5, 0.5);
+ const drawinglayer::primitive2d::Primitive2DReference aBorder(
+ new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
+ std::move(aPoly), aBorderColor));
+
+ aSeq.insert(aSeq.begin(), aBorder);
+ aSeq.insert(aSeq.begin(), aBg);
+
+ return aSeq;
+}
+
+drawinglayer::primitive2d::Primitive2DContainer ScOverlayHint::createOverlayObjectPrimitive2DSequence()
+{
+ basegfx::B2DRange aRange;
+ return createOverlaySequence(m_nLeft, m_nTop, m_aMapMode, aRange);
+}
+
+Size ScOverlayHint::GetSizePixel() const
+{
+ basegfx::B2DRange aRange;
+ createOverlaySequence(0, 0, MapMode(MapUnit::MapPixel), aRange);
+ return Size(aRange.getWidth(), aRange.getHeight());
+}
+
+void ScOverlayHint::SetPos(const Point& rPos, const MapMode& rMode)
+{
+ m_nLeft = rPos.X();
+ m_nTop = rPos.Y();
+ m_aMapMode = rMode;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/imapwrap.cxx b/sc/source/ui/view/imapwrap.cxx
new file mode 100644
index 0000000000..72d1373393
--- /dev/null
+++ b/sc/source/ui/view/imapwrap.cxx
@@ -0,0 +1,48 @@
+/* -*- 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 .
+ */
+
+#include <svx/imapdlg.hxx>
+
+#include "imapwrap.hxx"
+
+sal_uInt16 ScIMapChildWindowId()
+{
+ return SvxIMapDlgChildWindow::GetChildWindowId();
+}
+
+void ScIMapDlgSet( const Graphic& rGraphic, const ImageMap* pImageMap,
+ const TargetList* pTargetList, void* pEditingObj )
+{
+ SvxIMapDlgChildWindow::UpdateIMapDlg( rGraphic, pImageMap, pTargetList, pEditingObj );
+}
+
+const void* ScIMapDlgGetObj( const SvxIMapDlg* pDlg )
+{
+ if ( pDlg )
+ return pDlg->GetEditingObject();
+ else
+ return nullptr;
+}
+
+const ImageMap& ScIMapDlgGetMap( const SvxIMapDlg* pDlg )
+{
+ return pDlg->GetImageMap();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/imapwrap.hxx b/sc/source/ui/view/imapwrap.hxx
new file mode 100644
index 0000000000..9b46b1bc83
--- /dev/null
+++ b/sc/source/ui/view/imapwrap.hxx
@@ -0,0 +1,40 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+
+#include <sal/types.h>
+#include <sfx2/frame.hxx>
+
+class Graphic;
+class ImageMap;
+class SvxIMapDlg;
+
+sal_uInt16 ScIMapChildWindowId();
+
+ImageMap const & ScIMapDlgGetMap(const SvxIMapDlg * pDlg);
+
+void const * ScIMapDlgGetObj(const SvxIMapDlg * pDlg);
+
+void ScIMapDlgSet(
+ Graphic const & rGraphic, ImageMap const * pImageMap,
+ TargetList const * pTargetList, void * pEditingObj);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/invmerge.cxx b/sc/source/ui/view/invmerge.cxx
new file mode 100644
index 0000000000..a082221977
--- /dev/null
+++ b/sc/source/ui/view/invmerge.cxx
@@ -0,0 +1,162 @@
+/* -*- 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 .
+ */
+
+#include <osl/diagnose.h>
+
+#include <invmerge.hxx>
+
+ScInvertMerger::ScInvertMerger( ::std::vector< tools::Rectangle >* pRectangles ) :
+ pRects( pRectangles )
+{
+ // collect rectangles instead of inverting
+}
+
+ScInvertMerger::~ScInvertMerger()
+{
+ Flush();
+}
+
+void ScInvertMerger::Flush()
+{
+ FlushLine();
+ FlushTotal();
+
+ OSL_ENSURE( aLineRect.IsEmpty() && aTotalRect.IsEmpty(), "Flush: not empty" );
+
+ if ( !pRects )
+ return;
+
+ // also join vertically if there are non-adjacent columns involved
+
+ size_t nComparePos = 0;
+ while ( nComparePos < pRects->size() )
+ {
+ tools::Rectangle aCompRect = (*pRects)[nComparePos];
+ sal_Int32 nBottom = aCompRect.Bottom();
+ size_t nOtherPos = nComparePos + 1;
+
+ while ( nOtherPos < pRects->size() )
+ {
+ tools::Rectangle aOtherRect = (*pRects)[nOtherPos];
+ if ( aOtherRect.Top() > nBottom + 1 )
+ {
+ // rectangles are sorted, so we can stop searching
+ break;
+ }
+ if ( aOtherRect.Top() == nBottom + 1 &&
+ aOtherRect.Left() == aCompRect.Left() &&
+ aOtherRect.Right() == aCompRect.Right() )
+ {
+ // extend first rectangle
+ nBottom = aOtherRect.Bottom();
+ aCompRect.SetBottom( nBottom );
+ (*pRects)[nComparePos].SetBottom( nBottom );
+
+ // remove second rectangle
+ pRects->erase( pRects->begin() + nOtherPos );
+
+ // continue at unmodified nOtherPos
+ }
+ else
+ ++nOtherPos;
+ }
+
+ ++nComparePos;
+ }
+}
+
+void ScInvertMerger::FlushTotal()
+{
+ if( aTotalRect.IsEmpty() )
+ return; // nothing to do
+
+ if ( pRects )
+ pRects->push_back( aTotalRect );
+
+ aTotalRect.SetEmpty();
+}
+
+void ScInvertMerger::FlushLine()
+{
+ if( aLineRect.IsEmpty() )
+ return; // nothing to do
+
+ if ( aTotalRect.IsEmpty() )
+ {
+ aTotalRect = aLineRect; // start new total rect
+ }
+ else
+ {
+ if ( aLineRect.Left() == aTotalRect.Left() &&
+ aLineRect.Right() == aTotalRect.Right() &&
+ aLineRect.Top() == aTotalRect.Bottom() + 1 )
+ {
+ // extend total rect
+ aTotalRect.SetBottom( aLineRect.Bottom() );
+ }
+ else
+ {
+ FlushTotal(); // draw old total rect
+ aTotalRect = aLineRect; // and start new one
+ }
+ }
+
+ aLineRect.SetEmpty();
+}
+
+void ScInvertMerger::AddRect( const tools::Rectangle& rRect )
+{
+ tools::Rectangle aJustified = rRect;
+ if ( rRect.Left() > rRect.Right() ) // switch for RTL layout
+ {
+ aJustified.SetLeft( rRect.Right() );
+ aJustified.SetRight( rRect.Left() );
+ }
+
+ if ( aLineRect.IsEmpty() )
+ {
+ aLineRect = aJustified; // start new line rect
+ }
+ else
+ {
+ bool bDone = false;
+ if ( aJustified.Top() == aLineRect.Top() &&
+ aJustified.Bottom() == aLineRect.Bottom() )
+ {
+ // try to extend line rect
+ if ( aJustified.Left() == aLineRect.Right() + 1 )
+ {
+ aLineRect.SetRight( aJustified.Right() );
+ bDone = true;
+ }
+ else if ( aJustified.Right() + 1 == aLineRect.Left() ) // for RTL layout
+ {
+ aLineRect.SetLeft( aJustified.Left() );
+ bDone = true;
+ }
+ }
+ if (!bDone)
+ {
+ FlushLine(); // use old line rect for total rect
+ aLineRect = aJustified; // and start new one
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/notemark.cxx b/sc/source/ui/view/notemark.cxx
new file mode 100644
index 0000000000..2824a453e7
--- /dev/null
+++ b/sc/source/ui/view/notemark.cxx
@@ -0,0 +1,203 @@
+/* -*- 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 .
+ */
+
+#include <svx/svdoutl.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdocapt.hxx>
+#include <svl/itempool.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/window.hxx>
+
+#include <notemark.hxx>
+#include <document.hxx>
+#include <postit.hxx>
+
+#define SC_NOTEMARK_TIME 800
+#define SC_NOTEMARK_SHORT 70
+
+ScNoteMarker::ScNoteMarker( vcl::Window* pWin, vcl::Window* pRight, vcl::Window* pBottom, vcl::Window* pDiagonal,
+ ScDocument* pD, const ScAddress& aPos, OUString aUser,
+ const MapMode& rMap, bool bLeftEdge, bool bForce, bool bKeyboard) :
+ m_pWindow( pWin ),
+ m_pRightWin( pRight ),
+ m_pBottomWin( pBottom ),
+ m_pDiagWin( pDiagonal ),
+ m_pDoc( pD ),
+ m_aDocPos( aPos ),
+ m_aUserText(std::move( aUser )),
+ m_aTimer("ScNoteMarker m_aTimer"),
+ m_aMapMode( rMap ),
+ m_bLeft( bLeftEdge ),
+ m_bByKeyboard( bKeyboard ),
+ m_bVisible( false )
+{
+ Size aSizePixel = m_pWindow->GetOutputSizePixel();
+ if( m_pRightWin )
+ aSizePixel.AdjustWidth(m_pRightWin->GetOutputSizePixel().Width() );
+ if( m_pBottomWin )
+ aSizePixel.AdjustHeight(m_pBottomWin->GetOutputSizePixel().Height() );
+ tools::Rectangle aVisPixel( Point( 0, 0 ), aSizePixel );
+ m_aVisRect = m_pWindow->PixelToLogic( aVisPixel, m_aMapMode );
+
+ m_aTimer.SetInvokeHandler( LINK( this, ScNoteMarker, TimeHdl ) );
+ m_aTimer.SetTimeout( bForce ? SC_NOTEMARK_SHORT : SC_NOTEMARK_TIME );
+ m_aTimer.Start();
+}
+
+ScNoteMarker::~ScNoteMarker()
+{
+ m_xObject.clear();
+
+ InvalidateWin();
+
+ m_pModel.reset();
+}
+
+IMPL_LINK_NOARG(ScNoteMarker, TimeHdl, Timer *, void)
+{
+ if (!m_bVisible)
+ {
+ m_pModel.reset( new SdrModel() );
+ m_pModel->SetScaleUnit(MapUnit::Map100thMM);
+ SfxItemPool& rPool = m_pModel->GetItemPool();
+ rPool.SetDefaultMetric(MapUnit::Map100thMM);
+ rPool.FreezeIdRanges();
+
+ OutputDevice* pPrinter = m_pDoc->GetRefDevice();
+ if (pPrinter)
+ {
+ // On the outliner of the draw model also the printer is set as RefDevice,
+ // and it should look uniform.
+ Outliner& rOutliner = m_pModel->GetDrawOutliner();
+ rOutliner.SetRefDevice(pPrinter);
+ }
+
+ if( rtl::Reference<SdrPage> pPage = m_pModel->AllocPage( false ) )
+
+ {
+ m_xObject = ScNoteUtil::CreateTempCaption( *m_pDoc, m_aDocPos, *pPage, m_aUserText, m_aVisRect, m_bLeft );
+ if( m_xObject )
+ {
+ // Here, SyncForGrid and GetGridOffset was used with the comment:
+ // // Need to include grid offset: GetCurrentBoundRect is removing it
+ // // but we need to know actual rect position
+ // This is no longer true - SdrObject::RecalcBoundRect() uses the
+ // GetViewContact().getViewIndependentPrimitive2DContainer()) call
+ // that now by default adds the eventually needed GridOffset. Thus
+ // I have removed that adaptation stuff.
+ m_aRect = m_xObject->GetCurrentBoundRect();
+ }
+
+ // Insert page so that the model recognise it and also deleted
+ m_pModel->InsertPage( pPage.get() );
+
+ }
+ m_bVisible = true;
+ }
+
+ Draw();
+}
+
+static void lcl_DrawWin( const SdrObject* pObject, vcl::RenderContext* pWindow, const MapMode& rMap )
+{
+ MapMode aOld = pWindow->GetMapMode();
+ pWindow->SetMapMode( rMap );
+
+ DrawModeFlags nOldDrawMode = pWindow->GetDrawMode();
+ if ( Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
+ {
+ pWindow->SetDrawMode( nOldDrawMode | DrawModeFlags::SettingsLine | DrawModeFlags::SettingsFill |
+ DrawModeFlags::SettingsText | DrawModeFlags::SettingsGradient );
+ }
+
+ pObject->SingleObjectPainter( *pWindow ); // #110094#-17
+
+ pWindow->SetDrawMode( nOldDrawMode );
+ pWindow->SetMapMode( aOld );
+}
+
+static MapMode lcl_MoveMapMode( const MapMode& rMap, const Size& rMove )
+{
+ MapMode aNew = rMap;
+ Point aOrigin = aNew.GetOrigin();
+ aOrigin.AdjustX( -(rMove.Width()) );
+ aOrigin.AdjustY( -rMove.Height() );
+ aNew.SetOrigin(aOrigin);
+ return aNew;
+}
+
+void ScNoteMarker::Draw()
+{
+ if ( !(m_xObject && m_bVisible) )
+ return;
+
+ lcl_DrawWin( m_xObject.get(), m_pWindow->GetOutDev(), m_aMapMode );
+
+ if ( m_pRightWin || m_pBottomWin )
+ {
+ Size aWinSize = m_pWindow->PixelToLogic( m_pWindow->GetOutputSizePixel(), m_aMapMode );
+ if ( m_pRightWin )
+ lcl_DrawWin( m_xObject.get(), m_pRightWin->GetOutDev(),
+ lcl_MoveMapMode( m_aMapMode, Size( aWinSize.Width(), 0 ) ) );
+ if ( m_pBottomWin )
+ lcl_DrawWin( m_xObject.get(), m_pBottomWin->GetOutDev(),
+ lcl_MoveMapMode( m_aMapMode, Size( 0, aWinSize.Height() ) ) );
+ if ( m_pDiagWin )
+ lcl_DrawWin( m_xObject.get(), m_pDiagWin->GetOutDev(), lcl_MoveMapMode( m_aMapMode, aWinSize ) );
+ }
+}
+
+void ScNoteMarker::InvalidateWin()
+{
+ if (!m_bVisible)
+ return;
+
+ // Extend the invalidated rectangle by 1 pixel in each direction in case AA would slightly
+ // paint outside the nominal area.
+ tools::Rectangle aRect(m_aRect);
+ const Size aPixelSize = m_pWindow->PixelToLogic(Size(1, 1));
+ aRect.AdjustLeft(-aPixelSize.getWidth());
+ aRect.AdjustTop(-aPixelSize.getHeight());
+ aRect.AdjustRight(aPixelSize.getWidth());
+ aRect.AdjustBottom(aPixelSize.getHeight());
+
+ m_pWindow->Invalidate( OutputDevice::LogicToLogic(aRect, m_aMapMode, m_pWindow->GetMapMode()) );
+
+ if ( !(m_pRightWin || m_pBottomWin) )
+ return;
+
+ Size aWinSize = m_pWindow->PixelToLogic( m_pWindow->GetOutputSizePixel(), m_aMapMode );
+ if ( m_pRightWin )
+ m_pRightWin->Invalidate( OutputDevice::LogicToLogic(aRect,
+ lcl_MoveMapMode( m_aMapMode, Size( aWinSize.Width(), 0 ) ),
+ m_pRightWin->GetMapMode()) );
+ if ( m_pBottomWin )
+ m_pBottomWin->Invalidate( OutputDevice::LogicToLogic(aRect,
+ lcl_MoveMapMode( m_aMapMode, Size( 0, aWinSize.Height() ) ),
+ m_pBottomWin->GetMapMode()) );
+ if ( m_pDiagWin )
+ m_pDiagWin->Invalidate( OutputDevice::LogicToLogic(aRect,
+ lcl_MoveMapMode( m_aMapMode, aWinSize ),
+ m_pDiagWin->GetMapMode()) );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/olinewin.cxx b/sc/source/ui/view/olinewin.cxx
new file mode 100644
index 0000000000..1cc55a9f3f
--- /dev/null
+++ b/sc/source/ui/view/olinewin.cxx
@@ -0,0 +1,1040 @@
+/* -*- 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 .
+ */
+
+#include <sal/config.h>
+
+#include <vcl/image.hxx>
+#include <vcl/taskpanelist.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/syswin.hxx>
+#include <osl/diagnose.h>
+
+#include <olinewin.hxx>
+#include <olinetab.hxx>
+#include <document.hxx>
+#include <dbfunc.hxx>
+#include <bitmaps.hlst>
+
+const tools::Long SC_OL_BITMAPSIZE = 12;
+const tools::Long SC_OL_POSOFFSET = 2;
+
+const size_t SC_OL_NOLEVEL = static_cast< size_t >( -1 );
+const size_t SC_OL_HEADERENTRY = static_cast< size_t >( -1 );
+
+ScOutlineWindow::ScOutlineWindow( vcl::Window* pParent, ScOutlineMode eMode, ScViewData* pViewData, ScSplitPos eWhich ) :
+ Window( pParent ),
+ mrViewData( *pViewData ),
+ meWhich( eWhich ),
+ mbHoriz( eMode == SC_OUTLINE_HOR ),
+ mbMirrorEntries( false ), // updated in SetHeaderSize
+ mbMirrorLevels( false ), // updated in SetHeaderSize
+ maLineColor( COL_BLACK ),
+ mnHeaderSize( 0 ),
+ mnHeaderPos( 0 ),
+ mnMainFirstPos( 0 ),
+ mnMainLastPos( 0 ),
+ mbMTActive( false ),
+ mbMTPressed( false ),
+ mnFocusLevel( 0 ),
+ mnFocusEntry( SC_OL_HEADERENTRY ),
+ mbDontDrawFocus( false )
+{
+ EnableRTL( false ); // mirroring is done manually
+
+ InitSettings();
+ maFocusRect.SetEmpty();
+ SetHeaderSize( 0 );
+
+ // insert the window into task pane list for "F6 cycling"
+ if( SystemWindow* pSysWin = GetSystemWindow() )
+ if( TaskPaneList* pTaskPaneList = pSysWin->GetTaskPaneList() )
+ pTaskPaneList->AddWindow( this );
+}
+
+ScOutlineWindow::~ScOutlineWindow()
+{
+ disposeOnce();
+}
+
+void ScOutlineWindow::dispose()
+{
+ // remove the window from task pane list
+ if( SystemWindow* pSysWin = GetSystemWindow() )
+ if( TaskPaneList* pTaskPaneList = pSysWin->GetTaskPaneList() )
+ pTaskPaneList->RemoveWindow( this );
+ vcl::Window::dispose();
+}
+
+void ScOutlineWindow::SetHeaderSize( tools::Long nNewSize )
+{
+ bool bLayoutRTL = GetDoc().IsLayoutRTL( GetTab() );
+ mbMirrorEntries = bLayoutRTL && mbHoriz;
+ mbMirrorLevels = bLayoutRTL && !mbHoriz;
+
+ bool bNew = (nNewSize != mnHeaderSize);
+ mnHeaderSize = nNewSize;
+ mnHeaderPos = mbMirrorEntries ? (GetOutputSizeEntry() - mnHeaderSize) : 0;
+ mnMainFirstPos = mbMirrorEntries ? 0 : mnHeaderSize;
+ mnMainLastPos = GetOutputSizeEntry() - (mbMirrorEntries ? mnHeaderSize : 0) - 1;
+ if ( bNew )
+ Invalidate();
+}
+
+tools::Long ScOutlineWindow::GetDepthSize() const
+{
+ tools::Long nSize = GetLevelCount() * SC_OL_BITMAPSIZE;
+ if ( nSize > 0 )
+ nSize += 2 * SC_OL_POSOFFSET + 1;
+ return nSize;
+}
+
+void ScOutlineWindow::ScrollPixel( tools::Long nDiff )
+{
+ HideFocus();
+ mbDontDrawFocus = true;
+
+ tools::Long nStart = mnMainFirstPos;
+ tools::Long nEnd = mnMainLastPos;
+
+ tools::Long nInvStart, nInvEnd;
+ if (nDiff < 0)
+ {
+ nStart -= nDiff;
+ nInvStart = nEnd + nDiff;
+ nInvEnd = nEnd;
+ }
+ else
+ {
+ nEnd -= nDiff;
+ nInvStart = nStart;
+ nInvEnd = nStart + nDiff;
+ }
+
+ ScrollRel( nDiff, nStart, nEnd );
+ Invalidate( GetRectangle( 0, nInvStart, GetOutputSizeLevel() - 1, nInvEnd ) );
+
+ // if focus becomes invisible, move it to next visible button
+ ImplMoveFocusToVisible( nDiff < 0 );
+
+ mbDontDrawFocus = false;
+ ShowFocus();
+}
+
+void ScOutlineWindow::ScrollRel( tools::Long nEntryDiff, tools::Long nEntryStart, tools::Long nEntryEnd )
+{
+ tools::Rectangle aRect( GetRectangle( 0, nEntryStart, GetOutputSizeLevel() - 1, nEntryEnd ) );
+ if ( mbHoriz )
+ Scroll( nEntryDiff, 0, aRect );
+ else
+ Scroll( 0, nEntryDiff, aRect );
+}
+
+// internal -------------------------------------------------------------------
+
+void ScOutlineWindow::InitSettings()
+{
+ const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
+ SetBackground( rStyleSettings.GetFaceColor() );
+ maLineColor = rStyleSettings.GetButtonTextColor();
+ Invalidate();
+}
+
+const ScOutlineArray* ScOutlineWindow::GetOutlineArray() const
+{
+ const ScOutlineTable* pTable = GetDoc().GetOutlineTable( GetTab() );
+ if ( !pTable ) return nullptr;
+ return mbHoriz ? &pTable->GetColArray() : &pTable->GetRowArray();
+}
+
+const ScOutlineEntry* ScOutlineWindow::GetOutlineEntry( size_t nLevel, size_t nEntry ) const
+{
+ const ScOutlineArray* pArray = GetOutlineArray();
+ return pArray ? pArray->GetEntry( sal::static_int_cast<sal_uInt16>(nLevel), sal::static_int_cast<sal_uInt16>(nEntry) ) : nullptr;
+}
+
+bool ScOutlineWindow::IsHidden( SCCOLROW nColRowIndex ) const
+{
+ return mbHoriz ?
+ GetDoc().ColHidden(static_cast<SCCOL>(nColRowIndex), GetTab()) :
+ GetDoc().RowHidden(static_cast<SCROW>(nColRowIndex), GetTab());
+}
+
+bool ScOutlineWindow::IsFiltered( SCCOLROW nColRowIndex ) const
+{
+ // columns cannot be filtered
+ return !mbHoriz && GetDoc().RowFiltered( static_cast<SCROW>(nColRowIndex), GetTab() );
+}
+
+bool ScOutlineWindow::IsFirstVisible( SCCOLROW nColRowIndex ) const
+{
+ bool bAllHidden = true;
+ for ( SCCOLROW nPos = 0; (nPos < nColRowIndex) && bAllHidden; ++nPos )
+ bAllHidden = IsHidden( nPos );
+ return bAllHidden;
+}
+
+void ScOutlineWindow::GetVisibleRange( SCCOLROW& rnColRowStart, SCCOLROW& rnColRowEnd ) const
+{
+ if ( mbHoriz )
+ {
+ rnColRowStart = mrViewData.GetPosX( WhichH( meWhich ) );
+ rnColRowEnd = rnColRowStart + mrViewData.VisibleCellsX( WhichH( meWhich ) );
+ }
+ else
+ {
+ rnColRowStart = mrViewData.GetPosY( WhichV( meWhich ) );
+ rnColRowEnd = rnColRowStart + mrViewData.VisibleCellsY( WhichV( meWhich ) );
+ }
+
+ // include collapsed columns/rows in front of visible range
+ while ( (rnColRowStart > 0) && IsHidden( rnColRowStart - 1 ) )
+ --rnColRowStart;
+}
+
+Point ScOutlineWindow::GetPoint( tools::Long nLevelPos, tools::Long nEntryPos ) const
+{
+ return mbHoriz ? Point( nEntryPos, nLevelPos ) : Point( nLevelPos, nEntryPos );
+}
+
+tools::Rectangle ScOutlineWindow::GetRectangle(
+ tools::Long nLevelStart, tools::Long nEntryStart, tools::Long nLevelEnd, tools::Long nEntryEnd ) const
+{
+ return tools::Rectangle( GetPoint( nLevelStart, nEntryStart ), GetPoint( nLevelEnd, nEntryEnd ) );
+}
+
+tools::Long ScOutlineWindow::GetOutputSizeLevel() const
+{
+ Size aSize( GetOutputSizePixel() );
+ return mbHoriz ? aSize.Height() : aSize.Width();
+}
+
+tools::Long ScOutlineWindow::GetOutputSizeEntry() const
+{
+ Size aSize( GetOutputSizePixel() );
+ return mbHoriz ? aSize.Width() : aSize.Height();
+}
+
+size_t ScOutlineWindow::GetLevelCount() const
+{
+ const ScOutlineArray* pArray = GetOutlineArray();
+ size_t nLevelCount = pArray ? pArray->GetDepth() : 0;
+ return nLevelCount ? (nLevelCount + 1) : 0;
+}
+
+tools::Long ScOutlineWindow::GetLevelPos( size_t nLevel ) const
+{
+ // #i51970# must always return the *left* edge of the area used by a level
+ tools::Long nPos = static_cast< tools::Long >( SC_OL_POSOFFSET + nLevel * SC_OL_BITMAPSIZE );
+ return mbMirrorLevels ? (GetOutputSizeLevel() - nPos - SC_OL_BITMAPSIZE) : nPos;
+}
+
+size_t ScOutlineWindow::GetLevelFromPos( tools::Long nLevelPos ) const
+{
+ if( mbMirrorLevels ) nLevelPos = GetOutputSizeLevel() - nLevelPos - 1;
+ tools::Long nStart = SC_OL_POSOFFSET;
+ if ( nLevelPos < nStart ) return SC_OL_NOLEVEL;
+ size_t nLevel = static_cast< size_t >( (nLevelPos - nStart) / SC_OL_BITMAPSIZE );
+ return (nLevel < GetLevelCount()) ? nLevel : SC_OL_NOLEVEL;
+}
+
+tools::Long ScOutlineWindow::GetColRowPos( SCCOLROW nColRowIndex ) const
+{
+ tools::Long nDocPos = mbHoriz ?
+ mrViewData.GetScrPos( static_cast<SCCOL>(nColRowIndex), 0, meWhich, true ).X() :
+ mrViewData.GetScrPos( 0, static_cast<SCROW>(nColRowIndex), meWhich, true ).Y();
+ return mnMainFirstPos + nDocPos;
+}
+
+tools::Long ScOutlineWindow::GetHeaderEntryPos() const
+{
+ return mnHeaderPos + (mnHeaderSize - SC_OL_BITMAPSIZE) / 2;
+}
+
+bool ScOutlineWindow::GetEntryPos(
+ size_t nLevel, size_t nEntry,
+ tools::Long& rnStartPos, tools::Long& rnEndPos, tools::Long& rnImagePos ) const
+{
+ const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry );
+ if ( !pEntry || !pEntry->IsVisible() )
+ return false;
+
+ SCCOLROW nStart = pEntry->GetStart();
+ SCCOLROW nEnd = pEntry->GetEnd();
+
+ tools::Long nEntriesSign = mbMirrorEntries ? -1 : 1;
+
+ // --- common calculation ---
+
+ rnStartPos = GetColRowPos( nStart );
+ rnEndPos = GetColRowPos( nEnd + 1 );
+
+ bool bHidden = IsHidden( nStart );
+ rnImagePos = bHidden ?
+ (rnStartPos - ( SC_OL_BITMAPSIZE / 2 ) * nEntriesSign) :
+ rnStartPos + nEntriesSign;
+ tools::Long nCenter = (rnStartPos + rnEndPos - SC_OL_BITMAPSIZE * nEntriesSign +
+ ( mbMirrorEntries ? 1 : 0 )) / 2;
+ rnImagePos = mbMirrorEntries ? std::max( rnImagePos, nCenter ) : std::min( rnImagePos, nCenter );
+
+ // --- refinements ---
+
+ // do not cut leftmost/topmost image
+ if ( bHidden && IsFirstVisible( nStart ) )
+ rnImagePos = rnStartPos;
+
+ // do not cover previous collapsed image
+ bool bDoNoCover = !bHidden && nEntry;
+ const ScOutlineEntry* pPrevEntry = bDoNoCover ? GetOutlineEntry(nLevel, nEntry - 1) : nullptr;
+ if (pPrevEntry)
+ {
+ SCCOLROW nPrevEnd = pPrevEntry->GetEnd();
+ if ( (nPrevEnd + 1 == nStart) && IsHidden( nPrevEnd ) )
+ {
+ if ( IsFirstVisible( pPrevEntry->GetStart() ) )
+ rnStartPos += SC_OL_BITMAPSIZE * nEntriesSign;
+ else
+ rnStartPos += ( SC_OL_BITMAPSIZE / 2 ) * nEntriesSign;
+ rnImagePos = rnStartPos;
+ }
+ }
+
+ // restrict rnStartPos...rnEndPos to valid area
+ rnStartPos = std::max( rnStartPos, mnMainFirstPos );
+ rnEndPos = std::max( rnEndPos, mnMainFirstPos );
+
+ if ( mbMirrorEntries )
+ rnImagePos -= SC_OL_BITMAPSIZE - 1; // start pos aligns with right edge of bitmap
+
+ // --- all rows filtered? ---
+
+ bool bVisible = true;
+ if ( !mbHoriz )
+ {
+ bVisible = false;
+ for ( SCCOLROW nRow = nStart; (nRow <= nEnd) && !bVisible; ++nRow )
+ bVisible = !IsFiltered( nRow );
+ }
+ return bVisible;
+}
+
+bool ScOutlineWindow::GetImagePos( size_t nLevel, size_t nEntry, Point& rPos ) const
+{
+ bool bRet = nLevel < GetLevelCount();
+ if ( bRet )
+ {
+ tools::Long nLevelPos = GetLevelPos( nLevel );
+ if ( nEntry == SC_OL_HEADERENTRY )
+ rPos = GetPoint( nLevelPos, GetHeaderEntryPos() );
+ else
+ {
+ tools::Long nStartPos, nEndPos, nImagePos;
+ bRet = GetEntryPos( nLevel, nEntry, nStartPos, nEndPos, nImagePos );
+ rPos = GetPoint( nLevelPos, nImagePos );
+ }
+ }
+ return bRet;
+}
+
+bool ScOutlineWindow::IsButtonVisible( size_t nLevel, size_t nEntry ) const
+{
+ bool bRet = false;
+ if ( nEntry == SC_OL_HEADERENTRY )
+ bRet = (mnHeaderSize > 0) && (nLevel < GetLevelCount());
+ else
+ {
+ const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry );
+ if ( pEntry && pEntry->IsVisible() )
+ {
+ SCCOLROW nStart, nEnd;
+ GetVisibleRange( nStart, nEnd );
+ bRet = (nStart <= pEntry->GetStart()) && (pEntry->GetStart() <= nEnd);
+ }
+ }
+ return bRet;
+}
+
+bool ScOutlineWindow::ItemHit( const Point& rPos, size_t& rnLevel, size_t& rnEntry, bool& rbButton ) const
+{
+ const ScOutlineArray* pArray = GetOutlineArray();
+ if ( !pArray ) return false;
+
+ SCCOLROW nStartIndex, nEndIndex;
+ GetVisibleRange( nStartIndex, nEndIndex );
+
+ size_t nLevel = GetLevelFromPos( mbHoriz ? rPos.Y() : rPos.X() );
+ if ( nLevel == SC_OL_NOLEVEL )
+ return false;
+
+ tools::Long nEntryMousePos = mbHoriz ? rPos.X() : rPos.Y();
+
+ // --- level buttons ---
+
+ if ( mnHeaderSize > 0 )
+ {
+ tools::Long nImagePos = GetHeaderEntryPos();
+ if ( (nImagePos <= nEntryMousePos) && (nEntryMousePos < nImagePos + SC_OL_BITMAPSIZE) )
+ {
+ rnLevel = nLevel;
+ rnEntry = SC_OL_HEADERENTRY;
+ rbButton = true;
+ return true;
+ }
+ }
+
+ // --- expand/collapse buttons and expanded lines ---
+
+ // search outline entries backwards
+ size_t nEntry = pArray->GetCount( sal::static_int_cast<sal_uInt16>(nLevel) );
+ while ( nEntry )
+ {
+ --nEntry;
+
+ const ScOutlineEntry* pEntry = pArray->GetEntry( sal::static_int_cast<sal_uInt16>(nLevel),
+ sal::static_int_cast<sal_uInt16>(nEntry) );
+ SCCOLROW nStart = pEntry->GetStart();
+ SCCOLROW nEnd = pEntry->GetEnd();
+
+ if ( (nEnd >= nStartIndex) && (nStart <= nEndIndex) )
+ {
+ tools::Long nStartPos, nEndPos, nImagePos;
+ if ( GetEntryPos( nLevel, nEntry, nStartPos, nEndPos, nImagePos ) )
+ {
+ rnLevel = nLevel;
+ rnEntry = nEntry;
+
+ // button?
+ if ( (nStart >= nStartIndex) && (nImagePos <= nEntryMousePos) && (nEntryMousePos < nImagePos + SC_OL_BITMAPSIZE) )
+ {
+ rbButton = true;
+ return true;
+ }
+
+ // line?
+ if ( mbMirrorEntries )
+ ::std::swap( nStartPos, nEndPos ); // in RTL mode, nStartPos is the larger value
+ if ( (nStartPos <= nEntryMousePos) && (nEntryMousePos <= nEndPos) )
+ {
+ rbButton = false;
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+bool ScOutlineWindow::ButtonHit( const Point& rPos, size_t& rnLevel, size_t& rnEntry ) const
+{
+ bool bButton;
+ bool bRet = ItemHit( rPos, rnLevel, rnEntry, bButton );
+ return bRet && bButton;
+}
+
+bool ScOutlineWindow::LineHit( const Point& rPos, size_t& rnLevel, size_t& rnEntry ) const
+{
+ bool bButton;
+ bool bRet = ItemHit( rPos, rnLevel, rnEntry, bButton );
+ return bRet && !bButton;
+}
+
+void ScOutlineWindow::DoFunction( size_t nLevel, size_t nEntry ) const
+{
+ ScDBFunc& rFunc = *mrViewData.GetView();
+ if ( nEntry == SC_OL_HEADERENTRY )
+ rFunc.SelectLevel( mbHoriz, sal::static_int_cast<sal_uInt16>(nLevel) );
+ else
+ {
+ const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry );
+ if ( pEntry )
+ {
+ if ( pEntry->IsHidden() )
+ rFunc.ShowOutline( mbHoriz, sal::static_int_cast<sal_uInt16>(nLevel), sal::static_int_cast<sal_uInt16>(nEntry) );
+ else
+ rFunc.HideOutline( mbHoriz, sal::static_int_cast<sal_uInt16>(nLevel), sal::static_int_cast<sal_uInt16>(nEntry) );
+ }
+ }
+}
+
+void ScOutlineWindow::DoExpand( size_t nLevel, size_t nEntry ) const
+{
+ const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry );
+ if ( pEntry && pEntry->IsHidden() )
+ DoFunction( nLevel, nEntry );
+}
+
+void ScOutlineWindow::DoCollapse( size_t nLevel, size_t nEntry ) const
+{
+ const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry );
+ if ( pEntry && !pEntry->IsHidden() )
+ DoFunction( nLevel, nEntry );
+}
+
+void ScOutlineWindow::Resize()
+{
+ Window::Resize();
+ SetHeaderSize( mnHeaderSize ); // recalculates header/group positions
+ if ( !IsFocusButtonVisible() )
+ {
+ HideFocus();
+ ShowFocus(); // calculates valid position
+ }
+}
+
+void ScOutlineWindow::DataChanged( const DataChangedEvent& rDCEvt )
+{
+ if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
+ (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) )
+ {
+ InitSettings();
+ Invalidate();
+ }
+ Window::DataChanged( rDCEvt );
+}
+
+// drawing --------------------------------------------------------------------
+
+void ScOutlineWindow::SetEntryAreaClipRegion()
+{
+ GetOutDev()->SetClipRegion( vcl::Region(tools::Rectangle(
+ GetPoint( 0, mnMainFirstPos ),
+ GetPoint( GetOutputSizeLevel() - 1, mnMainLastPos ))));
+}
+
+void ScOutlineWindow::DrawLineRel(
+ tools::Long nLevelStart, tools::Long nEntryStart, tools::Long nLevelEnd, tools::Long nEntryEnd )
+{
+ GetOutDev()->DrawLine( GetPoint( nLevelStart, nEntryStart ), GetPoint( nLevelEnd, nEntryEnd ) );
+}
+
+void ScOutlineWindow::DrawRectRel(
+ tools::Long nLevelStart, tools::Long nEntryStart, tools::Long nLevelEnd, tools::Long nEntryEnd )
+{
+ GetOutDev()->DrawRect( GetRectangle( nLevelStart, nEntryStart, nLevelEnd, nEntryEnd ) );
+}
+
+namespace
+{
+ Image GetImage(const OUString& rId)
+ {
+ return Image(StockImage::Yes, rId);
+ }
+}
+
+void ScOutlineWindow::DrawImageRel(tools::Long nLevelPos, tools::Long nEntryPos, const OUString& rId)
+{
+ const Image& rImage = GetImage(rId);
+ GetOutDev()->SetLineColor();
+ GetOutDev()->SetFillColor( GetBackground().GetColor() );
+ Point aPos( GetPoint( nLevelPos, nEntryPos ) );
+ GetOutDev()->DrawRect( tools::Rectangle( aPos, rImage.GetSizePixel() ) );
+ GetOutDev()->DrawImage( aPos, rImage );
+}
+
+void ScOutlineWindow::DrawBorderRel( size_t nLevel, size_t nEntry, bool bPressed )
+{
+ Point aPos;
+ if ( GetImagePos( nLevel, nEntry, aPos ) )
+ {
+ OUString sId = bPressed ? RID_BMP_PRESSED : RID_BMP_NOTPRESSED;
+ bool bClip = (nEntry != SC_OL_HEADERENTRY);
+ if ( bClip )
+ SetEntryAreaClipRegion();
+ GetOutDev()->DrawImage(aPos, GetImage(sId));
+ if ( bClip )
+ GetOutDev()->SetClipRegion();
+ }
+ mbMTPressed = bPressed;
+}
+
+void ScOutlineWindow::ShowFocus()
+{
+ if ( !HasFocus() )
+ return;
+
+ // first move to a visible position
+ ImplMoveFocusToVisible( true );
+
+ if ( !IsFocusButtonVisible() )
+ return;
+
+ Point aPos;
+ if ( GetImagePos( mnFocusLevel, mnFocusEntry, aPos ) )
+ {
+ aPos += Point( 1, 1 );
+ maFocusRect = tools::Rectangle( aPos, Size( SC_OL_BITMAPSIZE - 2, SC_OL_BITMAPSIZE - 2 ) );
+ bool bClip = (mnFocusEntry != SC_OL_HEADERENTRY);
+ if ( bClip )
+ SetEntryAreaClipRegion();
+ InvertTracking( maFocusRect, ShowTrackFlags::Small | ShowTrackFlags::TrackWindow );
+ if ( bClip )
+ GetOutDev()->SetClipRegion();
+ }
+}
+
+void ScOutlineWindow::HideFocus()
+{
+ if ( !maFocusRect.IsEmpty() )
+ {
+ bool bClip = (mnFocusEntry != SC_OL_HEADERENTRY);
+ if ( bClip )
+ SetEntryAreaClipRegion();
+ InvertTracking( maFocusRect, ShowTrackFlags::Small | ShowTrackFlags::TrackWindow );
+ if ( bClip )
+ GetOutDev()->SetClipRegion();
+ maFocusRect.SetEmpty();
+ }
+}
+
+constexpr OUString aLevelBmps[]=
+{
+ RID_BMP_LEVEL1,
+ RID_BMP_LEVEL2,
+ RID_BMP_LEVEL3,
+ RID_BMP_LEVEL4,
+ RID_BMP_LEVEL5,
+ RID_BMP_LEVEL6,
+ RID_BMP_LEVEL7,
+ RID_BMP_LEVEL8
+};
+
+void ScOutlineWindow::Paint( vcl::RenderContext& /*rRenderContext*/, const tools::Rectangle& /* rRect */ )
+{
+ tools::Long nEntriesSign = mbMirrorEntries ? -1 : 1;
+ tools::Long nLevelsSign = mbMirrorLevels ? -1 : 1;
+
+ Size aSize = GetOutputSizePixel();
+ tools::Long nLevelEnd = (mbHoriz ? aSize.Height() : aSize.Width()) - 1;
+ tools::Long nEntryEnd = (mbHoriz ? aSize.Width() : aSize.Height()) - 1;
+
+ GetOutDev()->SetLineColor( maLineColor );
+ tools::Long nBorderPos = mbMirrorLevels ? 0 : nLevelEnd;
+ DrawLineRel( nBorderPos, 0, nBorderPos, nEntryEnd );
+
+ const ScOutlineArray* pArray = GetOutlineArray();
+ if ( !pArray ) return;
+
+ size_t nLevelCount = GetLevelCount();
+
+ // --- draw header images ---
+
+ if ( mnHeaderSize > 0 )
+ {
+ tools::Long nEntryPos = GetHeaderEntryPos();
+ for ( size_t nLevel = 0; nLevel < nLevelCount; ++nLevel )
+ DrawImageRel(GetLevelPos(nLevel), nEntryPos, aLevelBmps[nLevel]);
+
+ GetOutDev()->SetLineColor( maLineColor );
+ tools::Long nLinePos = mnHeaderPos + (mbMirrorEntries ? 0 : (mnHeaderSize - 1));
+ DrawLineRel( 0, nLinePos, nLevelEnd, nLinePos );
+ }
+
+ // --- draw lines & collapse/expand images ---
+
+ SetEntryAreaClipRegion();
+
+ SCCOLROW nStartIndex, nEndIndex;
+ GetVisibleRange( nStartIndex, nEndIndex );
+
+ for ( size_t nLevel = 0; nLevel + 1 < nLevelCount; ++nLevel )
+ {
+ tools::Long nLevelPos = GetLevelPos( nLevel );
+ tools::Long nEntryPos1 = 0, nEntryPos2 = 0, nImagePos = 0;
+
+ size_t nEntryCount = pArray->GetCount( sal::static_int_cast<sal_uInt16>(nLevel) );
+ size_t nEntry;
+
+ // first draw all lines in the current level
+ GetOutDev()->SetLineColor();
+ GetOutDev()->SetFillColor( maLineColor );
+ for ( nEntry = 0; nEntry < nEntryCount; ++nEntry )
+ {
+ const ScOutlineEntry* pEntry = pArray->GetEntry( sal::static_int_cast<sal_uInt16>(nLevel),
+ sal::static_int_cast<sal_uInt16>(nEntry) );
+ SCCOLROW nStart = pEntry->GetStart();
+ SCCOLROW nEnd = pEntry->GetEnd();
+
+ // visible range?
+ bool bDraw = (nEnd >= nStartIndex) && (nStart <= nEndIndex);
+ // find output coordinates
+ if ( bDraw )
+ bDraw = GetEntryPos( nLevel, nEntry, nEntryPos1, nEntryPos2, nImagePos );
+ // draw, if not collapsed
+ if ( bDraw && !pEntry->IsHidden() )
+ {
+ if ( nStart >= nStartIndex )
+ nEntryPos1 += nEntriesSign;
+ nEntryPos2 -= 2 * nEntriesSign;
+ tools::Long nLinePos = nLevelPos;
+ if ( mbMirrorLevels )
+ nLinePos += SC_OL_BITMAPSIZE - 1; // align with right edge of bitmap
+ DrawRectRel( nLinePos, nEntryPos1, nLinePos + nLevelsSign, nEntryPos2 );
+
+ if ( nEnd <= nEndIndex )
+ DrawRectRel( nLinePos, nEntryPos2 - nEntriesSign,
+ nLinePos + ( SC_OL_BITMAPSIZE / 3 ) * nLevelsSign, nEntryPos2 );
+ }
+ }
+
+ // draw all images in the level from last to first
+ nEntry = nEntryCount;
+ while ( nEntry )
+ {
+ --nEntry;
+
+ const ScOutlineEntry* pEntry = pArray->GetEntry( sal::static_int_cast<sal_uInt16>(nLevel),
+ sal::static_int_cast<sal_uInt16>(nEntry) );
+ SCCOLROW nStart = pEntry->GetStart();
+
+ // visible range?
+ bool bDraw = (nStartIndex <= nStart) && (nStart <= nEndIndex + 1);
+ // find output coordinates
+ if ( bDraw )
+ bDraw = GetEntryPos( nLevel, nEntry, nEntryPos1, nEntryPos2, nImagePos );
+ // draw, if not hidden by higher levels
+ if ( bDraw )
+ {
+ OUString sImageId = pEntry->IsHidden() ? RID_BMP_PLUS : RID_BMP_MINUS;
+ DrawImageRel(nLevelPos, nImagePos, sImageId);
+ }
+ }
+ }
+
+ GetOutDev()->SetClipRegion();
+
+ if ( !mbDontDrawFocus )
+ ShowFocus();
+}
+
+// focus ----------------------------------------------------------------------
+
+/** Increments or decrements a value and wraps at the specified limits.
+ @return true = value wrapped. */
+static bool lcl_RotateValue( size_t& rnValue, size_t nMin, size_t nMax, bool bForward )
+{
+ OSL_ENSURE( nMin <= nMax, "lcl_RotateValue - invalid range" );
+ OSL_ENSURE( nMax < static_cast< size_t >( -1 ), "lcl_RotateValue - range overflow" );
+ bool bWrap = false;
+ if ( bForward )
+ {
+ if ( rnValue < nMax )
+ ++rnValue;
+ else
+ {
+ rnValue = nMin;
+ bWrap = true;
+ }
+ }
+ else
+ {
+ if ( rnValue > nMin )
+ --rnValue;
+ else
+ {
+ rnValue = nMax;
+ bWrap = true;
+ }
+ }
+ return bWrap;
+}
+
+bool ScOutlineWindow::IsFocusButtonVisible() const
+{
+ return IsButtonVisible( mnFocusLevel, mnFocusEntry );
+}
+
+bool ScOutlineWindow::ImplMoveFocusByEntry( bool bForward, bool bFindVisible )
+{
+ const ScOutlineArray* pArray = GetOutlineArray();
+ if ( !pArray )
+ return false;
+
+ bool bWrapped = false;
+ size_t nEntryCount = pArray->GetCount( sal::static_int_cast<sal_uInt16>(mnFocusLevel) );
+ // #i29530# entry count may be decreased after changing active sheet
+ if( mnFocusEntry >= nEntryCount )
+ mnFocusEntry = SC_OL_HEADERENTRY;
+ size_t nOldEntry = mnFocusEntry;
+
+ do
+ {
+ if ( mnFocusEntry == SC_OL_HEADERENTRY )
+ {
+ // move from header to first or last entry
+ if ( nEntryCount > 0 )
+ mnFocusEntry = bForward ? 0 : (nEntryCount - 1);
+ /* wrapped, if forward from right header to first entry,
+ or if backward from left header to last entry */
+ // Header and entries are now always in consistent order,
+ // so there's no need to check for mirroring here.
+ if ( !nEntryCount || !bForward )
+ bWrapped = true;
+ }
+ else if ( lcl_RotateValue( mnFocusEntry, 0, nEntryCount - 1, bForward ) )
+ {
+ // lcl_RotateValue returns true -> wrapped the entry range -> move to header
+ mnFocusEntry = SC_OL_HEADERENTRY;
+ /* wrapped, if forward from last entry to left header,
+ or if backward from first entry to right header */
+ if ( bForward )
+ bWrapped = true;
+ }
+ }
+ while ( bFindVisible && !IsFocusButtonVisible() && (nOldEntry != mnFocusEntry) );
+
+ return bWrapped;
+}
+
+bool ScOutlineWindow::ImplMoveFocusByLevel( bool bForward )
+{
+ const ScOutlineArray* pArray = GetOutlineArray();
+ if ( !pArray )
+ return false;
+
+ bool bWrapped = false;
+ size_t nLevelCount = GetLevelCount();
+
+ if ( mnFocusEntry == SC_OL_HEADERENTRY )
+ {
+ if ( nLevelCount > 0 )
+ bWrapped = lcl_RotateValue( mnFocusLevel, 0, nLevelCount - 1, bForward );
+ }
+ else
+ {
+ const ScOutlineEntry* pEntry = pArray->GetEntry(
+ mnFocusLevel, mnFocusEntry);
+
+ if ( pEntry )
+ {
+ SCCOLROW nStart = pEntry->GetStart();
+ SCCOLROW nEnd = pEntry->GetEnd();
+ size_t nNewLevel = mnFocusLevel;
+ size_t nNewEntry = 0;
+
+ bool bFound = false;
+ if ( bForward && (mnFocusLevel + 2 < nLevelCount) )
+ {
+ // next level -> find first child entry
+ nNewLevel = mnFocusLevel + 1;
+ bFound = pArray->GetEntryIndexInRange(nNewLevel, nStart, nEnd, nNewEntry);
+ }
+ else if ( !bForward && (mnFocusLevel > 0) )
+ {
+ // previous level -> find parent entry
+ nNewLevel = mnFocusLevel - 1;
+ bFound = pArray->GetEntryIndex(nNewLevel, nStart, nNewEntry);
+ }
+
+ if ( bFound && IsButtonVisible( nNewLevel, nNewEntry ) )
+ {
+ mnFocusLevel = nNewLevel;
+ mnFocusEntry = nNewEntry;
+ }
+ }
+ }
+
+ return bWrapped;
+}
+
+bool ScOutlineWindow::ImplMoveFocusByTabOrder( bool bForward )
+{
+ bool bRet = false;
+ size_t nOldLevel = mnFocusLevel;
+ size_t nOldEntry = mnFocusEntry;
+
+ do
+ {
+ /* one level up, if backward from left header,
+ or one level down, if forward from right header */
+ if ( (!bForward) && (mnFocusEntry == SC_OL_HEADERENTRY) )
+ bRet |= ImplMoveFocusByLevel( bForward );
+ // move to next/previous entry
+ bool bWrapInLevel = ImplMoveFocusByEntry( bForward, false );
+ bRet |= bWrapInLevel;
+ /* one level up, if wrapped backward to right header,
+ or one level down, if wrapped forward to right header */
+ if ( bForward && bWrapInLevel )
+ bRet |= ImplMoveFocusByLevel( bForward );
+ }
+ while ( !IsFocusButtonVisible() && ((nOldLevel != mnFocusLevel) || (nOldEntry != mnFocusEntry)) );
+
+ return bRet;
+}
+
+void ScOutlineWindow::ImplMoveFocusToVisible( bool bForward )
+{
+ // first try to find an entry in the same level
+ if ( !IsFocusButtonVisible() )
+ ImplMoveFocusByEntry( bForward, true );
+ // then try to find any other entry
+ if ( !IsFocusButtonVisible() )
+ ImplMoveFocusByTabOrder( bForward );
+}
+
+void ScOutlineWindow::MoveFocusByEntry( bool bForward )
+{
+ HideFocus();
+ ImplMoveFocusByEntry( bForward, true );
+ ShowFocus();
+}
+
+void ScOutlineWindow::MoveFocusByLevel( bool bForward )
+{
+ HideFocus();
+ ImplMoveFocusByLevel( bForward );
+ ShowFocus();
+}
+
+void ScOutlineWindow::MoveFocusByTabOrder( bool bForward )
+{
+ HideFocus();
+ ImplMoveFocusByTabOrder( bForward );
+ ShowFocus();
+}
+
+void ScOutlineWindow::GetFocus()
+{
+ Window::GetFocus();
+ ShowFocus();
+}
+
+void ScOutlineWindow::LoseFocus()
+{
+ HideFocus();
+ Window::LoseFocus();
+}
+
+// mouse ----------------------------------------------------------------------
+
+void ScOutlineWindow::StartMouseTracking( size_t nLevel, size_t nEntry )
+{
+ mbMTActive = true;
+ mnMTLevel = nLevel;
+ mnMTEntry = nEntry;
+ DrawBorderRel( nLevel, nEntry, true );
+}
+
+void ScOutlineWindow::EndMouseTracking()
+{
+ if ( mbMTPressed )
+ DrawBorderRel( mnMTLevel, mnMTEntry, false );
+ mbMTActive = false;
+}
+
+void ScOutlineWindow::MouseMove( const MouseEvent& rMEvt )
+{
+ if ( IsMouseTracking() )
+ {
+ size_t nLevel, nEntry;
+ bool bHit = false;
+
+ if ( ButtonHit( rMEvt.GetPosPixel(), nLevel, nEntry ) )
+ bHit = (nLevel == mnMTLevel) && (nEntry == mnMTEntry);
+
+ if ( bHit != mbMTPressed )
+ DrawBorderRel( mnMTLevel, mnMTEntry, bHit );
+ }
+}
+
+void ScOutlineWindow::MouseButtonUp( const MouseEvent& rMEvt )
+{
+ if ( IsMouseTracking() )
+ {
+ EndMouseTracking();
+
+ size_t nLevel, nEntry;
+ if ( ButtonHit( rMEvt.GetPosPixel(), nLevel, nEntry ) )
+ if ( (nLevel == mnMTLevel) && (nEntry == mnMTEntry) )
+ DoFunction( nLevel, nEntry );
+ }
+}
+
+void ScOutlineWindow::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ size_t nLevel, nEntry;
+ bool bHit = ButtonHit( rMEvt.GetPosPixel(), nLevel, nEntry );
+ if ( bHit )
+ StartMouseTracking( nLevel, nEntry );
+ else if ( rMEvt.GetClicks() == 2 )
+ {
+ bHit = LineHit( rMEvt.GetPosPixel(), nLevel, nEntry );
+ if ( bHit )
+ DoFunction( nLevel, nEntry );
+ }
+
+ // if an item has been hit and window is focused, move focus to this item
+ if ( bHit && HasFocus() )
+ {
+ HideFocus();
+ mnFocusLevel = nLevel;
+ mnFocusEntry = nEntry;
+ ShowFocus();
+ }
+}
+
+// keyboard -------------------------------------------------------------------
+
+void ScOutlineWindow::KeyInput( const KeyEvent& rKEvt )
+{
+ const vcl::KeyCode& rKCode = rKEvt.GetKeyCode();
+ bool bNoMod = !rKCode.GetModifier();
+ bool bShift = (rKCode.GetModifier() == KEY_SHIFT);
+ bool bCtrl = (rKCode.GetModifier() == KEY_MOD1);
+
+ sal_uInt16 nCode = rKCode.GetCode();
+ bool bUpDownKey = (nCode == KEY_UP) || (nCode == KEY_DOWN);
+ bool bLeftRightKey = (nCode == KEY_LEFT) || (nCode == KEY_RIGHT);
+
+ // TAB key
+ if ( (nCode == KEY_TAB) && (bNoMod || bShift) )
+ // move forward without SHIFT key
+ MoveFocusByTabOrder( bNoMod ); // TAB uses logical order, regardless of mirroring
+
+ // LEFT/RIGHT/UP/DOWN keys
+ else if ( bNoMod && (bUpDownKey || bLeftRightKey) )
+ {
+ bool bForward = (nCode == KEY_DOWN) || (nCode == KEY_RIGHT);
+ if ( mbHoriz == bLeftRightKey )
+ // move inside level with LEFT/RIGHT in horizontal and with UP/DOWN in vertical
+ MoveFocusByEntry( bForward != mbMirrorEntries );
+ else
+ // move to next/prev level with LEFT/RIGHT in vertical and with UP/DOWN in horizontal
+ MoveFocusByLevel( bForward != mbMirrorLevels );
+ }
+
+ // CTRL + number
+ else if ( bCtrl && (nCode >= KEY_1) && (nCode <= KEY_9) )
+ {
+ size_t nLevel = static_cast< size_t >( nCode - KEY_1 );
+ if ( nLevel < GetLevelCount() )
+ DoFunction( nLevel, SC_OL_HEADERENTRY );
+ }
+
+ // other key codes
+ else switch ( rKCode.GetFullCode() )
+ {
+ case KEY_ADD: DoExpand( mnFocusLevel, mnFocusEntry ); break;
+ case KEY_SUBTRACT: DoCollapse( mnFocusLevel, mnFocusEntry ); break;
+ case KEY_SPACE:
+ case KEY_RETURN: DoFunction( mnFocusLevel, mnFocusEntry ); break;
+ default: Window::KeyInput( rKEvt );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/output.cxx b/sc/source/ui/view/output.cxx
new file mode 100644
index 0000000000..9d0fe14305
--- /dev/null
+++ b/sc/source/ui/view/output.cxx
@@ -0,0 +1,2773 @@
+/* -*- 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 .
+ */
+
+#include <scitems.hxx>
+#include <editeng/brushitem.hxx>
+#include <svtools/colorcfg.hxx>
+#include <svx/rotmodit.hxx>
+#include <editeng/shaditem.hxx>
+#include <editeng/svxfont.hxx>
+#include <tools/poly.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/pdfextoutdevdata.hxx>
+#include <svtools/accessibilityoptions.hxx>
+#include <svx/framelinkarray.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <drawinglayer/processor2d/baseprocessor2d.hxx>
+#include <drawinglayer/processor2d/processor2dtools.hxx>
+#include <vcl/lineinfo.hxx>
+#include <vcl/gradient.hxx>
+#include <vcl/settings.hxx>
+#include <svx/unoapi.hxx>
+#include <sal/log.hxx>
+#include <comphelper/lok.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/range/b2drectangle.hxx>
+
+#include <output.hxx>
+#include <document.hxx>
+#include <drwlayer.hxx>
+#include <formulacell.hxx>
+#include <attrib.hxx>
+#include <patattr.hxx>
+#include <progress.hxx>
+#include <pagedata.hxx>
+#include <chgtrack.hxx>
+#include <chgviset.hxx>
+#include <viewutil.hxx>
+#include <gridmerg.hxx>
+#include <fillinfo.hxx>
+#include <scmod.hxx>
+#include <appoptio.hxx>
+#include <postit.hxx>
+#include <validat.hxx>
+#include <detfunc.hxx>
+#include <editutil.hxx>
+
+#include <SparklineRenderer.hxx>
+#include <colorscale.hxx>
+
+#include <math.h>
+#include <memory>
+
+using namespace com::sun::star;
+
+// Static Data
+
+// color for ChangeTracking "by author" as in the writer (swmodul1.cxx)
+
+#define SC_AUTHORCOLORCOUNT 9
+
+const Color nAuthorColor[ SC_AUTHORCOLORCOUNT ] = {
+ COL_LIGHTRED, COL_LIGHTBLUE, COL_LIGHTMAGENTA,
+ COL_GREEN, COL_RED, COL_BLUE,
+ COL_BROWN, COL_MAGENTA, COL_CYAN };
+
+// Helper class for color assignment to avoid repeated lookups for the same user
+
+ScActionColorChanger::ScActionColorChanger( const ScChangeTrack& rTrack ) :
+ rOpt( SC_MOD()->GetAppOptions() ),
+ rUsers( rTrack.GetUserCollection() ),
+ nLastUserIndex( 0 ),
+ nColor( COL_BLACK )
+{
+}
+
+void ScActionColorChanger::Update( const ScChangeAction& rAction )
+{
+ Color nSetColor;
+ switch (rAction.GetType())
+ {
+ case SC_CAT_INSERT_COLS:
+ case SC_CAT_INSERT_ROWS:
+ case SC_CAT_INSERT_TABS:
+ nSetColor = rOpt.GetTrackInsertColor();
+ break;
+ case SC_CAT_DELETE_COLS:
+ case SC_CAT_DELETE_ROWS:
+ case SC_CAT_DELETE_TABS:
+ nSetColor = rOpt.GetTrackDeleteColor();
+ break;
+ case SC_CAT_MOVE:
+ nSetColor = rOpt.GetTrackMoveColor();
+ break;
+ default:
+ nSetColor = rOpt.GetTrackContentColor();
+ break;
+ }
+ if ( nSetColor != COL_TRANSPARENT ) // color assigned
+ nColor = nSetColor;
+ else // by author
+ {
+ if (aLastUserName != rAction.GetUser())
+ {
+ aLastUserName = rAction.GetUser();
+ std::set<OUString>::const_iterator it = rUsers.find(aLastUserName);
+ if (it == rUsers.end())
+ {
+ // empty string is possible if a name wasn't found while saving a 5.0 file
+ SAL_INFO_IF( aLastUserName.isEmpty(), "sc.ui", "Author not found" );
+ nLastUserIndex = 0;
+ }
+ else
+ {
+ size_t nPos = std::distance(rUsers.begin(), it);
+ nLastUserIndex = nPos % SC_AUTHORCOLORCOUNT;
+ }
+ }
+ nColor = nAuthorColor[nLastUserIndex];
+ }
+}
+
+ScOutputData::ScOutputData( OutputDevice* pNewDev, ScOutputType eNewType,
+ ScTableInfo& rTabInfo, ScDocument* pNewDoc,
+ SCTAB nNewTab, tools::Long nNewScrX, tools::Long nNewScrY,
+ SCCOL nNewX1, SCROW nNewY1, SCCOL nNewX2, SCROW nNewY2,
+ double nPixelPerTwipsX, double nPixelPerTwipsY,
+ const Fraction* pZoomX, const Fraction* pZoomY ) :
+ mpDev( pNewDev ),
+ mpRefDevice( pNewDev ), // default is output device
+ pFmtDevice( pNewDev ), // default is output device
+ mrTabInfo( rTabInfo ),
+ pRowInfo( rTabInfo.mpRowInfo.get() ),
+ nArrCount( rTabInfo.mnArrCount ),
+ mpDoc( pNewDoc ),
+ nTab( nNewTab ),
+ nScrX( nNewScrX ),
+ nScrY( nNewScrY ),
+ nX1( nNewX1 ),
+ nY1( nNewY1 ),
+ nX2( nNewX2 ),
+ nY2( nNewY2 ),
+ eType( eNewType ),
+ mnPPTX( nPixelPerTwipsX ),
+ mnPPTY( nPixelPerTwipsY ),
+ pViewShell( nullptr ),
+ pDrawView( nullptr ),
+ bEditMode( false ),
+ nEditCol( 0 ),
+ nEditRow( 0 ),
+ bMetaFile( false ),
+ bPagebreakMode( false ),
+ bSolidBackground( false ),
+ mbUseStyleColor( false ),
+ mbForceAutoColor( SvtAccessibilityOptions::GetIsAutomaticFontColor() ),
+ mbSyntaxMode( false ),
+ aGridColor( COL_BLACK ),
+ mbShowNullValues( true ),
+ mbShowFormulas( false ),
+ bShowSpellErrors( false ),
+ bMarkClipped( false ), // sal_False for printer/metafile etc.
+ bSnapPixel( false ),
+ bAnyClipped( false ),
+ bVertical(false),
+ mpTargetPaintWindow(nullptr), // #i74769# use SdrPaintWindow direct
+ mpSpellCheckCxt(nullptr)
+{
+ if (pZoomX)
+ aZoomX = *pZoomX;
+ else
+ aZoomX = Fraction(1,1);
+ if (pZoomY)
+ aZoomY = *pZoomY;
+ else
+ aZoomY = Fraction(1,1);
+
+ nVisX1 = nX1;
+ nVisY1 = nY1;
+ nVisX2 = nX2;
+ nVisY2 = nY2;
+ mpDoc->StripHidden( nVisX1, nVisY1, nVisX2, nVisY2, nTab );
+
+ nScrW = 0;
+ for (SCCOL nX=nVisX1; nX<=nVisX2; nX++)
+ nScrW += pRowInfo[0].basicCellInfo(nX).nWidth;
+
+ nMirrorW = nScrW;
+
+ nScrH = 0;
+ for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
+ nScrH += pRowInfo[nArrY].nHeight;
+
+ bTabProtected = mpDoc->IsTabProtected( nTab );
+ bLayoutRTL = mpDoc->IsLayoutRTL( nTab );
+
+ // always needed, so call at the end of the constructor
+ SetCellRotations();
+ InitOutputEditEngine();
+}
+
+ScOutputData::~ScOutputData()
+{
+}
+
+void ScOutputData::SetSpellCheckContext( const sc::SpellCheckContext* pCxt )
+{
+ mpSpellCheckCxt = pCxt;
+}
+
+void ScOutputData::SetContentDevice( OutputDevice* pContentDev )
+{
+ // use pContentDev instead of pDev where used
+
+ if ( mpRefDevice == mpDev )
+ mpRefDevice = pContentDev;
+ if ( pFmtDevice == mpDev )
+ pFmtDevice = pContentDev;
+ mpDev = pContentDev;
+}
+
+void ScOutputData::SetMirrorWidth( tools::Long nNew )
+{
+ nMirrorW = nNew;
+}
+
+void ScOutputData::SetGridColor( const Color& rColor )
+{
+ aGridColor = rColor;
+}
+
+void ScOutputData::SetMarkClipped( bool bSet )
+{
+ bMarkClipped = bSet;
+}
+
+void ScOutputData::SetShowNullValues( bool bSet )
+{
+ mbShowNullValues = bSet;
+}
+
+void ScOutputData::SetShowFormulas( bool bSet )
+{
+ mbShowFormulas = bSet;
+}
+
+void ScOutputData::SetShowSpellErrors( bool bSet )
+{
+ bShowSpellErrors = bSet;
+ // reset EditEngine because it depends on bShowSpellErrors
+ mxOutputEditEngine.reset();
+}
+
+void ScOutputData::SetSnapPixel()
+{
+ bSnapPixel = true;
+}
+
+void ScOutputData::SetEditCell( SCCOL nCol, SCROW nRow )
+{
+ nEditCol = nCol;
+ nEditRow = nRow;
+ bEditMode = true;
+}
+
+void ScOutputData::SetMetaFileMode( bool bNewMode )
+{
+ bMetaFile = bNewMode;
+}
+
+void ScOutputData::SetSyntaxMode( bool bNewMode )
+{
+ mbSyntaxMode = bNewMode;
+ if ( bNewMode && !mxValueColor )
+ {
+ const svtools::ColorConfig& rColorCfg = SC_MOD()->GetColorConfig();
+ mxValueColor = rColorCfg.GetColorValue( svtools::CALCVALUE ).nColor;
+ mxTextColor = rColorCfg.GetColorValue( svtools::CALCTEXT ).nColor;
+ mxFormulaColor = rColorCfg.GetColorValue( svtools::CALCFORMULA ).nColor;
+ }
+}
+
+void ScOutputData::DrawGrid(vcl::RenderContext& rRenderContext, bool bGrid, bool bPage, bool bMergeCover)
+{
+ // bMergeCover : Draw lines in sheet bgcolor to cover lok client grid lines in merged cell areas.
+ // When scNoGridBackground is set in lok mode, bMergeCover is set to true and bGrid to false.
+
+ SCCOL nX;
+ SCROW nY;
+ tools::Long nPosX;
+ tools::Long nPosY;
+ SCSIZE nArrY;
+ ScBreakType nBreak = ScBreakType::NONE;
+ ScBreakType nBreakOld = ScBreakType::NONE;
+
+ bool bSingle;
+ bool bDashed = false;
+ Color aPageColor;
+ Color aManualColor;
+
+ if (bPagebreakMode)
+ bPage = false; // no "normal" breaks over the whole width/height
+
+ // It is a big mess to distinguish when we are using pixels and when logic
+ // units for drawing. Ultimately we want to work only in the logic units,
+ // but until that happens, we need to special-case:
+ //
+ // * metafile
+ // * drawing to the screen - everything is internally counted in pixels there
+ //
+ // 'Internally' in the above means the pCellInfo[...].nWidth and
+ // pRowInfo[...]->nHeight:
+ //
+ // * when bWorksInPixels is true: these are in pixels
+ // * when bWorksInPixels is false: these are in the logic units
+ //
+ // This is where all the confusion comes from, ultimately we want them
+ // always in the logic units (100th of millimeters), but we need to get
+ // there gradually (get rid of setting MapUnit::MapPixel first), otherwise we'd
+ // break all the drawing by one change.
+ // So until that happens, we need to special case.
+ bool bWorksInPixels = bMetaFile;
+ const svtools::ColorConfig& rColorCfg = SC_MOD()->GetColorConfig();
+ Color aSheetBGColor = rColorCfg.GetColorValue(::svtools::DOCCOLOR).nColor;
+
+ if ( eType == OUTTYPE_WINDOW )
+ {
+ bWorksInPixels = true;
+ aPageColor = rColorCfg.GetColorValue(svtools::CALCPAGEBREAKAUTOMATIC).nColor;
+ aManualColor = rColorCfg.GetColorValue(svtools::CALCPAGEBREAKMANUAL).nColor;
+ }
+ else
+ {
+ aPageColor = aGridColor;
+ aManualColor = aGridColor;
+ }
+
+ tools::Long nOneX = 1;
+ tools::Long nOneY = 1;
+ if (!bWorksInPixels)
+ {
+ Size aOnePixel = rRenderContext.PixelToLogic(Size(1,1));
+ nOneX = aOnePixel.Width();
+ nOneY = aOnePixel.Height();
+ }
+
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+ tools::Long nSignedOneX = nOneX * nLayoutSign;
+
+ rRenderContext.SetLineColor(bMergeCover ? aSheetBGColor : aGridColor);
+
+ ScGridMerger aGrid(&rRenderContext, nOneX, nOneY);
+
+ // vertical lines
+
+ nPosX = nScrX;
+ if ( bLayoutRTL )
+ nPosX += nMirrorW - nOneX;
+
+ for (nX=nX1; nX<=nX2; nX++)
+ {
+ sal_uInt16 nWidth = pRowInfo[0].basicCellInfo(nX).nWidth;
+ if (nWidth)
+ {
+ nPosX += nWidth * nLayoutSign;
+
+ if ( bPage )
+ {
+ // Search also in hidden part for page breaks
+ SCCOL nCol = nX + 1;
+ while (nCol <= mpDoc->MaxCol())
+ {
+ nBreak = mpDoc->HasColBreak(nCol, nTab);
+ bool bHidden = mpDoc->ColHidden(nCol, nTab);
+
+ if ( nBreak != ScBreakType::NONE || !bHidden )
+ break;
+ ++nCol;
+ }
+
+ if (nBreak != nBreakOld)
+ {
+ aGrid.Flush();
+
+ if (static_cast<int>(nBreak))
+ {
+ rRenderContext.SetLineColor( (nBreak & ScBreakType::Manual) ? aManualColor :
+ aPageColor );
+ bDashed = true;
+ }
+ else
+ {
+ rRenderContext.SetLineColor(bMergeCover ? aSheetBGColor : aGridColor);
+ bDashed = false;
+ }
+
+ nBreakOld = nBreak;
+ }
+ }
+
+ bool bDraw = bGrid || nBreakOld != ScBreakType::NONE || bMergeCover; // simple grid only if set that way
+
+ sal_uInt16 nWidthXplus1 = pRowInfo[0].basicCellInfo(nX+1).nWidth;
+ bSingle = false; //! get into Fillinfo !!!!!
+ if ( nX<mpDoc->MaxCol() && !bSingle )
+ {
+ bSingle = ( nWidthXplus1 == 0 );
+ for (nArrY=1; nArrY+1<nArrCount && !bSingle; nArrY++)
+ {
+ if (pRowInfo[nArrY].cellInfo(nX+1).bHOverlapped)
+ bSingle = true;
+ if (pRowInfo[nArrY].cellInfo(nX).bHideGrid)
+ bSingle = true;
+ }
+ }
+
+ if (bDraw)
+ {
+ if ( nX<mpDoc->MaxCol() && bSingle )
+ {
+ SCCOL nVisX = nX + 1;
+ while ( nVisX < mpDoc->MaxCol() && !mpDoc->GetColWidth(nVisX,nTab) )
+ ++nVisX;
+
+ nPosY = nScrY;
+ for (nArrY=1; nArrY+1<nArrCount; nArrY++)
+ {
+ RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+ const tools::Long nNextY = nPosY + pThisRowInfo->nHeight;
+
+ bool bHOver = pThisRowInfo->cellInfo(nX).bHideGrid;
+ if (!bHOver)
+ {
+ if (nWidthXplus1)
+ bHOver = pThisRowInfo->cellInfo(nX+1).bHOverlapped;
+ else
+ {
+ if (nVisX <= nX2)
+ bHOver = pThisRowInfo->cellInfo(nVisX).bHOverlapped;
+ else
+ bHOver = mpDoc->GetAttr(
+ nVisX,pThisRowInfo->nRowNo,nTab,ATTR_MERGE_FLAG)
+ ->IsHorOverlapped();
+ if (bHOver)
+ bHOver = mpDoc->GetAttr(
+ nX + 1,pThisRowInfo->nRowNo,nTab,ATTR_MERGE_FLAG)
+ ->IsHorOverlapped();
+ }
+ }
+
+ if ((pThisRowInfo->bChanged && !bHOver && !bMergeCover) || (bHOver && bMergeCover))
+ {
+ aGrid.AddVerLine(bWorksInPixels, nPosX-nSignedOneX, nPosY, nNextY-nOneY, bDashed);
+ }
+ nPosY = nNextY;
+ }
+ }
+ else if (!bMergeCover)
+ {
+ aGrid.AddVerLine(bWorksInPixels, nPosX-nSignedOneX, nScrY, nScrY+nScrH-nOneY, bDashed);
+ }
+ }
+ }
+ }
+
+ // horizontal lines
+
+ bool bHiddenRow = true;
+ SCROW nHiddenEndRow = -1;
+ nPosY = nScrY;
+ for (nArrY=1; nArrY+1<nArrCount; nArrY++)
+ {
+ SCSIZE nArrYplus1 = nArrY+1;
+ nY = pRowInfo[nArrY].nRowNo;
+ SCROW nYplus1 = nY+1;
+ nPosY += pRowInfo[nArrY].nHeight;
+
+ if (pRowInfo[nArrY].bChanged)
+ {
+ if ( bPage )
+ {
+ for (SCROW i = nYplus1; i <= mpDoc->MaxRow(); ++i)
+ {
+ if (i > nHiddenEndRow)
+ bHiddenRow = mpDoc->RowHidden(i, nTab, nullptr, &nHiddenEndRow);
+ /* TODO: optimize the row break thing for large hidden
+ * segments where HasRowBreak() has to be called
+ * nevertheless for each row, as a row break is drawn also
+ * for hidden rows, above them. This needed to be done only
+ * once per hidden segment, maybe giving manual breaks
+ * priority. Something like GetNextRowBreak() and
+ * GetNextManualRowBreak(). */
+ nBreak = mpDoc->HasRowBreak(i, nTab);
+ if (!bHiddenRow || nBreak != ScBreakType::NONE)
+ break;
+ }
+
+ if (nBreakOld != nBreak)
+ {
+ aGrid.Flush();
+
+ if (static_cast<int>(nBreak))
+ {
+ rRenderContext.SetLineColor( (nBreak & ScBreakType::Manual) ? aManualColor :
+ aPageColor );
+ bDashed = true;
+ }
+ else
+ {
+ rRenderContext.SetLineColor(bMergeCover ? aSheetBGColor : aGridColor);
+ bDashed = false;
+ }
+
+ nBreakOld = nBreak;
+ }
+ }
+
+ bool bDraw = bGrid || nBreakOld != ScBreakType::NONE || bMergeCover; // simple grid only if set so
+
+ bool bNextYisNextRow = (pRowInfo[nArrYplus1].nRowNo == nYplus1);
+ bSingle = !bNextYisNextRow; // Hidden
+ for (SCCOL i=nX1; i<=nX2 && !bSingle; i++)
+ {
+ if (pRowInfo[nArrYplus1].cellInfo(i).bVOverlapped)
+ bSingle = true;
+ }
+
+ if (bDraw)
+ {
+ if ( bSingle && nY<mpDoc->MaxRow() )
+ {
+ SCROW nVisY = pRowInfo[nArrYplus1].nRowNo;
+
+ nPosX = nScrX;
+ if ( bLayoutRTL )
+ nPosX += nMirrorW - nOneX;
+
+ for (SCCOL i=nX1; i<=nX2; i++)
+ {
+ const tools::Long nNextX = nPosX + pRowInfo[0].basicCellInfo(i).nWidth * nLayoutSign;
+ if (nNextX != nPosX) // visible
+ {
+ bool bVOver;
+ if ( bNextYisNextRow )
+ bVOver = pRowInfo[nArrYplus1].cellInfo(i).bVOverlapped;
+ else
+ {
+ bVOver = mpDoc->GetAttr(
+ i,nYplus1,nTab,ATTR_MERGE_FLAG)
+ ->IsVerOverlapped()
+ && mpDoc->GetAttr(
+ i,nVisY,nTab,ATTR_MERGE_FLAG)
+ ->IsVerOverlapped();
+ //! nVisY from Array ??
+ }
+
+ if ((!bVOver && !bMergeCover) || (bVOver && bMergeCover))
+ {
+ aGrid.AddHorLine(bWorksInPixels, nPosX, nNextX-nSignedOneX, nPosY-nOneY, bDashed);
+ }
+ }
+ nPosX = nNextX;
+ }
+ }
+ else if (!bMergeCover)
+ {
+ aGrid.AddHorLine(bWorksInPixels, nScrX, nScrX+nScrW-nOneX, nPosY-nOneY, bDashed);
+ }
+ }
+ }
+ }
+}
+
+void ScOutputData::SetPagebreakMode( ScPageBreakData* pPageData )
+{
+ bPagebreakMode = true;
+ if (!pPageData)
+ return; // not yet initialized -> everything "not printed"
+
+ // mark printed range
+ // (everything in FillInfo is already initialized to sal_False)
+
+ sal_uInt16 nRangeCount = sal::static_int_cast<sal_uInt16>(pPageData->GetCount());
+ for (sal_uInt16 nPos=0; nPos<nRangeCount; nPos++)
+ {
+ ScRange aRange = pPageData->GetData( nPos ).GetPrintRange();
+
+ SCCOL nStartX = std::max( aRange.aStart.Col(), nX1 );
+ SCCOL nEndX = std::min( aRange.aEnd.Col(), nX2 );
+ SCROW nStartY = std::max( aRange.aStart.Row(), nY1 );
+ SCROW nEndY = std::min( aRange.aEnd.Row(), nY2 );
+
+ for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
+ {
+ RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+ if ( pThisRowInfo->bChanged && pThisRowInfo->nRowNo >= nStartY &&
+ pThisRowInfo->nRowNo <= nEndY )
+ {
+ for (SCCOL nX=nStartX; nX<=nEndX; nX++)
+ pThisRowInfo->cellInfo(nX).bPrinted = true;
+ }
+ }
+ }
+}
+
+void ScOutputData::SetCellRotations()
+{
+ //! save nRotMax
+ SCCOL nRotMax = nX2;
+ for (SCSIZE nRotY=0; nRotY<nArrCount; nRotY++)
+ if (pRowInfo[nRotY].nRotMaxCol != SC_ROTMAX_NONE && pRowInfo[nRotY].nRotMaxCol > nRotMax)
+ nRotMax = pRowInfo[nRotY].nRotMaxCol;
+
+ for (SCSIZE nArrY=1; nArrY<nArrCount; nArrY++)
+ {
+ RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+ if ( pThisRowInfo->nRotMaxCol != SC_ROTMAX_NONE &&
+ ( pThisRowInfo->bChanged || pRowInfo[nArrY-1].bChanged ||
+ ( nArrY+1<nArrCount && pRowInfo[nArrY+1].bChanged ) ) )
+ {
+ SCROW nY = pThisRowInfo->nRowNo;
+
+ for (SCCOL nX=0; nX<=nRotMax; nX++)
+ {
+ ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
+ const ScPatternAttr* pPattern = pInfo->pPatternAttr;
+ const SfxItemSet* pCondSet = pInfo->pConditionSet;
+
+ if ( !pPattern && !mpDoc->ColHidden(nX, nTab) )
+ {
+ pPattern = mpDoc->GetPattern( nX, nY, nTab );
+ pCondSet = mpDoc->GetCondResult( nX, nY, nTab );
+ }
+
+ if ( pPattern ) // column isn't hidden
+ {
+ ScRotateDir nDir = pPattern->GetRotateDir( pCondSet );
+ if (nDir != ScRotateDir::NONE)
+ {
+ // Needed for ScCellInfo internal decisions (bg fill, ...)
+ pInfo->nRotateDir = nDir;
+
+ // create target coordinates
+ const SCCOL nTargetX(nX - nVisX1 + 1);
+ const SCROW nTargetY(nY - nVisY1 + 1);
+
+ // Check for values - below in SetCellRotation these will
+ // be converted to size_t and thus may not be negative
+ if(nTargetX >= 0 && nTargetY >= 0)
+ {
+ // add rotation info to Array information
+ const Degree100 nAttrRotate(pPattern->GetRotateVal(pCondSet));
+ const SvxRotateMode eRotMode(pPattern->GetItem(ATTR_ROTATE_MODE, pCondSet).GetValue());
+ const double fOrient((bLayoutRTL ? -1.0 : 1.0) * toRadians(nAttrRotate)); // 1/100th degrees -> [0..2PI]
+ svx::frame::Array& rArray = mrTabInfo.maArray;
+
+ rArray.SetCellRotation(nTargetX, nTargetY, eRotMode, fOrient);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+static ScRotateDir lcl_GetRotateDir( const ScDocument* pDoc, SCCOL nCol, SCROW nRow, SCTAB nTab )
+{
+ const ScPatternAttr* pPattern = pDoc->GetPattern( nCol, nRow, nTab );
+ const SfxItemSet* pCondSet = pDoc->GetCondResult( nCol, nRow, nTab );
+
+ ScRotateDir nRet = ScRotateDir::NONE;
+
+ Degree100 nAttrRotate = pPattern->GetRotateVal( pCondSet );
+ if ( nAttrRotate )
+ {
+ SvxRotateMode eRotMode =
+ pPattern->GetItem(ATTR_ROTATE_MODE, pCondSet).GetValue();
+
+ if ( eRotMode == SVX_ROTATE_MODE_STANDARD )
+ nRet = ScRotateDir::Standard;
+ else if ( eRotMode == SVX_ROTATE_MODE_CENTER )
+ nRet = ScRotateDir::Center;
+ else if ( eRotMode == SVX_ROTATE_MODE_TOP || eRotMode == SVX_ROTATE_MODE_BOTTOM )
+ {
+ tools::Long nRot180 = nAttrRotate.get() % 18000; // 1/100 degree
+ if ( nRot180 == 9000 )
+ nRet = ScRotateDir::Center;
+ else if ( ( eRotMode == SVX_ROTATE_MODE_TOP && nRot180 < 9000 ) ||
+ ( eRotMode == SVX_ROTATE_MODE_BOTTOM && nRot180 > 9000 ) )
+ nRet = ScRotateDir::Left;
+ else
+ nRet = ScRotateDir::Right;
+ }
+ }
+
+ return nRet;
+}
+
+static const SvxBrushItem* lcl_FindBackground( const ScDocument* pDoc, SCCOL nCol, SCROW nRow, SCTAB nTab )
+{
+ const ScPatternAttr* pPattern = pDoc->GetPattern( nCol, nRow, nTab );
+ const SfxItemSet* pCondSet = pDoc->GetCondResult( nCol, nRow, nTab );
+ const SvxBrushItem* pBackground =
+ &pPattern->GetItem( ATTR_BACKGROUND, pCondSet );
+
+ ScRotateDir nDir = lcl_GetRotateDir( pDoc, nCol, nRow, nTab );
+
+ // treat CENTER like RIGHT
+ if ( nDir == ScRotateDir::Right || nDir == ScRotateDir::Center )
+ {
+ // text goes to the right -> take background from the left
+ while ( nCol > 0 && lcl_GetRotateDir( pDoc, nCol, nRow, nTab ) == nDir &&
+ pBackground->GetColor().GetAlpha() != 0 )
+ {
+ --nCol;
+ pPattern = pDoc->GetPattern( nCol, nRow, nTab );
+ pCondSet = pDoc->GetCondResult( nCol, nRow, nTab );
+ pBackground = &pPattern->GetItem( ATTR_BACKGROUND, pCondSet );
+ }
+ }
+ else if ( nDir == ScRotateDir::Left )
+ {
+ // text goes to the left -> take background from the right
+ while ( nCol < pDoc->MaxCol() && lcl_GetRotateDir( pDoc, nCol, nRow, nTab ) == nDir &&
+ pBackground->GetColor().GetAlpha() != 0 )
+ {
+ ++nCol;
+ pPattern = pDoc->GetPattern( nCol, nRow, nTab );
+ pCondSet = pDoc->GetCondResult( nCol, nRow, nTab );
+ pBackground = &pPattern->GetItem( ATTR_BACKGROUND, pCondSet );
+ }
+ }
+
+ return pBackground;
+}
+
+static bool lcl_EqualBack( const RowInfo& rFirst, const RowInfo& rOther,
+ SCCOL nX1, SCCOL nX2, bool bShowProt, bool bPagebreakMode )
+{
+ if ( rFirst.bChanged != rOther.bChanged ||
+ rFirst.bEmptyBack != rOther.bEmptyBack )
+ return false;
+
+ SCCOL nX;
+ if ( bShowProt )
+ {
+ for ( nX=nX1; nX<=nX2; nX++ )
+ {
+ const ScPatternAttr* pPat1 = rFirst.cellInfo(nX).pPatternAttr;
+ const ScPatternAttr* pPat2 = rOther.cellInfo(nX).pPatternAttr;
+ if ( !pPat1 || !pPat2 ||
+ !SfxPoolItem::areSame(pPat1->GetItem(ATTR_PROTECTION), pPat2->GetItem(ATTR_PROTECTION) ) )
+ return false;
+ }
+ }
+ else
+ {
+ for ( nX=nX1; nX<=nX2; nX++ )
+ if ( !SfxPoolItem::areSame(rFirst.cellInfo(nX).pBackground, rOther.cellInfo(nX).pBackground ) )
+ return false;
+ }
+
+ if ( rFirst.nRotMaxCol != SC_ROTMAX_NONE || rOther.nRotMaxCol != SC_ROTMAX_NONE )
+ for ( nX=nX1; nX<=nX2; nX++ )
+ if ( rFirst.cellInfo(nX).nRotateDir != rOther.cellInfo(nX).nRotateDir )
+ return false;
+
+ if ( bPagebreakMode )
+ for ( nX=nX1; nX<=nX2; nX++ )
+ if ( rFirst.cellInfo(nX).bPrinted != rOther.cellInfo(nX).bPrinted )
+ return false;
+
+ for ( nX=nX1; nX<=nX2; nX++ )
+ {
+ std::optional<Color> const & pCol1 = rFirst.cellInfo(nX).mxColorScale;
+ std::optional<Color> const & pCol2 = rOther.cellInfo(nX).mxColorScale;
+ if( (pCol1 && !pCol2) || (!pCol1 && pCol2) )
+ return false;
+
+ if (pCol1 && (*pCol1 != *pCol2))
+ return false;
+
+ const ScDataBarInfo* pInfo1 = rFirst.cellInfo(nX).pDataBar;
+ const ScDataBarInfo* pInfo2 = rOther.cellInfo(nX).pDataBar;
+
+ if( (pInfo1 && !pInfo2) || (!pInfo1 && pInfo2) )
+ return false;
+
+ if (pInfo1 && (*pInfo1 != *pInfo2))
+ return false;
+
+ // each cell with an icon set should be painted the same way
+ const ScIconSetInfo* pIconSet1 = rFirst.cellInfo(nX).pIconSet;
+ const ScIconSetInfo* pIconSet2 = rOther.cellInfo(nX).pIconSet;
+
+ if(pIconSet1 || pIconSet2)
+ return false;
+ }
+
+ return true;
+}
+
+void ScOutputData::DrawDocumentBackground()
+{
+ if ( !bSolidBackground )
+ return;
+
+ Color aBgColor( SC_MOD()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor );
+ mpDev->SetLineColor(aBgColor);
+ mpDev->SetFillColor(aBgColor);
+
+ Point aScreenPos = mpDev->PixelToLogic(Point(nScrX, nScrY));
+ Size aScreenSize = mpDev->PixelToLogic(Size(nScrW - 1,nScrH - 1));
+
+ mpDev->DrawRect(tools::Rectangle(aScreenPos, aScreenSize));
+}
+
+namespace {
+
+const double lclCornerRectTransparency = 40.0;
+
+void drawDataBars(vcl::RenderContext& rRenderContext, const ScDataBarInfo* pOldDataBarInfo, const tools::Rectangle& rRect, tools::Long nOneX, tools::Long nOneY)
+{
+ tools::Long nPosZero = 0;
+ tools::Rectangle aPaintRect = rRect;
+ aPaintRect.AdjustTop(2 * nOneY );
+ aPaintRect.AdjustBottom( -(2 * nOneY) );
+ aPaintRect.AdjustLeft( 2 * nOneX );
+ aPaintRect.AdjustRight( -(2 * nOneX) );
+ if(pOldDataBarInfo->mnZero)
+ {
+ // need to calculate null point in cell
+ tools::Long nLength = aPaintRect.Right() - aPaintRect.Left();
+ nPosZero = static_cast<tools::Long>(aPaintRect.Left() + nLength*pOldDataBarInfo->mnZero/100.0);
+ }
+ else
+ {
+ nPosZero = aPaintRect.Left();
+ }
+
+ if(pOldDataBarInfo->mnLength < 0)
+ {
+ aPaintRect.SetRight( nPosZero );
+ tools::Long nLength = nPosZero - aPaintRect.Left();
+ aPaintRect.SetLeft( nPosZero + static_cast<tools::Long>(nLength * pOldDataBarInfo->mnLength/100.0) );
+ }
+ else if(pOldDataBarInfo->mnLength > 0)
+ {
+ aPaintRect.SetLeft( nPosZero );
+ tools::Long nLength = aPaintRect.Right() - nPosZero;
+ aPaintRect.SetRight( nPosZero + static_cast<tools::Long>(nLength * pOldDataBarInfo->mnLength/100.0) );
+ }
+ else
+ return;
+
+ if(pOldDataBarInfo->mbGradient)
+ {
+ rRenderContext.SetLineColor(pOldDataBarInfo->maColor);
+ Gradient aGradient(css::awt::GradientStyle_LINEAR, pOldDataBarInfo->maColor, COL_TRANSPARENT);
+ aGradient.SetSteps(255);
+
+ if(pOldDataBarInfo->mnLength < 0)
+ aGradient.SetAngle(2700_deg10);
+ else
+ aGradient.SetAngle(900_deg10);
+
+ rRenderContext.DrawGradient(aPaintRect, aGradient);
+
+ rRenderContext.SetLineColor();
+ }
+ else
+ {
+ rRenderContext.SetFillColor(pOldDataBarInfo->maColor);
+ rRenderContext.DrawRect(aPaintRect);
+ }
+
+ //draw axis
+ if(!(pOldDataBarInfo->mnZero && pOldDataBarInfo->mnZero != 100))
+ return;
+
+ Point aPoint1(nPosZero, rRect.Top());
+ Point aPoint2(nPosZero, rRect.Bottom());
+ LineInfo aLineInfo(LineStyle::Dash, 1);
+ aLineInfo.SetDashCount( 4 );
+ aLineInfo.SetDistance( 3 );
+ aLineInfo.SetDashLen( 3 );
+ rRenderContext.SetFillColor(pOldDataBarInfo->maAxisColor);
+ rRenderContext.SetLineColor(pOldDataBarInfo->maAxisColor);
+ rRenderContext.DrawLine(aPoint1, aPoint2, aLineInfo);
+ rRenderContext.SetLineColor();
+ rRenderContext.SetFillColor();
+}
+
+const BitmapEx& getIcon(sc::IconSetBitmapMap & rIconSetBitmapMap, ScIconSetType eType, sal_Int32 nIndex)
+{
+ return ScIconSetFormat::getBitmap(rIconSetBitmapMap, eType, nIndex);
+}
+
+void drawIconSets(vcl::RenderContext& rRenderContext, const ScIconSetInfo* pOldIconSetInfo, const tools::Rectangle& rRect, tools::Long nOneX, tools::Long nOneY,
+ sc::IconSetBitmapMap & rIconSetBitmapMap)
+{
+ ScIconSetType eType = pOldIconSetInfo->eIconSetType;
+ sal_Int32 nIndex = pOldIconSetInfo->nIconIndex;
+ const BitmapEx& rIcon = getIcon(rIconSetBitmapMap, eType, nIndex);
+
+ tools::Long aHeight = o3tl::convert(10, o3tl::Length::pt, o3tl::Length::mm100);
+
+ if (pOldIconSetInfo->mnHeight)
+ {
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ aHeight = rRenderContext.LogicToPixel(Size(0, pOldIconSetInfo->mnHeight), MapMode(MapUnit::MapTwip)).Height();
+ aHeight *= comphelper::LibreOfficeKit::getDPIScale();
+ }
+ else
+ {
+ aHeight = o3tl::convert(pOldIconSetInfo->mnHeight, o3tl::Length::twip, o3tl::Length::mm100);
+ }
+ }
+
+ Size aSize = rIcon.GetSizePixel();
+ double fRatio = static_cast<double>(aSize.Width()) / aSize.Height();
+ tools::Long aWidth = fRatio * aHeight;
+
+ rRenderContext.Push();
+ rRenderContext.SetClipRegion(vcl::Region(rRect));
+ rRenderContext.DrawBitmapEx(Point(rRect.Left() + 2 * nOneX, rRect.Bottom() - 2 * nOneY - aHeight), Size(aWidth, aHeight), rIcon);
+ rRenderContext.Pop();
+}
+
+void drawCells(vcl::RenderContext& rRenderContext, std::optional<Color> const & pColor, const SvxBrushItem* pBackground, std::optional<Color>& pOldColor, const SvxBrushItem*& pOldBackground,
+ tools::Rectangle& rRect, tools::Long nPosX, tools::Long nLayoutSign, tools::Long nOneX, tools::Long nOneY, const ScDataBarInfo* pDataBarInfo, const ScDataBarInfo*& pOldDataBarInfo,
+ const ScIconSetInfo* pIconSetInfo, const ScIconSetInfo*& pOldIconSetInfo,
+ sc::IconSetBitmapMap & rIconSetBitmapMap)
+{
+ tools::Long nSignedOneX = nOneX * nLayoutSign;
+ // need to paint if old color scale has been used and now
+ // we have a different color or a style based background
+ // we can here fall back to pointer comparison
+ if (pOldColor && (pBackground || pOldColor != pColor || pOldDataBarInfo || pDataBarInfo || pIconSetInfo || pOldIconSetInfo))
+ {
+ rRect.SetRight( nPosX-nSignedOneX );
+ if( !pOldColor->IsTransparent() )
+ {
+ rRenderContext.SetFillColor( *pOldColor );
+ rRenderContext.DrawRect( rRect );
+ }
+ if( pOldDataBarInfo )
+ drawDataBars(rRenderContext, pOldDataBarInfo, rRect, nOneX, nOneY);
+ if( pOldIconSetInfo )
+ drawIconSets(rRenderContext, pOldIconSetInfo, rRect, nOneX, nOneY, rIconSetBitmapMap);
+
+ rRect.SetLeft( nPosX - nSignedOneX );
+ }
+
+ if ( pOldBackground && (pColor || !SfxPoolItem::areSame(pBackground, pOldBackground) || pOldDataBarInfo || pDataBarInfo || pIconSetInfo || pOldIconSetInfo) )
+ {
+ rRect.SetRight( nPosX-nSignedOneX );
+ if (pOldBackground) // ==0 if hidden
+ {
+ Color aBackCol = pOldBackground->GetColor();
+ if ( !aBackCol.IsTransparent() ) //! partial transparency?
+ {
+ rRenderContext.SetFillColor( aBackCol );
+ rRenderContext.DrawRect( rRect );
+ }
+ }
+ if( pOldDataBarInfo )
+ drawDataBars(rRenderContext, pOldDataBarInfo, rRect, nOneX, nOneY);
+ if( pOldIconSetInfo )
+ drawIconSets(rRenderContext, pOldIconSetInfo, rRect, nOneX, nOneY, rIconSetBitmapMap);
+
+ rRect.SetLeft( nPosX - nSignedOneX );
+ }
+
+ if (!pOldBackground && !pOldColor && (pDataBarInfo || pIconSetInfo))
+ {
+ rRect.SetRight( nPosX -nSignedOneX );
+ rRect.SetLeft( nPosX - nSignedOneX );
+ }
+
+ if(pColor)
+ {
+ // only update pOldColor if the colors changed
+ if (!pOldColor || *pOldColor != *pColor)
+ pOldColor = pColor;
+
+ pOldBackground = nullptr;
+ }
+ else if(pBackground)
+ {
+ pOldBackground = pBackground;
+ pOldColor.reset();
+ }
+
+ if(pDataBarInfo)
+ pOldDataBarInfo = pDataBarInfo;
+ else
+ pOldDataBarInfo = nullptr;
+
+ if(pIconSetInfo)
+ pOldIconSetInfo = pIconSetInfo;
+ else
+ pOldIconSetInfo = nullptr;
+}
+
+}
+
+void ScOutputData::DrawBackground(vcl::RenderContext& rRenderContext)
+{
+ Size aOnePixel = rRenderContext.PixelToLogic(Size(1,1));
+ tools::Long nOneXLogic = aOnePixel.Width();
+ tools::Long nOneYLogic = aOnePixel.Height();
+
+ // See more about bWorksInPixels in ScOutputData::DrawGrid
+ bool bWorksInPixels = false;
+ if (eType == OUTTYPE_WINDOW)
+ bWorksInPixels = true;
+
+ tools::Long nOneX = 1;
+ tools::Long nOneY = 1;
+ if (!bWorksInPixels)
+ {
+ nOneX = nOneXLogic;
+ nOneY = nOneYLogic;
+ }
+
+ tools::Rectangle aRect;
+
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ rRenderContext.SetLineColor();
+
+ bool bShowProt = mbSyntaxMode && mpDoc->IsTabProtected(nTab);
+ bool bDoAll = bShowProt || bPagebreakMode || bSolidBackground;
+
+ bool bCellContrast = mbUseStyleColor &&
+ Application::GetSettings().GetStyleSettings().GetHighContrastMode();
+
+ tools::Long nPosY = nScrY;
+
+ const svtools::ColorConfig& rColorCfg = SC_MOD()->GetColorConfig();
+ Color aProtectedColor( rColorCfg.GetColorValue( svtools::CALCPROTECTEDBACKGROUND ).nColor );
+ auto pProtectedBackground = std::make_shared<SvxBrushItem>( aProtectedColor, ATTR_BACKGROUND );
+
+ // iterate through the rows to show
+ for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
+ {
+ RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+ tools::Long nRowHeight = pThisRowInfo->nHeight;
+
+ if ( pThisRowInfo->bChanged )
+ {
+ if ( ( ( pThisRowInfo->bEmptyBack ) || mbSyntaxMode ) && !bDoAll )
+ {
+ // nothing
+ }
+ else
+ {
+ // scan for rows with the same background:
+ SCSIZE nSkip = 0;
+ while ( nArrY+nSkip+2<nArrCount &&
+ lcl_EqualBack( *pThisRowInfo, pRowInfo[nArrY+nSkip+1],
+ nX1, nX2, bShowProt, bPagebreakMode ) )
+ {
+ ++nSkip;
+ nRowHeight += pRowInfo[nArrY+nSkip].nHeight; // after incrementing
+ }
+
+ tools::Long nPosX = nScrX;
+
+ if ( bLayoutRTL )
+ nPosX += nMirrorW - nOneX;
+
+ aRect = tools::Rectangle(nPosX, nPosY - nOneY, nPosX, nPosY - nOneY + nRowHeight);
+ if (bWorksInPixels)
+ aRect = rRenderContext.PixelToLogic(aRect); // internal data in pixels, but we'll be drawing in logic units
+
+ const SvxBrushItem* pOldBackground = nullptr;
+ const SvxBrushItem* pBackground = nullptr;
+ std::optional<Color> pOldColor;
+ const ScDataBarInfo* pOldDataBarInfo = nullptr;
+ const ScIconSetInfo* pOldIconSetInfo = nullptr;
+ SCCOL nMergedCols = 1;
+ SCCOL nOldMerged = 0;
+
+ for (SCCOL nX=nX1; nX + nMergedCols <= nX2 + 1; nX += nOldMerged)
+ {
+ ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX-1+nMergedCols);
+
+ nOldMerged = nMergedCols;
+
+ tools::Long nNewPosX = nPosX;
+ // extend for all merged cells
+ nMergedCols = 1;
+ if (pInfo->bMerged && pInfo->pPatternAttr)
+ {
+ const ScMergeAttr* pMerge =
+ &pInfo->pPatternAttr->GetItem(ATTR_MERGE);
+ nMergedCols = std::max<SCCOL>(1, pMerge->GetColMerge());
+ }
+
+ for (SCCOL nMerged = 0; nMerged < nMergedCols; ++nMerged)
+ {
+ SCCOL nCol = nX+nOldMerged+nMerged;
+ if (nCol > nX2+2)
+ break;
+ nNewPosX += pRowInfo[0].basicCellInfo(nCol-1).nWidth * nLayoutSign;
+ }
+
+ if (nNewPosX == nPosX)
+ continue; // Zero width, no need to draw.
+
+ if (bCellContrast)
+ {
+ // high contrast for cell borders and backgrounds -> empty background
+ pBackground = ScGlobal::GetEmptyBrushItem();
+ }
+ else if (bShowProt) // show cell protection in syntax mode
+ {
+ const ScPatternAttr* pP = pInfo->pPatternAttr;
+ if (pP)
+ {
+ const ScProtectionAttr& rProt = pP->GetItem(ATTR_PROTECTION);
+ if (rProt.GetProtection() || rProt.GetHideCell())
+ pBackground = pProtectedBackground.get();
+ else
+ pBackground = ScGlobal::GetEmptyBrushItem();
+ }
+ else
+ pBackground = nullptr;
+ }
+ else
+ pBackground = pInfo->pBackground;
+
+ if ( bPagebreakMode && !pInfo->bPrinted )
+ pBackground = pProtectedBackground.get();
+
+ if ( pInfo->nRotateDir > ScRotateDir::Standard &&
+ !pBackground->GetColor().IsFullyTransparent() &&
+ !bCellContrast )
+ {
+ SCROW nY = pRowInfo[nArrY].nRowNo;
+ pBackground = lcl_FindBackground( mpDoc, nX, nY, nTab );
+ }
+
+ std::optional<Color> const & pColor = pInfo->mxColorScale;
+ const ScDataBarInfo* pDataBarInfo = pInfo->pDataBar;
+ const ScIconSetInfo* pIconSetInfo = pInfo->pIconSet;
+
+ tools::Long nPosXLogic = nPosX;
+ if (bWorksInPixels)
+ nPosXLogic = rRenderContext.PixelToLogic(Point(nPosX, 0)).X();
+
+ drawCells(rRenderContext, pColor, pBackground, pOldColor, pOldBackground, aRect, nPosXLogic, nLayoutSign, nOneXLogic, nOneYLogic, pDataBarInfo, pOldDataBarInfo, pIconSetInfo, pOldIconSetInfo, mpDoc->GetIconSetBitmapMap());
+
+ nPosX = nNewPosX;
+ }
+
+ tools::Long nPosXLogic = nPosX;
+ if (bWorksInPixels)
+ nPosXLogic = rRenderContext.PixelToLogic(Point(nPosX, 0)).X();
+
+ drawCells(rRenderContext, std::optional<Color>(), nullptr, pOldColor, pOldBackground, aRect, nPosXLogic, nLayoutSign, nOneXLogic, nOneYLogic, nullptr, pOldDataBarInfo, nullptr, pOldIconSetInfo, mpDoc->GetIconSetBitmapMap());
+
+ nArrY += nSkip;
+ }
+ }
+ nPosY += nRowHeight;
+ }
+}
+
+void ScOutputData::DrawShadow()
+{
+ DrawExtraShadow( false, false, false, false );
+}
+
+void ScOutputData::DrawExtraShadow(bool bLeft, bool bTop, bool bRight, bool bBottom)
+{
+ mpDev->SetLineColor();
+
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ bool bCellContrast = mbUseStyleColor && rStyleSettings.GetHighContrastMode();
+ Color aAutoTextColor;
+ if ( bCellContrast )
+ aAutoTextColor = SC_MOD()->GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor;
+
+ tools::Long nInitPosX = nScrX;
+ if ( bLayoutRTL )
+ {
+ Size aOnePixel = mpDev->PixelToLogic(Size(1,1));
+ tools::Long nOneX = aOnePixel.Width();
+ nInitPosX += nMirrorW - nOneX;
+ }
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ tools::Long nPosY = nScrY - pRowInfo[0].nHeight;
+ for (SCSIZE nArrY=0; nArrY<nArrCount; nArrY++)
+ {
+ bool bCornerY = ( nArrY == 0 ) || ( nArrY+1 == nArrCount );
+ bool bSkipY = ( nArrY==0 && !bTop ) || ( nArrY+1 == nArrCount && !bBottom );
+
+ RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+ tools::Long nRowHeight = pThisRowInfo->nHeight;
+
+ if ( pThisRowInfo->bChanged && !bSkipY )
+ {
+ tools::Long nPosX = nInitPosX - pRowInfo[0].basicCellInfo(nX1-1).nWidth * nLayoutSign;
+ for (SCCOL nCol=nX1-1; nCol<=nX2+1; nCol++)
+ {
+ bool bCornerX = ( nCol==nX1-1 || nCol==nX2+1 );
+ bool bSkipX = ( nCol==nX1-1 && !bLeft ) || ( nCol==nX2+1 && !bRight );
+
+ for (sal_uInt16 nPass=0; nPass<2; nPass++) // horizontal / vertical
+ {
+ const SvxShadowItem* pAttr = nPass ?
+ pThisRowInfo->cellInfo(nCol).pVShadowOrigin :
+ pThisRowInfo->cellInfo(nCol).pHShadowOrigin;
+ if ( pAttr && !bSkipX )
+ {
+ ScShadowPart ePart = nPass ?
+ pThisRowInfo->cellInfo(nCol).eVShadowPart :
+ pThisRowInfo->cellInfo(nCol).eHShadowPart;
+
+ bool bDo = true;
+ if ( (nPass==0 && bCornerX) || (nPass==1 && bCornerY) )
+ if ( ePart != SC_SHADOW_CORNER )
+ bDo = false;
+
+ if (bDo)
+ {
+ tools::Long nThisWidth = pRowInfo[0].basicCellInfo(nCol).nWidth;
+ tools::Long nMaxWidth = nThisWidth;
+ if (!nMaxWidth)
+ {
+ //! direction must depend on shadow location
+ SCCOL nWx = nCol+1;
+ while (nWx<nX2 && !pRowInfo[0].basicCellInfo(nWx).nWidth)
+ ++nWx;
+ nMaxWidth = pRowInfo[0].basicCellInfo(nWx).nWidth;
+ }
+
+ // rectangle is in logical orientation
+ tools::Rectangle aRect( nPosX, nPosY,
+ nPosX + ( nThisWidth - 1 ) * nLayoutSign,
+ nPosY + pRowInfo[nArrY].nHeight - 1 );
+
+ tools::Long nSize = pAttr->GetWidth();
+ tools::Long nSizeX = static_cast<tools::Long>(nSize*mnPPTX);
+ if (nSizeX >= nMaxWidth) nSizeX = nMaxWidth-1;
+ tools::Long nSizeY = static_cast<tools::Long>(nSize*mnPPTY);
+ if (nSizeY >= nRowHeight) nSizeY = nRowHeight-1;
+
+ nSizeX *= nLayoutSign; // used only to add to rectangle values
+
+ SvxShadowLocation eLoc = pAttr->GetLocation();
+ if ( bLayoutRTL )
+ {
+ // Shadow location is specified as "visual" (right is always right),
+ // so the attribute's location value is mirrored here and in FillInfo.
+ switch (eLoc)
+ {
+ case SvxShadowLocation::BottomRight: eLoc = SvxShadowLocation::BottomLeft; break;
+ case SvxShadowLocation::BottomLeft: eLoc = SvxShadowLocation::BottomRight; break;
+ case SvxShadowLocation::TopRight: eLoc = SvxShadowLocation::TopLeft; break;
+ case SvxShadowLocation::TopLeft: eLoc = SvxShadowLocation::TopRight; break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+
+ if (ePart == SC_SHADOW_HORIZ || ePart == SC_SHADOW_HSTART ||
+ ePart == SC_SHADOW_CORNER)
+ {
+ if (eLoc == SvxShadowLocation::TopLeft || eLoc == SvxShadowLocation::TopRight)
+ aRect.SetTop( aRect.Bottom() - nSizeY );
+ else
+ aRect.SetBottom( aRect.Top() + nSizeY );
+ }
+ if (ePart == SC_SHADOW_VERT || ePart == SC_SHADOW_VSTART ||
+ ePart == SC_SHADOW_CORNER)
+ {
+ if (eLoc == SvxShadowLocation::TopLeft || eLoc == SvxShadowLocation::BottomLeft)
+ aRect.SetLeft( aRect.Right() - nSizeX );
+ else
+ aRect.SetRight( aRect.Left() + nSizeX );
+ }
+ if (ePart == SC_SHADOW_HSTART)
+ {
+ if (eLoc == SvxShadowLocation::TopLeft || eLoc == SvxShadowLocation::BottomLeft)
+ aRect.AdjustRight( -nSizeX );
+ else
+ aRect.AdjustLeft(nSizeX );
+ }
+ if (ePart == SC_SHADOW_VSTART)
+ {
+ if (eLoc == SvxShadowLocation::TopLeft || eLoc == SvxShadowLocation::TopRight)
+ aRect.AdjustBottom( -nSizeY );
+ else
+ aRect.AdjustTop(nSizeY );
+ }
+
+ //! merge rectangles?
+ mpDev->SetFillColor( bCellContrast ? aAutoTextColor : pAttr->GetColor() );
+ mpDev->DrawRect( aRect );
+ }
+ }
+ }
+
+ nPosX += pRowInfo[0].basicCellInfo(nCol).nWidth * nLayoutSign;
+ }
+ }
+ nPosY += nRowHeight;
+ }
+}
+
+void ScOutputData::DrawClear()
+{
+ tools::Rectangle aRect;
+ Size aOnePixel = mpDev->PixelToLogic(Size(1,1));
+ tools::Long nOneX = aOnePixel.Width();
+ tools::Long nOneY = aOnePixel.Height();
+
+ // (called only for ScGridWindow)
+ Color aBgColor( SC_MOD()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor );
+
+ if (bMetaFile)
+ nOneX = nOneY = 0;
+
+ mpDev->SetLineColor();
+
+ mpDev->SetFillColor( aBgColor );
+
+ tools::Long nPosY = nScrY;
+ for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
+ {
+ RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+ tools::Long nRowHeight = pThisRowInfo->nHeight;
+
+ if ( pThisRowInfo->bChanged )
+ {
+ // scan for more rows which must be painted:
+ SCSIZE nSkip = 0;
+ while ( nArrY+nSkip+2<nArrCount && pRowInfo[nArrY+nSkip+1].bChanged )
+ {
+ ++nSkip;
+ nRowHeight += pRowInfo[nArrY+nSkip].nHeight; // after incrementing
+ }
+
+ aRect = tools::Rectangle( Point( nScrX, nPosY ),
+ Size( nScrW+1-nOneX, nRowHeight+1-nOneY) );
+ mpDev->DrawRect( aRect );
+
+ nArrY += nSkip;
+ }
+ nPosY += nRowHeight;
+ }
+}
+
+// Lines
+
+static tools::Long lclGetSnappedX( const OutputDevice& rDev, tools::Long nPosX, bool bSnapPixel )
+{
+ return (bSnapPixel && nPosX) ? rDev.PixelToLogic( rDev.LogicToPixel( Size( nPosX, 0 ) ) ).Width() : nPosX;
+}
+
+static tools::Long lclGetSnappedY( const OutputDevice& rDev, tools::Long nPosY, bool bSnapPixel )
+{
+ return (bSnapPixel && nPosY) ? rDev.PixelToLogic( rDev.LogicToPixel( Size( 0, nPosY ) ) ).Height() : nPosY;
+}
+
+void ScOutputData::DrawFrame(vcl::RenderContext& rRenderContext)
+{
+ DrawModeFlags nOldDrawMode = rRenderContext.GetDrawMode();
+
+ Color aSingleColor;
+ bool bUseSingleColor = false;
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ bool bCellContrast = mbUseStyleColor && rStyleSettings.GetHighContrastMode();
+
+ // if a Calc OLE object is embedded in Draw/Impress, the VCL DrawMode is used
+ // for display mode / B&W printing. The VCL DrawMode handling doesn't work for lines
+ // that are drawn with DrawRect, so if the line/background bits are set, the DrawMode
+ // must be reset and the border colors handled here.
+
+ if ( ( nOldDrawMode & DrawModeFlags::WhiteFill ) && ( nOldDrawMode & DrawModeFlags::BlackLine ) )
+ {
+ rRenderContext.SetDrawMode( nOldDrawMode & (~DrawModeFlags::WhiteFill) );
+ aSingleColor = COL_BLACK;
+ bUseSingleColor = true;
+ }
+ else if ( ( nOldDrawMode & DrawModeFlags::SettingsFill ) && ( nOldDrawMode & DrawModeFlags::SettingsLine ) )
+ {
+ rRenderContext.SetDrawMode( nOldDrawMode & (~DrawModeFlags::SettingsFill) );
+ aSingleColor = rStyleSettings.GetWindowTextColor(); // same as used in VCL for DrawModeFlags::SettingsLine
+ bUseSingleColor = true;
+ }
+ else if ( bCellContrast )
+ {
+ aSingleColor = SC_MOD()->GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor;
+ bUseSingleColor = true;
+ }
+
+ const Color* pForceColor = bUseSingleColor ? &aSingleColor : nullptr;
+
+ if (mrTabInfo.maArray.HasCellRotation())
+ {
+ DrawRotatedFrame(rRenderContext); // removes the lines that must not be painted here
+ }
+
+ tools::Long nInitPosX = nScrX;
+ if ( bLayoutRTL )
+ {
+ Size aOnePixel = rRenderContext.PixelToLogic(Size(1,1));
+ tools::Long nOneX = aOnePixel.Width();
+ nInitPosX += nMirrorW - nOneX;
+ }
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ // *** set column and row sizes of the frame border array ***
+
+ svx::frame::Array& rArray = mrTabInfo.maArray;
+ size_t nColCount = rArray.GetColCount();
+ size_t nRowCount = rArray.GetRowCount();
+
+ // row heights
+
+ // row 0 is not visible (dummy for borders from top) - subtract its height from initial position
+ // subtract 1 unit more, because position 0 is first *in* cell, grid line is one unit before
+ tools::Long nOldPosY = nScrY - 1 - pRowInfo[ 0 ].nHeight;
+ tools::Long nOldSnapY = lclGetSnappedY( rRenderContext, nOldPosY, bSnapPixel );
+ rArray.SetYOffset( nOldSnapY );
+ for( size_t nRow = 0; nRow < nRowCount; ++nRow )
+ {
+ tools::Long nNewPosY = nOldPosY + pRowInfo[ nRow ].nHeight;
+ tools::Long nNewSnapY = lclGetSnappedY( rRenderContext, nNewPosY, bSnapPixel );
+ rArray.SetRowHeight( nRow, nNewSnapY - nOldSnapY );
+ nOldPosY = nNewPosY;
+ nOldSnapY = nNewSnapY;
+ }
+
+ // column widths
+
+ // column nX1-1 is not visible (dummy for borders from left) - subtract its width from initial position
+ // subtract 1 unit more, because position 0 is first *in* cell, grid line is one unit above
+ tools::Long nOldPosX = nInitPosX - nLayoutSign * (1 + pRowInfo[ 0 ].basicCellInfo( nX1 - 1 ).nWidth);
+ tools::Long nOldSnapX = lclGetSnappedX( rRenderContext, nOldPosX, bSnapPixel );
+ // set X offset for left-to-right sheets; for right-to-left sheets this is done after for() loop
+ if( !bLayoutRTL )
+ rArray.SetXOffset( nOldSnapX );
+ for( SCCOL nCol = nX1 - 1; nCol <= nX2 + 1; ++nCol )
+ {
+ size_t nArrCol = bLayoutRTL ? nX2 + 1 - nCol : nCol - (nX1 - 1);
+ tools::Long nNewPosX = nOldPosX + pRowInfo[ 0 ].basicCellInfo( nCol ).nWidth * nLayoutSign;
+ tools::Long nNewSnapX = lclGetSnappedX( rRenderContext, nNewPosX, bSnapPixel );
+ rArray.SetColWidth( nArrCol, std::abs( nNewSnapX - nOldSnapX ) );
+ nOldPosX = nNewPosX;
+ nOldSnapX = nNewSnapX;
+ }
+ if( bLayoutRTL )
+ rArray.SetXOffset( nOldSnapX );
+
+ // *** draw the array ***
+
+ size_t nFirstCol = 1;
+ size_t nFirstRow = 1;
+ size_t nLastCol = nColCount - 2;
+ size_t nLastRow = nRowCount - 2;
+
+ if( mrTabInfo.mbPageMode )
+ rArray.SetClipRange( nFirstCol, nFirstRow, nLastCol, nLastRow );
+
+ // draw only rows with set RowInfo::bChanged flag
+ size_t nRow1 = nFirstRow;
+ std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor(CreateProcessor2D());
+ if (!pProcessor)
+ return;
+ while( nRow1 <= nLastRow )
+ {
+ while( (nRow1 <= nLastRow) && !pRowInfo[ nRow1 ].bChanged ) ++nRow1;
+ if( nRow1 <= nLastRow )
+ {
+ size_t nRow2 = nRow1;
+ while( (nRow2 + 1 <= nLastRow) && pRowInfo[ nRow2 + 1 ].bChanged ) ++nRow2;
+ auto xPrimitive = rArray.CreateB2DPrimitiveRange(
+ nFirstCol, nRow1, nLastCol, nRow2, pForceColor );
+ pProcessor->process(xPrimitive);
+ nRow1 = nRow2 + 1;
+ }
+ }
+ pProcessor.reset();
+
+ rRenderContext.SetDrawMode(nOldDrawMode);
+}
+
+void ScOutputData::DrawRotatedFrame(vcl::RenderContext& rRenderContext)
+{
+ //! save nRotMax
+ SCCOL nRotMax = nX2;
+ for (SCSIZE nRotY=0; nRotY<nArrCount; nRotY++)
+ if (pRowInfo[nRotY].nRotMaxCol != SC_ROTMAX_NONE && pRowInfo[nRotY].nRotMaxCol > nRotMax)
+ nRotMax = pRowInfo[nRotY].nRotMaxCol;
+
+ const ScPatternAttr* pPattern;
+ const SfxItemSet* pCondSet;
+
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ bool bCellContrast = mbUseStyleColor && rStyleSettings.GetHighContrastMode();
+
+ tools::Long nInitPosX = nScrX;
+ if ( bLayoutRTL )
+ {
+ Size aOnePixel = rRenderContext.PixelToLogic(Size(1,1));
+ tools::Long nOneX = aOnePixel.Width();
+ nInitPosX += nMirrorW - nOneX;
+ }
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ tools::Rectangle aClipRect( Point(nScrX, nScrY), Size(nScrW, nScrH) );
+ if (bMetaFile)
+ {
+ rRenderContext.Push();
+ rRenderContext.IntersectClipRegion( aClipRect );
+ }
+ else
+ rRenderContext.SetClipRegion( vcl::Region( aClipRect ) );
+
+ std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor(CreateProcessor2D( ));
+ tools::Long nPosY = nScrY;
+ for (SCSIZE nArrY=1; nArrY<nArrCount; nArrY++)
+ {
+ // Rotated is also drawn one line above/below Changed if parts extend into the cell
+
+ RowInfo& rPrevRowInfo = pRowInfo[nArrY-1];
+ RowInfo& rThisRowInfo = pRowInfo[nArrY];
+ RowInfo& rNextRowInfo = pRowInfo[nArrY+1];
+
+ tools::Long nRowHeight = rThisRowInfo.nHeight;
+ if ( rThisRowInfo.nRotMaxCol != SC_ROTMAX_NONE &&
+ ( rThisRowInfo.bChanged || rPrevRowInfo.bChanged ||
+ ( nArrY+1<nArrCount && rNextRowInfo.bChanged ) ) )
+ {
+ SCROW nY = rThisRowInfo.nRowNo;
+ tools::Long nPosX = 0;
+ SCCOL nX;
+ for (nX=0; nX<=nRotMax; nX++)
+ {
+ if (nX==nX1) nPosX = nInitPosX; // calculated individually for preceding positions
+
+ ScCellInfo* pInfo = &rThisRowInfo.cellInfo(nX);
+ tools::Long nColWidth = pRowInfo[0].basicCellInfo(nX).nWidth;
+ if ( pInfo->nRotateDir > ScRotateDir::Standard &&
+ !pInfo->bHOverlapped && !pInfo->bVOverlapped )
+ {
+ pPattern = pInfo->pPatternAttr;
+ pCondSet = pInfo->pConditionSet;
+ if (!pPattern)
+ {
+ pPattern = mpDoc->GetPattern( nX, nY, nTab );
+ pInfo->pPatternAttr = pPattern;
+ pCondSet = mpDoc->GetCondResult( nX, nY, nTab );
+ pInfo->pConditionSet = pCondSet;
+ }
+
+ //! LastPattern etc.
+
+ Degree100 nAttrRotate = pPattern->GetRotateVal( pCondSet );
+ SvxRotateMode eRotMode =
+ pPattern->GetItem(ATTR_ROTATE_MODE, pCondSet).GetValue();
+
+ if (nAttrRotate)
+ {
+ if (nX < nX1) // compute negative position
+ {
+ nPosX = nInitPosX;
+ SCCOL nCol = nX1;
+ while (nCol > nX)
+ {
+ --nCol;
+ nPosX -= nLayoutSign * static_cast<tools::Long>(pRowInfo[0].basicCellInfo(nCol).nWidth);
+ }
+ }
+
+ // start position minus 1 so rotated backgrounds suit the border
+ // (border is on the grid)
+
+ tools::Long nTop = nPosY - 1;
+ tools::Long nBottom = nPosY + nRowHeight - 1;
+ tools::Long nTopLeft = nPosX - nLayoutSign;
+ tools::Long nTopRight = nPosX + (nColWidth - 1) * nLayoutSign;
+ tools::Long nBotLeft = nTopLeft;
+ tools::Long nBotRight = nTopRight;
+
+ // inclusion of the sign here hasn't been decided yet
+ // (if not, the extension of the non-rotated background must also be changed)
+ double nRealOrient = nLayoutSign * toRadians(nAttrRotate); // 1/100th degrees
+ double nCos = cos(nRealOrient);
+ double nSin = sin(nRealOrient);
+ //! restrict !!!
+ tools::Long nSkew = static_cast<tools::Long>(nRowHeight * nCos / nSin);
+
+ switch (eRotMode)
+ {
+ case SVX_ROTATE_MODE_BOTTOM:
+ nTopLeft += nSkew;
+ nTopRight += nSkew;
+ break;
+ case SVX_ROTATE_MODE_CENTER:
+ nSkew /= 2;
+ nTopLeft += nSkew;
+ nTopRight += nSkew;
+ nBotLeft -= nSkew;
+ nBotRight -= nSkew;
+ break;
+ case SVX_ROTATE_MODE_TOP:
+ nBotLeft -= nSkew;
+ nBotRight -= nSkew;
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+
+ Point aPoints[4];
+ aPoints[0] = Point(nTopLeft, nTop);
+ aPoints[1] = Point(nTopRight, nTop);
+ aPoints[2] = Point(nBotRight, nBottom);
+ aPoints[3] = Point(nBotLeft, nBottom);
+
+ const SvxBrushItem* pBackground = pInfo->pBackground;
+ if (!pBackground)
+ pBackground = &pPattern->GetItem(ATTR_BACKGROUND, pCondSet);
+ if (bCellContrast)
+ {
+ // high contrast for cell borders and backgrounds -> empty background
+ pBackground = ScGlobal::GetEmptyBrushItem();
+ }
+ if (!pInfo->mxColorScale)
+ {
+ const Color& rColor = pBackground->GetColor();
+ if (rColor.GetAlpha() != 0)
+ {
+ // draw background only for the changed row itself
+ // (background doesn't extend into other cells).
+ // For the borders (rotated and normal), clipping should be
+ // set if the row isn't changed, but at least the borders
+ // don't cover the cell contents.
+ if (rThisRowInfo.bChanged)
+ {
+ tools::Polygon aPoly(4, aPoints);
+
+ // for DrawPolygon, without Pen one pixel is left out
+ // to the right and below...
+ if (!rColor.IsTransparent())
+ rRenderContext.SetLineColor(rColor);
+ else
+ rRenderContext.SetLineColor();
+ rRenderContext.SetFillColor(rColor);
+ rRenderContext.DrawPolygon(aPoly);
+ }
+ }
+ }
+ else
+ {
+ tools::Polygon aPoly(4, aPoints);
+ std::optional<Color> const & pColor = pInfo->mxColorScale;
+
+ // for DrawPolygon, without Pen one pixel is left out
+ // to the right and below...
+ if (!pColor->IsTransparent())
+ rRenderContext.SetLineColor(*pColor);
+ else
+ rRenderContext.SetLineColor();
+ rRenderContext.SetFillColor(*pColor);
+ rRenderContext.DrawPolygon(aPoly);
+
+ }
+ }
+ }
+ nPosX += nColWidth * nLayoutSign;
+ }
+ }
+ nPosY += nRowHeight;
+ }
+
+ pProcessor.reset();
+
+ if (bMetaFile)
+ rRenderContext.Pop();
+ else
+ rRenderContext.SetClipRegion();
+}
+
+std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> ScOutputData::CreateProcessor2D( )
+{
+ mpDoc->InitDrawLayer(mpDoc->GetDocumentShell());
+ ScDrawLayer* pDrawLayer = mpDoc->GetDrawLayer();
+ if (!pDrawLayer)
+ return nullptr;
+
+ basegfx::B2DRange aViewRange;
+ SdrPage *pDrawPage = pDrawLayer->GetPage( static_cast< sal_uInt16 >( nTab ) );
+ drawinglayer::geometry::ViewInformation2D aNewViewInfos;
+ aNewViewInfos.setViewTransformation(mpDev->GetViewTransformation());
+ aNewViewInfos.setViewport(aViewRange);
+ aNewViewInfos.setVisualizedPage(GetXDrawPageForSdrPage( pDrawPage ));
+
+ return drawinglayer::processor2d::createProcessor2DFromOutputDevice(
+ *mpDev, aNewViewInfos );
+}
+
+// Printer
+
+vcl::Region ScOutputData::GetChangedAreaRegion()
+{
+ vcl::Region aRegion;
+ tools::Rectangle aDrawingRect;
+ bool bHad(false);
+ tools::Long nPosY = nScrY;
+ SCSIZE nArrY;
+
+ aDrawingRect.SetLeft( nScrX );
+ aDrawingRect.SetRight( nScrX+nScrW-1 );
+
+ for(nArrY=1; nArrY+1<nArrCount; nArrY++)
+ {
+ RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+
+ if(pThisRowInfo->bChanged)
+ {
+ if(!bHad)
+ {
+ aDrawingRect.SetTop( nPosY );
+ bHad = true;
+ }
+
+ aDrawingRect.SetBottom( nPosY + pRowInfo[nArrY].nHeight - 1 );
+ }
+ else if(bHad)
+ {
+ aRegion.Union(mpDev->PixelToLogic(aDrawingRect));
+ bHad = false;
+ }
+
+ nPosY += pRowInfo[nArrY].nHeight;
+ }
+
+ if(bHad)
+ {
+ aRegion.Union(mpDev->PixelToLogic(aDrawingRect));
+ }
+
+ return aRegion;
+}
+
+bool ScOutputData::SetChangedClip()
+{
+ tools::PolyPolygon aPoly;
+
+ tools::Rectangle aDrawingRect;
+ aDrawingRect.SetLeft( nScrX );
+ aDrawingRect.SetRight( nScrX+nScrW-1 );
+
+ bool bHad = false;
+ tools::Long nPosY = nScrY;
+ SCSIZE nArrY;
+ for (nArrY=1; nArrY+1<nArrCount; nArrY++)
+ {
+ RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+
+ if ( pThisRowInfo->bChanged )
+ {
+ if (!bHad)
+ {
+ aDrawingRect.SetTop( nPosY );
+ bHad = true;
+ }
+ aDrawingRect.SetBottom( nPosY + pRowInfo[nArrY].nHeight - 1 );
+ }
+ else if (bHad)
+ {
+ aPoly.Insert( tools::Polygon( mpDev->PixelToLogic(aDrawingRect) ) );
+ bHad = false;
+ }
+ nPosY += pRowInfo[nArrY].nHeight;
+ }
+
+ if (bHad)
+ aPoly.Insert( tools::Polygon( mpDev->PixelToLogic(aDrawingRect) ) );
+
+ bool bRet = (aPoly.Count() != 0);
+ if (bRet)
+ mpDev->SetClipRegion(vcl::Region(aPoly));
+ return bRet;
+}
+
+void ScOutputData::FindChanged()
+{
+ SCCOL nX;
+ SCSIZE nArrY;
+
+ bool bWasIdleEnabled = mpDoc->IsIdleEnabled();
+ mpDoc->EnableIdle(false);
+ for (nArrY=0; nArrY<nArrCount; nArrY++)
+ pRowInfo[nArrY].bChanged = false;
+
+ SCCOL nCol1 = mpDoc->MaxCol(), nCol2 = 0;
+ SCROW nRow1 = mpDoc->MaxRow(), nRow2 = 0;
+ bool bAnyDirty = false;
+ bool bAnyChanged = false;
+
+ for (nArrY=0; nArrY<nArrCount; nArrY++)
+ {
+ RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+ for (nX=nX1; nX<=nX2; nX++)
+ {
+ const ScRefCellValue& rCell = pThisRowInfo->cellInfo(nX).maCell;
+
+ if (rCell.getType() != CELLTYPE_FORMULA)
+ continue;
+
+ ScFormulaCell* pFCell = rCell.getFormula();
+ if (pFCell->IsRunning())
+ // still being interpreted. Skip it.
+ continue;
+
+ bool bDirty = pFCell->GetDirty();
+ bAnyChanged = bAnyChanged || pFCell->IsChanged();
+
+ if (bDirty)
+ {
+ if (!bAnyDirty)
+ {
+ ScProgress::CreateInterpretProgress(mpDoc);
+ bAnyDirty = true;
+ }
+
+ ScAddress& rPos(pFCell->aPos);
+ nCol1 = std::min(rPos.Col(), nCol1);
+ nCol2 = std::max(rPos.Col(), nCol2);
+ nRow1 = std::min(rPos.Row(), nRow1);
+ nRow2 = std::max(rPos.Row(), nRow2);
+
+ const SfxUInt32Item* pItem = mpDoc->GetAttr(rPos, ATTR_VALIDDATA);
+ const ScValidationData* pData = mpDoc->GetValidationEntry(pItem->GetValue());
+ if (pData)
+ {
+ ScRefCellValue aCell(*mpDoc, rPos);
+ if (pData->IsDataValid(aCell, rPos))
+ ScDetectiveFunc(*mpDoc, rPos.Tab()).DeleteCirclesAt(rPos.Col(), rPos.Row());
+ }
+ }
+ }
+ }
+
+ if (bAnyDirty || bAnyChanged)
+ {
+ if (bAnyDirty)
+ mpDoc->EnsureFormulaCellResults(ScRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab), true);
+
+ for (nArrY=0; nArrY<nArrCount; nArrY++)
+ {
+ RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+ for (nX=nX1; nX<=nX2; nX++)
+ {
+ const ScRefCellValue& rCell = pThisRowInfo->cellInfo(nX).maCell;
+
+ if (rCell.getType() != CELLTYPE_FORMULA)
+ continue;
+
+ ScFormulaCell* pFCell = rCell.getFormula();
+ if (pFCell->IsRunning())
+ // still being interpreted. Skip it.
+ continue;
+
+ if (!pFCell->IsChanged())
+ // the result hasn't changed. Skip it.
+ continue;
+
+ pThisRowInfo->bChanged = true;
+ if ( pThisRowInfo->cellInfo(nX).bMerged )
+ {
+ SCSIZE nOverY = nArrY + 1;
+ while ( nOverY<nArrCount &&
+ pRowInfo[nOverY].cellInfo(nX).bVOverlapped )
+ {
+ pRowInfo[nOverY].bChanged = true;
+ ++nOverY;
+ }
+ }
+ }
+ }
+
+ if (bAnyDirty)
+ ScProgress::DeleteInterpretProgress();
+ }
+
+ mpDoc->EnableIdle(bWasIdleEnabled);
+}
+
+ReferenceMark ScOutputData::FillReferenceMark( SCCOL nRefStartX, SCROW nRefStartY,
+ SCCOL nRefEndX, SCROW nRefEndY, const Color& rColor)
+{
+ ReferenceMark aResult;
+
+ PutInOrder( nRefStartX, nRefEndX );
+ PutInOrder( nRefStartY, nRefEndY );
+
+ if ( nRefStartX == nRefEndX && nRefStartY == nRefEndY )
+ mpDoc->ExtendMerge( nRefStartX, nRefStartY, nRefEndX, nRefEndY, nTab );
+
+ if ( nRefStartX <= nVisX2 && nRefEndX >= nVisX1 &&
+ nRefStartY <= nVisY2 && nRefEndY >= nVisY1 )
+ {
+ tools::Long nMinX = nScrX;
+ tools::Long nMinY = nScrY;
+ tools::Long nMaxX = nScrX + nScrW - 1;
+ tools::Long nMaxY = nScrY + nScrH - 1;
+ if ( bLayoutRTL )
+ std::swap( nMinX, nMaxX );
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ bool bTop = false;
+ bool bBottom = false;
+ bool bLeft = false;
+ bool bRight = false;
+
+ tools::Long nPosY = nScrY;
+ bool bNoStartY = ( nY1 < nRefStartY );
+ bool bNoEndY = false;
+ for (SCSIZE nArrY=1; nArrY<nArrCount; nArrY++) // loop to end for bNoEndY check
+ {
+ SCROW nY = pRowInfo[nArrY].nRowNo;
+
+ if ( nY==nRefStartY || (nY>nRefStartY && bNoStartY) )
+ {
+ nMinY = nPosY;
+ bTop = true;
+ }
+ if ( nY==nRefEndY )
+ {
+ nMaxY = nPosY + pRowInfo[nArrY].nHeight - 2;
+ bBottom = true;
+ }
+ if ( nY>nRefEndY && bNoEndY )
+ {
+ nMaxY = nPosY-2;
+ bBottom = true;
+ }
+ bNoStartY = ( nY < nRefStartY );
+ bNoEndY = ( nY < nRefEndY );
+ nPosY += pRowInfo[nArrY].nHeight;
+ }
+
+ tools::Long nPosX = nScrX;
+ if ( bLayoutRTL )
+ nPosX += nMirrorW - 1; // always in pixels
+
+ for (SCCOL nX=nX1; nX<=nX2; nX++)
+ {
+ if ( nX==nRefStartX )
+ {
+ nMinX = nPosX;
+ bLeft = true;
+ }
+ if ( nX==nRefEndX )
+ {
+ nMaxX = nPosX + ( pRowInfo[0].basicCellInfo(nX).nWidth - 2 ) * nLayoutSign;
+ bRight = true;
+ }
+ nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
+ }
+
+ if (bTop && bBottom && bLeft && bRight)
+ {
+ // mnPPT[XY] already has the factor aZoom[XY] in it.
+ aResult = ReferenceMark( nMinX / mnPPTX,
+ nMinY / mnPPTY,
+ ( nMaxX - nMinX ) / mnPPTX,
+ ( nMaxY - nMinY ) / mnPPTY,
+ nTab,
+ rColor );
+ }
+ }
+
+ return aResult;
+}
+
+void ScOutputData::DrawRefMark( SCCOL nRefStartX, SCROW nRefStartY,
+ SCCOL nRefEndX, SCROW nRefEndY,
+ const Color& rColor, bool bHandle )
+{
+ PutInOrder( nRefStartX, nRefEndX );
+ PutInOrder( nRefStartY, nRefEndY );
+
+ if ( nRefStartX == nRefEndX && nRefStartY == nRefEndY )
+ mpDoc->ExtendMerge( nRefStartX, nRefStartY, nRefEndX, nRefEndY, nTab );
+ else if (mpDoc->HasAttrib(nRefEndX, nRefEndY, nTab, HasAttrFlags::Merged))
+ mpDoc->ExtendMerge(nRefEndX, nRefEndY, nRefEndX, nRefEndY, nTab);
+
+ if ( !(nRefStartX <= nVisX2 && nRefEndX >= nVisX1 &&
+ nRefStartY <= nVisY2 && nRefEndY >= nVisY1) )
+ return;
+
+ tools::Long nMinX = nScrX;
+ tools::Long nMinY = nScrY;
+ tools::Long nMaxX = nScrX + nScrW - 1;
+ tools::Long nMaxY = nScrY + nScrH - 1;
+ if ( bLayoutRTL )
+ std::swap( nMinX, nMaxX );
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ bool bTop = false;
+ bool bBottom = false;
+ bool bLeft = false;
+ bool bRight = false;
+
+ tools::Long nPosY = nScrY;
+ bool bNoStartY = ( nY1 < nRefStartY );
+ bool bNoEndY = false;
+ for (SCSIZE nArrY=1; nArrY<nArrCount; nArrY++) // loop to end for bNoEndY check
+ {
+ SCROW nY = pRowInfo[nArrY].nRowNo;
+
+ if ( nY==nRefStartY || (nY>nRefStartY && bNoStartY) )
+ {
+ nMinY = nPosY;
+ bTop = true;
+ }
+ if ( nY==nRefEndY )
+ {
+ nMaxY = nPosY + pRowInfo[nArrY].nHeight - 2;
+ bBottom = true;
+ }
+ if ( nY>nRefEndY && bNoEndY )
+ {
+ nMaxY = nPosY-2;
+ bBottom = true;
+ }
+ bNoStartY = ( nY < nRefStartY );
+ bNoEndY = ( nY < nRefEndY );
+ nPosY += pRowInfo[nArrY].nHeight;
+ }
+
+ tools::Long nPosX = nScrX;
+ if ( bLayoutRTL )
+ nPosX += nMirrorW - 1; // always in pixels
+
+ for (SCCOL nX=nX1; nX<=nX2; nX++)
+ {
+ if ( nX==nRefStartX )
+ {
+ nMinX = nPosX;
+ bLeft = true;
+ }
+ if ( nX==nRefEndX )
+ {
+ nMaxX = nPosX + ( pRowInfo[0].basicCellInfo(nX).nWidth - 2 ) * nLayoutSign;
+ bRight = true;
+ }
+ nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
+ }
+
+ if ( nMaxX * nLayoutSign < nMinX * nLayoutSign || nMaxY < nMinY )
+ return;
+
+ mpDev->SetLineColor( rColor );
+ if (bTop && bBottom && bLeft && bRight && !comphelper::LibreOfficeKit::isActive() )
+ {
+ mpDev->SetFillColor();
+ mpDev->DrawRect( tools::Rectangle( nMinX, nMinY, nMaxX, nMaxY ) );
+ }
+ else if ( !comphelper::LibreOfficeKit::isActive() )
+ {
+ if (bTop)
+ mpDev->DrawLine( Point( nMinX, nMinY ), Point( nMaxX, nMinY ) );
+ if (bBottom)
+ mpDev->DrawLine( Point( nMinX, nMaxY ), Point( nMaxX, nMaxY ) );
+ if (bLeft)
+ mpDev->DrawLine( Point( nMinX, nMinY ), Point( nMinX, nMaxY ) );
+ if (bRight)
+ mpDev->DrawLine( Point( nMaxX, nMinY ), Point( nMaxX, nMaxY ) );
+ }
+ if ( !bHandle || !bRight || !bBottom || comphelper::LibreOfficeKit::isActive() )
+ return;
+
+ mpDev->SetLineColor( rColor );
+ mpDev->SetFillColor( rColor );
+
+ const sal_Int32 aRadius = 4;
+
+ sal_Int32 aRectMaxX1 = nMaxX - nLayoutSign * aRadius;
+ sal_Int32 aRectMaxX2 = nMaxX + nLayoutSign;
+ sal_Int32 aRectMinX1 = nMinX - nLayoutSign;
+ sal_Int32 aRectMinX2 = nMinX + nLayoutSign * aRadius;
+
+ sal_Int32 aRectMaxY1 = nMaxY - aRadius;
+ sal_Int32 aRectMaxY2 = nMaxY + 1;
+ sal_Int32 aRectMinY1 = nMinY - 1;
+ sal_Int32 aRectMinY2 = nMinY + aRadius;
+
+ // Draw corner rectangles
+ tools::Rectangle aLowerRight( aRectMaxX1, aRectMaxY1, aRectMaxX2, aRectMaxY2 );
+ tools::Rectangle aUpperLeft ( aRectMinX1, aRectMinY1, aRectMinX2, aRectMinY2 );
+ tools::Rectangle aLowerLeft ( aRectMinX1, aRectMaxY1, aRectMinX2, aRectMaxY2 );
+ tools::Rectangle aUpperRight( aRectMaxX1, aRectMinY1, aRectMaxX2, aRectMinY2 );
+
+ mpDev->DrawTransparent( tools::PolyPolygon( tools::Polygon( aLowerRight ) ), lclCornerRectTransparency );
+ mpDev->DrawTransparent( tools::PolyPolygon( tools::Polygon( aUpperLeft ) ), lclCornerRectTransparency );
+ mpDev->DrawTransparent( tools::PolyPolygon( tools::Polygon( aLowerLeft ) ), lclCornerRectTransparency );
+ mpDev->DrawTransparent( tools::PolyPolygon( tools::Polygon( aUpperRight ) ), lclCornerRectTransparency );
+}
+
+void ScOutputData::DrawOneChange( SCCOL nRefStartX, SCROW nRefStartY,
+ SCCOL nRefEndX, SCROW nRefEndY,
+ const Color& rColor, sal_uInt16 nType )
+{
+ PutInOrder( nRefStartX, nRefEndX );
+ PutInOrder( nRefStartY, nRefEndY );
+
+ if ( nRefStartX == nRefEndX && nRefStartY == nRefEndY )
+ mpDoc->ExtendMerge( nRefStartX, nRefStartY, nRefEndX, nRefEndY, nTab );
+
+ if ( !(nRefStartX <= nVisX2 + 1 && nRefEndX >= nVisX1 &&
+ nRefStartY <= nVisY2 + 1 && nRefEndY >= nVisY1) ) // +1 because it touches next cells left/top
+ return;
+
+ tools::Long nMinX = nScrX;
+ tools::Long nMinY = nScrY;
+ tools::Long nMaxX = nScrX+nScrW-1;
+ tools::Long nMaxY = nScrY+nScrH-1;
+ if ( bLayoutRTL )
+ std::swap( nMinX, nMaxX );
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ bool bTop = false;
+ bool bBottom = false;
+ bool bLeft = false;
+ bool bRight = false;
+
+ tools::Long nPosY = nScrY;
+ bool bNoStartY = ( nY1 < nRefStartY );
+ bool bNoEndY = false;
+ for (SCSIZE nArrY=1; nArrY<nArrCount; nArrY++) // loop to end for bNoEndY check
+ {
+ SCROW nY = pRowInfo[nArrY].nRowNo;
+
+ if ( nY==nRefStartY || (nY>nRefStartY && bNoStartY) )
+ {
+ nMinY = nPosY - 1;
+ bTop = true;
+ }
+ if ( nY==nRefEndY )
+ {
+ nMaxY = nPosY + pRowInfo[nArrY].nHeight - 1;
+ bBottom = true;
+ }
+ if ( nY>nRefEndY && bNoEndY )
+ {
+ nMaxY = nPosY - 1;
+ bBottom = true;
+ }
+ bNoStartY = ( nY < nRefStartY );
+ bNoEndY = ( nY < nRefEndY );
+ nPosY += pRowInfo[nArrY].nHeight;
+ }
+
+ tools::Long nPosX = nScrX;
+ if ( bLayoutRTL )
+ nPosX += nMirrorW - 1; // always in pixels
+
+ for (SCCOL nX=nX1; nX<=nX2+1; nX++)
+ {
+ if ( nX==nRefStartX )
+ {
+ nMinX = nPosX - nLayoutSign;
+ bLeft = true;
+ }
+ if ( nX==nRefEndX )
+ {
+ nMaxX = nPosX + ( pRowInfo[0].basicCellInfo(nX).nWidth - 1 ) * nLayoutSign;
+ bRight = true;
+ }
+ nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
+ }
+
+ if ( nMaxX * nLayoutSign < nMinX * nLayoutSign || nMaxY < nMinY )
+ return;
+
+ if ( nType == SC_CAT_DELETE_ROWS )
+ bLeft = bRight = bBottom = false; //! thick lines???
+ else if ( nType == SC_CAT_DELETE_COLS )
+ bTop = bBottom = bRight = false; //! thick lines???
+
+ mpDev->SetLineColor( rColor );
+ if (bTop && bBottom && bLeft && bRight)
+ {
+ mpDev->SetFillColor();
+ mpDev->DrawRect( tools::Rectangle( nMinX, nMinY, nMaxX, nMaxY ) );
+ }
+ else
+ {
+ if (bTop)
+ {
+ mpDev->DrawLine( Point( nMinX,nMinY ), Point( nMaxX,nMinY ) );
+ if ( nType == SC_CAT_DELETE_ROWS )
+ mpDev->DrawLine( Point( nMinX,nMinY+1 ), Point( nMaxX,nMinY+1 ) );
+ }
+ if (bBottom)
+ mpDev->DrawLine( Point( nMinX,nMaxY ), Point( nMaxX,nMaxY ) );
+ if (bLeft)
+ {
+ mpDev->DrawLine( Point( nMinX,nMinY ), Point( nMinX,nMaxY ) );
+ if ( nType == SC_CAT_DELETE_COLS )
+ mpDev->DrawLine( Point( nMinX+nLayoutSign,nMinY ), Point( nMinX+nLayoutSign,nMaxY ) );
+ }
+ if (bRight)
+ mpDev->DrawLine( Point( nMaxX,nMinY ), Point( nMaxX,nMaxY ) );
+ }
+ if ( bLeft && bTop )
+ {
+ mpDev->SetLineColor();
+ mpDev->SetFillColor( rColor );
+ mpDev->DrawRect( tools::Rectangle( nMinX+nLayoutSign, nMinY+1, nMinX+3*nLayoutSign, nMinY+3 ) );
+ }
+}
+
+void ScOutputData::DrawChangeTrack()
+{
+ ScChangeTrack* pTrack = mpDoc->GetChangeTrack();
+ ScChangeViewSettings* pSettings = mpDoc->GetChangeViewSettings();
+ if ( !pTrack || !pTrack->GetFirst() || !pSettings || !pSettings->ShowChanges() )
+ return; // nothing there or hidden
+
+ ScActionColorChanger aColorChanger(*pTrack);
+
+ // clipping happens from the outside
+ //! without clipping, only paint affected cells ??!??!?
+
+ SCCOL nEndX = nX2;
+ SCROW nEndY = nY2;
+ if ( nEndX < mpDoc->MaxCol() ) ++nEndX; // also from the next cell since the mark
+ if ( nEndY < mpDoc->MaxRow() ) ++nEndY; // protrudes from the preceding cell
+ ScRange aViewRange( nX1, nY1, nTab, nEndX, nEndY, nTab );
+ const ScChangeAction* pAction = pTrack->GetFirst();
+ while (pAction)
+ {
+ if ( pAction->IsVisible() )
+ {
+ ScChangeActionType eActionType = pAction->GetType();
+ const ScBigRange& rBig = pAction->GetBigRange();
+ if ( rBig.aStart.Tab() == nTab )
+ {
+ ScRange aRange = rBig.MakeRange( *mpDoc );
+
+ if ( eActionType == SC_CAT_DELETE_ROWS )
+ aRange.aEnd.SetRow( aRange.aStart.Row() );
+ else if ( eActionType == SC_CAT_DELETE_COLS )
+ aRange.aEnd.SetCol( aRange.aStart.Col() );
+
+ if ( aRange.Intersects( aViewRange ) &&
+ ScViewUtil::IsActionShown( *pAction, *pSettings, *mpDoc ) )
+ {
+ aColorChanger.Update( *pAction );
+ Color aColor( aColorChanger.GetColor() );
+ DrawOneChange( aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row(), aColor, sal::static_int_cast<sal_uInt16>(eActionType) );
+
+ }
+ }
+ if ( eActionType == SC_CAT_MOVE &&
+ static_cast<const ScChangeActionMove*>(pAction)->
+ GetFromRange().aStart.Tab() == nTab )
+ {
+ ScRange aRange = static_cast<const ScChangeActionMove*>(pAction)->
+ GetFromRange().MakeRange( *mpDoc );
+ if ( aRange.Intersects( aViewRange ) &&
+ ScViewUtil::IsActionShown( *pAction, *pSettings, *mpDoc ) )
+ {
+ aColorChanger.Update( *pAction );
+ Color aColor( aColorChanger.GetColor() );
+ DrawOneChange( aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row(), aColor, sal::static_int_cast<sal_uInt16>(eActionType) );
+ }
+ }
+ }
+
+ pAction = pAction->GetNext();
+ }
+}
+
+void ScOutputData::DrawSparklines(vcl::RenderContext& rRenderContext)
+{
+ Size aOnePixel = rRenderContext.PixelToLogic(Size(1,1));
+ tools::Long nOneXLogic = aOnePixel.Width();
+ tools::Long nOneYLogic = aOnePixel.Height();
+
+ // See more about bWorksInPixels in ScOutputData::DrawGrid
+ bool bWorksInPixels = false;
+ if (eType == OUTTYPE_WINDOW)
+ bWorksInPixels = true;
+
+ tools::Long nOneX = 1;
+ tools::Long nOneY = 1;
+ if (!bWorksInPixels)
+ {
+ nOneX = nOneXLogic;
+ nOneY = nOneYLogic;
+ }
+
+ tools::Long nInitPosX = nScrX;
+ if ( bLayoutRTL )
+ nInitPosX += nMirrorW - 1; // always in pixels
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ tools::Long nPosY = nScrY;
+ for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
+ {
+ RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+ if ( pThisRowInfo->bChanged )
+ {
+ tools::Long nPosX = nInitPosX;
+ for (SCCOL nX=nX1; nX<=nX2; nX++)
+ {
+ ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
+ bool bIsMerged = false;
+
+ if ( nX==nX1 && pInfo->bHOverlapped && !pInfo->bVOverlapped )
+ {
+ // find start of merged cell
+ bIsMerged = true;
+ SCROW nY = pRowInfo[nArrY].nRowNo;
+ SCCOL nMergeX = nX;
+ SCROW nMergeY = nY;
+ mpDoc->ExtendOverlapped( nMergeX, nMergeY, nX, nY, nTab );
+ }
+
+ std::shared_ptr<sc::Sparkline> pSparkline;
+ ScAddress aCurrentAddress(nX, pRowInfo[nArrY].nRowNo, nTab);
+
+ if (!mpDoc->ColHidden(nX, nTab) && (pSparkline = mpDoc->GetSparkline(aCurrentAddress))
+ && (bIsMerged || (!pInfo->bHOverlapped && !pInfo->bVOverlapped)))
+ {
+ const tools::Long nWidth = pRowInfo[0].basicCellInfo(nX).nWidth;
+ const tools::Long nHeight = pThisRowInfo->nHeight;
+
+ Point aPoint(nPosX, nPosY);
+ Size aSize(nWidth, nHeight);
+
+ sc::SparklineRenderer renderer(*mpDoc);
+ renderer.render(pSparkline, rRenderContext, tools::Rectangle(aPoint, aSize), nOneX, nOneY, double(aZoomX), double(aZoomY));
+ }
+
+ nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
+ }
+ }
+ nPosY += pThisRowInfo->nHeight;
+ }
+
+}
+
+//TODO: moggi Need to check if this can't be written simpler
+void ScOutputData::DrawNoteMarks(vcl::RenderContext& rRenderContext)
+{
+ // cool#6911 draw the note indicator browser-side instead
+ if (comphelper::LibreOfficeKit::isActive())
+ return;
+
+ tools::Long nInitPosX = nScrX;
+ if ( bLayoutRTL )
+ nInitPosX += nMirrorW - 1; // always in pixels
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ tools::Long nPosY = nScrY;
+ for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
+ {
+ RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+ if ( pThisRowInfo->bChanged )
+ {
+ tools::Long nPosX = nInitPosX;
+ for (SCCOL nX=nX1; nX<=nX2; nX++)
+ {
+ ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
+ bool bIsMerged = false;
+
+ if ( nX==nX1 && pInfo->bHOverlapped && !pInfo->bVOverlapped )
+ {
+ // find start of merged cell
+ bIsMerged = true;
+ SCROW nY = pRowInfo[nArrY].nRowNo;
+ SCCOL nMergeX = nX;
+ SCROW nMergeY = nY;
+ mpDoc->ExtendOverlapped( nMergeX, nMergeY, nX, nY, nTab );
+ }
+
+ if (!mpDoc->ColHidden(nX, nTab) && mpDoc->GetNote(nX, pRowInfo[nArrY].nRowNo, nTab)
+ && (bIsMerged || (!pInfo->bHOverlapped && !pInfo->bVOverlapped)))
+ {
+
+ const bool bIsDarkBackground = SC_MOD()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor.IsDark();
+ const Color aColor = pInfo->pBackground->GetColor();
+ if ( aColor == COL_AUTO ? bIsDarkBackground : aColor.IsDark() )
+ rRenderContext.SetLineColor(COL_WHITE);
+ else
+ rRenderContext.SetLineColor(COL_BLACK);
+
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ if ( mbUseStyleColor && rStyleSettings.GetHighContrastMode() )
+ rRenderContext.SetFillColor( SC_MOD()->GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor );
+ else
+ rRenderContext.SetFillColor( SC_MOD()->GetColorConfig().GetColorValue(svtools::CALCCOMMENTS).nColor );
+
+ tools::Long nMarkX = nPosX + ( pRowInfo[0].basicCellInfo(nX).nWidth - 2 ) * nLayoutSign;
+ if ( bIsMerged || pInfo->bMerged )
+ {
+ // if merged, add widths of all cells
+ SCCOL nNextX = nX + 1;
+ while ( nNextX <= nX2 + 1 && pThisRowInfo->cellInfo(nNextX).bHOverlapped )
+ {
+ nMarkX += pRowInfo[0].basicCellInfo(nNextX).nWidth * nLayoutSign;
+ ++nNextX;
+ }
+ }
+ // DPI/ZOOM 100/100 => 10, 100/50 => 7, 100/150 => 13
+ // DPI/ZOOM 150/100 => 13, 150/50 => 8.5, 150/150 => 17.5
+ const double nSize( rRenderContext.GetDPIScaleFactor() * aZoomX * 6 + 4);
+ Point aPoints[3];
+ aPoints[0] = Point(nMarkX, nPosY);
+ aPoints[0].setX( bLayoutRTL ? aPoints[0].X() + nSize : aPoints[0].X() - nSize );
+ aPoints[1] = Point(nMarkX, nPosY);
+ aPoints[2] = Point(nMarkX, nPosY + nSize);
+ tools::Polygon aPoly(3, aPoints);
+
+ if ( bLayoutRTL ? ( nMarkX >= 0 ) : ( nMarkX < nScrX+nScrW ) )
+ rRenderContext.DrawPolygon(aPoly);
+ }
+
+ nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
+ }
+ }
+ nPosY += pThisRowInfo->nHeight;
+ }
+}
+
+void ScOutputData::DrawFormulaMarks(vcl::RenderContext& rRenderContext)
+{
+ bool bFirst = true;
+
+ tools::Long nInitPosX = nScrX;
+ if ( bLayoutRTL )
+ nInitPosX += nMirrorW - 1; // always in pixels
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ tools::Long nPosY = nScrY;
+ for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
+ {
+ RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+ if ( pThisRowInfo->bChanged )
+ {
+ tools::Long nPosX = nInitPosX;
+ for (SCCOL nX=nX1; nX<=nX2; nX++)
+ {
+ ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
+ if (!mpDoc->ColHidden(nX, nTab) && !mpDoc->GetFormula(nX, pRowInfo[nArrY].nRowNo, nTab).isEmpty()
+ && (!pInfo->bHOverlapped && !pInfo->bVOverlapped))
+ {
+ if (bFirst)
+ {
+ rRenderContext.SetLineColor(COL_WHITE);
+
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ if ( mbUseStyleColor && rStyleSettings.GetHighContrastMode() )
+ rRenderContext.SetFillColor( SC_MOD()->GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor );
+ else
+ rRenderContext.SetFillColor(COL_LIGHTBLUE);
+
+ bFirst = false;
+ }
+
+ tools::Long nMarkX = nPosX;
+ tools::Long nMarkY = nPosY + pThisRowInfo->nHeight - 2;
+ if ( pInfo->bMerged )
+ {
+ for (SCSIZE nNextY=nArrY+1; nNextY+1<nArrCount; nNextY++)
+ {
+ bool bVOver;
+ if (pRowInfo[nNextY + 1].nRowNo == (pRowInfo[nNextY].nRowNo + 1)) {
+ bVOver = pRowInfo[nNextY].cellInfo(nX).bVOverlapped;
+ } else {
+ bVOver = mpDoc->GetAttr(nX,nNextY,nTab,ATTR_MERGE_FLAG)->IsVerOverlapped();
+ }
+ if (!bVOver) break;
+ nMarkY += pRowInfo[nNextY].nHeight;
+ }
+ }
+ // DPI/ZOOM 100/100 => 10, 100/50 => 7, 100/150 => 13
+ // DPI/ZOOM 150/100 => 13, 150/50 => 8.5, 150/150 => 17.5
+ const double nSize( rRenderContext.GetDPIScaleFactor() * aZoomX * 6 + 4);
+ Point aPoints[3];
+ aPoints[0] = Point(nMarkX, nMarkY);
+ aPoints[0].setX( bLayoutRTL ? aPoints[0].X() - nSize : aPoints[0].X() + nSize );
+ aPoints[1] = Point(nMarkX, nMarkY);
+ aPoints[2] = Point(nMarkX, nMarkY - nSize);
+ tools::Polygon aPoly(3, aPoints);
+
+ if ( bLayoutRTL ? ( nMarkX >= 0 ) : ( nMarkX < nScrX+nScrW ) )
+ rRenderContext.DrawPolygon(aPoly);
+ }
+
+ nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
+ }
+ }
+ nPosY += pThisRowInfo->nHeight;
+ }
+}
+
+void ScOutputData::AddPDFNotes()
+{
+ vcl::PDFExtOutDevData* pPDFData = dynamic_cast< vcl::PDFExtOutDevData* >( mpDev->GetExtOutDevData() );
+ if ( !pPDFData || !pPDFData->GetIsExportNotes() )
+ return;
+
+ tools::Long nInitPosX = nScrX;
+ if ( bLayoutRTL )
+ {
+ Size aOnePixel = mpDev->PixelToLogic(Size(1,1));
+ tools::Long nOneX = aOnePixel.Width();
+ nInitPosX += nMirrorW - nOneX;
+ }
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ tools::Long nPosY = nScrY;
+ for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
+ {
+ RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+ if ( pThisRowInfo->bChanged )
+ {
+ tools::Long nPosX = nInitPosX;
+ for (SCCOL nX=nX1; nX<=nX2; nX++)
+ {
+ ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
+ bool bIsMerged = false;
+ SCROW nY = pRowInfo[nArrY].nRowNo;
+ SCCOL nMergeX = nX;
+ SCROW nMergeY = nY;
+
+ if ( nX==nX1 && pInfo->bHOverlapped && !pInfo->bVOverlapped )
+ {
+ // find start of merged cell
+ bIsMerged = true;
+ mpDoc->ExtendOverlapped( nMergeX, nMergeY, nX, nY, nTab );
+ // use origin's pCell for NotePtr test below
+ }
+
+ if ( mpDoc->GetNote(nMergeX, nMergeY, nTab) && ( bIsMerged ||
+ ( !pInfo->bHOverlapped && !pInfo->bVOverlapped ) ) )
+ {
+ tools::Long nNoteWidth = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTX );
+ tools::Long nNoteHeight = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTY );
+
+ tools::Long nMarkX = nPosX + ( pRowInfo[0].basicCellInfo(nX).nWidth - nNoteWidth ) * nLayoutSign;
+ if ( bIsMerged || pInfo->bMerged )
+ {
+ // if merged, add widths of all cells
+ SCCOL nNextX = nX + 1;
+ while ( nNextX <= nX2 + 1 && pThisRowInfo->cellInfo(nNextX).bHOverlapped )
+ {
+ nMarkX += pRowInfo[0].basicCellInfo(nNextX).nWidth * nLayoutSign;
+ ++nNextX;
+ }
+ }
+ if ( bLayoutRTL ? ( nMarkX >= 0 ) : ( nMarkX < nScrX+nScrW ) )
+ {
+ tools::Rectangle aNoteRect( nMarkX, nPosY, nMarkX+nNoteWidth*nLayoutSign, nPosY+nNoteHeight );
+ const ScPostIt* pNote = mpDoc->GetNote(nMergeX, nMergeY, nTab);
+
+ // Note title is the cell address (as on printed note pages)
+ ScAddress aAddress( nMergeX, nMergeY, nTab );
+ OUString aTitle(aAddress.Format(ScRefFlags::VALID, mpDoc, mpDoc->GetAddressConvention()));
+
+ // Content has to be a simple string without line breaks
+ OUString aContent = pNote->GetText();
+ aContent = aContent.replaceAll("\n", " ");
+
+ vcl::PDFNote aNote;
+ aNote.Title = aTitle;
+ aNote.Contents = aContent;
+ pPDFData->CreateNote( aNoteRect, aNote );
+ }
+ }
+
+ nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
+ }
+ }
+ nPosY += pThisRowInfo->nHeight;
+ }
+}
+
+void ScOutputData::DrawClipMarks()
+{
+ if (!bAnyClipped)
+ return;
+
+ Color aArrowFillCol( SC_MOD()->GetColorConfig().GetColorValue(svtools::CALCTEXTOVERFLOW).nColor );
+ const bool bIsDarkBackground = SC_MOD()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor.IsDark();
+
+ DrawModeFlags nOldDrawMode = mpDev->GetDrawMode();
+
+ tools::Long nInitPosX = nScrX;
+ if ( bLayoutRTL )
+ nInitPosX += nMirrorW - 1; // always in pixels
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ tools::Rectangle aCellRect;
+ tools::Long nPosY = nScrY;
+ for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
+ {
+ RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+ if ( pThisRowInfo->bChanged )
+ {
+ SCROW nY = pThisRowInfo->nRowNo;
+ tools::Long nPosX = nInitPosX;
+ for (SCCOL nX=nX1; nX<=nX2; nX++)
+ {
+ ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
+ if (pInfo->nClipMark != ScClipMark::NONE)
+ {
+ if (pInfo->bHOverlapped || pInfo->bVOverlapped)
+ {
+ // merge origin may be outside of visible area - use document functions
+
+ SCCOL nOverX = nX;
+ SCROW nOverY = nY;
+ tools::Long nStartPosX = nPosX;
+ tools::Long nStartPosY = nPosY;
+
+ while ( nOverX > 0 && ( mpDoc->GetAttr(
+ nOverX, nOverY, nTab, ATTR_MERGE_FLAG )->GetValue() & ScMF::Hor ) )
+ {
+ --nOverX;
+ nStartPosX -= nLayoutSign * static_cast<tools::Long>( mpDoc->GetColWidth(nOverX,nTab) * mnPPTX );
+ }
+
+ while ( nOverY > 0 && ( mpDoc->GetAttr(
+ nOverX, nOverY, nTab, ATTR_MERGE_FLAG )->GetValue() & ScMF::Ver ) )
+ {
+ --nOverY;
+ nStartPosY -= nLayoutSign * static_cast<tools::Long>( mpDoc->GetRowHeight(nOverY,nTab) * mnPPTY );
+ }
+
+ tools::Long nOutWidth = static_cast<tools::Long>( mpDoc->GetColWidth(nOverX,nTab) * mnPPTX );
+ tools::Long nOutHeight = static_cast<tools::Long>( mpDoc->GetRowHeight(nOverY,nTab) * mnPPTY );
+
+ const ScMergeAttr* pMerge = mpDoc->GetAttr( nOverX, nOverY, nTab, ATTR_MERGE );
+ SCCOL nCountX = pMerge->GetColMerge();
+ for (SCCOL i=1; i<nCountX; i++)
+ nOutWidth += mpDoc->GetColWidth(nOverX+i,nTab) * mnPPTX;
+ SCROW nCountY = pMerge->GetRowMerge();
+ nOutHeight += mpDoc->GetScaledRowHeight( nOverY+1, nOverY+nCountY-1, nTab, mnPPTY);
+
+ if ( bLayoutRTL )
+ nStartPosX -= nOutWidth - 1;
+ aCellRect = tools::Rectangle( Point( nStartPosX, nStartPosY ), Size( nOutWidth, nOutHeight ) );
+ }
+ else
+ {
+ tools::Long nOutWidth = pRowInfo[0].basicCellInfo(nX).nWidth;
+ tools::Long nOutHeight = pThisRowInfo->nHeight;
+
+ if ( pInfo->bMerged && pInfo->pPatternAttr )
+ {
+ SCCOL nOverX = nX;
+ SCROW nOverY = nY;
+ const ScMergeAttr* pMerge =
+ &pInfo->pPatternAttr->GetItem(ATTR_MERGE);
+ SCCOL nCountX = pMerge->GetColMerge();
+ for (SCCOL i=1; i<nCountX; i++)
+ nOutWidth += mpDoc->GetColWidth(nOverX+i,nTab) * mnPPTX;
+ SCROW nCountY = pMerge->GetRowMerge();
+ nOutHeight += mpDoc->GetScaledRowHeight( nOverY+1, nOverY+nCountY-1, nTab, mnPPTY);
+ }
+
+ tools::Long nStartPosX = nPosX;
+ if ( bLayoutRTL )
+ nStartPosX -= nOutWidth - 1;
+ // #i80447# create aCellRect from two points in case nOutWidth is 0
+ aCellRect = tools::Rectangle( Point( nStartPosX, nPosY ),
+ Point( nStartPosX+nOutWidth-1, nPosY+nOutHeight-1 ) );
+ }
+
+ aCellRect.AdjustBottom( -1 ); // don't paint over the cell grid
+ if ( bLayoutRTL )
+ aCellRect.AdjustLeft(1 );
+ else
+ aCellRect.AdjustRight( -1 );
+
+ tools::Long nMarkPixel = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTX );
+ Size aMarkSize( nMarkPixel, (nMarkPixel-1)*2 );
+
+ const Color aColor = pInfo->pBackground ? pInfo->pBackground->GetColor() : COL_AUTO;
+ if ( aColor == COL_AUTO ? bIsDarkBackground : aColor.IsDark() )
+ mpDev->SetDrawMode( nOldDrawMode | DrawModeFlags::WhiteLine );
+ else
+ mpDev->SetDrawMode( nOldDrawMode | DrawModeFlags::BlackLine );
+
+ if (bVertical)
+ {
+ if (pInfo->nClipMark & (bLayoutRTL ? ScClipMark::Bottom : ScClipMark::Top))
+ {
+ // visually top
+ tools::Rectangle aMarkRect = aCellRect;
+ aMarkRect.SetBottom(aCellRect.Top() + nMarkPixel - 1);
+ SvxFont::DrawArrow(*mpDev, aMarkRect, aMarkSize, aArrowFillCol, true, true);
+ }
+ if (pInfo->nClipMark & (bLayoutRTL ? ScClipMark::Top : ScClipMark::Bottom))
+ {
+ // visually bottom
+ tools::Rectangle aMarkRect = aCellRect;
+ aMarkRect.SetTop(aCellRect.Bottom() + nMarkPixel + 1);
+ SvxFont::DrawArrow(*mpDev, aMarkRect, aMarkSize, aArrowFillCol, false,
+ true);
+ }
+ }
+ else
+ {
+ if (pInfo->nClipMark & (bLayoutRTL ? ScClipMark::Right : ScClipMark::Left))
+ {
+ // visually left
+ tools::Rectangle aMarkRect = aCellRect;
+ aMarkRect.SetRight(aCellRect.Left() + nMarkPixel - 1);
+ SvxFont::DrawArrow(*mpDev, aMarkRect, aMarkSize, aArrowFillCol, true,
+ false);
+ }
+ if (pInfo->nClipMark & (bLayoutRTL ? ScClipMark::Left : ScClipMark::Right))
+ {
+ // visually right
+ tools::Rectangle aMarkRect = aCellRect;
+ aMarkRect.SetLeft(aCellRect.Right() - nMarkPixel + 1);
+ SvxFont::DrawArrow(*mpDev, aMarkRect, aMarkSize, aArrowFillCol, false,
+ false);
+ }
+ }
+ }
+ nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
+ }
+ }
+ nPosY += pThisRowInfo->nHeight;
+ }
+
+ mpDev->SetDrawMode(nOldDrawMode);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/output2.cxx b/sc/source/ui/view/output2.cxx
new file mode 100644
index 0000000000..d419981d8e
--- /dev/null
+++ b/sc/source/ui/view/output2.cxx
@@ -0,0 +1,5248 @@
+/* -*- 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 .
+ */
+
+#include <scitems.hxx>
+#include <editeng/eeitem.hxx>
+
+#include <editeng/adjustitem.hxx>
+#include <svx/algitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <svtools/colorcfg.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/charreliefitem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/editstat.hxx>
+#include <editeng/emphasismarkitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/forbiddenruleitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <svx/rotmodit.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/unolingu.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/wrlmitem.hxx>
+#include <formula/errorcodes.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <svl/zformat.hxx>
+#include <vcl/kernarray.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/metric.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/pdfextoutdevdata.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/glyphitem.hxx>
+#include <vcl/vcllayout.hxx>
+#include <vcl/glyphitemcache.hxx>
+#include <sal/log.hxx>
+#include <unotools/charclass.hxx>
+#include <osl/diagnose.h>
+#include <tools/stream.hxx>
+
+#include <output.hxx>
+#include <document.hxx>
+#include <formulacell.hxx>
+#include <attrib.hxx>
+#include <patattr.hxx>
+#include <cellform.hxx>
+#include <editutil.hxx>
+#include <progress.hxx>
+#include <scmod.hxx>
+#include <fillinfo.hxx>
+#include <stlsheet.hxx>
+#include <spellcheckcontext.hxx>
+#include <scopetools.hxx>
+
+#include <com/sun/star/i18n/DirectionProperty.hpp>
+#include <comphelper/scopeguard.hxx>
+#include <comphelper/string.hxx>
+
+#include <memory>
+#include <vector>
+#include <o3tl/lru_map.hxx>
+#include <o3tl/hash_combine.hxx>
+
+#include <math.h>
+
+using namespace com::sun::star;
+
+//! Merge Autofilter width with column.cxx
+#define DROPDOWN_BITMAP_SIZE 18
+
+#define DRAWTEXT_MAX 32767
+
+const sal_uInt16 SC_SHRINKAGAIN_MAX = 7;
+constexpr auto HMM_PER_TWIPS = o3tl::convert(1.0, o3tl::Length::twip, o3tl::Length::mm100);
+
+class ScDrawStringsVars
+{
+ ScOutputData* pOutput; // connection
+
+ const ScPatternAttr* pPattern; // attribute
+ const SfxItemSet* pCondSet; // from conditional formatting
+
+ vcl::Font aFont; // created from attributes
+ FontMetric aMetric;
+ tools::Long nAscentPixel; // always pixels
+ SvxCellOrientation eAttrOrient;
+ SvxCellHorJustify eAttrHorJust;
+ SvxCellVerJustify eAttrVerJust;
+ SvxCellJustifyMethod eAttrHorJustMethod;
+ const SvxMarginItem* pMargin;
+ sal_uInt16 nIndent;
+ bool bRotated;
+
+ OUString aString; // contents
+ Size aTextSize;
+ tools::Long nOriginalWidth;
+ tools::Long nMaxDigitWidth;
+ tools::Long nSignWidth;
+ tools::Long nDotWidth;
+ tools::Long nExpWidth;
+
+ ScRefCellValue maLastCell;
+ sal_uLong nValueFormat;
+ bool bLineBreak;
+ bool bRepeat;
+ bool bShrink;
+
+ bool bPixelToLogic;
+ bool bCellContrast;
+
+ Color aBackConfigColor; // used for ScPatternAttr::GetFont calls
+ Color aTextConfigColor;
+ sal_Int32 nRepeatPos;
+ sal_Unicode nRepeatChar;
+
+public:
+ ScDrawStringsVars(ScOutputData* pData, bool bPTL);
+
+ // SetPattern = ex-SetVars
+ // SetPatternSimple: without Font
+
+ void SetPattern(
+ const ScPatternAttr* pNew, const SfxItemSet* pSet, const ScRefCellValue& rCell,
+ SvtScriptType nScript );
+
+ void SetPatternSimple( const ScPatternAttr* pNew, const SfxItemSet* pSet );
+
+ bool SetText( const ScRefCellValue& rCell ); // TRUE -> drop pOldPattern
+ void SetHashText();
+ bool SetTextToWidthOrHash( ScRefCellValue& rCell, tools::Long nWidth );
+ void SetAutoText( const OUString& rAutoText );
+
+ SvxCellOrientation GetOrient() const { return eAttrOrient; }
+ SvxCellHorJustify GetHorJust() const { return eAttrHorJust; }
+ SvxCellVerJustify GetVerJust() const { return eAttrVerJust; }
+ SvxCellJustifyMethod GetHorJustMethod() const { return eAttrHorJustMethod; }
+ const SvxMarginItem* GetMargin() const { return pMargin; }
+
+ sal_uInt16 GetLeftTotal() const { return pMargin->GetLeftMargin() + nIndent; }
+ sal_uInt16 GetRightTotal() const { return pMargin->GetRightMargin() + nIndent; }
+
+ const OUString& GetString() const { return aString; }
+ const Size& GetTextSize() const { return aTextSize; }
+ tools::Long GetOriginalWidth() const { return nOriginalWidth; }
+ tools::Long GetFmtTextWidth(const OUString& rString);
+
+ // Get the effective number format, including formula result types.
+ // This assumes that a formula cell has already been calculated.
+ sal_uLong GetResultValueFormat() const { return nValueFormat;}
+
+ bool GetLineBreak() const { return bLineBreak; }
+ bool IsRepeat() const { return bRepeat; }
+ bool IsShrink() const { return bShrink; }
+ void RepeatToFill( tools::Long nColWidth );
+
+ tools::Long GetAscent() const { return nAscentPixel; }
+ bool IsRotated() const { return bRotated; }
+
+ void SetShrinkScale( tools::Long nScale, SvtScriptType nScript );
+
+ bool HasCondHeight() const { return pCondSet && SfxItemState::SET ==
+ pCondSet->GetItemState( ATTR_FONT_HEIGHT ); }
+
+ bool HasEditCharacters() const;
+
+ // ScOutputData::LayoutStrings() usually triggers a number of calls that require
+ // to lay out the text, which is relatively slow, so cache that operation.
+ const SalLayoutGlyphs* GetLayoutGlyphs(const OUString& rString) const
+ {
+ return SalLayoutGlyphsCache::self()->GetLayoutGlyphs(pOutput->pFmtDevice, rString);
+ }
+
+private:
+ tools::Long GetMaxDigitWidth(); // in logic units
+ tools::Long GetSignWidth();
+ tools::Long GetDotWidth();
+ tools::Long GetExpWidth();
+ void TextChanged();
+};
+
+ScDrawStringsVars::ScDrawStringsVars(ScOutputData* pData, bool bPTL) :
+ pOutput ( pData ),
+ pPattern ( nullptr ),
+ pCondSet ( nullptr ),
+ nAscentPixel(0),
+ eAttrOrient ( SvxCellOrientation::Standard ),
+ eAttrHorJust( SvxCellHorJustify::Standard ),
+ eAttrVerJust( SvxCellVerJustify::Bottom ),
+ eAttrHorJustMethod( SvxCellJustifyMethod::Auto ),
+ pMargin ( nullptr ),
+ nIndent ( 0 ),
+ bRotated ( false ),
+ nOriginalWidth( 0 ),
+ nMaxDigitWidth( 0 ),
+ nSignWidth( 0 ),
+ nDotWidth( 0 ),
+ nExpWidth( 0 ),
+ nValueFormat( 0 ),
+ bLineBreak ( false ),
+ bRepeat ( false ),
+ bShrink ( false ),
+ bPixelToLogic( bPTL ),
+ nRepeatPos( -1 ),
+ nRepeatChar( 0x0 )
+{
+ ScModule* pScMod = SC_MOD();
+ bCellContrast = pOutput->mbUseStyleColor &&
+ Application::GetSettings().GetStyleSettings().GetHighContrastMode();
+
+ const svtools::ColorConfig& rColorConfig = pScMod->GetColorConfig();
+ aBackConfigColor = rColorConfig.GetColorValue(svtools::DOCCOLOR).nColor;
+ aTextConfigColor = rColorConfig.GetColorValue(svtools::FONTCOLOR).nColor;
+}
+
+void ScDrawStringsVars::SetShrinkScale( tools::Long nScale, SvtScriptType nScript )
+{
+ // text remains valid, size is updated
+
+ OutputDevice* pDev = pOutput->mpDev;
+ OutputDevice* pRefDevice = pOutput->mpRefDevice;
+ OutputDevice* pFmtDevice = pOutput->pFmtDevice;
+
+ // call GetFont with a modified fraction, use only the height
+
+ Fraction aFraction( nScale, 100 );
+ if ( !bPixelToLogic )
+ aFraction *= pOutput->aZoomY;
+ vcl::Font aTmpFont;
+ pPattern->fillFontOnly(aTmpFont, pFmtDevice, &aFraction, pCondSet, nScript);
+ // only need font height
+ tools::Long nNewHeight = aTmpFont.GetFontHeight();
+ if ( nNewHeight > 0 )
+ aFont.SetFontHeight( nNewHeight );
+
+ // set font and dependent variables as in SetPattern
+
+ pDev->SetFont( aFont );
+ if ( pFmtDevice != pDev )
+ pFmtDevice->SetFont( aFont );
+
+ aMetric = pFmtDevice->GetFontMetric();
+ if ( pFmtDevice->GetOutDevType() == OUTDEV_PRINTER && aMetric.GetInternalLeading() == 0 )
+ {
+ OutputDevice* pDefaultDev = Application::GetDefaultDevice();
+ MapMode aOld = pDefaultDev->GetMapMode();
+ pDefaultDev->SetMapMode( pFmtDevice->GetMapMode() );
+ aMetric = pDefaultDev->GetFontMetric( aFont );
+ pDefaultDev->SetMapMode( aOld );
+ }
+
+ nAscentPixel = aMetric.GetAscent();
+ if ( bPixelToLogic )
+ nAscentPixel = pRefDevice->LogicToPixel( Size( 0, nAscentPixel ) ).Height();
+
+ SetAutoText( aString ); // same text again, to get text size
+}
+
+namespace {
+
+template<typename ItemType, typename EnumType>
+EnumType lcl_GetValue(const ScPatternAttr& rPattern, sal_uInt16 nWhich, const SfxItemSet* pCondSet)
+{
+ const ItemType& rItem = static_cast<const ItemType&>(rPattern.GetItem(nWhich, pCondSet));
+ return static_cast<EnumType>(rItem.GetValue());
+}
+
+bool lcl_GetBoolValue(const ScPatternAttr& rPattern, sal_uInt16 nWhich, const SfxItemSet* pCondSet)
+{
+ return lcl_GetValue<SfxBoolItem, bool>(rPattern, nWhich, pCondSet);
+}
+
+}
+
+static bool lcl_isNumberFormatText(const ScDocument* pDoc, SCCOL nCellX, SCROW nCellY, SCTAB nTab )
+{
+ sal_uInt32 nCurrentNumberFormat = pDoc->GetNumberFormat( nCellX, nCellY, nTab );
+ SvNumberFormatter* pNumberFormatter = pDoc->GetFormatTable();
+ return pNumberFormatter->GetType( nCurrentNumberFormat ) == SvNumFormatType::TEXT;
+}
+
+void ScDrawStringsVars::SetPattern(
+ const ScPatternAttr* pNew, const SfxItemSet* pSet, const ScRefCellValue& rCell,
+ SvtScriptType nScript )
+{
+ nMaxDigitWidth = 0;
+ nSignWidth = 0;
+ nDotWidth = 0;
+ nExpWidth = 0;
+
+ pPattern = pNew;
+ pCondSet = pSet;
+
+ // evaluate pPattern
+
+ OutputDevice* pDev = pOutput->mpDev;
+ OutputDevice* pRefDevice = pOutput->mpRefDevice;
+ OutputDevice* pFmtDevice = pOutput->pFmtDevice;
+
+ // font
+
+ ScAutoFontColorMode eColorMode;
+ if ( pOutput->mbUseStyleColor )
+ {
+ if ( pOutput->mbForceAutoColor )
+ eColorMode = bCellContrast ? ScAutoFontColorMode::IgnoreAll : ScAutoFontColorMode::IgnoreFont;
+ else
+ eColorMode = bCellContrast ? ScAutoFontColorMode::IgnoreBack : ScAutoFontColorMode::Display;
+ }
+ else
+ eColorMode = ScAutoFontColorMode::Print;
+
+ if (bPixelToLogic)
+ pPattern->fillFont(aFont, eColorMode, pFmtDevice, nullptr, pCondSet, nScript, &aBackConfigColor, &aTextConfigColor);
+ else
+ pPattern->fillFont(aFont, eColorMode, pFmtDevice, &pOutput->aZoomY, pCondSet, nScript, &aBackConfigColor, &aTextConfigColor );
+
+ aFont.SetAlignment(ALIGN_BASELINE);
+
+ // orientation
+
+ eAttrOrient = pPattern->GetCellOrientation( pCondSet );
+
+ // alignment
+
+ eAttrHorJust = pPattern->GetItem( ATTR_HOR_JUSTIFY, pCondSet ).GetValue();
+
+ eAttrVerJust = pPattern->GetItem( ATTR_VER_JUSTIFY, pCondSet ).GetValue();
+ if ( eAttrVerJust == SvxCellVerJustify::Standard )
+ eAttrVerJust = SvxCellVerJustify::Bottom;
+
+ // justification method
+
+ eAttrHorJustMethod = lcl_GetValue<SvxJustifyMethodItem, SvxCellJustifyMethod>(*pPattern, ATTR_HOR_JUSTIFY_METHOD, pCondSet);
+
+ // line break
+
+ bLineBreak = pPattern->GetItem( ATTR_LINEBREAK, pCondSet ).GetValue();
+
+ // handle "repeat" alignment
+
+ bRepeat = ( eAttrHorJust == SvxCellHorJustify::Repeat );
+ if ( bRepeat )
+ {
+ // "repeat" disables rotation (before constructing the font)
+ eAttrOrient = SvxCellOrientation::Standard;
+
+ // #i31843# "repeat" with "line breaks" is treated as default alignment (but rotation is still disabled)
+ if ( bLineBreak )
+ eAttrHorJust = SvxCellHorJustify::Standard;
+ }
+
+ sal_Int16 nRot;
+ switch (eAttrOrient)
+ {
+ case SvxCellOrientation::Standard:
+ nRot = 0;
+ bRotated = pPattern->GetItem( ATTR_ROTATE_VALUE, pCondSet ).GetValue() != 0_deg100 &&
+ !bRepeat;
+ break;
+ case SvxCellOrientation::Stacked:
+ nRot = 0;
+ bRotated = false;
+ break;
+ case SvxCellOrientation::TopBottom:
+ nRot = 2700;
+ bRotated = false;
+ break;
+ case SvxCellOrientation::BottomUp:
+ nRot = 900;
+ bRotated = false;
+ break;
+ default:
+ OSL_FAIL("Invalid SvxCellOrientation value");
+ nRot = 0;
+ bRotated = false;
+ break;
+ }
+ aFont.SetOrientation( Degree10(nRot) );
+
+ // syntax mode
+
+ if (pOutput->mbSyntaxMode)
+ pOutput->SetSyntaxColor(&aFont, rCell);
+
+ // There is no cell attribute for kerning, default is kerning OFF, all
+ // kerning is stored at an EditText object that is drawn using EditEngine.
+ // See also matching kerning cases in ScColumn::GetNeededSize and
+ // ScColumn::GetOptimalColWidth.
+ aFont.SetKerning(FontKerning::NONE);
+
+ pDev->SetFont( aFont );
+ if ( pFmtDevice != pDev )
+ pFmtDevice->SetFont( aFont );
+
+ aMetric = pFmtDevice->GetFontMetric();
+
+ // if there is the leading 0 on a printer device, we have problems
+ // -> take metric from the screen (as for EditEngine!)
+ if ( pFmtDevice->GetOutDevType() == OUTDEV_PRINTER && aMetric.GetInternalLeading() == 0 )
+ {
+ OutputDevice* pDefaultDev = Application::GetDefaultDevice();
+ MapMode aOld = pDefaultDev->GetMapMode();
+ pDefaultDev->SetMapMode( pFmtDevice->GetMapMode() );
+ aMetric = pDefaultDev->GetFontMetric( aFont );
+ pDefaultDev->SetMapMode( aOld );
+ }
+
+ nAscentPixel = aMetric.GetAscent();
+ if ( bPixelToLogic )
+ nAscentPixel = pRefDevice->LogicToPixel( Size( 0, nAscentPixel ) ).Height();
+
+ Color aULineColor( pPattern->GetItem( ATTR_FONT_UNDERLINE, pCondSet ).GetColor() );
+ pDev->SetTextLineColor( aULineColor );
+
+ Color aOLineColor( pPattern->GetItem( ATTR_FONT_OVERLINE, pCondSet ).GetColor() );
+ pDev->SetOverlineColor( aOLineColor );
+
+ // number format
+
+ nValueFormat = pPattern->GetNumberFormat( pOutput->mpDoc->GetFormatTable(), pCondSet );
+
+ // margins
+ pMargin = &pPattern->GetItem( ATTR_MARGIN, pCondSet );
+ if ( eAttrHorJust == SvxCellHorJustify::Left || eAttrHorJust == SvxCellHorJustify::Right )
+ nIndent = pPattern->GetItem( ATTR_INDENT, pCondSet ).GetValue();
+ else
+ nIndent = 0;
+
+ // "Shrink to fit"
+
+ bShrink = pPattern->GetItem( ATTR_SHRINKTOFIT, pCondSet ).GetValue();
+
+ // at least the text size needs to be retrieved again
+ //! differentiate and do not get the text again from the number format?
+ maLastCell.clear();
+}
+
+void ScDrawStringsVars::SetPatternSimple( const ScPatternAttr* pNew, const SfxItemSet* pSet )
+{
+ nMaxDigitWidth = 0;
+ nSignWidth = 0;
+ nDotWidth = 0;
+ nExpWidth = 0;
+
+ // Is called, when the font variables do not change (!StringDiffer)
+
+ pPattern = pNew;
+ pCondSet = pSet; //! is this needed ???
+
+ // number format
+
+ sal_uLong nOld = nValueFormat;
+ nValueFormat = pPattern->GetNumberFormat( pOutput->mpDoc->GetFormatTable(), pCondSet );
+
+ if (nValueFormat != nOld)
+ maLastCell.clear(); // always reformat
+
+ // margins
+
+ pMargin = &pPattern->GetItem( ATTR_MARGIN, pCondSet );
+
+ if ( eAttrHorJust == SvxCellHorJustify::Left )
+ nIndent = pPattern->GetItem( ATTR_INDENT, pCondSet ).GetValue();
+ else
+ nIndent = 0;
+
+ // "Shrink to fit"
+
+ bShrink = pPattern->GetItem( ATTR_SHRINKTOFIT, pCondSet ).GetValue();
+}
+
+static bool SameValue( const ScRefCellValue& rCell, const ScRefCellValue& rOldCell )
+{
+ return rOldCell.getType() == CELLTYPE_VALUE && rCell.getType() == CELLTYPE_VALUE &&
+ rCell.getDouble() == rOldCell.getDouble();
+}
+
+bool ScDrawStringsVars::SetText( const ScRefCellValue& rCell )
+{
+ bool bChanged = false;
+
+ if (!rCell.isEmpty())
+ {
+ if (!SameValue(rCell, maLastCell))
+ {
+ maLastCell = rCell; // store cell
+
+ const Color* pColor;
+ sal_uLong nFormat = nValueFormat;
+ aString = ScCellFormat::GetString( rCell,
+ nFormat, &pColor,
+ *pOutput->mpDoc->GetFormatTable(),
+ *pOutput->mpDoc,
+ pOutput->mbShowNullValues,
+ pOutput->mbShowFormulas,
+ true );
+ if ( nFormat )
+ {
+ nRepeatPos = aString.indexOf( 0x1B );
+ if ( nRepeatPos != -1 )
+ {
+ if (nRepeatPos + 1 == aString.getLength())
+ nRepeatPos = -1;
+ else
+ {
+ nRepeatChar = aString[ nRepeatPos + 1 ];
+ // delete placeholder and char to repeat
+ aString = aString.replaceAt( nRepeatPos, 2, u"" );
+ // Do not cache/reuse a repeat-filled string, column
+ // widths or fonts or sizes may differ.
+ maLastCell.clear();
+ }
+ }
+ }
+ else
+ {
+ nRepeatPos = -1;
+ nRepeatChar = 0x0;
+ }
+ if (aString.getLength() > DRAWTEXT_MAX)
+ aString = aString.copy(0, DRAWTEXT_MAX);
+
+ if ( pColor && !pOutput->mbSyntaxMode && !( pOutput->mbUseStyleColor && pOutput->mbForceAutoColor ) )
+ {
+ OutputDevice* pDev = pOutput->mpDev;
+ aFont.SetColor(*pColor);
+ pDev->SetFont( aFont ); // only for output
+ bChanged = true;
+ maLastCell.clear(); // next time return here again
+ }
+
+ TextChanged();
+ }
+ // otherwise keep string/size
+ }
+ else
+ {
+ aString.clear();
+ maLastCell.clear();
+ aTextSize = Size(0,0);
+ nOriginalWidth = 0;
+ }
+
+ return bChanged;
+}
+
+void ScDrawStringsVars::SetHashText()
+{
+ SetAutoText("###");
+}
+
+void ScDrawStringsVars::RepeatToFill( tools::Long nColWidth )
+{
+ if ( nRepeatPos == -1 || nRepeatPos > aString.getLength() )
+ return;
+
+ tools::Long nCharWidth = GetFmtTextWidth(OUString(nRepeatChar));
+
+ if ( nCharWidth < 1 || (bPixelToLogic && nCharWidth < pOutput->mpRefDevice->PixelToLogic(Size(1,0)).Width()) )
+ return;
+
+ // Are there restrictions on the cell type we should filter out here ?
+ tools::Long nTextWidth = aTextSize.Width();
+ if ( bPixelToLogic )
+ {
+ nColWidth = pOutput->mpRefDevice->PixelToLogic(Size(nColWidth,0)).Width();
+ nTextWidth = pOutput->mpRefDevice->PixelToLogic(Size(nTextWidth,0)).Width();
+ }
+
+ tools::Long nSpaceToFill = nColWidth - nTextWidth;
+ if ( nSpaceToFill <= nCharWidth )
+ return;
+
+ sal_Int32 nCharsToInsert = nSpaceToFill / nCharWidth;
+ OUStringBuffer aFill(nCharsToInsert);
+ comphelper::string::padToLength(aFill, nCharsToInsert, nRepeatChar);
+ aString = aString.replaceAt( nRepeatPos, 0, aFill );
+ TextChanged();
+}
+
+bool ScDrawStringsVars::SetTextToWidthOrHash( ScRefCellValue& rCell, tools::Long nWidth )
+{
+ // #i113045# do the single-character width calculations in logic units
+ if (bPixelToLogic)
+ nWidth = pOutput->mpRefDevice->PixelToLogic(Size(nWidth,0)).Width();
+
+ CellType eType = rCell.getType();
+ if (eType != CELLTYPE_VALUE && eType != CELLTYPE_FORMULA)
+ // must be a value or formula cell.
+ return false;
+
+ if (eType == CELLTYPE_FORMULA)
+ {
+ ScFormulaCell* pFCell = rCell.getFormula();
+ if (pFCell->GetErrCode() != FormulaError::NONE || pOutput->mbShowFormulas)
+ {
+ SetHashText(); // If the error string doesn't fit, always use "###". Also for "display formulas" (#i116691#)
+ return true;
+ }
+ // If it's formula, the result must be a value.
+ if (!pFCell->IsValue())
+ return false;
+ }
+
+ sal_uLong nFormat = GetResultValueFormat();
+ if ((nFormat % SV_COUNTRY_LANGUAGE_OFFSET) != 0)
+ {
+ // Not 'General' number format. Set hash text and bail out.
+ SetHashText();
+ return true;
+ }
+
+ double fVal = rCell.getValue();
+
+ const SvNumberformat* pNumFormat = pOutput->mpDoc->GetFormatTable()->GetEntry(nFormat);
+ if (!pNumFormat)
+ return false;
+
+ tools::Long nMaxDigit = GetMaxDigitWidth();
+ if (!nMaxDigit)
+ return false;
+
+ sal_uInt16 nNumDigits = static_cast<sal_uInt16>(nWidth / nMaxDigit);
+ {
+ OUString sTempOut(aString);
+ if (!pNumFormat->GetOutputString(fVal, nNumDigits, sTempOut))
+ {
+ aString = sTempOut;
+ // Failed to get output string. Bail out.
+ return false;
+ }
+ aString = sTempOut;
+ }
+ sal_uInt8 nSignCount = 0, nDecimalCount = 0, nExpCount = 0;
+ sal_Int32 nLen = aString.getLength();
+ sal_Unicode cDecSep = ScGlobal::getLocaleData().getLocaleItem().decimalSeparator[0];
+ for( sal_Int32 i = 0; i < nLen; ++i )
+ {
+ sal_Unicode c = aString[i];
+ if (c == '-')
+ ++nSignCount;
+ else if (c == cDecSep)
+ ++nDecimalCount;
+ else if (c == 'E')
+ ++nExpCount;
+ }
+
+ // #i112250# A small value might be formatted as "0" when only counting the digits,
+ // but fit into the column when considering the smaller width of the decimal separator.
+ if (aString == "0" && fVal != 0.0)
+ nDecimalCount = 1;
+
+ if (nDecimalCount)
+ nWidth += (nMaxDigit - GetDotWidth()) * nDecimalCount;
+ if (nSignCount)
+ nWidth += (nMaxDigit - GetSignWidth()) * nSignCount;
+ if (nExpCount)
+ nWidth += (nMaxDigit - GetExpWidth()) * nExpCount;
+
+ if (nDecimalCount || nSignCount || nExpCount)
+ {
+ // Re-calculate.
+ nNumDigits = static_cast<sal_uInt16>(nWidth / nMaxDigit);
+ OUString sTempOut(aString);
+ if (!pNumFormat->GetOutputString(fVal, nNumDigits, sTempOut))
+ {
+ aString = sTempOut;
+ // Failed to get output string. Bail out.
+ return false;
+ }
+ aString = sTempOut;
+ }
+
+ tools::Long nActualTextWidth = GetFmtTextWidth(aString);
+ if (nActualTextWidth > nWidth)
+ {
+ // Even after the decimal adjustment the text doesn't fit. Give up.
+ SetHashText();
+ return true;
+ }
+
+ TextChanged();
+ maLastCell.clear(); // #i113022# equal cell and format in another column may give different string
+ return false;
+}
+
+void ScDrawStringsVars::SetAutoText( const OUString& rAutoText )
+{
+ aString = rAutoText;
+
+ OutputDevice* pRefDevice = pOutput->mpRefDevice;
+ OutputDevice* pFmtDevice = pOutput->pFmtDevice;
+ aTextSize.setWidth( GetFmtTextWidth( aString ) );
+ aTextSize.setHeight( pFmtDevice->GetTextHeight() );
+
+ if ( !pRefDevice->GetConnectMetaFile() || pRefDevice->GetOutDevType() == OUTDEV_PRINTER )
+ {
+ double fMul = pOutput->GetStretch();
+ aTextSize.setWidth( static_cast<tools::Long>(aTextSize.Width() / fMul + 0.5) );
+ }
+
+ aTextSize.setHeight( aMetric.GetAscent() + aMetric.GetDescent() );
+ if ( GetOrient() != SvxCellOrientation::Standard )
+ {
+ tools::Long nTemp = aTextSize.Height();
+ aTextSize.setHeight( aTextSize.Width() );
+ aTextSize.setWidth( nTemp );
+ }
+
+ nOriginalWidth = aTextSize.Width();
+ if ( bPixelToLogic )
+ aTextSize = pRefDevice->LogicToPixel( aTextSize );
+
+ maLastCell.clear(); // the same text may fit in the next cell
+}
+
+tools::Long ScDrawStringsVars::GetMaxDigitWidth()
+{
+ if (nMaxDigitWidth > 0)
+ return nMaxDigitWidth;
+
+ for (char i = 0; i < 10; ++i)
+ {
+ char cDigit = '0' + i;
+ // Do not cache this with GetFmtTextWidth(), nMaxDigitWidth is already cached.
+ tools::Long n = pOutput->pFmtDevice->GetTextWidth(OUString(cDigit));
+ nMaxDigitWidth = ::std::max(nMaxDigitWidth, n);
+ }
+ return nMaxDigitWidth;
+}
+
+tools::Long ScDrawStringsVars::GetSignWidth()
+{
+ if (nSignWidth > 0)
+ return nSignWidth;
+
+ nSignWidth = pOutput->pFmtDevice->GetTextWidth(OUString('-'));
+ return nSignWidth;
+}
+
+tools::Long ScDrawStringsVars::GetDotWidth()
+{
+ if (nDotWidth > 0)
+ return nDotWidth;
+
+ const OUString& sep = ScGlobal::getLocaleData().getLocaleItem().decimalSeparator;
+ nDotWidth = pOutput->pFmtDevice->GetTextWidth(sep);
+ return nDotWidth;
+}
+
+tools::Long ScDrawStringsVars::GetExpWidth()
+{
+ if (nExpWidth > 0)
+ return nExpWidth;
+
+ nExpWidth = pOutput->pFmtDevice->GetTextWidth(OUString('E'));
+ return nExpWidth;
+}
+
+tools::Long ScDrawStringsVars::GetFmtTextWidth( const OUString& rString )
+{
+ return pOutput->pFmtDevice->GetTextWidth( rString, 0, -1, nullptr, GetLayoutGlyphs( rString ));
+}
+
+void ScDrawStringsVars::TextChanged()
+{
+ OutputDevice* pRefDevice = pOutput->mpRefDevice;
+ OutputDevice* pFmtDevice = pOutput->pFmtDevice;
+ aTextSize.setWidth( GetFmtTextWidth( aString ) );
+ aTextSize.setHeight( pFmtDevice->GetTextHeight() );
+
+ if ( !pRefDevice->GetConnectMetaFile() || pRefDevice->GetOutDevType() == OUTDEV_PRINTER )
+ {
+ double fMul = pOutput->GetStretch();
+ aTextSize.setWidth( static_cast<tools::Long>(aTextSize.Width() / fMul + 0.5) );
+ }
+
+ aTextSize.setHeight( aMetric.GetAscent() + aMetric.GetDescent() );
+ if ( GetOrient() != SvxCellOrientation::Standard )
+ {
+ tools::Long nTemp = aTextSize.Height();
+ aTextSize.setHeight( aTextSize.Width() );
+ aTextSize.setWidth( nTemp );
+ }
+
+ nOriginalWidth = aTextSize.Width();
+ if ( bPixelToLogic )
+ aTextSize = pRefDevice->LogicToPixel( aTextSize );
+}
+
+bool ScDrawStringsVars::HasEditCharacters() const
+{
+ for (sal_Int32 nIdx = 0; nIdx < aString.getLength(); ++nIdx)
+ {
+ switch(aString[nIdx])
+ {
+ case CHAR_NBSP:
+ // tdf#122676: Ignore CHAR_NBSP (this is thousand separator in any number)
+ // if repeat character is set
+ if (nRepeatPos < 0)
+ return true;
+ break;
+ case CHAR_SHY:
+ case CHAR_ZWSP:
+ case CHAR_LRM:
+ case CHAR_RLM:
+ case CHAR_NBHY:
+ case CHAR_WJ:
+ return true;
+ default:
+ break;
+ }
+ }
+
+ return false;
+}
+
+double ScOutputData::GetStretch() const
+{
+ if ( mpRefDevice->IsMapModeEnabled() )
+ {
+ // If a non-trivial MapMode is set, its scale is now already
+ // taken into account in the OutputDevice's font handling
+ // (OutputDevice::ImplNewFont, see #95414#).
+ // The old handling below is only needed for pixel output.
+ return 1.0;
+ }
+
+ // calculation in double is faster than Fraction multiplication
+ // and doesn't overflow
+
+ if ( mpRefDevice == pFmtDevice )
+ {
+ MapMode aOld = mpRefDevice->GetMapMode();
+ return static_cast<double>(aOld.GetScaleY()) / static_cast<double>(aOld.GetScaleX()) * static_cast<double>(aZoomY) / static_cast<double>(aZoomX);
+ }
+ else
+ {
+ // when formatting for printer, device map mode has already been taken care of
+ return static_cast<double>(aZoomY) / static_cast<double>(aZoomX);
+ }
+}
+
+// output strings
+
+static void lcl_DoHyperlinkResult( const OutputDevice* pDev, const tools::Rectangle& rRect, ScRefCellValue& rCell )
+{
+ vcl::PDFExtOutDevData* pPDFData = dynamic_cast< vcl::PDFExtOutDevData* >( pDev->GetExtOutDevData() );
+
+ OUString aURL;
+ OUString aCellText;
+ if (rCell.getType() == CELLTYPE_FORMULA)
+ {
+ ScFormulaCell* pFCell = rCell.getFormula();
+ if ( pFCell->IsHyperLinkCell() )
+ pFCell->GetURLResult( aURL, aCellText );
+ }
+
+ if ( !aURL.isEmpty() && pPDFData )
+ {
+ vcl::PDFExtOutDevBookmarkEntry aBookmark;
+ aBookmark.nLinkId = pPDFData->CreateLink(rRect, aCellText);
+ aBookmark.aBookmark = aURL;
+ std::vector< vcl::PDFExtOutDevBookmarkEntry >& rBookmarks = pPDFData->GetBookmarks();
+ rBookmarks.push_back( aBookmark );
+ }
+}
+
+void ScOutputData::SetSyntaxColor( vcl::Font* pFont, const ScRefCellValue& rCell )
+{
+ switch (rCell.getType())
+ {
+ case CELLTYPE_VALUE:
+ pFont->SetColor(*mxValueColor);
+ break;
+ case CELLTYPE_STRING:
+ pFont->SetColor(*mxTextColor);
+ break;
+ case CELLTYPE_FORMULA:
+ pFont->SetColor(*mxFormulaColor);
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+}
+
+static void lcl_SetEditColor( EditEngine& rEngine, const Color& rColor )
+{
+ ESelection aSel( 0, 0, rEngine.GetParagraphCount(), 0 );
+ SfxItemSet aSet( rEngine.GetEmptyItemSet() );
+ aSet.Put( SvxColorItem( rColor, EE_CHAR_COLOR ) );
+ rEngine.QuickSetAttribs( aSet, aSel );
+ // function is called with update mode set to FALSE
+}
+
+void ScOutputData::SetEditSyntaxColor( EditEngine& rEngine, const ScRefCellValue& rCell )
+{
+ Color aColor;
+ switch (rCell.getType())
+ {
+ case CELLTYPE_VALUE:
+ aColor = *mxValueColor;
+ break;
+ case CELLTYPE_STRING:
+ aColor = *mxTextColor;
+ break;
+ case CELLTYPE_FORMULA:
+ aColor = *mxFormulaColor;
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ lcl_SetEditColor( rEngine, aColor );
+}
+
+bool ScOutputData::GetMergeOrigin( SCCOL nX, SCROW nY, SCSIZE nArrY,
+ SCCOL& rOverX, SCROW& rOverY,
+ bool bVisRowChanged )
+{
+ bool bDoMerge = false;
+ bool bIsLeft = ( nX == nVisX1 );
+ bool bIsTop = ( nY == nVisY1 ) || bVisRowChanged;
+
+ bool bHOver;
+ bool bVOver;
+ bool bHidden;
+
+ if (!mpDoc->ColHidden(nX, nTab) && nX >= nX1 && nX <= nX2
+ && !mpDoc->RowHidden(nY, nTab) && nY >= nY1 && nY <= nY2)
+ {
+ ScCellInfo* pInfo = &pRowInfo[nArrY].cellInfo(nX);
+ bHOver = pInfo->bHOverlapped;
+ bVOver = pInfo->bVOverlapped;
+ }
+ else
+ {
+ ScMF nOverlap2 = mpDoc->GetAttr(nX, nY, nTab, ATTR_MERGE_FLAG)->GetValue();
+ bHOver = bool(nOverlap2 & ScMF::Hor);
+ bVOver = bool(nOverlap2 & ScMF::Ver);
+ }
+
+ if ( bHOver && bVOver )
+ bDoMerge = bIsLeft && bIsTop;
+ else if ( bHOver )
+ bDoMerge = bIsLeft;
+ else if ( bVOver )
+ bDoMerge = bIsTop;
+
+ rOverX = nX;
+ rOverY = nY;
+
+ while (bHOver) // nY constant
+ {
+ --rOverX;
+ bHidden = mpDoc->ColHidden(rOverX, nTab);
+ if ( !bDoMerge && !bHidden )
+ return false;
+
+ if (rOverX >= nX1 && !bHidden)
+ {
+ bHOver = pRowInfo[nArrY].cellInfo(rOverX).bHOverlapped;
+ bVOver = pRowInfo[nArrY].cellInfo(rOverX).bVOverlapped;
+ }
+ else
+ {
+ ScMF nOverlap = mpDoc->GetAttr(rOverX, rOverY, nTab, ATTR_MERGE_FLAG)->GetValue();
+ bHOver = bool(nOverlap & ScMF::Hor);
+ bVOver = bool(nOverlap & ScMF::Ver);
+ }
+ }
+
+ while (bVOver)
+ {
+ --rOverY;
+ bHidden = mpDoc->RowHidden(rOverY, nTab);
+ if ( !bDoMerge && !bHidden )
+ return false;
+
+ if (nArrY>0)
+ --nArrY; // local copy !
+
+ if (rOverX >= nX1 && rOverY >= nY1 &&
+ !mpDoc->ColHidden(rOverX, nTab) &&
+ !mpDoc->RowHidden(rOverY, nTab) &&
+ pRowInfo[nArrY].nRowNo == rOverY)
+ {
+ bVOver = pRowInfo[nArrY].cellInfo(rOverX).bVOverlapped;
+ }
+ else
+ {
+ ScMF nOverlap = mpDoc->GetAttr( rOverX, rOverY, nTab, ATTR_MERGE_FLAG )->GetValue();
+ bVOver = bool(nOverlap & ScMF::Ver);
+ }
+ }
+
+ return true;
+}
+
+static bool StringDiffer( const ScPatternAttr*& rpOldPattern, const ScPatternAttr* pNewPattern )
+{
+ OSL_ENSURE( pNewPattern, "pNewPattern" );
+
+ if ( SfxPoolItem::areSame( pNewPattern, rpOldPattern ) )
+ return false;
+ else if ( !rpOldPattern )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT ), rpOldPattern->GetItem( ATTR_FONT ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_CJK_FONT ), rpOldPattern->GetItem( ATTR_CJK_FONT ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_CTL_FONT ), rpOldPattern->GetItem( ATTR_CTL_FONT ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_HEIGHT ), rpOldPattern->GetItem( ATTR_FONT_HEIGHT ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_CJK_FONT_HEIGHT ), rpOldPattern->GetItem( ATTR_CJK_FONT_HEIGHT ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_CTL_FONT_HEIGHT ), rpOldPattern->GetItem( ATTR_CTL_FONT_HEIGHT ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_WEIGHT ), rpOldPattern->GetItem( ATTR_FONT_WEIGHT ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_CJK_FONT_WEIGHT ), rpOldPattern->GetItem( ATTR_CJK_FONT_WEIGHT ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_CTL_FONT_WEIGHT ), rpOldPattern->GetItem( ATTR_CTL_FONT_WEIGHT ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_POSTURE ), rpOldPattern->GetItem( ATTR_FONT_POSTURE ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_CJK_FONT_POSTURE ), rpOldPattern->GetItem( ATTR_CJK_FONT_POSTURE ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_CTL_FONT_POSTURE ), rpOldPattern->GetItem( ATTR_CTL_FONT_POSTURE ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_UNDERLINE ), rpOldPattern->GetItem( ATTR_FONT_UNDERLINE ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_OVERLINE ), rpOldPattern->GetItem( ATTR_FONT_OVERLINE ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_WORDLINE ), rpOldPattern->GetItem( ATTR_FONT_WORDLINE ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_CROSSEDOUT ), rpOldPattern->GetItem( ATTR_FONT_CROSSEDOUT ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_CONTOUR ), rpOldPattern->GetItem( ATTR_FONT_CONTOUR ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_SHADOWED ), rpOldPattern->GetItem( ATTR_FONT_SHADOWED ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_COLOR ), rpOldPattern->GetItem( ATTR_FONT_COLOR ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_HOR_JUSTIFY ), rpOldPattern->GetItem( ATTR_HOR_JUSTIFY ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_HOR_JUSTIFY_METHOD ), rpOldPattern->GetItem( ATTR_HOR_JUSTIFY_METHOD ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_VER_JUSTIFY ), rpOldPattern->GetItem( ATTR_VER_JUSTIFY ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_VER_JUSTIFY_METHOD ), rpOldPattern->GetItem( ATTR_VER_JUSTIFY_METHOD ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_STACKED ), rpOldPattern->GetItem( ATTR_STACKED ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_LINEBREAK ), rpOldPattern->GetItem( ATTR_LINEBREAK ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_MARGIN ), rpOldPattern->GetItem( ATTR_MARGIN ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_ROTATE_VALUE ), rpOldPattern->GetItem( ATTR_ROTATE_VALUE ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FORBIDDEN_RULES ), rpOldPattern->GetItem( ATTR_FORBIDDEN_RULES ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_EMPHASISMARK ), rpOldPattern->GetItem( ATTR_FONT_EMPHASISMARK ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_RELIEF ), rpOldPattern->GetItem( ATTR_FONT_RELIEF ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_BACKGROUND ), rpOldPattern->GetItem( ATTR_BACKGROUND ) ) )
+ return true; // needed with automatic text color
+ else
+ {
+ rpOldPattern = pNewPattern;
+ return false;
+ }
+}
+
+static void lcl_CreateInterpretProgress( bool& bProgress, ScDocument* pDoc,
+ const ScFormulaCell* pFCell )
+{
+ if ( !bProgress && pFCell->GetDirty() )
+ {
+ ScProgress::CreateInterpretProgress( pDoc );
+ bProgress = true;
+ }
+}
+
+static bool IsAmbiguousScript( SvtScriptType nScript )
+{
+ return ( nScript != SvtScriptType::LATIN &&
+ nScript != SvtScriptType::ASIAN &&
+ nScript != SvtScriptType::COMPLEX );
+}
+
+bool ScOutputData::IsEmptyCellText( const RowInfo* pThisRowInfo, SCCOL nX, SCROW nY )
+{
+ // pThisRowInfo may be NULL
+
+ bool bEmpty;
+ if ( pThisRowInfo && nX <= nX2 )
+ bEmpty = pThisRowInfo->basicCellInfo(nX).bEmptyCellText;
+ else
+ {
+ ScRefCellValue aCell(*mpDoc, ScAddress(nX, nY, nTab));
+ bEmpty = aCell.isEmpty();
+ }
+
+ if ( !bEmpty && ( nX < nX1 || nX > nX2 || !pThisRowInfo ) )
+ {
+ // for the range nX1..nX2 in RowInfo, cell protection attribute is already evaluated
+ // into bEmptyCellText in ScDocument::FillInfo / lcl_HidePrint (printfun)
+
+ bool bIsPrint = ( eType == OUTTYPE_PRINTER );
+
+ if ( bIsPrint || bTabProtected )
+ {
+ const ScProtectionAttr* pAttr =
+ mpDoc->GetEffItem( nX, nY, nTab, ATTR_PROTECTION );
+ if ( bIsPrint && pAttr->GetHidePrint() )
+ bEmpty = true;
+ else if ( bTabProtected )
+ {
+ if ( pAttr->GetHideCell() )
+ bEmpty = true;
+ else if ( mbShowFormulas && pAttr->GetHideFormula() )
+ {
+ if (mpDoc->GetCellType(ScAddress(nX, nY, nTab)) == CELLTYPE_FORMULA)
+ bEmpty = true;
+ }
+ }
+ }
+ }
+ return bEmpty;
+}
+
+void ScOutputData::GetVisibleCell( SCCOL nCol, SCROW nRow, SCTAB nTabP, ScRefCellValue& rCell )
+{
+ rCell.assign(*mpDoc, ScAddress(nCol, nRow, nTabP));
+ if (!rCell.isEmpty() && IsEmptyCellText(nullptr, nCol, nRow))
+ rCell.clear();
+}
+
+bool ScOutputData::IsAvailable( SCCOL nX, SCROW nY )
+{
+ // apply the same logic here as in DrawStrings/DrawEdit:
+ // Stop at non-empty or merged or overlapped cell,
+ // where a note is empty as well as a cell that's hidden by protection settings
+
+ ScRefCellValue aCell(*mpDoc, ScAddress(nX, nY, nTab));
+ if (!aCell.isEmpty() && !IsEmptyCellText(nullptr, nX, nY))
+ return false;
+
+ const ScPatternAttr* pPattern = mpDoc->GetPattern( nX, nY, nTab );
+ return !(pPattern->GetItem(ATTR_MERGE).IsMerged() ||
+ pPattern->GetItem(ATTR_MERGE_FLAG).IsOverlapped());
+}
+
+// nX, nArrY: loop variables from DrawStrings / DrawEdit
+// nPosX, nPosY: corresponding positions for nX, nArrY
+// nCellX, nCellY: position of the cell that contains the text
+// nNeeded: Text width, including margin
+// rPattern: cell format at nCellX, nCellY
+// nHorJustify: horizontal alignment (visual) to determine which cells to use for long strings
+// bCellIsValue: if set, don't extend into empty cells
+// bBreak: if set, don't extend, and don't set clip marks (but rLeftClip/rRightClip is set)
+// bOverwrite: if set, also extend into non-empty cells (for rotated text)
+// rParam output: various area parameters.
+
+void ScOutputData::GetOutputArea( SCCOL nX, SCSIZE nArrY, tools::Long nPosX, tools::Long nPosY,
+ SCCOL nCellX, SCROW nCellY, tools::Long nNeeded,
+ const ScPatternAttr& rPattern,
+ sal_uInt16 nHorJustify, bool bCellIsValue,
+ bool bBreak, bool bOverwrite,
+ OutputAreaParam& rParam )
+{
+ // rThisRowInfo may be for a different row than nCellY, is still used for clip marks
+ RowInfo& rThisRowInfo = pRowInfo[nArrY];
+
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ tools::Long nCellPosX = nPosX; // find nCellX position, starting at nX/nPosX
+ SCCOL nCompCol = nX;
+ while ( nCellX > nCompCol )
+ {
+ //! extra member function for width?
+ tools::Long nColWidth = ( nCompCol <= nX2 ) ?
+ pRowInfo[0].basicCellInfo(nCompCol).nWidth :
+ static_cast<tools::Long>( mpDoc->GetColWidth( nCompCol, nTab ) * mnPPTX );
+ nCellPosX += nColWidth * nLayoutSign;
+ ++nCompCol;
+ }
+ while ( nCellX < nCompCol )
+ {
+ --nCompCol;
+ tools::Long nColWidth = ( nCompCol <= nX2 ) ?
+ pRowInfo[0].basicCellInfo(nCompCol).nWidth :
+ static_cast<tools::Long>( mpDoc->GetColWidth( nCompCol, nTab ) * mnPPTX );
+ nCellPosX -= nColWidth * nLayoutSign;
+ }
+
+ tools::Long nCellPosY = nPosY; // find nCellY position, starting at nArrY/nPosY
+ SCSIZE nCompArr = nArrY;
+ SCROW nCompRow = pRowInfo[nCompArr].nRowNo;
+ while ( nCellY > nCompRow )
+ {
+ if ( nCompArr + 1 < nArrCount )
+ {
+ nCellPosY += pRowInfo[nCompArr].nHeight;
+ ++nCompArr;
+ nCompRow = pRowInfo[nCompArr].nRowNo;
+ }
+ else
+ {
+ sal_uInt16 nDocHeight = mpDoc->GetRowHeight( nCompRow, nTab );
+ if ( nDocHeight )
+ nCellPosY += static_cast<tools::Long>( nDocHeight * mnPPTY );
+ ++nCompRow;
+ }
+ }
+ nCellPosY -= mpDoc->GetScaledRowHeight( nCellY, nCompRow-1, nTab, mnPPTY );
+
+ const ScMergeAttr* pMerge = &rPattern.GetItem( ATTR_MERGE );
+ bool bMerged = pMerge->IsMerged();
+ tools::Long nMergeCols = pMerge->GetColMerge();
+ if ( nMergeCols == 0 )
+ nMergeCols = 1;
+ tools::Long nMergeRows = pMerge->GetRowMerge();
+ if ( nMergeRows == 0 )
+ nMergeRows = 1;
+
+ tools::Long nMergeSizeX = 0;
+ for ( tools::Long i=0; i<nMergeCols; i++ )
+ {
+ tools::Long nColWidth = ( nCellX+i <= nX2 ) ?
+ pRowInfo[0].basicCellInfo(nCellX+i).nWidth :
+ static_cast<tools::Long>( mpDoc->GetColWidth( sal::static_int_cast<SCCOL>(nCellX+i), nTab ) * mnPPTX );
+ nMergeSizeX += nColWidth;
+ }
+ tools::Long nMergeSizeY = 0;
+ short nDirect = 0;
+ if ( rThisRowInfo.nRowNo == nCellY )
+ {
+ // take first row's height from row info
+ nMergeSizeY += rThisRowInfo.nHeight;
+ nDirect = 1; // skip in loop
+ }
+ // following rows always from document
+ nMergeSizeY += mpDoc->GetScaledRowHeight( nCellY+nDirect, nCellY+nMergeRows-1, nTab, mnPPTY);
+
+ --nMergeSizeX; // leave out the grid horizontally, also for alignment (align between grid lines)
+
+ rParam.mnColWidth = nMergeSizeX; // store the actual column width.
+ rParam.mnLeftClipLength = rParam.mnRightClipLength = 0;
+
+ // construct the rectangles using logical left/right values (justify is called at the end)
+
+ // rAlignRect is the single cell or merged area, used for alignment.
+
+ rParam.maAlignRect.SetLeft( nCellPosX );
+ rParam.maAlignRect.SetRight( nCellPosX + ( nMergeSizeX - 1 ) * nLayoutSign );
+ rParam.maAlignRect.SetTop( nCellPosY );
+ rParam.maAlignRect.SetBottom( nCellPosY + nMergeSizeY - 1 );
+
+ // rClipRect is all cells that are used for output.
+ // For merged cells this is the same as rAlignRect, otherwise neighboring cells can also be used.
+
+ rParam.maClipRect = rParam.maAlignRect;
+ if ( nNeeded > nMergeSizeX )
+ {
+ SvxCellHorJustify eHorJust = static_cast<SvxCellHorJustify>(nHorJustify);
+
+ tools::Long nMissing = nNeeded - nMergeSizeX;
+ tools::Long nLeftMissing = 0;
+ tools::Long nRightMissing = 0;
+ switch ( eHorJust )
+ {
+ case SvxCellHorJustify::Left:
+ nRightMissing = nMissing;
+ break;
+ case SvxCellHorJustify::Right:
+ nLeftMissing = nMissing;
+ break;
+ case SvxCellHorJustify::Center:
+ nLeftMissing = nMissing / 2;
+ nRightMissing = nMissing - nLeftMissing;
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+
+ // nLeftMissing, nRightMissing are logical, eHorJust values are visual
+ if ( bLayoutRTL )
+ ::std::swap( nLeftMissing, nRightMissing );
+
+ SCCOL nRightX = nCellX;
+ SCCOL nLeftX = nCellX;
+ if ( !bMerged && !bCellIsValue && !bBreak )
+ {
+ // look for empty cells into which the text can be extended
+
+ while ( nRightMissing > 0 && nRightX < mpDoc->MaxCol() && ( bOverwrite || IsAvailable( nRightX+1, nCellY ) ) )
+ {
+ ++nRightX;
+ tools::Long nAdd = static_cast<tools::Long>( mpDoc->GetColWidth( nRightX, nTab ) * mnPPTX );
+ nRightMissing -= nAdd;
+ rParam.maClipRect.AdjustRight(nAdd * nLayoutSign );
+
+ if ( rThisRowInfo.nRowNo == nCellY && nRightX >= nX1 && nRightX <= nX2 )
+ rThisRowInfo.cellInfo(nRightX-1).bHideGrid = true;
+ }
+
+ while ( nLeftMissing > 0 && nLeftX > 0 && ( bOverwrite || IsAvailable( nLeftX-1, nCellY ) ) )
+ {
+ if ( rThisRowInfo.nRowNo == nCellY && nLeftX >= nX1 && nLeftX <= nX2 )
+ rThisRowInfo.cellInfo(nLeftX-1).bHideGrid = true;
+
+ --nLeftX;
+ tools::Long nAdd = static_cast<tools::Long>( mpDoc->GetColWidth( nLeftX, nTab ) * mnPPTX );
+ nLeftMissing -= nAdd;
+ rParam.maClipRect.AdjustLeft( -(nAdd * nLayoutSign) );
+ }
+ }
+
+ // Set flag and reserve space for clipping mark triangle,
+ // even if rThisRowInfo isn't for nCellY (merged cells).
+ if ( nRightMissing > 0 && bMarkClipped && nRightX >= nX1 && nRightX <= nX2 && !bBreak && !bCellIsValue )
+ {
+ rThisRowInfo.cellInfo(nRightX).nClipMark |= ScClipMark::Right;
+ bAnyClipped = true;
+ tools::Long nMarkPixel = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTX );
+ rParam.maClipRect.AdjustRight( -(nMarkPixel * nLayoutSign) );
+ }
+ if ( nLeftMissing > 0 && bMarkClipped && nLeftX >= nX1 && nLeftX <= nX2 && !bBreak && !bCellIsValue )
+ {
+ rThisRowInfo.cellInfo(nLeftX).nClipMark |= ScClipMark::Left;
+ bAnyClipped = true;
+ tools::Long nMarkPixel = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTX );
+ rParam.maClipRect.AdjustLeft(nMarkPixel * nLayoutSign );
+ }
+
+ rParam.mbLeftClip = ( nLeftMissing > 0 );
+ rParam.mbRightClip = ( nRightMissing > 0 );
+ rParam.mnLeftClipLength = nLeftMissing;
+ rParam.mnRightClipLength = nRightMissing;
+ }
+ else
+ {
+ rParam.mbLeftClip = rParam.mbRightClip = false;
+
+ // leave space for AutoFilter on screen
+ // (for automatic line break: only if not formatting for printer, as in ScColumn::GetNeededSize)
+
+ if ( eType==OUTTYPE_WINDOW &&
+ ( rPattern.GetItem(ATTR_MERGE_FLAG).GetValue() & (ScMF::Auto|ScMF::Button|ScMF::ButtonPopup) ) &&
+ ( !bBreak || mpRefDevice == pFmtDevice ) )
+ {
+ // filter drop-down width depends on row height
+ double fZoom = mpRefDevice ? static_cast<double>(mpRefDevice->GetMapMode().GetScaleY()) : 1.0;
+ fZoom = fZoom > 1.0 ? fZoom : 1.0;
+ const tools::Long nFilter = fZoom * DROPDOWN_BITMAP_SIZE;
+ bool bFit = ( nNeeded + nFilter <= nMergeSizeX );
+ if ( bFit )
+ {
+ // content fits even in the remaining area without the filter button
+ // -> align within that remaining area
+
+ rParam.maAlignRect.AdjustRight( -(nFilter * nLayoutSign) );
+ rParam.maClipRect.AdjustRight( -(nFilter * nLayoutSign) );
+ }
+ }
+ }
+
+ // justify both rectangles for alignment calculation, use with DrawText etc.
+
+ rParam.maAlignRect.Normalize();
+ rParam.maClipRect.Normalize();
+}
+
+namespace {
+
+bool beginsWithRTLCharacter(const OUString& rStr)
+{
+ if (rStr.isEmpty())
+ return false;
+
+ switch (ScGlobal::getCharClass().getCharacterDirection(rStr, 0))
+ {
+ case i18n::DirectionProperty_RIGHT_TO_LEFT:
+ case i18n::DirectionProperty_RIGHT_TO_LEFT_ARABIC:
+ case i18n::DirectionProperty_RIGHT_TO_LEFT_EMBEDDING:
+ case i18n::DirectionProperty_RIGHT_TO_LEFT_OVERRIDE:
+ return true;
+ default:
+ ;
+ }
+
+ return false;
+}
+
+}
+
+/** Get left, right or centered alignment from RTL context.
+
+ Does not return standard, block or repeat, for these the contextual left or
+ right alignment is returned.
+ */
+static SvxCellHorJustify getAlignmentFromContext( SvxCellHorJustify eInHorJust,
+ bool bCellIsValue, const OUString& rText,
+ const ScPatternAttr& rPattern, const SfxItemSet* pCondSet,
+ const ScDocument* pDoc, SCTAB nTab, const bool bNumberFormatIsText )
+{
+ SvxCellHorJustify eHorJustContext = eInHorJust;
+ bool bUseWritingDirection = false;
+ if (eInHorJust == SvxCellHorJustify::Standard)
+ {
+ // fdo#32530: Default alignment depends on value vs
+ // string, and the direction of the 1st letter.
+ if (beginsWithRTLCharacter( rText)) //If language is RTL
+ {
+ if (bCellIsValue)
+ eHorJustContext = bNumberFormatIsText ? SvxCellHorJustify::Right : SvxCellHorJustify::Left;
+ else
+ eHorJustContext = SvxCellHorJustify::Right;
+ }
+ else if (bCellIsValue) //If language is not RTL
+ eHorJustContext = bNumberFormatIsText ? SvxCellHorJustify::Left : SvxCellHorJustify::Right;
+ else
+ bUseWritingDirection = true;
+ }
+
+ if (bUseWritingDirection ||
+ eInHorJust == SvxCellHorJustify::Block || eInHorJust == SvxCellHorJustify::Repeat)
+ {
+ SvxFrameDirection nDirection = lcl_GetValue<SvxFrameDirectionItem, SvxFrameDirection>(rPattern, ATTR_WRITINGDIR, pCondSet);
+ if (nDirection == SvxFrameDirection::Horizontal_LR_TB || nDirection == SvxFrameDirection::Vertical_LR_TB)
+ eHorJustContext = SvxCellHorJustify::Left;
+ else if (nDirection == SvxFrameDirection::Environment)
+ {
+ SAL_WARN_IF( !pDoc, "sc.ui", "getAlignmentFromContext - pDoc==NULL");
+ // fdo#73588: The content of the cell must also
+ // begin with a RTL character to be right
+ // aligned; otherwise, it should be left aligned.
+ eHorJustContext = (pDoc && pDoc->IsLayoutRTL(nTab) && (beginsWithRTLCharacter( rText))) ? SvxCellHorJustify::Right : SvxCellHorJustify::Left;
+ }
+ else
+ eHorJustContext = SvxCellHorJustify::Right;
+ }
+ return eHorJustContext;
+}
+
+void ScOutputData::DrawStrings( bool bPixelToLogic )
+{
+ LayoutStrings(bPixelToLogic);
+}
+
+void ScOutputData::LayoutStrings(bool bPixelToLogic)
+{
+ bool bOrigIsInLayoutStrings = mpDoc->IsInLayoutStrings();
+ mpDoc->SetLayoutStrings(true);
+ OSL_ENSURE( mpDev == mpRefDevice ||
+ mpDev->GetMapMode().GetMapUnit() == mpRefDevice->GetMapMode().GetMapUnit(),
+ "LayoutStrings: different MapUnits ?!?!" );
+ vcl::text::ComplexTextLayoutFlags eTextLayout = mpDev->GetLayoutMode();
+ mpDev->SetLayoutMode(vcl::text::ComplexTextLayoutFlags::Default);
+
+ comphelper::ScopeGuard g([this, bOrigIsInLayoutStrings, eTextLayout] {
+ mpDoc->SetLayoutStrings(bOrigIsInLayoutStrings);
+ mpDev->SetLayoutMode(eTextLayout);
+ });
+
+ sc::IdleSwitch aIdleSwitch(*mpDoc, false);
+
+ // Try to limit interpreting to only visible cells. Calling e.g. IsValue()
+ // on a formula cell that needs interpreting would call Interpret()
+ // for the entire formula group, which could be large.
+ mpDoc->InterpretCellsIfNeeded( ScRange( nX1, nY1, nTab, nX2, nY2, nTab ));
+
+ vcl::PDFExtOutDevData* pPDFData = dynamic_cast< vcl::PDFExtOutDevData* >(mpDev->GetExtOutDevData() );
+
+ ScDrawStringsVars aVars( this, bPixelToLogic );
+
+ bool bProgress = false;
+
+ tools::Long nInitPosX = nScrX;
+ if ( bLayoutRTL )
+ nInitPosX += nMirrorW - 1; // pixels
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ SCCOL nLastContentCol = mpDoc->MaxCol();
+ if ( nX2 < mpDoc->MaxCol() )
+ {
+ SCROW nEndRow;
+ mpDoc->GetCellArea(nTab, nLastContentCol, nEndRow);
+ }
+
+ SCCOL nLoopStartX = nX1;
+ if ( nX1 > 0 )
+ --nLoopStartX; // start before nX1 for rest of long text to the left
+
+ // variables for GetOutputArea
+ OutputAreaParam aAreaParam;
+ bool bCellIsValue = false;
+ tools::Long nNeededWidth = 0;
+ const ScPatternAttr* pPattern = nullptr;
+ const SfxItemSet* pCondSet = nullptr;
+ const ScPatternAttr* pOldPattern = nullptr;
+ const SfxItemSet* pOldCondSet = nullptr;
+ SvtScriptType nOldScript = SvtScriptType::NONE;
+
+ // alternative pattern instances in case we need to modify the pattern
+ // before processing the cell value.
+ std::vector<std::unique_ptr<ScPatternAttr> > aAltPatterns;
+
+ KernArray aDX;
+ tools::Long nPosY = nScrY;
+ for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
+ {
+ RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+ SCROW nY = pThisRowInfo->nRowNo;
+ if (pThisRowInfo->bChanged)
+ {
+ tools::Long nPosX = nInitPosX;
+ if ( nLoopStartX < nX1 )
+ nPosX -= pRowInfo[0].basicCellInfo(nLoopStartX).nWidth * nLayoutSign;
+ for (SCCOL nX=nLoopStartX; nX<=nX2; nX++)
+ {
+ bool bMergeEmpty = false;
+ const ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
+ bool bEmpty = nX < nX1 || pThisRowInfo->basicCellInfo(nX).bEmptyCellText;
+
+ SCCOL nCellX = nX; // position where the cell really starts
+ SCROW nCellY = nY;
+ bool bDoCell = false;
+ bool bUseEditEngine = false;
+
+ // Part of a merged cell?
+
+ bool bOverlapped = (pInfo->bHOverlapped || pInfo->bVOverlapped);
+ if ( bOverlapped )
+ {
+ bEmpty = true;
+
+ SCCOL nOverX; // start of the merged cells
+ SCROW nOverY;
+ bool bVisChanged = !pRowInfo[nArrY-1].bChanged;
+ if (GetMergeOrigin( nX,nY, nArrY, nOverX,nOverY, bVisChanged ))
+ {
+ nCellX = nOverX;
+ nCellY = nOverY;
+ bDoCell = true;
+ }
+ else
+ bMergeEmpty = true;
+ }
+
+ // Rest of a long text further to the left?
+
+ if ( bEmpty && !bMergeEmpty && nX < nX1 && !bOverlapped )
+ {
+ SCCOL nTempX=nX1;
+ while (nTempX > 0 && IsEmptyCellText( pThisRowInfo, nTempX, nY ))
+ --nTempX;
+
+ if ( nTempX < nX1 &&
+ !IsEmptyCellText( pThisRowInfo, nTempX, nY ) &&
+ !mpDoc->HasAttrib( nTempX,nY,nTab, nX1,nY,nTab, HasAttrFlags::Merged | HasAttrFlags::Overlapped ) )
+ {
+ nCellX = nTempX;
+ bDoCell = true;
+ }
+ }
+
+ // Rest of a long text further to the right?
+
+ if ( bEmpty && !bMergeEmpty && nX == nX2 && !bOverlapped )
+ {
+ // don't have to look further than nLastContentCol
+
+ SCCOL nTempX=nX;
+ while (nTempX < nLastContentCol && IsEmptyCellText( pThisRowInfo, nTempX, nY ))
+ ++nTempX;
+
+ if ( nTempX > nX &&
+ !IsEmptyCellText( pThisRowInfo, nTempX, nY ) &&
+ !mpDoc->HasAttrib( nTempX,nY,nTab, nX,nY,nTab, HasAttrFlags::Merged | HasAttrFlags::Overlapped ) )
+ {
+ nCellX = nTempX;
+ bDoCell = true;
+ }
+ }
+
+ // normal visible cell
+
+ if (!bEmpty)
+ bDoCell = true;
+
+ // don't output the cell that's being edited
+
+ if ( bDoCell && bEditMode && nCellX == nEditCol && nCellY == nEditRow )
+ bDoCell = false;
+
+ // skip text in cell if data bar/icon set is set and only value selected
+ if ( bDoCell )
+ {
+ if(pInfo->pDataBar && !pInfo->pDataBar->mbShowValue)
+ bDoCell = false;
+ if(pInfo->pIconSet && !pInfo->pIconSet->mbShowValue)
+ bDoCell = false;
+ }
+
+ // output the cell text
+
+ ScRefCellValue aCell;
+ if (bDoCell)
+ {
+ if ( nCellY == nY && nCellX == nX && nCellX >= nX1 && nCellX <= nX2 )
+ aCell = pThisRowInfo->cellInfo(nCellX).maCell;
+ else
+ GetVisibleCell( nCellX, nCellY, nTab, aCell ); // get from document
+ if (aCell.isEmpty())
+ bDoCell = false;
+ else if (aCell.getType() == CELLTYPE_EDIT)
+ bUseEditEngine = true;
+ }
+
+ // Check if this cell is mis-spelled.
+ if (bDoCell && !bUseEditEngine && aCell.getType() == CELLTYPE_STRING)
+ {
+ if (mpSpellCheckCxt && mpSpellCheckCxt->isMisspelled(nCellX, nCellY))
+ bUseEditEngine = true;
+ }
+
+ if (bDoCell && !bUseEditEngine)
+ {
+ if ( nCellY == nY && nCellX >= nX1 && nCellX <= nX2 )
+ {
+ ScCellInfo& rCellInfo = pThisRowInfo->cellInfo(nCellX);
+ pPattern = rCellInfo.pPatternAttr;
+ pCondSet = rCellInfo.pConditionSet;
+
+ if ( !pPattern )
+ {
+ // #i68085# pattern from cell info for hidden columns is null,
+ // test for null is quicker than using column flags
+ pPattern = mpDoc->GetPattern( nCellX, nCellY, nTab );
+ pCondSet = mpDoc->GetCondResult( nCellX, nCellY, nTab );
+ }
+ }
+ else // get from document
+ {
+ pPattern = mpDoc->GetPattern( nCellX, nCellY, nTab );
+ pCondSet = mpDoc->GetCondResult( nCellX, nCellY, nTab );
+ }
+ if ( mpDoc->GetPreviewFont() || mpDoc->GetPreviewCellStyle() )
+ {
+ aAltPatterns.push_back(std::make_unique<ScPatternAttr>(*pPattern));
+ ScPatternAttr* pAltPattern = aAltPatterns.back().get();
+ if ( ScStyleSheet* pPreviewStyle = mpDoc->GetPreviewCellStyle( nCellX, nCellY, nTab ) )
+ {
+ pAltPattern->SetStyleSheet(pPreviewStyle);
+ }
+ else if ( SfxItemSet* pFontSet = mpDoc->GetPreviewFont( nCellX, nCellY, nTab ) )
+ {
+ if ( const SvxFontItem* pItem = pFontSet->GetItemIfSet( ATTR_FONT ) )
+ pAltPattern->GetItemSet().Put( *pItem );
+ if ( const SvxFontItem* pItem = pFontSet->GetItemIfSet( ATTR_CJK_FONT ) )
+ pAltPattern->GetItemSet().Put( *pItem );
+ if ( const SvxFontItem* pItem = pFontSet->GetItemIfSet( ATTR_CTL_FONT ) )
+ pAltPattern->GetItemSet().Put( *pItem );
+ }
+ pPattern = pAltPattern;
+ }
+
+ if (aCell.hasNumeric() &&
+ pPattern->GetItem(ATTR_LINEBREAK, pCondSet).GetValue())
+ {
+ // Disable line break when the cell content is numeric.
+ aAltPatterns.push_back(std::make_unique<ScPatternAttr>(*pPattern));
+ ScPatternAttr* pAltPattern = aAltPatterns.back().get();
+ ScLineBreakCell aLineBreak(false);
+ pAltPattern->GetItemSet().Put(aLineBreak);
+ pPattern = pAltPattern;
+ }
+
+ SvtScriptType nScript = mpDoc->GetCellScriptType(
+ ScAddress(nCellX, nCellY, nTab),
+ pPattern->GetNumberFormat(mpDoc->GetFormatTable(), pCondSet));
+
+ if (nScript == SvtScriptType::NONE)
+ nScript = ScGlobal::GetDefaultScriptType();
+
+ if ( !SfxPoolItem::areSame(pPattern, pOldPattern) || pCondSet != pOldCondSet ||
+ nScript != nOldScript || mbSyntaxMode )
+ {
+ if ( StringDiffer(pOldPattern,pPattern) ||
+ pCondSet != pOldCondSet || nScript != nOldScript || mbSyntaxMode )
+ {
+ aVars.SetPattern(pPattern, pCondSet, aCell, nScript);
+ }
+ else
+ aVars.SetPatternSimple( pPattern, pCondSet );
+ pOldPattern = pPattern;
+ pOldCondSet = pCondSet;
+ nOldScript = nScript;
+ }
+
+ // use edit engine for rotated, stacked or mixed-script text
+ if ( aVars.GetOrient() == SvxCellOrientation::Stacked ||
+ aVars.IsRotated() || IsAmbiguousScript(nScript) )
+ bUseEditEngine = true;
+ }
+ if (bDoCell && !bUseEditEngine)
+ {
+ bool bFormulaCell = (aCell.getType() == CELLTYPE_FORMULA);
+ if ( bFormulaCell )
+ lcl_CreateInterpretProgress(bProgress, mpDoc, aCell.getFormula());
+ if ( aVars.SetText(aCell) )
+ pOldPattern = nullptr;
+ bUseEditEngine = aVars.HasEditCharacters() || (bFormulaCell && aCell.getFormula()->IsMultilineResult());
+ }
+ tools::Long nTotalMargin = 0;
+ SvxCellHorJustify eOutHorJust = SvxCellHorJustify::Standard;
+ if (bDoCell && !bUseEditEngine)
+ {
+ CellType eCellType = aCell.getType();
+ bCellIsValue = ( eCellType == CELLTYPE_VALUE );
+ if ( eCellType == CELLTYPE_FORMULA )
+ {
+ ScFormulaCell* pFCell = aCell.getFormula();
+ bCellIsValue = pFCell->IsRunning() || pFCell->IsValue();
+ }
+
+ const bool bNumberFormatIsText = lcl_isNumberFormatText( mpDoc, nCellX, nCellY, nTab );
+ eOutHorJust = getAlignmentFromContext( aVars.GetHorJust(), bCellIsValue, aVars.GetString(),
+ *pPattern, pCondSet, mpDoc, nTab, bNumberFormatIsText );
+
+ bool bBreak = ( aVars.GetLineBreak() || aVars.GetHorJust() == SvxCellHorJustify::Block );
+ // #i111387# #o11817313# tdf#121040 disable automatic line breaks for all number formats
+ // Must be synchronized with ScColumn::GetNeededSize()
+ SvNumberFormatter* pFormatter = mpDoc->GetFormatTable();
+ if (bBreak && bCellIsValue && (pFormatter->GetType(aVars.GetResultValueFormat()) == SvNumFormatType::NUMBER))
+ bBreak = false;
+
+ bool bRepeat = aVars.IsRepeat() && !bBreak;
+ bool bShrink = aVars.IsShrink() && !bBreak && !bRepeat;
+
+ nTotalMargin =
+ static_cast<tools::Long>(aVars.GetLeftTotal() * mnPPTX) +
+ static_cast<tools::Long>(aVars.GetMargin()->GetRightMargin() * mnPPTX);
+
+ nNeededWidth = aVars.GetTextSize().Width() + nTotalMargin;
+
+ // GetOutputArea gives justified rectangles
+ GetOutputArea( nX, nArrY, nPosX, nPosY, nCellX, nCellY, nNeededWidth,
+ *pPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
+ bCellIsValue || bRepeat || bShrink, bBreak, false,
+ aAreaParam );
+
+ aVars.RepeatToFill( aAreaParam.mnColWidth - nTotalMargin );
+ if ( bShrink )
+ {
+ if ( aVars.GetOrient() != SvxCellOrientation::Standard )
+ {
+ // Only horizontal scaling is handled here.
+ // DrawEdit is used to vertically scale 90 deg rotated text.
+ bUseEditEngine = true;
+ }
+ else if ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) // horizontal
+ {
+ tools::Long nAvailable = aAreaParam.maAlignRect.GetWidth() - nTotalMargin;
+ tools::Long nScaleSize = aVars.GetTextSize().Width(); // without margin
+
+ if ( nAvailable > 0 && nScaleSize > 0 ) // 0 if the text is empty (formulas, number formats)
+ {
+ tools::Long nScale = ( nAvailable * 100 ) / nScaleSize;
+
+ aVars.SetShrinkScale( nScale, nOldScript );
+ tools::Long nNewSize = aVars.GetTextSize().Width();
+
+ sal_uInt16 nShrinkAgain = 0;
+ while ( nNewSize > nAvailable && nShrinkAgain < SC_SHRINKAGAIN_MAX )
+ {
+ // If the text is still too large, reduce the scale again by 10%, until it fits,
+ // at most 7 times (it's less than 50% of the calculated scale then).
+
+ nScale = ( nScale * 9 ) / 10;
+ aVars.SetShrinkScale( nScale, nOldScript );
+ nNewSize = aVars.GetTextSize().Width();
+ ++nShrinkAgain;
+ }
+ // If even at half the size the font still isn't rendered smaller,
+ // fall back to normal clipping (showing ### for numbers).
+ if ( nNewSize <= nAvailable )
+ {
+ // Reset relevant parameters.
+ aAreaParam.mbLeftClip = aAreaParam.mbRightClip = false;
+ aAreaParam.mnLeftClipLength = aAreaParam.mnRightClipLength = 0;
+ }
+
+ pOldPattern = nullptr;
+ }
+ }
+ }
+
+ if ( bRepeat && !aAreaParam.mbLeftClip && !aAreaParam.mbRightClip )
+ {
+ tools::Long nAvailable = aAreaParam.maAlignRect.GetWidth() - nTotalMargin;
+ tools::Long nRepeatSize = aVars.GetTextSize().Width(); // without margin
+ // When formatting for the printer, the text sizes don't always add up.
+ // Round down (too few repetitions) rather than exceeding the cell size then:
+ if ( pFmtDevice != mpRefDevice )
+ ++nRepeatSize;
+ if ( nRepeatSize > 0 )
+ {
+ tools::Long nRepeatCount = nAvailable / nRepeatSize;
+ if ( nRepeatCount > 1 )
+ {
+ OUString aCellStr = aVars.GetString();
+ OUStringBuffer aRepeated(aCellStr);
+ for ( tools::Long nRepeat = 1; nRepeat < nRepeatCount; nRepeat++ )
+ aRepeated.append(aCellStr);
+ aVars.SetAutoText( aRepeated.makeStringAndClear() );
+ }
+ }
+ }
+
+ // use edit engine if automatic line breaks are needed
+ if ( bBreak )
+ {
+ if ( aVars.GetOrient() == SvxCellOrientation::Standard )
+ bUseEditEngine = ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip );
+ else
+ {
+ tools::Long nHeight = aVars.GetTextSize().Height() +
+ static_cast<tools::Long>(aVars.GetMargin()->GetTopMargin()*mnPPTY) +
+ static_cast<tools::Long>(aVars.GetMargin()->GetBottomMargin()*mnPPTY);
+ bUseEditEngine = ( nHeight > aAreaParam.maClipRect.GetHeight() );
+ }
+ }
+ if (!bUseEditEngine)
+ {
+ bUseEditEngine =
+ aVars.GetHorJust() == SvxCellHorJustify::Block &&
+ aVars.GetHorJustMethod() == SvxCellJustifyMethod::Distribute;
+ }
+ }
+ if (bUseEditEngine)
+ {
+ // mark the cell in ScCellInfo to be drawn in DrawEdit:
+ // Cells to the left are marked directly, cells to the
+ // right are handled by the flag for nX2
+ SCCOL nMarkX = ( nCellX <= nX2 ) ? nCellX : nX2;
+ RowInfo* pMarkRowInfo = ( nCellY == nY ) ? pThisRowInfo : &pRowInfo[0];
+ pMarkRowInfo->basicCellInfo(nMarkX).bEditEngine = true;
+ bDoCell = false; // don't draw here
+ }
+ if ( bDoCell )
+ {
+ if ( bCellIsValue && ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) )
+ {
+ bool bHasHashText = false;
+ if (mbShowFormulas)
+ {
+ aVars.SetHashText();
+ bHasHashText = true;
+ }
+ else
+ // Adjust the decimals to fit the available column width.
+ bHasHashText = aVars.SetTextToWidthOrHash( aCell, aAreaParam.mnColWidth - nTotalMargin );
+
+ if ( bHasHashText )
+ {
+ tools::Long nMarkPixel = SC_CLIPMARK_SIZE * mnPPTX;
+
+ if ( eOutHorJust == SvxCellHorJustify::Left )
+ {
+ if ( nCellY == nY && nCellX >= nX1 && nCellX <= nX2 )
+ pRowInfo[nArrY].cellInfo(nCellX).nClipMark |= ScClipMark::Right;
+ bAnyClipped = true;
+ aAreaParam.maClipRect.AdjustRight( -(nMarkPixel * nLayoutSign) );
+ }
+ else if ( eOutHorJust == SvxCellHorJustify::Right )
+ {
+ if ( nCellY == nY && nCellX >= nX1 && nCellX <= nX2 )
+ pRowInfo[nArrY].cellInfo(nCellX).nClipMark |= ScClipMark::Left;
+ bAnyClipped = true;
+ aAreaParam.maClipRect.AdjustLeft(nMarkPixel * nLayoutSign);
+ }
+ else
+ {
+ if ( nCellY == nY && nCellX >= nX1 && nCellX <= nX2 )
+ {
+ pRowInfo[nArrY].cellInfo(nCellX).nClipMark |= ScClipMark::Right;
+ pRowInfo[nArrY].cellInfo(nCellX).nClipMark |= ScClipMark::Left;
+ }
+ bAnyClipped = true;
+ aAreaParam.maClipRect.AdjustRight( -(nMarkPixel * nLayoutSign) );
+ aAreaParam.maClipRect.AdjustLeft(nMarkPixel * nLayoutSign);
+ }
+ }
+
+ nNeededWidth = aVars.GetTextSize().Width() +
+ static_cast<tools::Long>( aVars.GetLeftTotal() * mnPPTX ) +
+ static_cast<tools::Long>( aVars.GetMargin()->GetRightMargin() * mnPPTX );
+ if ( nNeededWidth <= aAreaParam.maClipRect.GetWidth() )
+ {
+ // Cell value is no longer clipped. Reset relevant parameters.
+ aAreaParam.mbLeftClip = aAreaParam.mbRightClip = false;
+ aAreaParam.mnLeftClipLength = aAreaParam.mnRightClipLength = 0;
+ }
+ }
+
+ tools::Long nJustPosX = aAreaParam.maAlignRect.Left(); // "justified" - effect of alignment will be added
+ tools::Long nJustPosY = aAreaParam.maAlignRect.Top();
+ tools::Long nAvailWidth = aAreaParam.maAlignRect.GetWidth();
+ tools::Long nOutHeight = aAreaParam.maAlignRect.GetHeight();
+
+ bool bOutside = ( aAreaParam.maClipRect.Right() < nScrX || aAreaParam.maClipRect.Left() >= nScrX + nScrW );
+ // Take adjusted values of aAreaParam.mbLeftClip and aAreaParam.mbRightClip
+ bool bVClip = AdjustAreaParamClipRect(aAreaParam);
+ bool bHClip = aAreaParam.mbLeftClip || aAreaParam.mbRightClip;
+
+ // check horizontal space
+
+ if ( !bOutside )
+ {
+ bool bRightAdjusted = false; // to correct text width calculation later
+ switch (eOutHorJust)
+ {
+ case SvxCellHorJustify::Left:
+ nJustPosX += static_cast<tools::Long>( aVars.GetLeftTotal() * mnPPTX );
+ break;
+ case SvxCellHorJustify::Right:
+ nJustPosX += nAvailWidth - aVars.GetTextSize().Width() -
+ static_cast<tools::Long>( aVars.GetRightTotal() * mnPPTX );
+ bRightAdjusted = true;
+ break;
+ case SvxCellHorJustify::Center:
+ nJustPosX += ( nAvailWidth - aVars.GetTextSize().Width() +
+ static_cast<tools::Long>( aVars.GetLeftTotal() * mnPPTX ) -
+ static_cast<tools::Long>( aVars.GetMargin()->GetRightMargin() * mnPPTX ) ) / 2;
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+
+ tools::Long nTestClipHeight = aVars.GetTextSize().Height();
+ switch (aVars.GetVerJust())
+ {
+ case SvxCellVerJustify::Top:
+ case SvxCellVerJustify::Block:
+ {
+ tools::Long nTop = static_cast<tools::Long>( aVars.GetMargin()->GetTopMargin() * mnPPTY );
+ nJustPosY += nTop;
+ nTestClipHeight += nTop;
+ }
+ break;
+ case SvxCellVerJustify::Bottom:
+ {
+ tools::Long nBot = static_cast<tools::Long>( aVars.GetMargin()->GetBottomMargin() * mnPPTY );
+ nJustPosY += nOutHeight - aVars.GetTextSize().Height() - nBot;
+ nTestClipHeight += nBot;
+ }
+ break;
+ case SvxCellVerJustify::Center:
+ {
+ tools::Long nTop = static_cast<tools::Long>( aVars.GetMargin()->GetTopMargin() * mnPPTY );
+ tools::Long nBot = static_cast<tools::Long>( aVars.GetMargin()->GetBottomMargin() * mnPPTY );
+ nJustPosY += ( nOutHeight + nTop -
+ aVars.GetTextSize().Height() - nBot ) / 2;
+ nTestClipHeight += std::abs( nTop - nBot );
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+
+ if ( nTestClipHeight > nOutHeight )
+ {
+ // no vertical clipping when printing cells with optimal height,
+ // except when font size is from conditional formatting.
+ if ( eType != OUTTYPE_PRINTER ||
+ ( mpDoc->GetRowFlags( nCellY, nTab ) & CRFlags::ManualSize ) ||
+ ( aVars.HasCondHeight() ) )
+ bVClip = true;
+ }
+
+ if ( bHClip || bVClip )
+ {
+ // only clip the affected dimension so that not all right-aligned
+ // columns are cut off when performing a non-proportional resize
+ if (!bHClip)
+ {
+ aAreaParam.maClipRect.SetLeft( nScrX );
+ aAreaParam.maClipRect.SetRight( nScrX+nScrW );
+ }
+ if (!bVClip)
+ {
+ aAreaParam.maClipRect.SetTop( nScrY );
+ aAreaParam.maClipRect.SetBottom( nScrY+nScrH );
+ }
+
+ // aClipRect is not used after SetClipRegion/IntersectClipRegion,
+ // so it can be modified here
+ if (bPixelToLogic)
+ aAreaParam.maClipRect = mpRefDevice->PixelToLogic( aAreaParam.maClipRect );
+
+ if (bMetaFile)
+ {
+ mpDev->Push();
+ mpDev->IntersectClipRegion( aAreaParam.maClipRect );
+ }
+ else
+ mpDev->SetClipRegion( vcl::Region( aAreaParam.maClipRect ) );
+ }
+
+ Point aURLStart( nJustPosX, nJustPosY ); // copy before modifying for orientation
+
+ switch (aVars.GetOrient())
+ {
+ case SvxCellOrientation::Standard:
+ nJustPosY += aVars.GetAscent();
+ break;
+ case SvxCellOrientation::TopBottom:
+ nJustPosX += aVars.GetTextSize().Width() - aVars.GetAscent();
+ break;
+ case SvxCellOrientation::BottomUp:
+ nJustPosY += aVars.GetTextSize().Height();
+ nJustPosX += aVars.GetAscent();
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+
+ // When clipping, the visible part is now completely defined by the alignment,
+ // there's no more special handling to show the right part of RTL text.
+
+ Point aDrawTextPos( nJustPosX, nJustPosY );
+ if ( bPixelToLogic )
+ {
+ // undo text width adjustment in pixels
+ if (bRightAdjusted)
+ aDrawTextPos.AdjustX(aVars.GetTextSize().Width() );
+
+ aDrawTextPos = mpRefDevice->PixelToLogic( aDrawTextPos );
+
+ // redo text width adjustment in logic units
+ if (bRightAdjusted)
+ aDrawTextPos.AdjustX( -(aVars.GetOriginalWidth()) );
+ }
+
+ // in Metafiles always use DrawTextArray to ensure that positions are
+ // recorded (for non-proportional resize):
+
+ const OUString& aString = aVars.GetString();
+ if (!aString.isEmpty())
+ {
+ // If the string is clipped, make it shorter for
+ // better performance since drawing by HarfBuzz is
+ // quite expensive especially for long string.
+
+ OUString aShort = aString;
+
+ // But never fiddle with numeric values.
+ // (Which was the cause of tdf#86024).
+ // The General automatic format output takes
+ // care of this, or fixed width numbers either fit
+ // or display as ###.
+ if (!bCellIsValue)
+ {
+ double fVisibleRatio = 1.0;
+ double fTextWidth = aVars.GetTextSize().Width();
+ sal_Int32 nTextLen = aString.getLength();
+ if (eOutHorJust == SvxCellHorJustify::Left && aAreaParam.mnRightClipLength > 0)
+ {
+ fVisibleRatio = (fTextWidth - aAreaParam.mnRightClipLength) / fTextWidth;
+ if (0.0 < fVisibleRatio && fVisibleRatio < 1.0)
+ {
+ // Only show the left-end segment.
+ sal_Int32 nShortLen = fVisibleRatio*nTextLen + 1;
+ aShort = aShort.copy(0, nShortLen);
+ }
+ }
+ else if (eOutHorJust == SvxCellHorJustify::Right && aAreaParam.mnLeftClipLength > 0)
+ {
+ fVisibleRatio = (fTextWidth - aAreaParam.mnLeftClipLength) / fTextWidth;
+ if (0.0 < fVisibleRatio && fVisibleRatio < 1.0)
+ {
+ // Only show the right-end segment.
+ sal_Int32 nShortLen = fVisibleRatio*nTextLen + 1;
+ aShort = aShort.copy(nTextLen-nShortLen);
+
+ // Adjust the text position after shortening of the string.
+ double fShortWidth = aVars.GetFmtTextWidth(aShort);
+ double fOffset = fTextWidth - fShortWidth;
+ aDrawTextPos.Move(fOffset, 0);
+ }
+ }
+ }
+
+ if (bMetaFile || pFmtDevice != mpDev || aZoomX != aZoomY)
+ {
+ size_t nLen = aShort.getLength();
+ if (aDX.size() < nLen)
+ aDX.resize(nLen, 0);
+
+ pFmtDevice->GetTextArray(aShort, &aDX);
+
+ if ( !mpRefDevice->GetConnectMetaFile() ||
+ mpRefDevice->GetOutDevType() == OUTDEV_PRINTER )
+ {
+ double fMul = GetStretch();
+ for (size_t i = 0; i < nLen; ++i)
+ aDX.set(i, static_cast<sal_Int32>(aDX[i] / fMul + 0.5));
+ }
+
+ mpDev->DrawTextArray(aDrawTextPos, aShort, aDX, {}, 0, nLen);
+ }
+ else
+ {
+ mpDev->DrawText(aDrawTextPos, aShort, 0, -1, nullptr, nullptr,
+ aVars.GetLayoutGlyphs(aShort));
+ }
+ }
+
+ if ( bHClip || bVClip )
+ {
+ if (bMetaFile)
+ mpDev->Pop();
+ else
+ mpDev->SetClipRegion();
+ }
+
+ // PDF: whole-cell hyperlink from formula?
+ bool bHasURL = pPDFData && aCell.getType() == CELLTYPE_FORMULA && aCell.getFormula()->IsHyperLinkCell();
+ if (bHasURL)
+ {
+ tools::Rectangle aURLRect( aURLStart, aVars.GetTextSize() );
+ lcl_DoHyperlinkResult(mpDev, aURLRect, aCell);
+ }
+ }
+ }
+ nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
+ }
+ }
+ nPosY += pRowInfo[nArrY].nHeight;
+ }
+ if ( bProgress )
+ ScProgress::DeleteInterpretProgress();
+}
+
+void ScOutputData::SetRefDevice( OutputDevice* pRDev )
+{
+ mpRefDevice = pFmtDevice = pRDev;
+ // reset EditEngine because it depends on pFmtDevice and mpRefDevice
+ mxOutputEditEngine.reset();
+}
+
+void ScOutputData::SetFmtDevice( OutputDevice* pRDev )
+{
+ pFmtDevice = pRDev;
+ // reset EditEngine because it depends on pFmtDevice
+ mxOutputEditEngine.reset();
+}
+
+void ScOutputData::SetUseStyleColor( bool bSet )
+{
+ mbUseStyleColor = bSet;
+ // reset EditEngine because it depends on mbUseStyleColor
+ mxOutputEditEngine.reset();
+}
+
+void ScOutputData::InitOutputEditEngine()
+{
+ if (!mxOutputEditEngine)
+ {
+ mxOutputEditEngine = std::make_unique<ScFieldEditEngine>(mpDoc, mpDoc->GetEnginePool());
+ mxOutputEditEngine->SetUpdateLayout( false );
+ mxOutputEditEngine->EnableUndo( false ); // don't need undo for painting purposes
+ // a RefDevice always has to be set, otherwise EditEngine would create a VirtualDevice
+ mxOutputEditEngine->SetRefDevice( pFmtDevice );
+ EEControlBits nCtrl = mxOutputEditEngine->GetControlWord();
+ if ( bShowSpellErrors )
+ nCtrl |= EEControlBits::ONLINESPELLING;
+ if ( eType == OUTTYPE_PRINTER )
+ nCtrl &= ~EEControlBits::MARKFIELDS;
+ else
+ nCtrl &= ~EEControlBits::MARKURLFIELDS; // URLs not shaded for output
+ if ( eType == OUTTYPE_WINDOW && mpRefDevice == pFmtDevice )
+ nCtrl &= ~EEControlBits::FORMAT100; // use the actual MapMode
+ mxOutputEditEngine->SetControlWord( nCtrl );
+ mxOutputEditEngine->EnableAutoColor( mbUseStyleColor );
+ }
+ else
+ {
+ // just in case someone turned it on during the last paint cycle
+ mxOutputEditEngine->SetUpdateLayout( false );
+ }
+ // we don't track changes to these settings, so we have to apply them every time
+ mpDoc->ApplyAsianEditSettings( *mxOutputEditEngine );
+ mxOutputEditEngine->SetDefaultHorizontalTextDirection( mpDoc->GetEditTextDirection( nTab ) );
+}
+
+static void lcl_ClearEdit( EditEngine& rEngine ) // text and attributes
+{
+ rEngine.SetUpdateLayout( false );
+
+ rEngine.SetText(OUString());
+ // do not keep any para-attributes
+ const SfxItemSet& rPara = rEngine.GetParaAttribs(0);
+ if (rPara.Count())
+ rEngine.SetParaAttribs( 0,
+ SfxItemSet( *rPara.GetPool(), rPara.GetRanges() ) );
+ rEngine.EnableSkipOutsideFormat(false);
+}
+
+static bool lcl_SafeIsValue( ScRefCellValue& rCell )
+{
+ switch (rCell.getType())
+ {
+ case CELLTYPE_VALUE:
+ return true;
+ case CELLTYPE_FORMULA:
+ {
+ ScFormulaCell* pFCell = rCell.getFormula();
+ if (pFCell->IsRunning() || pFCell->IsValue())
+ return true;
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ return false;
+}
+
+static void lcl_ScaleFonts( EditEngine& rEngine, tools::Long nPercent )
+{
+ bool bUpdateMode = rEngine.SetUpdateLayout( false );
+
+ sal_Int32 nParCount = rEngine.GetParagraphCount();
+ for (sal_Int32 nPar=0; nPar<nParCount; nPar++)
+ {
+ std::vector<sal_Int32> aPortions;
+ rEngine.GetPortions( nPar, aPortions );
+
+ sal_Int32 nStart = 0;
+ for ( const sal_Int32 nEnd : aPortions )
+ {
+ ESelection aSel( nPar, nStart, nPar, nEnd );
+ SfxItemSet aAttribs = rEngine.GetAttribs( aSel );
+
+ tools::Long nWestern = aAttribs.Get(EE_CHAR_FONTHEIGHT).GetHeight();
+ tools::Long nCJK = aAttribs.Get(EE_CHAR_FONTHEIGHT_CJK).GetHeight();
+ tools::Long nCTL = aAttribs.Get(EE_CHAR_FONTHEIGHT_CTL).GetHeight();
+
+ nWestern = ( nWestern * nPercent ) / 100;
+ nCJK = ( nCJK * nPercent ) / 100;
+ nCTL = ( nCTL * nPercent ) / 100;
+
+ aAttribs.Put( SvxFontHeightItem( nWestern, 100, EE_CHAR_FONTHEIGHT ) );
+ aAttribs.Put( SvxFontHeightItem( nCJK, 100, EE_CHAR_FONTHEIGHT_CJK ) );
+ aAttribs.Put( SvxFontHeightItem( nCTL, 100, EE_CHAR_FONTHEIGHT_CTL ) );
+
+ rEngine.QuickSetAttribs( aAttribs, aSel ); //! remove paragraph attributes from aAttribs?
+
+ nStart = nEnd;
+ }
+ }
+
+ if ( bUpdateMode )
+ rEngine.SetUpdateLayout( true );
+}
+
+static tools::Long lcl_GetEditSize( EditEngine& rEngine, bool bWidth, bool bSwap, Degree100 nAttrRotate )
+{
+ if ( bSwap )
+ bWidth = !bWidth;
+
+ if ( nAttrRotate )
+ {
+ tools::Long nRealWidth = static_cast<tools::Long>(rEngine.CalcTextWidth());
+ tools::Long nRealHeight = rEngine.GetTextHeight();
+
+ // assuming standard mode, otherwise width isn't used
+
+ double nRealOrient = toRadians(nAttrRotate); // 1/100th degrees
+ double nAbsCos = fabs( cos( nRealOrient ) );
+ double nAbsSin = fabs( sin( nRealOrient ) );
+ if ( bWidth )
+ return static_cast<tools::Long>( nRealWidth * nAbsCos + nRealHeight * nAbsSin );
+ else
+ return static_cast<tools::Long>( nRealHeight * nAbsCos + nRealWidth * nAbsSin );
+ }
+ else if ( bWidth )
+ return static_cast<tools::Long>(rEngine.CalcTextWidth());
+ else
+ return rEngine.GetTextHeight();
+}
+
+void ScOutputData::ShrinkEditEngine( EditEngine& rEngine, const tools::Rectangle& rAlignRect,
+ tools::Long nLeftM, tools::Long nTopM, tools::Long nRightM, tools::Long nBottomM,
+ bool bWidth, SvxCellOrientation nOrient, Degree100 nAttrRotate, bool bPixelToLogic,
+ tools::Long& rEngineWidth, tools::Long& rEngineHeight, tools::Long& rNeededPixel, bool& rLeftClip, bool& rRightClip )
+{
+ if ( !bWidth )
+ {
+ // vertical
+
+ tools::Long nScaleSize = bPixelToLogic ?
+ mpRefDevice->LogicToPixel(Size(0,rEngineHeight)).Height() : rEngineHeight;
+
+ // Don't scale if it fits already.
+ // Allowing to extend into the margin, to avoid scaling at optimal height.
+ if ( nScaleSize <= rAlignRect.GetHeight() )
+ return;
+
+ bool bSwap = ( nOrient == SvxCellOrientation::TopBottom || nOrient == SvxCellOrientation::BottomUp );
+ tools::Long nAvailable = rAlignRect.GetHeight() - nTopM - nBottomM;
+ tools::Long nScale = ( nAvailable * 100 ) / nScaleSize;
+
+ lcl_ScaleFonts( rEngine, nScale );
+ rEngineHeight = lcl_GetEditSize( rEngine, false, bSwap, nAttrRotate );
+ tools::Long nNewSize = bPixelToLogic ?
+ mpRefDevice->LogicToPixel(Size(0,rEngineHeight)).Height() : rEngineHeight;
+
+ sal_uInt16 nShrinkAgain = 0;
+ while ( nNewSize > nAvailable && nShrinkAgain < SC_SHRINKAGAIN_MAX )
+ {
+ // further reduce, like in DrawStrings
+ lcl_ScaleFonts( rEngine, 90 ); // reduce by 10%
+ rEngineHeight = lcl_GetEditSize( rEngine, false, bSwap, nAttrRotate );
+ nNewSize = bPixelToLogic ?
+ mpRefDevice->LogicToPixel(Size(0,rEngineHeight)).Height() : rEngineHeight;
+ ++nShrinkAgain;
+ }
+
+ // sizes for further processing (alignment etc):
+ rEngineWidth = lcl_GetEditSize( rEngine, true, bSwap, nAttrRotate );
+ tools::Long nPixelWidth = bPixelToLogic ?
+ mpRefDevice->LogicToPixel(Size(rEngineWidth,0)).Width() : rEngineWidth;
+ rNeededPixel = nPixelWidth + nLeftM + nRightM;
+ }
+ else if ( rLeftClip || rRightClip )
+ {
+ // horizontal
+
+ tools::Long nAvailable = rAlignRect.GetWidth() - nLeftM - nRightM;
+ tools::Long nScaleSize = rNeededPixel - nLeftM - nRightM; // without margin
+
+ if ( nScaleSize <= nAvailable )
+ return;
+
+ tools::Long nScale = ( nAvailable * 100 ) / nScaleSize;
+
+ lcl_ScaleFonts( rEngine, nScale );
+ rEngineWidth = lcl_GetEditSize( rEngine, true, false, nAttrRotate );
+ tools::Long nNewSize = bPixelToLogic ?
+ mpRefDevice->LogicToPixel(Size(rEngineWidth,0)).Width() : rEngineWidth;
+
+ sal_uInt16 nShrinkAgain = 0;
+ while ( nNewSize > nAvailable && nShrinkAgain < SC_SHRINKAGAIN_MAX )
+ {
+ // further reduce, like in DrawStrings
+ lcl_ScaleFonts( rEngine, 90 ); // reduce by 10%
+ rEngineWidth = lcl_GetEditSize( rEngine, true, false, nAttrRotate );
+ nNewSize = bPixelToLogic ?
+ mpRefDevice->LogicToPixel(Size(rEngineWidth,0)).Width() : rEngineWidth;
+ ++nShrinkAgain;
+ }
+ if ( nNewSize <= nAvailable )
+ rLeftClip = rRightClip = false;
+
+ // sizes for further processing (alignment etc):
+ rNeededPixel = nNewSize + nLeftM + nRightM;
+ rEngineHeight = lcl_GetEditSize( rEngine, false, false, nAttrRotate );
+ }
+}
+
+ScOutputData::DrawEditParam::DrawEditParam(const ScPatternAttr* pPattern, const SfxItemSet* pCondSet, bool bCellIsValue) :
+ meHorJustAttr( lcl_GetValue<SvxHorJustifyItem, SvxCellHorJustify>(*pPattern, ATTR_HOR_JUSTIFY, pCondSet) ),
+ meHorJustContext( meHorJustAttr ),
+ meHorJustResult( meHorJustAttr ),
+ meVerJust( lcl_GetValue<SvxVerJustifyItem, SvxCellVerJustify>(*pPattern, ATTR_VER_JUSTIFY, pCondSet) ),
+ meHorJustMethod( lcl_GetValue<SvxJustifyMethodItem, SvxCellJustifyMethod>(*pPattern, ATTR_HOR_JUSTIFY_METHOD, pCondSet) ),
+ meVerJustMethod( lcl_GetValue<SvxJustifyMethodItem, SvxCellJustifyMethod>(*pPattern, ATTR_VER_JUSTIFY_METHOD, pCondSet) ),
+ meOrient( pPattern->GetCellOrientation(pCondSet) ),
+ mnArrY(0),
+ mnX(0), mnCellX(0), mnCellY(0),
+ mnPosX(0), mnPosY(0), mnInitPosX(0),
+ mbBreak( (meHorJustAttr == SvxCellHorJustify::Block) || lcl_GetBoolValue(*pPattern, ATTR_LINEBREAK, pCondSet) ),
+ mbCellIsValue(bCellIsValue),
+ mbAsianVertical(false),
+ mbPixelToLogic(false),
+ mbHyphenatorSet(false),
+ mpEngine(nullptr),
+ mpPattern(pPattern),
+ mpCondSet(pCondSet),
+ mpPreviewFontSet(nullptr),
+ mpOldPattern(nullptr),
+ mpOldCondSet(nullptr),
+ mpOldPreviewFontSet(nullptr),
+ mpThisRowInfo(nullptr),
+ mpMisspellRanges(nullptr)
+{}
+
+bool ScOutputData::DrawEditParam::readCellContent(
+ const ScDocument* pDoc, bool bShowNullValues, bool bShowFormulas, bool bSyntaxMode, bool bUseStyleColor, bool bForceAutoColor, bool& rWrapFields)
+{
+ if (maCell.getType() == CELLTYPE_EDIT)
+ {
+ const EditTextObject* pData = maCell.getEditText();
+ if (pData)
+ {
+ mpEngine->SetTextCurrentDefaults(*pData);
+
+ if ( mbBreak && !mbAsianVertical && pData->HasField() )
+ {
+ // Fields aren't wrapped, so clipping is enabled to prevent
+ // a field from being drawn beyond the cell size
+
+ rWrapFields = true;
+ }
+ }
+ else
+ {
+ OSL_FAIL("pData == 0");
+ return false;
+ }
+ }
+ else
+ {
+ sal_uInt32 nFormat = mpPattern->GetNumberFormat(
+ pDoc->GetFormatTable(), mpCondSet );
+ const Color* pColor;
+ OUString aString = ScCellFormat::GetString( maCell,
+ nFormat, &pColor,
+ *pDoc->GetFormatTable(),
+ *pDoc,
+ bShowNullValues,
+ bShowFormulas);
+
+ mpEngine->SetTextCurrentDefaults(aString);
+ if ( pColor && !bSyntaxMode && !( bUseStyleColor && bForceAutoColor ) )
+ lcl_SetEditColor( *mpEngine, *pColor );
+ }
+
+ if (mpMisspellRanges)
+ mpEngine->SetAllMisspellRanges(*mpMisspellRanges);
+
+ return true;
+}
+
+void ScOutputData::DrawEditParam::setPatternToEngine(bool bUseStyleColor)
+{
+ // syntax highlighting mode is ignored here
+ // StringDiffer doesn't look at hyphenate, language items
+
+ if (SfxPoolItem::areSame(mpPattern, mpOldPattern) && mpCondSet == mpOldCondSet && mpPreviewFontSet == mpOldPreviewFontSet )
+ return;
+
+ Color nConfBackColor = SC_MOD()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor;
+ bool bCellContrast = bUseStyleColor &&
+ Application::GetSettings().GetStyleSettings().GetHighContrastMode();
+
+ auto pSet = std::make_unique<SfxItemSet>( mpEngine->GetEmptyItemSet() );
+ mpPattern->FillEditItemSet( pSet.get(), mpCondSet );
+ if ( mpPreviewFontSet )
+ {
+ if ( const SvxFontItem* pItem = mpPreviewFontSet->GetItemIfSet( ATTR_FONT ) )
+ {
+ // tdf#125054 adapt WhichID
+ pSet->Put(*pItem, EE_CHAR_FONTINFO);
+ }
+ if ( const SvxFontItem* pItem = mpPreviewFontSet->GetItemIfSet( ATTR_CJK_FONT ) )
+ {
+ // tdf#125054 adapt WhichID
+ pSet->Put(*pItem, EE_CHAR_FONTINFO_CJK);
+ }
+ if ( const SvxFontItem* pItem = mpPreviewFontSet->GetItemIfSet( ATTR_CTL_FONT ) )
+ {
+ // tdf#125054 adapt WhichID
+ pSet->Put(*pItem, EE_CHAR_FONTINFO_CTL);
+ }
+ }
+ bool bParaHyphenate = pSet->Get(EE_PARA_HYPHENATE).GetValue();
+ mpEngine->SetDefaults( std::move(pSet) );
+ mpOldPattern = mpPattern;
+ mpOldCondSet = mpCondSet;
+ mpOldPreviewFontSet = mpPreviewFontSet;
+
+ EEControlBits nControl = mpEngine->GetControlWord();
+ if (meOrient == SvxCellOrientation::Stacked)
+ nControl |= EEControlBits::ONECHARPERLINE;
+ else
+ nControl &= ~EEControlBits::ONECHARPERLINE;
+ mpEngine->SetControlWord( nControl );
+
+ if ( !mbHyphenatorSet && bParaHyphenate )
+ {
+ // set hyphenator the first time it is needed
+ css::uno::Reference<css::linguistic2::XHyphenator> xXHyphenator( LinguMgr::GetHyphenator() );
+ mpEngine->SetHyphenator( xXHyphenator );
+ mbHyphenatorSet = true;
+ }
+
+ Color aBackCol = mpPattern->GetItem( ATTR_BACKGROUND, mpCondSet ).GetColor();
+ if ( bUseStyleColor && ( aBackCol.IsTransparent() || bCellContrast ) )
+ aBackCol = nConfBackColor;
+ mpEngine->SetBackgroundColor( aBackCol );
+}
+
+void ScOutputData::DrawEditParam::calcMargins(tools::Long& rTopM, tools::Long& rLeftM, tools::Long& rBottomM, tools::Long& rRightM, double nPPTX, double nPPTY) const
+{
+ const SvxMarginItem& rMargin = mpPattern->GetItem(ATTR_MARGIN, mpCondSet);
+
+ sal_uInt16 nIndent = 0;
+ if (meHorJustAttr == SvxCellHorJustify::Left || meHorJustAttr == SvxCellHorJustify::Right)
+ nIndent = lcl_GetValue<ScIndentItem, sal_uInt16>(*mpPattern, ATTR_INDENT, mpCondSet);
+
+ rLeftM = static_cast<tools::Long>(((rMargin.GetLeftMargin() + nIndent) * nPPTX));
+ rTopM = static_cast<tools::Long>((rMargin.GetTopMargin() * nPPTY));
+ rRightM = static_cast<tools::Long>((rMargin.GetRightMargin() * nPPTX));
+ rBottomM = static_cast<tools::Long>((rMargin.GetBottomMargin() * nPPTY));
+ if(meHorJustAttr == SvxCellHorJustify::Right)
+ {
+ rLeftM = static_cast<tools::Long>((rMargin.GetLeftMargin() * nPPTX));
+ rRightM = static_cast<tools::Long>(((rMargin.GetRightMargin() + nIndent) * nPPTX));
+ }
+}
+
+void ScOutputData::DrawEditParam::calcPaperSize(
+ Size& rPaperSize, const tools::Rectangle& rAlignRect, double nPPTX, double nPPTY) const
+{
+ tools::Long nTopM, nLeftM, nBottomM, nRightM;
+ calcMargins(nTopM, nLeftM, nBottomM, nRightM, nPPTX, nPPTY);
+
+ if (isVerticallyOriented())
+ {
+ rPaperSize.setWidth( rAlignRect.GetHeight() - nTopM - nBottomM );
+ rPaperSize.setHeight( rAlignRect.GetWidth() - nLeftM - nRightM );
+ }
+ else
+ {
+ rPaperSize.setWidth( rAlignRect.GetWidth() - nLeftM - nRightM );
+ rPaperSize.setHeight( rAlignRect.GetHeight() - nTopM - nBottomM );
+ }
+
+ if (mbAsianVertical)
+ {
+ rPaperSize.setHeight( rAlignRect.GetHeight() - nTopM - nBottomM );
+ // Subtract some extra value from the height or else the text would go
+ // outside the cell area. The value of 5 is arbitrary, and is based
+ // entirely on heuristics.
+ rPaperSize.AdjustHeight( -5 );
+ }
+}
+
+void ScOutputData::DrawEditParam::getEngineSize(ScFieldEditEngine* pEngine, tools::Long& rWidth, tools::Long& rHeight) const
+{
+ tools::Long nEngineWidth = 0;
+ if (!mbBreak || meOrient == SvxCellOrientation::Stacked || mbAsianVertical)
+ nEngineWidth = static_cast<tools::Long>(pEngine->CalcTextWidth());
+
+ tools::Long nEngineHeight = pEngine->GetTextHeight();
+
+ if (isVerticallyOriented())
+ std::swap( nEngineWidth, nEngineHeight );
+
+ if (meOrient == SvxCellOrientation::Stacked)
+ nEngineWidth = nEngineWidth * 11 / 10;
+
+ rWidth = nEngineWidth;
+ rHeight = nEngineHeight;
+}
+
+bool ScOutputData::DrawEditParam::hasLineBreak() const
+{
+ return (mbBreak || (meOrient == SvxCellOrientation::Stacked) || mbAsianVertical);
+}
+
+bool ScOutputData::DrawEditParam::isHyperlinkCell() const
+{
+ if (maCell.getType() != CELLTYPE_FORMULA)
+ return false;
+
+ return maCell.getFormula()->IsHyperLinkCell();
+}
+
+bool ScOutputData::DrawEditParam::isVerticallyOriented() const
+{
+ return (meOrient == SvxCellOrientation::TopBottom || meOrient == SvxCellOrientation::BottomUp);
+}
+
+void ScOutputData::DrawEditParam::calcStartPosForVertical(
+ Point& rLogicStart, tools::Long nCellWidth, tools::Long nEngineWidth, tools::Long nTopM, const OutputDevice* pRefDevice)
+{
+ OSL_ENSURE(isVerticallyOriented(), "Use this only for vertically oriented cell!");
+
+ if (mbPixelToLogic)
+ rLogicStart = pRefDevice->PixelToLogic(rLogicStart);
+
+ if (!mbBreak)
+ return;
+
+ // vertical adjustment is within the EditEngine
+ if (mbPixelToLogic)
+ rLogicStart.AdjustY(pRefDevice->PixelToLogic(Size(0,nTopM)).Height() );
+ else
+ rLogicStart.AdjustY(nTopM );
+
+ switch (meHorJustResult)
+ {
+ case SvxCellHorJustify::Center:
+ rLogicStart.AdjustX((nCellWidth - nEngineWidth) / 2 );
+ break;
+ case SvxCellHorJustify::Right:
+ rLogicStart.AdjustX(nCellWidth - nEngineWidth );
+ break;
+ default:
+ ; // do nothing
+ }
+}
+
+void ScOutputData::DrawEditParam::setAlignmentToEngine()
+{
+ if (isVerticallyOriented() || mbAsianVertical)
+ {
+ SvxAdjust eSvxAdjust = SvxAdjust::Left;
+ switch (meVerJust)
+ {
+ case SvxCellVerJustify::Top:
+ eSvxAdjust = (meOrient == SvxCellOrientation::TopBottom || mbAsianVertical) ?
+ SvxAdjust::Left : SvxAdjust::Right;
+ break;
+ case SvxCellVerJustify::Center:
+ eSvxAdjust = SvxAdjust::Center;
+ break;
+ case SvxCellVerJustify::Bottom:
+ case SvxCellVerJustify::Standard:
+ eSvxAdjust = (meOrient == SvxCellOrientation::TopBottom || mbAsianVertical) ?
+ SvxAdjust::Right : SvxAdjust::Left;
+ break;
+ case SvxCellVerJustify::Block:
+ eSvxAdjust = SvxAdjust::Block;
+ break;
+ }
+
+ mpEngine->SetDefaultItem( SvxAdjustItem(eSvxAdjust, EE_PARA_JUST) );
+ mpEngine->SetDefaultItem( SvxJustifyMethodItem(meVerJustMethod, EE_PARA_JUST_METHOD) );
+
+ if (meHorJustResult == SvxCellHorJustify::Block)
+ mpEngine->SetDefaultItem( SvxVerJustifyItem(SvxCellVerJustify::Block, EE_PARA_VER_JUST) );
+ }
+ else
+ {
+ // horizontal alignment now may depend on cell content
+ // (for values with number formats with mixed script types)
+ // -> always set adjustment
+
+ SvxAdjust eSvxAdjust = SvxAdjust::Left;
+ if (meOrient == SvxCellOrientation::Stacked)
+ eSvxAdjust = SvxAdjust::Center;
+ else if (mbBreak)
+ {
+ if (meOrient == SvxCellOrientation::Standard)
+ switch (meHorJustResult)
+ {
+ case SvxCellHorJustify::Repeat: // repeat is not yet implemented
+ case SvxCellHorJustify::Standard:
+ SAL_WARN("sc.ui","meHorJustResult does not match getAlignmentFromContext()");
+ [[fallthrough]];
+ case SvxCellHorJustify::Left:
+ eSvxAdjust = SvxAdjust::Left;
+ break;
+ case SvxCellHorJustify::Center:
+ eSvxAdjust = SvxAdjust::Center;
+ break;
+ case SvxCellHorJustify::Right:
+ eSvxAdjust = SvxAdjust::Right;
+ break;
+ case SvxCellHorJustify::Block:
+ eSvxAdjust = SvxAdjust::Block;
+ break;
+ }
+ else
+ switch (meVerJust)
+ {
+ case SvxCellVerJustify::Top:
+ eSvxAdjust = SvxAdjust::Right;
+ break;
+ case SvxCellVerJustify::Center:
+ eSvxAdjust = SvxAdjust::Center;
+ break;
+ case SvxCellVerJustify::Bottom:
+ case SvxCellVerJustify::Standard:
+ eSvxAdjust = SvxAdjust::Left;
+ break;
+ case SvxCellVerJustify::Block:
+ eSvxAdjust = SvxAdjust::Block;
+ break;
+ }
+ }
+
+ mpEngine->SetDefaultItem( SvxAdjustItem(eSvxAdjust, EE_PARA_JUST) );
+
+ if (mbAsianVertical)
+ {
+ mpEngine->SetDefaultItem( SvxJustifyMethodItem(meVerJustMethod, EE_PARA_JUST_METHOD) );
+ if (meHorJustResult == SvxCellHorJustify::Block)
+ mpEngine->SetDefaultItem( SvxVerJustifyItem(SvxCellVerJustify::Block, EE_PARA_VER_JUST) );
+ }
+ else
+ {
+ mpEngine->SetDefaultItem( SvxJustifyMethodItem(meHorJustMethod, EE_PARA_JUST_METHOD) );
+ if (meVerJust == SvxCellVerJustify::Block)
+ mpEngine->SetDefaultItem( SvxVerJustifyItem(SvxCellVerJustify::Block, EE_PARA_VER_JUST) );
+ }
+ }
+
+ mpEngine->SetVertical(mbAsianVertical);
+ if (maCell.getType() == CELLTYPE_EDIT)
+ {
+ // We need to synchronize the vertical mode in the EditTextObject
+ // instance too. No idea why we keep this state in two separate
+ // instances.
+ const EditTextObject* pData = maCell.getEditText();
+ if (pData)
+ const_cast<EditTextObject*>(pData)->SetVertical(mbAsianVertical);
+ }
+}
+
+bool ScOutputData::DrawEditParam::adjustHorAlignment(ScFieldEditEngine* pEngine)
+{
+ if (meHorJustResult == SvxCellHorJustify::Right || meHorJustResult == SvxCellHorJustify::Center)
+ {
+ SvxAdjust eEditAdjust = (meHorJustResult == SvxCellHorJustify::Center) ?
+ SvxAdjust::Center : SvxAdjust::Right;
+
+ const bool bPrevUpdateLayout = pEngine->SetUpdateLayout(false);
+ pEngine->SetDefaultItem( SvxAdjustItem(eEditAdjust, EE_PARA_JUST) );
+ pEngine->SetUpdateLayout(bPrevUpdateLayout);
+ return true;
+ }
+ return false;
+}
+
+void ScOutputData::DrawEditParam::adjustForHyperlinkInPDF(Point aURLStart, const OutputDevice* pDev)
+{
+ // PDF: whole-cell hyperlink from formula?
+ vcl::PDFExtOutDevData* pPDFData = dynamic_cast<vcl::PDFExtOutDevData* >( pDev->GetExtOutDevData() );
+ bool bHasURL = pPDFData && isHyperlinkCell();
+ if (!bHasURL)
+ return;
+
+ tools::Long nURLWidth = static_cast<tools::Long>(mpEngine->CalcTextWidth());
+ tools::Long nURLHeight = mpEngine->GetTextHeight();
+ if (mbBreak)
+ {
+ Size aPaper = mpEngine->GetPaperSize();
+ if ( mbAsianVertical )
+ nURLHeight = aPaper.Height();
+ else
+ nURLWidth = aPaper.Width();
+ }
+ if (isVerticallyOriented())
+ std::swap( nURLWidth, nURLHeight );
+ else if (mbAsianVertical)
+ aURLStart.AdjustX( -nURLWidth );
+
+ tools::Rectangle aURLRect( aURLStart, Size( nURLWidth, nURLHeight ) );
+ lcl_DoHyperlinkResult(pDev, aURLRect, maCell);
+}
+
+// Returns true if the rect is clipped vertically
+bool ScOutputData::AdjustAreaParamClipRect(OutputAreaParam& rAreaParam)
+{
+ if( rAreaParam.maClipRect.Left() < nScrX )
+ {
+ rAreaParam.maClipRect.SetLeft( nScrX );
+ rAreaParam.mbLeftClip = true;
+ }
+ if( rAreaParam.maClipRect.Right() > nScrX + nScrW )
+ {
+ rAreaParam.maClipRect.SetRight( nScrX + nScrW ); //! minus one?
+ rAreaParam.mbRightClip = true;
+ }
+
+ bool bVClip = false;
+
+ if( rAreaParam.maClipRect.Top() < nScrY )
+ {
+ rAreaParam.maClipRect.SetTop( nScrY );
+ bVClip = true;
+ }
+ if( rAreaParam.maClipRect.Bottom() > nScrY + nScrH )
+ {
+ rAreaParam.maClipRect.SetBottom( nScrY + nScrH ); //! minus one?
+ bVClip = true;
+ }
+ return bVClip;
+}
+
+// Doesn't handle clip marks - should be handled in advance using GetOutputArea
+class ClearableClipRegion
+{
+public:
+ ClearableClipRegion( const tools::Rectangle& rRect, bool bClip, bool bSimClip,
+ const VclPtr<OutputDevice>& pDev, bool bMetaFile )
+ :mbMetaFile(bMetaFile)
+ {
+ if (!(bClip || bSimClip))
+ return;
+
+ maRect = rRect;
+ if (bClip) // for bSimClip only initialize aClipRect
+ {
+ mpDev.reset(pDev);
+ if (mbMetaFile)
+ {
+ mpDev->Push();
+ mpDev->IntersectClipRegion(maRect);
+ }
+ else
+ mpDev->SetClipRegion(vcl::Region(maRect));
+ }
+ }
+
+ ~ClearableClipRegion() COVERITY_NOEXCEPT_FALSE
+ {
+ // Pop() or SetClipRegion() must only be called in case bClip was true
+ // in the ctor, and only then mpDev is set.
+ if (mpDev)
+ {
+ if (mbMetaFile)
+ mpDev->Pop();
+ else
+ mpDev->SetClipRegion();
+ }
+ }
+
+ const tools::Rectangle& getRect() const { return maRect; }
+
+private:
+ tools::Rectangle maRect;
+ VclPtr<OutputDevice> mpDev;
+ bool mbMetaFile;
+};
+
+// Returns needed width in current units; sets rNeededPixel to needed width in pixels
+tools::Long ScOutputData::SetEngineTextAndGetWidth( DrawEditParam& rParam, const OUString& rSetString,
+ tools::Long& rNeededPixel, tools::Long nAddWidthPixels )
+{
+ rParam.mpEngine->SetTextCurrentDefaults( rSetString );
+ tools::Long nEngineWidth = static_cast<tools::Long>( rParam.mpEngine->CalcTextWidth() );
+ if ( rParam.mbPixelToLogic )
+ rNeededPixel = mpRefDevice->LogicToPixel( Size( nEngineWidth, 0 ) ).Width();
+ else
+ rNeededPixel = nEngineWidth;
+
+ rNeededPixel += nAddWidthPixels;
+
+ return nEngineWidth;
+}
+
+void ScOutputData::DrawEditStandard(DrawEditParam& rParam)
+{
+ OSL_ASSERT(rParam.meOrient == SvxCellOrientation::Standard);
+ OSL_ASSERT(!rParam.mbAsianVertical);
+
+ Size aRefOne = mpRefDevice->PixelToLogic(Size(1,1));
+
+ bool bRepeat = (rParam.meHorJustAttr == SvxCellHorJustify::Repeat && !rParam.mbBreak);
+ bool bShrink = !rParam.mbBreak && !bRepeat && lcl_GetBoolValue(*rParam.mpPattern, ATTR_SHRINKTOFIT, rParam.mpCondSet);
+ Degree100 nAttrRotate = lcl_GetValue<ScRotateValueItem, Degree100>(*rParam.mpPattern, ATTR_ROTATE_VALUE, rParam.mpCondSet);
+
+ if ( rParam.meHorJustAttr == SvxCellHorJustify::Repeat )
+ {
+ // ignore orientation/rotation if "repeat" is active
+ rParam.meOrient = SvxCellOrientation::Standard;
+ nAttrRotate = 0_deg100;
+
+ // #i31843# "repeat" with "line breaks" is treated as default alignment
+ // (but rotation is still disabled).
+ // Default again leads to context dependent alignment instead of
+ // SvxCellHorJustify::Standard.
+ if ( rParam.mbBreak )
+ rParam.meHorJustResult = rParam.meHorJustContext;
+ }
+
+ if (nAttrRotate)
+ {
+ //! set flag to find the cell in DrawRotated again ?
+ //! (or flag already set during DrawBackground, then no query here)
+ return; // rotated is outputted separately
+ }
+
+ SvxCellHorJustify eOutHorJust = rParam.meHorJustContext;
+
+ //! mirror margin values for RTL?
+ //! move margin down to after final GetOutputArea call
+ tools::Long nTopM, nLeftM, nBottomM, nRightM;
+ rParam.calcMargins(nTopM, nLeftM, nBottomM, nRightM, mnPPTX, mnPPTY);
+
+ SCCOL nXForPos = rParam.mnX;
+ if ( nXForPos < nX1 )
+ {
+ nXForPos = nX1;
+ rParam.mnPosX = rParam.mnInitPosX;
+ }
+ SCSIZE nArrYForPos = rParam.mnArrY;
+ if ( nArrYForPos < 1 )
+ {
+ nArrYForPos = 1;
+ rParam.mnPosY = nScrY;
+ }
+
+ OutputAreaParam aAreaParam;
+
+ // Initial page size - large for normal text, cell size for automatic line breaks
+
+ Size aPaperSize( 1000000, 1000000 );
+ if (rParam.mbBreak)
+ {
+ // call GetOutputArea with nNeeded=0, to get only the cell width
+
+ //! handle nArrY == 0
+ GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, 0,
+ *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
+ rParam.mbCellIsValue, true, false, aAreaParam );
+
+ //! special ScEditUtil handling if formatting for printer
+ rParam.calcPaperSize(aPaperSize, aAreaParam.maAlignRect, mnPPTX, mnPPTY);
+ }
+ if (rParam.mbPixelToLogic)
+ {
+ Size aLogicSize = mpRefDevice->PixelToLogic(aPaperSize);
+ if ( rParam.mbBreak && !rParam.mbAsianVertical && mpRefDevice != pFmtDevice )
+ {
+ // #i85342# screen display and formatting for printer,
+ // use same GetEditArea call as in ScViewData::SetEditEngine
+
+ Fraction aFract(1,1);
+ tools::Rectangle aUtilRect = ScEditUtil( mpDoc, rParam.mnCellX, rParam.mnCellY, nTab, Point(0,0), pFmtDevice,
+ HMM_PER_TWIPS, HMM_PER_TWIPS, aFract, aFract ).GetEditArea( rParam.mpPattern, false );
+ aLogicSize.setWidth( aUtilRect.GetWidth() );
+ }
+ rParam.mpEngine->SetPaperSize(aLogicSize);
+ }
+ else
+ rParam.mpEngine->SetPaperSize(aPaperSize);
+
+ // Fill the EditEngine (cell attributes and text)
+
+ // default alignment for asian vertical mode is top-right
+ if ( rParam.mbAsianVertical && rParam.meVerJust == SvxCellVerJustify::Standard )
+ rParam.meVerJust = SvxCellVerJustify::Top;
+
+ rParam.setPatternToEngine(mbUseStyleColor);
+ rParam.setAlignmentToEngine();
+ // Don't format unnecessary parts if the text will be drawn from top (Standard will
+ // act that way if text doesn't fit, see below).
+ rParam.mpEngine->EnableSkipOutsideFormat(rParam.meVerJust==SvxCellVerJustify::Top
+ || rParam.meVerJust==SvxCellVerJustify::Standard);
+
+ // Read content from cell
+
+ bool bWrapFields = false;
+ if (!rParam.readCellContent(mpDoc, mbShowNullValues, mbShowFormulas, mbSyntaxMode, mbUseStyleColor, mbForceAutoColor, bWrapFields))
+ // Failed to read cell content. Bail out.
+ return;
+
+ if ( mbSyntaxMode )
+ SetEditSyntaxColor(*rParam.mpEngine, rParam.maCell);
+ else if ( mbUseStyleColor && mbForceAutoColor )
+ lcl_SetEditColor( *rParam.mpEngine, COL_AUTO ); //! or have a flag at EditEngine
+
+ rParam.mpEngine->SetUpdateLayout( true ); // after SetText, before CalcTextWidth/GetTextHeight
+
+ // Get final output area using the calculated width
+
+ tools::Long nEngineWidth, nEngineHeight;
+ rParam.getEngineSize(rParam.mpEngine, nEngineWidth, nEngineHeight);
+
+ tools::Long nNeededPixel = nEngineWidth;
+ if (rParam.mbPixelToLogic)
+ nNeededPixel = mpRefDevice->LogicToPixel(Size(nNeededPixel,0)).Width();
+ nNeededPixel += nLeftM + nRightM;
+
+ if (!rParam.mbBreak || bShrink)
+ {
+ // for break, the first GetOutputArea call is sufficient
+ GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, nNeededPixel,
+ *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
+ rParam.mbCellIsValue || bRepeat || bShrink, false, false, aAreaParam );
+
+ if ( bShrink )
+ {
+ ShrinkEditEngine( *rParam.mpEngine, aAreaParam.maAlignRect,
+ nLeftM, nTopM, nRightM, nBottomM, true,
+ rParam.meOrient, 0_deg100, rParam.mbPixelToLogic,
+ nEngineWidth, nEngineHeight, nNeededPixel,
+ aAreaParam.mbLeftClip, aAreaParam.mbRightClip );
+ }
+ if ( bRepeat && !aAreaParam.mbLeftClip && !aAreaParam.mbRightClip && rParam.mpEngine->GetParagraphCount() == 1 )
+ {
+ // First check if twice the space for the formatted text is available
+ // (otherwise just keep it unchanged).
+
+ tools::Long nFormatted = nNeededPixel - nLeftM - nRightM; // without margin
+ tools::Long nAvailable = aAreaParam.maAlignRect.GetWidth() - nLeftM - nRightM;
+ if ( nAvailable >= 2 * nFormatted )
+ {
+ // "repeat" is handled with unformatted text (for performance reasons)
+ OUString aCellStr = rParam.mpEngine->GetText();
+
+ tools::Long nRepeatSize = 0;
+ SetEngineTextAndGetWidth( rParam, aCellStr, nRepeatSize, 0 );
+ if ( pFmtDevice != mpRefDevice )
+ ++nRepeatSize;
+ if ( nRepeatSize > 0 )
+ {
+ tools::Long nRepeatCount = nAvailable / nRepeatSize;
+ if ( nRepeatCount > 1 )
+ {
+ OUStringBuffer aRepeated(aCellStr);
+ for ( tools::Long nRepeat = 1; nRepeat < nRepeatCount; nRepeat++ )
+ aRepeated.append(aCellStr);
+
+ SetEngineTextAndGetWidth( rParam, aRepeated.makeStringAndClear(),
+ nNeededPixel, (nLeftM + nRightM ) );
+
+ nEngineHeight = rParam.mpEngine->GetTextHeight();
+ }
+ }
+ }
+ }
+
+
+ if ( rParam.mbCellIsValue && ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) )
+ {
+ SetEngineTextAndGetWidth( rParam, "###", nNeededPixel, ( nLeftM + nRightM ) );
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+ ScCellInfo* pClipMarkCell = &rParam.mpThisRowInfo->cellInfo(rParam.mnX);
+ SetClipMarks( aAreaParam, pClipMarkCell, eOutHorJust, nLayoutSign );
+ }
+
+ if (eOutHorJust != SvxCellHorJustify::Left)
+ {
+ aPaperSize.setWidth( nNeededPixel + 1 );
+ if (rParam.mbPixelToLogic)
+ rParam.mpEngine->SetPaperSize(mpRefDevice->PixelToLogic(aPaperSize));
+ else
+ rParam.mpEngine->SetPaperSize(aPaperSize);
+ }
+ }
+
+ tools::Long nStartX = aAreaParam.maAlignRect.Left();
+ tools::Long nStartY = aAreaParam.maAlignRect.Top();
+ tools::Long nCellWidth = aAreaParam.maAlignRect.GetWidth();
+ tools::Long nOutWidth = nCellWidth - 1 - nLeftM - nRightM;
+ tools::Long nOutHeight = aAreaParam.maAlignRect.GetHeight() - nTopM - nBottomM;
+
+ if (rParam.mbBreak)
+ {
+ // text with automatic breaks is aligned only within the
+ // edit engine's paper size, the output of the whole area
+ // is always left-aligned
+
+ nStartX += nLeftM;
+ }
+ else
+ {
+ if ( eOutHorJust == SvxCellHorJustify::Right )
+ nStartX -= nNeededPixel - nCellWidth + nRightM + 1;
+ else if ( eOutHorJust == SvxCellHorJustify::Center )
+ nStartX -= ( nNeededPixel - nCellWidth + nRightM + 1 - nLeftM ) / 2;
+ else
+ nStartX += nLeftM;
+ }
+
+ bool bOutside = (aAreaParam.maClipRect.Right() < nScrX || aAreaParam.maClipRect.Left() >= nScrX + nScrW);
+ if (bOutside)
+ return;
+
+ // Also take fields in a cell with automatic breaks into account: clip to cell width
+ bool bClip = AdjustAreaParamClipRect(aAreaParam) || aAreaParam.mbLeftClip || aAreaParam.mbRightClip || bWrapFields;
+ bool bSimClip = false;
+
+ Size aCellSize; // output area, excluding margins, in logical units
+ if (rParam.mbPixelToLogic)
+ aCellSize = mpRefDevice->PixelToLogic( Size( nOutWidth, nOutHeight ) );
+ else
+ aCellSize = Size( nOutWidth, nOutHeight );
+
+ if ( nEngineHeight >= aCellSize.Height() + aRefOne.Height() )
+ {
+ const ScMergeAttr* pMerge = &rParam.mpPattern->GetItem(ATTR_MERGE);
+ bool bMerged = pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1;
+
+ // Don't clip for text height when printing rows with optimal height,
+ // except when font size is from conditional formatting.
+ //! Allow clipping when vertically merged?
+ if ( eType != OUTTYPE_PRINTER ||
+ ( mpDoc->GetRowFlags( rParam.mnCellY, nTab ) & CRFlags::ManualSize ) ||
+ ( rParam.mpCondSet && SfxItemState::SET ==
+ rParam.mpCondSet->GetItemState(ATTR_FONT_HEIGHT) ) )
+ bClip = true;
+ else
+ bSimClip = true;
+
+ // Show clip marks if height is at least 5pt too small and
+ // there are several lines of text.
+ // Not for asian vertical text, because that would interfere
+ // with the default right position of the text.
+ // Only with automatic line breaks, to avoid having to find
+ // the cells with the horizontal end of the text again.
+ if ( nEngineHeight - aCellSize.Height() > 100 &&
+ rParam.mbBreak && bMarkClipped &&
+ ( rParam.mpEngine->GetParagraphCount() > 1 || rParam.mpEngine->GetLineCount(0) > 1 ) )
+ {
+ ScCellInfo* pClipMarkCell = nullptr;
+ if ( bMerged )
+ {
+ // anywhere in the merged area...
+ SCCOL nClipX = ( rParam.mnX < nX1 ) ? nX1 : rParam.mnX;
+ pClipMarkCell = &pRowInfo[(rParam.mnArrY != 0) ? rParam.mnArrY : 1].cellInfo(nClipX);
+ }
+ else
+ pClipMarkCell = &rParam.mpThisRowInfo->cellInfo(rParam.mnX);
+
+ pClipMarkCell->nClipMark |= ScClipMark::Right; //! also allow left?
+ bAnyClipped = true;
+
+ tools::Long nMarkPixel = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTX );
+ if ( aAreaParam.maClipRect.Right() - nMarkPixel > aAreaParam.maClipRect.Left() )
+ aAreaParam.maClipRect.AdjustRight( -nMarkPixel );
+
+ // Standard is normally treated as Bottom, but if text height is clipped, then
+ // Top looks better and also allows using EditEngine::EnableSkipOutsideFormat().
+ if (rParam.meVerJust==SvxCellVerJustify::Standard)
+ rParam.meVerJust=SvxCellVerJustify::Top;
+ }
+ }
+
+ Point aURLStart;
+
+ { // Clip marks are already handled in GetOutputArea
+ ClearableClipRegion aClip(rParam.mbPixelToLogic ? mpRefDevice->PixelToLogic(aAreaParam.maClipRect)
+ : aAreaParam.maClipRect, bClip, bSimClip, mpDev, bMetaFile);
+
+ Point aLogicStart;
+ if (rParam.mbPixelToLogic)
+ aLogicStart = mpRefDevice->PixelToLogic( Point(nStartX,nStartY) );
+ else
+ aLogicStart = Point(nStartX, nStartY);
+
+ if (!rParam.mbBreak)
+ {
+ // horizontal alignment
+ if (rParam.adjustHorAlignment(rParam.mpEngine))
+ // reset adjustment for the next cell
+ rParam.mpOldPattern = nullptr;
+ }
+
+ if (rParam.meVerJust==SvxCellVerJustify::Bottom ||
+ rParam.meVerJust==SvxCellVerJustify::Standard)
+ {
+ //! if pRefDevice != pFmtDevice, keep heights in logic units,
+ //! only converting margin?
+
+ if (rParam.mbPixelToLogic)
+ aLogicStart.AdjustY(mpRefDevice->PixelToLogic( Size(0, nTopM +
+ mpRefDevice->LogicToPixel(aCellSize).Height() -
+ mpRefDevice->LogicToPixel(Size(0,nEngineHeight)).Height()
+ )).Height() );
+ else
+ aLogicStart.AdjustY(nTopM + aCellSize.Height() - nEngineHeight );
+ }
+ else if (rParam.meVerJust==SvxCellVerJustify::Center)
+ {
+ if (rParam.mbPixelToLogic)
+ aLogicStart.AdjustY(mpRefDevice->PixelToLogic( Size(0, nTopM + (
+ mpRefDevice->LogicToPixel(aCellSize).Height() -
+ mpRefDevice->LogicToPixel(Size(0,nEngineHeight)).Height() )
+ / 2)).Height() );
+ else
+ aLogicStart.AdjustY(nTopM + (aCellSize.Height() - nEngineHeight) / 2 );
+ }
+ else // top
+ {
+ if (rParam.mbPixelToLogic)
+ aLogicStart.AdjustY(mpRefDevice->PixelToLogic(Size(0,nTopM)).Height() );
+ else
+ aLogicStart.AdjustY(nTopM );
+ }
+
+ aURLStart = aLogicStart; // copy before modifying for orientation
+
+ // bMoveClipped handling has been replaced by complete alignment
+ // handling (also extending to the left).
+
+ if (bSimClip)
+ {
+ // no hard clip, only draw the affected rows
+ Point aDocStart = aClip.getRect().TopLeft();
+ aDocStart -= aLogicStart;
+ rParam.mpEngine->Draw(*mpDev, aClip.getRect(), aDocStart, false);
+ }
+ else
+ {
+ rParam.mpEngine->Draw(*mpDev, aLogicStart);
+ }
+ }
+
+ rParam.adjustForHyperlinkInPDF(aURLStart, mpDev);
+}
+
+void ScOutputData::SetClipMarks( OutputAreaParam &aAreaParam, ScCellInfo* pClipMarkCell,
+ SvxCellHorJustify eOutHorJust,
+ tools::Long nLayoutSign )
+{
+ tools::Long nMarkPixel = SC_CLIPMARK_SIZE * mnPPTX;
+
+ if ( eOutHorJust == SvxCellHorJustify::Left )
+ {
+ pClipMarkCell->nClipMark |= ScClipMark::Right;
+ bAnyClipped = true;
+ aAreaParam.maClipRect.AdjustRight( -( nMarkPixel * nLayoutSign ) );
+ }
+ else if ( eOutHorJust == SvxCellHorJustify::Right )
+ {
+ pClipMarkCell->nClipMark |= ScClipMark::Left;
+ bAnyClipped = true;
+ aAreaParam.maClipRect.AdjustLeft( nMarkPixel * nLayoutSign );
+ }
+ else
+ {
+ pClipMarkCell->nClipMark |= ScClipMark::Right;
+ pClipMarkCell->nClipMark |= ScClipMark::Left;
+ bAnyClipped = true;
+ aAreaParam.maClipRect.AdjustRight( -( nMarkPixel * nLayoutSign ) );
+ aAreaParam.maClipRect.AdjustLeft( nMarkPixel * nLayoutSign );
+ }
+
+}
+
+void ScOutputData::ShowClipMarks( DrawEditParam& rParam, tools::Long nEngineWidth, const Size& aCellSize,
+ bool bMerged, OutputAreaParam& aAreaParam, bool bTop)
+{
+ // Show clip marks if width is at least 5pt too small and
+ // there are several lines of text.
+ // Not for asian vertical text, because that would interfere
+ // with the default right position of the text.
+ // Only with automatic line breaks, to avoid having to find
+ // the cells with the horizontal end of the text again.
+ if (nEngineWidth - aCellSize.Width() <= 100 || !rParam.mbBreak || !bMarkClipped
+ || (rParam.mpEngine->GetParagraphCount() <= 1 && rParam.mpEngine->GetLineCount(0) <= 1))
+ return;
+
+ ScCellInfo* pClipMarkCell = nullptr;
+ if (bMerged)
+ {
+ // anywhere in the merged area...
+ SCCOL nClipX = (rParam.mnX < nX1) ? nX1 : rParam.mnX;
+ pClipMarkCell = &pRowInfo[(rParam.mnArrY != 0) ? rParam.mnArrY : 1].cellInfo(nClipX);
+ }
+ else
+ pClipMarkCell = &rParam.mpThisRowInfo->cellInfo(rParam.mnX);
+
+ bAnyClipped = true;
+ bVertical = true;
+ const tools::Long nMarkPixel = static_cast<tools::Long>(SC_CLIPMARK_SIZE * mnPPTX);
+ if (bTop)
+ {
+ pClipMarkCell->nClipMark |= ScClipMark::Top;
+ if (aAreaParam.maClipRect.Top() - nMarkPixel < aAreaParam.maClipRect.Bottom())
+ aAreaParam.maClipRect.AdjustTop(+nMarkPixel);
+ }
+ else
+ {
+ pClipMarkCell->nClipMark |= ScClipMark::Bottom;
+ if (aAreaParam.maClipRect.Top() - nMarkPixel < aAreaParam.maClipRect.Bottom())
+ aAreaParam.maClipRect.AdjustBottom(-nMarkPixel);
+ }
+}
+
+ClearableClipRegionPtr ScOutputData::Clip( DrawEditParam& rParam, const Size& aCellSize,
+ OutputAreaParam& aAreaParam, tools::Long nEngineWidth,
+ bool bWrapFields, bool bTop)
+{
+ // Also take fields in a cell with automatic breaks into account: clip to cell width
+ bool bClip = AdjustAreaParamClipRect(aAreaParam) || aAreaParam.mbLeftClip || aAreaParam.mbRightClip || bWrapFields;
+ bool bSimClip = false;
+
+ const Size& aRefOne = mpRefDevice->PixelToLogic(Size(1,1));
+ if ( nEngineWidth >= aCellSize.Width() + aRefOne.Width() )
+ {
+ const ScMergeAttr* pMerge = &rParam.mpPattern->GetItem(ATTR_MERGE);
+ const bool bMerged = pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1;
+
+ // Don't clip for text height when printing rows with optimal height,
+ // except when font size is from conditional formatting.
+ //! Allow clipping when vertically merged?
+ if ( eType != OUTTYPE_PRINTER ||
+ ( mpDoc->GetRowFlags( rParam.mnCellY, nTab ) & CRFlags::ManualSize ) ||
+ ( rParam.mpCondSet && SfxItemState::SET ==
+ rParam.mpCondSet->GetItemState(ATTR_FONT_HEIGHT) ) )
+ bClip = true;
+ else
+ bSimClip = true;
+
+ ShowClipMarks( rParam, nEngineWidth, aCellSize, bMerged, aAreaParam, bTop);
+ }
+
+ // Clip marks are already handled in GetOutputArea
+ return ClearableClipRegionPtr(new ClearableClipRegion(rParam.mbPixelToLogic ?
+ mpRefDevice->PixelToLogic(aAreaParam.maClipRect)
+ : aAreaParam.maClipRect, bClip, bSimClip, mpDev, bMetaFile));
+}
+
+void ScOutputData::DrawEditBottomTop(DrawEditParam& rParam)
+{
+ OSL_ASSERT(rParam.meHorJustAttr != SvxCellHorJustify::Repeat);
+
+ const bool bRepeat = (rParam.meHorJustAttr == SvxCellHorJustify::Repeat && !rParam.mbBreak);
+ const bool bShrink = !rParam.mbBreak && !bRepeat && lcl_GetBoolValue(*rParam.mpPattern, ATTR_SHRINKTOFIT, rParam.mpCondSet);
+
+ SvxCellHorJustify eOutHorJust = rParam.meHorJustContext;
+
+ //! mirror margin values for RTL?
+ //! move margin down to after final GetOutputArea call
+ tools::Long nTopM, nLeftM, nBottomM, nRightM;
+ rParam.calcMargins(nTopM, nLeftM, nBottomM, nRightM, mnPPTX, mnPPTY);
+
+ SCCOL nXForPos = rParam.mnX;
+ if ( nXForPos < nX1 )
+ {
+ nXForPos = nX1;
+ rParam.mnPosX = rParam.mnInitPosX;
+ }
+ SCSIZE nArrYForPos = rParam.mnArrY;
+ if ( nArrYForPos < 1 )
+ {
+ nArrYForPos = 1;
+ rParam.mnPosY = nScrY;
+ }
+
+ OutputAreaParam aAreaParam;
+
+ // Initial page size - large for normal text, cell size for automatic line breaks
+
+ Size aPaperSize( 1000000, 1000000 );
+ if (rParam.mbBreak)
+ {
+ // call GetOutputArea with nNeeded=0, to get only the cell width
+
+ //! handle nArrY == 0
+ GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, 0,
+ *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
+ rParam.mbCellIsValue, true, false, aAreaParam );
+
+ //! special ScEditUtil handling if formatting for printer
+ rParam.calcPaperSize(aPaperSize, aAreaParam.maAlignRect, mnPPTX, mnPPTY);
+ }
+ if (rParam.mbPixelToLogic)
+ {
+ Size aLogicSize = mpRefDevice->PixelToLogic(aPaperSize);
+ rParam.mpEngine->SetPaperSize(aLogicSize);
+ }
+ else
+ rParam.mpEngine->SetPaperSize(aPaperSize);
+
+ // Fill the EditEngine (cell attributes and text)
+
+ rParam.setPatternToEngine(mbUseStyleColor);
+ rParam.setAlignmentToEngine();
+
+ // Read content from cell
+
+ bool bWrapFields = false;
+ if (!rParam.readCellContent(mpDoc, mbShowNullValues, mbShowFormulas, mbSyntaxMode, mbUseStyleColor, mbForceAutoColor, bWrapFields))
+ // Failed to read cell content. Bail out.
+ return;
+
+ if ( mbSyntaxMode )
+ SetEditSyntaxColor( *rParam.mpEngine, rParam.maCell );
+ else if ( mbUseStyleColor && mbForceAutoColor )
+ lcl_SetEditColor( *rParam.mpEngine, COL_AUTO ); //! or have a flag at EditEngine
+
+ rParam.mpEngine->SetUpdateLayout( true ); // after SetText, before CalcTextWidth/GetTextHeight
+
+ // Get final output area using the calculated width
+
+ tools::Long nEngineWidth, nEngineHeight;
+ rParam.getEngineSize(rParam.mpEngine, nEngineWidth, nEngineHeight);
+
+ tools::Long nNeededPixel = nEngineWidth;
+ if (rParam.mbPixelToLogic)
+ nNeededPixel = mpRefDevice->LogicToPixel(Size(nNeededPixel,0)).Width();
+ nNeededPixel += nLeftM + nRightM;
+
+ if (!rParam.mbBreak || bShrink)
+ {
+ // for break, the first GetOutputArea call is sufficient
+ GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, nNeededPixel,
+ *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
+ rParam.mbCellIsValue || bRepeat || bShrink, false, false, aAreaParam );
+
+ if ( bShrink )
+ {
+ ShrinkEditEngine( *rParam.mpEngine, aAreaParam.maAlignRect,
+ nLeftM, nTopM, nRightM, nBottomM, false,
+ (rParam.meOrient), 0_deg100, rParam.mbPixelToLogic,
+ nEngineWidth, nEngineHeight, nNeededPixel,
+ aAreaParam.mbLeftClip, aAreaParam.mbRightClip );
+ }
+ if ( bRepeat && !aAreaParam.mbLeftClip && !aAreaParam.mbRightClip && rParam.mpEngine->GetParagraphCount() == 1 )
+ {
+ // First check if twice the space for the formatted text is available
+ // (otherwise just keep it unchanged).
+
+ const tools::Long nFormatted = nNeededPixel - nLeftM - nRightM; // without margin
+ const tools::Long nAvailable = aAreaParam.maAlignRect.GetWidth() - nLeftM - nRightM;
+ if ( nAvailable >= 2 * nFormatted )
+ {
+ // "repeat" is handled with unformatted text (for performance reasons)
+ OUString aCellStr = rParam.mpEngine->GetText();
+
+ tools::Long nRepeatSize = 0;
+ SetEngineTextAndGetWidth( rParam, aCellStr, nRepeatSize, 0 );
+ if ( pFmtDevice != mpRefDevice )
+ ++nRepeatSize;
+ if ( nRepeatSize > 0 )
+ {
+ const tools::Long nRepeatCount = nAvailable / nRepeatSize;
+ if ( nRepeatCount > 1 )
+ {
+ OUStringBuffer aRepeated(aCellStr);
+ for ( tools::Long nRepeat = 1; nRepeat < nRepeatCount; nRepeat++ )
+ aRepeated.append(aCellStr);
+
+ nEngineWidth = SetEngineTextAndGetWidth( rParam, aRepeated.makeStringAndClear(),
+ nNeededPixel, (nLeftM + nRightM ) );
+
+ nEngineHeight = rParam.mpEngine->GetTextHeight();
+ }
+ }
+ }
+ }
+ if ( rParam.mbCellIsValue && ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) )
+ {
+ nEngineWidth = SetEngineTextAndGetWidth( rParam, "###", nNeededPixel, ( nLeftM + nRightM ) );
+
+ // No clip marks if "###" doesn't fit (same as in DrawStrings)
+ }
+ }
+
+ tools::Long nStartX = aAreaParam.maAlignRect.Left();
+ const tools::Long nStartY = aAreaParam.maAlignRect.Top();
+ const tools::Long nCellWidth = aAreaParam.maAlignRect.GetWidth();
+ const tools::Long nOutWidth = nCellWidth - 1 - nLeftM - nRightM;
+ const tools::Long nOutHeight = aAreaParam.maAlignRect.GetHeight() - nTopM - nBottomM;
+
+ if (rParam.mbBreak)
+ {
+ // text with automatic breaks is aligned only within the
+ // edit engine's paper size, the output of the whole area
+ // is always left-aligned
+
+ nStartX += nLeftM;
+ }
+ else
+ {
+ if ( eOutHorJust == SvxCellHorJustify::Right )
+ nStartX -= nNeededPixel - nCellWidth + nRightM + 1;
+ else if ( eOutHorJust == SvxCellHorJustify::Center )
+ nStartX -= ( nNeededPixel - nCellWidth + nRightM + 1 - nLeftM ) / 2;
+ else
+ nStartX += nLeftM;
+ }
+
+ const bool bOutside = (aAreaParam.maClipRect.Right() < nScrX || aAreaParam.maClipRect.Left() >= nScrX + nScrW);
+ if (bOutside)
+ return;
+
+ // output area, excluding margins, in logical units
+ const Size& aCellSize = rParam.mbPixelToLogic
+ ? mpRefDevice->PixelToLogic( Size( nOutWidth, nOutHeight ) )
+ : Size( nOutWidth, nOutHeight );
+
+ Point aURLStart;
+
+ {
+ const auto pClipRegion = Clip( rParam, aCellSize, aAreaParam, nEngineWidth, bWrapFields, true );
+
+ Point aLogicStart(nStartX, nStartY);
+ rParam.calcStartPosForVertical(aLogicStart, aCellSize.Width(), nEngineWidth, nTopM, mpRefDevice);
+
+ aURLStart = aLogicStart; // copy before modifying for orientation
+
+ if (rParam.meHorJustResult == SvxCellHorJustify::Block || rParam.mbBreak)
+ {
+ Size aPSize = rParam.mpEngine->GetPaperSize();
+ aPSize.setWidth( aCellSize.Height() );
+ rParam.mpEngine->SetPaperSize(aPSize);
+ aLogicStart.AdjustY(
+ rParam.mbBreak ? aPSize.Width() : nEngineHeight );
+ }
+ else
+ {
+ // Note that the "paper" is rotated 90 degrees to the left, so
+ // paper's width is in vertical direction. Also, the whole text
+ // is on a single line, as text wrap is not in effect.
+
+ // Set the paper width to be the width of the text.
+ Size aPSize = rParam.mpEngine->GetPaperSize();
+ aPSize.setWidth( rParam.mpEngine->CalcTextWidth() );
+ rParam.mpEngine->SetPaperSize(aPSize);
+
+ tools::Long nGap = 0;
+ tools::Long nTopOffset = 0;
+ if (rParam.mbPixelToLogic)
+ {
+ nGap = mpRefDevice->LogicToPixel(aCellSize).Height() - mpRefDevice->LogicToPixel(aPSize).Width();
+ nGap = mpRefDevice->PixelToLogic(Size(0, nGap)).Height();
+ nTopOffset = mpRefDevice->PixelToLogic(Size(0,nTopM)).Height();
+ }
+ else
+ {
+ nGap = aCellSize.Height() - aPSize.Width();
+ nTopOffset = nTopM;
+ }
+
+ // First, align text to bottom.
+ aLogicStart.AdjustY(aCellSize.Height() );
+ aLogicStart.AdjustY(nTopOffset );
+
+ switch (rParam.meVerJust)
+ {
+ case SvxCellVerJustify::Standard:
+ case SvxCellVerJustify::Bottom:
+ // align to bottom (do nothing).
+ break;
+ case SvxCellVerJustify::Center:
+ // center it.
+ aLogicStart.AdjustY( -(nGap / 2) );
+ break;
+ case SvxCellVerJustify::Block:
+ case SvxCellVerJustify::Top:
+ // align to top
+ aLogicStart.AdjustY( -nGap );
+ break;
+ default:
+ ;
+ }
+ }
+
+ rParam.mpEngine->Draw(*mpDev, aLogicStart, 900_deg10);
+ }
+
+ rParam.adjustForHyperlinkInPDF(aURLStart, mpDev);
+}
+
+void ScOutputData::DrawEditTopBottom(DrawEditParam& rParam)
+{
+ OSL_ASSERT(rParam.meHorJustAttr != SvxCellHorJustify::Repeat);
+
+ const bool bRepeat = (rParam.meHorJustAttr == SvxCellHorJustify::Repeat && !rParam.mbBreak);
+ const bool bShrink = !rParam.mbBreak && !bRepeat && lcl_GetBoolValue(*rParam.mpPattern, ATTR_SHRINKTOFIT, rParam.mpCondSet);
+
+ SvxCellHorJustify eOutHorJust = rParam.meHorJustContext;
+
+ //! mirror margin values for RTL?
+ //! move margin down to after final GetOutputArea call
+ tools::Long nTopM, nLeftM, nBottomM, nRightM;
+ rParam.calcMargins(nTopM, nLeftM, nBottomM, nRightM, mnPPTX, mnPPTY);
+
+ SCCOL nXForPos = rParam.mnX;
+ if ( nXForPos < nX1 )
+ {
+ nXForPos = nX1;
+ rParam.mnPosX = rParam.mnInitPosX;
+ }
+ SCSIZE nArrYForPos = rParam.mnArrY;
+ if ( nArrYForPos < 1 )
+ {
+ nArrYForPos = 1;
+ rParam.mnPosY = nScrY;
+ }
+
+ OutputAreaParam aAreaParam;
+
+ // Initial page size - large for normal text, cell size for automatic line breaks
+
+ Size aPaperSize( 1000000, 1000000 );
+ if (rParam.hasLineBreak())
+ {
+ // call GetOutputArea with nNeeded=0, to get only the cell width
+
+ //! handle nArrY == 0
+ GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, 0,
+ *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
+ rParam.mbCellIsValue, true, false, aAreaParam );
+
+ //! special ScEditUtil handling if formatting for printer
+ rParam.calcPaperSize(aPaperSize, aAreaParam.maAlignRect, mnPPTX, mnPPTY);
+ }
+ if (rParam.mbPixelToLogic)
+ {
+ Size aLogicSize = mpRefDevice->PixelToLogic(aPaperSize);
+ rParam.mpEngine->SetPaperSize(aLogicSize);
+ }
+ else
+ rParam.mpEngine->SetPaperSize(aPaperSize);
+
+ // Fill the EditEngine (cell attributes and text)
+
+ rParam.setPatternToEngine(mbUseStyleColor);
+ rParam.setAlignmentToEngine();
+
+ // Read content from cell
+
+ bool bWrapFields = false;
+ if (!rParam.readCellContent(mpDoc, mbShowNullValues, mbShowFormulas, mbSyntaxMode, mbUseStyleColor, mbForceAutoColor, bWrapFields))
+ // Failed to read cell content. Bail out.
+ return;
+
+ if ( mbSyntaxMode )
+ SetEditSyntaxColor( *rParam.mpEngine, rParam.maCell );
+ else if ( mbUseStyleColor && mbForceAutoColor )
+ lcl_SetEditColor( *rParam.mpEngine, COL_AUTO ); //! or have a flag at EditEngine
+
+ rParam.mpEngine->SetUpdateLayout( true ); // after SetText, before CalcTextWidth/GetTextHeight
+
+ // Get final output area using the calculated width
+
+ tools::Long nEngineWidth, nEngineHeight;
+ rParam.getEngineSize(rParam.mpEngine, nEngineWidth, nEngineHeight);
+
+ tools::Long nNeededPixel = nEngineWidth;
+ if (rParam.mbPixelToLogic)
+ nNeededPixel = mpRefDevice->LogicToPixel(Size(nNeededPixel,0)).Width();
+ nNeededPixel += nLeftM + nRightM;
+
+ if (!rParam.mbBreak || bShrink)
+ {
+ // for break, the first GetOutputArea call is sufficient
+ GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, nNeededPixel,
+ *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
+ rParam.mbCellIsValue || bRepeat || bShrink, false, false, aAreaParam );
+
+ if ( bShrink )
+ {
+ ShrinkEditEngine( *rParam.mpEngine, aAreaParam.maAlignRect,
+ nLeftM, nTopM, nRightM, nBottomM, false,
+ rParam.meOrient, 0_deg100, rParam.mbPixelToLogic,
+ nEngineWidth, nEngineHeight, nNeededPixel,
+ aAreaParam.mbLeftClip, aAreaParam.mbRightClip );
+ }
+ if ( bRepeat && !aAreaParam.mbLeftClip && !aAreaParam.mbRightClip && rParam.mpEngine->GetParagraphCount() == 1 )
+ {
+ // First check if twice the space for the formatted text is available
+ // (otherwise just keep it unchanged).
+
+ const tools::Long nFormatted = nNeededPixel - nLeftM - nRightM; // without margin
+ const tools::Long nAvailable = aAreaParam.maAlignRect.GetWidth() - nLeftM - nRightM;
+ if ( nAvailable >= 2 * nFormatted )
+ {
+ // "repeat" is handled with unformatted text (for performance reasons)
+ OUString aCellStr = rParam.mpEngine->GetText();
+
+ tools::Long nRepeatSize = 0;
+ SetEngineTextAndGetWidth( rParam, aCellStr, nRepeatSize, 0 );
+
+ if ( pFmtDevice != mpRefDevice )
+ ++nRepeatSize;
+ if ( nRepeatSize > 0 )
+ {
+ const tools::Long nRepeatCount = nAvailable / nRepeatSize;
+ if ( nRepeatCount > 1 )
+ {
+ OUStringBuffer aRepeated(aCellStr);
+ for ( tools::Long nRepeat = 1; nRepeat < nRepeatCount; nRepeat++ )
+ aRepeated.append(aCellStr);
+
+ nEngineWidth = SetEngineTextAndGetWidth( rParam, aRepeated.makeStringAndClear(),
+ nNeededPixel, (nLeftM + nRightM ) );
+
+ nEngineHeight = rParam.mpEngine->GetTextHeight();
+ }
+ }
+ }
+ }
+ if ( rParam.mbCellIsValue && ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) )
+ {
+ nEngineWidth = SetEngineTextAndGetWidth( rParam, "###", nNeededPixel, ( nLeftM + nRightM ) );
+
+ // No clip marks if "###" doesn't fit (same as in DrawStrings)
+ }
+ }
+
+ tools::Long nStartX = aAreaParam.maAlignRect.Left();
+ const tools::Long nStartY = aAreaParam.maAlignRect.Top();
+ const tools::Long nCellWidth = aAreaParam.maAlignRect.GetWidth();
+ const tools::Long nOutWidth = nCellWidth - 1 - nLeftM - nRightM;
+ const tools::Long nOutHeight = aAreaParam.maAlignRect.GetHeight() - nTopM - nBottomM;
+
+ if (rParam.mbBreak)
+ {
+ // text with automatic breaks is aligned only within the
+ // edit engine's paper size, the output of the whole area
+ // is always left-aligned
+
+ nStartX += nLeftM;
+ if (rParam.meHorJustResult == SvxCellHorJustify::Block)
+ nStartX += aPaperSize.Height();
+ }
+ else
+ {
+ if ( eOutHorJust == SvxCellHorJustify::Right )
+ nStartX -= nNeededPixel - nCellWidth + nRightM + 1;
+ else if ( eOutHorJust == SvxCellHorJustify::Center )
+ nStartX -= ( nNeededPixel - nCellWidth + nRightM + 1 - nLeftM ) / 2;
+ else
+ nStartX += nLeftM;
+ }
+
+ const bool bOutside = (aAreaParam.maClipRect.Right() < nScrX || aAreaParam.maClipRect.Left() >= nScrX + nScrW);
+ if (bOutside)
+ return;
+
+ // output area, excluding margins, in logical units
+ const Size& aCellSize = rParam.mbPixelToLogic
+ ? mpRefDevice->PixelToLogic( Size( nOutWidth, nOutHeight ) )
+ : Size( nOutWidth, nOutHeight );
+
+ Point aURLStart;
+
+ {
+ const auto pClipRegion = Clip( rParam, aCellSize, aAreaParam, nEngineWidth, bWrapFields, false );
+
+ Point aLogicStart(nStartX, nStartY);
+ rParam.calcStartPosForVertical(aLogicStart, aCellSize.Width(), nEngineWidth, nTopM, mpRefDevice);
+
+ aURLStart = aLogicStart; // copy before modifying for orientation
+
+ if (rParam.meHorJustResult != SvxCellHorJustify::Block)
+ {
+ aLogicStart.AdjustX(nEngineWidth );
+ if (!rParam.mbBreak)
+ {
+ // Set the paper width to text size.
+ Size aPSize = rParam.mpEngine->GetPaperSize();
+ aPSize.setWidth( rParam.mpEngine->CalcTextWidth() );
+ rParam.mpEngine->SetPaperSize(aPSize);
+
+ tools::Long nGap = 0;
+ tools::Long nTopOffset = 0; // offset by top margin
+ if (rParam.mbPixelToLogic)
+ {
+ nGap = mpRefDevice->LogicToPixel(aPSize).Width() - mpRefDevice->LogicToPixel(aCellSize).Height();
+ nGap = mpRefDevice->PixelToLogic(Size(0, nGap)).Height();
+ nTopOffset = mpRefDevice->PixelToLogic(Size(0,nTopM)).Height();
+ }
+ else
+ {
+ nGap = aPSize.Width() - aCellSize.Height();
+ nTopOffset = nTopM;
+ }
+ aLogicStart.AdjustY(nTopOffset );
+
+ switch (rParam.meVerJust)
+ {
+ case SvxCellVerJustify::Standard:
+ case SvxCellVerJustify::Bottom:
+ // align to bottom
+ aLogicStart.AdjustY( -nGap );
+ break;
+ case SvxCellVerJustify::Center:
+ // center it.
+ aLogicStart.AdjustY( -(nGap / 2) );
+ break;
+ case SvxCellVerJustify::Block:
+ case SvxCellVerJustify::Top:
+ // align to top (do nothing)
+ default:
+ ;
+ }
+ }
+ }
+
+ // bMoveClipped handling has been replaced by complete alignment
+ // handling (also extending to the left).
+
+ rParam.mpEngine->Draw(*mpDev, aLogicStart, 2700_deg10);
+ }
+
+ rParam.adjustForHyperlinkInPDF(aURLStart, mpDev);
+}
+
+void ScOutputData::DrawEditStacked(DrawEditParam& rParam)
+{
+ OSL_ASSERT(rParam.meHorJustAttr != SvxCellHorJustify::Repeat);
+ Size aRefOne = mpRefDevice->PixelToLogic(Size(1,1));
+
+ bool bRepeat = (rParam.meHorJustAttr == SvxCellHorJustify::Repeat && !rParam.mbBreak);
+ bool bShrink = !rParam.mbBreak && !bRepeat && lcl_GetBoolValue(*rParam.mpPattern, ATTR_SHRINKTOFIT, rParam.mpCondSet);
+
+ rParam.mbAsianVertical =
+ lcl_GetBoolValue(*rParam.mpPattern, ATTR_VERTICAL_ASIAN, rParam.mpCondSet);
+
+ if ( rParam.mbAsianVertical )
+ {
+ // in asian mode, use EditEngine::SetVertical instead of EEControlBits::ONECHARPERLINE
+ rParam.meOrient = SvxCellOrientation::Standard;
+ DrawEditAsianVertical(rParam);
+ return;
+ }
+
+ SvxCellHorJustify eOutHorJust = rParam.meHorJustContext;
+
+ //! mirror margin values for RTL?
+ //! move margin down to after final GetOutputArea call
+ tools::Long nTopM, nLeftM, nBottomM, nRightM;
+ rParam.calcMargins(nTopM, nLeftM, nBottomM, nRightM, mnPPTX, mnPPTY);
+
+ SCCOL nXForPos = rParam.mnX;
+ if ( nXForPos < nX1 )
+ {
+ nXForPos = nX1;
+ rParam.mnPosX = rParam.mnInitPosX;
+ }
+ SCSIZE nArrYForPos = rParam.mnArrY;
+ if ( nArrYForPos < 1 )
+ {
+ nArrYForPos = 1;
+ rParam.mnPosY = nScrY;
+ }
+
+ OutputAreaParam aAreaParam;
+
+ // Initial page size - large for normal text, cell size for automatic line breaks
+
+ Size aPaperSize( 1000000, 1000000 );
+ // call GetOutputArea with nNeeded=0, to get only the cell width
+
+ //! handle nArrY == 0
+ GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, 0,
+ *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
+ rParam.mbCellIsValue, true, false, aAreaParam );
+
+ //! special ScEditUtil handling if formatting for printer
+ rParam.calcPaperSize(aPaperSize, aAreaParam.maAlignRect, mnPPTX, mnPPTY);
+
+ if (rParam.mbPixelToLogic)
+ {
+ Size aLogicSize = mpRefDevice->PixelToLogic(aPaperSize);
+ if ( rParam.mbBreak && mpRefDevice != pFmtDevice )
+ {
+ // #i85342# screen display and formatting for printer,
+ // use same GetEditArea call as in ScViewData::SetEditEngine
+
+ Fraction aFract(1,1);
+ tools::Rectangle aUtilRect = ScEditUtil( mpDoc, rParam.mnCellX, rParam.mnCellY, nTab, Point(0,0), pFmtDevice,
+ HMM_PER_TWIPS, HMM_PER_TWIPS, aFract, aFract ).GetEditArea( rParam.mpPattern, false );
+ aLogicSize.setWidth( aUtilRect.GetWidth() );
+ }
+ rParam.mpEngine->SetPaperSize(aLogicSize);
+ }
+ else
+ rParam.mpEngine->SetPaperSize(aPaperSize);
+
+ // Fill the EditEngine (cell attributes and text)
+
+ rParam.setPatternToEngine(mbUseStyleColor);
+ rParam.setAlignmentToEngine();
+
+ // Read content from cell
+
+ bool bWrapFields = false;
+ if (!rParam.readCellContent(mpDoc, mbShowNullValues, mbShowFormulas, mbSyntaxMode, mbUseStyleColor, mbForceAutoColor, bWrapFields))
+ // Failed to read cell content. Bail out.
+ return;
+
+ if ( mbSyntaxMode )
+ SetEditSyntaxColor( *rParam.mpEngine, rParam.maCell );
+ else if ( mbUseStyleColor && mbForceAutoColor )
+ lcl_SetEditColor( *rParam.mpEngine, COL_AUTO ); //! or have a flag at EditEngine
+
+ rParam.mpEngine->SetUpdateLayout( true ); // after SetText, before CalcTextWidth/GetTextHeight
+
+ // Get final output area using the calculated width
+
+ tools::Long nEngineWidth, nEngineHeight;
+ rParam.getEngineSize(rParam.mpEngine, nEngineWidth, nEngineHeight);
+
+ tools::Long nNeededPixel = nEngineWidth;
+ if (rParam.mbPixelToLogic)
+ nNeededPixel = mpRefDevice->LogicToPixel(Size(nNeededPixel,0)).Width();
+ nNeededPixel += nLeftM + nRightM;
+
+ if (bShrink)
+ {
+ // for break, the first GetOutputArea call is sufficient
+ GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, nNeededPixel,
+ *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
+ true, false, false, aAreaParam );
+
+ ShrinkEditEngine( *rParam.mpEngine, aAreaParam.maAlignRect,
+ nLeftM, nTopM, nRightM, nBottomM, true,
+ rParam.meOrient, 0_deg100, rParam.mbPixelToLogic,
+ nEngineWidth, nEngineHeight, nNeededPixel,
+ aAreaParam.mbLeftClip, aAreaParam.mbRightClip );
+
+ if ( rParam.mbCellIsValue && ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) )
+ {
+ nEngineWidth = SetEngineTextAndGetWidth( rParam, "###", nNeededPixel, ( nLeftM + nRightM ) );
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+ ScCellInfo* pClipMarkCell = &rParam.mpThisRowInfo->cellInfo(rParam.mnX);
+ SetClipMarks( aAreaParam, pClipMarkCell, eOutHorJust, nLayoutSign );
+ }
+
+ if ( eOutHorJust != SvxCellHorJustify::Left )
+ {
+ aPaperSize.setWidth( nNeededPixel + 1 );
+ if (rParam.mbPixelToLogic)
+ rParam.mpEngine->SetPaperSize(mpRefDevice->PixelToLogic(aPaperSize));
+ else
+ rParam.mpEngine->SetPaperSize(aPaperSize);
+ }
+ }
+
+ tools::Long nStartX = aAreaParam.maAlignRect.Left();
+ tools::Long nStartY = aAreaParam.maAlignRect.Top();
+ tools::Long nCellWidth = aAreaParam.maAlignRect.GetWidth();
+ tools::Long nOutWidth = nCellWidth - 1 - nLeftM - nRightM;
+ tools::Long nOutHeight = aAreaParam.maAlignRect.GetHeight() - nTopM - nBottomM;
+
+ if (rParam.mbBreak)
+ {
+ // text with automatic breaks is aligned only within the
+ // edit engine's paper size, the output of the whole area
+ // is always left-aligned
+
+ nStartX += nLeftM;
+ }
+ else
+ {
+ if ( eOutHorJust == SvxCellHorJustify::Right )
+ nStartX -= nNeededPixel - nCellWidth + nRightM + 1;
+ else if ( eOutHorJust == SvxCellHorJustify::Center )
+ nStartX -= ( nNeededPixel - nCellWidth + nRightM + 1 - nLeftM ) / 2;
+ else
+ nStartX += nLeftM;
+ }
+
+ bool bOutside = (aAreaParam.maClipRect.Right() < nScrX || aAreaParam.maClipRect.Left() >= nScrX + nScrW);
+ if (bOutside)
+ return;
+
+ // Also take fields in a cell with automatic breaks into account: clip to cell width
+ bool bClip = AdjustAreaParamClipRect(aAreaParam) || aAreaParam.mbLeftClip || aAreaParam.mbRightClip || bWrapFields;
+ bool bSimClip = false;
+
+ Size aCellSize; // output area, excluding margins, in logical units
+ if (rParam.mbPixelToLogic)
+ aCellSize = mpRefDevice->PixelToLogic( Size( nOutWidth, nOutHeight ) );
+ else
+ aCellSize = Size( nOutWidth, nOutHeight );
+
+ if ( nEngineHeight >= aCellSize.Height() + aRefOne.Height() )
+ {
+ const ScMergeAttr* pMerge = &rParam.mpPattern->GetItem(ATTR_MERGE);
+ bool bMerged = pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1;
+
+ // Don't clip for text height when printing rows with optimal height,
+ // except when font size is from conditional formatting.
+ //! Allow clipping when vertically merged?
+ if ( eType != OUTTYPE_PRINTER ||
+ ( mpDoc->GetRowFlags( rParam.mnCellY, nTab ) & CRFlags::ManualSize ) ||
+ ( rParam.mpCondSet && SfxItemState::SET ==
+ rParam.mpCondSet->GetItemState(ATTR_FONT_HEIGHT) ) )
+ bClip = true;
+ else
+ bSimClip = true;
+
+ // Show clip marks if height is at least 5pt too small and
+ // there are several lines of text.
+ // Not for asian vertical text, because that would interfere
+ // with the default right position of the text.
+ // Only with automatic line breaks, to avoid having to find
+ // the cells with the horizontal end of the text again.
+ if ( nEngineHeight - aCellSize.Height() > 100 &&
+ rParam.mbBreak && bMarkClipped &&
+ ( rParam.mpEngine->GetParagraphCount() > 1 || rParam.mpEngine->GetLineCount(0) > 1 ) )
+ {
+ ScCellInfo* pClipMarkCell = nullptr;
+ if ( bMerged )
+ {
+ // anywhere in the merged area...
+ SCCOL nClipX = ( rParam.mnX < nX1 ) ? nX1 : rParam.mnX;
+ pClipMarkCell = &pRowInfo[(rParam.mnArrY != 0) ? rParam.mnArrY : 1].cellInfo(nClipX);
+ }
+ else
+ pClipMarkCell = &rParam.mpThisRowInfo->cellInfo(rParam.mnX);
+
+ pClipMarkCell->nClipMark |= ScClipMark::Right; //! also allow left?
+ bAnyClipped = true;
+
+ tools::Long nMarkPixel = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTX );
+ if ( aAreaParam.maClipRect.Right() - nMarkPixel > aAreaParam.maClipRect.Left() )
+ aAreaParam.maClipRect.AdjustRight( -nMarkPixel );
+ }
+ }
+
+ Point aURLStart;
+
+ { // Clip marks are already handled in GetOutputArea
+ ClearableClipRegion aClip(rParam.mbPixelToLogic ? mpRefDevice->PixelToLogic(aAreaParam.maClipRect)
+ : aAreaParam.maClipRect, bClip, bSimClip, mpDev, bMetaFile);
+
+ Point aLogicStart;
+ if (rParam.mbPixelToLogic)
+ aLogicStart = mpRefDevice->PixelToLogic( Point(nStartX,nStartY) );
+ else
+ aLogicStart = Point(nStartX, nStartY);
+
+ if (rParam.meVerJust==SvxCellVerJustify::Bottom ||
+ rParam.meVerJust==SvxCellVerJustify::Standard)
+ {
+ //! if pRefDevice != pFmtDevice, keep heights in logic units,
+ //! only converting margin?
+
+ if (rParam.mbPixelToLogic)
+ aLogicStart.AdjustY(mpRefDevice->PixelToLogic( Size(0, nTopM +
+ mpRefDevice->LogicToPixel(aCellSize).Height() -
+ mpRefDevice->LogicToPixel(Size(0,nEngineHeight)).Height()
+ )).Height() );
+ else
+ aLogicStart.AdjustY(nTopM + aCellSize.Height() - nEngineHeight );
+ }
+ else if (rParam.meVerJust==SvxCellVerJustify::Center)
+ {
+ if (rParam.mbPixelToLogic)
+ aLogicStart.AdjustY(mpRefDevice->PixelToLogic( Size(0, nTopM + (
+ mpRefDevice->LogicToPixel(aCellSize).Height() -
+ mpRefDevice->LogicToPixel(Size(0,nEngineHeight)).Height() )
+ / 2)).Height() );
+ else
+ aLogicStart.AdjustY(nTopM + (aCellSize.Height() - nEngineHeight) / 2 );
+ }
+ else // top
+ {
+ if (rParam.mbPixelToLogic)
+ aLogicStart.AdjustY(mpRefDevice->PixelToLogic(Size(0,nTopM)).Height() );
+ else
+ aLogicStart.AdjustY(nTopM );
+ }
+
+ aURLStart = aLogicStart; // copy before modifying for orientation
+
+ Size aPaperLogic = rParam.mpEngine->GetPaperSize();
+ aPaperLogic.setWidth( nEngineWidth );
+ rParam.mpEngine->SetPaperSize(aPaperLogic);
+
+ // bMoveClipped handling has been replaced by complete alignment
+ // handling (also extending to the left).
+
+ if (bSimClip)
+ {
+ // no hard clip, only draw the affected rows
+ Point aDocStart = aClip.getRect().TopLeft();
+ aDocStart -= aLogicStart;
+ rParam.mpEngine->Draw(*mpDev, aClip.getRect(), aDocStart, false);
+ }
+ else
+ {
+ rParam.mpEngine->Draw(*mpDev, aLogicStart);
+ }
+ }
+
+ rParam.adjustForHyperlinkInPDF(aURLStart, mpDev);
+}
+
+void ScOutputData::DrawEditAsianVertical(DrawEditParam& rParam)
+{
+ // When in asian vertical orientation, the orientation value is STANDARD,
+ // and the asian vertical boolean is true.
+ OSL_ASSERT(rParam.meOrient == SvxCellOrientation::Standard);
+ OSL_ASSERT(rParam.mbAsianVertical);
+ OSL_ASSERT(rParam.meHorJustAttr != SvxCellHorJustify::Repeat);
+
+ Size aRefOne = mpRefDevice->PixelToLogic(Size(1,1));
+
+ bool bHidden = false;
+ bool bShrink = !rParam.mbBreak && lcl_GetBoolValue(*rParam.mpPattern, ATTR_SHRINKTOFIT, rParam.mpCondSet);
+ Degree100 nAttrRotate = lcl_GetValue<ScRotateValueItem, Degree100>(*rParam.mpPattern, ATTR_ROTATE_VALUE, rParam.mpCondSet);
+
+ if (nAttrRotate)
+ {
+ //! set flag to find the cell in DrawRotated again ?
+ //! (or flag already set during DrawBackground, then no query here)
+ bHidden = true; // rotated is outputted separately
+ }
+
+ // default alignment for asian vertical mode is top-right
+ /* TODO: is setting meHorJustContext and meHorJustResult unconditionally to
+ * SvxCellHorJustify::Right really wanted? Seems this was done all the time,
+ * also before context was introduced and everything was attr only. */
+ if ( rParam.meHorJustAttr == SvxCellHorJustify::Standard )
+ rParam.meHorJustResult = rParam.meHorJustContext = SvxCellHorJustify::Right;
+
+ if (bHidden)
+ return;
+
+ SvxCellHorJustify eOutHorJust = rParam.meHorJustContext;
+
+ //! mirror margin values for RTL?
+ //! move margin down to after final GetOutputArea call
+ tools::Long nTopM, nLeftM, nBottomM, nRightM;
+ rParam.calcMargins(nTopM, nLeftM, nBottomM, nRightM, mnPPTX, mnPPTY);
+
+ SCCOL nXForPos = rParam.mnX;
+ if ( nXForPos < nX1 )
+ {
+ nXForPos = nX1;
+ rParam.mnPosX = rParam.mnInitPosX;
+ }
+ SCSIZE nArrYForPos = rParam.mnArrY;
+ if ( nArrYForPos < 1 )
+ {
+ nArrYForPos = 1;
+ rParam.mnPosY = nScrY;
+ }
+
+ OutputAreaParam aAreaParam;
+
+ // Initial page size - large for normal text, cell size for automatic line breaks
+
+ Size aPaperSize( 1000000, 1000000 );
+ // call GetOutputArea with nNeeded=0, to get only the cell width
+
+ //! handle nArrY == 0
+ GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, 0,
+ *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
+ rParam.mbCellIsValue, true, false, aAreaParam );
+
+ //! special ScEditUtil handling if formatting for printer
+ rParam.calcPaperSize(aPaperSize, aAreaParam.maAlignRect, mnPPTX, mnPPTY);
+
+ if (rParam.mbPixelToLogic)
+ {
+ Size aLogicSize = mpRefDevice->PixelToLogic(aPaperSize);
+ if ( rParam.mbBreak && !rParam.mbAsianVertical && mpRefDevice != pFmtDevice )
+ {
+ // #i85342# screen display and formatting for printer,
+ // use same GetEditArea call as in ScViewData::SetEditEngine
+
+ Fraction aFract(1,1);
+ tools::Rectangle aUtilRect = ScEditUtil( mpDoc, rParam.mnCellX, rParam.mnCellY, nTab, Point(0,0), pFmtDevice,
+ HMM_PER_TWIPS, HMM_PER_TWIPS, aFract, aFract ).GetEditArea( rParam.mpPattern, false );
+ aLogicSize.setWidth( aUtilRect.GetWidth() );
+ }
+ rParam.mpEngine->SetPaperSize(aLogicSize);
+ }
+ else
+ rParam.mpEngine->SetPaperSize(aPaperSize);
+
+ // Fill the EditEngine (cell attributes and text)
+
+ // default alignment for asian vertical mode is top-right
+ if ( rParam.meVerJust == SvxCellVerJustify::Standard )
+ rParam.meVerJust = SvxCellVerJustify::Top;
+
+ rParam.setPatternToEngine(mbUseStyleColor);
+ rParam.setAlignmentToEngine();
+
+ // Read content from cell
+
+ bool bWrapFields = false;
+ if (!rParam.readCellContent(mpDoc, mbShowNullValues, mbShowFormulas, mbSyntaxMode, mbUseStyleColor, mbForceAutoColor, bWrapFields))
+ // Failed to read cell content. Bail out.
+ return;
+
+ if ( mbSyntaxMode )
+ SetEditSyntaxColor( *rParam.mpEngine, rParam.maCell );
+ else if ( mbUseStyleColor && mbForceAutoColor )
+ lcl_SetEditColor( *rParam.mpEngine, COL_AUTO ); //! or have a flag at EditEngine
+
+ rParam.mpEngine->SetUpdateLayout( true ); // after SetText, before CalcTextWidth/GetTextHeight
+
+ // Get final output area using the calculated width
+
+ tools::Long nEngineWidth, nEngineHeight;
+ rParam.getEngineSize(rParam.mpEngine, nEngineWidth, nEngineHeight);
+
+ tools::Long nNeededPixel = nEngineWidth;
+ if (rParam.mbPixelToLogic)
+ nNeededPixel = mpRefDevice->LogicToPixel(Size(nNeededPixel,0)).Width();
+ nNeededPixel += nLeftM + nRightM;
+
+ // for break, the first GetOutputArea call is sufficient
+ GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, nNeededPixel,
+ *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
+ rParam.mbCellIsValue || bShrink, false, false, aAreaParam );
+
+ if ( bShrink )
+ {
+ ShrinkEditEngine( *rParam.mpEngine, aAreaParam.maAlignRect,
+ nLeftM, nTopM, nRightM, nBottomM, false,
+ rParam.meOrient, 0_deg100, rParam.mbPixelToLogic,
+ nEngineWidth, nEngineHeight, nNeededPixel,
+ aAreaParam.mbLeftClip, aAreaParam.mbRightClip );
+ }
+ if ( rParam.mbCellIsValue && ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) )
+ {
+ nEngineWidth = SetEngineTextAndGetWidth( rParam, "###", nNeededPixel, ( nLeftM + nRightM ) );
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+ ScCellInfo* pClipMarkCell = &rParam.mpThisRowInfo->cellInfo(rParam.mnX);
+ SetClipMarks( aAreaParam, pClipMarkCell, eOutHorJust, nLayoutSign );
+ }
+
+ if (eOutHorJust != SvxCellHorJustify::Left)
+ {
+ aPaperSize.setWidth( nNeededPixel + 1 );
+ if (rParam.mbPixelToLogic)
+ rParam.mpEngine->SetPaperSize(mpRefDevice->PixelToLogic(aPaperSize));
+ else
+ rParam.mpEngine->SetPaperSize(aPaperSize);
+ }
+
+ tools::Long nStartX = aAreaParam.maAlignRect.Left();
+ tools::Long nStartY = aAreaParam.maAlignRect.Top();
+ tools::Long nCellWidth = aAreaParam.maAlignRect.GetWidth();
+ tools::Long nOutWidth = nCellWidth - 1 - nLeftM - nRightM;
+ tools::Long nOutHeight = aAreaParam.maAlignRect.GetHeight() - nTopM - nBottomM;
+
+ // text with automatic breaks is aligned only within the
+ // edit engine's paper size, the output of the whole area
+ // is always left-aligned
+
+ nStartX += nLeftM;
+
+ bool bOutside = (aAreaParam.maClipRect.Right() < nScrX || aAreaParam.maClipRect.Left() >= nScrX + nScrW);
+ if (bOutside)
+ return;
+
+ // Also take fields in a cell with automatic breaks into account: clip to cell width
+ bool bClip = AdjustAreaParamClipRect(aAreaParam) || aAreaParam.mbLeftClip || aAreaParam.mbRightClip || bWrapFields;
+ bool bSimClip = false;
+
+ Size aCellSize; // output area, excluding margins, in logical units
+ if (rParam.mbPixelToLogic)
+ aCellSize = mpRefDevice->PixelToLogic( Size( nOutWidth, nOutHeight ) );
+ else
+ aCellSize = Size( nOutWidth, nOutHeight );
+
+ if ( nEngineHeight >= aCellSize.Height() + aRefOne.Height() )
+ {
+ const ScMergeAttr* pMerge = &rParam.mpPattern->GetItem(ATTR_MERGE);
+ bool bMerged = pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1;
+
+ // Don't clip for text height when printing rows with optimal height,
+ // except when font size is from conditional formatting.
+ //! Allow clipping when vertically merged?
+ if ( eType != OUTTYPE_PRINTER ||
+ ( mpDoc->GetRowFlags( rParam.mnCellY, nTab ) & CRFlags::ManualSize ) ||
+ ( rParam.mpCondSet && SfxItemState::SET ==
+ rParam.mpCondSet->GetItemState(ATTR_FONT_HEIGHT) ) )
+ bClip = true;
+ else
+ bSimClip = true;
+
+ // Show clip marks if height is at least 5pt too small and
+ // there are several lines of text.
+ // Not for asian vertical text, because that would interfere
+ // with the default right position of the text.
+ // Only with automatic line breaks, to avoid having to find
+ // the cells with the horizontal end of the text again.
+ if ( nEngineHeight - aCellSize.Height() > 100 &&
+ ( rParam.mbBreak || rParam.meOrient == SvxCellOrientation::Stacked ) &&
+ !rParam.mbAsianVertical && bMarkClipped &&
+ ( rParam.mpEngine->GetParagraphCount() > 1 || rParam.mpEngine->GetLineCount(0) > 1 ) )
+ {
+ ScCellInfo* pClipMarkCell = nullptr;
+ if ( bMerged )
+ {
+ // anywhere in the merged area...
+ SCCOL nClipX = ( rParam.mnX < nX1 ) ? nX1 : rParam.mnX;
+ pClipMarkCell = &pRowInfo[(rParam.mnArrY != 0) ? rParam.mnArrY : 1].cellInfo(nClipX);
+ }
+ else
+ pClipMarkCell = &rParam.mpThisRowInfo->cellInfo(rParam.mnX);
+
+ pClipMarkCell->nClipMark |= ScClipMark::Right; //! also allow left?
+ bAnyClipped = true;
+
+ tools::Long nMarkPixel = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTX );
+ if ( aAreaParam.maClipRect.Right() - nMarkPixel > aAreaParam.maClipRect.Left() )
+ aAreaParam.maClipRect.AdjustRight( -nMarkPixel );
+ }
+ }
+
+ Point aURLStart;
+
+ { // Clip marks are already handled in GetOutputArea
+ ClearableClipRegion aClip(rParam.mbPixelToLogic ? mpRefDevice->PixelToLogic(aAreaParam.maClipRect)
+ : aAreaParam.maClipRect, bClip, bSimClip, mpDev, bMetaFile);
+
+ Point aLogicStart;
+ if (rParam.mbPixelToLogic)
+ aLogicStart = mpRefDevice->PixelToLogic( Point(nStartX,nStartY) );
+ else
+ aLogicStart = Point(nStartX, nStartY);
+
+ tools::Long nAvailWidth = aCellSize.Width();
+ // space for AutoFilter is already handled in GetOutputArea
+
+ // horizontal alignment
+
+ if (rParam.meHorJustResult==SvxCellHorJustify::Right)
+ aLogicStart.AdjustX(nAvailWidth - nEngineWidth );
+ else if (rParam.meHorJustResult==SvxCellHorJustify::Center)
+ aLogicStart.AdjustX((nAvailWidth - nEngineWidth) / 2 );
+
+ // paper size is subtracted below
+ aLogicStart.AdjustX(nEngineWidth );
+
+ // vertical adjustment is within the EditEngine
+ if (rParam.mbPixelToLogic)
+ aLogicStart.AdjustY(mpRefDevice->PixelToLogic(Size(0,nTopM)).Height() );
+ else
+ aLogicStart.AdjustY(nTopM );
+
+ aURLStart = aLogicStart; // copy before modifying for orientation
+
+ // bMoveClipped handling has been replaced by complete alignment
+ // handling (also extending to the left).
+
+ // with SetVertical, the start position is top left of
+ // the whole output area, not the text itself
+ aLogicStart.AdjustX( -(rParam.mpEngine->GetPaperSize().Width()) );
+
+ rParam.mpEngine->Draw(*mpDev, aLogicStart);
+ }
+
+ rParam.adjustForHyperlinkInPDF(aURLStart, mpDev);
+}
+
+void ScOutputData::DrawEdit(bool bPixelToLogic)
+{
+ InitOutputEditEngine();
+
+ bool bHyphenatorSet = false;
+ const ScPatternAttr* pOldPattern = nullptr;
+ const SfxItemSet* pOldCondSet = nullptr;
+ const SfxItemSet* pOldPreviewFontSet = nullptr;
+ ScRefCellValue aCell;
+
+ tools::Long nInitPosX = nScrX;
+ if ( bLayoutRTL )
+ {
+ nInitPosX += nMirrorW - 1;
+ }
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ SCCOL nLastContentCol = mpDoc->MaxCol();
+ if ( nX2 < mpDoc->MaxCol() )
+ {
+ SCROW nEndRow;
+ mpDoc->GetCellArea(nTab, nLastContentCol, nEndRow);
+ }
+
+ tools::Long nRowPosY = nScrY;
+ for (SCSIZE nArrY=0; nArrY+1<nArrCount; nArrY++) // 0 of the rest of the merged
+ {
+ RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+
+ if (nArrY==1) nRowPosY = nScrY; // positions before are calculated individually
+
+ if ( pThisRowInfo->bChanged || nArrY==0 )
+ {
+ tools::Long nPosX = 0;
+ for (SCCOL nX=0; nX<=nX2; nX++) // due to overflow
+ {
+ std::unique_ptr< ScPatternAttr > pPreviewPattr;
+ if (nX==nX1) nPosX = nInitPosX; // positions before nX1 are calculated individually
+
+ if (pThisRowInfo->basicCellInfo(nX).bEditEngine)
+ {
+ SCROW nY = pThisRowInfo->nRowNo;
+
+ SCCOL nCellX = nX; // position where the cell really starts
+ SCROW nCellY = nY;
+ bool bDoCell = false;
+
+ tools::Long nPosY = nRowPosY;
+ if ( nArrY == 0 )
+ {
+ nPosY = nScrY;
+ nY = pRowInfo[1].nRowNo;
+ SCCOL nOverX; // start of the merged cells
+ SCROW nOverY;
+ if (GetMergeOrigin( nX,nY, 1, nOverX,nOverY, true ))
+ {
+ nCellX = nOverX;
+ nCellY = nOverY;
+ bDoCell = true;
+ }
+ }
+ else if ( nX == nX2 && pThisRowInfo->cellInfo(nX).maCell.isEmpty() )
+ {
+ // Rest of a long text further to the right?
+
+ SCCOL nTempX=nX;
+ while (nTempX < nLastContentCol && IsEmptyCellText( pThisRowInfo, nTempX, nY ))
+ ++nTempX;
+
+ if ( nTempX > nX &&
+ !IsEmptyCellText( pThisRowInfo, nTempX, nY ) &&
+ !mpDoc->HasAttrib( nTempX,nY,nTab, nX,nY,nTab, HasAttrFlags::Merged | HasAttrFlags::Overlapped ) )
+ {
+ nCellX = nTempX;
+ bDoCell = true;
+ }
+ }
+ else
+ {
+ bDoCell = true;
+ }
+
+ if ( bDoCell && bEditMode && nCellX == nEditCol && nCellY == nEditRow )
+ bDoCell = false;
+
+ const ScPatternAttr* pPattern = nullptr;
+ const SfxItemSet* pCondSet = nullptr;
+ if (bDoCell)
+ {
+ if ( nCellY == nY && nCellX >= nX1 && nCellX <= nX2 &&
+ !mpDoc->ColHidden(nCellX, nTab) )
+ {
+ ScCellInfo& rCellInfo = pThisRowInfo->cellInfo(nCellX);
+ pPattern = rCellInfo.pPatternAttr;
+ pCondSet = rCellInfo.pConditionSet;
+ aCell = rCellInfo.maCell;
+ }
+ else // get from document
+ {
+ pPattern = mpDoc->GetPattern( nCellX, nCellY, nTab );
+ pCondSet = mpDoc->GetCondResult( nCellX, nCellY, nTab );
+ GetVisibleCell( nCellX, nCellY, nTab, aCell );
+ }
+ if (aCell.isEmpty())
+ bDoCell = false;
+ }
+ if (bDoCell)
+ {
+ if ( mpDoc->GetPreviewCellStyle() )
+ {
+ if ( ScStyleSheet* pPreviewStyle = mpDoc->GetPreviewCellStyle( nCellX, nCellY, nTab ) )
+ {
+ pPreviewPattr.reset( new ScPatternAttr(*pPattern) );
+ pPreviewPattr->SetStyleSheet(pPreviewStyle);
+ pPattern = pPreviewPattr.get();
+ }
+ }
+ SfxItemSet* pPreviewFontSet = mpDoc->GetPreviewFont( nCellX, nCellY, nTab );
+ lcl_ClearEdit( *mxOutputEditEngine ); // also calls SetUpdateMode(sal_False)
+
+ // fdo#32530: Check if the first character is RTL.
+ OUString aStr = mpDoc->GetString(nCellX, nCellY, nTab);
+
+ DrawEditParam aParam(pPattern, pCondSet, lcl_SafeIsValue(aCell));
+ const bool bNumberFormatIsText = lcl_isNumberFormatText( mpDoc, nCellX, nCellY, nTab );
+ aParam.meHorJustContext = getAlignmentFromContext( aParam.meHorJustAttr,
+ aParam.mbCellIsValue, aStr, *pPattern, pCondSet, mpDoc, nTab, bNumberFormatIsText);
+ aParam.meHorJustResult = (aParam.meHorJustAttr == SvxCellHorJustify::Block) ?
+ SvxCellHorJustify::Block : aParam.meHorJustContext;
+ aParam.mbPixelToLogic = bPixelToLogic;
+ aParam.mbHyphenatorSet = bHyphenatorSet;
+ aParam.mpEngine = mxOutputEditEngine.get();
+ aParam.maCell = aCell;
+ aParam.mnArrY = nArrY;
+ aParam.mnX = nX;
+ aParam.mnCellX = nCellX;
+ aParam.mnCellY = nCellY;
+ aParam.mnPosX = nPosX;
+ aParam.mnPosY = nPosY;
+ aParam.mnInitPosX = nInitPosX;
+ aParam.mpPreviewFontSet = pPreviewFontSet;
+ aParam.mpOldPattern = pOldPattern;
+ aParam.mpOldCondSet = pOldCondSet;
+ aParam.mpOldPreviewFontSet = pOldPreviewFontSet;
+ aParam.mpThisRowInfo = pThisRowInfo;
+ if (mpSpellCheckCxt)
+ aParam.mpMisspellRanges = mpSpellCheckCxt->getMisspellRanges(nCellX, nCellY);
+
+ if (aParam.meHorJustAttr == SvxCellHorJustify::Repeat)
+ {
+ // ignore orientation/rotation if "repeat" is active
+ aParam.meOrient = SvxCellOrientation::Standard;
+ }
+ switch (aParam.meOrient)
+ {
+ case SvxCellOrientation::BottomUp:
+ DrawEditBottomTop(aParam);
+ break;
+ case SvxCellOrientation::TopBottom:
+ DrawEditTopBottom(aParam);
+ break;
+ case SvxCellOrientation::Stacked:
+ // this can be vertically stacked or asian vertical.
+ DrawEditStacked(aParam);
+ break;
+ default:
+ DrawEditStandard(aParam);
+ }
+
+ // Retrieve parameters for next iteration.
+ pOldPattern = aParam.mpOldPattern;
+ pOldCondSet = aParam.mpOldCondSet;
+ pOldPreviewFontSet = aParam.mpOldPreviewFontSet;
+ bHyphenatorSet = aParam.mbHyphenatorSet;
+ }
+ }
+ nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
+ }
+ }
+ nRowPosY += pRowInfo[nArrY].nHeight;
+ }
+
+ if (mrTabInfo.maArray.HasCellRotation())
+ {
+ DrawRotated(bPixelToLogic); //! call from outside ?
+ }
+}
+
+void ScOutputData::DrawRotated(bool bPixelToLogic)
+{
+ InitOutputEditEngine();
+ //! store nRotMax
+ SCCOL nRotMax = nX2;
+ for (SCSIZE nRotY=0; nRotY<nArrCount; nRotY++)
+ if (pRowInfo[nRotY].nRotMaxCol != SC_ROTMAX_NONE && pRowInfo[nRotY].nRotMaxCol > nRotMax)
+ nRotMax = pRowInfo[nRotY].nRotMaxCol;
+
+ ScModule* pScMod = SC_MOD();
+ Color nConfBackColor = pScMod->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor;
+ bool bCellContrast = mbUseStyleColor &&
+ Application::GetSettings().GetStyleSettings().GetHighContrastMode();
+
+ bool bHyphenatorSet = false;
+ const ScPatternAttr* pPattern;
+ const SfxItemSet* pCondSet;
+ const ScPatternAttr* pOldPattern = nullptr;
+ const SfxItemSet* pOldCondSet = nullptr;
+ ScRefCellValue aCell;
+
+ tools::Long nInitPosX = nScrX;
+ if ( bLayoutRTL )
+ {
+ nInitPosX += nMirrorW - 1;
+ }
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ tools::Long nRowPosY = nScrY;
+ for (SCSIZE nArrY=0; nArrY+1<nArrCount; nArrY++) // 0 for the rest of the merged
+ {
+ RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+ tools::Long nCellHeight = static_cast<tools::Long>(pThisRowInfo->nHeight);
+ if (nArrY==1) nRowPosY = nScrY; // positions before are calculated individually
+
+ if ( ( pThisRowInfo->bChanged || nArrY==0 ) && pThisRowInfo->nRotMaxCol != SC_ROTMAX_NONE )
+ {
+ tools::Long nPosX = 0;
+ for (SCCOL nX=0; nX<=nRotMax; nX++)
+ {
+ if (nX==nX1) nPosX = nInitPosX; // positions before nX1 are calculated individually
+
+ const ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
+ if ( pInfo->nRotateDir != ScRotateDir::NONE )
+ {
+ SCROW nY = pThisRowInfo->nRowNo;
+
+ bool bHidden = false;
+ if (bEditMode)
+ if ( nX == nEditCol && nY == nEditRow )
+ bHidden = true;
+
+ if (!bHidden)
+ {
+ lcl_ClearEdit( *mxOutputEditEngine ); // also calls SetUpdateMode(sal_False)
+
+ tools::Long nPosY = nRowPosY;
+
+ //! rest from merged cells further up do not work!
+
+ bool bFromDoc = false;
+ pPattern = pInfo->pPatternAttr;
+ pCondSet = pInfo->pConditionSet;
+ if (!pPattern)
+ {
+ pPattern = mpDoc->GetPattern( nX, nY, nTab );
+ bFromDoc = true;
+ }
+ aCell = pInfo->maCell;
+ if (bFromDoc)
+ pCondSet = mpDoc->GetCondResult( nX, nY, nTab );
+
+ if (aCell.isEmpty() && nX>nX2)
+ GetVisibleCell( nX, nY, nTab, aCell );
+
+ if (aCell.isEmpty() || IsEmptyCellText(pThisRowInfo, nX, nY))
+ bHidden = true; // nRotateDir is also set without a cell
+
+ tools::Long nCellWidth = static_cast<tools::Long>(pRowInfo[0].basicCellInfo(nX).nWidth);
+
+ SvxCellHorJustify eHorJust =
+ pPattern->GetItem(ATTR_HOR_JUSTIFY, pCondSet).GetValue();
+ bool bBreak = ( eHorJust == SvxCellHorJustify::Block ) ||
+ pPattern->GetItem(ATTR_LINEBREAK, pCondSet).GetValue();
+ bool bRepeat = ( eHorJust == SvxCellHorJustify::Repeat && !bBreak );
+ bool bShrink = !bBreak && !bRepeat &&
+ pPattern->GetItem( ATTR_SHRINKTOFIT, pCondSet ).GetValue();
+ SvxCellOrientation eOrient = pPattern->GetCellOrientation( pCondSet );
+
+ const ScMergeAttr* pMerge = &pPattern->GetItem(ATTR_MERGE);
+ bool bMerged = pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1;
+
+ tools::Long nStartX = nPosX;
+ tools::Long nStartY = nPosY;
+ if (nX<nX1)
+ {
+ if ((bBreak || eOrient!=SvxCellOrientation::Standard) && !bMerged)
+ bHidden = true;
+ else
+ {
+ nStartX = nInitPosX;
+ SCCOL nCol = nX1;
+ while (nCol > nX)
+ {
+ --nCol;
+ nStartX -= nLayoutSign * static_cast<tools::Long>(pRowInfo[0].basicCellInfo(nCol).nWidth);
+ }
+ }
+ }
+ tools::Long nCellStartX = nStartX;
+
+ // omit substitute representation of small text
+
+ if (!bHidden)
+ {
+ tools::Long nOutWidth = nCellWidth - 1;
+ tools::Long nOutHeight = nCellHeight;
+
+ if ( bMerged )
+ {
+ SCCOL nCountX = pMerge->GetColMerge();
+ for (SCCOL i=1; i<nCountX; i++)
+ nOutWidth += mpDoc->GetColWidth(nX+i,nTab) * mnPPTX;
+ SCROW nCountY = pMerge->GetRowMerge();
+ nOutHeight += mpDoc->GetScaledRowHeight( nY+1, nY+nCountY-1, nTab, mnPPTY);
+ }
+
+ SvxCellVerJustify eVerJust =
+ pPattern->GetItem(ATTR_VER_JUSTIFY, pCondSet).GetValue();
+
+ // syntax mode is ignored here...
+
+ // StringDiffer doesn't look at hyphenate, language items
+ if ( !SfxPoolItem::areSame(pPattern, pOldPattern) || pCondSet != pOldCondSet )
+ {
+ auto pSet = std::make_unique<SfxItemSet>( mxOutputEditEngine->GetEmptyItemSet() );
+ pPattern->FillEditItemSet( pSet.get(), pCondSet );
+
+ // adjustment for EditEngine
+ SvxAdjust eSvxAdjust = SvxAdjust::Left;
+ if (eOrient==SvxCellOrientation::Stacked)
+ eSvxAdjust = SvxAdjust::Center;
+ // adjustment for bBreak is omitted here
+ pSet->Put( SvxAdjustItem( eSvxAdjust, EE_PARA_JUST ) );
+
+ bool bParaHyphenate = pSet->Get(EE_PARA_HYPHENATE).GetValue();
+ mxOutputEditEngine->SetDefaults( std::move(pSet) );
+ pOldPattern = pPattern;
+ pOldCondSet = pCondSet;
+
+ EEControlBits nControl = mxOutputEditEngine->GetControlWord();
+ if (eOrient==SvxCellOrientation::Stacked)
+ nControl |= EEControlBits::ONECHARPERLINE;
+ else
+ nControl &= ~EEControlBits::ONECHARPERLINE;
+ mxOutputEditEngine->SetControlWord( nControl );
+
+ if ( !bHyphenatorSet && bParaHyphenate )
+ {
+ // set hyphenator the first time it is needed
+ css::uno::Reference<css::linguistic2::XHyphenator> xXHyphenator( LinguMgr::GetHyphenator() );
+ mxOutputEditEngine->SetHyphenator( xXHyphenator );
+ bHyphenatorSet = true;
+ }
+
+ Color aBackCol =
+ pPattern->GetItem( ATTR_BACKGROUND, pCondSet ).GetColor();
+ if ( mbUseStyleColor && ( aBackCol.IsTransparent() || bCellContrast ) )
+ aBackCol = nConfBackColor;
+ mxOutputEditEngine->SetBackgroundColor( aBackCol );
+ }
+
+ // margins
+
+ //! change position and paper size to EditUtil !!!
+
+ const SvxMarginItem* pMargin =
+ &pPattern->GetItem(ATTR_MARGIN, pCondSet);
+ sal_uInt16 nIndent = 0;
+ if ( eHorJust == SvxCellHorJustify::Left )
+ nIndent = pPattern->GetItem(ATTR_INDENT, pCondSet).GetValue();
+
+ tools::Long nTotalHeight = nOutHeight; // without subtracting the margin
+ if ( bPixelToLogic )
+ nTotalHeight = mpRefDevice->PixelToLogic(Size(0,nTotalHeight)).Height();
+
+ tools::Long nLeftM = static_cast<tools::Long>( (pMargin->GetLeftMargin() + nIndent) * mnPPTX );
+ tools::Long nTopM = static_cast<tools::Long>( pMargin->GetTopMargin() * mnPPTY );
+ tools::Long nRightM = static_cast<tools::Long>( pMargin->GetRightMargin() * mnPPTX );
+ tools::Long nBottomM = static_cast<tools::Long>( pMargin->GetBottomMargin() * mnPPTY );
+ nStartX += nLeftM;
+ nStartY += nTopM;
+ nOutWidth -= nLeftM + nRightM;
+ nOutHeight -= nTopM + nBottomM;
+
+ // rotate here already, to adjust paper size for page breaks
+ Degree100 nAttrRotate;
+ double nSin = 0.0;
+ double nCos = 1.0;
+ SvxRotateMode eRotMode = SVX_ROTATE_MODE_STANDARD;
+ if ( eOrient == SvxCellOrientation::Standard )
+ {
+ nAttrRotate = pPattern->
+ GetItem(ATTR_ROTATE_VALUE, pCondSet).GetValue();
+ if ( nAttrRotate )
+ {
+ eRotMode = pPattern->GetItem(ATTR_ROTATE_MODE, pCondSet).GetValue();
+
+ // tdf#143377 To use the same limits to avoid too big Skew
+ // with TextOrientation in Calc, use 1/2 degree here, too.
+ // This equals '50' in the notation here (100th degree)
+ static const sal_Int32 nMinRad(50);
+
+ // bring nAttrRotate to the range [0..36000[
+ nAttrRotate = Degree100(((nAttrRotate.get() % 36000) + 36000) % 36000);
+
+ // check for to be avoided extreme values and correct
+ if (nAttrRotate < Degree100(nMinRad))
+ {
+ // range [0..50]
+ nAttrRotate = Degree100(nMinRad);
+ eRotMode = SVX_ROTATE_MODE_STANDARD; // no overflow
+ }
+ else if (nAttrRotate > Degree100(36000 - nMinRad))
+ {
+ // range [35950..36000[
+ nAttrRotate = Degree100(36000 - nMinRad);
+ eRotMode = SVX_ROTATE_MODE_STANDARD; // no overflow
+ }
+ else if (nAttrRotate > Degree100(18000 - nMinRad) && (nAttrRotate < Degree100(18000 + nMinRad)))
+ {
+ // range 50 around 18000, [17950..18050]
+ nAttrRotate = (nAttrRotate > Degree100(18000))
+ ? Degree100(18000 + nMinRad)
+ : Degree100(18000 - nMinRad);
+ eRotMode = SVX_ROTATE_MODE_STANDARD; // no overflow
+ }
+
+ if ( bLayoutRTL )
+ {
+ // keep in range [0..36000[
+ nAttrRotate = Degree100(36000 - nAttrRotate.get());
+ }
+
+ double nRealOrient = toRadians(nAttrRotate); // 1/100 degree
+ nCos = cos( nRealOrient );
+
+ // tdf#143377 new strategy: instead of using zero for nSin, which
+ // would be the *correct* value, continue with the corrected maximum
+ // allowed value which is then *not* zero. This is similar to
+ // the behaviour before where (just due to numerical unprecisions)
+ // nSin was also not zero (pure coincidence), but very close to it.
+ // I checked and tried to make safe all places below that use
+ // nSin and divide by it, but there is too much going on and that
+ // would not be safe, so rely on the same values as before, but
+ // now numerically limited to not get the Skew go havoc
+ nSin = sin( nRealOrient );
+ }
+ }
+
+ Size aPaperSize( 1000000, 1000000 );
+ if (eOrient==SvxCellOrientation::Stacked)
+ aPaperSize.setWidth( nOutWidth ); // to center
+ else if (bBreak)
+ {
+ if (nAttrRotate)
+ {
+ //! the correct paper size for break depends on the number
+ //! of rows, as long as the rows can not be outputted individually
+ //! offsetted -> therefore unlimited, so no wrapping.
+ //! With offset rows the following would be correct:
+ aPaperSize.setWidth( static_cast<tools::Long>(nOutHeight / fabs(nSin)) );
+ }
+ else if (eOrient == SvxCellOrientation::Standard)
+ aPaperSize.setWidth( nOutWidth );
+ else
+ aPaperSize.setWidth( nOutHeight - 1 );
+ }
+ if (bPixelToLogic)
+ mxOutputEditEngine->SetPaperSize(mpRefDevice->PixelToLogic(aPaperSize));
+ else
+ mxOutputEditEngine->SetPaperSize(aPaperSize); // scale is always 1
+
+ // read data from cell
+
+ if (aCell.getType() == CELLTYPE_EDIT)
+ {
+ if (aCell.getEditText())
+ mxOutputEditEngine->SetTextCurrentDefaults(*aCell.getEditText());
+ else
+ {
+ OSL_FAIL("pData == 0");
+ }
+ }
+ else
+ {
+ sal_uInt32 nFormat = pPattern->GetNumberFormat(
+ mpDoc->GetFormatTable(), pCondSet );
+ const Color* pColor;
+ OUString aString = ScCellFormat::GetString( aCell,
+ nFormat, &pColor,
+ *mpDoc->GetFormatTable(),
+ *mpDoc,
+ mbShowNullValues,
+ mbShowFormulas);
+
+ mxOutputEditEngine->SetTextCurrentDefaults(aString);
+ if ( pColor && !mbSyntaxMode && !( mbUseStyleColor && mbForceAutoColor ) )
+ lcl_SetEditColor( *mxOutputEditEngine, *pColor );
+ }
+
+ if ( mbSyntaxMode )
+ {
+ SetEditSyntaxColor(*mxOutputEditEngine, aCell);
+ }
+ else if ( mbUseStyleColor && mbForceAutoColor )
+ lcl_SetEditColor( *mxOutputEditEngine, COL_AUTO ); //! or have a flag at EditEngine
+
+ mxOutputEditEngine->SetUpdateLayout( true ); // after SetText, before CalcTextWidth/GetTextHeight
+
+ tools::Long nEngineWidth = static_cast<tools::Long>(mxOutputEditEngine->CalcTextWidth());
+ tools::Long nEngineHeight = mxOutputEditEngine->GetTextHeight();
+
+ if (nAttrRotate && bBreak)
+ {
+ double nAbsCos = fabs( nCos );
+ double nAbsSin = fabs( nSin );
+
+ // adjust width of papersize for height of text
+ int nSteps = 5;
+ while (nSteps > 0)
+ {
+ // everything is in pixels
+ tools::Long nEnginePixel = mpRefDevice->LogicToPixel(
+ Size(0,nEngineHeight)).Height();
+ tools::Long nEffHeight = nOutHeight - static_cast<tools::Long>(nEnginePixel * nAbsCos) + 2;
+ tools::Long nNewWidth = static_cast<tools::Long>(nEffHeight / nAbsSin) + 2;
+ bool bFits = ( nNewWidth >= aPaperSize.Width() );
+ if ( bFits )
+ nSteps = 0;
+ else
+ {
+ if ( nNewWidth < 4 )
+ {
+ // can't fit -> fall back to using half height
+ nEffHeight = nOutHeight / 2;
+ nNewWidth = static_cast<tools::Long>(nEffHeight / nAbsSin) + 2;
+ nSteps = 0;
+ }
+ else
+ --nSteps;
+
+ // set paper width and get new text height
+ aPaperSize.setWidth( nNewWidth );
+ if (bPixelToLogic)
+ mxOutputEditEngine->SetPaperSize(mpRefDevice->PixelToLogic(aPaperSize));
+ else
+ mxOutputEditEngine->SetPaperSize(aPaperSize); // Scale is always 1
+ //mxOutputEditEngine->QuickFormatDoc( sal_True );
+
+ nEngineWidth = static_cast<tools::Long>(mxOutputEditEngine->CalcTextWidth());
+ nEngineHeight = mxOutputEditEngine->GetTextHeight();
+ }
+ }
+ }
+
+ tools::Long nRealWidth = nEngineWidth;
+ tools::Long nRealHeight = nEngineHeight;
+
+ // when rotated, adjust size
+ if (nAttrRotate)
+ {
+ double nAbsCos = fabs( nCos );
+ double nAbsSin = fabs( nSin );
+
+ if ( eRotMode == SVX_ROTATE_MODE_STANDARD )
+ nEngineWidth = static_cast<tools::Long>( nRealWidth * nAbsCos +
+ nRealHeight * nAbsSin );
+ else
+ nEngineWidth = static_cast<tools::Long>( nRealHeight / nAbsSin );
+ //! limit !!!
+
+ nEngineHeight = static_cast<tools::Long>( nRealHeight * nAbsCos +
+ nRealWidth * nAbsSin );
+ }
+
+ if (!nAttrRotate) // only rotated text here
+ bHidden = true; //! check first !!!
+
+ //! omit which doesn't stick out
+
+ if (!bHidden)
+ {
+ Size aClipSize( nScrX+nScrW-nStartX, nScrY+nScrH-nStartY );
+
+ // go on writing
+
+ Size aCellSize;
+ if (bPixelToLogic)
+ aCellSize = mpRefDevice->PixelToLogic( Size( nOutWidth, nOutHeight ) );
+ else
+ aCellSize = Size( nOutWidth, nOutHeight ); // scale is one
+
+ tools::Long nGridWidth = nEngineWidth;
+ bool bNegative = false;
+ if ( eRotMode != SVX_ROTATE_MODE_STANDARD )
+ {
+ nGridWidth = aCellSize.Width() +
+ std::abs(static_cast<tools::Long>( aCellSize.Height() * nCos / nSin ));
+ bNegative = ( pInfo->nRotateDir == ScRotateDir::Left );
+ if ( bLayoutRTL )
+ bNegative = !bNegative;
+ }
+
+ // use GetOutputArea to hide the grid
+ // (clip region is done manually below)
+ OutputAreaParam aAreaParam;
+
+ SCCOL nCellX = nX;
+ SCROW nCellY = nY;
+ SvxCellHorJustify eOutHorJust = eHorJust;
+ if ( eRotMode != SVX_ROTATE_MODE_STANDARD )
+ eOutHorJust = bNegative ? SvxCellHorJustify::Right : SvxCellHorJustify::Left;
+ tools::Long nNeededWidth = nGridWidth; // in pixel for GetOutputArea
+ if ( bPixelToLogic )
+ nNeededWidth = mpRefDevice->LogicToPixel(Size(nNeededWidth,0)).Width();
+
+ GetOutputArea( nX, nArrY, nCellStartX, nPosY, nCellX, nCellY, nNeededWidth,
+ *pPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
+ false, false, true, aAreaParam );
+
+ if ( bShrink )
+ {
+ tools::Long nPixelWidth = bPixelToLogic ?
+ mpRefDevice->LogicToPixel(Size(nEngineWidth,0)).Width() : nEngineWidth;
+ tools::Long nNeededPixel = nPixelWidth + nLeftM + nRightM;
+
+ aAreaParam.mbLeftClip = aAreaParam.mbRightClip = true;
+
+ // always do height
+ ShrinkEditEngine( *mxOutputEditEngine, aAreaParam.maAlignRect, nLeftM, nTopM, nRightM, nBottomM,
+ false, eOrient, nAttrRotate, bPixelToLogic,
+ nEngineWidth, nEngineHeight, nNeededPixel, aAreaParam.mbLeftClip, aAreaParam.mbRightClip );
+
+ if ( eRotMode == SVX_ROTATE_MODE_STANDARD )
+ {
+ // do width only if rotating within the cell (standard mode)
+ ShrinkEditEngine( *mxOutputEditEngine, aAreaParam.maAlignRect, nLeftM, nTopM, nRightM, nBottomM,
+ true, eOrient, nAttrRotate, bPixelToLogic,
+ nEngineWidth, nEngineHeight, nNeededPixel, aAreaParam.mbLeftClip, aAreaParam.mbRightClip );
+ }
+
+ // nEngineWidth/nEngineHeight is updated in ShrinkEditEngine
+ // (but width is only valid for standard mode)
+ nRealWidth = static_cast<tools::Long>(mxOutputEditEngine->CalcTextWidth());
+ nRealHeight = mxOutputEditEngine->GetTextHeight();
+
+ if ( eRotMode != SVX_ROTATE_MODE_STANDARD )
+ nEngineWidth = static_cast<tools::Long>( nRealHeight / fabs( nSin ) );
+ }
+
+ tools::Long nClipStartX = nStartX;
+ if (nX<nX1)
+ {
+ //! clipping is not needed when on the left side of the window
+
+ if (nStartX<nScrX)
+ {
+ tools::Long nDif = nScrX - nStartX;
+ nClipStartX = nScrX;
+ aClipSize.AdjustWidth( -nDif );
+ }
+ }
+
+ tools::Long nClipStartY = nStartY;
+ if (nArrY==0 && nClipStartY < nRowPosY )
+ {
+ tools::Long nDif = nRowPosY - nClipStartY;
+ nClipStartY = nRowPosY;
+ aClipSize.AdjustHeight( -nDif );
+ }
+
+ if ( nAttrRotate /* && eRotMode != SVX_ROTATE_MODE_STANDARD */ )
+ {
+ // only clip rotated output text at the page border
+ nClipStartX = nScrX;
+ aClipSize.setWidth( nScrW );
+ }
+
+ if (bPixelToLogic)
+ aAreaParam.maClipRect = mpRefDevice->PixelToLogic( tools::Rectangle(
+ Point(nClipStartX,nClipStartY), aClipSize ) );
+ else
+ aAreaParam.maClipRect = tools::Rectangle(Point(nClipStartX, nClipStartY),
+ aClipSize ); // Scale = 1
+
+ if (bMetaFile)
+ {
+ mpDev->Push();
+ mpDev->IntersectClipRegion( aAreaParam.maClipRect );
+ }
+ else
+ mpDev->SetClipRegion( vcl::Region( aAreaParam.maClipRect ) );
+
+ Point aLogicStart;
+ if (bPixelToLogic)
+ aLogicStart = mpRefDevice->PixelToLogic( Point(nStartX,nStartY) );
+ else
+ aLogicStart = Point(nStartX, nStartY);
+ if ( eOrient!=SvxCellOrientation::Standard || !bBreak )
+ {
+ tools::Long nAvailWidth = aCellSize.Width();
+ if (eType==OUTTYPE_WINDOW &&
+ eOrient!=SvxCellOrientation::Stacked &&
+ pInfo->bAutoFilter)
+ {
+ // filter drop-down width depends on row height
+ double fZoom = mpRefDevice ? static_cast<double>(mpRefDevice->GetMapMode().GetScaleY()) : 1.0;
+ fZoom = fZoom > 1.0 ? fZoom : 1.0;
+ if (bPixelToLogic)
+ nAvailWidth -= mpRefDevice->PixelToLogic(Size(0,fZoom * DROPDOWN_BITMAP_SIZE)).Height();
+ else
+ nAvailWidth -= fZoom * DROPDOWN_BITMAP_SIZE;
+ tools::Long nComp = nEngineWidth;
+ if (nAvailWidth<nComp) nAvailWidth=nComp;
+ }
+
+ // horizontal orientation
+
+ if (eOrient==SvxCellOrientation::Standard && !nAttrRotate)
+ {
+ if (eHorJust==SvxCellHorJustify::Right ||
+ eHorJust==SvxCellHorJustify::Center)
+ {
+ mxOutputEditEngine->SetUpdateLayout( false );
+
+ SvxAdjust eSvxAdjust =
+ (eHorJust==SvxCellHorJustify::Right) ?
+ SvxAdjust::Right : SvxAdjust::Center;
+ mxOutputEditEngine->SetDefaultItem(
+ SvxAdjustItem( eSvxAdjust, EE_PARA_JUST ) );
+
+ aPaperSize.setWidth( nOutWidth );
+ if (bPixelToLogic)
+ mxOutputEditEngine->SetPaperSize(mpRefDevice->PixelToLogic(aPaperSize));
+ else
+ mxOutputEditEngine->SetPaperSize(aPaperSize);
+
+ mxOutputEditEngine->SetUpdateLayout( true );
+ }
+ }
+ else
+ {
+ // rotated text is centered by default
+ if (eHorJust==SvxCellHorJustify::Right)
+ aLogicStart.AdjustX(nAvailWidth - nEngineWidth );
+ else if (eHorJust==SvxCellHorJustify::Center ||
+ eHorJust==SvxCellHorJustify::Standard)
+ aLogicStart.AdjustX((nAvailWidth - nEngineWidth) / 2 );
+ }
+ }
+
+ if ( bLayoutRTL )
+ {
+ if (bPixelToLogic)
+ aLogicStart.AdjustX( -(mpRefDevice->PixelToLogic(
+ Size( nCellWidth, 0 ) ).Width()) );
+ else
+ aLogicStart.AdjustX( -nCellWidth );
+ }
+
+ if ( eOrient==SvxCellOrientation::Standard ||
+ eOrient==SvxCellOrientation::Stacked || !bBreak )
+ {
+ if (eVerJust==SvxCellVerJustify::Bottom ||
+ eVerJust==SvxCellVerJustify::Standard)
+ {
+ if (bPixelToLogic)
+ aLogicStart.AdjustY(mpRefDevice->PixelToLogic( Size(0,
+ mpRefDevice->LogicToPixel(aCellSize).Height() -
+ mpRefDevice->LogicToPixel(Size(0,nEngineHeight)).Height()
+ )).Height() );
+ else
+ aLogicStart.AdjustY(aCellSize.Height() - nEngineHeight );
+ }
+
+ else if (eVerJust==SvxCellVerJustify::Center)
+ {
+ if (bPixelToLogic)
+ aLogicStart.AdjustY(mpRefDevice->PixelToLogic( Size(0,(
+ mpRefDevice->LogicToPixel(aCellSize).Height() -
+ mpRefDevice->LogicToPixel(Size(0,nEngineHeight)).Height())
+ / 2)).Height() );
+ else
+ aLogicStart.AdjustY((aCellSize.Height() - nEngineHeight) / 2 );
+ }
+ }
+
+ // TOPBOTTOM and BOTTOMTOP are handled in DrawStrings/DrawEdit
+ OSL_ENSURE( eOrient == SvxCellOrientation::Standard && nAttrRotate,
+ "DrawRotated: no rotation" );
+
+ Degree10 nOriVal = 0_deg10;
+ if ( nAttrRotate )
+ {
+ // attribute is 1/100, Font 1/10 degrees
+ nOriVal = to<Degree10>(nAttrRotate);
+
+ double nAddX = 0.0;
+ double nAddY = 0.0;
+ if ( nCos > 0.0 && eRotMode != SVX_ROTATE_MODE_STANDARD )
+ {
+ //! limit !!!
+ double nH = nRealHeight * nCos;
+ nAddX += nH * ( nCos / fabs(nSin) );
+ }
+ if ( nCos < 0.0 && eRotMode == SVX_ROTATE_MODE_STANDARD )
+ nAddX -= nRealWidth * nCos;
+ if ( nSin < 0.0 )
+ nAddX -= nRealHeight * nSin;
+ if ( nSin > 0.0 )
+ nAddY += nRealWidth * nSin;
+ if ( nCos < 0.0 )
+ nAddY -= nRealHeight * nCos;
+
+ if ( eRotMode != SVX_ROTATE_MODE_STANDARD )
+ {
+ //! limit !!!
+ double nSkew = nTotalHeight * nCos / fabs(nSin);
+ if ( eRotMode == SVX_ROTATE_MODE_CENTER )
+ nAddX -= nSkew * 0.5;
+ if ( ( eRotMode == SVX_ROTATE_MODE_TOP && nSin > 0.0 ) ||
+ ( eRotMode == SVX_ROTATE_MODE_BOTTOM && nSin < 0.0 ) )
+ nAddX -= nSkew;
+
+ tools::Long nUp = 0;
+ if ( eVerJust == SvxCellVerJustify::Center )
+ nUp = ( aCellSize.Height() - nEngineHeight ) / 2;
+ else if ( eVerJust == SvxCellVerJustify::Top )
+ {
+ if ( nSin > 0.0 )
+ nUp = aCellSize.Height() - nEngineHeight;
+ }
+ else // BOTTOM / STANDARD
+ {
+ if ( nSin < 0.0 )
+ nUp = aCellSize.Height() - nEngineHeight;
+ }
+ if ( nUp )
+ nAddX += ( nUp * nCos / fabs(nSin) );
+ }
+
+ aLogicStart.AdjustX(static_cast<tools::Long>(nAddX) );
+ aLogicStart.AdjustY(static_cast<tools::Long>(nAddY) );
+ }
+
+ // bSimClip is not used here (because nOriVal is set)
+
+ mxOutputEditEngine->Draw(*mpDev, aLogicStart, nOriVal);
+
+ if (bMetaFile)
+ mpDev->Pop();
+ else
+ mpDev->SetClipRegion();
+ }
+ }
+ }
+ }
+ nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
+ }
+ }
+ nRowPosY += pRowInfo[nArrY].nHeight;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/output3.cxx b/sc/source/ui/view/output3.cxx
new file mode 100644
index 0000000000..bc6efec654
--- /dev/null
+++ b/sc/source/ui/view/output3.cxx
@@ -0,0 +1,267 @@
+/* -*- 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 .
+ */
+
+#include <sal/config.h>
+
+#include <o3tl/unit_conversion.hxx>
+#include <svx/svdoutl.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdview.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <osl/diagnose.h>
+
+#include <output.hxx>
+#include <drwlayer.hxx>
+#include <document.hxx>
+#include <tabvwsh.hxx>
+
+#include <svx/fmview.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <svx/sdrpagewindow.hxx>
+
+// #i72502#
+Point ScOutputData::PrePrintDrawingLayer(tools::Long nLogStX, tools::Long nLogStY )
+{
+ tools::Rectangle aRect;
+ SCCOL nCol;
+ Point aOffset;
+ tools::Long nLayoutSign(bLayoutRTL ? -1 : 1);
+
+ for (nCol=0; nCol<nX1; nCol++)
+ aOffset.AdjustX( -(mpDoc->GetColWidth( nCol, nTab ) * nLayoutSign) );
+ aOffset.AdjustY( -sal_Int32(mpDoc->GetRowHeight( 0, nY1-1, nTab )) );
+
+ tools::Long nDataWidth = 0;
+ for (nCol=nX1; nCol<=nX2; nCol++)
+ nDataWidth += mpDoc->GetColWidth( nCol, nTab );
+
+ if ( bLayoutRTL )
+ aOffset.AdjustX(nDataWidth );
+
+ aRect.SetLeft( -aOffset.X() );
+ aRect.SetRight( -aOffset.X() );
+ aRect.SetTop( -aOffset.Y() );
+ aRect.SetBottom( -aOffset.Y() );
+
+ Point aMMOffset( aOffset );
+ aMMOffset.setX(o3tl::convert(aMMOffset.X(), o3tl::Length::twip, o3tl::Length::mm100));
+ aMMOffset.setY(o3tl::convert(aMMOffset.Y(), o3tl::Length::twip, o3tl::Length::mm100));
+
+ if (!bMetaFile)
+ aMMOffset += Point( nLogStX, nLogStY );
+
+ for (nCol=nX1; nCol<=nX2; nCol++)
+ aRect.AdjustRight(mpDoc->GetColWidth( nCol, nTab ) );
+ aRect.AdjustBottom(mpDoc->GetRowHeight( nY1, nY2, nTab ) );
+
+ aRect.SetLeft(o3tl::convert(aRect.Left(), o3tl::Length::twip, o3tl::Length::mm100));
+ aRect.SetTop(o3tl::convert(aRect.Top(), o3tl::Length::twip, o3tl::Length::mm100));
+ aRect.SetRight(o3tl::convert(aRect.Right(), o3tl::Length::twip, o3tl::Length::mm100));
+ aRect.SetBottom(o3tl::convert(aRect.Bottom(), o3tl::Length::twip, o3tl::Length::mm100));
+
+ if(pViewShell || pDrawView)
+ {
+ SdrView* pLocalDrawView = pDrawView ? pDrawView : pViewShell->GetScDrawView();
+
+ if(pLocalDrawView)
+ {
+ // #i76114# MapMode has to be set because BeginDrawLayers uses GetPaintRegion
+ MapMode aOldMode = mpDev->GetMapMode();
+ if (!bMetaFile)
+ mpDev->SetMapMode( MapMode( MapUnit::Map100thMM, aMMOffset, aOldMode.GetScaleX(), aOldMode.GetScaleY() ) );
+
+ // #i74769# work with SdrPaintWindow directly
+ // #i76114# pass bDisableIntersect = true, because the intersection of the table area
+ // with the Window's paint region can be empty
+ vcl::Region aRectRegion(aRect);
+ mpTargetPaintWindow = pLocalDrawView->BeginDrawLayers(mpDev, aRectRegion, true);
+ OSL_ENSURE(mpTargetPaintWindow, "BeginDrawLayers: Got no SdrPaintWindow (!)");
+
+ if (!bMetaFile)
+ mpDev->SetMapMode( aOldMode );
+ }
+ }
+
+ return aMMOffset;
+}
+
+// #i72502#
+void ScOutputData::PostPrintDrawingLayer(const Point& rMMOffset) // #i74768#
+{
+ // #i74768# just use offset as in PrintDrawingLayer() to also get the form controls
+ // painted with offset
+ MapMode aOldMode = mpDev->GetMapMode();
+
+ if (!bMetaFile)
+ {
+ mpDev->SetMapMode( MapMode( MapUnit::Map100thMM, rMMOffset, aOldMode.GetScaleX(), aOldMode.GetScaleY() ) );
+ }
+
+ if(pViewShell || pDrawView)
+ {
+ SdrView* pLocalDrawView = pDrawView ? pDrawView : pViewShell->GetScDrawView();
+
+ if(pLocalDrawView)
+ {
+ // #i74769# work with SdrPaintWindow directly
+ pLocalDrawView->EndDrawLayers(*mpTargetPaintWindow, true);
+ mpTargetPaintWindow = nullptr;
+ }
+ }
+
+ // #i74768#
+ if (!bMetaFile)
+ {
+ mpDev->SetMapMode( aOldMode );
+ }
+}
+
+// #i72502#
+void ScOutputData::PrintDrawingLayer(SdrLayerID nLayer, const Point& rMMOffset)
+{
+ bool bHideAllDrawingLayer(false);
+
+ if(pViewShell || pDrawView)
+ {
+ SdrView* pLocalDrawView = pDrawView ? pDrawView : pViewShell->GetScDrawView();
+
+ if(pLocalDrawView)
+ {
+ bHideAllDrawingLayer = pLocalDrawView->getHideOle() && pLocalDrawView->getHideChart()
+ && pLocalDrawView->getHideDraw() && pLocalDrawView->getHideFormControl();
+ }
+ }
+
+ if(bHideAllDrawingLayer || (!mpDoc->GetDrawLayer()))
+ {
+ return;
+ }
+
+ MapMode aOldMode = mpDev->GetMapMode();
+
+ if (!bMetaFile)
+ {
+ mpDev->SetMapMode( MapMode( MapUnit::Map100thMM, rMMOffset, aOldMode.GetScaleX(), aOldMode.GetScaleY() ) );
+ }
+
+ DrawSelectiveObjects( nLayer );
+
+ if (!bMetaFile)
+ {
+ mpDev->SetMapMode( aOldMode );
+ }
+}
+
+void ScOutputData::DrawSelectiveObjects(SdrLayerID nLayer)
+{
+ ScDrawLayer* pModel = mpDoc->GetDrawLayer();
+ if (!pModel)
+ return;
+
+ // #i46362# high contrast mode (and default text direction) must be handled
+ // by the application, so it's still needed when using DrawLayer().
+
+ SdrOutliner& rOutl = pModel->GetDrawOutliner();
+ rOutl.EnableAutoColor( mbUseStyleColor );
+ rOutl.SetDefaultHorizontalTextDirection(
+ mpDoc->GetEditTextDirection( nTab ) );
+
+ // #i69767# The hyphenator must be set (used to be before drawing a text shape with hyphenation).
+ // LinguMgr::GetHyphenator (EditEngine) uses a wrapper now that creates the real hyphenator on demand,
+ // so it's not a performance problem to call UseHyphenator even when it's not needed.
+
+ pModel->UseHyphenator();
+
+ DrawModeFlags nOldDrawMode = mpDev->GetDrawMode();
+ if ( mbUseStyleColor && Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
+ {
+ mpDev->SetDrawMode( nOldDrawMode | DrawModeFlags::SettingsLine | DrawModeFlags::SettingsFill |
+ DrawModeFlags::SettingsText | DrawModeFlags::SettingsGradient );
+ }
+
+ if(pViewShell || pDrawView)
+ {
+ SdrView* pLocalDrawView = pDrawView ? pDrawView : pViewShell->GetScDrawView();
+
+ if(pLocalDrawView)
+ {
+ SdrPageView* pPageView = pLocalDrawView->GetSdrPageView();
+
+ if(pPageView)
+ {
+ if (nullptr != pPageView->FindPageWindow(*mpDev))
+ {
+ // Target OutputDevice is registered for this view
+ // (as it should be), we can just render
+ pPageView->DrawLayer(sal::static_int_cast<SdrLayerID>(nLayer), mpDev);
+ }
+ else if (0 != pPageView->PageWindowCount())
+ {
+ // We need to temporarily make the target OutputDevice being
+ // 'known/registered' in the paint mechanism so that
+ // SdrPageView::DrawLayer can find it.
+ // This situation can occur when someone interprets the
+ // OutputDevice parameter that gets handed over to DrawLayer
+ // (or other SdrPageView repaint methods) to be there to
+ // define a new render target.
+ // This is *not* the case: This parameter is used to
+ // *identify* an already registered target-OutputDevice.
+ // The default is even to call with a nullptr -> that triggers
+ // the repaint for *all* registered OutputDevices/Windows.
+ // Since this is a common and known misinterpretation it
+ // is good to offer workarounds in the code - there are some
+ // already.
+ // For now - use an already existing 'patch mechanism' and
+ // 'smuggle' the unknown/temporary OutputDevice as a
+ // temporary SdrPaintWindow to the SdrPageWindow, that is
+ // not very expensive.
+ // NOTE: Just using the 1st SdrPageWindow here will be OK
+ // in most cases, the splitting of a view is only used
+ // in calc nowadays and should have identical zoom.
+ // Still, trigger a warning...
+ OSL_ENSURE(1 == pPageView->PageWindowCount(),
+ "ScOutputData::DrawSelectiveObjects: More than one SdrPageView, still using 1st one (!)");
+ SdrPageWindow* patchedPageWindow(pPageView->GetPageWindow(0));
+ assert(nullptr != patchedPageWindow && "SdrPageWindow *must* exist when 0 != PageWindowCount()");
+ SdrPaintWindow temporaryPaintWindow(*pLocalDrawView, *mpDev);
+ SdrPaintWindow* previousPaintWindow(patchedPageWindow->patchPaintWindow(temporaryPaintWindow));
+ pPageView->DrawLayer(sal::static_int_cast<SdrLayerID>(nLayer), mpDev);
+ patchedPageWindow->unpatchPaintWindow(previousPaintWindow);
+ }
+ else
+ {
+ // There does not even exist a SdrPageWindow. Still call the
+ // paint to get the paint done, but be aware that this will create
+ // temporary SdrPaintWindow and SdrPageWindow and - due to the
+ // former - will not be able to use the decomposition buffering
+ // in the VC/VOC/OC mechanism. For that reason this might be
+ // somewhat 'expensive'.
+ // You will also get a warning about it (see OSL_FAIL in
+ // SdrPageView::DrawLayer)
+ pPageView->DrawLayer(sal::static_int_cast<SdrLayerID>(nLayer), mpDev);
+ }
+ }
+ }
+ }
+
+ mpDev->SetDrawMode(nOldDrawMode);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/overlayobject.cxx b/sc/source/ui/view/overlayobject.cxx
new file mode 100644
index 0000000000..a564265a95
--- /dev/null
+++ b/sc/source/ui/view/overlayobject.cxx
@@ -0,0 +1,89 @@
+/* -*- 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 .
+ */
+
+#include <overlayobject.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonMarkerPrimitive2D.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <vcl/settings.hxx>
+
+using sdr::overlay::OverlayObject;
+using sdr::overlay::OverlayManager;
+
+#define DASH_UPDATE_INTERVAL 500 // in msec
+
+ScOverlayDashedBorder::ScOverlayDashedBorder(const ::basegfx::B2DRange& rRange, const Color& rColor) :
+ OverlayObject(rColor),
+ mbToggle(true)
+{
+ // tdf#155414 include system "reduce animation" preferences
+ // Allow the system's "reduce animation" preferences to disable the
+ // Calc animated border when copying a selection of cells.
+ mbAllowsAnimation = (officecfg::Office::Common::VCL::AnimationsEnabled::get() && !MiscSettings::GetUseReducedAnimation());
+ maRange = rRange;
+}
+
+ScOverlayDashedBorder::~ScOverlayDashedBorder()
+{
+}
+
+void ScOverlayDashedBorder::Trigger(sal_uInt32 nTime)
+{
+ OverlayManager* pMgr = getOverlayManager();
+ if (pMgr)
+ {
+ SetTime(nTime + DASH_UPDATE_INTERVAL);
+ mbToggle = !mbToggle;
+ pMgr->InsertEvent(*this);
+ objectChange();
+ }
+}
+
+void ScOverlayDashedBorder::stripeDefinitionHasChanged()
+{
+ objectChange();
+}
+
+drawinglayer::primitive2d::Primitive2DContainer ScOverlayDashedBorder::createOverlayObjectPrimitive2DSequence()
+{
+ using ::basegfx::B2DPolygon;
+ using ::basegfx::B2DPolyPolygon;
+
+ OverlayManager* pMgr = getOverlayManager();
+ if (!pMgr)
+ return drawinglayer::primitive2d::Primitive2DContainer();
+
+ basegfx::BColor aColorA = pMgr->getStripeColorA().getBColor();
+ basegfx::BColor aColorB = pMgr->getStripeColorB().getBColor();
+ if (!mbToggle)
+ ::std::swap(aColorA, aColorB);
+
+ const basegfx::B2DPolygon aPoly = basegfx::utils::createPolygonFromRect(maRange);
+ B2DPolyPolygon aPolygon(aPoly);
+ const drawinglayer::primitive2d::Primitive2DReference aReference(
+ new drawinglayer::primitive2d::PolyPolygonMarkerPrimitive2D(
+ std::move(aPolygon), aColorA, aColorB, pMgr->getStripeLengthPixel()));
+
+ return drawinglayer::primitive2d::Primitive2DContainer { aReference };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/pfuncache.cxx b/sc/source/ui/view/pfuncache.cxx
new file mode 100644
index 0000000000..fe563ba961
--- /dev/null
+++ b/sc/source/ui/view/pfuncache.cxx
@@ -0,0 +1,191 @@
+/* -*- 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 .
+ */
+
+#include <tools/multisel.hxx>
+#include <osl/diagnose.h>
+
+#include <pfuncache.hxx>
+#include <printfun.hxx>
+#include <docsh.hxx>
+#include <markdata.hxx>
+#include <prevloc.hxx>
+#include <utility>
+
+ScPrintFuncCache::ScPrintFuncCache( ScDocShell* pD, const ScMarkData& rMark,
+ ScPrintSelectionStatus aStatus ) :
+ aSelection(std::move( aStatus )),
+ pDocSh( pD ),
+ nTotalPages( 0 ),
+ bLocInitialized( false )
+{
+ // page count uses the stored cell widths for the printer anyway,
+ // so ScPrintFunc with the document's printer can be used to count
+
+ SfxPrinter* pPrinter = pDocSh->GetPrinter();
+
+ ScRange aRange;
+ const ScRange* pSelRange = nullptr;
+ if ( rMark.IsMarked() )
+ {
+ aRange = rMark.GetMarkArea();
+ pSelRange = &aRange;
+ }
+
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTabCount = rDoc.GetTableCount();
+
+ // avoid repeated progress bars if row heights for all sheets are needed
+ if ( nTabCount > 1 && rMark.GetSelectCount() == nTabCount )
+ pDocSh->UpdatePendingRowHeights( nTabCount-1, true );
+
+ SCTAB nTab;
+ for ( nTab=0; nTab<nTabCount; nTab++ )
+ {
+ tools::Long nAttrPage = nTab > 0 ? nFirstAttr[nTab-1] : 1;
+
+ tools::Long nThisTab = 0;
+ if ( rMark.GetTableSelect( nTab ) )
+ {
+ ScPrintFunc aFunc( pDocSh, pPrinter, nTab, nAttrPage, 0, pSelRange, &aSelection.GetOptions() );
+ nThisTab = aFunc.GetTotalPages();
+ nFirstAttr.push_back( aFunc.GetFirstPageNo() ); // from page style or previous sheet
+ }
+ else
+ nFirstAttr.push_back( nAttrPage );
+
+ nPages.push_back( nThisTab );
+ nTotalPages += nThisTab;
+ }
+}
+
+ScPrintFuncCache::~ScPrintFuncCache()
+{
+}
+
+void ScPrintFuncCache::InitLocations( const ScMarkData& rMark, OutputDevice* pDev )
+{
+ if ( bLocInitialized )
+ return; // initialize only once
+
+ ScRange aRange;
+ const ScRange* pSelRange = nullptr;
+ if ( rMark.IsMarked() )
+ {
+ aRange = rMark.GetMarkArea();
+ pSelRange = &aRange;
+ }
+
+ tools::Long nRenderer = 0; // 0-based physical page number across sheets
+ tools::Long nTabStart = 0;
+
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (SCTAB nTab : rMark)
+ {
+ if (nTab >= nTabCount)
+ break;
+ ScPrintFunc aFunc( pDev, pDocSh, nTab, nFirstAttr[nTab], nTotalPages, pSelRange, &aSelection.GetOptions() );
+ aFunc.SetRenderFlag( true );
+
+ tools::Long nDisplayStart = GetDisplayStart( nTab );
+
+ for ( tools::Long nPage=0; nPage<nPages[nTab]; nPage++ )
+ {
+ Range aPageRange( nRenderer+1, nRenderer+1 );
+ MultiSelection aPage( aPageRange );
+ aPage.SetTotalRange( Range(0,RANGE_MAX) );
+ aPage.Select( aPageRange );
+
+ ScPreviewLocationData aLocData( &rDoc, pDev );
+ aFunc.DoPrint( aPage, nTabStart, nDisplayStart, false, &aLocData );
+
+ ScRange aCellRange;
+ tools::Rectangle aPixRect;
+ if ( aLocData.GetMainCellRange( aCellRange, aPixRect ) )
+ aLocations.emplace_back( nRenderer, aCellRange, aPixRect );
+
+ ++nRenderer;
+ }
+
+ nTabStart += nPages[nTab];
+ }
+
+ bLocInitialized = true;
+}
+
+bool ScPrintFuncCache::FindLocation( const ScAddress& rCell, ScPrintPageLocation& rLocation ) const
+{
+ auto aIter = std::find_if(aLocations.begin(), aLocations.end(),
+ [&rCell](const ScPrintPageLocation& rLoc) { return rLoc.aCellRange.Contains(rCell); });
+ if (aIter != aLocations.end())
+ {
+ rLocation = *aIter;
+ return true;
+ }
+ return false; // not found
+}
+
+bool ScPrintFuncCache::IsSameSelection( const ScPrintSelectionStatus& rStatus ) const
+{
+ return aSelection == rStatus;
+}
+
+SCTAB ScPrintFuncCache::GetTabForPage( tools::Long nPage ) const
+{
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTabCount = rDoc.GetTableCount();
+ SCTAB nTab = 0;
+ while ( nTab < nTabCount && nPage >= nPages[nTab] )
+ nPage -= nPages[nTab++];
+ if (nTab >= nTabCount)
+ nTab = nTabCount - 1;
+ return nTab;
+}
+
+tools::Long ScPrintFuncCache::GetTabStart( SCTAB nTab ) const
+{
+ tools::Long nRet = 0;
+ const SCTAB maxIndex = std::min(nTab, static_cast<SCTAB>(nPages.size()));
+ for ( SCTAB i=0; i<maxIndex; i++ )
+ nRet += nPages[i];
+ return nRet;
+}
+
+tools::Long ScPrintFuncCache::GetDisplayStart( SCTAB nTab ) const
+{
+ //! merge with lcl_GetDisplayStart in preview?
+
+ tools::Long nDisplayStart = 0;
+ ScDocument& rDoc = pDocSh->GetDocument();
+ for (SCTAB i=0; i<nTab; i++)
+ {
+ if ( rDoc.NeedPageResetAfterTab(i) )
+ nDisplayStart = 0;
+ else
+ {
+ if ( i < static_cast<SCTAB>(nPages.size()) )
+ nDisplayStart += nPages[i];
+ else
+ OSL_FAIL("nPages out of bounds, FIX IT!");
+ }
+ }
+ return nDisplayStart;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/pgbrksh.cxx b/sc/source/ui/view/pgbrksh.cxx
new file mode 100644
index 0000000000..a0cdb21be6
--- /dev/null
+++ b/sc/source/ui/view/pgbrksh.cxx
@@ -0,0 +1,53 @@
+/* -*- 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 .
+ */
+
+#include <sfx2/objface.hxx>
+#include <sfx2/objsh.hxx>
+
+#include <pgbrksh.hxx>
+#include <tabvwsh.hxx>
+#include <document.hxx>
+
+#define ShellClass_ScPageBreakShell
+#include <scslots.hxx>
+
+SFX_IMPL_INTERFACE(ScPageBreakShell, SfxShell)
+
+void ScPageBreakShell::InitInterface_Impl()
+{
+ GetStaticInterface()->RegisterPopupMenu("pagebreak");
+}
+
+ScPageBreakShell::ScPageBreakShell(ScTabViewShell* pViewSh)
+ : SfxShell(pViewSh)
+{
+ SetPool(&pViewSh->GetPool());
+ ScViewData& rViewData = pViewSh->GetViewData();
+ SfxUndoManager* pMgr = rViewData.GetSfxDocShell()->GetUndoManager();
+ SetUndoManager(pMgr);
+ if (!rViewData.GetDocument().IsUndoEnabled())
+ {
+ pMgr->SetMaxUndoActionCount(0);
+ }
+ SetName("PageBreak");
+}
+
+ScPageBreakShell::~ScPageBreakShell() {}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/pivotsh.cxx b/sc/source/ui/view/pivotsh.cxx
new file mode 100644
index 0000000000..b4ef807ffa
--- /dev/null
+++ b/sc/source/ui/view/pivotsh.cxx
@@ -0,0 +1,167 @@
+/* -*- 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 .
+ */
+
+#include <scitems.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/request.hxx>
+#include <svl/whiter.hxx>
+#include <vcl/EnumContext.hxx>
+
+#include <sc.hrc>
+#include <pivotsh.hxx>
+#include <tabvwsh.hxx>
+#include <docsh.hxx>
+#include <document.hxx>
+#include <dpobject.hxx>
+#include <dpshttab.hxx>
+#include <dbdocfun.hxx>
+#include <uiitems.hxx>
+#include <scabstdlg.hxx>
+
+#define ShellClass_ScPivotShell
+#include <scslots.hxx>
+
+
+SFX_IMPL_INTERFACE(ScPivotShell, SfxShell)
+
+void ScPivotShell::InitInterface_Impl()
+{
+ GetStaticInterface()->RegisterPopupMenu("pivot");
+}
+
+ScPivotShell::ScPivotShell( ScTabViewShell* pViewSh ) :
+ SfxShell(pViewSh),
+ pViewShell( pViewSh )
+{
+ SetPool( &pViewSh->GetPool() );
+ ScViewData& rViewData = pViewSh->GetViewData();
+ SfxUndoManager* pMgr = rViewData.GetSfxDocShell()->GetUndoManager();
+ SetUndoManager( pMgr );
+ if ( !rViewData.GetDocument().IsUndoEnabled() )
+ {
+ pMgr->SetMaxUndoActionCount( 0 );
+ }
+ SetName("Pivot");
+ SfxShell::SetContextName(vcl::EnumContext::GetContextName(vcl::EnumContext::Context::Pivot));
+}
+
+ScPivotShell::~ScPivotShell()
+{
+}
+
+void ScPivotShell::Execute( const SfxRequest& rReq )
+{
+ switch ( rReq.GetSlot() )
+ {
+ case SID_PIVOT_RECALC:
+ pViewShell->RecalcPivotTable();
+ break;
+
+ case SID_PIVOT_KILL:
+ pViewShell->DeletePivotTable();
+ break;
+
+ case SID_DP_FILTER:
+ {
+ ScDPObject* pDPObj = GetCurrDPObject();
+ if( pDPObj )
+ {
+ ScQueryParam aQueryParam;
+ SCTAB nSrcTab = 0;
+ const ScSheetSourceDesc* pDesc = pDPObj->GetSheetDesc();
+ OSL_ENSURE( pDesc, "no sheet source for DP filter dialog" );
+ if( pDesc )
+ {
+ aQueryParam = pDesc->GetQueryParam();
+ nSrcTab = pDesc->GetSourceRange().aStart.Tab();
+ }
+
+ ScViewData& rViewData = pViewShell->GetViewData();
+ SfxItemSetFixed<SCITEM_QUERYDATA, SCITEM_QUERYDATA> aArgSet( pViewShell->GetPool() );
+ aArgSet.Put( ScQueryItem( SCITEM_QUERYDATA, &rViewData, &aQueryParam ) );
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScPivotFilterDlg> pDlg(pFact->CreateScPivotFilterDlg(
+ pViewShell->GetFrameWeld(), aArgSet, nSrcTab));
+
+ if( pDlg->Execute() == RET_OK )
+ {
+ ScSheetSourceDesc aNewDesc(&rViewData.GetDocument());
+ if( pDesc )
+ aNewDesc = *pDesc;
+
+ const ScQueryItem& rQueryItem = pDlg->GetOutputItem();
+ aNewDesc.SetQueryParam(rQueryItem.GetQueryData());
+
+ ScDPObject aNewObj( *pDPObj );
+ aNewObj.SetSheetDesc( aNewDesc );
+ ScDBDocFunc aFunc( *rViewData.GetDocShell() );
+ aFunc.DataPilotUpdate( pDPObj, &aNewObj, true, false );
+ rViewData.GetView()->CursorPosChanged(); // shells may be switched
+ }
+ }
+ }
+ break;
+ }
+}
+
+void ScPivotShell::GetState( SfxItemSet& rSet )
+{
+ ScDocShell* pDocSh = pViewShell->GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ bool bDisable = pDocSh->IsReadOnly() || rDoc.GetChangeTrack();
+
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while (nWhich)
+ {
+ switch (nWhich)
+ {
+ case SID_PIVOT_RECALC:
+ case SID_PIVOT_KILL:
+ {
+ //! move ReadOnly check to idl flags
+ if ( bDisable )
+ {
+ rSet.DisableItem( nWhich );
+ }
+ }
+ break;
+ case SID_DP_FILTER:
+ {
+ ScDPObject* pDPObj = GetCurrDPObject();
+ if( bDisable || !pDPObj || !pDPObj->IsSheetData() )
+ rSet.DisableItem( nWhich );
+ }
+ break;
+ }
+ nWhich = aIter.NextWhich();
+ }
+}
+
+ScDPObject* ScPivotShell::GetCurrDPObject()
+{
+ const ScViewData& rViewData = pViewShell->GetViewData();
+ return rViewData.GetDocument().GetDPAtCursor(
+ rViewData.GetCurX(), rViewData.GetCurY(), rViewData.GetTabNo() );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/preview.cxx b/sc/source/ui/view/preview.cxx
new file mode 100644
index 0000000000..33430883da
--- /dev/null
+++ b/sc/source/ui/view/preview.cxx
@@ -0,0 +1,1575 @@
+/* -*- 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 .
+ */
+
+#include <scitems.hxx>
+#include <editeng/eeitem.hxx>
+
+#include <officecfg/Office/Common.hxx>
+#include <svtools/colorcfg.hxx>
+#include <svx/fmview.hxx>
+#include <editeng/sizeitem.hxx>
+#include <svx/svdpagv.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/dispatch.hxx>
+#include <svl/itemset.hxx>
+#include <tools/multisel.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/settings.hxx>
+#include <o3tl/deleter.hxx>
+#include <o3tl/unit_conversion.hxx>
+
+#include <preview.hxx>
+#include <prevwsh.hxx>
+#include <prevloc.hxx>
+#include <docsh.hxx>
+#include <docfunc.hxx>
+#include <printfun.hxx>
+#include <printopt.hxx>
+#include <stlpool.hxx>
+#include <undostyl.hxx>
+#include <drwlayer.hxx>
+#include <scmod.hxx>
+#include <markdata.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <sc.hrc>
+#include <helpids.h>
+#include <AccessibleDocumentPagePreview.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <com/sun/star/accessibility/XAccessible.hpp>
+#include <AccessibilityHints.hxx>
+#include <vcl/svapp.hxx>
+#include <viewutil.hxx>
+#include <docpool.hxx>
+#include <patattr.hxx>
+#include <columnspanset.hxx>
+
+#include <memory>
+
+#define SC_PREVIEW_SHADOWSIZE 2
+
+static tools::Long lcl_GetDisplayStart( SCTAB nTab, const ScDocument* pDoc, std::vector<tools::Long>& nPages )
+{
+ tools::Long nDisplayStart = 0;
+ for (SCTAB i=0; i<nTab; i++)
+ {
+ if ( pDoc->NeedPageResetAfterTab(i) )
+ nDisplayStart = 0;
+ else
+ nDisplayStart += nPages[i];
+ }
+ return nDisplayStart;
+}
+
+ScPreview::ScPreview( vcl::Window* pParent, ScDocShell* pDocSh, ScPreviewShell* pViewSh ) :
+ Window( pParent ),
+ nPageNo( 0 ),
+ nZoom( 100 ),
+ nTabCount( 0 ),
+ nTabsTested( 0 ),
+ nTab( 0 ),
+ nTabPage( 0 ),
+ nTabStart( 0 ),
+ nDisplayStart( 0 ),
+ aDateTime( DateTime::SYSTEM ),
+ nTotalPages( 0 ),
+ pDocShell( pDocSh ),
+ pViewShell( pViewSh ),
+ bInGetState( false ),
+ bValid( false ),
+ bStateValid( false ),
+ bLocationValid( false ),
+ bInPaint( false ),
+ bInSetZoom( false ),
+ bLeftRulerMove( false ),
+ bRightRulerMove( false ),
+ bTopRulerMove( false ),
+ bBottomRulerMove( false ),
+ bHeaderRulerMove( false ),
+ bFooterRulerMove( false ),
+ bLeftRulerChange( false ),
+ bRightRulerChange( false ),
+ bTopRulerChange( false ),
+ bBottomRulerChange( false ),
+ bHeaderRulerChange( false ),
+ bFooterRulerChange( false ),
+ bPageMargin ( false ),
+ bColRulerMove( false ),
+ mbHasEmptyRangeTable(false),
+ nLeftPosition( 0 ),
+ mnScale( 0 ),
+ nColNumberButtonDown( 0 ),
+ nHeaderHeight ( 0 ),
+ nFooterHeight ( 0 )
+{
+ GetOutDev()->SetOutDevViewType( OutDevViewType::PrintPreview );
+ SetBackground();
+
+ SetHelpId( HID_SC_WIN_PREVIEW );
+
+ GetOutDev()->SetDigitLanguage( ScModule::GetOptDigitLanguage() );
+}
+
+ScPreview::~ScPreview()
+{
+ disposeOnce();
+}
+
+void ScPreview::dispose()
+{
+ pDrawView.reset();
+ pLocationData.reset();
+ vcl::Window::dispose();
+}
+
+void ScPreview::UpdateDrawView() // nTab must be right
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScDrawLayer* pModel = rDoc.GetDrawLayer(); // is not 0
+
+ if ( pModel )
+ {
+ SdrPage* pPage = pModel->GetPage(nTab);
+ if ( pDrawView && ( !pDrawView->GetSdrPageView() || pDrawView->GetSdrPageView()->GetPage() != pPage ) )
+ {
+ // convert the displayed Page of drawView (see below) does not work?!?
+ pDrawView.reset();
+ }
+
+ if ( !pDrawView ) // New Drawing?
+ {
+ pDrawView.reset( new FmFormView( *pModel, GetOutDev()) );
+
+ // The DrawView takes over the Design-Mode from the Model
+ // (Settings "In opening Draftmode"), therefore to restore here
+ pDrawView->SetDesignMode();
+ pDrawView->SetPrintPreview();
+ pDrawView->ShowSdrPage(pPage);
+ }
+ }
+ else if ( pDrawView )
+ {
+ pDrawView.reset(); // for this Chart is not needed
+ }
+}
+
+void ScPreview::TestLastPage()
+{
+ if (nPageNo < nTotalPages)
+ return;
+
+ if (nTotalPages)
+ {
+ nPageNo = nTotalPages - 1;
+ nTab = static_cast<SCTAB>(nPages.size()) -1;
+ while (nTab > 0 && !nPages[nTab]) // not the last empty Table
+ --nTab;
+ OSL_ENSURE(0 < static_cast<SCTAB>(nPages.size()),"are all tables empty?");
+ nTabPage = nPages[nTab] - 1;
+ nTabStart = 0;
+ for (sal_uInt16 i=0; i<nTab; i++)
+ nTabStart += nPages[i];
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ nDisplayStart = lcl_GetDisplayStart( nTab, &rDoc, nPages );
+ }
+ else // empty Document
+ {
+ nTab = 0;
+ nPageNo = nTabPage = nTabStart = nDisplayStart = 0;
+ aState.nPrintTab = 0;
+ aState.nStartCol = aState.nEndCol = 0;
+ aState.nStartRow = aState.nEndRow = 0;
+ aState.nZoom = 0;
+ aState.nPagesX = aState.nPagesY = 0;
+ aState.nTabPages = aState.nTotalPages =
+ aState.nPageStart = aState.nDocPages = 0;
+ }
+}
+
+void ScPreview::CalcPages()
+{
+ weld::WaitObject aWait(GetFrameWeld());
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ nTabCount = rDoc.GetTableCount();
+
+ if (maSelectedTabs.empty())
+ {
+ SCTAB nCurrentTab = ScDocShell::GetCurTab();
+ maSelectedTabs.insert(nCurrentTab);
+ }
+
+ SCTAB nStart = nTabsTested;
+ if (!bValid)
+ {
+ nStart = 0;
+ nTotalPages = 0;
+ nTabsTested = 0;
+ }
+
+ // update all pending row heights with a single progress bar,
+ // instead of a separate progress for each sheet from ScPrintFunc
+ pDocShell->UpdatePendingRowHeights( nTabCount-1, true );
+
+ // PrintOptions is passed to PrintFunc for SkipEmpty flag,
+ // but always all sheets are used (there is no selected sheet)
+ ScPrintOptions aOptions = SC_MOD()->GetPrintOptions();
+
+ while (nStart > static_cast<SCTAB>(nPages.size()))
+ nPages.push_back(0);
+ while (nStart > static_cast<SCTAB>(nFirstAttr.size()))
+ nFirstAttr.push_back(1);
+
+ for (SCTAB i=nStart; i<nTabCount; i++)
+ {
+ if ( i == static_cast<SCTAB>(nPages.size()))
+ nPages.push_back(0);
+ if ( i == static_cast<SCTAB>(nFirstAttr.size()))
+ nFirstAttr.push_back(1);
+ if (!aOptions.GetAllSheets() && maSelectedTabs.count(i) == 0)
+ {
+ nPages[i] = 0;
+ nFirstAttr[i] = 1;
+ continue;
+ }
+
+ tools::Long nAttrPage = i > 0 ? nFirstAttr[i-1] : 1;
+
+ tools::Long nThisStart = nTotalPages;
+ ScPrintFunc aPrintFunc( GetOutDev(), pDocShell, i, nAttrPage, 0, nullptr, &aOptions );
+ tools::Long nThisTab = aPrintFunc.GetTotalPages();
+ if (!aPrintFunc.HasPrintRange())
+ mbHasEmptyRangeTable = true;
+
+ nPages[i] = nThisTab;
+ nTotalPages += nThisTab;
+ nFirstAttr[i] = aPrintFunc.GetFirstPageNo(); // to keep or from template
+
+ if (nPageNo>=nThisStart && nPageNo<nTotalPages)
+ {
+ nTab = i;
+ nTabPage = nPageNo - nThisStart;
+ nTabStart = nThisStart;
+
+ aPrintFunc.GetPrintState( aState );
+ }
+ }
+
+ nDisplayStart = lcl_GetDisplayStart( nTab, &rDoc, nPages );
+
+ if (nTabCount > nTabsTested)
+ nTabsTested = nTabCount;
+
+ TestLastPage();
+
+ aState.nDocPages = nTotalPages;
+
+ bValid = true;
+ bStateValid = true;
+ DoInvalidate();
+}
+
+void ScPreview::RecalcPages() // only nPageNo is changed
+{
+ if (!bValid)
+ return; // then CalcPages is called
+
+ SCTAB nOldTab = nTab;
+
+ bool bDone = false;
+ while (nPageNo >= nTotalPages && nTabsTested < nTabCount)
+ {
+ CalcPages();
+ bDone = true;
+ }
+
+ if (!bDone)
+ {
+ tools::Long nPartPages = 0;
+ for (SCTAB i=0; i<nTabsTested && nTab < static_cast<SCTAB>(nPages.size()); i++)
+ {
+ tools::Long nThisStart = nPartPages;
+ nPartPages += nPages[i];
+
+ if (nPageNo>=nThisStart && nPageNo<nPartPages)
+ {
+ nTab = i;
+ nTabPage = nPageNo - nThisStart;
+ nTabStart = nThisStart;
+ }
+ }
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ nDisplayStart = lcl_GetDisplayStart( nTab, &rDoc, nPages );
+ }
+
+ TestLastPage(); // to test, if after last page
+
+ if ( nTab != nOldTab )
+ bStateValid = false;
+
+ DoInvalidate();
+}
+
+void ScPreview::DoPrint( ScPreviewLocationData* pFillLocation )
+{
+ if (!bValid)
+ {
+ CalcPages();
+ RecalcPages();
+ UpdateDrawView(); // Spreadsheet eventually changes
+ }
+
+ Fraction aPreviewZoom( nZoom, 100 );
+ Fraction aHorPrevZoom( static_cast<tools::Long>( 100 * nZoom / pDocShell->GetOutputFactor() ), 10000 );
+ MapMode aMMMode( MapUnit::Map100thMM, Point(), aHorPrevZoom, aPreviewZoom );
+
+ bool bDoPrint = ( pFillLocation == nullptr );
+ bool bValidPage = ( nPageNo < nTotalPages );
+
+ ScModule* pScMod = SC_MOD();
+ const svtools::ColorConfig& rColorCfg = pScMod->GetColorConfig();
+ Color aBackColor( rColorCfg.GetColorValue(svtools::APPBACKGROUND).nColor );
+
+ if ( bDoPrint && ( aOffset.X() < 0 || aOffset.Y() < 0 ) && bValidPage )
+ {
+ SetMapMode( aMMMode );
+ GetOutDev()->SetLineColor();
+ GetOutDev()->SetFillColor(aBackColor);
+
+ Size aWinSize = GetOutDev()->GetOutputSize();
+ if ( aOffset.X() < 0 )
+ GetOutDev()->DrawRect(tools::Rectangle( 0, 0, -aOffset.X(), aWinSize.Height() ));
+ if ( aOffset.Y() < 0 )
+ GetOutDev()->DrawRect(tools::Rectangle( 0, 0, aWinSize.Width(), -aOffset.Y() ));
+ }
+
+ tools::Long nLeftMargin = 0;
+ tools::Long nRightMargin = 0;
+ tools::Long nTopMargin = 0;
+ tools::Long nBottomMargin = 0;
+ bool bHeaderOn = false;
+ bool bFooterOn = false;
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+
+ Size aLocalPageSize;
+ if ( bValidPage )
+ {
+ ScPrintOptions aOptions = pScMod->GetPrintOptions();
+
+ std::unique_ptr<ScPrintFunc, o3tl::default_delete<ScPrintFunc>> pPrintFunc;
+ if (bStateValid)
+ pPrintFunc.reset(new ScPrintFunc(GetOutDev(), pDocShell, aState, &aOptions));
+ else
+ pPrintFunc.reset(new ScPrintFunc(GetOutDev(), pDocShell, nTab, nFirstAttr[nTab], nTotalPages, nullptr, &aOptions));
+
+ pPrintFunc->SetOffset(aOffset);
+ pPrintFunc->SetManualZoom(nZoom);
+ pPrintFunc->SetDateTime(aDateTime);
+ pPrintFunc->SetClearFlag(true);
+ pPrintFunc->SetUseStyleColor( officecfg::Office::Common::Accessibility::IsForPagePreviews::get() );
+
+ pPrintFunc->SetDrawView( pDrawView.get() );
+
+ // MultiSelection for the one Page must produce something inconvenient
+ Range aPageRange( nPageNo+1, nPageNo+1 );
+ MultiSelection aPage( aPageRange );
+ aPage.SetTotalRange( Range(0,RANGE_MAX) );
+ aPage.Select( aPageRange );
+
+ tools::Long nPrinted = pPrintFunc->DoPrint( aPage, nTabStart, nDisplayStart, bDoPrint, pFillLocation );
+ OSL_ENSURE(nPrinted<=1, "What is happening?");
+
+ SetMapMode(aMMMode);
+
+ //init nLeftMargin ... in the ScPrintFunc::InitParam!!!
+ nLeftMargin = pPrintFunc->GetLeftMargin();
+ nRightMargin = pPrintFunc->GetRightMargin();
+ nTopMargin = pPrintFunc->GetTopMargin();
+ nBottomMargin = pPrintFunc->GetBottomMargin();
+ nHeaderHeight = pPrintFunc->GetHeader().nHeight;
+ nFooterHeight = pPrintFunc->GetFooter().nHeight;
+ bHeaderOn = pPrintFunc->GetHeader().bEnable;
+ bFooterOn = pPrintFunc->GetFooter().bEnable;
+ mnScale = pPrintFunc->GetZoom();
+
+ if ( bDoPrint && bPageMargin && pLocationData ) // don't make use of pLocationData while filling it
+ {
+ tools::Rectangle aPixRect;
+ tools::Rectangle aRectCellPosition;
+ tools::Rectangle aRectPosition;
+ pLocationData->GetMainCellRange( aPageArea, aPixRect );
+ mvRight.resize(aPageArea.aEnd.Col()+1);
+ if( !bLayoutRTL )
+ {
+ pLocationData->GetCellPosition( aPageArea.aStart, aRectPosition );
+ nLeftPosition = aRectPosition.Left();
+ for( SCCOL i = aPageArea.aStart.Col(); i <= aPageArea.aEnd.Col(); i++ )
+ {
+ pLocationData->GetCellPosition( ScAddress( i,aPageArea.aStart.Row(),aPageArea.aStart.Tab()),aRectCellPosition );
+ mvRight[i] = aRectCellPosition.Right();
+ }
+ }
+ else
+ {
+ pLocationData->GetCellPosition( aPageArea.aEnd, aRectPosition );
+ nLeftPosition = aRectPosition.Right()+1;
+
+ pLocationData->GetCellPosition( aPageArea.aStart,aRectCellPosition );
+ mvRight[ aPageArea.aEnd.Col() ] = aRectCellPosition.Left();
+ for( SCCOL i = aPageArea.aEnd.Col(); i > aPageArea.aStart.Col(); i-- )
+ {
+ pLocationData->GetCellPosition( ScAddress( i,aPageArea.aEnd.Row(),aPageArea.aEnd.Tab()),aRectCellPosition );
+ mvRight[ i-1 ] = mvRight[ i ] + aRectCellPosition.Right() - aRectCellPosition.Left() + 1;
+ }
+ }
+ }
+
+ if (nPrinted) // if not, draw everything grey
+ {
+ aLocalPageSize = pPrintFunc->GetPageSize();
+ aLocalPageSize.setWidth(
+ o3tl::convert(aLocalPageSize.Width(), o3tl::Length::twip, o3tl::Length::mm100));
+ aLocalPageSize.setHeight(
+ o3tl::convert(aLocalPageSize.Height(), o3tl::Length::twip, o3tl::Length::mm100));
+
+ nLeftMargin = o3tl::convert(nLeftMargin, o3tl::Length::twip, o3tl::Length::mm100);
+ nRightMargin = o3tl::convert(nRightMargin, o3tl::Length::twip, o3tl::Length::mm100);
+ nTopMargin = o3tl::convert(nTopMargin, o3tl::Length::twip, o3tl::Length::mm100);
+ nBottomMargin = o3tl::convert(nBottomMargin, o3tl::Length::twip, o3tl::Length::mm100);
+ constexpr auto md = o3tl::getConversionMulDiv(o3tl::Length::twip, o3tl::Length::mm10);
+ const auto m = md.first * mnScale, d = md.second * 100;
+ nHeaderHeight = o3tl::convert(nHeaderHeight, m, d) + nTopMargin;
+ nFooterHeight = o3tl::convert(nFooterHeight, m, d) + nBottomMargin;
+ }
+
+ if (!bStateValid)
+ {
+ pPrintFunc->GetPrintState( aState );
+ aState.nDocPages = nTotalPages;
+ bStateValid = true;
+ }
+ }
+
+ if ( !bDoPrint )
+ return;
+
+ tools::Long nPageEndX = aLocalPageSize.Width() - aOffset.X();
+ tools::Long nPageEndY = aLocalPageSize.Height() - aOffset.Y();
+ if ( !bValidPage )
+ nPageEndX = nPageEndY = 0;
+
+ Size aWinSize = GetOutDev()->GetOutputSize();
+ Point aWinEnd( aWinSize.Width(), aWinSize.Height() );
+ bool bRight = nPageEndX <= aWinEnd.X();
+ bool bBottom = nPageEndY <= aWinEnd.Y();
+
+ if (!nTotalPages)
+ {
+ // There is no data to print. Print a friendly warning message and
+ // bail out.
+
+ SetMapMode(aMMMode);
+
+ // Draw background first.
+ GetOutDev()->SetLineColor();
+ GetOutDev()->SetFillColor(aBackColor);
+ GetOutDev()->DrawRect(tools::Rectangle(0, 0, aWinEnd.X(), aWinEnd.Y()));
+
+ const ScPatternAttr& rDefPattern =
+ rDoc.GetPool()->GetDefaultItem(ATTR_PATTERN);
+
+ std::unique_ptr<ScEditEngineDefaulter> pEditEng(
+ new ScEditEngineDefaulter(EditEngine::CreatePool().get(), true));
+
+ pEditEng->SetRefMapMode(aMMMode);
+ auto pEditDefaults = std::make_unique<SfxItemSet>( pEditEng->GetEmptyItemSet() );
+ rDefPattern.FillEditItemSet(pEditDefaults.get());
+ pEditDefaults->Put(SvxColorItem(COL_LIGHTGRAY, EE_CHAR_COLOR));
+ pEditEng->SetDefaults(std::move(pEditDefaults));
+
+ OUString aEmptyMsg;
+ if (mbHasEmptyRangeTable)
+ aEmptyMsg = ScResId(STR_PRINT_PREVIEW_EMPTY_RANGE);
+ else
+ aEmptyMsg = ScResId(STR_PRINT_PREVIEW_NODATA);
+
+ tools::Long nHeight = 3000;
+ pEditEng->SetDefaultItem(SvxFontHeightItem(nHeight, 100, EE_CHAR_FONTHEIGHT));
+ pEditEng->SetDefaultItem(SvxFontHeightItem(nHeight, 100, EE_CHAR_FONTHEIGHT_CJK));
+ pEditEng->SetDefaultItem(SvxFontHeightItem(nHeight, 100, EE_CHAR_FONTHEIGHT_CTL));
+
+ pEditEng->SetTextCurrentDefaults(aEmptyMsg);
+
+ Point aCenter(
+ (aWinEnd.X() - pEditEng->CalcTextWidth())/2,
+ (aWinEnd.Y() - pEditEng->GetTextHeight())/2);
+
+ pEditEng->Draw(*GetOutDev(), aCenter);
+
+ return;
+ }
+
+ if( bPageMargin && bValidPage )
+ {
+ SetMapMode(aMMMode);
+ GetOutDev()->SetLineColor( COL_BLACK );
+ DrawInvert( static_cast<tools::Long>( nTopMargin - aOffset.Y() ), PointerStyle::VSizeBar );
+ DrawInvert( static_cast<tools::Long>(nPageEndY - nBottomMargin ), PointerStyle::VSizeBar );
+ DrawInvert( static_cast<tools::Long>( nLeftMargin - aOffset.X() ), PointerStyle::HSizeBar );
+ DrawInvert( static_cast<tools::Long>( nPageEndX - nRightMargin ) , PointerStyle::HSizeBar );
+ if( bHeaderOn )
+ {
+ DrawInvert( nHeaderHeight - aOffset.Y(), PointerStyle::VSizeBar );
+ }
+ if( bFooterOn )
+ {
+ DrawInvert( nPageEndY - nFooterHeight, PointerStyle::VSizeBar );
+ }
+
+ SetMapMode( MapMode( MapUnit::MapPixel ) );
+ for( int i= aPageArea.aStart.Col(); i<= aPageArea.aEnd.Col(); i++ )
+ {
+ Point aColumnTop = LogicToPixel( Point( 0, -aOffset.Y() ) ,aMMMode );
+ GetOutDev()->SetLineColor( COL_BLACK );
+ GetOutDev()->SetFillColor( COL_BLACK );
+ GetOutDev()->DrawRect( tools::Rectangle( Point( mvRight[i] - 2, aColumnTop.Y() ),Point( mvRight[i] + 2 , 4 + aColumnTop.Y()) ));
+ GetOutDev()->DrawLine( Point( mvRight[i], aColumnTop.Y() ), Point( mvRight[i], 10 + aColumnTop.Y()) );
+ }
+ SetMapMode( aMMMode );
+ }
+
+ if (bRight || bBottom)
+ {
+ SetMapMode(aMMMode);
+ GetOutDev()->SetLineColor();
+ GetOutDev()->SetFillColor(aBackColor);
+ if (bRight)
+ GetOutDev()->DrawRect(tools::Rectangle(nPageEndX,0, aWinEnd.X(),aWinEnd.Y()));
+ if (bBottom)
+ {
+ if (bRight)
+ GetOutDev()->DrawRect(tools::Rectangle(0,nPageEndY, nPageEndX,aWinEnd.Y())); // Corner not duplicated
+ else
+ GetOutDev()->DrawRect(tools::Rectangle(0,nPageEndY, aWinEnd.X(),aWinEnd.Y()));
+ }
+ }
+
+ if ( !bValidPage )
+ return;
+
+ Color aBorderColor( SC_MOD()->GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor );
+
+ // draw border
+
+ if ( aOffset.X() <= 0 || aOffset.Y() <= 0 || bRight || bBottom )
+ {
+ GetOutDev()->SetLineColor( aBorderColor );
+ GetOutDev()->SetFillColor();
+
+ tools::Rectangle aPixel( LogicToPixel( tools::Rectangle( -aOffset.X(), -aOffset.Y(), nPageEndX, nPageEndY ) ) );
+ aPixel.AdjustRight( -1 );
+ aPixel.AdjustBottom( -1 );
+ GetOutDev()->DrawRect( PixelToLogic( aPixel ) );
+ }
+
+ // draw shadow
+
+ GetOutDev()->SetLineColor();
+ GetOutDev()->SetFillColor( aBorderColor );
+
+ tools::Rectangle aPixel;
+
+ aPixel = LogicToPixel( tools::Rectangle( nPageEndX, -aOffset.Y(), nPageEndX, nPageEndY ) );
+ aPixel.AdjustTop(SC_PREVIEW_SHADOWSIZE );
+ aPixel.AdjustRight(SC_PREVIEW_SHADOWSIZE - 1 );
+ aPixel.AdjustBottom(SC_PREVIEW_SHADOWSIZE - 1 );
+ GetOutDev()->DrawRect( PixelToLogic( aPixel ) );
+
+ aPixel = LogicToPixel( tools::Rectangle( -aOffset.X(), nPageEndY, nPageEndX, nPageEndY ) );
+ aPixel.AdjustLeft(SC_PREVIEW_SHADOWSIZE );
+ aPixel.AdjustRight(SC_PREVIEW_SHADOWSIZE - 1 );
+ aPixel.AdjustBottom(SC_PREVIEW_SHADOWSIZE - 1 );
+ GetOutDev()->DrawRect( PixelToLogic( aPixel ) );
+}
+
+void ScPreview::Paint( vcl::RenderContext& /*rRenderContext*/, const tools::Rectangle& /* rRect */ )
+{
+ bool bWasInPaint = bInPaint; // nested calls shouldn't be necessary, but allow for now
+ bInPaint = true;
+
+ if (bPageMargin)
+ GetLocationData(); // fill location data for column positions
+ DoPrint( nullptr );
+ pViewShell->UpdateScrollBars();
+
+ bInPaint = bWasInPaint;
+}
+
+void ScPreview::Command( const CommandEvent& rCEvt )
+{
+ CommandEventId nCmd = rCEvt.GetCommand();
+ if ( nCmd == CommandEventId::Wheel || nCmd == CommandEventId::StartAutoScroll || nCmd == CommandEventId::AutoScroll )
+ {
+ bool bDone = pViewShell->ScrollCommand( rCEvt );
+ if (!bDone)
+ Window::Command(rCEvt);
+ }
+ else if ( nCmd == CommandEventId::ContextMenu )
+ SfxDispatcher::ExecutePopup();
+ else
+ Window::Command( rCEvt );
+}
+
+void ScPreview::KeyInput( const KeyEvent& rKEvt )
+{
+ // The + and - keys can't be configured as accelerator entries, so they must be handled directly
+ // (in ScPreview, not ScPreviewShell -> only if the preview window has the focus)
+
+ const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
+ sal_uInt16 nKey = rKeyCode.GetCode();
+ bool bHandled = false;
+ if(!rKeyCode.GetModifier())
+ {
+ sal_uInt16 nSlot = 0;
+ switch(nKey)
+ {
+ case KEY_ADD: nSlot = SID_ZOOM_IN; break;
+ case KEY_ESCAPE: nSlot = ScViewUtil::IsFullScreen( *pViewShell ) ? SID_CANCEL : SID_PREVIEW_CLOSE; break;
+ case KEY_SUBTRACT: nSlot = SID_ZOOM_OUT; break;
+ }
+ if(nSlot)
+ {
+ bHandled = true;
+ pViewShell->GetViewFrame().GetDispatcher()->Execute( nSlot, SfxCallMode::ASYNCHRON );
+ }
+ }
+
+ if ( !bHandled && !pViewShell->KeyInput(rKEvt) )
+ Window::KeyInput(rKEvt);
+}
+
+const ScPreviewLocationData& ScPreview::GetLocationData()
+{
+ if ( !pLocationData )
+ {
+ pLocationData.reset( new ScPreviewLocationData( &pDocShell->GetDocument(), GetOutDev() ) );
+ bLocationValid = false;
+ }
+ if ( !bLocationValid )
+ {
+ pLocationData->Clear();
+ DoPrint( pLocationData.get() );
+ bLocationValid = true;
+ }
+ return *pLocationData;
+}
+
+void ScPreview::DataChanged(bool bNewTime)
+{
+ if (bNewTime)
+ aDateTime = DateTime( DateTime::SYSTEM );
+
+ bValid = false;
+ InvalidateLocationData( SfxHintId::ScDataChanged );
+ Invalidate();
+}
+
+OUString ScPreview::GetPosString()
+{
+ if (!bValid)
+ {
+ CalcPages();
+ UpdateDrawView(); // The table eventually changes
+ }
+
+ OUString aString = ScResId( STR_PAGE ) +
+ " " + OUString::number(nPageNo+1);
+
+ if (nTabsTested >= nTabCount)
+ aString += " / " + OUString::number(nTotalPages);
+
+ return aString;
+}
+
+void ScPreview::SetZoom(sal_uInt16 nNewZoom)
+{
+ if (nNewZoom < 20)
+ nNewZoom = 20;
+ if (nNewZoom > 400)
+ nNewZoom = 400;
+ if (nNewZoom == nZoom)
+ return;
+
+ nZoom = nNewZoom;
+
+ // apply new MapMode and call UpdateScrollBars to update aOffset
+
+ Fraction aPreviewZoom( nZoom, 100 );
+ Fraction aHorPrevZoom( static_cast<tools::Long>( 100 * nZoom / pDocShell->GetOutputFactor() ), 10000 );
+ MapMode aMMMode( MapUnit::Map100thMM, Point(), aHorPrevZoom, aPreviewZoom );
+ SetMapMode( aMMMode );
+
+ bInSetZoom = true; // don't scroll during SetYOffset in UpdateScrollBars
+ pViewShell->UpdateNeededScrollBars(true);
+ bInSetZoom = false;
+
+ bStateValid = false;
+ InvalidateLocationData( SfxHintId::ScAccVisAreaChanged );
+ DoInvalidate();
+ Invalidate();
+}
+
+void ScPreview::SetPageNo( tools::Long nPage )
+{
+ nPageNo = nPage;
+ RecalcPages();
+ UpdateDrawView(); // The table eventually changes
+ InvalidateLocationData( SfxHintId::ScDataChanged );
+ Invalidate();
+}
+
+tools::Long ScPreview::GetFirstPage(SCTAB nTabP)
+{
+ SCTAB nDocTabCount = pDocShell->GetDocument().GetTableCount();
+ if (nTabP >= nDocTabCount)
+ nTabP = nDocTabCount-1;
+
+ tools::Long nPage = 0;
+ if (nTabP>0)
+ {
+ CalcPages();
+ if (nTabP >= static_cast<SCTAB>(nPages.size()) )
+ OSL_FAIL("nPages out of bounds, FIX IT");
+ UpdateDrawView(); // The table eventually changes
+
+ for (SCTAB i=0; i<nTabP; i++)
+ nPage += nPages[i];
+
+ // An empty Table on the previous Page
+
+ if ( nPages[nTabP]==0 && nPage > 0 )
+ --nPage;
+ }
+
+ return nPage;
+}
+
+static Size lcl_GetDocPageSize( const ScDocument* pDoc, SCTAB nTab )
+{
+ OUString aName = pDoc->GetPageStyle( nTab );
+ ScStyleSheetPool* pStylePool = pDoc->GetStyleSheetPool();
+ SfxStyleSheetBase* pStyleSheet = pStylePool->Find( aName, SfxStyleFamily::Page );
+ if ( pStyleSheet )
+ {
+ SfxItemSet& rStyleSet = pStyleSheet->GetItemSet();
+ return rStyleSet.Get(ATTR_PAGE_SIZE).GetSize();
+ }
+ else
+ {
+ OSL_FAIL( "PageStyle not found" );
+ return Size();
+ }
+}
+
+sal_uInt16 ScPreview::GetOptimalZoom(bool bWidthOnly)
+{
+ double nWinScaleX = ScGlobal::nScreenPPTX / pDocShell->GetOutputFactor();
+ double nWinScaleY = ScGlobal::nScreenPPTY;
+ Size aWinSize = GetOutputSizePixel();
+
+ // desired margin is 0.25cm in default MapMode (like Writer),
+ // but some additional margin is introduced by integer scale values
+ // -> add only 0.10cm, so there is some margin in all cases.
+ Size aMarginSize( LogicToPixel(Size(100, 100), MapMode(MapUnit::Map100thMM)) );
+ aWinSize.AdjustWidth( -(2 * aMarginSize.Width()) );
+ aWinSize.AdjustHeight( -(2 * aMarginSize.Height()) );
+
+ Size aLocalPageSize = lcl_GetDocPageSize( &pDocShell->GetDocument(), nTab );
+ if ( aLocalPageSize.Width() && aLocalPageSize.Height() )
+ {
+ tools::Long nZoomX = static_cast<tools::Long>( aWinSize.Width() * 100 / ( aLocalPageSize.Width() * nWinScaleX ));
+ tools::Long nZoomY = static_cast<tools::Long>( aWinSize.Height() * 100 / ( aLocalPageSize.Height() * nWinScaleY ));
+
+ tools::Long nOptimal = nZoomX;
+ if (!bWidthOnly && nZoomY<nOptimal)
+ nOptimal = nZoomY;
+
+ if (nOptimal<20)
+ nOptimal = 20;
+ if (nOptimal>400)
+ nOptimal = 400;
+
+ return static_cast<sal_uInt16>(nOptimal);
+ }
+ else
+ return nZoom;
+}
+
+void ScPreview::SetXOffset( tools::Long nX )
+{
+ if ( aOffset.X() == nX )
+ return;
+
+ if (bValid)
+ {
+ tools::Long nDif = LogicToPixel(aOffset).X() - LogicToPixel(Point(nX,0)).X();
+ aOffset.setX( nX );
+ if (nDif && !bInSetZoom)
+ {
+ MapMode aOldMode = GetMapMode();
+ SetMapMode(MapMode(MapUnit::MapPixel));
+ Scroll( nDif, 0 );
+ SetMapMode(aOldMode);
+ }
+ }
+ else
+ {
+ aOffset.setX( nX );
+ if (!bInSetZoom)
+ Invalidate();
+ }
+ InvalidateLocationData( SfxHintId::ScAccVisAreaChanged );
+ Invalidate();
+}
+
+void ScPreview::SetYOffset( tools::Long nY )
+{
+ if ( aOffset.Y() == nY )
+ return;
+
+ if (bValid)
+ {
+ tools::Long nDif = LogicToPixel(aOffset).Y() - LogicToPixel(Point(0,nY)).Y();
+ aOffset.setY( nY );
+ if (nDif && !bInSetZoom)
+ {
+ MapMode aOldMode = GetMapMode();
+ SetMapMode(MapMode(MapUnit::MapPixel));
+ Scroll( 0, nDif );
+ SetMapMode(aOldMode);
+ }
+ }
+ else
+ {
+ aOffset.setY( nY );
+ if (!bInSetZoom)
+ Invalidate();
+ }
+ InvalidateLocationData( SfxHintId::ScAccVisAreaChanged );
+ Invalidate();
+}
+
+void ScPreview::DoInvalidate()
+{
+ // If the whole GetState of the shell is called
+ // The Invalidate must come behind asynchronously
+
+ if (bInGetState)
+ Application::PostUserEvent( LINK( this, ScPreview, InvalidateHdl ), nullptr, true );
+ else
+ StaticInvalidate(); // Immediately
+}
+
+void ScPreview::StaticInvalidate()
+{
+ // static method, because it's called asynchronously
+ // -> must use current viewframe
+
+ SfxViewFrame* pViewFrm = SfxViewFrame::Current();
+ if (!pViewFrm)
+ return;
+
+ SfxBindings& rBindings = pViewFrm->GetBindings();
+ rBindings.Invalidate(SID_STATUS_DOCPOS);
+ rBindings.Invalidate(SID_ROWCOL_SELCOUNT);
+ rBindings.Invalidate(SID_STATUS_PAGESTYLE);
+ rBindings.Invalidate(SID_PREVIEW_PREVIOUS);
+ rBindings.Invalidate(SID_PREVIEW_NEXT);
+ rBindings.Invalidate(SID_PREVIEW_FIRST);
+ rBindings.Invalidate(SID_PREVIEW_LAST);
+ rBindings.Invalidate(SID_ATTR_ZOOM);
+ rBindings.Invalidate(SID_ZOOM_IN);
+ rBindings.Invalidate(SID_ZOOM_OUT);
+ rBindings.Invalidate(SID_PREVIEW_SCALINGFACTOR);
+ rBindings.Invalidate(SID_ATTR_ZOOMSLIDER);
+}
+
+IMPL_STATIC_LINK_NOARG( ScPreview, InvalidateHdl, void*, void )
+{
+ StaticInvalidate();
+}
+
+void ScPreview::DataChanged( const DataChangedEvent& rDCEvt )
+{
+ Window::DataChanged(rDCEvt);
+
+ if ( !((rDCEvt.GetType() == DataChangedEventType::PRINTER) ||
+ (rDCEvt.GetType() == DataChangedEventType::DISPLAY) ||
+ (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
+ (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
+ ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
+ (rDCEvt.GetFlags() & AllSettingsFlags::STYLE))) )
+ return;
+
+ if ( rDCEvt.GetType() == DataChangedEventType::FONTS )
+ pDocShell->UpdateFontList();
+
+ // #i114518# Paint of form controls may modify the window's settings.
+ // Ignore the event if it is called from within Paint.
+ if ( !bInPaint )
+ {
+ if ( rDCEvt.GetType() == DataChangedEventType::SETTINGS &&
+ (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) )
+ {
+ // scroll bar size may have changed
+ pViewShell->InvalidateBorder(); // calls OuterResizePixel
+ }
+ Invalidate();
+ InvalidateLocationData( SfxHintId::ScDataChanged );
+ }
+}
+
+void ScPreview::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ Fraction aPreviewZoom( nZoom, 100 );
+ Fraction aHorPrevZoom( static_cast<tools::Long>( 100 * nZoom / pDocShell->GetOutputFactor() ), 10000 );
+ MapMode aMMMode( MapUnit::Map100thMM, Point(), aHorPrevZoom, aPreviewZoom );
+
+ aButtonDownChangePoint = PixelToLogic( rMEvt.GetPosPixel(),aMMMode );
+ aButtonDownPt = PixelToLogic( rMEvt.GetPosPixel(),aMMMode );
+
+ CaptureMouse();
+
+ if( rMEvt.IsLeft() && GetPointer() == PointerStyle::HSizeBar )
+ {
+ SetMapMode( aMMMode );
+ if( bLeftRulerChange )
+ {
+ DrawInvert( aButtonDownChangePoint.X(), PointerStyle::HSizeBar );
+ bLeftRulerMove = true;
+ bRightRulerMove = false;
+ }
+ else if( bRightRulerChange )
+ {
+ DrawInvert( aButtonDownChangePoint.X(), PointerStyle::HSizeBar );
+ bLeftRulerMove = false;
+ bRightRulerMove = true;
+ }
+ }
+
+ if( rMEvt.IsLeft() && GetPointer() == PointerStyle::VSizeBar )
+ {
+ SetMapMode( aMMMode );
+ if( bTopRulerChange )
+ {
+ DrawInvert( aButtonDownChangePoint.Y(), PointerStyle::VSizeBar );
+ bTopRulerMove = true;
+ bBottomRulerMove = false;
+ }
+ else if( bBottomRulerChange )
+ {
+ DrawInvert( aButtonDownChangePoint.Y(), PointerStyle::VSizeBar );
+ bTopRulerMove = false;
+ bBottomRulerMove = true;
+ }
+ else if( bHeaderRulerChange )
+ {
+ DrawInvert( aButtonDownChangePoint.Y(), PointerStyle::VSizeBar );
+ bHeaderRulerMove = true;
+ bFooterRulerMove = false;
+ }
+ else if( bFooterRulerChange )
+ {
+ DrawInvert( aButtonDownChangePoint.Y(), PointerStyle::VSizeBar );
+ bHeaderRulerMove = false;
+ bFooterRulerMove = true;
+ }
+ }
+
+ if( !(rMEvt.IsLeft() && GetPointer() == PointerStyle::HSplit) )
+ return;
+
+ Point aNowPt = rMEvt.GetPosPixel();
+ SCCOL i = 0;
+ for( i = aPageArea.aStart.Col(); i<= aPageArea.aEnd.Col(); i++ )
+ {
+ if( aNowPt.X() < mvRight[i] + 2 && aNowPt.X() > mvRight[i] - 2 )
+ {
+ nColNumberButtonDown = i;
+ break;
+ }
+ }
+ if( i == aPageArea.aEnd.Col()+1 )
+ return;
+
+ SetMapMode( aMMMode );
+ if( nColNumberButtonDown == aPageArea.aStart.Col() )
+ DrawInvert( PixelToLogic( Point( nLeftPosition, 0 ),aMMMode ).X() ,PointerStyle::HSplit );
+ else
+ DrawInvert( PixelToLogic( Point( mvRight[ nColNumberButtonDown-1 ], 0 ),aMMMode ).X() ,PointerStyle::HSplit );
+
+ DrawInvert( aButtonDownChangePoint.X(), PointerStyle::HSplit );
+ bColRulerMove = true;
+}
+
+void ScPreview::MouseButtonUp( const MouseEvent& rMEvt )
+{
+ Fraction aPreviewZoom( nZoom, 100 );
+ Fraction aHorPrevZoom( static_cast<tools::Long>( 100 * nZoom / pDocShell->GetOutputFactor() ), 10000 );
+ MapMode aMMMode( MapUnit::Map100thMM, Point(), aHorPrevZoom, aPreviewZoom );
+
+ aButtonUpPt = PixelToLogic( rMEvt.GetPosPixel(),aMMMode );
+
+ tools::Long nWidth = lcl_GetDocPageSize(&pDocShell->GetDocument(), nTab).Width();
+ tools::Long nHeight = lcl_GetDocPageSize(&pDocShell->GetDocument(), nTab).Height();
+
+ if( rMEvt.IsLeft() && GetPointer() == PointerStyle::HSizeBar )
+ {
+ SetPointer( PointerStyle::Arrow );
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ OUString aOldName = rDoc.GetPageStyle( nTab );
+ bool bUndo = rDoc.IsUndoEnabled();
+ ScStyleSheetPool* pStylePool = rDoc.GetStyleSheetPool();
+ SfxStyleSheetBase* pStyleSheet = pStylePool->Find( aOldName, SfxStyleFamily::Page );
+
+ if ( pStyleSheet )
+ {
+ bool bMoveRulerAction= true;
+ ScStyleSaveData aOldData;
+ if( bUndo )
+ aOldData.InitFromStyle( pStyleSheet );
+
+ SfxItemSet& rStyleSet = pStyleSheet->GetItemSet();
+
+ SvxLRSpaceItem aLRItem = rStyleSet.Get( ATTR_LRSPACE );
+
+ if(( bLeftRulerChange || bRightRulerChange ) && ( aButtonUpPt.X() <= ( 0 - aOffset.X() ) || aButtonUpPt.X() > o3tl::convert(nWidth, o3tl::Length::twip, o3tl::Length::mm100) - aOffset.X() ) )
+ {
+ bMoveRulerAction = false;
+ Invalidate(tools::Rectangle(0, 0, 10000, 10000));
+ }
+ else if( bLeftRulerChange && ( o3tl::convert(aButtonUpPt.X(), o3tl::Length::mm100, o3tl::Length::twip) > nWidth - aLRItem.GetRight() - o3tl::convert(aOffset.X(), o3tl::Length::mm100, o3tl::Length::twip) ) )
+ {
+ bMoveRulerAction = false;
+ Invalidate(tools::Rectangle(0, 0, 10000, 10000));
+ }
+ else if( bRightRulerChange && ( o3tl::convert(aButtonUpPt.X(), o3tl::Length::mm100, o3tl::Length::twip) < aLRItem.GetLeft() - o3tl::convert(aOffset.X(), o3tl::Length::mm100, o3tl::Length::twip) ) )
+ {
+ bMoveRulerAction = false;
+ Invalidate(tools::Rectangle(0, 0, 10000, 10000));
+ }
+ else if( aButtonDownPt.X() == aButtonUpPt.X() )
+ {
+ bMoveRulerAction = false;
+ DrawInvert( aButtonUpPt.X(), PointerStyle::HSizeBar );
+ }
+ if( bMoveRulerAction )
+ {
+ ScDocShellModificator aModificator( *pDocShell );
+ if( bLeftRulerChange && bLeftRulerMove )
+ {
+ aLRItem.SetLeft(o3tl::convert( aButtonUpPt.X(), o3tl::Length::mm100, o3tl::Length::twip) + o3tl::convert(aOffset.X(), o3tl::Length::mm100, o3tl::Length::twip));
+ rStyleSet.Put( aLRItem );
+ pDocShell->SetModified();
+ }
+ else if( bRightRulerChange && bRightRulerMove )
+ {
+ aLRItem.SetRight(nWidth - o3tl::convert(aButtonUpPt.X(), o3tl::Length::mm100, o3tl::Length::twip) - o3tl::convert(aOffset.X(), o3tl::Length::mm100, o3tl::Length::twip));
+ rStyleSet.Put( aLRItem );
+ pDocShell->SetModified();
+ }
+
+ ScStyleSaveData aNewData;
+ aNewData.InitFromStyle( pStyleSheet );
+ if( bUndo )
+ {
+ pDocShell->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoModifyStyle>( pDocShell, SfxStyleFamily::Page,
+ aOldData, aNewData ) );
+ }
+
+ if ( ValidTab( nTab ) )
+ {
+ ScPrintFunc aPrintFunc( GetOutDev(), pDocShell, nTab );
+ aPrintFunc.UpdatePages();
+ }
+
+ tools::Rectangle aRect(0,0,10000,10000);
+ Invalidate(aRect);
+ aModificator.SetDocumentModified();
+ bLeftRulerChange = false;
+ bRightRulerChange = false;
+ }
+ }
+ bLeftRulerMove = false;
+ bRightRulerMove = false;
+ }
+
+ if( rMEvt.IsLeft() && GetPointer() == PointerStyle::VSizeBar )
+ {
+ SetPointer( PointerStyle::Arrow );
+
+ bool bMoveRulerAction = true;
+ if( ( bTopRulerChange || bBottomRulerChange || bHeaderRulerChange || bFooterRulerChange ) && ( aButtonUpPt.Y() <= ( 0 - aOffset.Y() ) || aButtonUpPt.Y() > o3tl::convert(nHeight, o3tl::Length::twip, o3tl::Length::mm100) -aOffset.Y() ) )
+ {
+ bMoveRulerAction = false;
+ Invalidate(tools::Rectangle(0, 0, 10000, 10000));
+ }
+ else if( aButtonDownPt.Y() == aButtonUpPt.Y() )
+ {
+ bMoveRulerAction = false;
+ DrawInvert( aButtonUpPt.Y(), PointerStyle::VSizeBar );
+ }
+ if( bMoveRulerAction )
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ bool bUndo = rDoc.IsUndoEnabled();
+ ScStyleSheetPool* pStylePool = rDoc.GetStyleSheetPool();
+ SfxStyleSheetBase* pStyleSheet = pStylePool->Find( rDoc.GetPageStyle( nTab ), SfxStyleFamily::Page );
+ OSL_ENSURE( pStyleSheet, "PageStyle not found" );
+ if ( pStyleSheet )
+ {
+ ScDocShellModificator aModificator( *pDocShell );
+ ScStyleSaveData aOldData;
+ if( bUndo )
+ aOldData.InitFromStyle( pStyleSheet );
+
+ SfxItemSet& rStyleSet = pStyleSheet->GetItemSet();
+
+ SvxULSpaceItem aULItem = rStyleSet.Get( ATTR_ULSPACE );
+
+ if( bTopRulerMove && bTopRulerChange )
+ {
+ aULItem.SetUpperValue(o3tl::convert(aButtonUpPt.Y(), o3tl::Length::mm100, o3tl::Length::twip) + o3tl::convert(aOffset.Y(), o3tl::Length::mm100, o3tl::Length::twip));
+ rStyleSet.Put( aULItem );
+ pDocShell->SetModified();
+ }
+ else if( bBottomRulerMove && bBottomRulerChange )
+ {
+ aULItem.SetLowerValue(nHeight - o3tl::convert(aButtonUpPt.Y(), o3tl::Length::mm100, o3tl::Length::twip) - o3tl::convert(aOffset.Y(), o3tl::Length::mm100, o3tl::Length::twip));
+ rStyleSet.Put( aULItem );
+ pDocShell->SetModified();
+ }
+ else if( bHeaderRulerMove && bHeaderRulerChange )
+ {
+ if ( const SvxSetItem* pSetItem = rStyleSet.GetItemIfSet( ATTR_PAGE_HEADERSET, false ) )
+ {
+ const SfxItemSet& rHeaderSet = pSetItem->GetItemSet();
+ Size aHeaderSize = rHeaderSet.Get(ATTR_PAGE_SIZE).GetSize();
+ aHeaderSize.setHeight(o3tl::convert( aButtonUpPt.Y(), o3tl::Length::mm100, o3tl::Length::twip) + o3tl::convert(aOffset.Y(), o3tl::Length::mm100, o3tl::Length::twip) - aULItem.GetUpper());
+ aHeaderSize.setHeight( aHeaderSize.Height() * 100 / mnScale );
+ SvxSetItem aNewHeader( rStyleSet.Get(ATTR_PAGE_HEADERSET) );
+ aNewHeader.GetItemSet().Put( SvxSizeItem( ATTR_PAGE_SIZE, aHeaderSize ) );
+ rStyleSet.Put( aNewHeader );
+ pDocShell->SetModified();
+ }
+ }
+ else if( bFooterRulerMove && bFooterRulerChange )
+ {
+ if( const SvxSetItem* pSetItem = rStyleSet.GetItemIfSet( ATTR_PAGE_FOOTERSET, false ) )
+ {
+ const SfxItemSet& rFooterSet = pSetItem->GetItemSet();
+ Size aFooterSize = rFooterSet.Get(ATTR_PAGE_SIZE).GetSize();
+ aFooterSize.setHeight(nHeight - o3tl::convert(aButtonUpPt.Y(), o3tl::Length::mm100, o3tl::Length::twip) - o3tl::convert(aOffset.Y(), o3tl::Length::mm100, o3tl::Length::twip) - aULItem.GetLower());
+ aFooterSize.setHeight( aFooterSize.Height() * 100 / mnScale );
+ SvxSetItem aNewFooter( rStyleSet.Get(ATTR_PAGE_FOOTERSET) );
+ aNewFooter.GetItemSet().Put( SvxSizeItem( ATTR_PAGE_SIZE, aFooterSize ) );
+ rStyleSet.Put( aNewFooter );
+ pDocShell->SetModified();
+ }
+ }
+
+ ScStyleSaveData aNewData;
+ aNewData.InitFromStyle( pStyleSheet );
+ if( bUndo )
+ {
+ pDocShell->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoModifyStyle>( pDocShell, SfxStyleFamily::Page,
+ aOldData, aNewData ) );
+ }
+
+ if ( ValidTab( nTab ) )
+ {
+ ScPrintFunc aPrintFunc( GetOutDev(), pDocShell, nTab );
+ aPrintFunc.UpdatePages();
+ }
+
+ tools::Rectangle aRect(0, 0, 10000, 10000);
+ Invalidate(aRect);
+ aModificator.SetDocumentModified();
+ bTopRulerChange = false;
+ bBottomRulerChange = false;
+ bHeaderRulerChange = false;
+ bFooterRulerChange = false;
+ }
+ }
+ bTopRulerMove = false;
+ bBottomRulerMove = false;
+ bHeaderRulerMove = false;
+ bFooterRulerMove = false;
+ }
+ if( rMEvt.IsLeft() && GetPointer() == PointerStyle::HSplit )
+ {
+ SetPointer(PointerStyle::Arrow);
+ ScDocument& rDoc = pDocShell->GetDocument();
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+ bool bMoveRulerAction = true;
+ if( aButtonDownPt.X() == aButtonUpPt.X() )
+ {
+ bMoveRulerAction = false;
+ if( nColNumberButtonDown == aPageArea.aStart.Col() )
+ DrawInvert( PixelToLogic( Point( nLeftPosition, 0 ),aMMMode ).X() ,PointerStyle::HSplit );
+ else
+ DrawInvert( PixelToLogic( Point( mvRight[ nColNumberButtonDown-1 ], 0 ),aMMMode ).X() ,PointerStyle::HSplit );
+ DrawInvert( aButtonUpPt.X(), PointerStyle::HSplit );
+ }
+ if( bMoveRulerAction )
+ {
+ tools::Long nNewColWidth = 0;
+ std::vector<sc::ColRowSpan> aCols(1, sc::ColRowSpan(nColNumberButtonDown,nColNumberButtonDown));
+
+ constexpr auto md = o3tl::getConversionMulDiv(o3tl::Length::mm100, o3tl::Length::twip);
+ const auto m = md.first * 100, d = md.second * mnScale;
+ if( !bLayoutRTL )
+ {
+ nNewColWidth = o3tl::convert(PixelToLogic( Point( rMEvt.GetPosPixel().X() - mvRight[ nColNumberButtonDown ], 0), aMMMode ).X(), m, d);
+ nNewColWidth += pDocShell->GetDocument().GetColWidth( nColNumberButtonDown, nTab );
+ }
+ else
+ {
+
+ nNewColWidth = o3tl::convert(PixelToLogic( Point( mvRight[ nColNumberButtonDown ] - rMEvt.GetPosPixel().X(), 0), aMMMode ).X(), m, d);
+ nNewColWidth += pDocShell->GetDocument().GetColWidth( nColNumberButtonDown, nTab );
+ }
+
+ if( nNewColWidth >= 0 )
+ {
+ pDocShell->GetDocFunc().SetWidthOrHeight(
+ true, aCols, nTab, SC_SIZE_DIRECT, static_cast<sal_uInt16>(nNewColWidth), true, true);
+ pDocShell->SetModified();
+ }
+ if ( ValidTab( nTab ) )
+ {
+ ScPrintFunc aPrintFunc( GetOutDev(), pDocShell, nTab );
+ aPrintFunc.UpdatePages();
+ }
+ tools::Rectangle aRect(0, 0, 10000, 10000);
+ Invalidate(aRect);
+ }
+ bColRulerMove = false;
+ }
+ ReleaseMouse();
+}
+
+void ScPreview::MouseMove( const MouseEvent& rMEvt )
+{
+ Fraction aPreviewZoom( nZoom, 100 );
+ Fraction aHorPrevZoom( static_cast<tools::Long>( 100 * nZoom / pDocShell->GetOutputFactor() ), 10000 );
+ MapMode aMMMode( MapUnit::Map100thMM, Point(), aHorPrevZoom, aPreviewZoom );
+ Point aMouseMovePoint = PixelToLogic( rMEvt.GetPosPixel(), aMMMode );
+
+ tools::Long nLeftMargin = 0;
+ tools::Long nRightMargin = 0;
+ tools::Long nTopMargin = 0;
+ tools::Long nBottomMargin = 0;
+
+ tools::Long nWidth = lcl_GetDocPageSize(&pDocShell->GetDocument(), nTab).Width();
+ tools::Long nHeight = lcl_GetDocPageSize(&pDocShell->GetDocument(), nTab).Height();
+
+ if ( nPageNo < nTotalPages )
+ {
+ ScPrintOptions aOptions = SC_MOD()->GetPrintOptions();
+
+ std::unique_ptr<ScPrintFunc, o3tl::default_delete<ScPrintFunc>> pPrintFunc;
+ if (bStateValid)
+ pPrintFunc.reset(new ScPrintFunc( GetOutDev(), pDocShell, aState, &aOptions ));
+ else
+ pPrintFunc.reset(new ScPrintFunc( GetOutDev(), pDocShell, nTab, nFirstAttr[nTab], nTotalPages, nullptr, &aOptions ));
+
+ nLeftMargin = o3tl::convert(pPrintFunc->GetLeftMargin(), o3tl::Length::twip, o3tl::Length::mm100) - aOffset.X();
+ nRightMargin = o3tl::convert(pPrintFunc->GetRightMargin(), o3tl::Length::twip, o3tl::Length::mm100);
+ nRightMargin = o3tl::convert(nWidth, o3tl::Length::twip, o3tl::Length::mm100) - nRightMargin - aOffset.X();
+ nTopMargin = o3tl::convert(pPrintFunc->GetTopMargin(), o3tl::Length::twip, o3tl::Length::mm100) - aOffset.Y();
+ nBottomMargin = o3tl::convert(pPrintFunc->GetBottomMargin(), o3tl::Length::twip, o3tl::Length::mm100);
+ nBottomMargin = o3tl::convert(nHeight, o3tl::Length::twip, o3tl::Length::mm100) - nBottomMargin - aOffset.Y();
+ if( mnScale > 0 )
+ {
+ constexpr auto md = o3tl::getConversionMulDiv(o3tl::Length::twip, o3tl::Length::mm100);
+ const auto m = md.first * mnScale, d = md.second * 100;
+ nHeaderHeight = nTopMargin + o3tl::convert(pPrintFunc->GetHeader().nHeight, m, d);
+ nFooterHeight = nBottomMargin - o3tl::convert(pPrintFunc->GetFooter().nHeight, m, d);
+ }
+ else
+ {
+ nHeaderHeight = nTopMargin + o3tl::convert(pPrintFunc->GetHeader().nHeight, o3tl::Length::twip, o3tl::Length::mm100);
+ nFooterHeight = nBottomMargin - o3tl::convert(pPrintFunc->GetFooter().nHeight, o3tl::Length::twip, o3tl::Length::mm100);
+ }
+ }
+
+ Point aPixPt( rMEvt.GetPosPixel() );
+ Point aLeftTop = LogicToPixel( Point( nLeftMargin, -aOffset.Y() ) , aMMMode );
+ Point aLeftBottom = LogicToPixel( Point( nLeftMargin, o3tl::convert(nHeight, o3tl::Length::twip, o3tl::Length::mm100) - aOffset.Y()), aMMMode );
+ Point aRightTop = LogicToPixel( Point( nRightMargin, -aOffset.Y() ), aMMMode );
+ Point aTopLeft = LogicToPixel( Point( -aOffset.X(), nTopMargin ), aMMMode );
+ Point aTopRight = LogicToPixel( Point( o3tl::convert(nWidth, o3tl::Length::twip, o3tl::Length::mm100) - aOffset.X(), nTopMargin ), aMMMode );
+ Point aBottomLeft = LogicToPixel( Point( -aOffset.X(), nBottomMargin ), aMMMode );
+ Point aHeaderLeft = LogicToPixel( Point( -aOffset.X(), nHeaderHeight ), aMMMode );
+ Point aFooderLeft = LogicToPixel( Point( -aOffset.X(), nFooterHeight ), aMMMode );
+
+ bool bOnColRulerChange = false;
+
+ for( SCCOL i=aPageArea.aStart.Col(); i<= aPageArea.aEnd.Col(); i++ )
+ {
+ Point aColumnTop = LogicToPixel( Point( 0, -aOffset.Y() ) ,aMMMode );
+ Point aColumnBottom = LogicToPixel( Point( 0, o3tl::convert(nHeight, o3tl::Length::twip, o3tl::Length::mm100) - aOffset.Y()), aMMMode );
+ tools::Long nRight = i < static_cast<SCCOL>(mvRight.size()) ? mvRight[i] : 0;
+ if( aPixPt.X() < ( nRight + 2 ) && ( aPixPt.X() > ( nRight - 2 ) ) && ( aPixPt.X() < aRightTop.X() ) && ( aPixPt.X() > aLeftTop.X() )
+ && ( aPixPt.Y() > aColumnTop.Y() ) && ( aPixPt.Y() < aColumnBottom.Y() ) && !bLeftRulerMove && !bRightRulerMove
+ && !bTopRulerMove && !bBottomRulerMove && !bHeaderRulerMove && !bFooterRulerMove )
+ {
+ bOnColRulerChange = true;
+ if( !rMEvt.GetButtons() && GetPointer() == PointerStyle::HSplit )
+ nColNumberButtonDown = i;
+ break;
+ }
+ }
+
+ if( aPixPt.X() < ( aLeftTop.X() + 2 ) && aPixPt.X() > ( aLeftTop.X() - 2 ) && !bRightRulerMove )
+ {
+ bLeftRulerChange = true;
+ bRightRulerChange = false;
+ }
+ else if( aPixPt.X() < ( aRightTop.X() + 2 ) && aPixPt.X() > ( aRightTop.X() - 2 ) && !bLeftRulerMove )
+ {
+ bLeftRulerChange = false;
+ bRightRulerChange = true;
+ }
+ else if( aPixPt.Y() < ( aTopLeft.Y() + 2 ) && aPixPt.Y() > ( aTopLeft.Y() - 2 ) && !bBottomRulerMove && !bHeaderRulerMove && !bFooterRulerMove )
+ {
+ bTopRulerChange = true;
+ bBottomRulerChange = false;
+ bHeaderRulerChange = false;
+ bFooterRulerChange = false;
+ }
+ else if( aPixPt.Y() < ( aBottomLeft.Y() + 2 ) && aPixPt.Y() > ( aBottomLeft.Y() - 2 ) && !bTopRulerMove && !bHeaderRulerMove && !bFooterRulerMove )
+ {
+ bTopRulerChange = false;
+ bBottomRulerChange = true;
+ bHeaderRulerChange = false;
+ bFooterRulerChange = false;
+ }
+ else if( aPixPt.Y() < ( aHeaderLeft.Y() + 2 ) && aPixPt.Y() > ( aHeaderLeft.Y() - 2 ) && !bTopRulerMove && !bBottomRulerMove && !bFooterRulerMove )
+ {
+ bTopRulerChange = false;
+ bBottomRulerChange = false;
+ bHeaderRulerChange = true;
+ bFooterRulerChange = false;
+ }
+ else if( aPixPt.Y() < ( aFooderLeft.Y() + 2 ) && aPixPt.Y() > ( aFooderLeft.Y() - 2 ) && !bTopRulerMove && !bBottomRulerMove && !bHeaderRulerMove )
+ {
+ bTopRulerChange = false;
+ bBottomRulerChange = false;
+ bHeaderRulerChange = false;
+ bFooterRulerChange = true;
+ }
+
+ if( !bPageMargin )
+ return;
+
+ if(( (aPixPt.X() < ( aLeftTop.X() + 2 ) && aPixPt.X() > ( aLeftTop.X() - 2 )) || bLeftRulerMove ||
+ ( aPixPt.X() < ( aRightTop.X() + 2 ) && aPixPt.X() > ( aRightTop.X() - 2 ) ) || bRightRulerMove || bOnColRulerChange || bColRulerMove )
+ && aPixPt.Y() > aLeftTop.Y() && aPixPt.Y() < aLeftBottom.Y() )
+ {
+ if( bOnColRulerChange || bColRulerMove )
+ {
+ SetPointer( PointerStyle::HSplit );
+ if( bColRulerMove )
+ {
+ if( aMouseMovePoint.X() > -aOffset.X() && aMouseMovePoint.X() < o3tl::convert(nWidth, o3tl::Length::twip, o3tl::Length::mm100) - aOffset.X() )
+ DragMove( aMouseMovePoint.X(), PointerStyle::HSplit );
+ }
+ }
+ else
+ {
+ if( bLeftRulerChange && !bTopRulerMove && !bBottomRulerMove && !bHeaderRulerMove && !bFooterRulerMove )
+ {
+ SetPointer( PointerStyle::HSizeBar );
+ if( bLeftRulerMove )
+ {
+ if( aMouseMovePoint.X() > -aOffset.X() && aMouseMovePoint.X() < o3tl::convert(nWidth, o3tl::Length::twip, o3tl::Length::mm100) - aOffset.X() )
+ DragMove( aMouseMovePoint.X(), PointerStyle::HSizeBar );
+ }
+ }
+ else if( bRightRulerChange && !bTopRulerMove && !bBottomRulerMove && !bHeaderRulerMove && !bFooterRulerMove )
+ {
+ SetPointer( PointerStyle::HSizeBar );
+ if( bRightRulerMove )
+ {
+ if( aMouseMovePoint.X() > -aOffset.X() && aMouseMovePoint.X() < o3tl::convert(nWidth, o3tl::Length::twip, o3tl::Length::mm100) - aOffset.X() )
+ DragMove( aMouseMovePoint.X(), PointerStyle::HSizeBar );
+ }
+ }
+ }
+ }
+ else
+ {
+ if( ( ( aPixPt.Y() < ( aTopLeft.Y() + 2 ) && aPixPt.Y() > ( aTopLeft.Y() - 2 ) ) || bTopRulerMove ||
+ ( aPixPt.Y() < ( aBottomLeft.Y() + 2 ) && aPixPt.Y() > ( aBottomLeft.Y() - 2 ) ) || bBottomRulerMove ||
+ ( aPixPt.Y() < ( aHeaderLeft.Y() + 2 ) && aPixPt.Y() > ( aHeaderLeft.Y() - 2 ) ) || bHeaderRulerMove ||
+ ( aPixPt.Y() < ( aFooderLeft.Y() + 2 ) && aPixPt.Y() > ( aFooderLeft.Y() - 2 ) ) || bFooterRulerMove )
+ && aPixPt.X() > aTopLeft.X() && aPixPt.X() < aTopRight.X() )
+ {
+ if( bTopRulerChange )
+ {
+ SetPointer( PointerStyle::VSizeBar );
+ if( bTopRulerMove )
+ {
+ if( aMouseMovePoint.Y() > -aOffset.Y() && aMouseMovePoint.Y() < o3tl::convert(nHeight, o3tl::Length::twip, o3tl::Length::mm100) - aOffset.Y() )
+ DragMove( aMouseMovePoint.Y(), PointerStyle::VSizeBar );
+ }
+ }
+ else if( bBottomRulerChange )
+ {
+ SetPointer( PointerStyle::VSizeBar );
+ if( bBottomRulerMove )
+ {
+ if( aMouseMovePoint.Y() > -aOffset.Y() && aMouseMovePoint.Y() < o3tl::convert(nHeight, o3tl::Length::twip, o3tl::Length::mm100) - aOffset.Y() )
+ DragMove( aMouseMovePoint.Y(), PointerStyle::VSizeBar );
+ }
+ }
+ else if( bHeaderRulerChange )
+ {
+ SetPointer( PointerStyle::VSizeBar );
+ if( bHeaderRulerMove )
+ {
+ if( aMouseMovePoint.Y() > -aOffset.Y() && aMouseMovePoint.Y() < o3tl::convert(nHeight, o3tl::Length::twip, o3tl::Length::mm100) - aOffset.Y() )
+ DragMove( aMouseMovePoint.Y(), PointerStyle::VSizeBar );
+ }
+ }
+ else if( bFooterRulerChange )
+ {
+ SetPointer( PointerStyle::VSizeBar );
+ if( bFooterRulerMove )
+ {
+ if( aMouseMovePoint.Y() > -aOffset.Y() && aMouseMovePoint.Y() < o3tl::convert(nHeight, o3tl::Length::twip, o3tl::Length::mm100) - aOffset.Y() )
+ DragMove( aMouseMovePoint.Y(), PointerStyle::VSizeBar );
+ }
+ }
+ }
+ else
+ SetPointer( PointerStyle::Arrow );
+ }
+}
+
+void ScPreview::InvalidateLocationData(SfxHintId nId)
+{
+ bLocationValid = false;
+ if (pViewShell->HasAccessibilityObjects())
+ pViewShell->BroadcastAccessibility( SfxHint( nId ) );
+}
+
+void ScPreview::GetFocus()
+{
+ Window::GetFocus();
+ if (pViewShell && pViewShell->HasAccessibilityObjects())
+ pViewShell->BroadcastAccessibility( ScAccWinFocusGotHint() );
+}
+
+void ScPreview::LoseFocus()
+{
+ if (pViewShell && pViewShell->HasAccessibilityObjects())
+ pViewShell->BroadcastAccessibility( ScAccWinFocusLostHint() );
+ Window::LoseFocus();
+}
+
+css::uno::Reference<css::accessibility::XAccessible> ScPreview::CreateAccessible()
+{
+ css::uno::Reference<css::accessibility::XAccessible> xAcc= GetAccessible(false);
+ if (xAcc.is())
+ {
+ return xAcc;
+ }
+
+ rtl::Reference<ScAccessibleDocumentPagePreview> pAccessible =
+ new ScAccessibleDocumentPagePreview( GetAccessibleParentWindow()->GetAccessible(), pViewShell );
+
+ xAcc = pAccessible;
+ SetAccessible(xAcc);
+ pAccessible->Init();
+ return xAcc;
+}
+
+void ScPreview::DragMove( tools::Long nDragMovePos, PointerStyle nFlags )
+{
+ Fraction aPreviewZoom( nZoom, 100 );
+ Fraction aHorPrevZoom( static_cast<tools::Long>( 100 * nZoom / pDocShell->GetOutputFactor() ), 10000 );
+ MapMode aMMMode( MapUnit::Map100thMM, Point(), aHorPrevZoom, aPreviewZoom );
+ SetMapMode( aMMMode );
+ tools::Long nPos = nDragMovePos;
+ if( nFlags == PointerStyle::HSizeBar || nFlags == PointerStyle::HSplit )
+ {
+ if( nDragMovePos != aButtonDownChangePoint.X() )
+ {
+ DrawInvert( aButtonDownChangePoint.X(), nFlags );
+ aButtonDownChangePoint.setX( nPos );
+ DrawInvert( aButtonDownChangePoint.X(), nFlags );
+ }
+ }
+ else if( nFlags == PointerStyle::VSizeBar )
+ {
+ if( nDragMovePos != aButtonDownChangePoint.Y() )
+ {
+ DrawInvert( aButtonDownChangePoint.Y(), nFlags );
+ aButtonDownChangePoint.setY( nPos );
+ DrawInvert( aButtonDownChangePoint.Y(), nFlags );
+ }
+ }
+}
+
+void ScPreview::DrawInvert( tools::Long nDragPos, PointerStyle nFlags )
+{
+ tools::Long nHeight = lcl_GetDocPageSize( &pDocShell->GetDocument(), nTab ).Height();
+ tools::Long nWidth = lcl_GetDocPageSize( &pDocShell->GetDocument(), nTab ).Width();
+ if( nFlags == PointerStyle::HSizeBar || nFlags == PointerStyle::HSplit )
+ {
+ tools::Rectangle aRect( nDragPos, -aOffset.Y(), nDragPos + 1, o3tl::convert(nHeight, o3tl::Length::twip, o3tl::Length::mm100) - aOffset.Y());
+ GetOutDev()->Invert( aRect, InvertFlags::N50 );
+ }
+ else if( nFlags == PointerStyle::VSizeBar )
+ {
+ tools::Rectangle aRect( -aOffset.X(), nDragPos, o3tl::convert(nWidth, o3tl::Length::twip, o3tl::Length::mm100) - aOffset.X(), nDragPos + 1 );
+ GetOutDev()->Invert( aRect, InvertFlags::N50 );
+ }
+}
+
+void ScPreview::SetSelectedTabs(const ScMarkData& rMark)
+{
+ maSelectedTabs = rMark.GetSelectedTabs();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/prevloc.cxx b/sc/source/ui/view/prevloc.cxx
new file mode 100644
index 0000000000..1e2375ab5c
--- /dev/null
+++ b/sc/source/ui/view/prevloc.cxx
@@ -0,0 +1,718 @@
+/* -*- 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 .
+ */
+
+#include <prevloc.hxx>
+#include <document.hxx>
+
+#include <o3tl/unit_conversion.hxx>
+#include <osl/diagnose.h>
+#include <vcl/outdev.hxx>
+
+namespace {
+
+enum ScPreviewLocationType : sal_uInt8
+{
+ SC_PLOC_CELLRANGE,
+ SC_PLOC_COLHEADER,
+ SC_PLOC_ROWHEADER,
+ SC_PLOC_LEFTHEADER,
+ SC_PLOC_RIGHTHEADER,
+ SC_PLOC_LEFTFOOTER,
+ SC_PLOC_RIGHTFOOTER,
+ SC_PLOC_NOTEMARK,
+ SC_PLOC_NOTETEXT
+};
+
+}
+
+struct ScPreviewLocationEntry
+{
+ tools::Rectangle aPixelRect;
+ ScRange aCellRange;
+ ScPreviewLocationType eType;
+ bool bRepeatCol;
+ bool bRepeatRow;
+
+ ScPreviewLocationEntry( ScPreviewLocationType eNewType, const tools::Rectangle& rPixel, const ScRange& rRange,
+ bool bRepCol, bool bRepRow ) :
+ aPixelRect( rPixel ),
+ aCellRange( rRange ),
+ eType( eNewType ),
+ bRepeatCol( bRepCol ),
+ bRepeatRow( bRepRow )
+ {
+ }
+};
+
+ScPreviewTableInfo::ScPreviewTableInfo() :
+ nTab(0),
+ nCols(0),
+ nRows(0)
+{
+}
+
+ScPreviewTableInfo::~ScPreviewTableInfo()
+{
+}
+
+void ScPreviewTableInfo::SetTab( SCTAB nNewTab )
+{
+ nTab = nNewTab;
+}
+
+void ScPreviewTableInfo::SetColInfo( SCCOL nCount, ScPreviewColRowInfo* pNewInfo )
+{
+ pColInfo.reset(pNewInfo);
+ nCols = nCount;
+}
+
+void ScPreviewTableInfo::SetRowInfo( SCROW nCount, ScPreviewColRowInfo* pNewInfo )
+{
+ pRowInfo.reset(pNewInfo);
+ nRows = nCount;
+}
+
+void ScPreviewTableInfo::LimitToArea( const tools::Rectangle& rPixelArea )
+{
+ if ( pColInfo )
+ {
+ // cells completely left of the visible area
+ SCCOL nStart = 0;
+ while ( nStart < nCols && pColInfo[nStart].nPixelEnd < rPixelArea.Left() )
+ ++nStart;
+
+ // cells completely right of the visible area
+ SCCOL nEnd = nCols;
+ while ( nEnd > 0 && pColInfo[nEnd-1].nPixelStart > rPixelArea.Right() )
+ --nEnd;
+
+ if ( nStart > 0 || nEnd < nCols )
+ {
+ if ( nEnd > nStart )
+ {
+ SCCOL nNewCount = nEnd - nStart;
+ ScPreviewColRowInfo* pNewInfo = new ScPreviewColRowInfo[nNewCount];
+ for (SCCOL i=0; i<nNewCount; i++)
+ pNewInfo[i] = pColInfo[nStart + i];
+ SetColInfo( nNewCount, pNewInfo );
+ }
+ else
+ SetColInfo( 0, nullptr ); // all invisible
+ }
+ }
+
+ if ( !pRowInfo )
+ return;
+
+ // cells completely above the visible area
+ SCROW nStart = 0;
+ while ( nStart < nRows && pRowInfo[nStart].nPixelEnd < rPixelArea.Top() )
+ ++nStart;
+
+ // cells completely below the visible area
+ SCROW nEnd = nRows;
+ while ( nEnd > 0 && pRowInfo[nEnd-1].nPixelStart > rPixelArea.Bottom() )
+ --nEnd;
+
+ if ( nStart <= 0 && nEnd >= nRows )
+ return;
+
+ if ( nEnd > nStart )
+ {
+ SCROW nNewCount = nEnd - nStart;
+ ScPreviewColRowInfo* pNewInfo = new ScPreviewColRowInfo[nNewCount];
+ for (SCROW i=0; i<nNewCount; i++)
+ pNewInfo[i] = pRowInfo[nStart + i];
+ SetRowInfo( nNewCount, pNewInfo );
+ }
+ else
+ SetRowInfo( 0, nullptr ); // all invisible
+}
+
+ScPreviewLocationData::ScPreviewLocationData( ScDocument* pDocument, OutputDevice* pWin ) :
+ pWindow( pWin ),
+ pDoc( pDocument ),
+ nDrawRanges( 0 ),
+ nPrintTab( 0 )
+{
+}
+
+ScPreviewLocationData::~ScPreviewLocationData()
+{
+ Clear();
+}
+
+void ScPreviewLocationData::SetCellMapMode( const MapMode& rMapMode )
+{
+ aCellMapMode = rMapMode;
+}
+
+void ScPreviewLocationData::SetPrintTab( SCTAB nNew )
+{
+ nPrintTab = nNew;
+}
+
+void ScPreviewLocationData::Clear()
+{
+ m_Entries.clear();
+
+ nDrawRanges = 0;
+}
+
+void ScPreviewLocationData::AddCellRange( const tools::Rectangle& rRect, const ScRange& rRange, bool bRepCol, bool bRepRow,
+ const MapMode& rDrawMap )
+{
+ tools::Rectangle aPixelRect( pWindow->LogicToPixel( rRect ) );
+ m_Entries.push_front( std::make_unique<ScPreviewLocationEntry>(SC_PLOC_CELLRANGE, aPixelRect, rRange, bRepCol, bRepRow) );
+
+ OSL_ENSURE( nDrawRanges < SC_PREVIEW_MAXRANGES, "too many ranges" );
+
+ if ( nDrawRanges >= SC_PREVIEW_MAXRANGES )
+ return;
+
+ aDrawRectangle[nDrawRanges] = aPixelRect;
+ aDrawMapMode[nDrawRanges] = rDrawMap;
+
+ if (bRepCol)
+ {
+ if (bRepRow)
+ aDrawRangeId[nDrawRanges] = SC_PREVIEW_RANGE_EDGE;
+ else
+ aDrawRangeId[nDrawRanges] = SC_PREVIEW_RANGE_REPCOL;
+ }
+ else
+ {
+ if (bRepRow)
+ aDrawRangeId[nDrawRanges] = SC_PREVIEW_RANGE_REPROW;
+ else
+ aDrawRangeId[nDrawRanges] = SC_PREVIEW_RANGE_TAB;
+ }
+
+ ++nDrawRanges;
+}
+
+void ScPreviewLocationData::AddColHeaders( const tools::Rectangle& rRect, SCCOL nStartCol, SCCOL nEndCol, bool bRepCol )
+{
+ SCTAB nTab = 0; //! ?
+ ScRange aRange( nStartCol, 0, nTab, nEndCol, 0, nTab );
+ tools::Rectangle aPixelRect( pWindow->LogicToPixel( rRect ) );
+
+ m_Entries.push_front( std::make_unique<ScPreviewLocationEntry>(SC_PLOC_COLHEADER, aPixelRect, aRange, bRepCol, false) );
+}
+
+void ScPreviewLocationData::AddRowHeaders( const tools::Rectangle& rRect, SCROW nStartRow, SCROW nEndRow, bool bRepRow )
+{
+ SCTAB nTab = 0; //! ?
+ ScRange aRange( 0, nStartRow, nTab, 0, nEndRow, nTab );
+ tools::Rectangle aPixelRect( pWindow->LogicToPixel( rRect ) );
+
+ m_Entries.push_front( std::make_unique<ScPreviewLocationEntry>(SC_PLOC_ROWHEADER, aPixelRect, aRange, false, bRepRow) );
+}
+
+void ScPreviewLocationData::AddHeaderFooter( const tools::Rectangle& rRect, bool bHeader, bool bLeft )
+{
+ ScRange aRange; //! ?
+ tools::Rectangle aPixelRect( pWindow->LogicToPixel( rRect ) );
+
+ ScPreviewLocationType eType = bHeader ?
+ ( bLeft ? SC_PLOC_LEFTHEADER : SC_PLOC_RIGHTHEADER ) :
+ ( bLeft ? SC_PLOC_LEFTFOOTER : SC_PLOC_RIGHTFOOTER );
+
+ m_Entries.push_front( std::make_unique<ScPreviewLocationEntry>(eType, aPixelRect, aRange, false, false) );
+}
+
+void ScPreviewLocationData::AddNoteMark( const tools::Rectangle& rRect, const ScAddress& rPos )
+{
+ ScRange aRange( rPos );
+ tools::Rectangle aPixelRect( pWindow->LogicToPixel( rRect ) );
+
+ m_Entries.push_front( std::make_unique<ScPreviewLocationEntry>(SC_PLOC_NOTEMARK, aPixelRect, aRange, false, false) );
+}
+
+void ScPreviewLocationData::AddNoteText( const tools::Rectangle& rRect, const ScAddress& rPos )
+{
+ ScRange aRange( rPos );
+ tools::Rectangle aPixelRect( pWindow->LogicToPixel( rRect ) );
+
+ m_Entries.push_front( std::make_unique<ScPreviewLocationEntry>(SC_PLOC_NOTETEXT, aPixelRect, aRange, false, false) );
+}
+
+void ScPreviewLocationData::GetDrawRange( sal_uInt16 nPos, tools::Rectangle& rPixelRect, MapMode& rMapMode, sal_uInt8& rRangeId ) const
+{
+ OSL_ENSURE( nPos < nDrawRanges, "wrong position" );
+ if ( nPos < nDrawRanges )
+ {
+ rPixelRect = aDrawRectangle[nPos];
+ rMapMode = aDrawMapMode[nPos];
+ rRangeId = aDrawRangeId[nPos];
+ }
+}
+
+static ScPreviewLocationEntry* lcl_GetEntryByAddress(
+ ScPreviewLocationData::Entries_t const& rEntries,
+ const ScAddress& rPos, ScPreviewLocationType const eType)
+{
+ for (auto const& it : rEntries)
+ {
+ if ( it->eType == eType && it->aCellRange.Contains( rPos ) )
+ return it.get();
+ }
+
+ return nullptr;
+}
+
+tools::Rectangle ScPreviewLocationData::GetOffsetPixel( const ScAddress& rCellPos, const ScRange& rRange ) const
+{
+ SCTAB nTab = rRange.aStart.Tab();
+
+ tools::Long nPosX = 0;
+ SCCOL nEndCol = rCellPos.Col();
+ for (SCCOL nCol = rRange.aStart.Col(); nCol < nEndCol; nCol++)
+ {
+ sal_uInt16 nDocW = pDoc->GetColWidth( nCol, nTab );
+ if (nDocW)
+ nPosX += o3tl::convert(nDocW, o3tl::Length::twip, o3tl::Length::mm100);
+ }
+ const tools::Long nSizeX
+ = o3tl::convert(pDoc->GetColWidth(nEndCol, nTab), o3tl::Length::twip, o3tl::Length::mm100);
+
+ SCROW nEndRow = rCellPos.Row();
+ tools::Long nPosY = o3tl::convert(pDoc->GetRowHeight(rRange.aStart.Row(), nEndRow, nTab),
+ o3tl::Length::twip, o3tl::Length::mm100);
+ tools::Long nSizeY
+ = o3tl::convert(pDoc->GetRowHeight(nEndRow, nTab), o3tl::Length::twip, o3tl::Length::mm100);
+
+ Size aOffsetLogic( nPosX, nPosY );
+ Size aSizeLogic( nSizeX, nSizeY );
+ Size aOffsetPixel = pWindow->LogicToPixel( aOffsetLogic, aCellMapMode );
+ Size aSizePixel = pWindow->LogicToPixel( aSizeLogic, aCellMapMode );
+
+ return tools::Rectangle( Point( aOffsetPixel.Width(), aOffsetPixel.Height() ), aSizePixel );
+}
+
+void ScPreviewLocationData::GetCellPosition( const ScAddress& rCellPos, tools::Rectangle& rCellRect ) const
+{
+ ScPreviewLocationEntry* pEntry = lcl_GetEntryByAddress( m_Entries, rCellPos, SC_PLOC_CELLRANGE );
+ if ( pEntry )
+ {
+ tools::Rectangle aOffsetRect = GetOffsetPixel( rCellPos, pEntry->aCellRange );
+ rCellRect = tools::Rectangle( aOffsetRect.Left() + pEntry->aPixelRect.Left(),
+ aOffsetRect.Top() + pEntry->aPixelRect.Top(),
+ aOffsetRect.Right() + pEntry->aPixelRect.Left(),
+ aOffsetRect.Bottom() + pEntry->aPixelRect.Top() );
+ }
+}
+
+bool ScPreviewLocationData::HasCellsInRange( const tools::Rectangle& rVisiblePixel ) const
+{
+ for (auto const& it : m_Entries)
+ {
+ if ( it->eType == SC_PLOC_CELLRANGE || it->eType == SC_PLOC_COLHEADER || it->eType == SC_PLOC_ROWHEADER )
+ if ( it->aPixelRect.Overlaps( rVisiblePixel ) )
+ return true;
+ }
+
+ return false;
+}
+
+bool ScPreviewLocationData::GetHeaderPosition( tools::Rectangle& rRect ) const
+{
+ for (auto const& it : m_Entries)
+ {
+ if ( it->eType == SC_PLOC_LEFTHEADER || it->eType == SC_PLOC_RIGHTHEADER )
+ {
+ rRect = it->aPixelRect;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool ScPreviewLocationData::GetFooterPosition( tools::Rectangle& rRect ) const
+{
+ for (auto const& it : m_Entries)
+ {
+ if ( it->eType == SC_PLOC_LEFTFOOTER || it->eType == SC_PLOC_RIGHTFOOTER )
+ {
+ rRect = it->aPixelRect;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool ScPreviewLocationData::IsHeaderLeft() const
+{
+ for (auto const& it : m_Entries)
+ {
+ if ( it->eType == SC_PLOC_LEFTHEADER )
+ return true;
+
+ if ( it->eType == SC_PLOC_RIGHTHEADER )
+ return false;
+ }
+
+ return false;
+}
+
+bool ScPreviewLocationData::IsFooterLeft() const
+{
+ for (auto const& it : m_Entries)
+ {
+ if ( it->eType == SC_PLOC_LEFTFOOTER )
+ return true;
+
+ if ( it->eType == SC_PLOC_RIGHTFOOTER )
+ return false;
+ }
+
+ return false;
+}
+
+tools::Long ScPreviewLocationData::GetNoteCountInRange( const tools::Rectangle& rVisiblePixel, bool bNoteMarks ) const
+{
+ ScPreviewLocationType eType = bNoteMarks ? SC_PLOC_NOTEMARK : SC_PLOC_NOTETEXT;
+
+ sal_uLong nRet = 0;
+ for (auto const& it : m_Entries)
+ {
+ if ( it->eType == eType && it->aPixelRect.Overlaps( rVisiblePixel ) )
+ ++nRet;
+ }
+
+ return nRet;
+}
+
+bool ScPreviewLocationData::GetNoteInRange( const tools::Rectangle& rVisiblePixel, tools::Long nIndex, bool bNoteMarks,
+ ScAddress& rCellPos, tools::Rectangle& rNoteRect ) const
+{
+ ScPreviewLocationType eType = bNoteMarks ? SC_PLOC_NOTEMARK : SC_PLOC_NOTETEXT;
+
+ sal_uLong nPos = 0;
+ for (auto const& it : m_Entries)
+ {
+ if ( it->eType == eType && it->aPixelRect.Overlaps( rVisiblePixel ) )
+ {
+ if ( nPos == sal::static_int_cast<sal_uLong>(nIndex) )
+ {
+ rCellPos = it->aCellRange.aStart;
+ rNoteRect = it->aPixelRect;
+ return true;
+ }
+ ++nPos;
+ }
+ }
+
+ return false;
+}
+
+tools::Rectangle ScPreviewLocationData::GetNoteInRangeOutputRect(const tools::Rectangle& rVisiblePixel, bool bNoteMarks, const ScAddress& aCellPos) const
+{
+ ScPreviewLocationType eType = bNoteMarks ? SC_PLOC_NOTEMARK : SC_PLOC_NOTETEXT;
+
+ for (auto const& it : m_Entries)
+ {
+ if ( it->eType == eType && it->aPixelRect.Overlaps( rVisiblePixel ) )
+ {
+ if ( aCellPos == it->aCellRange.aStart )
+ return it->aPixelRect;
+ }
+ }
+
+ return tools::Rectangle();
+}
+
+void ScPreviewLocationData::GetTableInfo( const tools::Rectangle& rVisiblePixel, ScPreviewTableInfo& rInfo ) const
+{
+ // from left to right:
+ bool bHasHeaderCol = false;
+ bool bHasRepCols = false;
+ bool bHasMainCols = false;
+ SCCOL nRepeatColStart = 0;
+ SCCOL nRepeatColEnd = 0;
+ SCCOL nMainColStart = 0;
+ SCCOL nMainColEnd = 0;
+
+ // from top to bottom:
+ bool bHasHeaderRow = false;
+ bool bHasRepRows = false;
+ bool bHasMainRows = false;
+ SCROW nRepeatRowStart = 0;
+ SCROW nRepeatRowEnd = 0;
+ SCROW nMainRowStart = 0;
+ SCROW nMainRowEnd = 0;
+
+ tools::Rectangle aHeaderRect, aRepeatRect, aMainRect;
+ SCTAB nTab = 0;
+
+ for (auto const& it : m_Entries)
+ {
+ if ( it->eType == SC_PLOC_CELLRANGE )
+ {
+ if ( it->bRepeatCol )
+ {
+ bHasRepCols = true;
+ nRepeatColStart = it->aCellRange.aStart.Col();
+ nRepeatColEnd = it->aCellRange.aEnd.Col();
+ aRepeatRect.SetLeft( it->aPixelRect.Left() );
+ aRepeatRect.SetRight( it->aPixelRect.Right() );
+ }
+ else
+ {
+ bHasMainCols = true;
+ nMainColStart = it->aCellRange.aStart.Col();
+ nMainColEnd = it->aCellRange.aEnd.Col();
+ aMainRect.SetLeft( it->aPixelRect.Left() );
+ aMainRect.SetRight( it->aPixelRect.Right() );
+ }
+ if ( it->bRepeatRow )
+ {
+ bHasRepRows = true;
+ nRepeatRowStart = it->aCellRange.aStart.Row();
+ nRepeatRowEnd = it->aCellRange.aEnd.Row();
+ aRepeatRect.SetTop( it->aPixelRect.Top() );
+ aRepeatRect.SetBottom( it->aPixelRect.Bottom() );
+ }
+ else
+ {
+ bHasMainRows = true;
+ nMainRowStart = it->aCellRange.aStart.Row();
+ nMainRowEnd = it->aCellRange.aEnd.Row();
+ aMainRect.SetTop( it->aPixelRect.Top() );
+ aMainRect.SetBottom( it->aPixelRect.Bottom() );
+ }
+ nTab = it->aCellRange.aStart.Tab(); //! store separately?
+ }
+ else if ( it->eType == SC_PLOC_ROWHEADER )
+ {
+ // row headers result in an additional column
+ bHasHeaderCol = true;
+ aHeaderRect.SetLeft( it->aPixelRect.Left() );
+ aHeaderRect.SetRight( it->aPixelRect.Right() );
+ }
+ else if ( it->eType == SC_PLOC_COLHEADER )
+ {
+ // column headers result in an additional row
+ bHasHeaderRow = true;
+ aHeaderRect.SetTop( it->aPixelRect.Top() );
+ aHeaderRect.SetBottom( it->aPixelRect.Bottom() );
+ }
+ }
+
+ // get column info
+
+ SCCOL nColCount = 0;
+ SCCOL nCol;
+ if ( bHasHeaderCol )
+ ++nColCount;
+ if ( bHasRepCols )
+ for ( nCol=nRepeatColStart; nCol<=nRepeatColEnd; nCol++ )
+ if (!pDoc->ColHidden(nCol, nTab))
+ ++nColCount;
+ if ( bHasMainCols )
+ for ( nCol=nMainColStart; nCol<=nMainColEnd; nCol++ )
+ if (!pDoc->ColHidden(nCol, nTab))
+ ++nColCount;
+
+ if ( nColCount > 0 )
+ {
+ ScPreviewColRowInfo* pColInfo = new ScPreviewColRowInfo[ nColCount ];
+ SCCOL nColPos = 0;
+
+ if ( bHasHeaderCol )
+ {
+ pColInfo[nColPos].Set( true, 0, aHeaderRect.Left(), aHeaderRect.Right() );
+ ++nColPos;
+ }
+ if ( bHasRepCols )
+ {
+ tools::Long nPosX = 0;
+ for ( nCol=nRepeatColStart; nCol<=nRepeatColEnd; nCol++ )
+ if (!pDoc->ColHidden(nCol, nTab))
+ {
+ sal_uInt16 nDocW = pDoc->GetColWidth( nCol, nTab );
+ tools::Long nNextX
+ = nPosX + o3tl::convert(nDocW, o3tl::Length::twip, o3tl::Length::mm100);
+
+ tools::Long nPixelStart = pWindow->LogicToPixel( Size( nPosX, 0 ), aCellMapMode ).Width();
+ tools::Long nPixelEnd = pWindow->LogicToPixel( Size( nNextX, 0 ), aCellMapMode ).Width() - 1;
+ pColInfo[nColPos].Set( false, nCol,
+ aRepeatRect.Left() + nPixelStart,
+ aRepeatRect.Left() + nPixelEnd );
+
+ nPosX = nNextX;
+ ++nColPos;
+ }
+ }
+ if ( bHasMainCols )
+ {
+ tools::Long nPosX = 0;
+ for ( nCol=nMainColStart; nCol<=nMainColEnd; nCol++ )
+ if (!pDoc->ColHidden(nCol, nTab))
+ {
+ sal_uInt16 nDocW = pDoc->GetColWidth( nCol, nTab );
+ tools::Long nNextX
+ = nPosX + o3tl::convert(nDocW, o3tl::Length::twip, o3tl::Length::mm100);
+
+ tools::Long nPixelStart = pWindow->LogicToPixel( Size( nPosX, 0 ), aCellMapMode ).Width();
+ tools::Long nPixelEnd = pWindow->LogicToPixel( Size( nNextX, 0 ), aCellMapMode ).Width() - 1;
+ pColInfo[nColPos].Set( false, nCol,
+ aMainRect.Left() + nPixelStart,
+ aMainRect.Left() + nPixelEnd );
+
+ nPosX = nNextX;
+ ++nColPos;
+ }
+ }
+ rInfo.SetColInfo( nColCount, pColInfo );
+ }
+ else
+ rInfo.SetColInfo( 0, nullptr );
+
+ // get row info
+
+ SCROW nRowCount = 0;
+ if ( bHasHeaderRow )
+ ++nRowCount;
+ if ( bHasRepRows )
+ nRowCount += pDoc->CountVisibleRows(nRepeatRowStart, nRepeatRowEnd, nTab);
+ if ( bHasMainRows )
+ nRowCount += pDoc->CountVisibleRows(nMainRowStart, nMainRowEnd, nTab);
+
+ if ( nRowCount > 0 )
+ {
+ ScPreviewColRowInfo* pRowInfo = new ScPreviewColRowInfo[ nRowCount ];
+ SCROW nRowPos = 0;
+
+ if ( bHasHeaderRow )
+ {
+ pRowInfo[nRowPos].Set( true, 0, aHeaderRect.Top(), aHeaderRect.Bottom() );
+ ++nRowPos;
+ }
+ if ( bHasRepRows )
+ {
+ tools::Long nPosY = 0;
+ for (SCROW nRow = nRepeatRowStart; nRow <= nRepeatRowEnd; ++nRow)
+ {
+ if (pDoc->RowHidden(nRow, nTab))
+ continue;
+
+ sal_uInt16 nDocH = pDoc->GetOriginalHeight( nRow, nTab );
+ tools::Long nNextY
+ = nPosY + o3tl::convert(nDocH, o3tl::Length::twip, o3tl::Length::mm100);
+
+ tools::Long nPixelStart = pWindow->LogicToPixel( Size( 0, nPosY ), aCellMapMode ).Height();
+ tools::Long nPixelEnd = pWindow->LogicToPixel( Size( 0, nNextY ), aCellMapMode ).Height() - 1;
+ pRowInfo[nRowPos].Set( false, nRow,
+ aRepeatRect.Top() + nPixelStart,
+ aRepeatRect.Top() + nPixelEnd );
+
+ nPosY = nNextY;
+ ++nRowPos;
+ }
+ }
+ if ( bHasMainRows )
+ {
+ tools::Long nPosY = 0;
+ for (SCROW nRow = nMainRowStart; nRow <= nMainRowEnd; ++nRow)
+ {
+ if (pDoc->RowHidden(nRow, nTab))
+ continue;
+
+ sal_uInt16 nDocH = pDoc->GetOriginalHeight( nRow, nTab );
+ tools::Long nNextY
+ = nPosY + o3tl::convert(nDocH, o3tl::Length::twip, o3tl::Length::mm100);
+
+ tools::Long nPixelStart = pWindow->LogicToPixel( Size( 0, nPosY ), aCellMapMode ).Height();
+ tools::Long nPixelEnd = pWindow->LogicToPixel( Size( 0, nNextY ), aCellMapMode ).Height() - 1;
+ pRowInfo[nRowPos].Set( false, nRow,
+ aMainRect.Top() + nPixelStart,
+ aMainRect.Top() + nPixelEnd );
+
+ nPosY = nNextY;
+ ++nRowPos;
+ }
+ }
+ rInfo.SetRowInfo( nRowCount, pRowInfo );
+ }
+ else
+ rInfo.SetRowInfo( 0, nullptr );
+
+ // limit to visible area
+
+ rInfo.SetTab( nTab );
+ rInfo.LimitToArea( rVisiblePixel );
+}
+
+tools::Rectangle ScPreviewLocationData::GetHeaderCellOutputRect(const tools::Rectangle& rVisRect, const ScAddress& rCellPos, bool bColHeader) const
+{
+ // first a stupid implementation
+ // NN says here should be done more
+ tools::Rectangle aClipRect;
+ ScPreviewTableInfo aTableInfo;
+ GetTableInfo( rVisRect, aTableInfo );
+
+ if ( (rCellPos.Col() >= 0) &&
+ (rCellPos.Row() >= 0) && (rCellPos.Col() < aTableInfo.GetCols()) &&
+ (rCellPos.Row() < aTableInfo.GetRows()) )
+ {
+ SCCOL nCol(0);
+ SCROW nRow(0);
+ if (bColHeader)
+ nCol = rCellPos.Col();
+ else
+ nRow = rCellPos.Row();
+ const ScPreviewColRowInfo& rColInfo = aTableInfo.GetColInfo()[nCol];
+ const ScPreviewColRowInfo& rRowInfo = aTableInfo.GetRowInfo()[nRow];
+
+ if ( rColInfo.bIsHeader || rRowInfo.bIsHeader )
+ aClipRect = tools::Rectangle( rColInfo.nPixelStart, rRowInfo.nPixelStart, rColInfo.nPixelEnd, rRowInfo.nPixelEnd );
+ }
+ return aClipRect;
+}
+
+tools::Rectangle ScPreviewLocationData::GetCellOutputRect(const ScAddress& rCellPos) const
+{
+ // first a stupid implementation
+ // NN says here should be done more
+ tools::Rectangle aRect;
+ GetCellPosition(rCellPos, aRect);
+ return aRect;
+}
+
+// GetMainCellRange is used for links in PDF export
+
+bool ScPreviewLocationData::GetMainCellRange( ScRange& rRange, tools::Rectangle& rPixRect ) const
+{
+ for (auto const& it : m_Entries)
+ {
+ if ( it->eType == SC_PLOC_CELLRANGE && !it->bRepeatCol && !it->bRepeatRow )
+ {
+ rRange = it->aCellRange;
+ rPixRect = it->aPixelRect;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/prevwsh.cxx b/sc/source/ui/view/prevwsh.cxx
new file mode 100644
index 0000000000..c526331d58
--- /dev/null
+++ b/sc/source/ui/view/prevwsh.cxx
@@ -0,0 +1,1181 @@
+/* -*- 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 .
+ */
+
+#include <sal/config.h>
+
+#include <scitems.hxx>
+
+#include <comphelper/SetFlagContextHelper.hxx>
+#include <sfx2/app.hxx>
+#include <editeng/sizeitem.hxx>
+#include <svx/zoomslideritem.hxx>
+#include <svx/svdview.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/request.hxx>
+#include <svl/stritem.hxx>
+#include <svl/whiter.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/help.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <tools/urlobj.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/printer.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/viewfac.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <drwlayer.hxx>
+#include <prevwsh.hxx>
+#include <preview.hxx>
+#include <printfun.hxx>
+#include <scmod.hxx>
+#include <inputhdl.hxx>
+#include <docsh.hxx>
+#include <tabvwsh.hxx>
+#include <stlpool.hxx>
+#include <editutil.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <sc.hrc>
+#include <ViewSettingsSequenceDefines.hxx>
+#include <viewuno.hxx>
+
+#include <svx/svxdlg.hxx>
+#include <svx/dialogs.hrc>
+
+#include <basegfx/utils/zoomtools.hxx>
+#include <svx/zoom_def.hxx>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+
+#include <scabstdlg.hxx>
+#include <vcl/EnumContext.hxx>
+#include <vcl/notebookbar/notebookbar.hxx>
+
+// for mouse wheel
+#define MINZOOM_SLIDER 10
+#define MAXZOOM_SLIDER 400
+
+#define SC_USERDATA_SEP ';'
+
+using namespace com::sun::star;
+
+#define ShellClass_ScPreviewShell
+#include <scslots.hxx>
+
+#include <memory>
+
+
+SFX_IMPL_INTERFACE(ScPreviewShell, SfxViewShell)
+
+void ScPreviewShell::InitInterface_Impl()
+{
+ GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT,
+ SfxVisibilityFlags::Standard|SfxVisibilityFlags::Server|SfxVisibilityFlags::ReadonlyDoc,
+ ToolbarId::Objectbar_Preview);
+
+ GetStaticInterface()->RegisterPopupMenu("preview");
+}
+
+SFX_IMPL_NAMED_VIEWFACTORY( ScPreviewShell, "PrintPreview" )
+{
+ SFX_VIEW_REGISTRATION(ScDocShell);
+}
+
+void ScPreviewShell::Construct( vcl::Window* pParent )
+{
+ // Find the top-most window, and set the close window handler to intercept
+ // the window close event.
+ vcl::Window* pWin = pParent;
+ while (!pWin->IsSystemWindow())
+ {
+ if (pWin->GetParent())
+ pWin = pWin->GetParent();
+ else
+ break;
+ }
+
+ mpFrameWindow = dynamic_cast<SystemWindow*>(pWin);
+ if (mpFrameWindow)
+ mpFrameWindow->SetCloseHdl(LINK(this, ScPreviewShell, CloseHdl));
+
+ eZoom = SvxZoomType::WHOLEPAGE;
+
+ pHorScroll = VclPtr<ScrollAdaptor>::Create(pParent, true);
+ pVerScroll = VclPtr<ScrollAdaptor>::Create(pParent, false);
+
+ // RTL: no mirroring for horizontal scrollbars
+ pHorScroll->EnableRTL( false );
+
+ pHorScroll->SetScrollHdl(LINK(this, ScPreviewShell, HorzScrollHandler));
+ pVerScroll->SetScrollHdl(LINK(this, ScPreviewShell, VertScrollHandler));
+
+ pPreview = VclPtr<ScPreview>::Create( pParent, pDocShell, this );
+
+ SetPool( &SC_MOD()->GetPool() );
+ SetWindow( pPreview );
+ StartListening(*pDocShell, DuplicateHandling::Prevent);
+ StartListening(*SfxGetpApp(), DuplicateHandling::Prevent); // #i62045# #i62046# application is needed for Calc's own hints
+ SfxBroadcaster* pDrawBC = pDocShell->GetDocument().GetDrawBroadcaster();
+ if (pDrawBC)
+ StartListening(*pDrawBC);
+
+ pHorScroll->Show( false );
+ pVerScroll->Show( false );
+ SetName("Preview");
+}
+
+ScPreviewShell::ScPreviewShell(SfxViewFrame& rViewFrame,
+ SfxViewShell* pOldSh) :
+ SfxViewShell(rViewFrame, SfxViewShellFlags::HAS_PRINTOPTIONS),
+ pDocShell( static_cast<ScDocShell*>(rViewFrame.GetObjectShell()) ),
+ mpFrameWindow(nullptr),
+ nSourceDesignMode( TRISTATE_INDET ),
+ nMaxVertPos(0),
+ nPrevHThumbPos(0),
+ nPrevVThumbPos(0)
+{
+ Construct(&rViewFrame.GetWindow());
+ SfxShell::SetContextName(vcl::EnumContext::GetContextName(vcl::EnumContext::Context::Printpreview));
+
+ if ( auto pTabViewShell = dynamic_cast<ScTabViewShell*>( pOldSh) )
+ {
+ // store view settings, show table from TabView
+ //! store live ScViewData instead, and update on ScTablesHint?
+ //! or completely forget aSourceData on ScTablesHint?
+
+ const ScViewData& rData = pTabViewShell->GetViewData();
+ pPreview->SetSelectedTabs(rData.GetMarkData());
+ InitStartTable( rData.GetTabNo() );
+
+ // also have to store the TabView's DesignMode state
+ // (only if draw view exists)
+ SdrView* pDrawView = pTabViewShell->GetScDrawView();
+ if ( pDrawView )
+ nSourceDesignMode
+ = pDrawView->IsDesignMode() ? TRISTATE_TRUE : TRISTATE_FALSE;
+ }
+
+ new ScPreviewObj(this);
+}
+
+ScPreviewShell::~ScPreviewShell()
+{
+ if (mpFrameWindow)
+ mpFrameWindow->SetCloseHdl(Link<SystemWindow&,void>()); // Remove close handler.
+
+ // #108333#; notify Accessibility that Shell is dying and before destroy all
+ BroadcastAccessibility( SfxHint( SfxHintId::Dying ) );
+ pAccessibilityBroadcaster.reset();
+
+ SfxBroadcaster* pDrawBC = pDocShell->GetDocument().GetDrawBroadcaster();
+ if (pDrawBC)
+ EndListening(*pDrawBC);
+ EndListening(*SfxGetpApp());
+ EndListening(*pDocShell);
+
+ SetWindow(nullptr);
+ pPreview.disposeAndClear();
+ pHorScroll.disposeAndClear();
+ pVerScroll.disposeAndClear();
+
+ // normal mode of operation is switching back to default view in the same frame,
+ // so there's no need to activate any other window here anymore
+}
+
+void ScPreviewShell::InitStartTable(SCTAB nTab)
+{
+ pPreview->SetPageNo( pPreview->GetFirstPage(nTab) );
+}
+
+void ScPreviewShell::AdjustPosSizePixel( const Point &rPos, const Size &rSize )
+{
+ Size aOutSize( rSize );
+ pPreview->SetPosSizePixel( rPos, aOutSize );
+
+ if ( SvxZoomType::WHOLEPAGE == eZoom )
+ pPreview->SetZoom( pPreview->GetOptimalZoom(false) );
+ else if ( SvxZoomType::PAGEWIDTH == eZoom )
+ pPreview->SetZoom( pPreview->GetOptimalZoom(true) );
+
+ UpdateNeededScrollBars(false);
+}
+
+void ScPreviewShell::InnerResizePixel( const Point &rOfs, const Size &rSize, bool )
+{
+ AdjustPosSizePixel( rOfs,rSize );
+}
+
+void ScPreviewShell::OuterResizePixel( const Point &rOfs, const Size &rSize )
+{
+ AdjustPosSizePixel( rOfs,rSize );
+}
+
+bool ScPreviewShell::GetPageSize( Size& aPageSize )
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ SCTAB nTab = pPreview->GetTab();
+
+ ScStyleSheetPool* pStylePool = rDoc.GetStyleSheetPool();
+ SfxStyleSheetBase* pStyleSheet = pStylePool->Find( rDoc.GetPageStyle( nTab ),
+ SfxStyleFamily::Page );
+ OSL_ENSURE(pStyleSheet,"No style sheet");
+ if (!pStyleSheet) return false;
+ const SfxItemSet* pParamSet = &pStyleSheet->GetItemSet();
+
+ aPageSize = pParamSet->Get(ATTR_PAGE_SIZE).GetSize();
+ aPageSize.setWidth(o3tl::convert(aPageSize.Width(), o3tl::Length::twip, o3tl::Length::mm100));
+ aPageSize.setHeight(o3tl::convert(aPageSize.Height(), o3tl::Length::twip, o3tl::Length::mm100));
+ return true;
+}
+
+void ScPreviewShell::UpdateNeededScrollBars( bool bFromZoom )
+{
+ Size aPageSize;
+ OutputDevice* pDevice = Application::GetDefaultDevice();
+
+ tools::Long nBarW = GetViewFrame().GetWindow().GetSettings().GetStyleSettings().GetScrollBarSize();
+ tools::Long nBarH = nBarW;
+
+ tools::Long aHeightOffSet = pDevice ? pDevice->PixelToLogic( Size( nBarW, nBarH ), pPreview->GetMapMode() ).Height() : 0;
+ tools::Long aWidthOffSet = aHeightOffSet;
+
+ if (!GetPageSize( aPageSize ))
+ return;
+
+ // for centering, page size without the shadow is used
+ bool bVert = pVerScroll->IsVisible();
+ bool bHori = pHorScroll->IsVisible();
+ Size aWindowSize = pPreview->GetOutDev()->GetOutputSize();
+ Point aPos = pPreview->GetPosPixel();
+ Size aWindowPixelSize = pPreview->GetOutputSizePixel();
+
+ // if we are called from Zoom then we need to compensate for whatever
+ // scrollbars were displayed before the zoom was called
+ if ( bFromZoom )
+ {
+ if ( bVert )
+ {
+ aWindowPixelSize.AdjustWidth(nBarH );
+ aWindowSize.AdjustWidth(aHeightOffSet );
+ }
+ if ( bHori )
+ {
+ aWindowPixelSize.AdjustHeight(nBarW );
+ aWindowSize.AdjustHeight(aWidthOffSet );
+ }
+ }
+
+ // recalculate any needed scrollbars
+ tools::Long nMaxWidthPos = aPageSize.Width() - aWindowSize.Width();
+ bHori = nMaxWidthPos >= 0;
+ tools::Long nMaxHeightPos = aPageSize.Height() - aWindowSize.Height();
+ bVert = nMaxHeightPos >= 0;
+
+ // see if having a scroll bar requires the other
+ if ( bVert != bHori && ( bVert || bHori ) )
+ {
+ if ( bVert && ( (nMaxWidthPos + aWidthOffSet ) > 0 ) )
+ bHori = true;
+ else if ( (nMaxHeightPos + aHeightOffSet ) > 0 )
+ bVert = true;
+ }
+ pHorScroll->Show( bHori );
+ pVerScroll->Show( bVert );
+
+ // make room for needed scrollbars ( and reduce the size
+ // of the preview appropriately )
+ if ( bHori )
+ aWindowPixelSize.AdjustHeight( -nBarW );
+ if ( bVert )
+ aWindowPixelSize.AdjustWidth( -nBarH );
+
+ pPreview->SetSizePixel( aWindowPixelSize );
+ pHorScroll->SetPosSizePixel( Point( aPos.X(), aPos.Y() + aWindowPixelSize.Height() ),
+ Size( aWindowPixelSize.Width(), nBarH ) );
+ pVerScroll->SetPosSizePixel( Point( aPos.X() + aWindowPixelSize.Width(), aPos.Y() ),
+ Size( nBarW, aWindowPixelSize.Height() ) );
+ UpdateScrollBars();
+}
+
+void ScPreviewShell::UpdateScrollBars()
+{
+ Size aPageSize;
+ if ( !GetPageSize( aPageSize ) )
+ return;
+
+ // for centering, page size without the shadow is used
+
+ Size aWindowSize = pPreview->GetOutDev()->GetOutputSize();
+
+ Point aOfs = pPreview->GetOffset();
+
+ if( pHorScroll )
+ {
+ pHorScroll->SetRange( Range( 0, aPageSize.Width() ) );
+ pHorScroll->SetLineSize( aWindowSize.Width() / 16 );
+ pHorScroll->SetPageSize( aWindowSize.Width() );
+ pHorScroll->SetVisibleSize( aWindowSize.Width() );
+ tools::Long nMaxPos = aPageSize.Width() - aWindowSize.Width();
+ if ( nMaxPos<0 )
+ {
+ // page smaller than window -> center (but put scrollbar to 0)
+ aOfs.setX( 0 );
+ pPreview->SetXOffset( nMaxPos / 2 );
+ }
+ else if (aOfs.X() < 0)
+ {
+ // page larger than window -> never use negative offset
+ aOfs.setX( 0 );
+ pPreview->SetXOffset( 0 );
+ }
+ else if (aOfs.X() > nMaxPos)
+ {
+ // limit offset to align with right edge of window
+ aOfs.setX( nMaxPos );
+ pPreview->SetXOffset(nMaxPos);
+ }
+ pHorScroll->SetThumbPos( aOfs.X() );
+ nPrevHThumbPos = pHorScroll->GetThumbPos();
+ }
+
+ if( !pVerScroll )
+ return;
+
+ tools::Long nPageNo = pPreview->GetPageNo();
+ tools::Long nTotalPages = pPreview->GetTotalPages();
+
+ nMaxVertPos = aPageSize.Height() - aWindowSize.Height();
+ pVerScroll->SetLineSize( aWindowSize.Height() / 16 );
+ pVerScroll->SetPageSize( aWindowSize.Height() );
+ pVerScroll->SetVisibleSize( aWindowSize.Height() );
+ if ( nMaxVertPos < 0 )
+ {
+ // page smaller than window -> center (but put scrollbar to 0)
+ aOfs.setY( 0 );
+ pPreview->SetYOffset( nMaxVertPos / 2 );
+ pVerScroll->SetThumbPos( nPageNo * aWindowSize.Height() );
+ pVerScroll->SetRange( Range( 0, aWindowSize.Height() * nTotalPages ));
+ }
+ else if (aOfs.Y() < 0)
+ {
+ // page larger than window -> never use negative offset
+ pVerScroll->SetRange( Range( 0, aPageSize.Height() ) );
+ aOfs.setY( 0 );
+ pPreview->SetYOffset( 0 );
+ pVerScroll->SetThumbPos( aOfs.Y() );
+ }
+ else if (aOfs.Y() > nMaxVertPos )
+ {
+ // limit offset to align with window bottom
+ pVerScroll->SetRange( Range( 0, aPageSize.Height() ) );
+ aOfs.setY( nMaxVertPos );
+ pPreview->SetYOffset( nMaxVertPos );
+ pVerScroll->SetThumbPos( aOfs.Y() );
+ }
+ nPrevVThumbPos = pVerScroll->GetThumbPos();
+}
+
+IMPL_LINK_NOARG(ScPreviewShell, HorzScrollHandler, weld::Scrollbar&, void)
+{
+ ScrollHandler(pHorScroll);
+}
+
+IMPL_LINK_NOARG(ScPreviewShell, VertScrollHandler, weld::Scrollbar&, void)
+{
+ ScrollHandler(pVerScroll);
+}
+
+void ScPreviewShell::ScrollHandler(ScrollAdaptor* pScroll)
+{
+ tools::Long nPos = pScroll->GetThumbPos();
+ tools::Long nMaxRange = pScroll->GetRangeMax();
+ tools::Long nTotalPages = pPreview->GetTotalPages();
+ tools::Long nPageNo = 0;
+ tools::Long nPerPageLength = 0;
+ bool bIsDivide = true;
+
+ if( nTotalPages )
+ nPerPageLength = nMaxRange / nTotalPages;
+
+ if( nPerPageLength )
+ {
+ nPageNo = nPos / nPerPageLength;
+ if( nPos % nPerPageLength )
+ {
+ bIsDivide = false;
+ nPageNo ++;
+ }
+ }
+
+ bool bHoriz = ( pScroll == pHorScroll );
+
+ tools::Long nDelta = bHoriz ? (pHorScroll->GetThumbPos() - nPrevHThumbPos)
+ : (pVerScroll->GetThumbPos() - nPrevVThumbPos);
+
+ if( bHoriz )
+ pPreview->SetXOffset( nPos );
+ else
+ {
+ if( nMaxVertPos > 0 )
+ pPreview->SetYOffset( nPos );
+ else
+ {
+ Point aMousePos = pScroll->OutputToNormalizedScreenPixel( pScroll->GetPointerPosPixel() );
+ Point aPos = pScroll->GetParent()->OutputToNormalizedScreenPixel( pScroll->GetPosPixel() );
+ OUString aHelpStr;
+ tools::Rectangle aRect;
+ QuickHelpFlags nAlign;
+
+ if( nDelta < 0 )
+ {
+ if ( nTotalPages && nPageNo > 0 && !bIsDivide )
+ pPreview->SetPageNo( nPageNo-1 );
+ if( bIsDivide )
+ pPreview->SetPageNo( nPageNo );
+
+ aHelpStr = ScResId( STR_PAGE ) +
+ " " + OUString::number( nPageNo ) +
+ " / " + OUString::number( nTotalPages );
+ }
+ else if( nDelta > 0 )
+ {
+ bool bAllTested = pPreview->AllTested();
+ if ( nTotalPages && ( nPageNo < nTotalPages || !bAllTested ) )
+ pPreview->SetPageNo( nPageNo );
+
+ aHelpStr = ScResId( STR_PAGE ) +
+ " " + OUString::number( nPageNo+1 ) +
+ " / " + OUString::number( nTotalPages );
+ }
+
+ aRect.SetLeft( aPos.X() - 8 );
+ aRect.SetTop( aMousePos.Y() );
+ aRect.SetRight( aRect.Left() );
+ aRect.SetBottom( aRect.Top() );
+ nAlign = QuickHelpFlags::Bottom|QuickHelpFlags::Center;
+ Help::ShowQuickHelp( pScroll->GetParent(), aRect, aHelpStr, nAlign );
+ }
+ }
+}
+
+IMPL_LINK_NOARG(ScPreviewShell, CloseHdl, SystemWindow&, void)
+{
+ ExitPreview();
+}
+
+bool ScPreviewShell::ScrollCommand( const CommandEvent& rCEvt )
+{
+ bool bDone = false;
+ const CommandWheelData* pData = rCEvt.GetWheelData();
+ if ( pData && pData->GetMode() == CommandWheelMode::ZOOM )
+ {
+ sal_uInt16 nOld = pPreview->GetZoom();
+ sal_uInt16 nNew;
+ if ( pData->GetDelta() < 0 )
+ nNew = std::max( MINZOOM, basegfx::zoomtools::zoomOut( nOld ));
+ else
+ nNew = std::min( MAXZOOM, basegfx::zoomtools::zoomIn( nOld ));
+
+ if ( nNew != nOld )
+ {
+ eZoom = SvxZoomType::PERCENT;
+ pPreview->SetZoom( nNew );
+ }
+
+ bDone = true;
+ }
+ else
+ {
+ bDone = pPreview->HandleScrollCommand( rCEvt, pHorScroll, pVerScroll );
+ }
+
+ return bDone;
+}
+
+SfxPrinter* ScPreviewShell::GetPrinter( bool bCreate )
+{
+ return pDocShell->GetPrinter(bCreate);
+}
+
+sal_uInt16 ScPreviewShell::SetPrinter( SfxPrinter *pNewPrinter, SfxPrinterChangeFlags nDiffFlags )
+{
+ return pDocShell->SetPrinter( pNewPrinter, nDiffFlags );
+}
+
+bool ScPreviewShell::HasPrintOptionsPage() const
+{
+ return true;
+}
+
+std::unique_ptr<SfxTabPage> ScPreviewShell::CreatePrintOptionsPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rOptions)
+{
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+ ::CreateTabPage ScTpPrintOptionsCreate = pFact->GetTabPageCreatorFunc(RID_SC_TP_PRINT);
+ if ( ScTpPrintOptionsCreate )
+ return ScTpPrintOptionsCreate(pPage, pController, &rOptions);
+ return nullptr;
+}
+
+void ScPreviewShell::Activate(bool bMDI)
+{
+ SfxViewShell::Activate(bMDI);
+
+ //! Basic etc. -> outsource to its own file (see tabvwsh4)
+
+ if (bMDI)
+ {
+ // InputHdl is now mostly Null, no more assertion!
+ ScInputHandler* pInputHdl = SC_MOD()->GetInputHdl();
+ if ( pInputHdl )
+ pInputHdl->NotifyChange( nullptr );
+ }
+
+ SfxShell::Activate(bMDI);
+}
+
+void ScPreviewShell::Execute( SfxRequest& rReq )
+{
+ sal_uInt16 nSlot = rReq.GetSlot();
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+
+ switch ( nSlot )
+ {
+ case SID_FORMATPAGE:
+ case SID_STATUS_PAGESTYLE:
+ case SID_HFEDIT:
+ pDocShell->ExecutePageStyle( *this, rReq, pPreview->GetTab() );
+ break;
+ case SID_REPAINT:
+ pPreview->Invalidate();
+ rReq.Done();
+ break;
+ case SID_PREV_TABLE: // Accelerator
+ case SID_PREVIEW_PREVIOUS:
+ {
+ tools::Long nPage = pPreview->GetPageNo();
+ tools::Long nTotal = pPreview->GetTotalPages();
+ if (nTotal && nPage > 0)
+ pPreview->SetPageNo( nPage-1 );
+ }
+ break;
+ case SID_NEXT_TABLE: // Accelerator
+ case SID_PREVIEW_NEXT:
+ {
+ bool bAllTested = pPreview->AllTested();
+ tools::Long nPage = pPreview->GetPageNo();
+ tools::Long nTotal = pPreview->GetTotalPages();
+ if (nTotal && (nPage+1 < nTotal || !bAllTested))
+ pPreview->SetPageNo( nPage+1 );
+ }
+ break;
+ case SID_CURSORTOPOFFILE: // Accelerator
+ case SID_PREVIEW_FIRST:
+ {
+ tools::Long nPage = pPreview->GetPageNo();
+ tools::Long nTotal = pPreview->GetTotalPages();
+ if (nTotal && nPage != 0)
+ pPreview->SetPageNo( 0 );
+ }
+ break;
+ case SID_CURSORENDOFFILE: // Accelerator
+ case SID_PREVIEW_LAST:
+ {
+ if (!pPreview->AllTested())
+ pPreview->CalcAll();
+
+ tools::Long nPage = pPreview->GetPageNo();
+ tools::Long nTotal = pPreview->GetTotalPages();
+ if (nTotal && nPage+1 != nTotal)
+ pPreview->SetPageNo( nTotal-1 );
+ }
+ break;
+ case SID_ATTR_ZOOM:
+ case FID_SCALE:
+ {
+ sal_uInt16 nZoom = 100;
+ bool bCancel = false;
+
+ eZoom = SvxZoomType::PERCENT;
+
+ if ( pReqArgs )
+ {
+
+ const SvxZoomItem& rZoomItem = pReqArgs->Get(SID_ATTR_ZOOM);
+
+ eZoom = rZoomItem.GetType();
+ nZoom = rZoomItem.GetValue();
+ }
+ else
+ {
+ SfxItemSetFixed<SID_ATTR_ZOOM, SID_ATTR_ZOOM> aSet( GetPool() );
+ SvxZoomItem aZoomItem( SvxZoomType::PERCENT, pPreview->GetZoom(), SID_ATTR_ZOOM );
+
+ aSet.Put( aZoomItem );
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractSvxZoomDialog> pDlg(pFact->CreateSvxZoomDialog(nullptr, aSet));
+ pDlg->SetLimits( 20, 400 );
+ pDlg->HideButton( ZoomButtonId::OPTIMAL );
+ bCancel = ( RET_CANCEL == pDlg->Execute() );
+
+ if ( !bCancel )
+ {
+ const SvxZoomItem& rZoomItem = pDlg->GetOutputItemSet()->
+ Get( SID_ATTR_ZOOM );
+
+ eZoom = rZoomItem.GetType();
+ nZoom = rZoomItem.GetValue();
+ }
+ }
+
+ if ( !bCancel )
+ {
+ switch ( eZoom )
+ {
+ case SvxZoomType::OPTIMAL:
+ case SvxZoomType::WHOLEPAGE:
+ nZoom = pPreview->GetOptimalZoom(false);
+ break;
+ case SvxZoomType::PAGEWIDTH:
+ nZoom = pPreview->GetOptimalZoom(true);
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+
+ pPreview->SetZoom( nZoom );
+ rReq.Done();
+ }
+ }
+ break;
+ case SID_ZOOM_IN:
+ {
+ sal_uInt16 nNew = pPreview->GetZoom() + 20 ;
+ nNew -= nNew % 20;
+ pPreview->SetZoom( nNew );
+ eZoom = SvxZoomType::PERCENT;
+ rReq.Done();
+ }
+ break;
+ case SID_ZOOM_OUT:
+ {
+ sal_uInt16 nNew = pPreview->GetZoom() - 1;
+ nNew -= nNew % 20;
+ pPreview->SetZoom( nNew );
+ eZoom = SvxZoomType::PERCENT;
+ rReq.Done();
+ }
+ break;
+ case SID_PREVIEW_MARGIN:
+ {
+ bool bMargin = pPreview->GetPageMargins();
+ pPreview->SetPageMargins( !bMargin );
+ pPreview->Invalidate();
+ rReq.Done();
+ }
+ break;
+ case SID_ATTR_ZOOMSLIDER:
+ {
+ const SvxZoomSliderItem* pItem;
+ eZoom = SvxZoomType::PERCENT;
+ if( pReqArgs && (pItem = pReqArgs->GetItemIfSet( SID_ATTR_ZOOMSLIDER )) )
+ {
+ const sal_uInt16 nCurrentZoom = pItem->GetValue();
+ if( nCurrentZoom )
+ {
+ pPreview->SetZoom( nCurrentZoom );
+ rReq.Done();
+ }
+ }
+ }
+ break;
+ case SID_PREVIEW_SCALINGFACTOR:
+ {
+ const SvxZoomSliderItem* pItem;
+ SCTAB nTab = pPreview->GetTab();
+ OUString aOldName = pDocShell->GetDocument().GetPageStyle( pPreview->GetTab() );
+ ScStyleSheetPool* pStylePool = pDocShell->GetDocument().GetStyleSheetPool();
+ SfxStyleSheetBase* pStyleSheet = pStylePool->Find( aOldName, SfxStyleFamily::Page );
+ OSL_ENSURE( pStyleSheet, "PageStyle not found! :-/" );
+
+ if ( pReqArgs && pStyleSheet && (pItem = pReqArgs->GetItemIfSet( SID_PREVIEW_SCALINGFACTOR )) )
+ {
+ const sal_uInt16 nCurrentZoom = pItem->GetValue();
+ SfxItemSet& rSet = pStyleSheet->GetItemSet();
+ rSet.Put( SfxUInt16Item( ATTR_PAGE_SCALE, nCurrentZoom ) );
+ ScPrintFunc aPrintFunc( pDocShell, pDocShell->GetPrinter(), nTab );
+ aPrintFunc.UpdatePages();
+ rReq.Done();
+ }
+ GetViewFrame().GetBindings().Invalidate( nSlot );
+ }
+ break;
+ case SID_PRINTPREVIEW:
+ case SID_PREVIEW_CLOSE:
+ // print preview is now always in the same frame as the tab view
+ // -> always switch this frame back to normal view
+ // (ScTabViewShell ctor reads stored view data)
+
+ ExitPreview();
+ break;
+ case SID_CURSORPAGEUP:
+ case SID_CURSORPAGEDOWN:
+ case SID_CURSORHOME:
+ case SID_CURSOREND:
+ case SID_CURSORUP:
+ case SID_CURSORDOWN:
+ case SID_CURSORLEFT:
+ case SID_CURSORRIGHT:
+ DoScroll( nSlot );
+ break;
+ case SID_CANCEL:
+ if( ScViewUtil::IsFullScreen( *this ) )
+ ScViewUtil::SetFullScreen( *this, false );
+ break;
+
+ default:
+ break;
+ }
+}
+
+void ScPreviewShell::GetState( SfxItemSet& rSet )
+{
+ pPreview->SetInGetState(true);
+
+ SCTAB nTab = pPreview->GetTab();
+ tools::Long nPage = pPreview->GetPageNo();
+ tools::Long nTotal = pPreview->GetTotalPages();
+ sal_uInt16 nZoom = pPreview->GetZoom();
+ bool bAllTested = pPreview->AllTested();
+
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while ( nWhich )
+ {
+ switch (nWhich)
+ {
+ case SID_STATUS_PAGESTYLE:
+ case SID_HFEDIT:
+ pDocShell->GetStatePageStyle( rSet, nTab );
+ break;
+ case SID_UNDO:
+ case SID_REDO:
+ case SID_REPEAT:
+ case SID_SAVEDOC:
+ case SID_SAVEASDOC:
+ case SID_MAIL_SENDDOC:
+ case SID_VIEW_DATA_SOURCE_BROWSER:
+ case SID_QUITAPP:
+ rSet.DisableItem(nWhich);
+ break;
+ case SID_PREVIEW_PREVIOUS:
+ case SID_PREVIEW_FIRST:
+ if (!nTotal || nPage==0)
+ rSet.DisableItem(nWhich);
+ break;
+ case SID_PREVIEW_NEXT:
+ case SID_PREVIEW_LAST:
+ if (bAllTested)
+ if (!nTotal || nPage==nTotal-1)
+ rSet.DisableItem(nWhich);
+ break;
+ case SID_ZOOM_IN:
+ if (nZoom >= 400)
+ rSet.DisableItem(nWhich);
+ break;
+ case SID_ZOOM_OUT:
+ if (nZoom <= 20)
+ rSet.DisableItem(nWhich);
+ break;
+ case SID_ATTR_ZOOM:
+ {
+ SvxZoomItem aZoom( eZoom, nZoom, TypedWhichId<SvxZoomItem>(nWhich) );
+ aZoom.SetValueSet( SvxZoomEnableFlags::ALL & ~SvxZoomEnableFlags::OPTIMAL );
+ rSet.Put( aZoom );
+ }
+ break;
+ case SID_ATTR_ZOOMSLIDER:
+ {
+ SvxZoomSliderItem aZoomSliderItem( nZoom, MINZOOM, MAXZOOM, SID_ATTR_ZOOMSLIDER );
+ aZoomSliderItem.AddSnappingPoint( 100 );
+ rSet.Put( aZoomSliderItem );
+ }
+ break;
+ case SID_PREVIEW_SCALINGFACTOR:
+ {
+ if( pDocShell->IsReadOnly() )
+ rSet.DisableItem( nWhich );
+ else
+ {
+ OUString aOldName = pDocShell->GetDocument().GetPageStyle( pPreview->GetTab() );
+ ScStyleSheetPool* pStylePool = pDocShell->GetDocument().GetStyleSheetPool();
+ SfxStyleSheetBase* pStyleSheet = pStylePool->Find( aOldName, SfxStyleFamily::Page );
+ OSL_ENSURE( pStyleSheet, "PageStyle not found! :-/" );
+
+ if ( pStyleSheet )
+ {
+ SfxItemSet& rStyleSet = pStyleSheet->GetItemSet();
+ sal_uInt16 nCurrentZoom = rStyleSet.Get(ATTR_PAGE_SCALE).GetValue();
+ if( nCurrentZoom )
+ {
+ SvxZoomSliderItem aZoomSliderItem( nCurrentZoom, MINZOOM_SLIDER, MAXZOOM_SLIDER, SID_PREVIEW_SCALINGFACTOR );
+ aZoomSliderItem.AddSnappingPoint( 100 );
+ rSet.Put( aZoomSliderItem );
+ }
+ else
+ rSet.DisableItem( nWhich );
+ }
+ }
+ }
+ break;
+ case SID_STATUS_DOCPOS:
+ rSet.Put( SfxStringItem( nWhich, pPreview->GetPosString() ) );
+ break;
+ case SID_PRINTPREVIEW:
+ rSet.Put( SfxBoolItem( nWhich, true ) );
+ break;
+ case SID_FORMATPAGE:
+ case SID_PREVIEW_MARGIN:
+ if( pDocShell->IsReadOnly() )
+ rSet.DisableItem( nWhich );
+ break;
+ }
+
+ nWhich = aIter.NextWhich();
+ }
+
+ pPreview->SetInGetState(false);
+}
+
+void ScPreviewShell::FillFieldData( ScHeaderFieldData& rData )
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ SCTAB nTab = pPreview->GetTab();
+ OUString aTmp;
+ rDoc.GetName(nTab, aTmp);
+ rData.aTabName = aTmp;
+
+ if( pDocShell->getDocProperties()->getTitle().getLength() != 0 )
+ rData.aTitle = pDocShell->getDocProperties()->getTitle();
+ else
+ rData.aTitle = pDocShell->GetTitle();
+
+ const INetURLObject& rURLObj = pDocShell->GetMedium()->GetURLObject();
+ rData.aLongDocName = rURLObj.GetMainURL( INetURLObject::DecodeMechanism::Unambiguous );
+ if ( !rData.aLongDocName.isEmpty() )
+ rData.aShortDocName = rURLObj.GetLastName(INetURLObject::DecodeMechanism::Unambiguous);
+ else
+ rData.aShortDocName = rData.aLongDocName = rData.aTitle;
+ rData.nPageNo = pPreview->GetPageNo() + 1;
+
+ bool bAllTested = pPreview->AllTested();
+ if (bAllTested)
+ rData.nTotalPages = pPreview->GetTotalPages();
+ else
+ rData.nTotalPages = 99;
+
+ // the dialog knows eNumType
+}
+
+void ScPreviewShell::WriteUserData(OUString& rData, bool /* bBrowse */)
+{
+ // nZoom
+ // nPageNo
+
+ rData = OUString::number(pPreview->GetZoom())
+ + OUStringChar(SC_USERDATA_SEP)
+ + OUString::number(pPreview->GetPageNo());
+}
+
+void ScPreviewShell::ReadUserData(const OUString& rData, bool /* bBrowse */)
+{
+ if (!rData.isEmpty())
+ {
+ sal_Int32 nIndex = 0;
+ pPreview->SetZoom(static_cast<sal_uInt16>(o3tl::toInt32(o3tl::getToken(rData, 0, SC_USERDATA_SEP, nIndex))));
+ pPreview->SetPageNo(o3tl::toInt32(o3tl::getToken(rData, 0, SC_USERDATA_SEP, nIndex)));
+ eZoom = SvxZoomType::PERCENT;
+ }
+}
+
+void ScPreviewShell::WriteUserDataSequence(uno::Sequence < beans::PropertyValue >& rSeq)
+{
+ // tdf#130559: don't export preview view data if active
+ if (comphelper::IsContextFlagActive("NoPreviewData"))
+ return;
+
+ rSeq.realloc(3);
+ beans::PropertyValue* pSeq = rSeq.getArray();
+ sal_uInt16 nViewID(GetViewFrame().GetCurViewId());
+ pSeq[0].Name = SC_VIEWID;
+ pSeq[0].Value <<= SC_VIEW + OUString::number(nViewID);
+ pSeq[1].Name = SC_ZOOMVALUE;
+ pSeq[1].Value <<= sal_Int32 (pPreview->GetZoom());
+ pSeq[2].Name = "PageNumber";
+ pSeq[2].Value <<= pPreview->GetPageNo();
+
+ // Common SdrModel processing
+ if (ScDrawLayer* pDrawLayer = GetDocument().GetDrawLayer())
+ pDrawLayer->WriteUserDataSequence(rSeq);
+}
+
+void ScPreviewShell::ReadUserDataSequence(const uno::Sequence < beans::PropertyValue >& rSeq)
+{
+ for (const auto& propval : rSeq)
+ {
+ if (propval.Name == SC_ZOOMVALUE)
+ {
+ sal_Int32 nTemp = 0;
+ if (propval.Value >>= nTemp)
+ pPreview->SetZoom(sal_uInt16(nTemp));
+ }
+ else if (propval.Name == "PageNumber")
+ {
+ sal_Int32 nTemp = 0;
+ if (propval.Value >>= nTemp)
+ pPreview->SetPageNo(nTemp);
+ }
+ // Fallback to common SdrModel processing
+ else
+ pDocShell->MakeDrawLayer()->ReadUserDataSequenceValue(&propval);
+ }
+}
+
+void ScPreviewShell::DoScroll( sal_uInt16 nMode )
+{
+ Point aCurPos, aPrevPos;
+
+ tools::Long nHRange = pHorScroll->GetRange().Max();
+ tools::Long nHLine = pHorScroll->GetLineSize();
+ tools::Long nHPage = pHorScroll->GetPageSize();
+ tools::Long nVRange = pVerScroll->GetRange().Max();
+ tools::Long nVLine = pVerScroll->GetLineSize();
+ tools::Long nVPage = pVerScroll->GetPageSize();
+
+ aCurPos.setX( pHorScroll->GetThumbPos() );
+ aCurPos.setY( pVerScroll->GetThumbPos() );
+ aPrevPos = aCurPos;
+
+ tools::Long nThumbPos = pVerScroll->GetThumbPos();
+ tools::Long nRangeMax = pVerScroll->GetRangeMax();
+
+ switch( nMode )
+ {
+ case SID_CURSORUP:
+ if( nMaxVertPos<0 )
+ {
+ tools::Long nPage = pPreview->GetPageNo();
+
+ if( nPage>0 )
+ {
+ SfxViewFrame& rSfxViewFrame = GetViewFrame();
+ SfxRequest aSfxRequest(rSfxViewFrame, SID_PREVIEW_PREVIOUS);
+ Execute( aSfxRequest );
+ }
+ }
+ else
+ aCurPos.AdjustY( -nVLine );
+ break;
+ case SID_CURSORDOWN:
+ if( nMaxVertPos<0 )
+ {
+ tools::Long nPage = pPreview->GetPageNo();
+ tools::Long nTotal = pPreview->GetTotalPages();
+
+ // before testing for last page, make sure all page counts are calculated
+ if ( nPage+1 == nTotal && !pPreview->AllTested() )
+ {
+ pPreview->CalcAll();
+ nTotal = pPreview->GetTotalPages();
+ }
+
+ if( nPage<nTotal-1 )
+ {
+ SfxViewFrame& rSfxViewFrame = GetViewFrame();
+ SfxRequest aSfxRequest(rSfxViewFrame, SID_PREVIEW_NEXT);
+ Execute( aSfxRequest );
+ }
+ }
+ else
+ aCurPos.AdjustY(nVLine );
+ break;
+ case SID_CURSORLEFT:
+ aCurPos.AdjustX( -nHLine );
+ break;
+ case SID_CURSORRIGHT:
+ aCurPos.AdjustX(nHLine );
+ break;
+ case SID_CURSORPAGEUP:
+ if( nThumbPos==0 || nMaxVertPos<0 )
+ {
+ tools::Long nPage = pPreview->GetPageNo();
+
+ if( nPage>0 )
+ {
+ SfxViewFrame& rSfxViewFrame = GetViewFrame();
+ SfxRequest aSfxRequest(rSfxViewFrame, SID_PREVIEW_PREVIOUS);
+ Execute( aSfxRequest );
+ aCurPos.setY( nVRange );
+ }
+ }
+ else
+ aCurPos.AdjustY( -nVPage );
+ break;
+ case SID_CURSORPAGEDOWN:
+ if( (std::abs(nVPage+nThumbPos-nRangeMax)<10) || nMaxVertPos<0 )
+ {
+ tools::Long nPage = pPreview->GetPageNo();
+ tools::Long nTotal = pPreview->GetTotalPages();
+
+ // before testing for last page, make sure all page counts are calculated
+ if ( nPage+1 == nTotal && !pPreview->AllTested() )
+ {
+ pPreview->CalcAll();
+ nTotal = pPreview->GetTotalPages();
+ }
+ if( nPage<nTotal-1 )
+ {
+ SfxViewFrame& rSfxViewFrame = GetViewFrame();
+ SfxRequest aSfxRequest(rSfxViewFrame, SID_PREVIEW_NEXT);
+ Execute( aSfxRequest );
+ aCurPos.setY( 0 );
+ }
+ }
+ else
+ aCurPos.AdjustY(nVPage );
+ break;
+ case SID_CURSORHOME:
+ if( nMaxVertPos<0 )
+ {
+ tools::Long nPage = pPreview->GetPageNo();
+ tools::Long nTotal = pPreview->GetTotalPages();
+ if( nTotal && nPage != 0 )
+ {
+ SfxViewFrame& rSfxViewFrame = GetViewFrame();
+ SfxRequest aSfxRequest(rSfxViewFrame, SID_PREVIEW_FIRST);
+ Execute( aSfxRequest );
+ }
+ }
+ else
+ {
+ aCurPos.setY( 0 );
+ aCurPos.setX( 0 );
+ }
+ break;
+ case SID_CURSOREND:
+ if( nMaxVertPos<0 )
+ {
+ if( !pPreview->AllTested() )
+ pPreview->CalcAll();
+ tools::Long nPage = pPreview->GetPageNo();
+ tools::Long nTotal = pPreview->GetTotalPages();
+ if( nTotal && nPage+1 != nTotal )
+ {
+ SfxViewFrame& rSfxViewFrame = GetViewFrame();
+ SfxRequest aSfxRequest(rSfxViewFrame, SID_PREVIEW_LAST);
+ Execute( aSfxRequest );
+ }
+ }
+ else
+ {
+ aCurPos.setY( nVRange );
+ aCurPos.setX( nHRange );
+ }
+ break;
+ }
+
+ // nHRange-nHPage might be negative, that's why we check for < 0 afterwards
+
+ if( aCurPos.Y() > (nVRange-nVPage) )
+ aCurPos.setY( nVRange-nVPage );
+ if( aCurPos.Y() < 0 )
+ aCurPos.setY( 0 );
+ if( aCurPos.X() > (nHRange-nHPage) )
+ aCurPos.setX( nHRange-nHPage );
+ if( aCurPos.X() < 0 )
+ aCurPos.setX( 0 );
+
+ if( nMaxVertPos>=0 )
+ {
+ if( aCurPos.Y() != aPrevPos.Y() )
+ {
+ pVerScroll->SetThumbPos( aCurPos.Y() );
+ nPrevVThumbPos = pVerScroll->GetThumbPos();
+ pPreview->SetYOffset( aCurPos.Y() );
+ }
+ }
+
+ if( aCurPos.X() != aPrevPos.X() )
+ {
+ pHorScroll->SetThumbPos( aCurPos.X() );
+ nPrevHThumbPos = pHorScroll->GetThumbPos();
+ pPreview->SetXOffset( aCurPos.X() );
+ }
+
+}
+
+void ScPreviewShell::ExitPreview()
+{
+ GetViewFrame().GetDispatcher()->Execute(SID_VIEWSHELL0, SfxCallMode::ASYNCHRON);
+}
+
+void ScPreviewShell::AddAccessibilityObject( SfxListener& rObject )
+{
+ if (!pAccessibilityBroadcaster)
+ pAccessibilityBroadcaster.reset( new SfxBroadcaster );
+
+ rObject.StartListening( *pAccessibilityBroadcaster );
+}
+
+void ScPreviewShell::RemoveAccessibilityObject( SfxListener& rObject )
+{
+ if (pAccessibilityBroadcaster)
+ rObject.EndListening( *pAccessibilityBroadcaster );
+ else
+ {
+ OSL_FAIL("no accessibility broadcaster?");
+ }
+}
+
+void ScPreviewShell::BroadcastAccessibility( const SfxHint &rHint )
+{
+ if (pAccessibilityBroadcaster)
+ pAccessibilityBroadcaster->Broadcast( rHint );
+}
+
+bool ScPreviewShell::HasAccessibilityObjects() const
+{
+ return pAccessibilityBroadcaster && pAccessibilityBroadcaster->HasListeners();
+}
+
+const ScPreviewLocationData& ScPreviewShell::GetLocationData()
+{
+ return pPreview->GetLocationData();
+}
+
+ScDocument& ScPreviewShell::GetDocument()
+{
+ return pDocShell->GetDocument();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/prevwsh2.cxx b/sc/source/ui/view/prevwsh2.cxx
new file mode 100644
index 0000000000..bb15ac24ad
--- /dev/null
+++ b/sc/source/ui/view/prevwsh2.cxx
@@ -0,0 +1,68 @@
+/* -*- 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 .
+ */
+
+#include <svx/svdmodel.hxx>
+#include <svl/hint.hxx>
+
+#include <prevwsh.hxx>
+#include <docsh.hxx>
+#include <preview.hxx>
+#include <hints.hxx>
+
+void ScPreviewShell::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ bool bDataChanged = false;
+
+ if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint)
+ {
+ const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint);
+ // SdrHints are no longer used for invalidating, thus react on objectchange instead
+ if(SdrHintKind::ObjectChange == pSdrHint->GetKind())
+ bDataChanged = true;
+ }
+ else if (const ScPaintHint* pPaintHint = dynamic_cast<const ScPaintHint*>(&rHint))
+ {
+ PaintPartFlags nParts = pPaintHint->GetParts();
+ if (nParts & ( PaintPartFlags::Grid | PaintPartFlags::Left | PaintPartFlags::Top | PaintPartFlags::Size ))
+ bDataChanged = true;
+ }
+ else
+ {
+ switch ( rHint.GetId() )
+ {
+ case SfxHintId::ScDataChanged:
+ case SfxHintId::ScPrintOptions:
+ bDataChanged = true;
+ break;
+ case SfxHintId::ScDrawLayerNew:
+ {
+ SfxBroadcaster* pDrawBC = pDocShell->GetDocument().GetDrawBroadcaster();
+ if (pDrawBC)
+ StartListening(*pDrawBC);
+ }
+ break;
+ default: break;
+ }
+ }
+
+ if (bDataChanged)
+ pPreview->DataChanged(true);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/printfun.cxx b/sc/source/ui/view/printfun.cxx
new file mode 100644
index 0000000000..84e21da022
--- /dev/null
+++ b/sc/source/ui/view/printfun.cxx
@@ -0,0 +1,3204 @@
+/* -*- 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 .
+ */
+
+#include <scitems.hxx>
+#include <editeng/eeitem.hxx>
+
+#include <printfun.hxx>
+
+#include <editeng/adjustitem.hxx>
+#include <editeng/borderline.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <svtools/colorcfg.hxx>
+#include <editeng/editstat.hxx>
+#include <svx/fmview.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/paperinf.hxx>
+#include <editeng/pbinitem.hxx>
+#include <editeng/shaditem.hxx>
+#include <editeng/sizeitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <sfx2/printer.hxx>
+#include <tools/multisel.hxx>
+#include <sfx2/docfile.hxx>
+#include <tools/urlobj.hxx>
+#include <osl/diagnose.h>
+
+#include <editutil.hxx>
+#include <docsh.hxx>
+#include <output.hxx>
+#include <viewdata.hxx>
+#include <viewopti.hxx>
+#include <stlpool.hxx>
+#include <pagepar.hxx>
+#include <attrib.hxx>
+#include <patattr.hxx>
+#include <docpool.hxx>
+#include <dociter.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <pagedata.hxx>
+#include <printopt.hxx>
+#include <prevloc.hxx>
+#include <scmod.hxx>
+#include <drwlayer.hxx>
+#include <fillinfo.hxx>
+#include <postit.hxx>
+
+#include <memory>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+
+#define ZOOM_MIN 10
+
+namespace{
+
+bool lcl_GetBool(const SfxItemSet* pSet, sal_uInt16 nWhich)
+{
+ return static_cast<const SfxBoolItem&>(pSet->Get(nWhich)).GetValue();
+}
+
+sal_uInt16 lcl_GetUShort(const SfxItemSet* pSet, sal_uInt16 nWhich)
+{
+ return static_cast<const SfxUInt16Item&>(pSet->Get(nWhich)).GetValue();
+}
+
+bool lcl_GetShow(const SfxItemSet* pSet, sal_uInt16 nWhich)
+{
+ return ScVObjMode::VOBJ_MODE_SHOW == static_cast<const ScViewObjectModeItem&>(pSet->Get(nWhich)).GetValue();
+}
+
+
+} // namespace
+
+ScPageRowEntry::ScPageRowEntry(const ScPageRowEntry& r)
+{
+ nStartRow = r.nStartRow;
+ nEndRow = r.nEndRow;
+ nPagesX = r.nPagesX;
+ aHidden = r.aHidden;
+ aHidden.resize(nPagesX, false);
+}
+
+ScPageRowEntry& ScPageRowEntry::operator=(const ScPageRowEntry& r)
+{
+ nStartRow = r.nStartRow;
+ nEndRow = r.nEndRow;
+ nPagesX = r.nPagesX;
+ aHidden = r.aHidden;
+ aHidden.resize(nPagesX, false);
+ return *this;
+}
+
+void ScPageRowEntry::SetPagesX(size_t nNew)
+{
+ nPagesX = nNew;
+ aHidden.resize(nPagesX, false);
+}
+
+void ScPageRowEntry::SetHidden(size_t nX)
+{
+ if ( nX < nPagesX )
+ {
+ if ( nX+1 == nPagesX ) // last page?
+ --nPagesX;
+ else
+ {
+ aHidden.resize(nPagesX, false);
+ aHidden[nX] = true;
+ }
+ }
+}
+
+bool ScPageRowEntry::IsHidden(size_t nX) const
+{
+ return nX >= nPagesX || aHidden[nX]; //! inline?
+}
+
+size_t ScPageRowEntry::CountVisible() const
+{
+ if (!aHidden.empty())
+ {
+ size_t nVis = 0;
+ for (size_t i=0; i<nPagesX; i++)
+ if (!aHidden[i])
+ ++nVis;
+ return nVis;
+ }
+ else
+ return nPagesX;
+}
+
+static tools::Long lcl_LineTotal(const ::editeng::SvxBorderLine* pLine)
+{
+ return pLine ? ( pLine->GetScaledWidth() ) : 0;
+}
+
+void ScPrintFunc::Construct( const ScPrintOptions* pOptions )
+{
+ pDocShell->UpdatePendingRowHeights( nPrintTab );
+
+ SfxPrinter* pDocPrinter = rDoc.GetPrinter(); // use the printer, even for preview
+ if (pDocPrinter)
+ aOldPrinterMode = pDocPrinter->GetMapMode();
+
+ // unified MapMode for all calls (e.g. Repaint!!!)
+ // else, EditEngine outputs different text heights
+ pDev->SetMapMode(MapMode(MapUnit::MapPixel));
+
+ pBorderItem = nullptr;
+ pBackgroundItem = nullptr;
+ pShadowItem = nullptr;
+
+ pEditEngine = nullptr;
+ pEditDefaults = nullptr;
+
+ ScStyleSheetPool* pStylePool = rDoc.GetStyleSheetPool();
+ SfxStyleSheetBase* pStyleSheet = pStylePool->Find(
+ rDoc.GetPageStyle( nPrintTab ),
+ SfxStyleFamily::Page );
+ if (pStyleSheet)
+ pParamSet = &pStyleSheet->GetItemSet();
+ else
+ {
+ OSL_FAIL("Template not found" );
+ pParamSet = nullptr;
+ }
+
+ if (!bFromPrintState)
+ nZoom = 100;
+ nManualZoom = 100;
+ bClearWin = false;
+ bUseStyleColor = false;
+ bIsRender = false;
+
+ InitParam(pOptions);
+
+ pPageData = nullptr; // is only needed for initialisation
+}
+
+ScPrintFunc::ScPrintFunc( ScDocShell* pShell, SfxPrinter* pNewPrinter, SCTAB nTab,
+ tools::Long nPage, tools::Long nDocP, const ScRange* pArea,
+ const ScPrintOptions* pOptions,
+ ScPageBreakData* pData )
+ : pDocShell ( pShell ),
+ rDoc(pDocShell->GetDocument()),
+ pPrinter ( pNewPrinter ),
+ pDrawView ( nullptr ),
+ nPrintTab ( nTab ),
+ nPageStart ( nPage ),
+ nDocPages ( nDocP ),
+ pUserArea ( pArea ),
+ bFromPrintState ( false ),
+ bSourceRangeValid ( false ),
+ bPrintCurrentTable ( false ),
+ bMultiArea ( false ),
+ mbHasPrintRange(true),
+ nTabPages ( 0 ),
+ nTotalPages ( 0 ),
+ bPrintAreaValid ( false ),
+ pPageData ( pData )
+{
+ pDev = pPrinter.get();
+ aSrcOffset = pPrinter->PixelToLogic(pPrinter->GetPageOffsetPixel(), MapMode(MapUnit::Map100thMM));
+ m_aRanges.m_xPageEndX = std::make_shared<std::vector<SCCOL>>();
+ m_aRanges.m_xPageEndY = std::make_shared<std::vector<SCROW>>();
+ m_aRanges.m_xPageRows = std::make_shared<std::map<size_t, ScPageRowEntry>>();
+ Construct( pOptions );
+}
+
+ScPrintFunc::ScPrintFunc(ScDocShell* pShell, SfxPrinter* pNewPrinter,
+ const ScPrintState& rState, const ScPrintOptions* pOptions)
+ : pDocShell ( pShell ),
+ rDoc(pDocShell->GetDocument()),
+ pPrinter ( pNewPrinter ),
+ pDrawView ( nullptr ),
+ pUserArea ( nullptr ),
+ bSourceRangeValid ( false ),
+ bPrintCurrentTable ( false ),
+ bMultiArea ( false ),
+ mbHasPrintRange(true),
+ pPageData ( nullptr )
+{
+ pDev = pPrinter.get();
+
+ nPrintTab = rState.nPrintTab;
+ nStartCol = rState.nStartCol;
+ nStartRow = rState.nStartRow;
+ nEndCol = rState.nEndCol;
+ nEndRow = rState.nEndRow;
+ bPrintAreaValid = rState.bPrintAreaValid;
+ nZoom = rState.nZoom;
+ m_aRanges.m_nPagesX = rState.nPagesX;
+ m_aRanges.m_nPagesY = rState.nPagesY;
+ nTabPages = rState.nTabPages;
+ nTotalPages = rState.nTotalPages;
+ nPageStart = rState.nPageStart;
+ nDocPages = rState.nDocPages;
+ bFromPrintState = true;
+
+ if (rState.bSavedStateRanges)
+ {
+ m_aRanges.m_nTotalY = rState.nTotalY;
+ m_aRanges.m_xPageEndX = rState.xPageEndX;
+ m_aRanges.m_xPageEndY = rState.xPageEndY;
+ m_aRanges.m_xPageRows = rState.xPageRows;
+ m_aRanges.m_aInput = rState.aPrintPageRangesInput;
+ }
+ else
+ {
+ m_aRanges.m_xPageEndX = std::make_shared<std::vector<SCCOL>>();
+ m_aRanges.m_xPageEndY = std::make_shared<std::vector<SCROW>>();
+ m_aRanges.m_xPageRows = std::make_shared<std::map<size_t, ScPageRowEntry>>();
+ }
+
+ aSrcOffset = pPrinter->PixelToLogic(pPrinter->GetPageOffsetPixel(), MapMode(MapUnit::Map100thMM));
+ Construct( pOptions );
+}
+
+ScPrintFunc::ScPrintFunc( OutputDevice* pOutDev, ScDocShell* pShell, SCTAB nTab,
+ tools::Long nPage, tools::Long nDocP, const ScRange* pArea,
+ const ScPrintOptions* pOptions )
+ : pDocShell ( pShell ),
+ rDoc(pDocShell->GetDocument()),
+ pPrinter ( nullptr ),
+ pDrawView ( nullptr ),
+ nPrintTab ( nTab ),
+ nPageStart ( nPage ),
+ nDocPages ( nDocP ),
+ pUserArea ( pArea ),
+ bFromPrintState ( false ),
+ bSourceRangeValid ( false ),
+ bPrintCurrentTable ( false ),
+ bMultiArea ( false ),
+ mbHasPrintRange(true),
+ nTabPages ( 0 ),
+ nTotalPages ( 0 ),
+ bPrintAreaValid ( false ),
+ pPageData ( nullptr )
+{
+ pDev = pOutDev;
+ m_aRanges.m_xPageEndX = std::make_shared<std::vector<SCCOL>>();
+ m_aRanges.m_xPageEndY = std::make_shared<std::vector<SCROW>>();
+ m_aRanges.m_xPageRows = std::make_shared<std::map<size_t, ScPageRowEntry>>();
+ Construct( pOptions );
+}
+
+ScPrintFunc::ScPrintFunc( OutputDevice* pOutDev, ScDocShell* pShell,
+ const ScPrintState& rState, const ScPrintOptions* pOptions )
+ : pDocShell ( pShell ),
+ rDoc(pDocShell->GetDocument()),
+ pPrinter ( nullptr ),
+ pDrawView ( nullptr ),
+ pUserArea ( nullptr ),
+ bSourceRangeValid ( false ),
+ bPrintCurrentTable ( false ),
+ bMultiArea ( false ),
+ mbHasPrintRange(true),
+ pPageData ( nullptr )
+{
+ pDev = pOutDev;
+
+ nPrintTab = rState.nPrintTab;
+ nStartCol = rState.nStartCol;
+ nStartRow = rState.nStartRow;
+ nEndCol = rState.nEndCol;
+ nEndRow = rState.nEndRow;
+ bPrintAreaValid = rState.bPrintAreaValid;
+ nZoom = rState.nZoom;
+ m_aRanges.m_nPagesX = rState.nPagesX;
+ m_aRanges.m_nPagesY = rState.nPagesY;
+ nTabPages = rState.nTabPages;
+ nTotalPages = rState.nTotalPages;
+ nPageStart = rState.nPageStart;
+ nDocPages = rState.nDocPages;
+ bFromPrintState = true;
+
+ if (rState.bSavedStateRanges)
+ {
+ m_aRanges.m_nTotalY = rState.nTotalY;
+ m_aRanges.m_xPageEndX = rState.xPageEndX;
+ m_aRanges.m_xPageEndY = rState.xPageEndY;
+ m_aRanges.m_xPageRows = rState.xPageRows;
+ m_aRanges.m_aInput = rState.aPrintPageRangesInput;
+ }
+ else
+ {
+ m_aRanges.m_xPageEndX = std::make_shared<std::vector<SCCOL>>();
+ m_aRanges.m_xPageEndY = std::make_shared<std::vector<SCROW>>();
+ m_aRanges.m_xPageRows = std::make_shared<std::map<size_t, ScPageRowEntry>>();
+ }
+
+ Construct( pOptions );
+}
+
+void ScPrintFunc::GetPrintState(ScPrintState& rState, bool bSavePageRanges)
+{
+ rState.nPrintTab = nPrintTab;
+ rState.nStartCol = nStartCol;
+ rState.nStartRow = nStartRow;
+ rState.nEndCol = nEndCol;
+ rState.nEndRow = nEndRow;
+ rState.bPrintAreaValid = bPrintAreaValid;
+ rState.nZoom = nZoom;
+ rState.nPagesX = m_aRanges.m_nPagesX;
+ rState.nPagesY = m_aRanges.m_nPagesY;
+ rState.nTabPages = nTabPages;
+ rState.nTotalPages = nTotalPages;
+ rState.nPageStart = nPageStart;
+ rState.nDocPages = nDocPages;
+ if (bSavePageRanges)
+ {
+ rState.bSavedStateRanges = true;
+ rState.nTotalY = m_aRanges.m_nTotalY;
+ rState.xPageEndX = m_aRanges.m_xPageEndX;
+ rState.xPageEndY = m_aRanges.m_xPageEndY;
+ rState.xPageRows = m_aRanges.m_xPageRows;
+ rState.aPrintPageRangesInput = m_aRanges.m_aInput;
+ }
+}
+
+bool ScPrintFunc::GetLastSourceRange( ScRange& rRange ) const
+{
+ rRange = aLastSourceRange;
+ return bSourceRangeValid;
+}
+
+void ScPrintFunc::FillPageData()
+{
+ if (!pPageData)
+ return;
+
+ sal_uInt16 nCount = sal::static_int_cast<sal_uInt16>( pPageData->GetCount() );
+ ScPrintRangeData& rData = pPageData->GetData(nCount); // count up
+
+ assert( bPrintAreaValid );
+ rData.SetPrintRange( ScRange( nStartCol, nStartRow, nPrintTab,
+ nEndCol, nEndRow, nPrintTab ) );
+ // #i123672#
+ if(m_aRanges.m_xPageEndX->empty())
+ {
+ OSL_ENSURE(false, "vector access error for maPageEndX (!)");
+ }
+ else
+ {
+ rData.SetPagesX( m_aRanges.m_nPagesX, m_aRanges.m_xPageEndX->data());
+ }
+
+ // #i123672#
+ if(m_aRanges.m_xPageEndY->empty())
+ {
+ OSL_ENSURE(false, "vector access error for maPageEndY (!)");
+ }
+ else
+ {
+ rData.SetPagesY( m_aRanges.m_nTotalY, m_aRanges.m_xPageEndY->data());
+ }
+
+ // Settings
+ rData.SetTopDown( aTableParam.bTopDown );
+ rData.SetAutomatic( !aAreaParam.bPrintArea );
+}
+
+ScPrintFunc::~ScPrintFunc()
+{
+ pEditDefaults.reset();
+ pEditEngine.reset();
+
+ // Printer settings are now restored from outside
+
+ // For DrawingLayer/Charts, the MapMode of the printer (RefDevice) must always be correct
+ SfxPrinter* pDocPrinter = rDoc.GetPrinter(); // use Preview also for the printer
+ if (pDocPrinter)
+ pDocPrinter->SetMapMode(aOldPrinterMode);
+}
+
+void ScPrintFunc::SetDrawView( FmFormView* pNew )
+{
+ pDrawView = pNew;
+}
+
+static void lcl_HidePrint( const ScTableInfo& rTabInfo, SCCOL nX1, SCCOL nX2 )
+{
+ for (SCSIZE nArrY=1; nArrY+1<rTabInfo.mnArrCount; nArrY++)
+ {
+ RowInfo* pThisRowInfo = &rTabInfo.mpRowInfo[nArrY];
+ for (SCCOL nX=nX1; nX<=nX2; nX++)
+ {
+ ScCellInfo& rCellInfo = pThisRowInfo->cellInfo(nX);
+ ScBasicCellInfo& rBasicCellInfo = pThisRowInfo->basicCellInfo(nX);
+ if (!rBasicCellInfo.bEmptyCellText)
+ if (rCellInfo.pPatternAttr->
+ GetItem(ATTR_PROTECTION, rCellInfo.pConditionSet).GetHidePrint())
+ {
+ rCellInfo.maCell.clear();
+ rBasicCellInfo.bEmptyCellText = true;
+ }
+ }
+ }
+}
+
+// output to Device (static)
+//
+// us used for:
+// - Clipboard/Bitmap
+// - Ole-Object (DocShell::Draw)
+// - Preview of templates
+
+void ScPrintFunc::DrawToDev(ScDocument& rDoc, OutputDevice* pDev, double /* nPrintFactor */,
+ const tools::Rectangle& rBound, ScViewData* pViewData, bool bMetaFile)
+{
+ if (rDoc.GetMaxTableNumber() < 0)
+ return;
+
+ //! evaluate nPrintFactor !!!
+
+ SCTAB nTab = 0;
+ if (pViewData)
+ nTab = pViewData->GetTabNo();
+
+ bool bDoGrid, bNullVal, bFormula;
+ ScStyleSheetPool* pStylePool = rDoc.GetStyleSheetPool();
+ SfxStyleSheetBase* pStyleSheet = pStylePool->Find( rDoc.GetPageStyle( nTab ), SfxStyleFamily::Page );
+ if (pStyleSheet)
+ {
+ SfxItemSet& rSet = pStyleSheet->GetItemSet();
+ bDoGrid = rSet.Get(ATTR_PAGE_GRID).GetValue();
+ bNullVal = rSet.Get(ATTR_PAGE_NULLVALS).GetValue();
+ bFormula = rSet.Get(ATTR_PAGE_FORMULAS).GetValue();
+ }
+ else
+ {
+ const ScViewOptions& rOpt = rDoc.GetViewOptions();
+ bDoGrid = rOpt.GetOption(VOPT_GRID);
+ bNullVal = rOpt.GetOption(VOPT_NULLVALS);
+ bFormula = rOpt.GetOption(VOPT_FORMULAS);
+ }
+
+ MapMode aMode = pDev->GetMapMode();
+
+ tools::Rectangle aRect = rBound;
+
+ if (aRect.Right() < aRect.Left() || aRect.Bottom() < aRect.Top())
+ aRect = tools::Rectangle( Point(), pDev->GetOutputSize() );
+
+ SCCOL nX1 = 0;
+ SCROW nY1 = 0;
+ SCCOL nX2 = OLE_STD_CELLS_X - 1;
+ SCROW nY2 = OLE_STD_CELLS_Y - 1;
+ if (bMetaFile)
+ {
+ ScRange aRange = rDoc.GetRange( nTab, rBound );
+ nX1 = aRange.aStart.Col();
+ nY1 = aRange.aStart.Row();
+ nX2 = aRange.aEnd.Col();
+ nY2 = aRange.aEnd.Row();
+ }
+ else if (pViewData)
+ {
+ ScSplitPos eWhich = pViewData->GetActivePart();
+ ScHSplitPos eHWhich = WhichH(eWhich);
+ ScVSplitPos eVWhich = WhichV(eWhich);
+ nX1 = pViewData->GetPosX(eHWhich);
+ nY1 = pViewData->GetPosY(eVWhich);
+ nX2 = nX1 + pViewData->VisibleCellsX(eHWhich);
+ if (nX2>nX1) --nX2;
+ nY2 = nY1 + pViewData->VisibleCellsY(eVWhich);
+ if (nY2>nY1) --nY2;
+ }
+
+ if (nX1 > rDoc.MaxCol()) nX1 = rDoc.MaxCol();
+ if (nX2 > rDoc.MaxCol()) nX2 = rDoc.MaxCol();
+ if (nY1 > rDoc.MaxRow()) nY1 = rDoc.MaxRow();
+ if (nY2 > rDoc.MaxRow()) nY2 = rDoc.MaxRow();
+
+ tools::Long nDevSizeX = aRect.Right()-aRect.Left()+1;
+ tools::Long nDevSizeY = aRect.Bottom()-aRect.Top()+1;
+
+ tools::Long nTwipsSizeX = 0;
+ for (SCCOL i=nX1; i<=nX2; i++)
+ nTwipsSizeX += rDoc.GetColWidth( i, nTab );
+ tools::Long nTwipsSizeY = rDoc.GetRowHeight( nY1, nY2, nTab );
+
+ // if no lines, still space for the outline frame (20 Twips = 1pt)
+ // (HasLines initializes aLines to 0,0,0,0)
+ nTwipsSizeX += 20;
+ nTwipsSizeY += 20;
+
+ double nScaleX = static_cast<double>(nDevSizeX) / nTwipsSizeX;
+ double nScaleY = static_cast<double>(nDevSizeY) / nTwipsSizeY;
+
+ //! hand over Flag at FillInfo !!!!!
+ ScRange aERange;
+ bool bEmbed = rDoc.IsEmbedded();
+ if (bEmbed)
+ {
+ rDoc.GetEmbedded(aERange);
+ rDoc.ResetEmbedded();
+ }
+
+ // Assemble data
+
+ ScTableInfo aTabInfo;
+ rDoc.FillInfo( aTabInfo, nX1, nY1, nX2, nY2, nTab,
+ nScaleX, nScaleY, false, bFormula );
+ lcl_HidePrint( aTabInfo, nX1, nX2 );
+
+ if (bEmbed)
+ rDoc.SetEmbedded(aERange);
+
+ tools::Long nScrX = aRect.Left();
+ tools::Long nScrY = aRect.Top();
+
+ // If no lines, still leave space for grid lines
+ // (would be elseways cut away)
+ nScrX += 1;
+ nScrY += 1;
+
+ ScOutputData aOutputData( pDev, OUTTYPE_PRINTER, aTabInfo, &rDoc, nTab,
+ nScrX, nScrY, nX1, nY1, nX2, nY2, nScaleX, nScaleY );
+ aOutputData.SetMetaFileMode(bMetaFile);
+ aOutputData.SetShowNullValues(bNullVal);
+ aOutputData.SetShowFormulas(bFormula);
+
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ std::unique_ptr<FmFormView> pDrawView;
+
+ if( pModel )
+ {
+ pDrawView.reset(
+ new FmFormView(
+ *pModel,
+ pDev));
+ pDrawView->ShowSdrPage(pDrawView->GetModel().GetPage(nTab));
+ pDrawView->SetPrintPreview();
+ aOutputData.SetDrawView( pDrawView.get() );
+ }
+
+ //! SetUseStyleColor ??
+
+ if ( bMetaFile && pDev->IsVirtual() )
+ aOutputData.SetSnapPixel();
+
+ Point aLogStart = pDev->PixelToLogic(Point(nScrX, nScrY), MapMode(MapUnit::Map100thMM));
+ tools::Long nLogStX = aLogStart.X();
+ tools::Long nLogStY = aLogStart.Y();
+
+ //! nZoom for GetFont in OutputData ???
+
+ if (!bMetaFile && pViewData)
+ pDev->SetMapMode(pViewData->GetLogicMode(pViewData->GetActivePart()));
+
+ // #i72502#
+ const Point aMMOffset(aOutputData.PrePrintDrawingLayer(nLogStX, nLogStY));
+ aOutputData.PrintDrawingLayer(SC_LAYER_BACK, aMMOffset);
+
+ if (!bMetaFile && pViewData)
+ pDev->SetMapMode(aMode);
+
+ aOutputData.DrawBackground(*pDev);
+
+ aOutputData.DrawShadow();
+ aOutputData.DrawFrame(*pDev);
+ aOutputData.DrawSparklines(*pDev);
+ aOutputData.DrawStrings();
+
+ if (!bMetaFile && pViewData)
+ pDev->SetMapMode(pViewData->GetLogicMode(pViewData->GetActivePart()));
+
+ aOutputData.DrawEdit(!bMetaFile);
+
+ if (bDoGrid)
+ {
+ if (!bMetaFile && pViewData)
+ pDev->SetMapMode(aMode);
+
+ aOutputData.DrawGrid(*pDev, true, false); // no page breaks
+
+ pDev->SetLineColor( COL_BLACK );
+
+ Size aOne = pDev->PixelToLogic( Size(1,1) );
+ if (bMetaFile)
+ aOne = Size(1,1); // compatible with DrawGrid
+ tools::Long nRight = nScrX + aOutputData.GetScrW() - aOne.Width();
+ tools::Long nBottom = nScrY + aOutputData.GetScrH() - aOne.Height();
+
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+
+ // extra line at the left edge for left-to-right, right for right-to-left
+ if ( bLayoutRTL )
+ pDev->DrawLine( Point(nRight,nScrY), Point(nRight,nBottom) );
+ else
+ pDev->DrawLine( Point(nScrX,nScrY), Point(nScrX,nBottom) );
+ // extra line at the top in both cases
+ pDev->DrawLine( Point(nScrX,nScrY), Point(nRight,nScrY) );
+ }
+
+ // #i72502#
+ aOutputData.PrintDrawingLayer(SC_LAYER_FRONT, aMMOffset);
+ aOutputData.PrintDrawingLayer(SC_LAYER_INTERN, aMMOffset);
+ aOutputData.PostPrintDrawingLayer(aMMOffset); // #i74768#
+}
+
+// Printing
+
+static void lcl_FillHFParam( ScPrintHFParam& rParam, const SfxItemSet* pHFSet )
+{
+ // nDistance must be initialized differently before
+
+ if ( pHFSet == nullptr )
+ {
+ rParam.bEnable = false;
+ rParam.pBorder = nullptr;
+ rParam.pBack = nullptr;
+ rParam.pShadow = nullptr;
+ }
+ else
+ {
+ rParam.bEnable = pHFSet->Get(ATTR_PAGE_ON).GetValue();
+ rParam.bDynamic = pHFSet->Get(ATTR_PAGE_DYNAMIC).GetValue();
+ rParam.bShared = pHFSet->Get(ATTR_PAGE_SHARED).GetValue();
+ rParam.bSharedFirst = pHFSet->Get(ATTR_PAGE_SHARED_FIRST).GetValue();
+ rParam.nHeight = pHFSet->Get(ATTR_PAGE_SIZE).GetSize().Height();
+ const SvxLRSpaceItem* pHFLR = &pHFSet->Get(ATTR_LRSPACE);
+ tools::Long nTmp;
+ nTmp = pHFLR->GetLeft();
+ rParam.nLeft = nTmp < 0 ? 0 : sal_uInt16(nTmp);
+ nTmp = pHFLR->GetRight();
+ rParam.nRight = nTmp < 0 ? 0 : sal_uInt16(nTmp);
+ rParam.pBorder = &pHFSet->Get(ATTR_BORDER);
+ rParam.pBack = &pHFSet->Get(ATTR_BACKGROUND);
+ rParam.pShadow = &pHFSet->Get(ATTR_SHADOW);
+
+// now back in the dialog:
+// rParam.nHeight += rParam.nDistance; // not in the dialog any more ???
+
+ rParam.nHeight += lcl_LineTotal( rParam.pBorder->GetTop() ) +
+ lcl_LineTotal( rParam.pBorder->GetBottom() );
+
+ rParam.nManHeight = rParam.nHeight;
+ }
+
+ if (!rParam.bEnable)
+ rParam.nHeight = 0;
+}
+
+// bNew = TRUE: search for used part of the document
+// bNew = FALSE: only limit whole lines/columns
+
+bool ScPrintFunc::AdjustPrintArea( bool bNew )
+{
+ SCCOL nOldEndCol = nEndCol; // only important for !bNew
+ SCROW nOldEndRow = nEndRow;
+ bool bChangeCol = true; // at bNew both are being adjusted
+ bool bChangeRow = true;
+
+ bool bNotes = aTableParam.bNotes;
+ if ( bNew )
+ {
+ nStartCol = 0;
+ nStartRow = 0;
+ if (!rDoc.GetPrintArea( nPrintTab, nEndCol, nEndRow, bNotes ))
+ return false; // nothing
+ bPrintAreaValid = true;
+ }
+ else
+ {
+ bool bFound = true;
+ bChangeCol = ( nStartCol == 0 && nEndCol == rDoc.MaxCol() );
+ bChangeRow = ( nStartRow == 0 && nEndRow == rDoc.MaxRow() );
+ bool bForcedChangeRow = false;
+
+ // #i53558# Crop entire column of old row limit to real print area with
+ // some fuzzyness.
+ if (!bChangeRow && nStartRow == 0)
+ {
+ SCROW nPAEndRow;
+ bFound = rDoc.GetPrintAreaVer( nPrintTab, nStartCol, nEndCol, nPAEndRow, bNotes );
+ // Say we don't want to print more than ~1000 empty rows, which are
+ // about 14 pages intentionally left blank...
+ const SCROW nFuzzy = 23*42;
+ if (nPAEndRow + nFuzzy < nEndRow)
+ {
+ bForcedChangeRow = true;
+ nEndRow = nPAEndRow;
+ }
+ else
+ bFound = true; // user seems to _want_ to print some empty rows
+ }
+ // TODO: in case we extend the number of columns we may have to do the
+ // same for horizontal cropping.
+
+ if ( bChangeCol && bChangeRow )
+ bFound = rDoc.GetPrintArea( nPrintTab, nEndCol, nEndRow, bNotes );
+ else if ( bChangeCol )
+ bFound = rDoc.GetPrintAreaHor( nPrintTab, nStartRow, nEndRow, nEndCol );
+ else if ( bChangeRow )
+ bFound = rDoc.GetPrintAreaVer( nPrintTab, nStartCol, nEndCol, nEndRow, bNotes );
+
+ if (!bFound)
+ return false; // empty
+
+ bPrintAreaValid = true;
+ if (bForcedChangeRow)
+ bChangeRow = true;
+ }
+
+ assert( bPrintAreaValid );
+ rDoc.ExtendMerge( nStartCol,nStartRow, nEndCol,nEndRow, nPrintTab ); // no Refresh, incl. Attrs
+
+ if ( bChangeCol )
+ {
+ OutputDevice* pRefDev = rDoc.GetPrinter(); // use the printer also for Preview
+ pRefDev->SetMapMode(MapMode(MapUnit::MapPixel)); // important for GetNeededSize
+
+ rDoc.ExtendPrintArea( pRefDev,
+ nPrintTab, nStartCol, nStartRow, nEndCol, nEndRow );
+ // changing nEndCol
+ }
+
+ if ( nEndCol < rDoc.MaxCol() && rDoc.HasAttrib(
+ nEndCol,nStartRow,nPrintTab, nEndCol,nEndRow,nPrintTab, HasAttrFlags::ShadowRight ) )
+ ++nEndCol;
+ if ( nEndRow < rDoc.MaxRow() && rDoc.HasAttrib(
+ nStartCol,nEndRow,nPrintTab, nEndCol,nEndRow,nPrintTab, HasAttrFlags::ShadowDown ) )
+ ++nEndRow;
+
+ if (!bChangeCol) nEndCol = nOldEndCol;
+ if (!bChangeRow) nEndRow = nOldEndRow;
+
+ return true;
+}
+
+tools::Long ScPrintFunc::TextHeight( const EditTextObject* pObject )
+{
+ if (!pObject)
+ return 0;
+
+ pEditEngine->SetTextNewDefaults( *pObject, *pEditDefaults, false );
+
+ return static_cast<tools::Long>(pEditEngine->GetTextHeight());
+}
+
+// nZoom must be set !!!
+// and the respective Twip-MapMode configured
+
+void ScPrintFunc::UpdateHFHeight( ScPrintHFParam& rParam )
+{
+ OSL_ENSURE( aPageSize.Width(), "UpdateHFHeight without aPageSize");
+
+ if (!(rParam.bEnable && rParam.bDynamic))
+ return;
+
+ // calculate nHeight from content
+
+ MakeEditEngine();
+ tools::Long nPaperWidth = ( aPageSize.Width() - nLeftMargin - nRightMargin -
+ rParam.nLeft - rParam.nRight ) * 100 / nZoom;
+ if (rParam.pBorder)
+ nPaperWidth -= ( rParam.pBorder->GetDistance(SvxBoxItemLine::LEFT) +
+ rParam.pBorder->GetDistance(SvxBoxItemLine::RIGHT) +
+ lcl_LineTotal(rParam.pBorder->GetLeft()) +
+ lcl_LineTotal(rParam.pBorder->GetRight()) ) * 100 / nZoom;
+
+ if (rParam.pShadow && rParam.pShadow->GetLocation() != SvxShadowLocation::NONE)
+ nPaperWidth -= ( rParam.pShadow->CalcShadowSpace(SvxShadowItemSide::LEFT) +
+ rParam.pShadow->CalcShadowSpace(SvxShadowItemSide::RIGHT) ) * 100 / nZoom;
+
+ pEditEngine->SetPaperSize( Size( nPaperWidth, 10000 ) );
+
+ tools::Long nMaxHeight = 0;
+ if ( rParam.pLeft )
+ {
+ nMaxHeight = std::max( nMaxHeight, TextHeight( rParam.pLeft->GetLeftArea() ) );
+ nMaxHeight = std::max( nMaxHeight, TextHeight( rParam.pLeft->GetCenterArea() ) );
+ nMaxHeight = std::max( nMaxHeight, TextHeight( rParam.pLeft->GetRightArea() ) );
+ }
+ if ( rParam.pRight )
+ {
+ nMaxHeight = std::max( nMaxHeight, TextHeight( rParam.pRight->GetLeftArea() ) );
+ nMaxHeight = std::max( nMaxHeight, TextHeight( rParam.pRight->GetCenterArea() ) );
+ nMaxHeight = std::max( nMaxHeight, TextHeight( rParam.pRight->GetRightArea() ) );
+ }
+ if ( rParam.pFirst )
+ {
+ nMaxHeight = std::max( nMaxHeight, TextHeight( rParam.pFirst->GetLeftArea() ) );
+ nMaxHeight = std::max( nMaxHeight, TextHeight( rParam.pFirst->GetCenterArea() ) );
+ nMaxHeight = std::max( nMaxHeight, TextHeight( rParam.pFirst->GetRightArea() ) );
+ }
+
+ rParam.nHeight = nMaxHeight + rParam.nDistance;
+ if (rParam.pBorder)
+ rParam.nHeight += rParam.pBorder->GetDistance(SvxBoxItemLine::TOP) +
+ rParam.pBorder->GetDistance(SvxBoxItemLine::BOTTOM) +
+ lcl_LineTotal( rParam.pBorder->GetTop() ) +
+ lcl_LineTotal( rParam.pBorder->GetBottom() );
+ if (rParam.pShadow && rParam.pShadow->GetLocation() != SvxShadowLocation::NONE)
+ rParam.nHeight += rParam.pShadow->CalcShadowSpace(SvxShadowItemSide::TOP) +
+ rParam.pShadow->CalcShadowSpace(SvxShadowItemSide::BOTTOM);
+
+ if (rParam.nHeight < rParam.nManHeight)
+ rParam.nHeight = rParam.nManHeight; // configured minimum
+}
+
+void ScPrintFunc::InitParam( const ScPrintOptions* pOptions )
+{
+ if (!pParamSet)
+ return;
+
+ // TabPage "Page"
+ const SvxLRSpaceItem* pLRItem = &pParamSet->Get( ATTR_LRSPACE );
+ tools::Long nTmp;
+ nTmp = pLRItem->GetLeft();
+ nLeftMargin = nTmp < 0 ? 0 : sal_uInt16(nTmp);
+ nTmp = pLRItem->GetRight();
+ nRightMargin = nTmp < 0 ? 0 : sal_uInt16(nTmp);
+ const SvxULSpaceItem* pULItem = &pParamSet->Get( ATTR_ULSPACE );
+ nTopMargin = pULItem->GetUpper();
+ nBottomMargin = pULItem->GetLower();
+
+ const SvxPageItem* pPageItem = &pParamSet->Get( ATTR_PAGE );
+ nPageUsage = pPageItem->GetPageUsage();
+ bLandscape = pPageItem->IsLandscape();
+ aFieldData.eNumType = pPageItem->GetNumType();
+
+ bCenterHor = pParamSet->Get(ATTR_PAGE_HORCENTER).GetValue();
+ bCenterVer = pParamSet->Get(ATTR_PAGE_VERCENTER).GetValue();
+
+ aPageSize = pParamSet->Get(ATTR_PAGE_SIZE).GetSize();
+ if ( !aPageSize.Width() || !aPageSize.Height() )
+ {
+ OSL_FAIL("PageSize Null ?!?!?");
+ aPageSize = SvxPaperInfo::GetPaperSize( PAPER_A4 );
+ }
+
+ pBorderItem = &pParamSet->Get(ATTR_BORDER);
+ pBackgroundItem = &pParamSet->Get(ATTR_BACKGROUND);
+ pShadowItem = &pParamSet->Get(ATTR_SHADOW);
+
+ // TabPage "Headline"
+
+ aHdr.pLeft = &pParamSet->Get(ATTR_PAGE_HEADERLEFT); // Content
+ aHdr.pRight = &pParamSet->Get(ATTR_PAGE_HEADERRIGHT);
+ aHdr.pFirst = &pParamSet->Get(ATTR_PAGE_HEADERFIRST);
+
+ const SfxItemSet* pHeaderSet = nullptr;
+ if ( const SvxSetItem* pHeaderSetItem = pParamSet->GetItemIfSet( ATTR_PAGE_HEADERSET, false ) )
+ {
+ pHeaderSet = &pHeaderSetItem->GetItemSet();
+ // Headline has space below
+ aHdr.nDistance = pHeaderSet->Get(ATTR_ULSPACE).GetLower();
+ }
+ lcl_FillHFParam( aHdr, pHeaderSet );
+
+ // TabPage "Footline"
+
+ aFtr.pLeft = &pParamSet->Get(ATTR_PAGE_FOOTERLEFT); // Content
+ aFtr.pRight = &pParamSet->Get(ATTR_PAGE_FOOTERRIGHT);
+ aFtr.pFirst = &pParamSet->Get(ATTR_PAGE_FOOTERFIRST);
+
+ const SfxItemSet* pFooterSet = nullptr;
+ if ( const SvxSetItem* pFooterSetItem = pParamSet->GetItemIfSet( ATTR_PAGE_FOOTERSET, false ) )
+ {
+ pFooterSet = &pFooterSetItem->GetItemSet();
+ // Footline has space above
+ aFtr.nDistance = pFooterSet->Get(ATTR_ULSPACE).GetUpper();
+ }
+ lcl_FillHFParam( aFtr, pFooterSet );
+
+ // Compile Table-/Area-Params from single Items
+
+ // TabPage "Table"
+
+ const SfxUInt16Item* pScaleItem = nullptr;
+ const ScPageScaleToItem* pScaleToItem = nullptr;
+ const SfxUInt16Item* pScaleToPagesItem = nullptr;
+ SfxItemState eState;
+
+ eState = pParamSet->GetItemState( ATTR_PAGE_SCALE, false,
+ reinterpret_cast<const SfxPoolItem**>(&pScaleItem) );
+ if ( SfxItemState::DEFAULT == eState )
+ pScaleItem = &pParamSet->GetPool()->GetDefaultItem( ATTR_PAGE_SCALE );
+
+ eState = pParamSet->GetItemState( ATTR_PAGE_SCALETO, false,
+ reinterpret_cast<const SfxPoolItem**>(&pScaleToItem) );
+ if ( SfxItemState::DEFAULT == eState )
+ pScaleToItem = &pParamSet->GetPool()->GetDefaultItem( ATTR_PAGE_SCALETO );
+
+ eState = pParamSet->GetItemState( ATTR_PAGE_SCALETOPAGES, false,
+ reinterpret_cast<const SfxPoolItem**>(&pScaleToPagesItem) );
+ if ( SfxItemState::DEFAULT == eState )
+ pScaleToPagesItem = &pParamSet->GetPool()->GetDefaultItem( ATTR_PAGE_SCALETOPAGES );
+
+ OSL_ENSURE( pScaleItem && pScaleToItem && pScaleToPagesItem, "Missing ScaleItem! :-/" );
+
+ aTableParam.bCellContent = true;
+ aTableParam.bNotes = lcl_GetBool(pParamSet,ATTR_PAGE_NOTES);
+ aTableParam.bGrid = lcl_GetBool(pParamSet,ATTR_PAGE_GRID);
+ aTableParam.bHeaders = lcl_GetBool(pParamSet,ATTR_PAGE_HEADERS);
+ aTableParam.bFormulas = lcl_GetBool(pParamSet,ATTR_PAGE_FORMULAS);
+ aTableParam.bNullVals = lcl_GetBool(pParamSet,ATTR_PAGE_NULLVALS);
+ aTableParam.bCharts = lcl_GetShow(pParamSet,ATTR_PAGE_CHARTS);
+ aTableParam.bObjects = lcl_GetShow(pParamSet,ATTR_PAGE_OBJECTS);
+ aTableParam.bDrawings = lcl_GetShow(pParamSet,ATTR_PAGE_DRAWINGS);
+ aTableParam.bTopDown = lcl_GetBool(pParamSet,ATTR_PAGE_TOPDOWN);
+ aTableParam.bLeftRight = !aTableParam.bLeftRight;
+ aTableParam.nFirstPageNo = lcl_GetUShort(pParamSet,ATTR_PAGE_FIRSTPAGENO);
+ if (!aTableParam.nFirstPageNo)
+ aTableParam.nFirstPageNo = static_cast<sal_uInt16>(nPageStart); // from previous table
+
+ if ( pScaleItem && pScaleToItem && pScaleToPagesItem )
+ {
+ sal_uInt16 nScaleAll = pScaleItem->GetValue();
+ sal_uInt16 nScaleToPages = pScaleToPagesItem->GetValue();
+
+ aTableParam.bScaleNone = (nScaleAll == 100);
+ aTableParam.bScaleAll = (nScaleAll > 0 );
+ aTableParam.bScaleTo = pScaleToItem->IsValid();
+ aTableParam.bScalePageNum = (nScaleToPages > 0 );
+ aTableParam.nScaleAll = nScaleAll;
+ aTableParam.nScaleWidth = pScaleToItem->GetWidth();
+ aTableParam.nScaleHeight = pScaleToItem->GetHeight();
+ aTableParam.nScalePageNum = nScaleToPages;
+ }
+ else
+ {
+ aTableParam.bScaleNone = true;
+ aTableParam.bScaleAll = false;
+ aTableParam.bScaleTo = false;
+ aTableParam.bScalePageNum = false;
+ aTableParam.nScaleAll = 0;
+ aTableParam.nScaleWidth = 0;
+ aTableParam.nScaleHeight = 0;
+ aTableParam.nScalePageNum = 0;
+ }
+
+ // skip empty pages only if options with that flag are passed
+ aTableParam.bSkipEmpty = pOptions && pOptions->GetSkipEmpty();
+ if ( pPageData )
+ aTableParam.bSkipEmpty = false;
+ // If pPageData is set, only the breaks are interesting for the
+ // pagebreak preview, empty pages are not addressed separately.
+
+ aTableParam.bForceBreaks = pOptions && pOptions->GetForceBreaks();
+
+ // TabPage "Parts":
+
+ //! walk through all PrintAreas of the table !!!
+ const ScRange* pPrintArea = rDoc.GetPrintRange( nPrintTab, 0 );
+ std::optional<ScRange> oRepeatCol = rDoc.GetRepeatColRange( nPrintTab );
+ std::optional<ScRange> oRepeatRow = rDoc.GetRepeatRowRange( nPrintTab );
+
+ // ignoring ATTR_PAGE_PRINTTABLES
+
+ bool bHasPrintRange = rDoc.HasPrintRange();
+ sal_uInt16 nPrintRangeCount = rDoc.GetPrintRangeCount(nPrintTab);
+ bool bPrintEntireSheet = rDoc.IsPrintEntireSheet(nPrintTab);
+
+ if (!bPrintEntireSheet && !nPrintRangeCount)
+ mbHasPrintRange = false;
+
+ if ( pUserArea ) // UserArea (selection) has priority
+ {
+ bPrintCurrentTable =
+ aAreaParam.bPrintArea = true; // Selection
+ aAreaParam.aPrintArea = *pUserArea;
+
+ // The table-query is already in DocShell::Print, here always
+ aAreaParam.aPrintArea.aStart.SetTab(nPrintTab);
+ aAreaParam.aPrintArea.aEnd.SetTab(nPrintTab);
+ }
+ else if (bHasPrintRange)
+ {
+ if ( pPrintArea ) // at least one set?
+ {
+ bPrintCurrentTable =
+ aAreaParam.bPrintArea = true;
+ aAreaParam.aPrintArea = *pPrintArea;
+
+ bMultiArea = nPrintRangeCount > 1;
+ }
+ else
+ {
+ // do not print hidden sheets with "Print entire sheet" flag
+ bPrintCurrentTable = rDoc.IsPrintEntireSheet( nPrintTab ) && rDoc.IsVisible( nPrintTab );
+ aAreaParam.bPrintArea = !bPrintCurrentTable; // otherwise the table is always counted
+ }
+ }
+ else
+ {
+ // don't print hidden tables if there's no print range defined there
+ if ( rDoc.IsVisible( nPrintTab ) )
+ {
+ aAreaParam.bPrintArea = false;
+ bPrintCurrentTable = true;
+ }
+ else
+ {
+ aAreaParam.bPrintArea = true; // otherwise the table is always counted
+ bPrintCurrentTable = false;
+ }
+ }
+
+ if ( oRepeatCol )
+ {
+ aAreaParam.bRepeatCol = true;
+ nRepeatStartCol = oRepeatCol->aStart.Col();
+ nRepeatEndCol = oRepeatCol->aEnd .Col();
+ }
+ else
+ {
+ aAreaParam.bRepeatCol = false;
+ nRepeatStartCol = nRepeatEndCol = SCCOL_REPEAT_NONE;
+ }
+
+ if ( oRepeatRow )
+ {
+ aAreaParam.bRepeatRow = true;
+ nRepeatStartRow = oRepeatRow->aStart.Row();
+ nRepeatEndRow = oRepeatRow->aEnd .Row();
+ }
+ else
+ {
+ aAreaParam.bRepeatRow = false;
+ nRepeatStartRow = nRepeatEndRow = SCROW_REPEAT_NONE;
+ }
+
+ // Split pages
+
+ if (!bPrintAreaValid)
+ {
+ nTabPages = CountPages(); // also calculates zoom
+ nTotalPages = nTabPages;
+ nTotalPages += CountNotePages();
+ }
+ else
+ {
+ CalcPages(); // search breaks only
+ CountNotePages(); // Count notes, even if number of pages is already known
+ }
+
+ if (nDocPages)
+ aFieldData.nTotalPages = nDocPages;
+ else
+ aFieldData.nTotalPages = nTotalPages;
+
+ SetDateTime( DateTime( DateTime::SYSTEM ) );
+
+ if( pDocShell->getDocProperties()->getTitle().getLength() != 0 )
+ aFieldData.aTitle = pDocShell->getDocProperties()->getTitle();
+ else
+ aFieldData.aTitle = pDocShell->GetTitle();
+
+ const INetURLObject& rURLObj = pDocShell->GetMedium()->GetURLObject();
+ aFieldData.aLongDocName = rURLObj.GetMainURL( INetURLObject::DecodeMechanism::Unambiguous );
+ if ( !aFieldData.aLongDocName.isEmpty() )
+ aFieldData.aShortDocName = rURLObj.GetLastName(INetURLObject::DecodeMechanism::Unambiguous);
+ else
+ aFieldData.aShortDocName = aFieldData.aLongDocName = aFieldData.aTitle;
+
+ // Printer settings (Orientation, Paper) at DoPrint
+}
+
+Size ScPrintFunc::GetDataSize() const
+{
+ Size aSize = aPageSize;
+ aSize.AdjustWidth( -(nLeftMargin + nRightMargin) );
+ aSize.AdjustHeight( -(nTopMargin + nBottomMargin) );
+ aSize.AdjustHeight( -(aHdr.nHeight + aFtr.nHeight) );
+ return aSize;
+}
+
+void ScPrintFunc::GetScaleData( Size& rPhysSize, tools::Long& rDocHdr, tools::Long& rDocFtr )
+{
+ rPhysSize = aPageSize;
+ rPhysSize.AdjustWidth( -(nLeftMargin + nRightMargin) );
+ rPhysSize.AdjustHeight( -(nTopMargin + nBottomMargin) );
+
+ rDocHdr = aHdr.nHeight;
+ rDocFtr = aFtr.nHeight;
+}
+
+void ScPrintFunc::SetDateTime( const DateTime& rDateTime )
+{
+ aFieldData.aDateTime = rDateTime;
+}
+
+static void lcl_DrawGraphic( const Graphic &rGraphic, vcl::RenderContext& rOutDev,
+ const tools::Rectangle &rGrf, const tools::Rectangle &rOut )
+{
+ const bool bNotInside = !rOut.Contains( rGrf );
+ if ( bNotInside )
+ {
+ rOutDev.Push();
+ rOutDev.IntersectClipRegion( rOut );
+ }
+
+ rGraphic.Draw(rOutDev, rGrf.TopLeft(), rGrf.GetSize());
+
+ if ( bNotInside )
+ rOutDev.Pop();
+}
+
+static void lcl_DrawGraphic( const SvxBrushItem &rBrush, vcl::RenderContext& rOutDev, const OutputDevice* pRefDev,
+ const tools::Rectangle &rOrg, const tools::Rectangle &rOut,
+ OUString const & referer )
+{
+ Size aGrfSize(0,0);
+ const Graphic *pGraphic = rBrush.GetGraphic(referer);
+ SvxGraphicPosition ePos;
+ if ( pGraphic && pGraphic->IsSupportedGraphic() )
+ {
+ const MapMode aMapMM( MapUnit::Map100thMM );
+ if ( pGraphic->GetPrefMapMode().GetMapUnit() == MapUnit::MapPixel )
+ aGrfSize = pRefDev->PixelToLogic( pGraphic->GetPrefSize(), aMapMM );
+ else
+ aGrfSize = OutputDevice::LogicToLogic( pGraphic->GetPrefSize(),
+ pGraphic->GetPrefMapMode(), aMapMM );
+ ePos = rBrush.GetGraphicPos();
+ }
+ else
+ ePos = GPOS_NONE;
+
+ Point aPos;
+ Size aDrawSize = aGrfSize;
+
+ bool bDraw = true;
+ switch ( ePos )
+ {
+ case GPOS_LT: aPos = rOrg.TopLeft();
+ break;
+ case GPOS_MT: aPos.setY( rOrg.Top() );
+ aPos.setX( rOrg.Left() + rOrg.GetSize().Width()/2 - aGrfSize.Width()/2 );
+ break;
+ case GPOS_RT: aPos.setY( rOrg.Top() );
+ aPos.setX( rOrg.Right() - aGrfSize.Width() );
+ break;
+
+ case GPOS_LM: aPos.setY( rOrg.Top() + rOrg.GetSize().Height()/2 - aGrfSize.Height()/2 );
+ aPos.setX( rOrg.Left() );
+ break;
+ case GPOS_MM: aPos.setY( rOrg.Top() + rOrg.GetSize().Height()/2 - aGrfSize.Height()/2 );
+ aPos.setX( rOrg.Left() + rOrg.GetSize().Width()/2 - aGrfSize.Width()/2 );
+ break;
+ case GPOS_RM: aPos.setY( rOrg.Top() + rOrg.GetSize().Height()/2 - aGrfSize.Height()/2 );
+ aPos.setX( rOrg.Right() - aGrfSize.Width() );
+ break;
+
+ case GPOS_LB: aPos.setY( rOrg.Bottom() - aGrfSize.Height() );
+ aPos.setX( rOrg.Left() );
+ break;
+ case GPOS_MB: aPos.setY( rOrg.Bottom() - aGrfSize.Height() );
+ aPos.setX( rOrg.Left() + rOrg.GetSize().Width()/2 - aGrfSize.Width()/2 );
+ break;
+ case GPOS_RB: aPos.setY( rOrg.Bottom() - aGrfSize.Height() );
+ aPos.setX( rOrg.Right() - aGrfSize.Width() );
+ break;
+
+ case GPOS_AREA:
+ aPos = rOrg.TopLeft();
+ aDrawSize = rOrg.GetSize();
+ break;
+ case GPOS_TILED:
+ {
+ // use GraphicObject::DrawTiled instead of an own loop
+ // (pixel rounding is handled correctly, and a very small bitmap
+ // is duplicated into a bigger one for better performance)
+
+ GraphicObject aObject( *pGraphic );
+
+ if( rOutDev.GetOutDevType() == OUTDEV_PDF &&
+ (aObject.GetType() == GraphicType::Bitmap || aObject.GetType() == GraphicType::Default) )
+ {
+ // For PDF export, every draw
+ // operation for bitmaps takes a noticeable
+ // amount of place (~50 characters). Thus,
+ // optimize between tile bitmap size and
+ // number of drawing operations here.
+ //
+ // A_out
+ // n_chars = k1 * ---------- + k2 * A_bitmap
+ // A_bitmap
+ //
+ // minimum n_chars is obtained for (derive for
+ // A_bitmap, set to 0, take positive
+ // solution):
+ // k1
+ // A_bitmap = Sqrt( ---- A_out )
+ // k2
+ //
+ // where k1 is the number of chars per draw
+ // operation, and k2 is the number of chars
+ // per bitmap pixel. This is approximately 50
+ // and 7 for current PDF writer, respectively.
+
+ const double k1( 50 );
+ const double k2( 7 );
+ const Size aSize( rOrg.GetSize() );
+ const double Abitmap( k1/k2 * aSize.Width()*aSize.Height() );
+
+ aObject.DrawTiled( rOutDev, rOrg, aGrfSize, Size(0,0),
+ ::std::max( 128, static_cast<int>( sqrt(sqrt( Abitmap)) + .5 ) ) );
+ }
+ else
+ {
+ aObject.DrawTiled( rOutDev, rOrg, aGrfSize, Size(0,0) );
+ }
+
+ bDraw = false;
+ }
+ break;
+
+ case GPOS_NONE:
+ bDraw = false;
+ break;
+
+ default: OSL_ENSURE( false, "new Graphic position?" );
+ }
+ tools::Rectangle aGrf( aPos,aDrawSize );
+ if ( bDraw && aGrf.Overlaps( rOut ) )
+ {
+ lcl_DrawGraphic( *pGraphic, rOutDev, aGrf, rOut );
+ }
+}
+
+// The frame is drawn inwards
+
+void ScPrintFunc::DrawBorder( tools::Long nScrX, tools::Long nScrY, tools::Long nScrW, tools::Long nScrH,
+ const SvxBoxItem* pBorderData, const SvxBrushItem* pBackground,
+ const SvxShadowItem* pShadow )
+{
+ //! direct output from SvxBoxItem !!!
+
+ if (pBorderData)
+ if ( !pBorderData->GetTop() && !pBorderData->GetBottom() && !pBorderData->GetLeft() &&
+ !pBorderData->GetRight() )
+ pBorderData = nullptr;
+
+ if (!pBorderData && !pBackground && !pShadow)
+ return; // nothing to do
+
+ tools::Long nLeft = 0;
+ tools::Long nRight = 0;
+ tools::Long nTop = 0;
+ tools::Long nBottom = 0;
+
+ // aFrameRect - outside around frame, without shadow
+ if ( pShadow && pShadow->GetLocation() != SvxShadowLocation::NONE )
+ {
+ nLeft += static_cast<tools::Long>( pShadow->CalcShadowSpace(SvxShadowItemSide::LEFT) * nScaleX );
+ nRight += static_cast<tools::Long>( pShadow->CalcShadowSpace(SvxShadowItemSide::RIGHT) * nScaleX );
+ nTop += static_cast<tools::Long>( pShadow->CalcShadowSpace(SvxShadowItemSide::TOP) * nScaleY );
+ nBottom += static_cast<tools::Long>( pShadow->CalcShadowSpace(SvxShadowItemSide::BOTTOM) * nScaleY );
+ }
+ tools::Rectangle aFrameRect( Point(nScrX+nLeft, nScrY+nTop),
+ Size(nScrW-nLeft-nRight, nScrH-nTop-nBottom) );
+
+ // center of frame, to paint lines through OutputData
+ if (pBorderData)
+ {
+ nLeft += static_cast<tools::Long>( lcl_LineTotal(pBorderData->GetLeft()) * nScaleX / 2 );
+ nRight += static_cast<tools::Long>( lcl_LineTotal(pBorderData->GetRight()) * nScaleX / 2 );
+ nTop += static_cast<tools::Long>( lcl_LineTotal(pBorderData->GetTop()) * nScaleY / 2 );
+ nBottom += static_cast<tools::Long>( lcl_LineTotal(pBorderData->GetBottom()) * nScaleY / 2 );
+ }
+ tools::Long nEffHeight = nScrH - nTop - nBottom;
+ tools::Long nEffWidth = nScrW - nLeft - nRight;
+ if (nEffHeight<=0 || nEffWidth<=0)
+ return; // empty
+
+ if ( pBackground )
+ {
+ if (pBackground->GetGraphicPos() != GPOS_NONE)
+ {
+ OutputDevice* pRefDev;
+ if ( bIsRender )
+ pRefDev = pDev; // don't use printer for PDF
+ else
+ pRefDev = rDoc.GetPrinter(); // use printer also for preview
+ OUString referer;
+ if (pDocShell->HasName()) {
+ referer = pDocShell->GetMedium()->GetName();
+ }
+ lcl_DrawGraphic(*pBackground, *pDev, pRefDev, aFrameRect, aFrameRect, referer);
+ }
+ else
+ {
+ pDev->SetFillColor(pBackground->GetColor());
+ pDev->SetLineColor();
+ pDev->DrawRect(aFrameRect);
+ }
+ }
+
+ if ( pShadow && pShadow->GetLocation() != SvxShadowLocation::NONE )
+ {
+ pDev->SetFillColor(pShadow->GetColor());
+ pDev->SetLineColor();
+ tools::Long nShadowX = static_cast<tools::Long>( pShadow->GetWidth() * nScaleX );
+ tools::Long nShadowY = static_cast<tools::Long>( pShadow->GetWidth() * nScaleY );
+ switch (pShadow->GetLocation())
+ {
+ case SvxShadowLocation::TopLeft:
+ pDev->DrawRect( tools::Rectangle(
+ aFrameRect.Left()-nShadowX, aFrameRect.Top()-nShadowY,
+ aFrameRect.Right()-nShadowX, aFrameRect.Top() ) );
+ pDev->DrawRect( tools::Rectangle(
+ aFrameRect.Left()-nShadowX, aFrameRect.Top()-nShadowY,
+ aFrameRect.Left(), aFrameRect.Bottom()-nShadowY ) );
+ break;
+ case SvxShadowLocation::TopRight:
+ pDev->DrawRect( tools::Rectangle(
+ aFrameRect.Left()+nShadowX, aFrameRect.Top()-nShadowY,
+ aFrameRect.Right()+nShadowX, aFrameRect.Top() ) );
+ pDev->DrawRect( tools::Rectangle(
+ aFrameRect.Right(), aFrameRect.Top()-nShadowY,
+ aFrameRect.Right()+nShadowX, aFrameRect.Bottom()-nShadowY ) );
+ break;
+ case SvxShadowLocation::BottomLeft:
+ pDev->DrawRect( tools::Rectangle(
+ aFrameRect.Left()-nShadowX, aFrameRect.Bottom(),
+ aFrameRect.Right()-nShadowX, aFrameRect.Bottom()+nShadowY ) );
+ pDev->DrawRect( tools::Rectangle(
+ aFrameRect.Left()-nShadowX, aFrameRect.Top()+nShadowY,
+ aFrameRect.Left(), aFrameRect.Bottom()+nShadowY ) );
+ break;
+ case SvxShadowLocation::BottomRight:
+ pDev->DrawRect( tools::Rectangle(
+ aFrameRect.Left()+nShadowX, aFrameRect.Bottom(),
+ aFrameRect.Right()+nShadowX, aFrameRect.Bottom()+nShadowY ) );
+ pDev->DrawRect( tools::Rectangle(
+ aFrameRect.Right(), aFrameRect.Top()+nShadowY,
+ aFrameRect.Right()+nShadowX, aFrameRect.Bottom()+nShadowY ) );
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+
+ if (!pBorderData)
+ return;
+
+ ScDocumentUniquePtr pBorderDoc(new ScDocument( SCDOCMODE_UNDO ));
+ pBorderDoc->InitUndo( rDoc, 0,0, true,true );
+ pBorderDoc->ApplyAttr( 0,0,0, *pBorderData );
+
+ ScTableInfo aTabInfo;
+ pBorderDoc->FillInfo( aTabInfo, 0,0, 0,0, 0,
+ nScaleX, nScaleY, false, false );
+ OSL_ENSURE(aTabInfo.mnArrCount,"nArrCount == 0");
+
+ aTabInfo.mpRowInfo[1].nHeight = static_cast<sal_uInt16>(nEffHeight);
+ aTabInfo.mpRowInfo[0].basicCellInfo(0).nWidth =
+ aTabInfo.mpRowInfo[1].basicCellInfo(0).nWidth = static_cast<sal_uInt16>(nEffWidth);
+
+ ScOutputData aOutputData( pDev, OUTTYPE_PRINTER, aTabInfo, pBorderDoc.get(), 0,
+ nScrX+nLeft, nScrY+nTop, 0,0, 0,0, nScaleX, nScaleY );
+ aOutputData.SetUseStyleColor( bUseStyleColor );
+
+ aOutputData.DrawFrame(*pDev);
+}
+
+void ScPrintFunc::PrintColHdr( SCCOL nX1, SCCOL nX2, tools::Long nScrX, tools::Long nScrY )
+{
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nPrintTab );
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ Size aOnePixel = pDev->PixelToLogic(Size(1,1));
+ tools::Long nOneX = aOnePixel.Width();
+ tools::Long nOneY = aOnePixel.Height();
+ SCCOL nCol;
+
+ tools::Long nHeight = static_cast<tools::Long>(PRINT_HEADER_HEIGHT * nScaleY);
+ tools::Long nEndY = nScrY + nHeight - nOneY;
+
+ tools::Long nPosX = nScrX;
+ if ( bLayoutRTL )
+ {
+ for (nCol=nX1; nCol<=nX2; nCol++)
+ nPosX += static_cast<tools::Long>( rDoc.GetColWidth( nCol, nPrintTab ) * nScaleX );
+ }
+ else
+ nPosX -= nOneX;
+ tools::Long nPosY = nScrY - nOneY;
+ OUString aText;
+
+ for (nCol=nX1; nCol<=nX2; nCol++)
+ {
+ sal_uInt16 nDocW = rDoc.GetColWidth( nCol, nPrintTab );
+ if (nDocW)
+ {
+ tools::Long nWidth = static_cast<tools::Long>(nDocW * nScaleX);
+ tools::Long nEndX = nPosX + nWidth * nLayoutSign;
+
+ pDev->DrawRect( tools::Rectangle( nPosX,nPosY,nEndX,nEndY ) );
+
+ aText = ::ScColToAlpha( nCol);
+ tools::Long nTextWidth = pDev->GetTextWidth(aText);
+ tools::Long nTextHeight = pDev->GetTextHeight();
+ tools::Long nAddX = ( nWidth - nTextWidth ) / 2;
+ tools::Long nAddY = ( nHeight - nTextHeight ) / 2;
+ tools::Long nTextPosX = nPosX+nAddX;
+ if ( bLayoutRTL )
+ nTextPosX -= nWidth;
+ pDev->DrawText( Point( nTextPosX,nPosY+nAddY ), aText );
+
+ nPosX = nEndX;
+ }
+ }
+}
+
+void ScPrintFunc::PrintRowHdr( SCROW nY1, SCROW nY2, tools::Long nScrX, tools::Long nScrY )
+{
+ Size aOnePixel = pDev->PixelToLogic(Size(1,1));
+ tools::Long nOneX = aOnePixel.Width();
+ tools::Long nOneY = aOnePixel.Height();
+
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nPrintTab );
+
+ tools::Long nWidth = static_cast<tools::Long>(PRINT_HEADER_WIDTH * nScaleX);
+ tools::Long nEndX = nScrX + nWidth;
+ tools::Long nPosX = nScrX;
+ if ( !bLayoutRTL )
+ {
+ nEndX -= nOneX;
+ nPosX -= nOneX;
+ }
+ tools::Long nPosY = nScrY - nOneY;
+ OUString aText;
+
+ for (SCROW nRow=nY1; nRow<=nY2; nRow++)
+ {
+ sal_uInt16 nDocH = rDoc.GetRowHeight( nRow, nPrintTab );
+ if (nDocH)
+ {
+ tools::Long nHeight = static_cast<tools::Long>(nDocH * nScaleY);
+ tools::Long nEndY = nPosY + nHeight;
+
+ pDev->DrawRect( tools::Rectangle( nPosX,nPosY,nEndX,nEndY ) );
+
+ aText = OUString::number( nRow+1 );
+ tools::Long nTextWidth = pDev->GetTextWidth(aText);
+ tools::Long nTextHeight = pDev->GetTextHeight();
+ tools::Long nAddX = ( nWidth - nTextWidth ) / 2;
+ tools::Long nAddY = ( nHeight - nTextHeight ) / 2;
+ pDev->DrawText( Point( nPosX+nAddX,nPosY+nAddY ), aText );
+
+ nPosY = nEndY;
+ }
+ }
+}
+
+void ScPrintFunc::LocateColHdr( SCCOL nX1, SCCOL nX2, tools::Long nScrX, tools::Long nScrY,
+ bool bRepCol, ScPreviewLocationData& rLocationData )
+{
+ Size aOnePixel = pDev->PixelToLogic(Size(1,1));
+ tools::Long nOneX = aOnePixel.Width();
+ tools::Long nOneY = aOnePixel.Height();
+
+ tools::Long nHeight = static_cast<tools::Long>(PRINT_HEADER_HEIGHT * nScaleY);
+ tools::Long nEndY = nScrY + nHeight - nOneY;
+
+ tools::Long nPosX = nScrX - nOneX;
+ for (SCCOL nCol=nX1; nCol<=nX2; nCol++)
+ {
+ sal_uInt16 nDocW = rDoc.GetColWidth( nCol, nPrintTab );
+ if (nDocW)
+ nPosX += static_cast<tools::Long>(nDocW * nScaleX);
+ }
+ tools::Rectangle aCellRect( nScrX, nScrY, nPosX, nEndY );
+ rLocationData.AddColHeaders( aCellRect, nX1, nX2, bRepCol );
+}
+
+void ScPrintFunc::LocateRowHdr( SCROW nY1, SCROW nY2, tools::Long nScrX, tools::Long nScrY,
+ bool bRepRow, ScPreviewLocationData& rLocationData )
+{
+ Size aOnePixel = pDev->PixelToLogic(Size(1,1));
+ tools::Long nOneX = aOnePixel.Width();
+ tools::Long nOneY = aOnePixel.Height();
+
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nPrintTab );
+
+ tools::Long nWidth = static_cast<tools::Long>(PRINT_HEADER_WIDTH * nScaleX);
+ tools::Long nEndX = nScrX + nWidth;
+ if ( !bLayoutRTL )
+ nEndX -= nOneX;
+
+ tools::Long nPosY = nScrY - nOneY;
+ nPosY += rDoc.GetScaledRowHeight( nY1, nY2, nPrintTab, nScaleY);
+ tools::Rectangle aCellRect( nScrX, nScrY, nEndX, nPosY );
+ rLocationData.AddRowHeaders( aCellRect, nY1, nY2, bRepRow );
+}
+
+void ScPrintFunc::LocateArea( SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2,
+ tools::Long nScrX, tools::Long nScrY, bool bRepCol, bool bRepRow,
+ ScPreviewLocationData& rLocationData )
+{
+ // get MapMode for drawing objects (same MapMode as in ScOutputData::PrintDrawingLayer)
+
+ Point aLogPos = OutputDevice::LogicToLogic(Point(nScrX,nScrY), aOffsetMode, aLogicMode);
+ tools::Long nLogStX = aLogPos.X();
+ tools::Long nLogStY = aLogPos.Y();
+
+ SCCOL nCol;
+ Point aTwipOffset;
+ for (nCol=0; nCol<nX1; nCol++)
+ aTwipOffset.AdjustX( -(rDoc.GetColWidth( nCol, nPrintTab )) );
+ aTwipOffset.AdjustY( -sal_Int32(rDoc.GetRowHeight( 0, nY1-1, nPrintTab )) );
+
+ Point aMMOffset(o3tl::convert(aTwipOffset, o3tl::Length::twip, o3tl::Length::mm100));
+ aMMOffset += Point( nLogStX, nLogStY );
+ MapMode aDrawMapMode( MapUnit::Map100thMM, aMMOffset, aLogicMode.GetScaleX(), aLogicMode.GetScaleY() );
+
+ // get pixel rectangle
+
+ Size aOnePixel = pDev->PixelToLogic(Size(1,1));
+ tools::Long nOneX = aOnePixel.Width();
+ tools::Long nOneY = aOnePixel.Height();
+
+ tools::Long nPosX = nScrX - nOneX;
+ for (nCol=nX1; nCol<=nX2; nCol++)
+ {
+ sal_uInt16 nDocW = rDoc.GetColWidth( nCol, nPrintTab );
+ if (nDocW)
+ nPosX += static_cast<tools::Long>(nDocW * nScaleX);
+ }
+
+ tools::Long nPosY = nScrY - nOneY;
+ nPosY += rDoc.GetScaledRowHeight( nY1, nY2, nPrintTab, nScaleY);
+ tools::Rectangle aCellRect( nScrX, nScrY, nPosX, nPosY );
+ rLocationData.AddCellRange( aCellRect, ScRange( nX1,nY1,nPrintTab, nX2,nY2,nPrintTab ),
+ bRepCol, bRepRow, aDrawMapMode );
+}
+
+void ScPrintFunc::PrintArea( SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2,
+ tools::Long nScrX, tools::Long nScrY,
+ bool bShLeft, bool bShTop, bool bShRight, bool bShBottom )
+{
+ // #i47547# nothing to do if the end of the print area is before the end of
+ // the repeat columns/rows (don't use negative size for ScOutputData)
+ if ( nX2 < nX1 || nY2 < nY1 )
+ return;
+
+ //! hand over Flag at FillInfo !!!!!
+ ScRange aERange;
+ bool bEmbed = rDoc.IsEmbedded();
+ if (bEmbed)
+ {
+ rDoc.GetEmbedded(aERange);
+ rDoc.ResetEmbedded();
+ }
+
+ Point aPos = OutputDevice::LogicToLogic(Point(nScrX,nScrY), aOffsetMode, aLogicMode);
+ tools::Long nLogStX = aPos.X();
+ tools::Long nLogStY = aPos.Y();
+
+ // Assemble data
+
+ ScTableInfo aTabInfo;
+ rDoc.FillInfo( aTabInfo, nX1, nY1, nX2, nY2, nPrintTab,
+ nScaleX, nScaleY, true, aTableParam.bFormulas );
+ lcl_HidePrint( aTabInfo, nX1, nX2 );
+
+ if (bEmbed)
+ rDoc.SetEmbedded(aERange);
+
+ ScOutputData aOutputData( pDev, OUTTYPE_PRINTER, aTabInfo, &rDoc, nPrintTab,
+ nScrX, nScrY, nX1, nY1, nX2, nY2, nScaleX, nScaleY );
+
+ aOutputData.SetDrawView( pDrawView );
+
+ // test if all paint parts are hidden, then a paint is not necessary at all
+ const Point aMMOffset(aOutputData.PrePrintDrawingLayer(nLogStX, nLogStY));
+ const bool bHideAllDrawingLayer( pDrawView && pDrawView->getHideOle() && pDrawView->getHideChart()
+ && pDrawView->getHideDraw() && pDrawView->getHideFormControl() );
+
+ if(!bHideAllDrawingLayer)
+ {
+ pDev->SetMapMode(aLogicMode);
+ // don's set Clipping here (Mapmode is being moved)
+
+ // #i72502#
+ aOutputData.PrintDrawingLayer(SC_LAYER_BACK, aMMOffset);
+ }
+
+ pDev->SetMapMode(aOffsetMode);
+
+ aOutputData.SetShowFormulas( aTableParam.bFormulas );
+ aOutputData.SetShowNullValues( aTableParam.bNullVals );
+ aOutputData.SetUseStyleColor( bUseStyleColor );
+
+ Color aGridColor( COL_BLACK );
+ if ( bUseStyleColor )
+ aGridColor = SC_MOD()->GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor;
+ aOutputData.SetGridColor( aGridColor );
+
+ if ( !pPrinter )
+ {
+ OutputDevice* pRefDev = rDoc.GetPrinter(); // use the printer also for Preview
+ Fraction aPrintFrac( nZoom, 100 ); // without nManualZoom
+ // MapMode, as it would arrive at the printer:
+ pRefDev->SetMapMode( MapMode( MapUnit::Map100thMM, Point(), aPrintFrac, aPrintFrac ) );
+
+ // when rendering (PDF), don't use printer as ref device, but printer's MapMode
+ // has to be set anyway, as charts still use it (#106409#)
+ if ( !bIsRender )
+ aOutputData.SetRefDevice( pRefDev );
+ }
+
+ if( aTableParam.bCellContent )
+ aOutputData.DrawBackground(*pDev);
+
+ pDev->SetClipRegion(vcl::Region(tools::Rectangle(
+ aPos, Size(aOutputData.GetScrW(), aOutputData.GetScrH()))));
+ pDev->SetClipRegion();
+
+ if( aTableParam.bCellContent )
+ {
+ aOutputData.DrawExtraShadow( bShLeft, bShTop, bShRight, bShBottom );
+ aOutputData.DrawFrame(*pDev);
+ aOutputData.DrawSparklines(*pDev);
+ aOutputData.DrawStrings();
+ aOutputData.DrawEdit(false);
+ }
+
+ if (aTableParam.bGrid)
+ aOutputData.DrawGrid(*pDev, true, false); // no page breaks
+
+ aOutputData.AddPDFNotes(); // has no effect if not rendering PDF with notes enabled
+
+ // test if all paint parts are hidden, then a paint is not necessary at all
+ if(!bHideAllDrawingLayer)
+ {
+ // #i72502#
+ aOutputData.PrintDrawingLayer(SC_LAYER_FRONT, aMMOffset);
+ }
+
+ // #i72502#
+ aOutputData.PrintDrawingLayer(SC_LAYER_INTERN, aMMOffset);
+ aOutputData.PostPrintDrawingLayer(aMMOffset); // #i74768#
+}
+
+bool ScPrintFunc::IsMirror( tools::Long nPageNo ) // Mirror margins?
+{
+ return nPageUsage == SvxPageUsage::Mirror && (nPageNo & 1);
+}
+
+bool ScPrintFunc::IsLeft( tools::Long nPageNo ) // left foot notes?
+{
+ bool bLeft;
+ if (nPageUsage == SvxPageUsage::Left)
+ bLeft = true;
+ else if (nPageUsage == SvxPageUsage::Right)
+ bLeft = false;
+ else
+ bLeft = (nPageNo & 1) != 0;
+ return bLeft;
+}
+
+void ScPrintFunc::MakeTableString()
+{
+ OUString aTmp;
+ rDoc.GetName(nPrintTab, aTmp);
+ aFieldData.aTabName = aTmp;
+}
+
+void ScPrintFunc::MakeEditEngine()
+{
+ if (!pEditEngine)
+ {
+ // can't use document's edit engine pool here,
+ // because pool must have twips as default metric
+ pEditEngine.reset( new ScHeaderEditEngine( EditEngine::CreatePool().get() ) );
+
+ pEditEngine->EnableUndo(false);
+ //fdo#45869 we want text to be positioned as it would be for the
+ //high dpi printed output, not as would be ideal for the 96dpi preview
+ //window itself
+ pEditEngine->SetRefDevice(pPrinter ? pPrinter : rDoc.GetRefDevice());
+ pEditEngine->SetWordDelimiters(
+ ScEditUtil::ModifyDelimiters( pEditEngine->GetWordDelimiters() ) );
+ pEditEngine->SetControlWord( pEditEngine->GetControlWord() & ~EEControlBits::RTFSTYLESHEETS );
+ rDoc.ApplyAsianEditSettings( *pEditEngine );
+ pEditEngine->EnableAutoColor( bUseStyleColor );
+
+ // Default-Set for alignment
+ pEditDefaults.reset( new SfxItemSet( pEditEngine->GetEmptyItemSet() ) );
+
+ const ScPatternAttr& rPattern = rDoc.GetPool()->GetDefaultItem(ATTR_PATTERN);
+ rPattern.FillEditItemSet( pEditDefaults.get() );
+ // FillEditItemSet adjusts font height to 1/100th mm,
+ // but for header/footer twips is needed, as in the PatternAttr:
+ pEditDefaults->Put( rPattern.GetItem(ATTR_FONT_HEIGHT).CloneSetWhich(EE_CHAR_FONTHEIGHT) );
+ pEditDefaults->Put( rPattern.GetItem(ATTR_CJK_FONT_HEIGHT).CloneSetWhich(EE_CHAR_FONTHEIGHT_CJK) );
+ pEditDefaults->Put( rPattern.GetItem(ATTR_CTL_FONT_HEIGHT).CloneSetWhich(EE_CHAR_FONTHEIGHT_CTL) );
+ // don't use font color, because background color is not used
+ //! there's no way to set the background for note pages
+ pEditDefaults->ClearItem( EE_CHAR_COLOR );
+ if (ScGlobal::IsSystemRTL())
+ pEditDefaults->Put( SvxFrameDirectionItem( SvxFrameDirection::Horizontal_RL_TB, EE_PARA_WRITINGDIR ) );
+ }
+
+ pEditEngine->SetData( aFieldData ); // Set page count etc.
+}
+
+// nStartY = logic
+void ScPrintFunc::PrintHF( tools::Long nPageNo, bool bHeader, tools::Long nStartY,
+ bool bDoPrint, ScPreviewLocationData* pLocationData )
+{
+ const ScPrintHFParam& rParam = bHeader ? aHdr : aFtr;
+
+ pDev->SetMapMode( aTwipMode ); // Head-/Footlines in Twips
+
+ bool bFirst = 0 == nPageNo && !rParam.bSharedFirst;
+ bool bLeft = IsLeft(nPageNo) && !rParam.bShared;
+ const ScPageHFItem* pHFItem = bFirst ? rParam.pFirst : (bLeft ? rParam.pLeft : rParam.pRight);
+
+ tools::Long nLineStartX = aPageRect.Left() + rParam.nLeft;
+ tools::Long nLineEndX = aPageRect.Right() - rParam.nRight;
+ tools::Long nLineWidth = nLineEndX - nLineStartX + 1;
+
+ // Edit-Engine
+
+ Point aStart( nLineStartX, nStartY );
+ Size aPaperSize( nLineWidth, rParam.nHeight-rParam.nDistance );
+ if ( rParam.pBorder )
+ {
+ tools::Long nLeft = lcl_LineTotal( rParam.pBorder->GetLeft() ) + rParam.pBorder->GetDistance(SvxBoxItemLine::LEFT);
+ tools::Long nTop = lcl_LineTotal( rParam.pBorder->GetTop() ) + rParam.pBorder->GetDistance(SvxBoxItemLine::TOP);
+ aStart.AdjustX(nLeft );
+ aStart.AdjustY(nTop );
+ aPaperSize.AdjustWidth( -(nLeft + lcl_LineTotal( rParam.pBorder->GetRight() ) + rParam.pBorder->GetDistance(SvxBoxItemLine::RIGHT)) );
+ aPaperSize.AdjustHeight( -(nTop + lcl_LineTotal( rParam.pBorder->GetBottom() ) + rParam.pBorder->GetDistance(SvxBoxItemLine::BOTTOM)) );
+ }
+
+ if ( rParam.pShadow && rParam.pShadow->GetLocation() != SvxShadowLocation::NONE )
+ {
+ tools::Long nLeft = rParam.pShadow->CalcShadowSpace(SvxShadowItemSide::LEFT);
+ tools::Long nTop = rParam.pShadow->CalcShadowSpace(SvxShadowItemSide::TOP);
+ aStart.AdjustX(nLeft );
+ aStart.AdjustY(nTop );
+ aPaperSize.AdjustWidth( -(nLeft + rParam.pShadow->CalcShadowSpace(SvxShadowItemSide::RIGHT)) );
+ aPaperSize.AdjustHeight( -(nTop + rParam.pShadow->CalcShadowSpace(SvxShadowItemSide::BOTTOM)) );
+ }
+
+ aFieldData.nPageNo = nPageNo+aTableParam.nFirstPageNo;
+ MakeEditEngine();
+
+ pEditEngine->SetPaperSize(aPaperSize);
+
+ // Frame / Background
+
+ Point aBorderStart( nLineStartX, nStartY );
+ Size aBorderSize( nLineWidth, rParam.nHeight-rParam.nDistance );
+ if ( rParam.bDynamic )
+ {
+ // adjust here again, for even/odd head-/footlines
+ // and probably other breaks by variable (page number etc.)
+
+ tools::Long nMaxHeight = 0;
+ nMaxHeight = std::max( nMaxHeight, TextHeight( pHFItem->GetLeftArea() ) );
+ nMaxHeight = std::max( nMaxHeight, TextHeight( pHFItem->GetCenterArea() ) );
+ nMaxHeight = std::max( nMaxHeight, TextHeight( pHFItem->GetRightArea() ) );
+ if (rParam.pBorder)
+ nMaxHeight += lcl_LineTotal( rParam.pBorder->GetTop() ) +
+ lcl_LineTotal( rParam.pBorder->GetBottom() ) +
+ rParam.pBorder->GetDistance(SvxBoxItemLine::TOP) +
+ rParam.pBorder->GetDistance(SvxBoxItemLine::BOTTOM);
+ if (rParam.pShadow && rParam.pShadow->GetLocation() != SvxShadowLocation::NONE)
+ nMaxHeight += rParam.pShadow->CalcShadowSpace(SvxShadowItemSide::TOP) +
+ rParam.pShadow->CalcShadowSpace(SvxShadowItemSide::BOTTOM);
+
+ if (nMaxHeight < rParam.nManHeight-rParam.nDistance)
+ nMaxHeight = rParam.nManHeight-rParam.nDistance; // configured Minimum
+
+ aBorderSize.setHeight( nMaxHeight );
+ }
+
+ if ( bDoPrint )
+ {
+ double nOldScaleX = nScaleX;
+ double nOldScaleY = nScaleY;
+ nScaleX = nScaleY = 1.0; // output directly in Twips
+ DrawBorder( aBorderStart.X(), aBorderStart.Y(), aBorderSize.Width(), aBorderSize.Height(),
+ rParam.pBorder, rParam.pBack, rParam.pShadow );
+ nScaleX = nOldScaleX;
+ nScaleY = nOldScaleY;
+
+ // Clipping for Text
+
+ pDev->SetClipRegion(vcl::Region(tools::Rectangle(aStart, aPaperSize)));
+
+ // left
+
+ const EditTextObject* pObject = pHFItem->GetLeftArea();
+ if (pObject)
+ {
+ pEditDefaults->Put( SvxAdjustItem( SvxAdjust::Left, EE_PARA_JUST ) );
+ pEditEngine->SetTextNewDefaults( *pObject, *pEditDefaults, false );
+ Point aDraw = aStart;
+ tools::Long nDif = aPaperSize.Height() - static_cast<tools::Long>(pEditEngine->GetTextHeight());
+ if (nDif > 0)
+ aDraw.AdjustY(nDif / 2 );
+ pEditEngine->Draw(*pDev, aDraw);
+ }
+
+ // center
+
+ pObject = pHFItem->GetCenterArea();
+ if (pObject)
+ {
+ pEditDefaults->Put( SvxAdjustItem( SvxAdjust::Center, EE_PARA_JUST ) );
+ pEditEngine->SetTextNewDefaults( *pObject, *pEditDefaults, false );
+ Point aDraw = aStart;
+ tools::Long nDif = aPaperSize.Height() - static_cast<tools::Long>(pEditEngine->GetTextHeight());
+ if (nDif > 0)
+ aDraw.AdjustY(nDif / 2 );
+ pEditEngine->Draw(*pDev, aDraw);
+ }
+
+ // right
+
+ pObject = pHFItem->GetRightArea();
+ if (pObject)
+ {
+ pEditDefaults->Put( SvxAdjustItem( SvxAdjust::Right, EE_PARA_JUST ) );
+ pEditEngine->SetTextNewDefaults( *pObject, *pEditDefaults, false );
+ Point aDraw = aStart;
+ tools::Long nDif = aPaperSize.Height() - static_cast<tools::Long>(pEditEngine->GetTextHeight());
+ if (nDif > 0)
+ aDraw.AdjustY(nDif / 2 );
+ pEditEngine->Draw(*pDev, aDraw);
+ }
+
+ pDev->SetClipRegion();
+ }
+
+ if ( pLocationData )
+ {
+ tools::Rectangle aHeaderRect( aBorderStart, aBorderSize );
+ pLocationData->AddHeaderFooter( aHeaderRect, bHeader, bLeft );
+ }
+}
+
+tools::Long ScPrintFunc::DoNotes( tools::Long nNoteStart, bool bDoPrint, ScPreviewLocationData* pLocationData )
+{
+ if (bDoPrint)
+ pDev->SetMapMode(aTwipMode);
+
+ MakeEditEngine();
+ pEditDefaults->Put( SvxAdjustItem( SvxAdjust::Left, EE_PARA_JUST ) );
+ pEditEngine->SetDefaults( *pEditDefaults );
+
+ vcl::Font aMarkFont;
+ ScAutoFontColorMode eColorMode = bUseStyleColor ? ScAutoFontColorMode::Display : ScAutoFontColorMode::Print;
+ rDoc.GetPool()->GetDefaultItem(ATTR_PATTERN).fillFont(aMarkFont, eColorMode);
+ pDev->SetFont(aMarkFont);
+ tools::Long nMarkLen = pDev->GetTextWidth("GW99999:");
+ // without Space-Char, because it rarely arrives there
+
+ Size aDataSize = aPageRect.GetSize();
+ if ( nMarkLen > aDataSize.Width() / 2 ) // everything much too small?
+ nMarkLen = aDataSize.Width() / 2; // split the page appropriately
+ aDataSize.AdjustWidth( -nMarkLen );
+
+ pEditEngine->SetPaperSize( aDataSize );
+ tools::Long nPosX = aPageRect.Left() + nMarkLen;
+ tools::Long nPosY = aPageRect.Top();
+
+ tools::Long nCount = 0;
+ tools::Long nSize = aNotePosList.size();
+ bool bOk;
+ do
+ {
+ bOk = false;
+ if ( nNoteStart + nCount < nSize)
+ {
+ ScAddress &rPos = aNotePosList[ nNoteStart + nCount ];
+
+ if( const ScPostIt* pNote = rDoc.GetNote( rPos ) )
+ {
+ if(const EditTextObject *pEditText = pNote->GetEditTextObject())
+ pEditEngine->SetTextCurrentDefaults(*pEditText);
+ tools::Long nTextHeight = pEditEngine->GetTextHeight();
+ if ( nPosY + nTextHeight < aPageRect.Bottom() )
+ {
+ if (bDoPrint)
+ {
+ pEditEngine->Draw(*pDev, Point(nPosX, nPosY));
+
+ OUString aMarkStr(rPos.Format(ScRefFlags::VALID, &rDoc, rDoc.GetAddressConvention()) + ":");
+
+ // cell position also via EditEngine, for correct positioning
+ pEditEngine->SetTextCurrentDefaults(aMarkStr);
+ pEditEngine->Draw(*pDev, Point(aPageRect.Left(), nPosY));
+ }
+
+ if ( pLocationData )
+ {
+ tools::Rectangle aTextRect( Point( nPosX, nPosY ), Size( aDataSize.Width(), nTextHeight ) );
+ pLocationData->AddNoteText( aTextRect, rPos );
+ tools::Rectangle aMarkRect( Point( aPageRect.Left(), nPosY ), Size( nMarkLen, nTextHeight ) );
+ pLocationData->AddNoteMark( aMarkRect, rPos );
+ }
+
+ nPosY += nTextHeight;
+ nPosY += 200; // Distance
+ ++nCount;
+ bOk = true;
+ }
+ }
+ }
+ }
+ while (bOk);
+
+ return nCount;
+}
+
+tools::Long ScPrintFunc::PrintNotes( tools::Long nPageNo, tools::Long nNoteStart, bool bDoPrint, ScPreviewLocationData* pLocationData )
+{
+ if ( nNoteStart >= static_cast<tools::Long>(aNotePosList.size()) || !aTableParam.bNotes )
+ return 0;
+
+ if ( bDoPrint && bClearWin )
+ {
+ //! aggregate PrintPage !!!
+
+ Color aBackgroundColor( COL_WHITE );
+ if ( bUseStyleColor )
+ aBackgroundColor = SC_MOD()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor;
+
+ pDev->SetMapMode(aOffsetMode);
+ pDev->SetLineColor();
+ pDev->SetFillColor(aBackgroundColor);
+ pDev->DrawRect(tools::Rectangle(Point(),
+ Size(static_cast<tools::Long>(aPageSize.Width() * nScaleX * 100 / nZoom),
+ static_cast<tools::Long>(aPageSize.Height() * nScaleY * 100 / nZoom))));
+ }
+
+ // adjust aPageRect for left/right page
+
+ tools::Rectangle aTempRect( Point(), aPageSize );
+ if (IsMirror(nPageNo))
+ {
+ aPageRect.SetLeft( ( aTempRect.Left() + nRightMargin ) * 100 / nZoom );
+ aPageRect.SetRight( ( aTempRect.Right() - nLeftMargin ) * 100 / nZoom );
+ }
+ else
+ {
+ aPageRect.SetLeft( ( aTempRect.Left() + nLeftMargin ) * 100 / nZoom );
+ aPageRect.SetRight( ( aTempRect.Right() - nRightMargin ) * 100 / nZoom );
+ }
+
+ if ( pPrinter && bDoPrint )
+ {
+ OSL_FAIL( "StartPage does not exist anymore" );
+ }
+
+ if ( bDoPrint || pLocationData )
+ {
+ // Head and foot lines
+
+ if (aHdr.bEnable)
+ {
+ tools::Long nHeaderY = aPageRect.Top()-aHdr.nHeight;
+ PrintHF( nPageNo, true, nHeaderY, bDoPrint, pLocationData );
+ }
+ if (aFtr.bEnable)
+ {
+ tools::Long nFooterY = aPageRect.Bottom()+aFtr.nDistance;
+ PrintHF( nPageNo, false, nFooterY, bDoPrint, pLocationData );
+ }
+ }
+
+ tools::Long nCount = DoNotes( nNoteStart, bDoPrint, pLocationData );
+
+ if ( pPrinter && bDoPrint )
+ {
+ OSL_FAIL( "EndPage does not exist anymore" );
+ }
+
+ return nCount;
+}
+
+void ScPrintFunc::PrintPage( tools::Long nPageNo, SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2,
+ bool bDoPrint, ScPreviewLocationData* pLocationData )
+{
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nPrintTab );
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ // nPageNo is the page number within all sheets of one "start page" setting
+
+ if ( bClearWin && bDoPrint )
+ {
+ // must exactly fit to painting the frame in preview.cxx !!!
+
+ Color aBackgroundColor( COL_WHITE );
+ if ( bUseStyleColor )
+ aBackgroundColor = SC_MOD()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor;
+
+ pDev->SetMapMode(aOffsetMode);
+ pDev->SetLineColor();
+ pDev->SetFillColor(aBackgroundColor);
+ pDev->DrawRect(tools::Rectangle(Point(),
+ Size(static_cast<tools::Long>(aPageSize.Width() * nScaleX * 100 / nZoom),
+ static_cast<tools::Long>(aPageSize.Height() * nScaleY * 100 / nZoom))));
+ }
+
+ // adjust aPageRect for left/right page
+
+ tools::Rectangle aTempRect( Point(), aPageSize );
+ if (IsMirror(nPageNo))
+ {
+ aPageRect.SetLeft( ( aTempRect.Left() + nRightMargin ) * 100 / nZoom );
+ aPageRect.SetRight( ( aTempRect.Right() - nLeftMargin ) * 100 / nZoom );
+ }
+ else
+ {
+ aPageRect.SetLeft( ( aTempRect.Left() + nLeftMargin ) * 100 / nZoom );
+ aPageRect.SetRight( ( aTempRect.Right() - nRightMargin ) * 100 / nZoom );
+ }
+
+ if ( aAreaParam.bRepeatCol )
+ if ( nX1 > nRepeatStartCol && nX1 <= nRepeatEndCol )
+ nX1 = nRepeatEndCol + 1;
+ bool bDoRepCol = (aAreaParam.bRepeatCol && nX1 > nRepeatEndCol);
+ if ( aAreaParam.bRepeatRow )
+ if ( nY1 > nRepeatStartRow && nY1 <= nRepeatEndRow )
+ nY1 = nRepeatEndRow + 1;
+ bool bDoRepRow = (aAreaParam.bRepeatRow && nY1 > nRepeatEndRow);
+
+ // use new object hide flags in SdrPaintView
+ if(pDrawView)
+ {
+ pDrawView->setHideOle(!aTableParam.bObjects);
+ pDrawView->setHideChart(!aTableParam.bCharts);
+ pDrawView->setHideDraw(!aTableParam.bDrawings);
+ pDrawView->setHideFormControl(!aTableParam.bDrawings);
+ }
+
+ if ( pPrinter && bDoPrint )
+ {
+ OSL_FAIL( "StartPage does not exist anymore" );
+ }
+
+ // head and foot lines (without centering)
+
+ if (aHdr.bEnable)
+ {
+ tools::Long nHeaderY = aPageRect.Top()-aHdr.nHeight;
+ PrintHF( nPageNo, true, nHeaderY, bDoPrint, pLocationData );
+ }
+ if (aFtr.bEnable)
+ {
+ tools::Long nFooterY = aPageRect.Bottom()+aFtr.nDistance;
+ PrintHF( nPageNo, false, nFooterY, bDoPrint, pLocationData );
+ }
+
+ // Position ( margins / centering )
+
+ tools::Long nLeftSpace = aPageRect.Left(); // Document-Twips
+ tools::Long nTopSpace = aPageRect.Top();
+ if ( bCenterHor || bLayoutRTL )
+ {
+ tools::Long nDataWidth = 0;
+ SCCOL i;
+ for (i=nX1; i<=nX2; i++)
+ nDataWidth += rDoc.GetColWidth( i,nPrintTab );
+ if (bDoRepCol)
+ for (i=nRepeatStartCol; i<=nRepeatEndCol; i++)
+ nDataWidth += rDoc.GetColWidth( i,nPrintTab );
+ if (aTableParam.bHeaders)
+ nDataWidth += tools::Long(PRINT_HEADER_WIDTH);
+ if (pBorderItem)
+ nDataWidth += pBorderItem->GetDistance(SvxBoxItemLine::LEFT) +
+ pBorderItem->GetDistance(SvxBoxItemLine::RIGHT); //! Line width?
+ if (pShadowItem && pShadowItem->GetLocation() != SvxShadowLocation::NONE)
+ nDataWidth += pShadowItem->CalcShadowSpace(SvxShadowItemSide::LEFT) +
+ pShadowItem->CalcShadowSpace(SvxShadowItemSide::RIGHT);
+ if ( bCenterHor )
+ {
+ nLeftSpace += ( aPageRect.GetWidth() - nDataWidth ) / 2; // LTR or RTL
+ if (pBorderItem)
+ nLeftSpace -= lcl_LineTotal(pBorderItem->GetLeft());
+ }
+ else if ( bLayoutRTL )
+ nLeftSpace += aPageRect.GetWidth() - nDataWidth; // align to the right edge of the page
+ }
+ if ( bCenterVer )
+ {
+ tools::Long nDataHeight = rDoc.GetRowHeight( nY1, nY2, nPrintTab);
+ if (bDoRepRow)
+ nDataHeight += rDoc.GetRowHeight( nRepeatStartRow,
+ nRepeatEndRow, nPrintTab);
+ if (aTableParam.bHeaders)
+ nDataHeight += tools::Long(PRINT_HEADER_HEIGHT);
+ if (pBorderItem)
+ nDataHeight += pBorderItem->GetDistance(SvxBoxItemLine::TOP) +
+ pBorderItem->GetDistance(SvxBoxItemLine::BOTTOM); //! Line width?
+ if (pShadowItem && pShadowItem->GetLocation() != SvxShadowLocation::NONE)
+ nDataHeight += pShadowItem->CalcShadowSpace(SvxShadowItemSide::TOP) +
+ pShadowItem->CalcShadowSpace(SvxShadowItemSide::BOTTOM);
+ nTopSpace += ( aPageRect.GetHeight() - nDataHeight ) / 2;
+ if (pBorderItem)
+ nTopSpace -= lcl_LineTotal(pBorderItem->GetTop());
+ }
+
+ // calculate sizes of the elements for partitioning
+ // (header, repeat, data)
+
+ tools::Long nHeaderWidth = 0;
+ tools::Long nHeaderHeight = 0;
+ tools::Long nRepeatWidth = 0;
+ tools::Long nRepeatHeight = 0;
+ tools::Long nContentWidth = 0; // scaled - not the same as nDataWidth above
+ tools::Long nContentHeight = 0;
+ if (aTableParam.bHeaders)
+ {
+ nHeaderWidth = static_cast<tools::Long>(PRINT_HEADER_WIDTH * nScaleX);
+ nHeaderHeight = static_cast<tools::Long>(PRINT_HEADER_HEIGHT * nScaleY);
+ }
+ if (bDoRepCol)
+ for (SCCOL i=nRepeatStartCol; i<=nRepeatEndCol; i++)
+ nRepeatWidth += static_cast<tools::Long>(rDoc.GetColWidth(i,nPrintTab) * nScaleX);
+ if (bDoRepRow)
+ nRepeatHeight += rDoc.GetScaledRowHeight( nRepeatStartRow,
+ nRepeatEndRow, nPrintTab, nScaleY);
+ for (SCCOL i=nX1; i<=nX2; i++)
+ nContentWidth += static_cast<tools::Long>(rDoc.GetColWidth(i,nPrintTab) * nScaleX);
+ nContentHeight += rDoc.GetScaledRowHeight( nY1, nY2, nPrintTab,
+ nScaleY);
+
+ // partition the page
+
+ tools::Long nStartX = static_cast<tools::Long>( nLeftSpace * nScaleX );
+ tools::Long nStartY = static_cast<tools::Long>( nTopSpace * nScaleY );
+ tools::Long nInnerStartX = nStartX;
+ tools::Long nInnerStartY = nStartY;
+ if (pBorderItem)
+ {
+ nInnerStartX += static_cast<tools::Long>( ( lcl_LineTotal(pBorderItem->GetLeft()) +
+ pBorderItem->GetDistance(SvxBoxItemLine::LEFT) ) * nScaleX );
+ nInnerStartY += static_cast<tools::Long>( ( lcl_LineTotal(pBorderItem->GetTop()) +
+ pBorderItem->GetDistance(SvxBoxItemLine::TOP) ) * nScaleY );
+ }
+ if (pShadowItem && pShadowItem->GetLocation() != SvxShadowLocation::NONE)
+ {
+ nInnerStartX += static_cast<tools::Long>( pShadowItem->CalcShadowSpace(SvxShadowItemSide::LEFT) * nScaleX );
+ nInnerStartY += static_cast<tools::Long>( pShadowItem->CalcShadowSpace(SvxShadowItemSide::TOP) * nScaleY );
+ }
+
+ if ( bLayoutRTL )
+ {
+ // arrange elements starting from the right edge
+ nInnerStartX += nHeaderWidth + nRepeatWidth + nContentWidth;
+
+ // make rounding easier so the elements are really next to each other in preview
+ Size aOffsetOnePixel = pDev->PixelToLogic( Size(1,1), aOffsetMode );
+ tools::Long nOffsetOneX = aOffsetOnePixel.Width();
+ nInnerStartX += nOffsetOneX / 2;
+ }
+
+ tools::Long nFrameStartX = nInnerStartX;
+ tools::Long nFrameStartY = nInnerStartY;
+
+ tools::Long nRepStartX = nInnerStartX + nHeaderWidth * nLayoutSign; // widths/heights are 0 if not used
+ tools::Long nRepStartY = nInnerStartY + nHeaderHeight;
+ tools::Long nDataX = nRepStartX + nRepeatWidth * nLayoutSign;
+ tools::Long nDataY = nRepStartY + nRepeatHeight;
+ tools::Long nEndX = nDataX + nContentWidth * nLayoutSign;
+ tools::Long nEndY = nDataY + nContentHeight;
+ tools::Long nFrameEndX = nEndX;
+ tools::Long nFrameEndY = nEndY;
+
+ if ( bLayoutRTL )
+ {
+ // each element's start position is its left edge
+ //! subtract one pixel less?
+ nInnerStartX -= nHeaderWidth; // used for header
+ nRepStartX -= nRepeatWidth;
+ nDataX -= nContentWidth;
+
+ // continue right of the main elements again
+ nEndX += nHeaderWidth + nRepeatWidth + nContentWidth;
+ }
+
+ // Page frame / background
+
+ //! adjust nEndX/Y
+
+ tools::Long nBorderEndX = nEndX;
+ tools::Long nBorderEndY = nEndY;
+ if (pBorderItem)
+ {
+ nBorderEndX += static_cast<tools::Long>( ( lcl_LineTotal(pBorderItem->GetRight()) +
+ pBorderItem->GetDistance(SvxBoxItemLine::RIGHT) ) * nScaleX );
+ nBorderEndY += static_cast<tools::Long>( ( lcl_LineTotal(pBorderItem->GetBottom()) +
+ pBorderItem->GetDistance(SvxBoxItemLine::BOTTOM) ) * nScaleY );
+ }
+ if (pShadowItem && pShadowItem->GetLocation() != SvxShadowLocation::NONE)
+ {
+ nBorderEndX += static_cast<tools::Long>( pShadowItem->CalcShadowSpace(SvxShadowItemSide::RIGHT) * nScaleX );
+ nBorderEndY += static_cast<tools::Long>( pShadowItem->CalcShadowSpace(SvxShadowItemSide::BOTTOM) * nScaleY );
+ }
+
+ if ( bDoPrint )
+ {
+ pDev->SetMapMode( aOffsetMode );
+ DrawBorder( nStartX, nStartY, nBorderEndX-nStartX, nBorderEndY-nStartY,
+ pBorderItem, pBackgroundItem, pShadowItem );
+
+ pDev->SetMapMode( aTwipMode );
+ }
+
+ pDev->SetMapMode( aOffsetMode );
+
+ // Output repeating rows/columns
+
+ if (bDoRepCol && bDoRepRow)
+ {
+ if ( bDoPrint )
+ PrintArea( nRepeatStartCol,nRepeatStartRow, nRepeatEndCol,nRepeatEndRow,
+ nRepStartX,nRepStartY, true, true, false, false );
+ if ( pLocationData )
+ LocateArea( nRepeatStartCol,nRepeatStartRow, nRepeatEndCol,nRepeatEndRow,
+ nRepStartX,nRepStartY, true, true, *pLocationData );
+ }
+ if (bDoRepCol)
+ {
+ if ( bDoPrint )
+ PrintArea( nRepeatStartCol,nY1, nRepeatEndCol,nY2, nRepStartX,nDataY,
+ true, !bDoRepRow, false, true );
+ if ( pLocationData )
+ LocateArea( nRepeatStartCol,nY1, nRepeatEndCol,nY2, nRepStartX,nDataY, true, false, *pLocationData );
+ }
+ if (bDoRepRow)
+ {
+ if ( bDoPrint )
+ PrintArea( nX1,nRepeatStartRow, nX2,nRepeatEndRow, nDataX,nRepStartY,
+ !bDoRepCol, true, true, false );
+ if ( pLocationData )
+ LocateArea( nX1,nRepeatStartRow, nX2,nRepeatEndRow, nDataX,nRepStartY, false, true, *pLocationData );
+ }
+
+ // output data
+
+ if ( bDoPrint )
+ PrintArea( nX1,nY1, nX2,nY2, nDataX,nDataY, !bDoRepCol,!bDoRepRow, true, true );
+ if ( pLocationData )
+ LocateArea( nX1,nY1, nX2,nY2, nDataX,nDataY, false,false, *pLocationData );
+
+ // output column/row headers
+ // after data (through probably shadow)
+
+ Color aGridColor( COL_BLACK );
+ if ( bUseStyleColor )
+ aGridColor = SC_MOD()->GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor;
+
+ if (aTableParam.bHeaders)
+ {
+ if ( bDoPrint )
+ {
+ pDev->SetLineColor( aGridColor );
+ pDev->SetFillColor();
+ pDev->SetMapMode(aOffsetMode);
+ }
+
+ ScPatternAttr aPattern( rDoc.GetPool() );
+ vcl::Font aFont;
+ ScAutoFontColorMode eColorMode = bUseStyleColor ? ScAutoFontColorMode::Display : ScAutoFontColorMode::Print;
+ aPattern.fillFont(aFont, eColorMode, pDev);
+ pDev->SetFont(aFont);
+
+ if (bDoRepCol)
+ {
+ if ( bDoPrint )
+ PrintColHdr( nRepeatStartCol,nRepeatEndCol, nRepStartX,nInnerStartY );
+ if ( pLocationData )
+ LocateColHdr( nRepeatStartCol,nRepeatEndCol, nRepStartX,nInnerStartY, true, *pLocationData );
+ }
+ if ( bDoPrint )
+ PrintColHdr( nX1,nX2, nDataX,nInnerStartY );
+ if ( pLocationData )
+ LocateColHdr( nX1,nX2, nDataX,nInnerStartY, false, *pLocationData );
+ if (bDoRepRow)
+ {
+ if ( bDoPrint )
+ PrintRowHdr( nRepeatStartRow,nRepeatEndRow, nInnerStartX,nRepStartY );
+ if ( pLocationData )
+ LocateRowHdr( nRepeatStartRow,nRepeatEndRow, nInnerStartX,nRepStartY, true, *pLocationData );
+ }
+ if ( bDoPrint )
+ PrintRowHdr( nY1,nY2, nInnerStartX,nDataY );
+ if ( pLocationData )
+ LocateRowHdr( nY1,nY2, nInnerStartX,nDataY, false, *pLocationData );
+ }
+
+ // simple frame
+
+ if ( bDoPrint && ( aTableParam.bGrid || aTableParam.bHeaders ) )
+ {
+ Size aOnePixel = pDev->PixelToLogic(Size(1,1));
+ tools::Long nOneX = aOnePixel.Width();
+ tools::Long nOneY = aOnePixel.Height();
+
+ tools::Long nLeftX = nFrameStartX;
+ tools::Long nTopY = nFrameStartY - nOneY;
+ tools::Long nRightX = nFrameEndX;
+ tools::Long nBottomY = nFrameEndY - nOneY;
+ if ( !bLayoutRTL )
+ {
+ nLeftX -= nOneX;
+ nRightX -= nOneX;
+ }
+ pDev->SetMapMode(aOffsetMode);
+ pDev->SetLineColor( aGridColor );
+ pDev->SetFillColor();
+ pDev->DrawRect( tools::Rectangle( nLeftX, nTopY, nRightX, nBottomY ) );
+ // nEndX/Y without frame-adaptation
+ }
+
+ if ( pPrinter && bDoPrint )
+ {
+ OSL_FAIL( "EndPage does not exist anymore" );
+ }
+
+ aLastSourceRange = ScRange( nX1, nY1, nPrintTab, nX2, nY2, nPrintTab );
+ bSourceRangeValid = true;
+}
+
+void ScPrintFunc::SetOffset( const Point& rOfs )
+{
+ aSrcOffset = rOfs;
+}
+
+void ScPrintFunc::SetManualZoom( sal_uInt16 nNewZoom )
+{
+ nManualZoom = nNewZoom;
+}
+
+void ScPrintFunc::SetClearFlag( bool bFlag )
+{
+ bClearWin = bFlag;
+}
+
+void ScPrintFunc::SetUseStyleColor( bool bFlag )
+{
+ bUseStyleColor = bFlag;
+ if (pEditEngine)
+ pEditEngine->EnableAutoColor( bUseStyleColor );
+}
+
+void ScPrintFunc::SetRenderFlag( bool bFlag )
+{
+ bIsRender = bFlag; // set when using XRenderable (PDF)
+}
+
+void ScPrintFunc::SetExclusivelyDrawOleAndDrawObjects()
+{
+ aTableParam.bCellContent = false;
+ aTableParam.bNotes = false;
+ aTableParam.bGrid = false;
+ aTableParam.bHeaders = false;
+ aTableParam.bFormulas = false;
+ aTableParam.bNullVals = false;
+}
+
+// UpdatePages is only called from outside to set the breaks correctly for viewing
+// - always without UserArea
+
+bool ScPrintFunc::UpdatePages()
+{
+ if (!pParamSet)
+ return false;
+
+ // Zoom
+
+ nZoom = 100;
+ if (aTableParam.bScalePageNum || aTableParam.bScaleTo)
+ nZoom = ZOOM_MIN; // correct for breaks
+ else if (aTableParam.bScaleAll)
+ {
+ nZoom = aTableParam.nScaleAll;
+ if ( nZoom <= ZOOM_MIN )
+ nZoom = ZOOM_MIN;
+ }
+
+ OUString aName = rDoc.GetPageStyle( nPrintTab );
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (SCTAB nTab=0; nTab<nTabCount; nTab++)
+ if ( nTab==nPrintTab || rDoc.GetPageStyle(nTab)==aName )
+ {
+ // Repeating rows/columns
+ rDoc.SetRepeatArea( nTab, nRepeatStartCol,nRepeatEndCol, nRepeatStartRow,nRepeatEndRow );
+
+ // set breaks
+ ResetBreaks(nTab);
+ pDocShell->PostPaint(0,0,nTab,rDoc.MaxCol(),rDoc.MaxRow(),nTab, PaintPartFlags::Grid);
+ }
+
+ return true;
+}
+
+tools::Long ScPrintFunc::CountPages() // sets also nPagesX, nPagesY
+{
+ bool bAreaOk = false;
+
+ if (rDoc.HasTable( nPrintTab ))
+ {
+ if (aAreaParam.bPrintArea) // Specify print area?
+ {
+ if ( bPrintCurrentTable )
+ {
+ ScRange& rRange = aAreaParam.aPrintArea;
+
+ // Here, no comparison of the tables any more. Area is always valid for this table
+ // If comparison should be done here, the table of print ranges must be adjusted
+ // when inserting tables etc.!
+
+ nStartCol = rRange.aStart.Col();
+ nStartRow = rRange.aStart.Row();
+ nEndCol = rRange.aEnd .Col();
+ nEndRow = rRange.aEnd .Row();
+ bAreaOk = AdjustPrintArea(false); // limit
+ }
+ else
+ bAreaOk = false;
+ }
+ else // search from document
+ bAreaOk = AdjustPrintArea(true);
+ }
+
+ if (bAreaOk)
+ {
+ tools::Long nPages = 0;
+ size_t nY;
+ if (bMultiArea)
+ {
+ sal_uInt16 nRCount = rDoc.GetPrintRangeCount( nPrintTab );
+ for (sal_uInt16 i=0; i<nRCount; i++)
+ {
+ CalcZoom(i);
+ if ( aTableParam.bSkipEmpty )
+ for (nY=0; nY< m_aRanges.m_nPagesY; nY++)
+ nPages += (*m_aRanges.m_xPageRows)[nY].CountVisible();
+ else
+ nPages += static_cast<tools::Long>(m_aRanges.m_nPagesX) * m_aRanges.m_nPagesY;
+ if ( pPageData )
+ FillPageData();
+ }
+ }
+ else
+ {
+ CalcZoom(RANGENO_NORANGE); // calculate Zoom
+ if ( aTableParam.bSkipEmpty )
+ for (nY=0; nY<m_aRanges.m_nPagesY; nY++)
+ nPages += (*m_aRanges.m_xPageRows)[nY].CountVisible();
+ else
+ nPages += static_cast<tools::Long>(m_aRanges.m_nPagesX) * m_aRanges.m_nPagesY;
+ if ( pPageData )
+ FillPageData();
+ }
+ return nPages;
+ }
+ else
+ {
+ m_aRanges.m_nPagesX = m_aRanges.m_nPagesY = m_aRanges.m_nTotalY = 0;
+ return 0;
+ }
+}
+
+tools::Long ScPrintFunc::CountNotePages()
+{
+ if ( !aTableParam.bNotes || !bPrintCurrentTable )
+ return 0;
+
+ bool bError = false;
+ if (!aAreaParam.bPrintArea)
+ bError = !AdjustPrintArea(true); // completely search in Doc
+
+ sal_uInt16 nRepeats = 1; // how often go through it ?
+ if (bMultiArea)
+ nRepeats = rDoc.GetPrintRangeCount(nPrintTab);
+ if (bError)
+ nRepeats = 0;
+
+ for (sal_uInt16 nStep=0; nStep<nRepeats; nStep++)
+ {
+ bool bDoThis = true;
+ if (bMultiArea) // go through all Areas
+ {
+ const ScRange* pThisRange = rDoc.GetPrintRange( nPrintTab, nStep );
+ if ( pThisRange )
+ {
+ nStartCol = pThisRange->aStart.Col();
+ nStartRow = pThisRange->aStart.Row();
+ nEndCol = pThisRange->aEnd .Col();
+ nEndRow = pThisRange->aEnd .Row();
+ bDoThis = AdjustPrintArea(false);
+ }
+ }
+
+ if (bDoThis)
+ {
+ assert( bPrintAreaValid );
+ for ( SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol )
+ {
+ if (rDoc.HasColNotes(nCol, nPrintTab))
+ {
+ for ( SCROW nRow = nStartRow; nRow <= nEndRow; ++nRow )
+ {
+ if ( rDoc.HasNote(nCol, nRow, nPrintTab) )
+ aNotePosList.emplace_back( nCol, nRow, nPrintTab );
+ }
+ }
+ }
+ }
+ }
+
+ tools::Long nPages = 0;
+ tools::Long nNoteNr = 0;
+ tools::Long nNoteAdd;
+ do
+ {
+ nNoteAdd = PrintNotes( nPages, nNoteNr, false, nullptr );
+ if (nNoteAdd)
+ {
+ nNoteNr += nNoteAdd;
+ ++nPages;
+ }
+ }
+ while (nNoteAdd);
+
+ return nPages;
+}
+
+void ScPrintFunc::InitModes() // set MapModes from nZoom etc.
+{
+ aOffset = Point( aSrcOffset.X()*100/nZoom, aSrcOffset.Y()*100/nZoom );
+
+ tools::Long nEffZoom = nZoom * static_cast<tools::Long>(nManualZoom);
+ constexpr auto HMM_PER_TWIPS = o3tl::convert(1.0, o3tl::Length::twip, o3tl::Length::mm100);
+ nScaleX = nScaleY = HMM_PER_TWIPS; // output in 1/100 mm
+
+ Fraction aZoomFract( nEffZoom,10000 );
+ Fraction aHorFract = aZoomFract;
+
+ if ( !pPrinter && !bIsRender ) // adjust scale for preview
+ {
+ double nFact = pDocShell->GetOutputFactor();
+ aHorFract = Fraction( static_cast<tools::Long>( nEffZoom / nFact ), 10000 );
+ }
+
+ aLogicMode = MapMode( MapUnit::Map100thMM, Point(), aHorFract, aZoomFract );
+
+ Point aLogicOfs( -aOffset.X(), -aOffset.Y() );
+ aOffsetMode = MapMode( MapUnit::Map100thMM, aLogicOfs, aHorFract, aZoomFract );
+
+ Point aTwipsOfs( static_cast<tools::Long>( -aOffset.X() / nScaleX + 0.5 ), static_cast<tools::Long>( -aOffset.Y() / nScaleY + 0.5 ) );
+ aTwipMode = MapMode( MapUnit::MapTwip, aTwipsOfs, aHorFract, aZoomFract );
+}
+
+void ScPrintFunc::ApplyPrintSettings()
+{
+ if ( !pPrinter )
+ return;
+
+ // Configure Printer to Printing
+
+ Size aEnumSize = aPageSize;
+
+ pPrinter->SetOrientation( bLandscape ? Orientation::Landscape : Orientation::Portrait );
+ if ( bLandscape )
+ {
+ // landscape is always interpreted as a rotation by 90 degrees !
+ // this leads to non WYSIWIG but at least it prints!
+ // #i21775#
+ tools::Long nTemp = aEnumSize.Width();
+ aEnumSize.setWidth( aEnumSize.Height() );
+ aEnumSize.setHeight( nTemp );
+ }
+ Paper ePaper = SvxPaperInfo::GetSvxPaper( aEnumSize, MapUnit::MapTwip );
+ sal_uInt16 nPaperBin = pParamSet->Get(ATTR_PAGE_PAPERBIN).GetValue();
+
+ pPrinter->SetPaper( ePaper );
+ if ( PAPER_USER == ePaper )
+ {
+ MapMode aPrinterMode = pPrinter->GetMapMode();
+ MapMode aLocalMode( MapUnit::MapTwip );
+ pPrinter->SetMapMode( aLocalMode );
+ pPrinter->SetPaperSizeUser( aEnumSize );
+ pPrinter->SetMapMode( aPrinterMode );
+ }
+
+ pPrinter->SetPaperBin( nPaperBin );
+}
+
+// rPageRanges = range for all tables
+// nStartPage = rPageRanges starts at nStartPage
+// nDisplayStart = continuous number for displaying the page number
+
+tools::Long ScPrintFunc::DoPrint( const MultiSelection& rPageRanges,
+ tools::Long nStartPage, tools::Long nDisplayStart, bool bDoPrint,
+ ScPreviewLocationData* pLocationData )
+{
+ OSL_ENSURE(pDev,"Device == NULL");
+ if (!pParamSet)
+ return 0;
+
+ if ( pPrinter && bDoPrint )
+ ApplyPrintSettings();
+
+ InitModes();
+ if ( pLocationData )
+ {
+ pLocationData->SetCellMapMode( aOffsetMode );
+ pLocationData->SetPrintTab( nPrintTab );
+ }
+
+ MakeTableString();
+
+ tools::Long nPageNo = 0;
+ tools::Long nPrinted = 0;
+ tools::Long nEndPage = rPageRanges.GetTotalRange().Max();
+
+ sal_uInt16 nRepeats = 1;
+ if (bMultiArea)
+ nRepeats = rDoc.GetPrintRangeCount(nPrintTab);
+ for (sal_uInt16 nStep=0; nStep<nRepeats; nStep++)
+ {
+ if (bMultiArea) // replace area
+ {
+ CalcZoom(nStep); // also sets nStartCol etc. new
+ InitModes();
+ }
+
+ SCCOL nX1;
+ SCROW nY1;
+ SCCOL nX2;
+ SCROW nY2;
+ size_t nCountX;
+ size_t nCountY;
+
+ if (aTableParam.bTopDown) // top-bottom
+ {
+ nX1 = nStartCol;
+ for (nCountX=0; nCountX<m_aRanges.m_nPagesX; nCountX++)
+ {
+ OSL_ENSURE(nCountX < m_aRanges.m_xPageEndX->size(), "vector access error for aPageEndX (!)");
+ nX2 = (*m_aRanges.m_xPageEndX)[nCountX];
+ for (nCountY=0; nCountY<m_aRanges.m_nPagesY; nCountY++)
+ {
+ auto& rPageRow = (*m_aRanges.m_xPageRows)[nCountY];
+ nY1 = rPageRow.GetStartRow();
+ nY2 = rPageRow.GetEndRow();
+ if ( !aTableParam.bSkipEmpty || !rPageRow.IsHidden(nCountX) )
+ {
+ if ( rPageRanges.IsSelected( nPageNo+nStartPage+1 ) )
+ {
+ PrintPage( nPageNo+nDisplayStart, nX1, nY1, nX2, nY2,
+ bDoPrint, pLocationData );
+ ++nPrinted;
+ }
+ ++nPageNo;
+ }
+ }
+ nX1 = nX2 + 1;
+ }
+ }
+ else // left to right
+ {
+ for (nCountY=0; nCountY<m_aRanges.m_nPagesY; nCountY++)
+ {
+ auto& rPageRow = (*m_aRanges.m_xPageRows)[nCountY];
+ nY1 = rPageRow.GetStartRow();
+ nY2 = rPageRow.GetEndRow();
+ nX1 = nStartCol;
+ for (nCountX=0; nCountX<m_aRanges.m_nPagesX; nCountX++)
+ {
+ OSL_ENSURE(nCountX < m_aRanges.m_xPageEndX->size(), "vector access error for aPageEndX");
+ nX2 = (*m_aRanges.m_xPageEndX)[nCountX];
+ if ( !aTableParam.bSkipEmpty || !rPageRow.IsHidden(nCountX) )
+ {
+ if ( rPageRanges.IsSelected( nPageNo+nStartPage+1 ) )
+ {
+ PrintPage( nPageNo+nDisplayStart, nX1, nY1, nX2, nY2,
+ bDoPrint, pLocationData );
+ ++nPrinted;
+ }
+ ++nPageNo;
+ }
+ nX1 = nX2 + 1;
+ }
+ }
+ }
+ }
+
+ aFieldData.aTabName = ScResId( STR_NOTES );
+
+ tools::Long nNoteNr = 0;
+ tools::Long nNoteAdd;
+ do
+ {
+ if ( nPageNo+nStartPage <= nEndPage )
+ {
+ bool bPageSelected = rPageRanges.IsSelected( nPageNo+nStartPage+1 );
+ nNoteAdd = PrintNotes( nPageNo+nStartPage, nNoteNr, bDoPrint && bPageSelected,
+ ( bPageSelected ? pLocationData : nullptr ) );
+ if ( nNoteAdd )
+ {
+ nNoteNr += nNoteAdd;
+ if (bPageSelected)
+ {
+ ++nPrinted;
+ bSourceRangeValid = false; // last page was no cell range
+ }
+ ++nPageNo;
+ }
+ }
+ else
+ nNoteAdd = 0;
+ }
+ while (nNoteAdd);
+
+ if ( bMultiArea )
+ ResetBreaks(nPrintTab); //breaks correct for displaying
+
+ return nPrinted;
+}
+
+void ScPrintFunc::CalcZoom( sal_uInt16 nRangeNo ) // calculate zoom
+{
+ sal_uInt16 nRCount = rDoc.GetPrintRangeCount( nPrintTab );
+ const ScRange* pThisRange = nullptr;
+ if (nRangeNo != RANGENO_NORANGE && nRangeNo < nRCount)
+ pThisRange = rDoc.GetPrintRange( nPrintTab, nRangeNo );
+ if ( pThisRange )
+ {
+ nStartCol = pThisRange->aStart.Col();
+ nStartRow = pThisRange->aStart.Row();
+ nEndCol = pThisRange->aEnd .Col();
+ nEndRow = pThisRange->aEnd .Row();
+ }
+
+ if (!AdjustPrintArea(false)) // empty
+ {
+ nZoom = 100;
+ m_aRanges.m_nPagesX = m_aRanges.m_nPagesY = m_aRanges.m_nTotalY = 0;
+ return;
+ }
+
+ rDoc.SetRepeatArea( nPrintTab, nRepeatStartCol,nRepeatEndCol, nRepeatStartRow,nRepeatEndRow );
+
+ if (aTableParam.bScalePageNum)
+ {
+ nZoom = 100;
+ sal_uInt16 nPagesToFit = aTableParam.nScalePageNum;
+
+ // If manual breaks are forced, calculate minimum # pages required
+ if (aTableParam.bForceBreaks)
+ {
+ sal_uInt16 nMinPages = 0;
+ std::set<SCROW> aRowBreaks;
+ std::set<SCCOL> aColBreaks;
+ rDoc.GetAllRowBreaks(aRowBreaks, nPrintTab, false, true);
+ rDoc.GetAllColBreaks(aColBreaks, nPrintTab, false, true);
+ nMinPages = (aRowBreaks.size() + 1) * (aColBreaks.size() + 1);
+
+ // #i54993# use min forced by breaks if it's > # pages in
+ // scale parameter to avoid bottoming out at <= ZOOM_MIN
+ nPagesToFit = std::max(nMinPages, nPagesToFit);
+ }
+
+ sal_uInt16 nLastFitZoom = 0, nLastNonFitZoom = 0;
+ while (true)
+ {
+ if (nZoom <= ZOOM_MIN)
+ break;
+
+ CalcPages();
+ bool bFitsPage = (m_aRanges.m_nPagesX * m_aRanges.m_nPagesY <= nPagesToFit);
+
+ if (bFitsPage)
+ {
+ if (nZoom == 100)
+ // If it fits at 100%, it's good enough for me.
+ break;
+
+ nLastFitZoom = nZoom;
+ nZoom = (nLastNonFitZoom + nZoom) / 2;
+
+ if (nLastFitZoom == nZoom)
+ // It converged. Use this zoom level.
+ break;
+ }
+ else
+ {
+ if (nZoom - nLastFitZoom <= 1)
+ {
+ nZoom = nLastFitZoom;
+ CalcPages();
+ break;
+ }
+
+ nLastNonFitZoom = nZoom;
+ nZoom = (nLastFitZoom + nZoom) / 2;
+ }
+ }
+ }
+ else if (aTableParam.bScaleTo)
+ {
+ nZoom = 100;
+ sal_uInt16 nW = aTableParam.nScaleWidth;
+ sal_uInt16 nH = aTableParam.nScaleHeight;
+
+ // If manual breaks are forced, calculate minimum # pages required
+ if (aTableParam.bForceBreaks)
+ {
+ sal_uInt16 nMinPagesW = 0, nMinPagesH = 0;
+ std::set<SCROW> aRowBreaks;
+ std::set<SCCOL> aColBreaks;
+ rDoc.GetAllRowBreaks(aRowBreaks, nPrintTab, false, true);
+ rDoc.GetAllColBreaks(aColBreaks, nPrintTab, false, true);
+ nMinPagesW = aColBreaks.size() + 1;
+ nMinPagesH = aRowBreaks.size() + 1;
+
+ // #i54993# use min forced by breaks if it's > # pages in
+ // scale parameters to avoid bottoming out at <= ZOOM_MIN
+ nW = std::max(nMinPagesW, nW);
+ nH = std::max(nMinPagesH, nH);
+ }
+
+ sal_uInt16 nLastFitZoom = 0, nLastNonFitZoom = 0;
+ while (true)
+ {
+ if (nZoom <= ZOOM_MIN)
+ break;
+
+ CalcPages();
+ bool bFitsPage = ((!nW || (m_aRanges.m_nPagesX <= nW)) && (!nH || (m_aRanges.m_nPagesY <= nH)));
+
+ if (bFitsPage)
+ {
+ if (nZoom == 100)
+ // If it fits at 100%, it's good enough for me.
+ break;
+
+ nLastFitZoom = nZoom;
+ nZoom = (nLastNonFitZoom + nZoom) / 2;
+
+ if (nLastFitZoom == nZoom)
+ // It converged. Use this zoom level.
+ break;
+ }
+ else
+ {
+ if (nZoom - nLastFitZoom <= 1)
+ {
+ nZoom = nLastFitZoom;
+ CalcPages();
+ break;
+ }
+
+ nLastNonFitZoom = nZoom;
+ nZoom = (nLastFitZoom + nZoom) / 2;
+ }
+ }
+ // tdf#103516 remove the almost blank page(s) for better
+ // interoperability by using slightly smaller zoom
+ if (nW > 0 && nH == 0 && m_aRanges.m_nPagesY > 1)
+ {
+ sal_uInt32 nLastPagesY = m_aRanges.m_nPagesY;
+ nLastFitZoom = nZoom;
+ nZoom *= 0.98;
+ if (nZoom < nLastFitZoom)
+ {
+ CalcPages();
+ // same page count with smaller zoom: use the original zoom
+ if (m_aRanges.m_nPagesY == nLastPagesY)
+ {
+ nZoom = nLastFitZoom;
+ CalcPages();
+ }
+ }
+ }
+ }
+ else if (aTableParam.bScaleAll)
+ {
+ nZoom = aTableParam.nScaleAll;
+ if ( nZoom <= ZOOM_MIN )
+ nZoom = ZOOM_MIN;
+ CalcPages();
+ }
+ else
+ {
+ OSL_ENSURE( aTableParam.bScaleNone, "no scale flag is set" );
+ nZoom = 100;
+ CalcPages();
+ }
+}
+
+Size ScPrintFunc::GetDocPageSize()
+{
+ // Adjust height of head/foot line
+
+ InitModes(); // initialize aTwipMode from nZoom
+ pDev->SetMapMode( aTwipMode ); // head/foot line in Twips
+ UpdateHFHeight( aHdr );
+ UpdateHFHeight( aFtr );
+
+ // Page size in Document-Twips
+ // Calculating Left / Right also in PrintPage
+
+ aPageRect = tools::Rectangle( Point(), aPageSize );
+ aPageRect.SetLeft( ( aPageRect.Left() + nLeftMargin ) * 100 / nZoom );
+ aPageRect.SetRight( ( aPageRect.Right() - nRightMargin ) * 100 / nZoom );
+ aPageRect.SetTop( ( aPageRect.Top() + nTopMargin ) * 100 / nZoom + aHdr.nHeight );
+ aPageRect.SetBottom( ( aPageRect.Bottom() - nBottomMargin ) * 100 / nZoom - aFtr.nHeight );
+
+ Size aDocPageSize = aPageRect.GetSize();
+ if (aTableParam.bHeaders)
+ {
+ aDocPageSize.AdjustWidth( -(tools::Long(PRINT_HEADER_WIDTH)) );
+ aDocPageSize.AdjustHeight( -(tools::Long(PRINT_HEADER_HEIGHT)) );
+ }
+ if (pBorderItem)
+ {
+ aDocPageSize.AdjustWidth( -(lcl_LineTotal(pBorderItem->GetLeft()) +
+ lcl_LineTotal(pBorderItem->GetRight()) +
+ pBorderItem->GetDistance(SvxBoxItemLine::LEFT) +
+ pBorderItem->GetDistance(SvxBoxItemLine::RIGHT)) );
+ aDocPageSize.AdjustHeight( -(lcl_LineTotal(pBorderItem->GetTop()) +
+ lcl_LineTotal(pBorderItem->GetBottom()) +
+ pBorderItem->GetDistance(SvxBoxItemLine::TOP) +
+ pBorderItem->GetDistance(SvxBoxItemLine::BOTTOM)) );
+ }
+ if (pShadowItem && pShadowItem->GetLocation() != SvxShadowLocation::NONE)
+ {
+ aDocPageSize.AdjustWidth( -(pShadowItem->CalcShadowSpace(SvxShadowItemSide::LEFT) +
+ pShadowItem->CalcShadowSpace(SvxShadowItemSide::RIGHT)) );
+ aDocPageSize.AdjustHeight( -(pShadowItem->CalcShadowSpace(SvxShadowItemSide::TOP) +
+ pShadowItem->CalcShadowSpace(SvxShadowItemSide::BOTTOM)) );
+ }
+ return aDocPageSize;
+}
+
+void ScPrintFunc::ResetBreaks( SCTAB nTab ) // Set Breaks correctly for view
+{
+ rDoc.SetPageSize( nTab, GetDocPageSize() );
+ rDoc.UpdatePageBreaks( nTab );
+}
+
+static void lcl_SetHidden( const ScDocument& rDoc, SCTAB nPrintTab, ScPageRowEntry& rPageRowEntry,
+ SCCOL nStartCol, const std::vector< SCCOL >& rPageEndX )
+{
+ size_t nPagesX = rPageRowEntry.GetPagesX();
+ SCROW nStartRow = rPageRowEntry.GetStartRow();
+ SCROW nEndRow = rPageRowEntry.GetEndRow();
+
+ bool bLeftIsEmpty = false;
+ ScRange aTempRange;
+ tools::Rectangle aTempRect = rDoc.GetMMRect( 0,0, 0,0, 0 );
+
+ for (size_t i=0; i<nPagesX; i++)
+ {
+ OSL_ENSURE(i < rPageEndX.size(), "vector access error for aPageEndX");
+ SCCOL nEndCol = rPageEndX[i];
+ if ( rDoc.IsPrintEmpty( nStartCol, nStartRow, nEndCol, nEndRow, nPrintTab,
+ bLeftIsEmpty, &aTempRange, &aTempRect ) )
+ {
+ rPageRowEntry.SetHidden(i);
+ bLeftIsEmpty = true;
+ }
+ else
+ bLeftIsEmpty = false;
+
+ nStartCol = nEndCol+1;
+ }
+}
+
+void ScPrintFunc::CalcPages() // calculates aPageRect and pages from nZoom
+{
+ assert( bPrintAreaValid );
+
+ sc::PrintPageRangesInput aInput(aTableParam.bSkipEmpty, aAreaParam.bPrintArea,
+ ScRange(nStartCol, nStartRow, nPrintTab, nEndCol, nEndRow, nPrintTab),
+ GetDocPageSize());
+ m_aRanges.calculate(rDoc, aInput);
+}
+
+namespace sc
+{
+
+PrintPageRanges::PrintPageRanges()
+ : m_nPagesX(0)
+ , m_nPagesY(0)
+ , m_nTotalY(0)
+{}
+
+void PrintPageRanges::calculate(ScDocument& rDoc, PrintPageRangesInput const& rInput)
+{
+ // Already calculated?
+ if (m_aInput == rInput)
+ return;
+
+ m_aInput = rInput;
+
+ rDoc.SetPageSize(m_aInput.getPrintTab(), m_aInput.m_aDocSize);
+
+ // Clear the map to prevent any outdated values to "survive" when
+ // we have to recalculate the new values anyway
+ m_xPageRows->clear();
+
+ // #i123672# use dynamic mem to react on size changes
+ if (m_xPageEndX->size() < static_cast<size_t>(rDoc.MaxCol()) + 1)
+ {
+ m_xPageEndX->resize(rDoc.MaxCol()+1, SCCOL());
+ }
+
+ if (m_aInput.m_bPrintArea)
+ {
+ ScRange aRange(m_aInput.getStartColumn(), m_aInput.getStartRow(), m_aInput.getPrintTab(), m_aInput.getEndColumn(), m_aInput.getEndRow(), m_aInput.getPrintTab());
+ rDoc.UpdatePageBreaks(m_aInput.getPrintTab(), &aRange);
+ }
+ else
+ {
+ rDoc.UpdatePageBreaks(m_aInput.getPrintTab()); // else, end is marked
+ }
+
+ const size_t nRealCnt = m_aInput.getEndRow() - m_aInput.getStartRow() + 1;
+
+ // #i123672# use dynamic mem to react on size changes
+ if (m_xPageEndY->size() < nRealCnt+1)
+ {
+ m_xPageEndY->resize(nRealCnt + 1, SCROW());
+ }
+
+ // Page alignment/splitting after breaks in Col/RowFlags
+ // Of several breaks in a hidden area, only one counts.
+
+ m_nPagesX = 0;
+ m_nPagesY = 0;
+ m_nTotalY = 0;
+
+ bool bVisCol = false;
+ for (SCCOL i = m_aInput.getStartColumn(); i <= m_aInput.getEndColumn(); i++)
+ {
+ bool bHidden = rDoc.ColHidden(i, m_aInput.getPrintTab());
+ bool bPageBreak(rDoc.HasColBreak(i, m_aInput.getPrintTab()) & ScBreakType::Page);
+ if (i > m_aInput.getStartColumn() && bVisCol && bPageBreak)
+ {
+ OSL_ENSURE(m_nPagesX < m_xPageEndX->size(), "vector access error for aPageEndX");
+ (*m_xPageEndX)[m_nPagesX] = i-1;
+ ++m_nPagesX;
+ bVisCol = false;
+ }
+ if (!bHidden)
+ bVisCol = true;
+ }
+ if (bVisCol) // also at the end, no empty pages
+ {
+ OSL_ENSURE(m_nPagesX < m_xPageEndX->size(), "vector access error for aPageEndX");
+ (*m_xPageEndX)[m_nPagesX] = m_aInput.getEndColumn();
+ ++m_nPagesX;
+ }
+
+ bool bVisRow = false;
+ SCROW nPageStartRow = m_aInput.getStartRow();
+ SCROW nLastVisibleRow = -1;
+
+ std::unique_ptr<ScRowBreakIterator> pRowBreakIter(rDoc.GetRowBreakIterator(m_aInput.getPrintTab()));
+ SCROW nNextPageBreak = pRowBreakIter->first();
+ while (nNextPageBreak != ScRowBreakIterator::NOT_FOUND && nNextPageBreak < m_aInput.getStartRow())
+ // Skip until the page break position is at the start row or greater.
+ nNextPageBreak = pRowBreakIter->next();
+
+ for (SCROW nRow = m_aInput.getStartRow(); nRow <= m_aInput.getEndRow(); ++nRow)
+ {
+ bool bPageBreak = (nNextPageBreak == nRow);
+ if (bPageBreak)
+ nNextPageBreak = pRowBreakIter->next();
+
+ if (nRow > m_aInput.getStartRow() && bVisRow && bPageBreak)
+ {
+ OSL_ENSURE(m_nTotalY < m_xPageEndY->size(), "vector access error for rPageEndY");
+ (*m_xPageEndY)[m_nTotalY] = nRow - 1;
+ ++m_nTotalY;
+
+ if (!m_aInput.m_bSkipEmpty || !rDoc.IsPrintEmpty(m_aInput.getStartColumn(), nPageStartRow, m_aInput.getEndColumn(), nRow-1, m_aInput.getPrintTab()))
+ {
+ auto& rPageRow = (*m_xPageRows)[m_nPagesY];
+ rPageRow.SetStartRow(nPageStartRow);
+ rPageRow.SetEndRow(nRow - 1);
+ rPageRow.SetPagesX(m_nPagesX);
+ if (m_aInput.m_bSkipEmpty)
+ lcl_SetHidden(rDoc, m_aInput.getPrintTab(), rPageRow, m_aInput.getStartColumn(), *m_xPageEndX);
+ ++m_nPagesY;
+ }
+
+ nPageStartRow = nRow;
+ bVisRow = false;
+ }
+
+ if (nRow <= nLastVisibleRow)
+ {
+ // This row is still visible. Don't bother calling RowHidden() to
+ // find out, for speed optimization.
+ bVisRow = true;
+ continue;
+ }
+
+ SCROW nLastRow = -1;
+ if (!rDoc.RowHidden(nRow, m_aInput.getPrintTab(), nullptr, &nLastRow))
+ {
+ bVisRow = true;
+ nLastVisibleRow = nLastRow;
+ }
+ else
+ {
+ // Skip all hidden rows until next pagebreak.
+ nRow = ((nNextPageBreak == ScRowBreakIterator::NOT_FOUND) ? nLastRow :
+ std::min(nLastRow, nNextPageBreak - 1));
+ }
+ }
+
+ if (!bVisRow)
+ return;
+
+ OSL_ENSURE(m_nTotalY < m_xPageEndY->size(), "vector access error for maPageEndY");
+ (*m_xPageEndY)[m_nTotalY] = m_aInput.getEndRow();
+ ++m_nTotalY;
+
+ if (!m_aInput.m_bSkipEmpty || !rDoc.IsPrintEmpty(m_aInput.getStartColumn(), nPageStartRow, m_aInput.getEndColumn(), m_aInput.getEndRow(), m_aInput.getPrintTab()))
+ {
+ auto& rPageRow = (*m_xPageRows)[m_nPagesY];
+ rPageRow.SetStartRow(nPageStartRow);
+ rPageRow.SetEndRow(m_aInput.getEndRow());
+ rPageRow.SetPagesX(m_nPagesX);
+ if (m_aInput.m_bSkipEmpty)
+ lcl_SetHidden(rDoc, m_aInput.getPrintTab(), rPageRow, m_aInput.getStartColumn(), *m_xPageEndX);
+ ++m_nPagesY;
+ }
+}
+
+} // end namespace sc
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/reffact.cxx b/sc/source/ui/view/reffact.cxx
new file mode 100644
index 0000000000..3834e3b8c3
--- /dev/null
+++ b/sc/source/ui/view/reffact.cxx
@@ -0,0 +1,299 @@
+/* -*- 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 .
+ */
+
+#include <sfx2/basedlgs.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/viewfrm.hxx>
+
+#include <reffact.hxx>
+#include <tabvwsh.hxx>
+#include <sc.hrc>
+#include <acredlin.hxx>
+#include <simpref.hxx>
+#include <scmod.hxx>
+#include <scres.hrc>
+#include <validate.hxx>
+
+SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(ScNameDlgWrapper, FID_DEFINE_NAME)
+SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(ScNameDefDlgWrapper, FID_ADD_NAME)
+SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(ScSolverDlgWrapper, SID_OPENDLG_SOLVE)
+SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(ScOptSolverDlgWrapper, SID_OPENDLG_OPTSOLVER)
+SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(ScXMLSourceDlgWrapper, SID_MANAGE_XML_SOURCE)
+SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(ScPivotLayoutWrapper, SID_OPENDLG_PIVOTTABLE)
+SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(ScTabOpDlgWrapper, SID_OPENDLG_TABOP)
+SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(ScFilterDlgWrapper, SID_FILTER)
+SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(ScSpecialFilterDlgWrapper, SID_SPECIAL_FILTER)
+SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(ScDbNameDlgWrapper, SID_DEFINE_DBNAME)
+SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(ScConsolidateDlgWrapper, SID_OPENDLG_CONSOLIDATE)
+SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(ScPrintAreasDlgWrapper, SID_OPENDLG_EDIT_PRINTAREA)
+SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(ScColRowNameRangesDlgWrapper, SID_DEFINE_COLROWNAMERANGES)
+SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(ScFormulaDlgWrapper, SID_OPENDLG_FUNCTION)
+SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(ScAcceptChgDlgWrapper, FID_CHG_ACCEPT)
+SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(ScHighlightChgDlgWrapper, FID_CHG_SHOW)
+SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(ScSimpleRefDlgWrapper, WID_SIMPLE_REF)
+SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(ScCondFormatDlgWrapper, WID_CONDFRMT_REF)
+
+SFX_IMPL_CHILDWINDOW_WITHID(ScValidityRefChildWin, SID_VALIDITY_REFERENCE)
+
+SfxChildWinInfo ScValidityRefChildWin::GetInfo() const
+{
+ SfxChildWinInfo aInfo = SfxChildWindow::GetInfo();
+ return aInfo;
+}
+
+namespace
+{
+ ScTabViewShell* lcl_GetTabViewShell( const SfxBindings* pBindings );
+}
+
+#define IMPL_CONTROLLER_CHILD_CTOR(Class,sid) \
+ Class::Class( vcl::Window* pParentP, \
+ sal_uInt16 nId, \
+ SfxBindings* p, \
+ const SfxChildWinInfo* pInfo ) \
+ : SfxChildWindow(pParentP, nId) \
+ { \
+ /************************************************************************************/\
+ /* When a new document is creating, the SfxViewFrame may be ready, */\
+ /* But the ScTabViewShell may have not been activated yet. In this */\
+ /* situation, SfxViewShell::Current() does not get the correct shell, */\
+ /* and we should lcl_GetTabViewShell( p ) instead of SfxViewShell::Current() */\
+ /************************************************************************************/\
+ ScTabViewShell* pViewShell = lcl_GetTabViewShell( p ); \
+ if (!pViewShell) \
+ pViewShell = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() ); \
+ OSL_ENSURE( pViewShell, "missing view shell :-(" ); \
+ SetController( pViewShell ? \
+ pViewShell->CreateRefDialogController( p, this, pInfo, pParentP->GetFrameWeld(), sid ) : nullptr ); \
+ if (pViewShell && !GetController()) \
+ pViewShell->GetViewFrame().SetChildWindow( nId, false ); \
+ }
+
+
+IMPL_CONTROLLER_CHILD_CTOR( ScNameDlgWrapper, FID_DEFINE_NAME )
+
+IMPL_CONTROLLER_CHILD_CTOR( ScNameDefDlgWrapper, FID_ADD_NAME )
+
+IMPL_CONTROLLER_CHILD_CTOR( ScSolverDlgWrapper, SID_OPENDLG_SOLVE )
+
+IMPL_CONTROLLER_CHILD_CTOR( ScOptSolverDlgWrapper, SID_OPENDLG_OPTSOLVER )
+
+IMPL_CONTROLLER_CHILD_CTOR( ScXMLSourceDlgWrapper, SID_MANAGE_XML_SOURCE)
+
+IMPL_CONTROLLER_CHILD_CTOR( ScPivotLayoutWrapper, SID_OPENDLG_PIVOTTABLE )
+
+IMPL_CONTROLLER_CHILD_CTOR( ScTabOpDlgWrapper, SID_OPENDLG_TABOP )
+
+IMPL_CONTROLLER_CHILD_CTOR( ScFilterDlgWrapper, SID_FILTER )
+
+IMPL_CONTROLLER_CHILD_CTOR( ScSpecialFilterDlgWrapper, SID_SPECIAL_FILTER )
+
+IMPL_CONTROLLER_CHILD_CTOR( ScDbNameDlgWrapper, SID_DEFINE_DBNAME )
+
+IMPL_CONTROLLER_CHILD_CTOR( ScColRowNameRangesDlgWrapper, SID_DEFINE_COLROWNAMERANGES )
+
+IMPL_CONTROLLER_CHILD_CTOR( ScConsolidateDlgWrapper, SID_OPENDLG_CONSOLIDATE )
+
+IMPL_CONTROLLER_CHILD_CTOR( ScPrintAreasDlgWrapper, SID_OPENDLG_EDIT_PRINTAREA )
+
+IMPL_CONTROLLER_CHILD_CTOR( ScFormulaDlgWrapper, SID_OPENDLG_FUNCTION )
+
+
+// ScSimpleRefDlgWrapper
+
+static bool bScSimpleRefFlag;
+static tools::Long nScSimpleRefHeight;
+static tools::Long nScSimpleRefWidth;
+static tools::Long nScSimpleRefX;
+static tools::Long nScSimpleRefY;
+static bool bAutoReOpen = true;
+
+ScSimpleRefDlgWrapper::ScSimpleRefDlgWrapper( vcl::Window* pParentP,
+ sal_uInt16 nId,
+ SfxBindings* p,
+ SfxChildWinInfo* pInfo )
+ : SfxChildWindow(pParentP, nId)
+{
+
+ ScTabViewShell* pViewShell = nullptr;
+ SfxDispatcher* pDisp = p->GetDispatcher();
+ if ( pDisp )
+ {
+ SfxViewFrame* pViewFrm = pDisp->GetFrame();
+ if ( pViewFrm )
+ pViewShell = dynamic_cast<ScTabViewShell*>( pViewFrm->GetViewShell() );
+ }
+
+ OSL_ENSURE( pViewShell, "missing view shell :-(" );
+
+ if(pInfo!=nullptr && bScSimpleRefFlag)
+ {
+ pInfo->aPos.setX(nScSimpleRefX );
+ pInfo->aPos.setY(nScSimpleRefY );
+ pInfo->aSize.setHeight(nScSimpleRefHeight );
+ pInfo->aSize.setWidth(nScSimpleRefWidth );
+ }
+ SetController(nullptr);
+
+ if (bAutoReOpen && pViewShell)
+ SetController(pViewShell->CreateRefDialogController(p, this, pInfo, pParentP->GetFrameWeld(), WID_SIMPLE_REF));
+
+ if (!GetController())
+ {
+ SC_MOD()->SetRefDialog( nId, false );
+ }
+}
+
+void ScSimpleRefDlgWrapper::SetAutoReOpen(bool bFlag)
+{
+ bAutoReOpen=bFlag;
+}
+
+void ScSimpleRefDlgWrapper::SetRefString(const OUString& rStr)
+{
+ auto xDlgController = GetController();
+ if (xDlgController)
+ {
+ static_cast<ScSimpleRefDlg*>(xDlgController.get())->SetRefString(rStr);
+ }
+}
+
+void ScSimpleRefDlgWrapper::SetCloseHdl( const Link<const OUString*,void>& rLink )
+{
+ auto xDlgController = GetController();
+ if (xDlgController)
+ {
+ static_cast<ScSimpleRefDlg*>(xDlgController.get())->SetCloseHdl(rLink);
+ }
+}
+
+void ScSimpleRefDlgWrapper::SetUnoLinks( const Link<const OUString&,void>& rDone,
+ const Link<const OUString&,void>& rAbort, const Link<const OUString&,void>& rChange )
+{
+ auto xDlgController = GetController();
+ if (xDlgController)
+ {
+ static_cast<ScSimpleRefDlg*>(xDlgController.get())->SetUnoLinks( rDone, rAbort, rChange );
+ }
+}
+
+void ScSimpleRefDlgWrapper::SetFlags( bool bCloseOnButtonUp, bool bSingleCell, bool bMultiSelection )
+{
+ auto xDlgController = GetController();
+ if (xDlgController)
+ {
+ static_cast<ScSimpleRefDlg*>(xDlgController.get())->SetFlags( bCloseOnButtonUp, bSingleCell, bMultiSelection );
+ }
+}
+
+void ScSimpleRefDlgWrapper::StartRefInput()
+{
+ auto xDlgController = GetController();
+ if (xDlgController)
+ {
+ static_cast<ScSimpleRefDlg*>(xDlgController.get())->StartRefInput();
+ }
+}
+
+// ScAcceptChgDlgWrapper //FIXME: should be moved into ViewShell
+
+ScAcceptChgDlgWrapper::ScAcceptChgDlgWrapper(vcl::Window* pParentP,
+ sal_uInt16 nId,
+ SfxBindings* pBindings,
+ SfxChildWinInfo* pInfo ) :
+ SfxChildWindow( pParentP, nId )
+{
+ ScTabViewShell* pViewShell =
+ dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() );
+ OSL_ENSURE( pViewShell, "missing view shell :-(" );
+ if (pViewShell)
+ {
+ auto xDlg = std::make_shared<ScAcceptChgDlg>(pBindings, this, pParentP->GetFrameWeld(), &pViewShell->GetViewData());
+ SetController(xDlg);
+ xDlg->Initialize( pInfo );
+ }
+ else
+ SetController( nullptr );
+ if (pViewShell && !GetController())
+ pViewShell->GetViewFrame().SetChildWindow( nId, false );
+}
+
+void ScAcceptChgDlgWrapper::ReInitDlg()
+{
+ ScTabViewShell* pViewShell =
+ dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() );
+ OSL_ENSURE( pViewShell, "missing view shell :-(" );
+
+ if (GetController() && pViewShell)
+ {
+ static_cast<ScAcceptChgDlg*>(GetController().get())->ReInit(&pViewShell->GetViewData());
+ }
+}
+
+// ScHighlightChgDlgWrapper
+
+IMPL_CONTROLLER_CHILD_CTOR(ScHighlightChgDlgWrapper, FID_CHG_SHOW)
+
+namespace
+{
+ ScTabViewShell * lcl_GetTabViewShell( const SfxBindings *pBindings )
+ {
+ if( pBindings )
+ if( SfxDispatcher* pDisp = pBindings ->GetDispatcher() )
+ if( SfxViewFrame *pFrm = pDisp->GetFrame() )
+ if( SfxViewShell* pViewSh = pFrm->GetViewShell() )
+ return dynamic_cast<ScTabViewShell*>( pViewSh );
+
+ return nullptr;
+ }
+}
+
+ScValidityRefChildWin::ScValidityRefChildWin(vcl::Window* pParentP,
+ sal_uInt16 nId,
+ const SfxBindings* p,
+ SAL_UNUSED_PARAMETER SfxChildWinInfo* /*pInfo*/ )
+ : SfxChildWindow(pParentP, nId)
+ , m_bVisibleLock(false)
+ , m_bFreeWindowLock(false)
+{
+ SetWantsFocus( false );
+ std::shared_ptr<SfxDialogController> xDlg(ScValidationDlg::Find1AliveObject(pParentP->GetFrameWeld()));
+ SetController(xDlg);
+ ScTabViewShell* pViewShell;
+ if (xDlg)
+ pViewShell = static_cast<ScValidationDlg*>(xDlg.get())->GetTabViewShell();
+ else
+ pViewShell = lcl_GetTabViewShell( p );
+ if (!pViewShell)
+ pViewShell = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() );
+ OSL_ENSURE( pViewShell, "missing view shell :-(" );
+ if (pViewShell && !xDlg)
+ pViewShell->GetViewFrame().SetChildWindow( nId, false );
+}
+
+ScValidityRefChildWin::~ScValidityRefChildWin()
+{
+ if (m_bFreeWindowLock)
+ SetController(nullptr);
+}
+
+IMPL_CONTROLLER_CHILD_CTOR( ScCondFormatDlgWrapper, WID_CONDFRMT_REF )
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/scextopt.cxx b/sc/source/ui/view/scextopt.cxx
new file mode 100644
index 0000000000..206b7cdc8d
--- /dev/null
+++ b/sc/source/ui/view/scextopt.cxx
@@ -0,0 +1,218 @@
+/* -*- 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 .
+ */
+
+#include <scextopt.hxx>
+
+#include <osl/diagnose.h>
+
+#include <map>
+#include <memory>
+#include <vector>
+
+ScExtDocSettings::ScExtDocSettings() :
+ mfTabBarWidth( -1.0 ),
+ mnLinkCnt( 0 ),
+ mnDisplTab( -1 )
+{
+}
+
+ScExtTabSettings::ScExtTabSettings() :
+ maUsedArea( ScAddress::INITIALIZE_INVALID ),
+ maCursor( ScAddress::INITIALIZE_INVALID ),
+ maFirstVis( ScAddress::INITIALIZE_INVALID ),
+ maSecondVis( ScAddress::INITIALIZE_INVALID ),
+ maFreezePos( 0, 0, 0 ),
+ maSplitPos( 0, 0 ),
+ meActivePane( SCEXT_PANE_TOPLEFT ),
+ maGridColor( COL_AUTO ),
+ mnNormalZoom( 0 ),
+ mnPageZoom( 0 ),
+ mbSelected( false ),
+ mbFrozenPanes( false ),
+ mbPageMode( false ),
+ mbShowGrid( true )
+{
+}
+
+namespace {
+
+/** A container for ScExtTabSettings objects.
+ @descr Internally, a std::map with shared pointers to ScExtTabSettings is
+ used. The copy constructor and assignment operator make deep copies of the
+ objects. */
+class ScExtTabSettingsCont
+{
+public:
+ explicit ScExtTabSettingsCont();
+ ScExtTabSettingsCont( const ScExtTabSettingsCont& rSrc );
+ ScExtTabSettingsCont& operator=( const ScExtTabSettingsCont& rSrc );
+
+ const ScExtTabSettings* GetTabSettings( SCTAB nTab ) const;
+ ScExtTabSettings& GetOrCreateTabSettings( SCTAB nTab );
+
+ SCTAB GetLastTab() const;
+
+private:
+ typedef std::shared_ptr< ScExtTabSettings > ScExtTabSettingsRef;
+ typedef ::std::map< SCTAB, ScExtTabSettingsRef > ScExtTabSettingsMap;
+
+ /** Makes a deep copy of all objects in the passed map. */
+ void CopyFromMap( const ScExtTabSettingsMap& rMap );
+
+ ScExtTabSettingsMap maMap;
+};
+
+}
+
+ScExtTabSettingsCont::ScExtTabSettingsCont()
+{
+}
+
+ScExtTabSettingsCont::ScExtTabSettingsCont( const ScExtTabSettingsCont& rSrc )
+{
+ CopyFromMap( rSrc.maMap );
+}
+
+ScExtTabSettingsCont& ScExtTabSettingsCont::operator=( const ScExtTabSettingsCont& rSrc )
+{
+ CopyFromMap( rSrc.maMap );
+ return *this;
+}
+
+const ScExtTabSettings* ScExtTabSettingsCont::GetTabSettings( SCTAB nTab ) const
+{
+ ScExtTabSettingsMap::const_iterator aIt = maMap.find( nTab );
+ return (aIt == maMap.end()) ? nullptr : aIt->second.get();
+}
+
+ScExtTabSettings& ScExtTabSettingsCont::GetOrCreateTabSettings( SCTAB nTab )
+{
+ ScExtTabSettingsRef& rxTabSett = maMap[ nTab ];
+ if( !rxTabSett )
+ rxTabSett = std::make_shared<ScExtTabSettings>();
+ return *rxTabSett;
+}
+
+SCTAB ScExtTabSettingsCont::GetLastTab() const
+{
+ return maMap.empty() ? -1 : maMap.rbegin()->first;
+}
+
+void ScExtTabSettingsCont::CopyFromMap( const ScExtTabSettingsMap& rMap )
+{
+ maMap.clear();
+ for( const auto& [rTab, rxSettings] : rMap )
+ maMap[ rTab ] = std::make_shared<ScExtTabSettings>( *rxSettings );
+}
+
+/** Implementation struct for ScExtDocOptions containing all members. */
+struct ScExtDocOptionsImpl
+{
+ ScExtDocSettings maDocSett; /// Global document settings.
+ ScExtTabSettingsCont maTabSett; /// Settings for all sheets.
+ std::vector< OUString > maCodeNames; /// Codenames for all sheets (VBA module names).
+ bool mbChanged; /// Use only if something has been changed.
+
+ explicit ScExtDocOptionsImpl();
+};
+
+ScExtDocOptionsImpl::ScExtDocOptionsImpl() :
+ mbChanged( false )
+{
+}
+
+ScExtDocOptions::ScExtDocOptions() :
+ mxImpl( new ScExtDocOptionsImpl )
+{
+}
+
+ScExtDocOptions::ScExtDocOptions( const ScExtDocOptions& rSrc ) :
+ mxImpl( new ScExtDocOptionsImpl( *rSrc.mxImpl ) )
+{
+}
+
+ScExtDocOptions::~ScExtDocOptions()
+{
+}
+
+ScExtDocOptions& ScExtDocOptions::operator=( const ScExtDocOptions& rSrc )
+{
+ *mxImpl = *rSrc.mxImpl;
+ return *this;
+}
+
+bool ScExtDocOptions::IsChanged() const
+{
+ return mxImpl->mbChanged;
+}
+
+void ScExtDocOptions::SetChanged( bool bChanged )
+{
+ mxImpl->mbChanged = bChanged;
+}
+
+const ScExtDocSettings& ScExtDocOptions::GetDocSettings() const
+{
+ return mxImpl->maDocSett;
+}
+
+ScExtDocSettings& ScExtDocOptions::GetDocSettings()
+{
+ return mxImpl->maDocSett;
+}
+
+const ScExtTabSettings* ScExtDocOptions::GetTabSettings( SCTAB nTab ) const
+{
+ return mxImpl->maTabSett.GetTabSettings( nTab );
+}
+
+SCTAB ScExtDocOptions::GetLastTab() const
+{
+ return mxImpl->maTabSett.GetLastTab();
+}
+
+ScExtTabSettings& ScExtDocOptions::GetOrCreateTabSettings( SCTAB nTab )
+{
+ return mxImpl->maTabSett.GetOrCreateTabSettings( nTab );
+}
+
+SCTAB ScExtDocOptions::GetCodeNameCount() const
+{
+ return static_cast< SCTAB >( mxImpl->maCodeNames.size() );
+}
+
+OUString ScExtDocOptions::GetCodeName( SCTAB nTab ) const
+{
+ OSL_ENSURE( (0 <= nTab) && (nTab < GetCodeNameCount()), "ScExtDocOptions::GetCodeName - invalid sheet index" );
+ return ((0 <= nTab) && (nTab < GetCodeNameCount())) ? mxImpl->maCodeNames[ static_cast< size_t >( nTab ) ] : OUString();
+}
+
+void ScExtDocOptions::SetCodeName( SCTAB nTab, const OUString& rCodeName )
+{
+ OSL_ENSURE( nTab >= 0, "ScExtDocOptions::SetCodeName - invalid sheet index" );
+ if( nTab >= 0 )
+ {
+ size_t nIndex = static_cast< size_t >( nTab );
+ if( nIndex >= mxImpl->maCodeNames.size() )
+ mxImpl->maCodeNames.resize( nIndex + 1 );
+ mxImpl->maCodeNames[ nIndex ] = rCodeName;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/select.cxx b/sc/source/ui/view/select.cxx
new file mode 100644
index 0000000000..d972c9b4eb
--- /dev/null
+++ b/sc/source/ui/view/select.cxx
@@ -0,0 +1,986 @@
+/* -*- 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 .
+ */
+
+#include <tools/urlobj.hxx>
+#include <sfx2/docfile.hxx>
+#include <osl/diagnose.h>
+
+#include <select.hxx>
+#include <tabvwsh.hxx>
+#include <scmod.hxx>
+#include <document.hxx>
+#include <transobj.hxx>
+#include <docsh.hxx>
+#include <tabprotection.hxx>
+#include <markdata.hxx>
+#include <gridwin.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <comphelper/lok.hxx>
+
+#if defined(_WIN32)
+#define SC_SELENG_REFMODE_UPDATE_INTERVAL_MIN 65
+#endif
+
+using namespace com::sun::star;
+
+static Point aSwitchPos; //! Member
+static bool bDidSwitch = false;
+
+// View (Gridwin / keyboard)
+ScViewFunctionSet::ScViewFunctionSet( ScViewData* pNewViewData ) :
+ m_pViewData( pNewViewData ),
+ m_pEngine( nullptr ),
+ m_bAnchor( false ),
+ m_bStarted( false )
+{
+ OSL_ENSURE(m_pViewData, "ViewData==0 at FunctionSet");
+}
+
+ScSplitPos ScViewFunctionSet::GetWhich() const
+{
+ if (m_pEngine)
+ return m_pEngine->GetWhich();
+ else
+ return m_pViewData->GetActivePart();
+}
+
+sal_uInt64 ScViewFunctionSet::CalcUpdateInterval( const Size& rWinSize, const Point& rEffPos,
+ bool bLeftScroll, bool bTopScroll, bool bRightScroll, bool bBottomScroll )
+{
+ sal_uInt64 nUpdateInterval = SELENG_AUTOREPEAT_INTERVAL_MAX;
+ vcl::Window* pWin = m_pEngine->GetWindow();
+ AbsoluteScreenPixelRectangle aScrRect = pWin->GetDesktopRectPixel();
+ AbsoluteScreenPixelPoint aRootPos = pWin->OutputToAbsoluteScreenPixel(Point(0,0));
+ if (bRightScroll)
+ {
+ double nWinRight = rWinSize.getWidth() + aRootPos.getX();
+ double nMarginRight = aScrRect.GetWidth() - nWinRight;
+ double nHOffset = rEffPos.X() - rWinSize.Width();
+ double nHAccelRate = nHOffset / nMarginRight;
+
+ if (nHAccelRate > 1.0)
+ nHAccelRate = 1.0;
+
+ nUpdateInterval = SELENG_AUTOREPEAT_INTERVAL_MAX*(1.0 - nHAccelRate);
+ }
+
+ if (bLeftScroll)
+ {
+ double nMarginLeft = aRootPos.getX();
+ double nHOffset = -rEffPos.X();
+ double nHAccelRate = nHOffset / nMarginLeft;
+
+ if (nHAccelRate > 1.0)
+ nHAccelRate = 1.0;
+
+ sal_uLong nTmp = static_cast<sal_uLong>(SELENG_AUTOREPEAT_INTERVAL_MAX*(1.0 - nHAccelRate));
+ if (nUpdateInterval > nTmp)
+ nUpdateInterval = nTmp;
+ }
+
+ if (bBottomScroll)
+ {
+ double nWinBottom = rWinSize.getHeight() + aRootPos.getY();
+ double nMarginBottom = aScrRect.GetHeight() - nWinBottom;
+ double nVOffset = rEffPos.Y() - rWinSize.Height();
+ double nVAccelRate = nVOffset / nMarginBottom;
+
+ if (nVAccelRate > 1.0)
+ nVAccelRate = 1.0;
+
+ sal_uInt64 nTmp = SELENG_AUTOREPEAT_INTERVAL_MAX*(1.0 - nVAccelRate);
+ if (nUpdateInterval > nTmp)
+ nUpdateInterval = nTmp;
+ }
+
+ if (bTopScroll)
+ {
+ double nMarginTop = aRootPos.getY();
+ double nVOffset = -rEffPos.Y();
+ double nVAccelRate = nVOffset / nMarginTop;
+
+ if (nVAccelRate > 1.0)
+ nVAccelRate = 1.0;
+
+ sal_uInt64 nTmp = SELENG_AUTOREPEAT_INTERVAL_MAX*(1.0 - nVAccelRate);
+ if (nUpdateInterval > nTmp)
+ nUpdateInterval = nTmp;
+ }
+
+#ifdef _WIN32
+ ScTabViewShell* pViewShell = m_pViewData->GetViewShell();
+ bool bRefMode = pViewShell && pViewShell->IsRefInputMode();
+ if (bRefMode && nUpdateInterval < SC_SELENG_REFMODE_UPDATE_INTERVAL_MIN)
+ // Lower the update interval during ref mode, because re-draw can be
+ // expensive on Windows. Making this interval too small would queue up
+ // the scroll/paint requests which would cause semi-infinite
+ // scrolls even after the mouse cursor is released. We don't have
+ // this problem on Linux.
+ nUpdateInterval = SC_SELENG_REFMODE_UPDATE_INTERVAL_MIN;
+#endif
+ return nUpdateInterval;
+}
+
+void ScViewFunctionSet::SetSelectionEngine( ScViewSelectionEngine* pSelEngine )
+{
+ m_pEngine = pSelEngine;
+}
+
+// Drag & Drop
+void ScViewFunctionSet::BeginDrag()
+{
+ SCTAB nTab = m_pViewData->GetTabNo();
+
+ SCCOL nPosX;
+ SCROW nPosY;
+ if (m_pEngine)
+ {
+ Point aMPos = m_pEngine->GetMousePosPixel();
+ m_pViewData->GetPosFromPixel( aMPos.X(), aMPos.Y(), GetWhich(), nPosX, nPosY );
+ }
+ else
+ {
+ nPosX = m_pViewData->GetCurX();
+ nPosY = m_pViewData->GetCurY();
+ }
+
+ ScModule* pScMod = SC_MOD();
+ bool bRefMode = pScMod->IsFormulaMode();
+ if (bRefMode)
+ return;
+
+ m_pViewData->GetView()->FakeButtonUp( GetWhich() ); // ButtonUp is swallowed
+
+ ScMarkData& rMark = m_pViewData->GetMarkData();
+ rMark.MarkToSimple();
+ if ( !rMark.IsMarked() || rMark.IsMultiMarked() )
+ return;
+
+ ScDocumentUniquePtr pClipDoc(new ScDocument( SCDOCMODE_CLIP ));
+ // bApi = TRUE -> no error messages
+ bool bCopied = m_pViewData->GetView()->CopyToClip( pClipDoc.get(), false, true );
+ if ( !bCopied )
+ return;
+
+ sal_Int8 nDragActions = m_pViewData->GetView()->SelectionEditable() ?
+ ( DND_ACTION_COPYMOVE | DND_ACTION_LINK ) :
+ ( DND_ACTION_COPY | DND_ACTION_LINK );
+
+ ScDocShell* pDocSh = m_pViewData->GetDocShell();
+ TransferableObjectDescriptor aObjDesc;
+ pDocSh->FillTransferableObjectDescriptor( aObjDesc );
+ aObjDesc.maDisplayName = pDocSh->GetMedium()->GetURLObject().GetURLNoPass();
+ // maSize is set in ScTransferObj ctor
+
+ rtl::Reference<ScTransferObj> pTransferObj = new ScTransferObj( std::move(pClipDoc), std::move(aObjDesc) );
+
+ // set position of dragged cell within range
+ ScRange aMarkRange = pTransferObj->GetRange();
+ SCCOL nStartX = aMarkRange.aStart.Col();
+ SCROW nStartY = aMarkRange.aStart.Row();
+ SCCOL nHandleX = (nPosX >= nStartX) ? nPosX - nStartX : 0;
+ SCROW nHandleY = (nPosY >= nStartY) ? nPosY - nStartY : 0;
+ pTransferObj->SetDragHandlePos( nHandleX, nHandleY );
+ pTransferObj->SetSourceCursorPos( m_pViewData->GetCurX(), m_pViewData->GetCurY() );
+ pTransferObj->SetVisibleTab( nTab );
+
+ pTransferObj->SetDragSource( pDocSh, rMark );
+
+ vcl::Window* pWindow = m_pViewData->GetActiveWin();
+ if ( pWindow->IsTracking() )
+ pWindow->EndTracking( TrackingEventFlags::Cancel ); // abort selecting
+
+ if (comphelper::LibreOfficeKit::isActive())
+ pWindow->LocalStartDrag();
+
+ SC_MOD()->SetDragObject( pTransferObj.get(), nullptr ); // for internal D&D
+ pTransferObj->StartDrag( pWindow, nDragActions );
+
+ return; // dragging started
+
+}
+
+// Selection
+void ScViewFunctionSet::CreateAnchor()
+{
+ if (m_bAnchor) return;
+
+ bool bRefMode = SC_MOD()->IsFormulaMode();
+ if (bRefMode)
+ SetAnchor( m_pViewData->GetRefStartX(), m_pViewData->GetRefStartY() );
+ else
+ SetAnchor( m_pViewData->GetCurX(), m_pViewData->GetCurY() );
+}
+
+void ScViewFunctionSet::SetAnchor( SCCOL nPosX, SCROW nPosY )
+{
+ bool bRefMode = SC_MOD()->IsFormulaMode();
+ ScTabView* pView = m_pViewData->GetView();
+ SCTAB nTab = m_pViewData->GetTabNo();
+
+ if (bRefMode)
+ {
+ pView->DoneRefMode();
+ m_aAnchorPos.Set( nPosX, nPosY, nTab );
+ pView->InitRefMode( m_aAnchorPos.Col(), m_aAnchorPos.Row(), m_aAnchorPos.Tab(),
+ SC_REFTYPE_REF );
+ m_bStarted = true;
+ }
+ else if (m_pViewData->IsAnyFillMode())
+ {
+ m_aAnchorPos.Set( nPosX, nPosY, nTab );
+ m_bStarted = true;
+ }
+ else
+ {
+ // don't go there and back again
+ if ( m_bStarted && pView->IsMarking( nPosX, nPosY, nTab ) )
+ {
+ // don't do anything
+ }
+ else
+ {
+ pView->DoneBlockMode( true );
+ m_aAnchorPos.Set( nPosX, nPosY, nTab );
+ ScMarkData& rMark = m_pViewData->GetMarkData();
+ if ( rMark.IsMarked() || rMark.IsMultiMarked() )
+ {
+ pView->InitBlockMode( m_aAnchorPos.Col(), m_aAnchorPos.Row(),
+ m_aAnchorPos.Tab(), true );
+ m_bStarted = true;
+ }
+ else
+ m_bStarted = false;
+ }
+ }
+ m_bAnchor = true;
+}
+
+void ScViewFunctionSet::DestroyAnchor()
+{
+ if (m_pViewData->IsAnyFillMode())
+ return;
+
+ bool bRefMode = SC_MOD()->IsFormulaMode();
+ if (bRefMode)
+ m_pViewData->GetView()->DoneRefMode( true );
+ else
+ m_pViewData->GetView()->DoneBlockMode( true );
+
+ m_bAnchor = false;
+}
+
+void ScViewFunctionSet::SetAnchorFlag( bool bSet )
+{
+ m_bAnchor = bSet;
+}
+
+void ScViewFunctionSet::SetCursorAtPoint( const Point& rPointPixel, bool /* bDontSelectAtCursor */ )
+{
+ if ( bDidSwitch )
+ {
+ if ( rPointPixel == aSwitchPos )
+ return; // don't scroll in wrong window
+ else
+ bDidSwitch = false;
+ }
+ aSwitchPos = rPointPixel; // only important, if bDidSwitch
+
+ // treat position 0 as -1, so scrolling is always possible
+ // (with full screen and hidden headers, the top left border may be at 0)
+ // (moved from ScViewData::GetPosFromPixel)
+
+ Point aEffPos = rPointPixel;
+ if ( aEffPos.X() == 0 )
+ aEffPos.setX( -1 );
+ if ( aEffPos.Y() == 0 )
+ aEffPos.setY( -1 );
+
+ // Scrolling
+ Size aWinSize = m_pEngine->GetWindow()->GetOutputSizePixel();
+ bool bLeftScroll = ( aEffPos.X() < 0 );
+ bool bTopScroll = ( aEffPos.Y() < 0 );
+
+ SCCOL nPosX;
+ SCROW nPosY;
+ m_pViewData->GetPosFromPixel( aEffPos.X(), aEffPos.Y(), GetWhich(),
+ nPosX, nPosY, true, true ); // with Repair
+
+ tools::Rectangle aEditArea = m_pViewData->GetEditArea(GetWhich(), nPosX, nPosY,
+ m_pEngine->GetWindow(),
+ nullptr, false);
+
+ bool bFillingSelection = m_pViewData->IsFillMode() || m_pViewData->GetFillMode() == ScFillMode::MATRIX;
+ bool bBottomScroll;
+ bool bRightScroll;
+ // for Autofill don't yet assume we want to auto-scroll to the cell under the mouse
+ // because the autofill handle extends into a cells neighbours so initial click is usually
+ // above a neighbour cell
+ if (bFillingSelection)
+ {
+ bBottomScroll = aEffPos.Y() >= aWinSize.Height();
+ bRightScroll = aEffPos.X() >= aWinSize.Width();
+ }
+ else
+ {
+ //in the normal case make the full selected cell visible
+ bBottomScroll = aEditArea.Bottom() >= aWinSize.Height();
+ bRightScroll = aEditArea.Right() >= aWinSize.Width();
+ }
+
+ bool bScroll = bRightScroll || bBottomScroll || bLeftScroll || bTopScroll;
+
+ // for Autofill switch in the center of cell thereby don't prevent scrolling to bottom/right
+ if (bFillingSelection)
+ {
+ bool bLeft, bTop;
+ m_pViewData->GetMouseQuadrant( aEffPos, GetWhich(), nPosX, nPosY, bLeft, bTop );
+ ScDocument& rDoc = m_pViewData->GetDocument();
+ SCTAB nTab = m_pViewData->GetTabNo();
+ if ( bLeft && !bRightScroll )
+ do --nPosX; while ( nPosX>=0 && rDoc.ColHidden( nPosX, nTab ) );
+ if ( bTop && !bBottomScroll )
+ {
+ if (--nPosY >= 0)
+ {
+ nPosY = rDoc.LastVisibleRow(0, nPosY, nTab);
+ if (!rDoc.ValidRow(nPosY))
+ nPosY = -1;
+ }
+ }
+ // negative value is allowed
+ }
+
+ // moved out of fix limit?
+ ScSplitPos eWhich = GetWhich();
+ if ( eWhich == m_pViewData->GetActivePart() )
+ {
+ if ( m_pViewData->GetHSplitMode() == SC_SPLIT_FIX )
+ if ( aEffPos.X() >= aWinSize.Width() )
+ {
+ if ( eWhich == SC_SPLIT_TOPLEFT )
+ {
+ m_pViewData->GetView()->ActivatePart( SC_SPLIT_TOPRIGHT );
+ bScroll = false;
+ bDidSwitch = true;
+ }
+ else if ( eWhich == SC_SPLIT_BOTTOMLEFT )
+ {
+ m_pViewData->GetView()->ActivatePart( SC_SPLIT_BOTTOMRIGHT );
+ bScroll = false;
+ bDidSwitch = true;
+ }
+ }
+
+ if ( m_pViewData->GetVSplitMode() == SC_SPLIT_FIX )
+ if ( aEffPos.Y() >= aWinSize.Height() )
+ {
+ if ( eWhich == SC_SPLIT_TOPLEFT )
+ {
+ m_pViewData->GetView()->ActivatePart( SC_SPLIT_BOTTOMLEFT );
+ bScroll = false;
+ bDidSwitch = true;
+ }
+ else if ( eWhich == SC_SPLIT_TOPRIGHT )
+ {
+ m_pViewData->GetView()->ActivatePart( SC_SPLIT_BOTTOMRIGHT );
+ bScroll = false;
+ bDidSwitch = true;
+ }
+ }
+ }
+
+ if (bScroll)
+ {
+ // Adjust update interval based on how far the mouse pointer is from the edge.
+ sal_uInt64 nUpdateInterval = CalcUpdateInterval(
+ aWinSize, aEffPos, bLeftScroll, bTopScroll, bRightScroll, bBottomScroll);
+ m_pEngine->SetUpdateInterval(nUpdateInterval);
+ }
+ else
+ {
+ // Don't forget to reset the interval when not scrolling!
+ m_pEngine->SetUpdateInterval(SELENG_AUTOREPEAT_INTERVAL);
+ }
+
+ m_pViewData->ResetOldCursor();
+ SetCursorAtCell( nPosX, nPosY, bScroll );
+}
+
+bool ScViewFunctionSet::CheckRefBounds(SCCOL nPosX, SCROW nPosY)
+{
+ SCCOL startX = m_pViewData->GetRefStartX();
+ SCROW startY = m_pViewData->GetRefStartY();
+
+ SCCOL endX = m_pViewData->GetRefEndX();
+ SCROW endY = m_pViewData->GetRefEndY();
+
+ return nPosX >= startX && nPosX <= endX && nPosY >= startY && nPosY <= endY;
+}
+
+bool ScViewFunctionSet::SetCursorAtCell( SCCOL nPosX, SCROW nPosY, bool bScroll )
+{
+ ScTabView* pView = m_pViewData->GetView();
+ SCTAB nTab = m_pViewData->GetTabNo();
+ ScDocument& rDoc = m_pViewData->GetDocument();
+
+ if ( rDoc.IsTabProtected(nTab) )
+ {
+ if (nPosX < 0 || nPosY < 0)
+ return false;
+
+ const ScTableProtection* pProtect = rDoc.GetTabProtection(nTab);
+ if (!pProtect)
+ return false;
+
+ bool bSkipProtected = !pProtect->isOptionEnabled(ScTableProtection::SELECT_LOCKED_CELLS);
+ bool bSkipUnprotected = !pProtect->isOptionEnabled(ScTableProtection::SELECT_UNLOCKED_CELLS);
+
+ if ( bSkipProtected && bSkipUnprotected )
+ return false;
+
+ bool bCellProtected = rDoc.HasAttrib(nPosX, nPosY, nTab, nPosX, nPosY, nTab, HasAttrFlags::Protected);
+ if ( (bCellProtected && bSkipProtected) || (!bCellProtected && bSkipUnprotected) )
+ // Don't select this cell!
+ return false;
+ }
+
+ ScModule* pScMod = SC_MOD();
+ ScTabViewShell* pViewShell = m_pViewData->GetViewShell();
+ bool bRefMode = pViewShell && pViewShell->IsRefInputMode();
+
+ bool bHide = !bRefMode && !m_pViewData->IsAnyFillMode() &&
+ ( nPosX != m_pViewData->GetCurX() || nPosY != m_pViewData->GetCurY() );
+
+ if (bHide)
+ pView->HideAllCursors();
+
+ if (bScroll)
+ {
+ if (bRefMode)
+ {
+ ScSplitPos eWhich = GetWhich();
+ pView->AlignToCursor( nPosX, nPosY, SC_FOLLOW_LINE, &eWhich );
+ }
+ else
+ pView->AlignToCursor( nPosX, nPosY, SC_FOLLOW_LINE );
+ }
+
+ if (bRefMode)
+ {
+ // if no input is possible from this doc, don't move the reference cursor around
+ if ( !pScMod->IsModalMode(m_pViewData->GetSfxDocShell()) && (!CheckRefBounds(nPosX, nPosY) || SfxLokHelper::getDeviceFormFactor() != LOKDeviceFormFactor::MOBILE))
+ {
+ if (!m_bAnchor)
+ {
+ pView->DoneRefMode( true );
+ pView->InitRefMode( nPosX, nPosY, m_pViewData->GetTabNo(), SC_REFTYPE_REF );
+ }
+
+ if(SfxLokHelper::getDeviceFormFactor() != LOKDeviceFormFactor::MOBILE)
+ pView->UpdateRef( nPosX, nPosY, m_pViewData->GetTabNo() );
+
+ pView->SelectionChanged();
+ }
+ }
+ else if (m_pViewData->IsFillMode() ||
+ (m_pViewData->GetFillMode() == ScFillMode::MATRIX && (nScFillModeMouseModifier & KEY_MOD1) ))
+ {
+ // If a matrix got touched, switch back to Autofill is possible with Ctrl
+
+ SCCOL nStartX, nEndX;
+ SCROW nStartY, nEndY; // Block
+ SCTAB nDummy;
+ m_pViewData->GetSimpleArea( nStartX, nStartY, nDummy, nEndX, nEndY, nDummy );
+
+ if (m_pViewData->GetRefType() != SC_REFTYPE_FILL)
+ {
+ pView->InitRefMode( nStartX, nStartY, nTab, SC_REFTYPE_FILL );
+ CreateAnchor();
+ }
+
+ ScRange aDelRange;
+ bool bOldDelMark = m_pViewData->GetDelMark( aDelRange );
+
+ if ( nPosX+1 >= nStartX && nPosX <= nEndX &&
+ nPosY+1 >= nStartY && nPosY <= nEndY &&
+ ( nPosX != nEndX || nPosY != nEndY ) ) // minimize?
+ {
+ // direction (left or top)
+
+ tools::Long nSizeX = 0;
+ for (SCCOL i=nPosX+1; i<=nEndX; i++)
+ nSizeX += rDoc.GetColWidth( i, nTab );
+ tools::Long nSizeY = rDoc.GetRowHeight( nPosY+1, nEndY, nTab );
+
+ SCCOL nDelStartX = nStartX;
+ SCROW nDelStartY = nStartY;
+ if ( nSizeX > nSizeY )
+ nDelStartX = nPosX + 1;
+ else
+ nDelStartY = nPosY + 1;
+ // there is no need to check for zero, because nPosX/Y is also negative
+
+ if ( nDelStartX < nStartX )
+ nDelStartX = nStartX;
+ if ( nDelStartY < nStartY )
+ nDelStartY = nStartY;
+
+ // set range
+
+ m_pViewData->SetDelMark( ScRange( nDelStartX,nDelStartY,nTab,
+ nEndX,nEndY,nTab ) );
+ m_pViewData->GetView()->UpdateShrinkOverlay();
+
+ m_pViewData->GetView()->
+ PaintArea( nStartX,nDelStartY, nEndX,nEndY, ScUpdateMode::Marks );
+
+ nPosX = nEndX; // keep red border around range
+ nPosY = nEndY;
+
+ // reference the right way up, if it's upside down below
+ if ( nStartX != m_pViewData->GetRefStartX() || nStartY != m_pViewData->GetRefStartY() )
+ {
+ m_pViewData->GetView()->DoneRefMode();
+ m_pViewData->GetView()->InitRefMode( nStartX, nStartY, nTab, SC_REFTYPE_FILL );
+ }
+ }
+ else
+ {
+ if ( bOldDelMark )
+ {
+ m_pViewData->ResetDelMark();
+ m_pViewData->GetView()->UpdateShrinkOverlay();
+ }
+
+ bool bNegX = ( nPosX < nStartX );
+ bool bNegY = ( nPosY < nStartY );
+
+ tools::Long nSizeX = 0;
+ if ( bNegX )
+ {
+ // in SetCursorAtPoint hidden columns are skipped.
+ // They must be skipped here too, or the result will always be the first hidden column.
+ do ++nPosX; while ( nPosX<nStartX && rDoc.ColHidden(nPosX, nTab) );
+ for (SCCOL i=nPosX; i<nStartX; i++)
+ nSizeX += rDoc.GetColWidth( i, nTab );
+ }
+ else
+ for (SCCOL i=nEndX+1; i<=nPosX; i++)
+ nSizeX += rDoc.GetColWidth( i, nTab );
+
+ tools::Long nSizeY = 0;
+ if ( bNegY )
+ {
+ // in SetCursorAtPoint hidden rows are skipped.
+ // They must be skipped here too, or the result will always be the first hidden row.
+ if (++nPosY < nStartY)
+ {
+ nPosY = rDoc.FirstVisibleRow(nPosY, nStartY-1, nTab);
+ if (!rDoc.ValidRow(nPosY))
+ nPosY = nStartY;
+ }
+ nSizeY += rDoc.GetRowHeight( nPosY, nStartY-1, nTab );
+ }
+ else
+ nSizeY += rDoc.GetRowHeight( nEndY+1, nPosY, nTab );
+
+ if ( nSizeX > nSizeY ) // Fill only ever in one direction
+ {
+ nPosY = nEndY;
+ bNegY = false;
+ }
+ else
+ {
+ nPosX = nEndX;
+ bNegX = false;
+ }
+
+ SCCOL nRefStX = bNegX ? nEndX : nStartX;
+ SCROW nRefStY = bNegY ? nEndY : nStartY;
+ if ( nRefStX != m_pViewData->GetRefStartX() || nRefStY != m_pViewData->GetRefStartY() )
+ {
+ m_pViewData->GetView()->DoneRefMode();
+ m_pViewData->GetView()->InitRefMode( nRefStX, nRefStY, nTab, SC_REFTYPE_FILL );
+ }
+ }
+
+ pView->UpdateRef( nPosX, nPosY, nTab );
+ }
+ else if (m_pViewData->IsAnyFillMode())
+ {
+ ScFillMode nMode = m_pViewData->GetFillMode();
+ if ( nMode == ScFillMode::EMBED_LT || nMode == ScFillMode::EMBED_RB )
+ {
+ OSL_ENSURE( rDoc.IsEmbedded(), "!rDoc.IsEmbedded()" );
+ ScRange aRange;
+ rDoc.GetEmbedded( aRange);
+ ScRefType eRefMode = (nMode == ScFillMode::EMBED_LT) ? SC_REFTYPE_EMBED_LT : SC_REFTYPE_EMBED_RB;
+ if (m_pViewData->GetRefType() != eRefMode)
+ {
+ if ( nMode == ScFillMode::EMBED_LT )
+ pView->InitRefMode( aRange.aEnd.Col(), aRange.aEnd.Row(), nTab, eRefMode );
+ else
+ pView->InitRefMode( aRange.aStart.Col(), aRange.aStart.Row(), nTab, eRefMode );
+ CreateAnchor();
+ }
+
+ pView->UpdateRef( nPosX, nPosY, nTab );
+ }
+ else if ( nMode == ScFillMode::MATRIX )
+ {
+ SCCOL nStartX, nEndX;
+ SCROW nStartY, nEndY; // Block
+ SCTAB nDummy;
+ m_pViewData->GetSimpleArea( nStartX, nStartY, nDummy, nEndX, nEndY, nDummy );
+
+ if (m_pViewData->GetRefType() != SC_REFTYPE_FILL)
+ {
+ pView->InitRefMode( nStartX, nStartY, nTab, SC_REFTYPE_FILL );
+ CreateAnchor();
+ }
+
+ if ( nPosX < nStartX ) nPosX = nStartX;
+ if ( nPosY < nStartY ) nPosY = nStartY;
+
+ pView->UpdateRef( nPosX, nPosY, nTab );
+ }
+ // else new modes
+ }
+ else // regular selection
+ {
+ bool bHideCur = m_bAnchor && ( nPosX != m_pViewData->GetCurX() ||
+ nPosY != m_pViewData->GetCurY() );
+ if (bHideCur)
+ pView->HideAllCursors(); // otherwise twice: Block and SetCursor
+
+ if (m_bAnchor)
+ {
+ if (!m_bStarted)
+ {
+ bool bMove = ( nPosX != m_aAnchorPos.Col() ||
+ nPosY != m_aAnchorPos.Row() );
+ if ( bMove || ( m_pEngine && m_pEngine->GetMouseEvent().IsShift() ) )
+ {
+ pView->InitBlockMode( m_aAnchorPos.Col(), m_aAnchorPos.Row(),
+ m_aAnchorPos.Tab(), true );
+ m_bStarted = true;
+ }
+ }
+ if (m_bStarted)
+ // If the selection is already started, don't set the cursor.
+ pView->MarkCursor( nPosX, nPosY, nTab, false, false, true );
+ else
+ pView->SetCursor( nPosX, nPosY );
+ }
+ else
+ {
+ ScMarkData& rMark = m_pViewData->GetMarkData();
+ if (rMark.IsMarked() || rMark.IsMultiMarked())
+ {
+ pView->DoneBlockMode(true);
+ pView->InitBlockMode( nPosX, nPosY, nTab, true );
+ pView->MarkCursor( nPosX, nPosY, nTab );
+
+ m_aAnchorPos.Set( nPosX, nPosY, nTab );
+ m_bStarted = true;
+ }
+ // #i3875# *Hack* When a new cell is Ctrl-clicked with no pre-selected cells,
+ // it highlights that new cell as well as the old cell where the cursor is
+ // positioned prior to the click. A selection mode via Shift-F8 should also
+ // follow the same behavior.
+ else if ( m_pViewData->IsSelCtrlMouseClick() )
+ {
+ SCCOL nOldX = m_pViewData->GetCurX();
+ SCROW nOldY = m_pViewData->GetCurY();
+
+ pView->InitBlockMode( nOldX, nOldY, nTab, true );
+ pView->MarkCursor( nOldX, nOldY, nTab );
+
+ if ( nOldX != nPosX || nOldY != nPosY )
+ {
+ pView->DoneBlockMode( true );
+ pView->InitBlockMode( nPosX, nPosY, nTab, true );
+ pView->MarkCursor( nPosX, nPosY, nTab );
+ m_aAnchorPos.Set( nPosX, nPosY, nTab );
+ }
+
+ m_bStarted = true;
+ }
+ pView->SetCursor( nPosX, nPosY );
+ }
+
+ m_pViewData->SetRefStart( nPosX, nPosY, nTab );
+ if (bHideCur)
+ pView->ShowAllCursors();
+ }
+
+ if (bHide)
+ pView->ShowAllCursors();
+
+ return true;
+}
+
+bool ScViewFunctionSet::IsSelectionAtPoint( const Point& rPointPixel )
+{
+ bool bRefMode = SC_MOD()->IsFormulaMode();
+ if (bRefMode)
+ return false;
+
+ if (m_pViewData->IsAnyFillMode())
+ return false;
+
+ ScMarkData& rMark = m_pViewData->GetMarkData();
+ if (m_bAnchor || !rMark.IsMultiMarked())
+ {
+ SCCOL nPosX;
+ SCROW nPosY;
+ m_pViewData->GetPosFromPixel( rPointPixel.X(), rPointPixel.Y(), GetWhich(), nPosX, nPosY );
+ return m_pViewData->GetMarkData().IsCellMarked( nPosX, nPosY );
+ }
+
+ return false;
+}
+
+void ScViewFunctionSet::DeselectAtPoint( const Point& /* rPointPixel */ )
+{
+ // doesn't exist
+}
+
+void ScViewFunctionSet::DeselectAll()
+{
+ if (m_pViewData->IsAnyFillMode())
+ return;
+
+ bool bRefMode = SC_MOD()->IsFormulaMode();
+ if (bRefMode)
+ {
+ m_pViewData->GetView()->DoneRefMode();
+ }
+ else
+ {
+ m_pViewData->GetView()->DoneBlockMode();
+ m_pViewData->GetViewShell()->UpdateInputHandler();
+ }
+
+ m_bAnchor = false;
+}
+
+ScViewSelectionEngine::ScViewSelectionEngine( vcl::Window* pWindow, ScTabView* pView,
+ ScSplitPos eSplitPos ) :
+ SelectionEngine( pWindow, &pView->GetFunctionSet() ),
+ eWhich( eSplitPos )
+{
+ SetSelectionMode( SelectionMode::Multiple );
+ EnableDrag( true );
+}
+
+// column and row headers
+ScHeaderFunctionSet::ScHeaderFunctionSet( ScViewData* pNewViewData ) :
+ pViewData( pNewViewData ),
+ bColumn( false ),
+ eWhich( SC_SPLIT_TOPLEFT ),
+ bAnchor( false ),
+ nCursorPos( 0 )
+{
+ OSL_ENSURE(pViewData, "ViewData==0 at FunctionSet");
+}
+
+void ScHeaderFunctionSet::SetColumn( bool bSet )
+{
+ bColumn = bSet;
+}
+
+void ScHeaderFunctionSet::SetWhich( ScSplitPos eNew )
+{
+ eWhich = eNew;
+}
+
+void ScHeaderFunctionSet::BeginDrag()
+{
+ // doesn't exist
+}
+
+void ScHeaderFunctionSet::CreateAnchor()
+{
+ if (bAnchor)
+ return;
+
+ ScTabView* pView = pViewData->GetView();
+ pView->DoneBlockMode( true );
+ if (bColumn)
+ {
+ pView->InitBlockMode( static_cast<SCCOL>(nCursorPos), 0, pViewData->GetTabNo(), true, true );
+ pView->MarkCursor( static_cast<SCCOL>(nCursorPos), pViewData->MaxRow(), pViewData->GetTabNo() );
+ }
+ else
+ {
+ pView->InitBlockMode( 0, nCursorPos, pViewData->GetTabNo(), true, false, true );
+ pView->MarkCursor( pViewData->MaxCol(), nCursorPos, pViewData->GetTabNo() );
+ }
+ bAnchor = true;
+}
+
+void ScHeaderFunctionSet::DestroyAnchor()
+{
+ pViewData->GetView()->DoneBlockMode( true );
+ bAnchor = false;
+}
+
+void ScHeaderFunctionSet::SetCursorAtPoint( const Point& rPointPixel, bool /* bDontSelectAtCursor */ )
+{
+ if ( bDidSwitch )
+ {
+ // next valid position has to be originated from another window
+ if ( rPointPixel == aSwitchPos )
+ return; // don't scroll in the wrong window
+ else
+ bDidSwitch = false;
+ }
+
+ // Scrolling
+ Size aWinSize = pViewData->GetActiveWin()->GetOutputSizePixel();
+ bool bScroll;
+ if (bColumn)
+ bScroll = ( rPointPixel.X() < 0 || rPointPixel.X() >= aWinSize.Width() );
+ else
+ bScroll = ( rPointPixel.Y() < 0 || rPointPixel.Y() >= aWinSize.Height() );
+
+ // moved out of fix limit?
+ bool bSwitched = false;
+ if ( bColumn )
+ {
+ if ( pViewData->GetHSplitMode() == SC_SPLIT_FIX )
+ {
+ if ( rPointPixel.X() > aWinSize.Width() )
+ {
+ if ( eWhich == SC_SPLIT_TOPLEFT )
+ {
+ pViewData->GetView()->ActivatePart( SC_SPLIT_TOPRIGHT );
+ bSwitched = true;
+ }
+ else if ( eWhich == SC_SPLIT_BOTTOMLEFT )
+ {
+ pViewData->GetView()->ActivatePart( SC_SPLIT_BOTTOMRIGHT );
+ bSwitched = true;
+ }
+ }
+ }
+ }
+ else // column headers
+ {
+ if ( pViewData->GetVSplitMode() == SC_SPLIT_FIX )
+ {
+ if ( rPointPixel.Y() > aWinSize.Height() )
+ {
+ if ( eWhich == SC_SPLIT_TOPLEFT )
+ {
+ pViewData->GetView()->ActivatePart( SC_SPLIT_BOTTOMLEFT );
+ bSwitched = true;
+ }
+ else if ( eWhich == SC_SPLIT_TOPRIGHT )
+ {
+ pViewData->GetView()->ActivatePart( SC_SPLIT_BOTTOMRIGHT );
+ bSwitched = true;
+ }
+ }
+ }
+ }
+ if (bSwitched)
+ {
+ aSwitchPos = rPointPixel;
+ bDidSwitch = true;
+ return; // do not crunch with wrong positions
+ }
+
+ SCCOL nPosX;
+ SCROW nPosY;
+ pViewData->GetPosFromPixel( rPointPixel.X(), rPointPixel.Y(), pViewData->GetActivePart(),
+ nPosX, nPosY, false );
+ if (bColumn)
+ {
+ nCursorPos = static_cast<SCCOLROW>(nPosX);
+ nPosY = pViewData->GetPosY(WhichV(pViewData->GetActivePart()));
+ }
+ else
+ {
+ nCursorPos = static_cast<SCCOLROW>(nPosY);
+ nPosX = pViewData->GetPosX(WhichH(pViewData->GetActivePart()));
+ }
+
+ ScTabView* pView = pViewData->GetView();
+ bool bHide = pViewData->GetCurX() != nPosX ||
+ pViewData->GetCurY() != nPosY;
+ if (bHide)
+ pView->HideAllCursors();
+
+ if (bScroll)
+ pView->AlignToCursor( nPosX, nPosY, SC_FOLLOW_LINE );
+ pView->SetCursor( nPosX, nPosY );
+
+ if ( !bAnchor || !pView->IsBlockMode() )
+ {
+ pView->DoneBlockMode( true );
+ pViewData->GetMarkData().MarkToMulti(); //! who changes this?
+ pView->InitBlockMode( nPosX, nPosY, pViewData->GetTabNo(), true, bColumn, !bColumn );
+
+ bAnchor = true;
+ }
+
+ pView->MarkCursor( nPosX, nPosY, pViewData->GetTabNo(), bColumn, !bColumn );
+
+ // SelectionChanged inside of HideCursor because of UpdateAutoFillMark
+ pView->SelectionChanged();
+
+ if (bHide)
+ pView->ShowAllCursors();
+}
+
+bool ScHeaderFunctionSet::IsSelectionAtPoint( const Point& rPointPixel )
+{
+ SCCOL nPosX;
+ SCROW nPosY;
+ pViewData->GetPosFromPixel( rPointPixel.X(), rPointPixel.Y(), pViewData->GetActivePart(),
+ nPosX, nPosY, false );
+
+ ScMarkData& rMark = pViewData->GetMarkData();
+ if (bColumn)
+ return rMark.IsColumnMarked( nPosX );
+ else
+ return rMark.IsRowMarked( nPosY );
+}
+
+void ScHeaderFunctionSet::DeselectAtPoint( const Point& /* rPointPixel */ )
+{
+}
+
+void ScHeaderFunctionSet::DeselectAll()
+{
+ pViewData->GetView()->DoneBlockMode();
+ bAnchor = false;
+}
+
+ScHeaderSelectionEngine::ScHeaderSelectionEngine( vcl::Window* pWindow, ScHeaderFunctionSet* pFuncSet ) :
+ SelectionEngine( pWindow, pFuncSet )
+{
+ SetSelectionMode( SelectionMode::Multiple );
+ EnableDrag( false );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/selectionstate.cxx b/sc/source/ui/view/selectionstate.cxx
new file mode 100644
index 0000000000..91c6a278cb
--- /dev/null
+++ b/sc/source/ui/view/selectionstate.cxx
@@ -0,0 +1,54 @@
+/* -*- 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 .
+ */
+
+#include <selectionstate.hxx>
+
+#include <editeng/editview.hxx>
+#include <viewdata.hxx>
+#include <markdata.hxx>
+
+ScSelectionState::ScSelectionState( ScViewData& rViewData ) :
+ meType( SC_SELECTTYPE_NONE )
+{
+ maCursor.SetTab( rViewData.GetTabNo() );
+ ScSplitPos eWhich = rViewData.GetActivePart();
+
+ if( rViewData.HasEditView( eWhich ) )
+ {
+ meType = SC_SELECTTYPE_EDITCELL;
+ maCursor.SetCol( rViewData.GetEditViewCol() );
+ maCursor.SetRow( rViewData.GetEditViewRow() );
+ maEditSel = rViewData.GetEditView( eWhich )->GetSelection();
+ }
+ else
+ {
+ maCursor.SetCol( rViewData.GetCurX() );
+ maCursor.SetRow( rViewData.GetCurY() );
+
+ ScMarkData& rMarkData = rViewData.GetMarkData();
+ rMarkData.MarkToMulti();
+ if( rMarkData.IsMultiMarked() )
+ {
+ meType = SC_SELECTTYPE_SHEET;
+ }
+ // else type is SC_SELECTTYPE_NONE - already initialized
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/spellcheckcontext.cxx b/sc/source/ui/view/spellcheckcontext.cxx
new file mode 100644
index 0000000000..b18483aa88
--- /dev/null
+++ b/sc/source/ui/view/spellcheckcontext.cxx
@@ -0,0 +1,387 @@
+/* -*- 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/.
+ */
+
+#include <spellcheckcontext.hxx>
+#include <svl/sharedstring.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/unolingu.hxx>
+
+#include <scitems.hxx>
+#include <document.hxx>
+#include <cellvalue.hxx>
+#include <editutil.hxx>
+#include <dpobject.hxx>
+
+#include <com/sun/star/linguistic2/XSpellChecker1.hpp>
+
+#include <o3tl/hash_combine.hxx>
+
+#include <unordered_map>
+
+using namespace css;
+
+using sc::SpellCheckContext;
+
+class SpellCheckContext::SpellCheckCache
+{
+ struct CellPos
+ {
+ struct Hash
+ {
+ size_t operator() (const CellPos& rPos) const
+ {
+ std::size_t seed = 0;
+ o3tl::hash_combine(seed, rPos.mnCol);
+ o3tl::hash_combine(seed, rPos.mnRow);
+ return seed;
+ }
+ };
+
+ SCCOL mnCol;
+ SCROW mnRow;
+
+ CellPos(SCCOL nCol, SCROW nRow) : mnCol(nCol), mnRow(nRow) {}
+
+ bool operator== (const CellPos& r) const
+ {
+ return mnCol == r.mnCol && mnRow == r.mnRow;
+ }
+
+ };
+
+ typedef std::vector<editeng::MisspellRanges> MisspellType;
+ typedef std::unordered_map<CellPos, std::unique_ptr<MisspellType>, CellPos::Hash> CellMapType;
+ typedef std::unordered_map<const rtl_uString*, std::unique_ptr<MisspellType>> SharedStringMapType;
+ typedef std::unordered_map<CellPos, LanguageType, CellPos::Hash> CellLangMapType;
+
+ SharedStringMapType maStringMisspells;
+ CellMapType maEditTextMisspells;
+ CellLangMapType maCellLanguages;
+ LanguageType meDefCellLanguage;
+
+public:
+
+ SpellCheckCache(LanguageType eDefaultCellLanguage) : meDefCellLanguage(eDefaultCellLanguage)
+ {
+ }
+
+ bool query(SCCOL nCol, SCROW nRow, const ScRefCellValue& rCell, MisspellType*& rpRanges) const
+ {
+ CellType eType = rCell.getType();
+ if (eType == CELLTYPE_STRING)
+ {
+ SharedStringMapType::const_iterator it = maStringMisspells.find(rCell.getSharedString()->getData());
+ if (it == maStringMisspells.end())
+ return false; // Not available
+
+ rpRanges = it->second.get();
+ return true;
+ }
+
+ if (eType == CELLTYPE_EDIT)
+ {
+ CellMapType::const_iterator it = maEditTextMisspells.find(CellPos(nCol, nRow));
+ if (it == maEditTextMisspells.end())
+ return false; // Not available
+
+ rpRanges = it->second.get();
+ return true;
+ }
+
+ rpRanges = nullptr;
+ return true;
+ }
+
+ void set(SCCOL nCol, SCROW nRow, const ScRefCellValue& rCell, std::unique_ptr<MisspellType> pRanges)
+ {
+ CellType eType = rCell.getType();
+ if (eType == CELLTYPE_STRING)
+ maStringMisspells.insert_or_assign(rCell.getSharedString()->getData(), std::move(pRanges));
+ else if (eType == CELLTYPE_EDIT)
+ maEditTextMisspells.insert_or_assign(CellPos(nCol, nRow), std::move(pRanges));
+ }
+
+ LanguageType getLanguage(SCCOL nCol, SCROW nRow) const
+ {
+ CellLangMapType::const_iterator it = maCellLanguages.find(CellPos(nCol, nRow));
+ if (it == maCellLanguages.end())
+ return meDefCellLanguage;
+
+ return it->second;
+ }
+
+ void setLanguage(LanguageType eCellLang, SCCOL nCol, SCROW nRow)
+ {
+ if (eCellLang == meDefCellLanguage)
+ maCellLanguages.erase(CellPos(nCol, nRow));
+ else
+ maCellLanguages.insert_or_assign(CellPos(nCol, nRow), eCellLang);
+ }
+
+ void clear(LanguageType eDefaultCellLanguage)
+ {
+ maStringMisspells.clear();
+ maEditTextMisspells.clear();
+ maCellLanguages.clear();
+ meDefCellLanguage = eDefaultCellLanguage;
+ }
+
+ void clearEditTextMap()
+ {
+ maEditTextMisspells.clear();
+ }
+};
+
+struct SpellCheckContext::SpellCheckStatus
+{
+ bool mbModified;
+
+ SpellCheckStatus() : mbModified(false) {};
+
+ DECL_LINK( EventHdl, EditStatus&, void );
+};
+
+IMPL_LINK(SpellCheckContext::SpellCheckStatus, EventHdl, EditStatus&, rStatus, void)
+{
+ EditStatusFlags nStatus = rStatus.GetStatusWord();
+ if (nStatus & EditStatusFlags::WRONGWORDCHANGED)
+ mbModified = true;
+}
+
+struct SpellCheckContext::SpellCheckResult
+{
+ SCCOL mnCol;
+ SCROW mnRow;
+ const std::vector<editeng::MisspellRanges>* pRanges;
+
+ SpellCheckResult() : mnCol(-1), mnRow(-1), pRanges(nullptr) {}
+
+ void set(SCCOL nCol, SCROW nRow, const std::vector<editeng::MisspellRanges>* pMisspells)
+ {
+ mnCol = nCol;
+ mnRow = nRow;
+ pRanges = pMisspells;
+ }
+
+ const std::vector<editeng::MisspellRanges>* query(SCCOL nCol, SCROW nRow) const
+ {
+ assert(mnCol == nCol);
+ assert(mnRow == nRow);
+ (void)nCol;
+ (void)nRow;
+ return pRanges;
+ }
+
+ void clear()
+ {
+ mnCol = -1;
+ mnRow = -1;
+ pRanges = nullptr;
+ }
+};
+
+SpellCheckContext::SpellCheckContext(ScDocument* pDocument, SCTAB nTab) :
+ pDoc(pDocument),
+ mnTab(nTab),
+ meLanguage(ScGlobal::GetEditDefaultLanguage())
+{
+ // defer init of engine and cache till the first query/set
+}
+
+SpellCheckContext::~SpellCheckContext()
+{
+}
+
+void SpellCheckContext::dispose()
+{
+ mpEngine.reset();
+ mpCache.reset();
+ pDoc = nullptr;
+}
+
+void SpellCheckContext::setTabNo(SCTAB nTab)
+{
+ if (mnTab == nTab)
+ return;
+ mnTab = nTab;
+ reset();
+}
+
+bool SpellCheckContext::isMisspelled(SCCOL nCol, SCROW nRow) const
+{
+ const_cast<SpellCheckContext*>(this)->ensureResults(nCol, nRow);
+ return mpResult->query(nCol, nRow);
+}
+
+const std::vector<editeng::MisspellRanges>* SpellCheckContext::getMisspellRanges(
+ SCCOL nCol, SCROW nRow ) const
+{
+ const_cast<SpellCheckContext*>(this)->ensureResults(nCol, nRow);
+ return mpResult->query(nCol, nRow);
+}
+
+void SpellCheckContext::setMisspellRanges(
+ SCCOL nCol, SCROW nRow, const std::vector<editeng::MisspellRanges>* pRanges )
+{
+ if (!mpEngine || !mpCache)
+ reset();
+
+ ScRefCellValue aCell(*pDoc, ScAddress(nCol, nRow, mnTab));
+ CellType eType = aCell.getType();
+
+ if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
+ return;
+
+ typedef std::vector<editeng::MisspellRanges> MisspellType;
+ std::unique_ptr<MisspellType> pMisspells(pRanges ? new MisspellType(*pRanges) : nullptr);
+ mpCache->set(nCol, nRow, aCell, std::move(pMisspells));
+}
+
+void SpellCheckContext::reset()
+{
+ meLanguage = ScGlobal::GetEditDefaultLanguage();
+ resetCache();
+ mpEngine.reset();
+ mpStatus.reset();
+}
+
+void SpellCheckContext::resetForContentChange()
+{
+ resetCache(true /* bContentChangeOnly */);
+}
+
+void SpellCheckContext::ensureResults(SCCOL nCol, SCROW nRow)
+{
+ if (!mpEngine || !mpCache ||
+ ScGlobal::GetEditDefaultLanguage() != meLanguage)
+ {
+ reset();
+ setup();
+ }
+
+ // perhaps compute the pivot rangelist once in some pivot-table change handler ?
+ if (pDoc->HasPivotTable())
+ {
+ if (ScDPCollection* pDPs = pDoc->GetDPCollection())
+ {
+ ScRangeList aPivotRanges = pDPs->GetAllTableRanges(mnTab);
+ if (aPivotRanges.Contains(ScAddress(nCol, nRow, mnTab))) // Don't spell check within pivot tables
+ {
+ mpResult->set(nCol, nRow, nullptr);
+ return;
+ }
+ }
+ }
+
+ ScRefCellValue aCell(*pDoc, ScAddress(nCol, nRow, mnTab));
+ CellType eType = aCell.getType();
+
+ if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
+ {
+ // No spell-check required.
+ mpResult->set(nCol, nRow, nullptr);
+ return;
+ }
+
+
+ // Cell content is either shared-string or EditTextObject
+
+ // For spell-checking, we currently only use the primary
+ // language; not CJK nor CTL.
+ const ScPatternAttr* pPattern = pDoc->GetPattern(nCol, nRow, mnTab);
+ LanguageType eCellLang = pPattern->GetItem(ATTR_FONT_LANGUAGE).GetValue();
+
+ if (eCellLang == LANGUAGE_SYSTEM)
+ eCellLang = meLanguage; // never use SYSTEM for spelling
+
+ if (eCellLang == LANGUAGE_NONE)
+ {
+ mpResult->set(nCol, nRow, nullptr); // No need to spell check this cell.
+ return;
+ }
+
+ typedef std::vector<editeng::MisspellRanges> MisspellType;
+
+ LanguageType eCachedCellLang = mpCache->getLanguage(nCol, nRow);
+
+ if (eCellLang != eCachedCellLang)
+ mpCache->setLanguage(eCellLang, nCol, nRow);
+
+ else
+ {
+ MisspellType* pRanges = nullptr;
+ bool bFound = mpCache->query(nCol, nRow, aCell, pRanges);
+ if (bFound)
+ {
+ // Cache hit.
+ mpResult->set(nCol, nRow, pRanges);
+ return;
+ }
+ }
+
+ // Cache miss, the cell needs spell-check..
+ if (eType == CELLTYPE_STRING)
+ mpEngine->SetText(aCell.getSharedString()->getString());
+ else
+ mpEngine->SetText(*aCell.getEditText());
+
+ // it has to happen after we set text
+ mpEngine->SetDefaultItem(SvxLanguageItem(eCellLang, EE_CHAR_LANGUAGE));
+
+ mpStatus->mbModified = false;
+ mpEngine->CompleteOnlineSpelling();
+ std::unique_ptr<MisspellType> pRanges;
+ if (mpStatus->mbModified)
+ {
+ pRanges.reset(new MisspellType);
+ mpEngine->GetAllMisspellRanges(*pRanges);
+
+ if (pRanges->empty())
+ pRanges.reset(nullptr);
+ }
+ // else : No change in status for EditStatusFlags::WRONGWORDCHANGED => no spell errors (which is the default status).
+
+ mpResult->set(nCol, nRow, pRanges.get());
+ mpCache->set(nCol, nRow, aCell, std::move(pRanges));
+}
+
+void SpellCheckContext::resetCache(bool bContentChangeOnly)
+{
+ if (!mpResult)
+ mpResult.reset(new SpellCheckResult());
+ else
+ mpResult->clear();
+
+ if (!mpCache)
+ mpCache.reset(new SpellCheckCache(meLanguage));
+ else if (bContentChangeOnly)
+ mpCache->clearEditTextMap();
+ else
+ mpCache->clear(meLanguage);
+}
+
+void SpellCheckContext::setup()
+{
+ mpEngine.reset(new ScTabEditEngine(pDoc));
+ mpStatus.reset(new SpellCheckStatus());
+
+ mpEngine->SetControlWord(
+ mpEngine->GetControlWord() | (EEControlBits::ONLINESPELLING | EEControlBits::ALLOWBIGOBJS));
+ mpEngine->SetStatusEventHdl(LINK(mpStatus.get(), SpellCheckStatus, EventHdl));
+ // Delimiters here like in inputhdl.cxx !!!
+ mpEngine->SetWordDelimiters(
+ ScEditUtil::ModifyDelimiters(mpEngine->GetWordDelimiters()));
+
+ uno::Reference<linguistic2::XSpellChecker1> xXSpellChecker1(LinguMgr::GetSpellChecker());
+ mpEngine->SetSpeller(xXSpellChecker1);
+ mpEngine->SetDefaultLanguage(meLanguage);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/spelldialog.cxx b/sc/source/ui/view/spelldialog.cxx
new file mode 100644
index 0000000000..da1e90698b
--- /dev/null
+++ b/sc/source/ui/view/spelldialog.cxx
@@ -0,0 +1,281 @@
+/* -*- 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 .
+ */
+
+#include <spelldialog.hxx>
+
+#include <sfx2/bindings.hxx>
+#include <svx/svxids.hrc>
+#include <editeng/editstat.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/unolingu.hxx>
+#include <selectionstate.hxx>
+#include <osl/diagnose.h>
+
+#include <spelleng.hxx>
+#include <tabvwsh.hxx>
+#include <docsh.hxx>
+#include <scmod.hxx>
+#include <editable.hxx>
+#include <undoblk.hxx>
+#include <gridwin.hxx>
+#include <refupdatecontext.hxx>
+#include <vcl/svapp.hxx>
+
+SFX_IMPL_CHILDWINDOW_WITHID( ScSpellDialogChildWindow, SID_SPELL_DIALOG )
+
+ScSpellDialogChildWindow::ScSpellDialogChildWindow( vcl::Window* pParentP, sal_uInt16 nId,
+ SfxBindings* pBindings, SAL_UNUSED_PARAMETER SfxChildWinInfo* /*pInfo*/ ) :
+ svx::SpellDialogChildWindow( pParentP, nId, pBindings ),
+ mpViewShell( nullptr ),
+ mpViewData( nullptr ),
+ mpDocShell( nullptr ),
+ mpDoc( nullptr ),
+ mbNeedNextObj( false ),
+ mbOldIdleEnabled(true)
+{
+ Init();
+}
+
+ScSpellDialogChildWindow::~ScSpellDialogChildWindow()
+{
+ Reset();
+}
+
+SfxChildWinInfo ScSpellDialogChildWindow::GetInfo() const
+{
+ return svx::SpellDialogChildWindow::GetInfo();
+}
+
+void ScSpellDialogChildWindow::InvalidateSpellDialog()
+{
+ svx::SpellDialogChildWindow::InvalidateSpellDialog();
+}
+
+// protected ------------------------------------------------------------------
+
+svx::SpellPortions ScSpellDialogChildWindow::GetNextWrongSentence( bool /*bRecheck*/ )
+{
+ svx::SpellPortions aPortions;
+ if( mxEngine && mpViewData )
+ {
+ if( EditView* pEditView = mpViewData->GetSpellingView() )
+ {
+ // edit engine handles cell iteration internally
+ do
+ {
+ if( mbNeedNextObj )
+ mxEngine->SpellNextDocument();
+ mbNeedNextObj = !mxEngine->IsFinished() && !mxEngine->SpellSentence( *pEditView, aPortions );
+ }
+ while( mbNeedNextObj );
+ }
+ }
+ return aPortions;
+}
+
+void ScSpellDialogChildWindow::ApplyChangedSentence( const svx::SpellPortions& rChanged, bool bRecheck )
+{
+ if( mxEngine && mpViewData )
+ if( EditView* pEditView = mpViewData->GetSpellingView() )
+ {
+ mxEngine->ApplyChangedSentence( *pEditView, rChanged, bRecheck );
+
+ // Reset the spell checking results to clear the markers.
+ mpViewData->GetActiveWin()->ResetAutoSpell();
+ }
+}
+
+void ScSpellDialogChildWindow::GetFocus()
+{
+ SolarMutexGuard aGuard;
+
+ if( IsSelectionChanged() )
+ {
+ Reset();
+ InvalidateSpellDialog();
+ Init();
+ }
+}
+
+void ScSpellDialogChildWindow::LoseFocus()
+{
+}
+
+// private --------------------------------------------------------------------
+
+void ScSpellDialogChildWindow::Reset()
+{
+ if( mpViewShell && (mpViewShell == dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() )) )
+ {
+ if( mxEngine && mxEngine->IsAnyModified() )
+ {
+ const ScAddress& rCursor = mxOldSel->GetCellCursor();
+ SCTAB nTab = rCursor.Tab();
+ SCCOL nOldCol = rCursor.Col();
+ SCROW nOldRow = rCursor.Row();
+ SCCOL nNewCol = mpViewData->GetCurX();
+ SCROW nNewRow = mpViewData->GetCurY();
+ mpDocShell->GetUndoManager()->AddUndoAction( std::make_unique<ScUndoConversion>(
+ mpDocShell, mpViewData->GetMarkData(),
+ nOldCol, nOldRow, nTab, std::move(mxUndoDoc),
+ nNewCol, nNewRow, nTab, std::move(mxRedoDoc),
+ ScConversionParam( SC_CONVERSION_SPELLCHECK ) ) );
+
+ sc::SetFormulaDirtyContext aCxt;
+ mpDoc->SetAllFormulasDirty(aCxt);
+
+ mpDocShell->SetDocumentModified();
+ }
+
+ mpViewData->SetSpellingView( nullptr );
+ mpViewShell->KillEditView( true );
+ mpDocShell->PostPaintGridAll();
+ mpViewShell->UpdateInputHandler();
+ mpDoc->EnableIdle(mbOldIdleEnabled);
+ }
+ mxEngine.reset();
+ mxUndoDoc.reset();
+ mxRedoDoc.reset();
+ mxOldSel.reset();
+ mxOldRangeList.clear();
+ mpViewShell = nullptr;
+ mpViewData = nullptr;
+ mpDocShell = nullptr;
+ mpDoc = nullptr;
+ mbNeedNextObj = false;
+ mbOldIdleEnabled = true;
+}
+
+void ScSpellDialogChildWindow::Init()
+{
+ if( mpViewShell )
+ return;
+ if( (mpViewShell = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() )) == nullptr )
+ return;
+
+ mpViewData = &mpViewShell->GetViewData();
+
+ // exit edit mode - TODO support spelling in edit mode
+ if( mpViewData->HasEditView( mpViewData->GetActivePart() ) )
+ SC_MOD()->InputEnterHandler();
+
+ mxOldSel.reset( new ScSelectionState( *mpViewData ) );
+
+ mpDocShell = mpViewData->GetDocShell();
+ mpDoc = &mpDocShell->GetDocument();
+
+ const ScAddress& rCursor = mxOldSel->GetCellCursor();
+ SCCOL nCol = rCursor.Col();
+ SCROW nRow = rCursor.Row();
+ SCTAB nTab = rCursor.Tab();
+
+ ScMarkData& rMarkData = mpViewData->GetMarkData();
+
+ mxOldRangeList = new ScRangeList;
+ rMarkData.FillRangeListWithMarks(mxOldRangeList.get(), true);
+
+ rMarkData.MarkToMulti();
+
+ switch( mxOldSel->GetSelectionType() )
+ {
+ case SC_SELECTTYPE_NONE:
+ case SC_SELECTTYPE_SHEET:
+ {
+ // test if there is something editable
+ ScEditableTester aTester( *mpDoc, rMarkData );
+ if( !aTester.IsEditable() )
+ {
+ // #i85751# Don't show an ErrorMessage here, because the vcl
+ // parent of the InfoBox is not fully initialized yet.
+ // This leads to problems in the modality behaviour of the
+ // ScSpellDialogChildWindow.
+
+ //mpViewShell->ErrorMessage( aTester.GetMessageId() );
+ return;
+ }
+ }
+ break;
+
+ // edit mode exited, see TODO above
+// case SC_SELECTTYPE_EDITCELL:
+// break;
+
+ default:
+ OSL_FAIL( "ScSpellDialogChildWindow::Init - unknown selection type" );
+ }
+
+ mbOldIdleEnabled = mpDoc->IsIdleEnabled();
+ mpDoc->EnableIdle(false); // stop online spelling
+
+ // *** create Undo/Redo documents *** -------------------------------------
+
+ mxUndoDoc.reset( new ScDocument( SCDOCMODE_UNDO ) );
+ mxUndoDoc->InitUndo( *mpDoc, nTab, nTab );
+ mxRedoDoc.reset( new ScDocument( SCDOCMODE_UNDO ) );
+ mxRedoDoc->InitUndo( *mpDoc, nTab, nTab );
+
+ if ( rMarkData.GetSelectCount() > 1 )
+ {
+ for (const auto& rTab : rMarkData)
+ {
+ if( rTab != nTab )
+ {
+ mxUndoDoc->AddUndoTab( rTab, rTab );
+ mxRedoDoc->AddUndoTab( rTab, rTab );
+ }
+ }
+ }
+
+ // *** create and init the edit engine *** --------------------------------
+
+ mxEngine.reset( new ScSpellingEngine(
+ mpDoc->GetEnginePool(), *mpViewData, mxUndoDoc.get(), mxRedoDoc.get(), LinguMgr::GetSpellChecker() ) );
+ mxEngine->SetRefDevice( mpViewData->GetActiveWin()->GetOutDev() );
+
+ mpViewShell->MakeEditView( mxEngine.get(), nCol, nRow );
+ EditView* pEditView = mpViewData->GetEditView( mpViewData->GetActivePart() );
+ mpViewData->SetSpellingView( pEditView );
+ tools::Rectangle aRect( Point( 0, 0 ), Point( 0, 0 ) );
+ pEditView->SetOutputArea( aRect );
+ mxEngine->SetControlWord( EEControlBits::USECHARATTRIBS );
+ mxEngine->EnableUndo( false );
+ mxEngine->SetPaperSize( aRect.GetSize() );
+ mxEngine->SetTextCurrentDefaults( OUString() );
+ mxEngine->ClearModifyFlag();
+
+ mbNeedNextObj = true;
+}
+
+bool ScSpellDialogChildWindow::IsSelectionChanged()
+{
+ if (!mxOldRangeList || !mpViewShell
+ || (mpViewShell != dynamic_cast<ScTabViewShell*>(SfxViewShell::Current())))
+ return true;
+
+ if( EditView* pEditView = mpViewData->GetSpellingView() )
+ if( pEditView->GetEditEngine() != mxEngine.get() )
+ return true;
+
+ ScRangeList aCurrentRangeList;
+ mpViewData->GetMarkData().FillRangeListWithMarks(&aCurrentRangeList, true);
+
+ return (*mxOldRangeList != aCurrentRangeList);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/spelleng.cxx b/sc/source/ui/view/spelleng.cxx
new file mode 100644
index 0000000000..ae50d82930
--- /dev/null
+++ b/sc/source/ui/view/spelleng.cxx
@@ -0,0 +1,448 @@
+/* -*- 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 .
+ */
+
+#include <spelleng.hxx>
+#include <com/sun/star/i18n/TextConversionOption.hpp>
+
+#include <scitems.hxx>
+
+#include <editeng/langitem.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/eeitem.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <utility>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <osl/diagnose.h>
+
+#include <spelldialog.hxx>
+#include <tabvwsh.hxx>
+#include <docsh.hxx>
+#include <cellvalue.hxx>
+#include <cellform.hxx>
+#include <patattr.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <markdata.hxx>
+#include <docpool.hxx>
+
+#include <memory>
+
+using namespace ::com::sun::star;
+
+ScConversionEngineBase::ScConversionEngineBase(
+ SfxItemPool* pEnginePoolP, ScViewData& rViewData,
+ ScDocument* pUndoDoc, ScDocument* pRedoDoc ) :
+ ScEditEngineDefaulter( pEnginePoolP ),
+ mrViewData( rViewData ),
+ mrDocShell( *rViewData.GetDocShell() ),
+ mrDoc( rViewData.GetDocShell()->GetDocument() ),
+ maSelState( rViewData ),
+ mpUndoDoc( pUndoDoc ),
+ mpRedoDoc( pRedoDoc ),
+ meCurrLang( LANGUAGE_ENGLISH_US ),
+ mbIsAnyModified( false ),
+ mbInitialState( true ),
+ mbWrappedInTable( false ),
+ mbFinished( false )
+{
+ maSelState.GetCellCursor().GetVars( mnStartCol, mnStartRow, mnStartTab );
+ // start with cell A1 in cell/range/multi-selection, will seek to first selected
+ if( maSelState.GetSelectionType() == SC_SELECTTYPE_SHEET )
+ {
+ mnStartCol = 0;
+ mnStartRow = 0;
+ }
+ mnCurrCol = mnStartCol;
+ mnCurrRow = mnStartRow;
+}
+
+ScConversionEngineBase::~ScConversionEngineBase()
+{
+}
+
+bool ScConversionEngineBase::FindNextConversionCell()
+{
+ ScMarkData& rMark = mrViewData.GetMarkData();
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+ const ScPatternAttr* pPattern = nullptr;
+ const ScPatternAttr* pLastPattern = nullptr;
+
+ SfxItemSet aEditDefaults(GetEmptyItemSet());
+
+ if( IsModified() )
+ {
+ mbIsAnyModified = true;
+
+ OUString aNewStr = GetText();
+
+ // Check if the user has changed the language. If the new language is
+ // applied to the entire string length, we will set the language as cell
+ // attribute. Otherwise we will commit this as an edit-engine string.
+ editeng::LanguageSpan aLang = GetLanguage(0, 0);
+
+ bool bSimpleString = GetParagraphCount() == 1 &&
+ aLang.nLang != LANGUAGE_DONTKNOW &&
+ aLang.nStart == 0 &&
+ aLang.nEnd == aNewStr.getLength();
+
+ bool bMultiTab = (rMark.GetSelectCount() > 1);
+
+ OUString aVisibleStr;
+ if( bMultiTab )
+ aVisibleStr = mrDoc.GetString(mnCurrCol, mnCurrRow, mnStartTab);
+
+ for( SCTAB nTab = 0, nTabCount = mrDoc.GetTableCount(); nTab < nTabCount; ++nTab )
+ {
+ // always change the cell on the visible tab,
+ // on the other selected tabs only if they contain the same text
+
+ if ((nTab == mnStartTab) ||
+ (bMultiTab && rMark.GetTableSelect(nTab) && mrDoc.GetString(mnCurrCol, mnCurrRow, nTab) == aVisibleStr))
+ {
+ ScAddress aPos( mnCurrCol, mnCurrRow, nTab );
+ CellType eCellType = mrDoc.GetCellType( aPos );
+ bool bEmptyCell = eCellType == CELLTYPE_NONE;
+
+ if (mpUndoDoc && !bEmptyCell)
+ mrDoc.CopyCellToDocument(aPos, aPos, *mpUndoDoc);
+
+ if (!bSimpleString || eCellType == CELLTYPE_EDIT)
+ {
+ std::unique_ptr<EditTextObject> pEditObj(CreateTextObject());
+ mrDoc.SetEditText(aPos, *pEditObj, GetEditTextObjectPool());
+ }
+ else
+ {
+ // Set the new string and update the language with the cell.
+ mrDoc.SetString(aPos, aNewStr);
+
+ const ScPatternAttr* pAttr = mrDoc.GetPattern(aPos);
+ std::unique_ptr<ScPatternAttr> pNewAttr;
+
+ if (pAttr)
+ pNewAttr = std::make_unique<ScPatternAttr>(*pAttr);
+ else
+ pNewAttr = std::make_unique<ScPatternAttr>(mrDoc.GetPool());
+
+ pNewAttr->GetItemSet().Put(SvxLanguageItem(aLang.nLang, EE_CHAR_LANGUAGE), ATTR_FONT_LANGUAGE);
+ mrDoc.SetPattern(aPos, std::move(pNewAttr));
+ }
+
+ if (mpRedoDoc && !bEmptyCell)
+ mrDoc.CopyCellToDocument(aPos, aPos, *mpRedoDoc);
+
+ mrDocShell.PostPaintCell(aPos);
+ }
+ }
+ }
+
+ SCCOL nNewCol = mnCurrCol;
+ SCROW nNewRow = mnCurrRow;
+
+ if( mbInitialState )
+ {
+ /* On very first call, decrement row to let GetNextSpellingCell() find
+ the first cell of current range. */
+ mbInitialState = false;
+ --nNewRow;
+ }
+
+ bool bSheetSel = maSelState.GetSelectionType() == SC_SELECTTYPE_SHEET;
+ bool bLoop = true;
+ bool bFound = false;
+ while( bLoop && !bFound )
+ {
+ bLoop = mrDoc.GetNextSpellingCell( nNewCol, nNewRow, mnStartTab, bSheetSel, rMark );
+ if( bLoop )
+ {
+ FillFromCell( mnCurrCol, mnCurrRow, mnStartTab );
+
+ if( mbWrappedInTable && ((nNewCol > mnStartCol) || ((nNewCol == mnStartCol) && (nNewRow >= mnStartRow))) )
+ {
+ ShowFinishDialog();
+ bLoop = false;
+ mbFinished = true;
+ }
+ else if( nNewCol >= mrDoc.GetAllocatedColumnsCount(mnStartTab) )
+ {
+ // no more cells in the sheet - try to restart at top of sheet
+
+ if( bSheetSel || ((mnStartCol == 0) && (mnStartRow == 0)) )
+ {
+ // conversion started at cell A1 or in selection, do not query to restart at top
+ ShowFinishDialog();
+ bLoop = false;
+ mbFinished = true;
+ }
+ else if( ShowTableWrapDialog() )
+ {
+ // conversion started anywhere but in cell A1, user wants to restart
+ nNewRow = mrDoc.MaxRow() + 2;
+ mbWrappedInTable = true;
+ }
+ else
+ {
+ bLoop = false;
+ mbFinished = true;
+ }
+ }
+ else
+ {
+ // GetPattern may implicitly allocates the column if not exists,
+ pPattern = mrDoc.GetPattern( nNewCol, nNewRow, mnStartTab );
+ if( pPattern && !SfxPoolItem::areSame(pPattern, pLastPattern) )
+ {
+ pPattern->FillEditItemSet( &aEditDefaults );
+ SetDefaults( aEditDefaults );
+ pLastPattern = pPattern;
+ }
+
+ // language changed?
+ const SfxPoolItem* pItem = mrDoc.GetAttr( nNewCol, nNewRow, mnStartTab, ATTR_FONT_LANGUAGE );
+ if( const SvxLanguageItem* pLangItem = dynamic_cast<const SvxLanguageItem*>( pItem ) )
+ {
+ LanguageType eLang = pLangItem->GetValue();
+ if( eLang == LANGUAGE_SYSTEM )
+ eLang = Application::GetSettings().GetLanguageTag().getLanguageType(); // never use SYSTEM for spelling
+ if( eLang != meCurrLang )
+ {
+ meCurrLang = eLang;
+ SetDefaultLanguage( eLang );
+ }
+ }
+
+ FillFromCell( nNewCol, nNewRow, mnStartTab );
+
+ bFound = bLoop && NeedsConversion();
+ }
+ }
+ }
+
+ if( bFound )
+ {
+ pViewShell->AlignToCursor( nNewCol, nNewRow, SC_FOLLOW_JUMP );
+ pViewShell->SetCursor( nNewCol, nNewRow, true );
+ mrViewData.GetView()->MakeEditView( this, nNewCol, nNewRow );
+ EditView* pEditView = mrViewData.GetSpellingView();
+ // maSelState.GetEditSelection() returns (0,0) if not in edit mode -> ok
+ pEditView->SetSelection( maSelState.GetEditSelection() );
+
+ ClearModifyFlag();
+ mnCurrCol = nNewCol;
+ mnCurrRow = nNewRow;
+ }
+
+ return bFound;
+}
+
+void ScConversionEngineBase::RestoreCursorPos()
+{
+ const ScAddress& rPos = maSelState.GetCellCursor();
+ mrViewData.GetViewShell()->SetCursor( rPos.Col(), rPos.Row() );
+}
+
+bool ScConversionEngineBase::ShowTableWrapDialog()
+{
+ // default: no dialog, always restart at top
+ return true;
+}
+
+void ScConversionEngineBase::ShowFinishDialog()
+{
+ // default: no dialog
+}
+
+// private --------------------------------------------------------------------
+
+void ScConversionEngineBase::FillFromCell( SCCOL nCol, SCROW nRow, SCTAB nTab )
+{
+ ScAddress aPos(nCol, nRow, nTab);
+
+ ScRefCellValue aCell(mrDoc, aPos);
+ switch (aCell.getType())
+ {
+ case CELLTYPE_STRING:
+ {
+ SvNumberFormatter* pFormatter = mrDoc.GetFormatTable();
+ sal_uInt32 nNumFmt = mrDoc.GetNumberFormat(aPos);
+ const Color* pColor;
+ OUString aText = ScCellFormat::GetString(aCell, nNumFmt, &pColor, *pFormatter, mrDoc);
+
+ SetTextCurrentDefaults(aText);
+ }
+ break;
+ case CELLTYPE_EDIT:
+ {
+ const EditTextObject* pNewEditObj = aCell.getEditText();
+ SetTextCurrentDefaults(*pNewEditObj);
+ }
+ break;
+ default:
+ SetTextCurrentDefaults(OUString());
+ }
+}
+
+ScSpellingEngine::ScSpellingEngine(
+ SfxItemPool* pEnginePoolP, ScViewData& rViewData,
+ ScDocument* pUndoDoc, ScDocument* pRedoDoc,
+ css::uno::Reference< css::linguistic2::XSpellChecker1 > const & xSpeller ) :
+ ScConversionEngineBase( pEnginePoolP, rViewData, pUndoDoc, pRedoDoc )
+{
+ SetSpeller( xSpeller );
+}
+
+void ScSpellingEngine::ConvertAll(weld::Widget* pDialogParent, EditView& rEditView)
+{
+ EESpellState eState = EESpellState::Ok;
+ if( FindNextConversionCell() )
+ eState = rEditView.StartSpeller(pDialogParent, true);
+
+ OSL_ENSURE( eState != EESpellState::NoSpeller, "ScSpellingEngine::Convert - no spell checker" );
+}
+
+bool ScSpellingEngine::SpellNextDocument()
+{
+ return FindNextConversionCell();
+}
+
+bool ScSpellingEngine::NeedsConversion()
+{
+ return HasSpellErrors() != EESpellState::Ok;
+}
+
+bool ScSpellingEngine::ShowTableWrapDialog()
+{
+ weld::Widget* pParent = GetDialogParent();
+ weld::WaitObject aWaitOff(pParent);
+
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pParent,
+ VclMessageType::Question, VclButtonsType::YesNo,
+ ScResId(STR_SPELLING_BEGIN_TAB))); // "delete data?"
+ xBox->set_title(ScResId(STR_MSSG_DOSUBTOTALS_0));
+ xBox->set_default_response(RET_YES);
+ return xBox->run() == RET_YES;
+}
+
+void ScSpellingEngine::ShowFinishDialog()
+{
+ weld::Widget* pParent = GetDialogParent();
+ weld::WaitObject aWaitOff(pParent);
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(pParent,
+ VclMessageType::Info, VclButtonsType::Ok,
+ ScResId(STR_SPELLING_STOP_OK)));
+ xInfoBox->run();
+}
+
+weld::Widget* ScSpellingEngine::GetDialogParent()
+{
+ sal_uInt16 nWinId = ScSpellDialogChildWindow::GetChildWindowId();
+ SfxViewFrame& rViewFrm = mrViewData.GetViewShell()->GetViewFrame();
+ if( rViewFrm.HasChildWindow( nWinId ) )
+ {
+ if( SfxChildWindow* pChild = rViewFrm.GetChildWindow( nWinId ) )
+ {
+ auto xController = pChild->GetController();
+ if (xController)
+ {
+ if (weld::Window *pRet = xController->getDialog())
+ {
+ if (pRet->get_visible())
+ return pRet;
+ }
+ }
+ }
+ }
+
+ // fall back to standard dialog parent
+ return ScDocShell::GetActiveDialogParent();
+}
+
+ScConversionParam::ScConversionParam( ScConversionType eConvType ) :
+ meConvType( eConvType ),
+ meSourceLang( LANGUAGE_NONE ),
+ meTargetLang( LANGUAGE_NONE ),
+ mnOptions( 0 ),
+ mbUseTargetFont( false ),
+ mbIsInteractive( false )
+{
+}
+
+ScConversionParam::ScConversionParam( ScConversionType eConvType,
+ LanguageType eLang, sal_Int32 nOptions, bool bIsInteractive ) :
+ meConvType( eConvType ),
+ meSourceLang( eLang ),
+ meTargetLang( eLang ),
+ mnOptions( nOptions ),
+ mbUseTargetFont( false ),
+ mbIsInteractive( bIsInteractive )
+{
+ if (LANGUAGE_KOREAN == eLang)
+ mnOptions = i18n::TextConversionOption::CHARACTER_BY_CHARACTER;
+}
+
+ScConversionParam::ScConversionParam( ScConversionType eConvType,
+ LanguageType eSourceLang, LanguageType eTargetLang, vcl::Font aTargetFont,
+ sal_Int32 nOptions, bool bIsInteractive ) :
+ meConvType( eConvType ),
+ meSourceLang( eSourceLang ),
+ meTargetLang( eTargetLang ),
+ maTargetFont(std::move( aTargetFont )),
+ mnOptions( nOptions ),
+ mbUseTargetFont( true ),
+ mbIsInteractive( bIsInteractive )
+{
+ if (LANGUAGE_KOREAN == meSourceLang && LANGUAGE_KOREAN == meTargetLang)
+ mnOptions = i18n::TextConversionOption::CHARACTER_BY_CHARACTER;
+}
+
+ScTextConversionEngine::ScTextConversionEngine(
+ SfxItemPool* pEnginePoolP, ScViewData& rViewData,
+ ScConversionParam aConvParam,
+ ScDocument* pUndoDoc, ScDocument* pRedoDoc ) :
+ ScConversionEngineBase( pEnginePoolP, rViewData, pUndoDoc, pRedoDoc ),
+ maConvParam(std::move( aConvParam ))
+{
+}
+
+void ScTextConversionEngine::ConvertAll(weld::Widget* pDialogParent, EditView& rEditView)
+{
+ if( FindNextConversionCell() )
+ {
+ rEditView.StartTextConversion(pDialogParent,
+ maConvParam.GetSourceLang(), maConvParam.GetTargetLang(), maConvParam.GetTargetFont(),
+ maConvParam.GetOptions(), maConvParam.IsInteractive(), true );
+ // #i34769# restore initial cursor position
+ RestoreCursorPos();
+ }
+}
+
+bool ScTextConversionEngine::ConvertNextDocument()
+{
+ return FindNextConversionCell();
+}
+
+bool ScTextConversionEngine::NeedsConversion()
+{
+ return HasConvertibleTextPortion( maConvParam.GetSourceLang() );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabcont.cxx b/sc/source/ui/view/tabcont.cxx
new file mode 100644
index 0000000000..12fc1a7a6c
--- /dev/null
+++ b/sc/source/ui/view/tabcont.cxx
@@ -0,0 +1,666 @@
+/* -*- 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 .
+ */
+
+#include <osl/diagnose.h>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/docfile.hxx>
+#include <tools/urlobj.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weldutils.hxx>
+#include <tabcont.hxx>
+#include <tabvwsh.hxx>
+#include <docsh.hxx>
+#include <scmod.hxx>
+#include <sc.hrc>
+#include <globstr.hrc>
+#include <transobj.hxx>
+#include <clipparam.hxx>
+#include <dragdata.hxx>
+#include <markdata.hxx>
+#include <gridwin.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <comphelper/lok.hxx>
+
+ScTabControl::ScTabControl( vcl::Window* pParent, ScViewData* pData )
+ : TabBar(pParent, WB_3DLOOK | WB_MINSCROLL | WB_SCROLL | WB_RANGESELECT | WB_MULTISELECT | WB_DRAG, true)
+ , DropTargetHelper(this)
+ , DragSourceHelper(this)
+ , pViewData(pData)
+ , nMouseClickPageId(TabBar::PAGE_NOT_FOUND)
+ , nSelPageIdByMouse(TabBar::PAGE_NOT_FOUND)
+ , bErrorShown(false)
+{
+ ScDocument& rDoc = pViewData->GetDocument();
+
+ OUString aString;
+ Color aTabBgColor;
+ SCTAB nCount = rDoc.GetTableCount();
+ for (SCTAB i=0; i<nCount; i++)
+ {
+ if (rDoc.IsVisible(i))
+ {
+ if (rDoc.GetName(i,aString))
+ {
+ if ( rDoc.IsScenario(i) )
+ InsertPage( static_cast<sal_uInt16>(i)+1, aString, TabBarPageBits::Blue);
+ else
+ InsertPage( static_cast<sal_uInt16>(i)+1, aString );
+
+ if ( rDoc.IsTabProtected(i) )
+ SetProtectionSymbol(static_cast<sal_uInt16>(i)+1, true);
+
+ if ( !rDoc.IsDefaultTabBgColor(i) )
+ {
+ aTabBgColor = rDoc.GetTabBgColor(i);
+ SetTabBgColor( static_cast<sal_uInt16>(i)+1, aTabBgColor );
+ }
+ }
+ }
+ }
+
+ SetCurPageId( static_cast<sal_uInt16>(pViewData->GetTabNo()) + 1 );
+
+ SetSizePixel( Size(SC_TABBAR_DEFWIDTH, 0) );
+
+ SetSplitHdl( LINK( pViewData->GetView(), ScTabView, TabBarResize ) );
+
+ EnableEditMode();
+ UpdateInputContext();
+
+ SetScrollAlwaysEnabled(false);
+
+ SetScrollAreaContextHdl( LINK( this, ScTabControl, ShowPageList ) );
+}
+
+IMPL_LINK(ScTabControl, ShowPageList, const CommandEvent &, rEvent, void)
+{
+ tools::Rectangle aRect(rEvent.GetMousePosPixel(), Size(1, 1));
+ weld::Window* pPopupParent = weld::GetPopupParent(*this, aRect);
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(pPopupParent, "modules/scalc/ui/pagelistmenu.ui"));
+ std::unique_ptr<weld::Menu> xPopup(xBuilder->weld_menu("menu"));
+
+ sal_uInt16 nCurPageId = GetCurPageId();
+
+ ScDocument& rDoc = pViewData->GetDocument();
+ SCTAB nCount = rDoc.GetTableCount();
+ for (SCTAB i=0; i<nCount; ++i)
+ {
+ if (!rDoc.IsVisible(i))
+ continue;
+ OUString aString;
+ if (!rDoc.GetName(i, aString))
+ continue;
+ sal_uInt16 nId = static_cast<sal_uInt16>(i)+1;
+ OUString sId = OUString::number(nId);
+ xPopup->append_radio(sId, aString);
+ if (nId == nCurPageId)
+ xPopup->set_active(sId, true);
+ }
+
+ OUString sIdent(xPopup->popup_at_rect(pPopupParent, aRect));
+ if (!sIdent.isEmpty())
+ SwitchToPageId(sIdent.toUInt32());
+}
+
+ScTabControl::~ScTabControl()
+{
+ disposeOnce();
+}
+
+void ScTabControl::dispose()
+{
+ DragSourceHelper::dispose();
+ DropTargetHelper::dispose();
+ TabBar::dispose();
+}
+
+sal_uInt16 ScTabControl::GetMaxId() const
+{
+ sal_uInt16 nVisCnt = GetPageCount();
+ if (nVisCnt)
+ return GetPageId(nVisCnt-1);
+
+ return 0;
+}
+
+SCTAB ScTabControl::GetPrivatDropPos(const Point& rPos )
+{
+ sal_uInt16 nPos = ShowDropPos(rPos);
+
+ SCTAB nRealPos = static_cast<SCTAB>(nPos);
+
+ if(nPos !=0 )
+ {
+ ScDocument& rDoc = pViewData->GetDocument();
+
+ SCTAB nCount = rDoc.GetTableCount();
+
+ sal_uInt16 nViewPos=0;
+ nRealPos = nCount;
+ for (SCTAB i=0; i<nCount; i++)
+ {
+ if (rDoc.IsVisible(i))
+ {
+ nViewPos++;
+ if(nViewPos==nPos)
+ {
+ SCTAB j;
+ for (j=i+1; j<nCount; j++)
+ {
+ if (rDoc.IsVisible(j))
+ {
+ break;
+ }
+ }
+ nRealPos =j;
+ break;
+ }
+ }
+ }
+ }
+ return nRealPos ;
+}
+
+void ScTabControl::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ ScModule* pScMod = SC_MOD();
+ if ( !pScMod->IsModalMode() && !pScMod->IsFormulaMode() && !IsInEditMode() )
+ {
+ // activate View
+ pViewData->GetViewShell()->SetActive(); // Appear and SetViewFrame
+ pViewData->GetView()->ActiveGrabFocus();
+ }
+
+ if (rMEvt.IsLeft() && rMEvt.GetModifier() == 0)
+ nMouseClickPageId = GetPageId(rMEvt.GetPosPixel());
+
+ TabBar::MouseButtonDown( rMEvt );
+}
+
+void ScTabControl::MouseButtonUp( const MouseEvent& rMEvt )
+{
+ Point aPos = PixelToLogic( rMEvt.GetPosPixel() );
+
+ // mouse button down and up on same page?
+ if( nMouseClickPageId != GetPageId(aPos))
+ nMouseClickPageId = TabBar::PAGE_NOT_FOUND;
+
+ if ( rMEvt.GetClicks() == 2 && rMEvt.IsLeft() && nMouseClickPageId != 0 && nMouseClickPageId != TabBar::PAGE_NOT_FOUND )
+ {
+ SfxDispatcher* pDispatcher = pViewData->GetViewShell()->GetViewFrame().GetDispatcher();
+ pDispatcher->Execute( FID_TAB_MENU_RENAME, SfxCallMode::SYNCHRON | SfxCallMode::RECORD );
+ return;
+ }
+
+ if( nMouseClickPageId == 0 )
+ {
+ // Click in the area next to the existing tabs:
+ SfxDispatcher* pDispatcher = pViewData->GetViewShell()->GetViewFrame().GetDispatcher();
+ pDispatcher->Execute( FID_TAB_DESELECTALL, SfxCallMode::SYNCHRON | SfxCallMode::RECORD );
+ // forget page ID, to be really sure that the dialog is not called twice
+ nMouseClickPageId = TabBar::PAGE_NOT_FOUND;
+ }
+
+ TabBar::MouseButtonUp( rMEvt );
+}
+
+void ScTabControl::AddTabClick()
+{
+ TabBar::AddTabClick();
+
+ // Insert a new sheet at the right end, with default name.
+ ScDocument& rDoc = pViewData->GetDocument();
+ ScModule* pScMod = SC_MOD();
+ if (!rDoc.IsDocEditable() || pScMod->IsTableLocked())
+ return;
+
+ // auto-accept any in-process input - which would otherwise end up on the new sheet
+ if (!pScMod->IsFormulaMode())
+ pScMod->InputEnterHandler();
+
+ OUString aName;
+ rDoc.CreateValidTabName(aName);
+ SCTAB nTabCount = rDoc.GetTableCount();
+ pViewData->GetViewShell()->InsertTable(aName, nTabCount);
+}
+
+void ScTabControl::Select()
+{
+ /* Remember last clicked page ID. */
+ nSelPageIdByMouse = nMouseClickPageId;
+ /* Reset nMouseClickPageId, so that next Select() call may invalidate
+ nSelPageIdByMouse (i.e. if called from keyboard). */
+ nMouseClickPageId = TabBar::PAGE_NOT_FOUND;
+
+ ScModule* pScMod = SC_MOD();
+ ScDocument& rDoc = pViewData->GetDocument();
+ ScMarkData& rMark = pViewData->GetMarkData();
+ SCTAB nCount = rDoc.GetTableCount();
+ SCTAB i;
+
+ if ( pScMod->IsTableLocked() ) // may not be switched now ?
+ {
+ // restore the old state of TabControls
+
+ for (i=0; i<nCount; i++)
+ SelectPage( static_cast<sal_uInt16>(i)+1, rMark.GetTableSelect(i) );
+ SetCurPageId( static_cast<sal_uInt16>(pViewData->GetTabNo()) + 1 );
+
+ return;
+ }
+
+ sal_uInt16 nCurId = GetCurPageId();
+ if (!nCurId) return; // for Excel import it can happen that everything is hidden
+ sal_uInt16 nPage = nCurId - 1;
+
+ // OLE-inplace deactivate
+ if ( nPage != static_cast<sal_uInt16>(pViewData->GetTabNo()) )
+ pViewData->GetView()->DrawMarkListHasChanged();
+
+ // InputEnterHandler onlw when not reference input
+
+ bool bRefMode = pScMod->IsFormulaMode();
+ if (!bRefMode)
+ pScMod->InputEnterHandler();
+
+ for (i=0; i<nCount; i++)
+ rMark.SelectTable( i, IsPageSelected(static_cast<sal_uInt16>(i)+1) );
+
+ SfxDispatcher& rDisp = pViewData->GetDispatcher();
+ if (rDisp.IsLocked())
+ pViewData->GetView()->SetTabNo( static_cast<SCTAB>(nPage) );
+ else
+ {
+ // sheet for basic is 1-based
+ SfxUInt16Item aItem( SID_CURRENTTAB, nPage + 1 );
+ rDisp.ExecuteList(SID_CURRENTTAB,
+ SfxCallMode::SLOT | SfxCallMode::RECORD, { &aItem });
+ }
+
+ SfxBindings& rBind = pViewData->GetBindings();
+ rBind.Invalidate( FID_FILL_TAB );
+ rBind.Invalidate( FID_TAB_DESELECTALL );
+
+ rBind.Invalidate( FID_INS_TABLE );
+ rBind.Invalidate( FID_TAB_APPEND );
+ rBind.Invalidate( FID_TAB_MOVE );
+ rBind.Invalidate( FID_TAB_DUPLICATE );
+ rBind.Invalidate( FID_TAB_RENAME );
+ rBind.Invalidate( FID_DELETE_TABLE );
+ rBind.Invalidate( FID_TABLE_SHOW );
+ rBind.Invalidate( FID_TABLE_HIDE );
+ rBind.Invalidate( FID_TAB_SET_TAB_BG_COLOR );
+
+ // Recalculate status bar functions.
+ rBind.Invalidate( SID_TABLE_CELL );
+
+ // SetReference onlw when the consolidate dialog is open
+ // (for references over multiple sheets)
+ // for others this is only needed fidgeting
+
+ if ( bRefMode && pViewData->GetRefType() == SC_REFTYPE_REF )
+ if ( pViewData->GetViewShell()->GetViewFrame().HasChildWindow(SID_OPENDLG_CONSOLIDATE) )
+ {
+ ScRange aRange(
+ pViewData->GetRefStartX(), pViewData->GetRefStartY(), pViewData->GetRefStartZ(),
+ pViewData->GetRefEndX(), pViewData->GetRefEndY(), pViewData->GetRefEndZ() );
+ pScMod->SetReference( aRange, rDoc, &rMark );
+ pScMod->EndReference(); // due to Auto-Hide
+ }
+}
+
+void ScTabControl::UpdateInputContext()
+{
+ ScDocument& rDoc = pViewData->GetDocument();
+ WinBits nStyle = GetStyle();
+ if (rDoc.GetDocumentShell()->IsReadOnly())
+ // no insert sheet tab for readonly doc.
+ SetStyle(nStyle & ~WB_INSERTTAB);
+ else
+ SetStyle(nStyle | WB_INSERTTAB);
+}
+
+void ScTabControl::UpdateStatus()
+{
+ ScDocument& rDoc = pViewData->GetDocument();
+ ScMarkData& rMark = pViewData->GetMarkData();
+ bool bActive = pViewData->IsActive();
+
+ SCTAB nCount = rDoc.GetTableCount();
+ SCTAB i;
+ OUString aString;
+ SCTAB nMaxCnt = std::max( nCount, static_cast<SCTAB>(GetMaxId()) );
+ Color aTabBgColor;
+
+ bool bModified = false; // sheet name
+ for (i=0; i<nMaxCnt && !bModified; i++)
+ {
+ if (rDoc.IsVisible(i))
+ {
+ rDoc.GetName(i,aString);
+ aTabBgColor = rDoc.GetTabBgColor(i);
+ }
+ else
+ {
+ aString.clear();
+ }
+
+ if ( aString != GetPageText(static_cast<sal_uInt16>(i)+1) || (GetTabBgColor(static_cast<sal_uInt16>(i)+1) != aTabBgColor) )
+ bModified = true;
+ }
+
+ if (bModified)
+ {
+ Clear();
+ for (i=0; i<nCount; i++)
+ {
+ if (rDoc.IsVisible(i))
+ {
+ if (rDoc.GetName(i,aString))
+ {
+ if ( rDoc.IsScenario(i) )
+ InsertPage(static_cast<sal_uInt16>(i)+1, aString, TabBarPageBits::Blue);
+ else
+ InsertPage( static_cast<sal_uInt16>(i)+1, aString );
+
+ if ( rDoc.IsTabProtected(i) )
+ SetProtectionSymbol(static_cast<sal_uInt16>(i)+1, true);
+
+ if ( !rDoc.IsDefaultTabBgColor(i) )
+ {
+ aTabBgColor = rDoc.GetTabBgColor(i);
+ SetTabBgColor(static_cast<sal_uInt16>(i)+1, aTabBgColor );
+ }
+ }
+ }
+ }
+ }
+ SetCurPageId( static_cast<sal_uInt16>(pViewData->GetTabNo()) + 1 );
+
+ if (bActive)
+ {
+ bModified = false; // selection
+ for (i=0; i<nMaxCnt && !bModified; i++)
+ if ( rMark.GetTableSelect(i) != IsPageSelected(static_cast<sal_uInt16>(i)+1) )
+ bModified = true;
+
+ if ( bModified )
+ for (i=0; i<nCount; i++)
+ SelectPage( static_cast<sal_uInt16>(i)+1, rMark.GetTableSelect(i) );
+ }
+}
+
+void ScTabControl::SetSheetLayoutRTL( bool bSheetRTL )
+{
+ SetEffectiveRTL( bSheetRTL );
+ nSelPageIdByMouse = TabBar::PAGE_NOT_FOUND;
+}
+
+void ScTabControl::SwitchToPageId(sal_uInt16 nId)
+{
+ if (!nId)
+ return;
+
+ bool bAlreadySelected = IsPageSelected( nId );
+ //make the clicked page the current one
+ SetCurPageId( nId );
+ //change the selection when the current one is not already
+ //selected or part of a multi selection
+ if(bAlreadySelected)
+ return;
+
+ sal_uInt16 nCount = GetMaxId();
+
+ for (sal_uInt16 i=1; i<=nCount; i++)
+ SelectPage( i, i==nId );
+ Select();
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ // notify LibreOfficeKit about changed page
+ OString aPayload = OString::number(nId - 1);
+ pViewData->GetViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_SET_PART, aPayload);
+ }
+}
+
+void ScTabControl::Command( const CommandEvent& rCEvt )
+{
+ ScModule* pScMod = SC_MOD();
+ ScTabViewShell* pViewSh = pViewData->GetViewShell();
+ bool bDisable = pScMod->IsFormulaMode() || pScMod->IsModalMode();
+
+ // first activate ViewFrame (Bug 19493):
+ pViewSh->SetActive();
+
+ if (rCEvt.GetCommand() != CommandEventId::ContextMenu || bDisable)
+ return;
+
+ // #i18735# select the page that is under the mouse cursor
+ // if multiple tables are selected and the one under the cursor
+ // is not part of them then unselect them
+ sal_uInt16 nId = GetPageId( rCEvt.GetMousePosPixel() );
+ SwitchToPageId(nId);
+
+ // #i52073# OLE inplace editing has to be stopped before showing the sheet tab context menu
+ pViewSh->DeactivateOle();
+
+ // Popup-Menu:
+ // get Dispatcher from ViewData (ViewFrame) instead of Shell (Frame), so it can't be null
+ pViewData->GetDispatcher().ExecutePopup( "sheettab" );
+}
+
+void ScTabControl::StartDrag( sal_Int8 /* nAction */, const Point& rPosPixel )
+{
+ ScModule* pScMod = SC_MOD();
+ bool bDisable = pScMod->IsFormulaMode() || pScMod->IsModalMode();
+
+ if (!bDisable)
+ {
+ vcl::Region aRegion( tools::Rectangle(0,0,0,0) );
+ CommandEvent aCEvt( rPosPixel, CommandEventId::StartDrag, true ); // needed for StartDrag
+ if (TabBar::StartDrag( aCEvt, aRegion ))
+ DoDrag();
+ }
+}
+
+void ScTabControl::DoDrag()
+{
+ ScDocShell* pDocSh = pViewData->GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+
+ SCTAB nTab = pViewData->GetTabNo();
+ ScRange aTabRange( 0, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab );
+ ScMarkData aTabMark = pViewData->GetMarkData();
+ aTabMark.ResetMark(); // doesn't change marked table information
+ aTabMark.SetMarkArea( aTabRange );
+
+ ScDocumentUniquePtr pClipDoc(new ScDocument( SCDOCMODE_CLIP ));
+ ScClipParam aClipParam(aTabRange, false);
+ rDoc.CopyToClip(aClipParam, pClipDoc.get(), &aTabMark, false, false);
+
+ TransferableObjectDescriptor aObjDesc;
+ pDocSh->FillTransferableObjectDescriptor( aObjDesc );
+ aObjDesc.maDisplayName = pDocSh->GetMedium()->GetURLObject().GetURLNoPass();
+ // maSize is set in ScTransferObj ctor
+
+ rtl::Reference<ScTransferObj> pTransferObj = new ScTransferObj( std::move(pClipDoc), std::move(aObjDesc) );
+
+ pTransferObj->SetDragSourceFlags(ScDragSrc::Table);
+
+ pTransferObj->SetDragSource( pDocSh, aTabMark );
+
+ pTransferObj->SetSourceCursorPos( pViewData->GetCurX(), pViewData->GetCurY() );
+
+ vcl::Window* pWindow = pViewData->GetActiveWin();
+ SC_MOD()->SetDragObject( pTransferObj.get(), nullptr ); // for internal D&D
+ pTransferObj->StartDrag( pWindow, DND_ACTION_COPYMOVE | DND_ACTION_LINK );
+}
+
+static sal_uInt16 lcl_DocShellNr( const ScDocument& rDoc )
+{
+ sal_uInt16 nShellCnt = 0;
+ SfxObjectShell* pShell = SfxObjectShell::GetFirst();
+ while ( pShell )
+ {
+ if ( auto pDocShell = dynamic_cast<const ScDocShell *>(pShell) )
+ {
+ if ( &pDocShell->GetDocument() == &rDoc )
+ return nShellCnt;
+
+ ++nShellCnt;
+ }
+ pShell = SfxObjectShell::GetNext( *pShell );
+ }
+
+ OSL_FAIL("Document not found");
+ return 0;
+}
+
+sal_Int8 ScTabControl::ExecuteDrop( const ExecuteDropEvent& rEvt )
+{
+ EndSwitchPage();
+
+ ScDocument& rDoc = pViewData->GetDocument();
+ const ScDragData& rData = SC_MOD()->GetDragData();
+ if ( rData.pCellTransfer && (rData.pCellTransfer->GetDragSourceFlags() & ScDragSrc::Table) &&
+ rData.pCellTransfer->GetSourceDocument() == &rDoc )
+ {
+ // moving of tables within the document
+ SCTAB nPos = GetPrivatDropPos( rEvt.maPosPixel );
+ HideDropPos();
+
+ if ( nPos == rData.pCellTransfer->GetVisibleTab() && rEvt.mnAction == DND_ACTION_MOVE )
+ {
+ // #i83005# do nothing - don't move to the same position
+ // (too easily triggered unintentionally, and might take a long time in large documents)
+ }
+ else
+ {
+ if ( !rDoc.GetChangeTrack() && rDoc.IsDocEditable() )
+ {
+ //! use table selection from the tab control where dragging was started?
+ pViewData->GetView()->MoveTable( lcl_DocShellNr(rDoc), nPos, rEvt.mnAction != DND_ACTION_MOVE );
+
+ rData.pCellTransfer->SetDragWasInternal(); // don't delete
+ return DND_ACTION_COPY;
+ }
+ }
+ }
+
+ return DND_ACTION_NONE;
+}
+
+sal_Int8 ScTabControl::AcceptDrop( const AcceptDropEvent& rEvt )
+{
+ if ( rEvt.mbLeaving )
+ {
+ EndSwitchPage();
+ HideDropPos();
+ return rEvt.mnAction;
+ }
+
+ const ScDocument& rDoc = pViewData->GetDocument();
+ const ScDragData& rData = SC_MOD()->GetDragData();
+ if ( rData.pCellTransfer && (rData.pCellTransfer->GetDragSourceFlags() & ScDragSrc::Table) &&
+ rData.pCellTransfer->GetSourceDocument() == &rDoc )
+ {
+ // moving of tables within the document
+ if ( !rDoc.GetChangeTrack() && rDoc.IsDocEditable() )
+ {
+ ShowDropPos( rEvt.maPosPixel );
+ return rEvt.mnAction;
+ }
+ }
+ else // switch sheets for all formats
+ {
+ SwitchPage( rEvt.maPosPixel ); // switch sheet after timeout
+ return 0; // nothing can be dropped here
+ }
+
+ return 0;
+}
+
+bool ScTabControl::StartRenaming()
+{
+ return pViewData->GetDocument().IsDocEditable();
+}
+
+TabBarAllowRenamingReturnCode ScTabControl::AllowRenaming()
+{
+ ScTabViewShell* pViewSh = pViewData->GetViewShell();
+ OSL_ENSURE( pViewSh, "pViewData->GetViewShell()" );
+
+ TabBarAllowRenamingReturnCode nRet = TABBAR_RENAMING_CANCEL;
+ sal_uInt16 nId = GetEditPageId();
+ if ( nId )
+ {
+ SCTAB nTab = nId - 1;
+ OUString aNewName = GetEditText();
+ bool bDone = pViewSh->RenameTable( aNewName, nTab );
+ if ( bDone )
+ nRet = TABBAR_RENAMING_YES;
+ else if ( bErrorShown )
+ {
+ // if the error message from this TabControl is currently visible,
+ // don't end edit mode now, to avoid problems when returning to
+ // the other call (showing the error) - this should not happen
+ OSL_FAIL("ScTabControl::AllowRenaming: nested calls");
+ nRet = TABBAR_RENAMING_NO;
+ }
+ else if (pViewData->GetDocShell()->IsInModalMode())
+ {
+ // don't show error message above any modal dialog
+ // instead cancel renaming without error message
+ // e.g. start with default Sheet1, add another sheet
+ // alt+left click on Sheet2 tab, edit to say Sheet1
+ // ctrl+S to trigger modal file save dialog
+ nRet = TABBAR_RENAMING_CANCEL;
+ }
+ else
+ {
+ bErrorShown = true;
+ pViewSh->ErrorMessage( STR_INVALIDTABNAME );
+ bErrorShown = false;
+ nRet = TABBAR_RENAMING_NO;
+ }
+ }
+ return nRet;
+}
+
+void ScTabControl::EndRenaming()
+{
+ if ( HasFocus() )
+ pViewData->GetView()->ActiveGrabFocus();
+}
+
+void ScTabControl::Mirror()
+{
+ TabBar::Mirror();
+ if( nSelPageIdByMouse != TabBar::PAGE_NOT_FOUND )
+ {
+ tools::Rectangle aRect( GetPageRect( GetCurPageId() ) );
+ if( !aRect.IsEmpty() )
+ SetPointerPosPixel( aRect.Center() );
+ nSelPageIdByMouse = TabBar::PAGE_NOT_FOUND; // only once after a Select()
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabsplit.cxx b/sc/source/ui/view/tabsplit.cxx
new file mode 100644
index 0000000000..fb8435b271
--- /dev/null
+++ b/sc/source/ui/view/tabsplit.cxx
@@ -0,0 +1,127 @@
+/* -*- 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 .
+ */
+
+#include <tabsplit.hxx>
+#include <viewdata.hxx>
+
+#include <vcl/settings.hxx>
+
+ScTabSplitter::ScTabSplitter( vcl::Window* pParent, WinBits nWinStyle, const ScViewData* pData ) :
+ Splitter(pParent, nWinStyle),
+ pViewData(pData)
+{
+ SetFixed(false);
+ EnableRTL(false);
+}
+
+ScTabSplitter::~ScTabSplitter()
+{
+}
+
+void ScTabSplitter::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ if (bFixed)
+ Window::MouseButtonDown( rMEvt );
+ else
+ Splitter::MouseButtonDown( rMEvt );
+}
+
+void ScTabSplitter::SetFixed(bool bSet)
+{
+ bFixed = bSet;
+ if (bSet)
+ SetPointer(PointerStyle::Arrow);
+ else if (IsHorizontal())
+ SetPointer(PointerStyle::HSplit);
+ else
+ SetPointer(PointerStyle::VSplit);
+}
+
+void ScTabSplitter::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect )
+{
+ rRenderContext.Push(vcl::PushFlags::FILLCOLOR | vcl::PushFlags::LINECOLOR);
+ const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
+
+ if (IsHorizontal())
+ {
+ switch (pViewData->GetHSplitMode())
+ {
+ case SC_SPLIT_NONE:
+ {
+ rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
+ rRenderContext.SetFillColor(rStyleSettings.GetShadowColor());
+ rRenderContext.DrawRect(tools::Rectangle(rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom()));
+
+ // Draw handle
+ rRenderContext.SetLineColor(COL_BLACK);
+ rRenderContext.SetFillColor(COL_BLACK);
+ const tools::Long xc = rRect.Right() + rRect.Left();
+ const tools::Long h4 = rRect.GetHeight() / 4;
+ // First xc fraction is truncated, second one is rounded. This will draw a centered line
+ // in handlers with odd width and a centered rectangle in those with even width.
+ rRenderContext.DrawRect(tools::Rectangle(Point(xc / 2, rRect.Top() + h4),
+ Point((xc + 1) / 2, rRect.Bottom() - h4)));
+ break;
+ }
+ case SC_SPLIT_NORMAL:
+ rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
+ rRenderContext.SetFillColor(rStyleSettings.GetShadowColor());
+ rRenderContext.DrawRect(tools::Rectangle(rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom()));
+ break;
+ case SC_SPLIT_FIX:
+ // Nothing to draw
+ break;
+ }
+ }
+ else
+ {
+ switch (pViewData->GetVSplitMode())
+ {
+ case SC_SPLIT_NONE:
+ {
+ rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
+ rRenderContext.SetFillColor(rStyleSettings.GetShadowColor());
+ rRenderContext.DrawRect(tools::Rectangle(rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom()));
+
+ // Draw handle
+ rRenderContext.SetLineColor(COL_BLACK);
+ rRenderContext.SetFillColor(COL_BLACK);
+ const tools::Long yc = rRect.Top() + rRect.Bottom();
+ const tools::Long w4 = rRect.GetWidth() / 4;
+ // First yc fraction is truncated, second one is rounded. This will draw a centered line
+ // in handlers with odd height and a centered rectangle in those with even height.
+ GetOutDev()->DrawRect(tools::Rectangle(Point(rRect.Left() + w4, yc / 2),
+ Point(rRect.Right() - w4, (yc + 1) / 2)));
+ break;
+ }
+ case SC_SPLIT_NORMAL:
+ rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
+ rRenderContext.SetFillColor(rStyleSettings.GetShadowColor());
+ rRenderContext.DrawRect(tools::Rectangle(rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom()));
+ break;
+ case SC_SPLIT_FIX:
+ // Nothing to draw
+ break;
+ }
+ }
+
+ rRenderContext.Pop();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabview.cxx b/sc/source/ui/view/tabview.cxx
new file mode 100644
index 0000000000..9eff50195e
--- /dev/null
+++ b/sc/source/ui/view/tabview.cxx
@@ -0,0 +1,3162 @@
+/* -*- 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 .
+ */
+
+#include <scitems.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/bindings.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/help.hxx>
+#include <vcl/settings.hxx>
+#include <sal/log.hxx>
+#include <tools/svborder.hxx>
+#include <tools/json_writer.hxx>
+#include <o3tl/unit_conversion.hxx>
+
+#include <pagedata.hxx>
+#include <tabview.hxx>
+#include <tabvwsh.hxx>
+#include <document.hxx>
+#include <gridwin.hxx>
+#include <olinewin.hxx>
+#include <olinetab.hxx>
+#include <tabsplit.hxx>
+#include <colrowba.hxx>
+#include <tabcont.hxx>
+#include <scmod.hxx>
+#include <sc.hrc>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <drawview.hxx>
+#include <docsh.hxx>
+#include <viewuno.hxx>
+#include <appoptio.hxx>
+#include <attrib.hxx>
+#include <spellcheckcontext.hxx>
+#include <comphelper/lok.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <osl/diagnose.h>
+
+#include <boost/property_tree/ptree.hpp>
+#include <boost/property_tree/json_parser.hpp>
+
+#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
+
+#include <algorithm>
+
+#include <basegfx/utils/zoomtools.hxx>
+
+#define SPLIT_MARGIN 30
+#define SPLIT_HANDLE_SIZE 5
+constexpr sal_Int32 TAB_HEIGHT_MARGIN = 10;
+
+#define SC_ICONSIZE 36
+
+#define SC_SCROLLBAR_MIN 30
+#define SC_TABBAR_MIN 6
+
+using namespace ::com::sun::star;
+
+// Corner-Button
+
+ScCornerButton::ScCornerButton( vcl::Window* pParent, ScViewData* pData ) :
+ Window( pParent, WinBits( 0 ) ),
+ pViewData( pData )
+{
+ EnableRTL( false );
+}
+
+ScCornerButton::~ScCornerButton()
+{
+}
+
+void ScCornerButton::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
+{
+ const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
+ SetBackground(rStyleSettings.GetFaceColor());
+
+ Size aSize(GetOutputSizePixel());
+ tools::Long nPosX = aSize.Width() - 1;
+ tools::Long nPosY = aSize.Height() - 1;
+
+ Window::Paint(rRenderContext, rRect);
+
+ bool bLayoutRTL = pViewData->GetDocument().IsLayoutRTL( pViewData->GetTabNo() );
+ tools::Long nDarkX = bLayoutRTL ? 0 : nPosX;
+
+ // both buttons have the same look now - only dark right/bottom lines
+ rRenderContext.SetLineColor(rStyleSettings.GetDarkShadowColor());
+ rRenderContext.DrawLine(Point(0, nPosY), Point(nPosX, nPosY));
+ rRenderContext.DrawLine(Point(nDarkX, 0), Point(nDarkX, nPosY));
+}
+
+void ScCornerButton::StateChanged( StateChangedType nType )
+{
+ Window::StateChanged( nType );
+
+ const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
+ SetBackground( rStyleSettings.GetFaceColor() );
+ Invalidate();
+}
+
+void ScCornerButton::DataChanged( const DataChangedEvent& rDCEvt )
+{
+ Window::DataChanged( rDCEvt );
+
+ const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
+ SetBackground( rStyleSettings.GetFaceColor() );
+ Invalidate();
+}
+
+void ScCornerButton::Resize()
+{
+ Invalidate();
+}
+
+void ScCornerButton::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ ScModule* pScMod = SC_MOD();
+ bool bDisable = pScMod->IsFormulaMode() || pScMod->IsModalMode();
+ if (!bDisable)
+ {
+ ScTabViewShell* pViewSh = pViewData->GetViewShell();
+ pViewSh->SetActive(); // Appear and SetViewFrame
+ pViewSh->ActiveGrabFocus();
+
+ bool bControl = rMEvt.IsMod1();
+ pViewSh->SelectAll( bControl );
+ }
+}
+namespace
+{
+
+bool lcl_HasColOutline( const ScViewData& rViewData )
+{
+ const ScOutlineTable* pTable = rViewData.GetDocument().GetOutlineTable(rViewData.GetTabNo());
+ if (pTable)
+ {
+ const ScOutlineArray& rArray = pTable->GetColArray();
+ if ( rArray.GetDepth() > 0 )
+ return true;
+ }
+ return false;
+}
+
+bool lcl_HasRowOutline( const ScViewData& rViewData )
+{
+ const ScOutlineTable* pTable = rViewData.GetDocument().GetOutlineTable(rViewData.GetTabNo());
+ if (pTable)
+ {
+ const ScOutlineArray& rArray = pTable->GetRowArray();
+ if ( rArray.GetDepth() > 0 )
+ return true;
+ }
+ return false;
+}
+
+} // anonymous namespace
+
+ScTabView::ScTabView( vcl::Window* pParent, ScDocShell& rDocSh, ScTabViewShell* pViewShell ) :
+ pFrameWin( pParent ),
+ aViewData( rDocSh, pViewShell ),
+ aFunctionSet( &aViewData ),
+ aHdrFunc( &aViewData ),
+ aVScrollTop( VclPtr<ScrollAdaptor>::Create( pFrameWin, false ) ),
+ aVScrollBottom( VclPtr<ScrollAdaptor>::Create( pFrameWin, false ) ),
+ aHScrollLeft( VclPtr<ScrollAdaptor>::Create( pFrameWin, true ) ),
+ aHScrollRight( VclPtr<ScrollAdaptor>::Create( pFrameWin, true ) ),
+ aCornerButton( VclPtr<ScCornerButton>::Create( pFrameWin, &aViewData ) ),
+ aTopButton( VclPtr<ScCornerButton>::Create( pFrameWin, &aViewData ) ),
+ aScrollTimer("ScTabView aScrollTimer"),
+ pTimerWindow( nullptr ),
+ aExtraEditViewManager( pViewShell, pGridWin ),
+ nTipVisible( nullptr ),
+ nTipAlign( QuickHelpFlags::NONE ),
+ nPrevDragPos( 0 ),
+ meBlockMode(None),
+ meHighlightBlockMode(None),
+ nBlockStartX( 0 ),
+ nBlockStartXOrig( 0 ),
+ nBlockEndX( 0 ),
+ nBlockStartY( 0 ),
+ nBlockStartYOrig( 0 ),
+ nBlockEndY( 0 ),
+ nBlockStartZ( 0 ),
+ nBlockEndZ( 0 ),
+ nOldCurX( 0 ),
+ nOldCurY( 0 ),
+ mfPendingTabBarWidth( -1.0 ),
+ mnLOKStartHeaderRow( -2 ),
+ mnLOKEndHeaderRow( -1 ),
+ mnLOKStartHeaderCol( -2 ),
+ mnLOKEndHeaderCol( -1 ),
+ bMinimized( false ),
+ bInUpdateHeader( false ),
+ bInActivatePart( false ),
+ bInZoomUpdate( false ),
+ bMoveIsShift( false ),
+ bDrawSelMode( false ),
+ bLockPaintBrush( false ),
+ bDragging( false ),
+ bBlockNeg( false ),
+ bBlockCols( false ),
+ bBlockRows( false ),
+ mbInlineWithScrollbar( false )
+{
+ Init();
+}
+
+void ScTabView::InitScrollBar(ScrollAdaptor& rScrollBar, tools::Long nMaxVal, const Link<weld::Scrollbar&, void>& rLink)
+{
+ rScrollBar.SetRange( Range( 0, nMaxVal ) );
+ rScrollBar.SetLineSize( 1 );
+ rScrollBar.SetPageSize( 1 ); // is queried separately
+ rScrollBar.SetVisibleSize( 10 ); // is reset by Resize
+
+ rScrollBar.SetScrollHdl(rLink);
+ rScrollBar.SetMouseReleaseHdl(LINK(this, ScTabView, EndScrollHdl));
+
+ rScrollBar.EnableRTL( aViewData.GetDocument().IsLayoutRTL( aViewData.GetTabNo() ) );
+
+ // Related: tdf#155266 Eliminate delayed scrollbar redrawing when swiping
+ // By default, the layout idle timer in the InterimWindowItem class
+ // is set to TaskPriority::RESIZE. That is too high of a priority as
+ // it appears that other timers are drawing after the scrollbar has been
+ // redrawn.
+ // As a result, when swiping, the content moves fluidly but the scrollbar
+ // thumb does not move until after swiping stops or pauses. Then, after a
+ // short lag, the scrollbar thumb finally "jumps" to the expected
+ // position.
+ // So, to fix this scrollbar "stickiness" when swiping, setting the
+ // priority to TaskPriority::POST_PAINT causes the scrollbar to be
+ // redrawn after any competing timers.
+ rScrollBar.SetPriority(TaskPriority::POST_PAINT);
+}
+
+// Scroll-Timer
+void ScTabView::SetTimer( ScGridWindow* pWin, const MouseEvent& rMEvt )
+{
+ pTimerWindow = pWin;
+ aTimerMEvt = rMEvt;
+ aScrollTimer.Start();
+}
+
+void ScTabView::ResetTimer()
+{
+ aScrollTimer.Stop();
+ pTimerWindow = nullptr;
+}
+
+IMPL_LINK_NOARG(ScTabView, TimerHdl, Timer *, void)
+{
+ if (pTimerWindow)
+ pTimerWindow->MouseMove( aTimerMEvt );
+}
+
+// --- Resize ---------------------------------------------------------------------
+
+static void lcl_SetPosSize( vcl::Window& rWindow, const Point& rPos, const Size& rSize,
+ tools::Long nTotalWidth, bool bLayoutRTL )
+{
+ Point aNewPos = rPos;
+ if ( bLayoutRTL )
+ {
+ aNewPos.setX( nTotalWidth - rPos.X() - rSize.Width() );
+ if ( aNewPos == rWindow.GetPosPixel() && rSize.Width() != rWindow.GetSizePixel().Width() )
+ {
+ // Document windows are manually painted right-to-left, so they need to
+ // be repainted if the size changes.
+ rWindow.Invalidate();
+ }
+ }
+ rWindow.SetPosSizePixel( aNewPos, rSize );
+}
+
+void ScTabView::DoResize( const Point& rOffset, const Size& rSize, bool bInner )
+{
+ HideListBox();
+
+ bool bHasHint = HasHintWindow();
+ if (bHasHint)
+ RemoveHintWindow();
+
+ bool bLayoutRTL = aViewData.GetDocument().IsLayoutRTL( aViewData.GetTabNo() );
+ tools::Long nTotalWidth = rSize.Width();
+ if ( bLayoutRTL )
+ nTotalWidth += 2*rOffset.X();
+
+ bool bVScroll = aViewData.IsVScrollMode();
+ bool bHScroll = aViewData.IsHScrollMode();
+ bool bTabControl = aViewData.IsTabMode();
+ bool bHeaders = aViewData.IsHeaderMode();
+ bool bOutlMode = aViewData.IsOutlineMode();
+ bool bHOutline = bOutlMode && lcl_HasColOutline(aViewData);
+ bool bVOutline = bOutlMode && lcl_HasRowOutline(aViewData);
+
+ if ( aViewData.GetDocShell()->IsPreview() )
+ bHScroll = bVScroll = bTabControl = bHeaders = bHOutline = bVOutline = false;
+
+ tools::Long nBarX = 0;
+ tools::Long nBarY = 0;
+ tools::Long nOutlineX = 0;
+ tools::Long nOutlineY = 0;
+ tools::Long nOutPosX;
+ tools::Long nOutPosY;
+
+ tools::Long nPosX = rOffset.X();
+ tools::Long nPosY = rOffset.Y();
+ tools::Long nSizeX = rSize.Width();
+ tools::Long nSizeY = rSize.Height();
+
+ bMinimized = ( nSizeX<=SC_ICONSIZE || nSizeY<=SC_ICONSIZE );
+ if ( bMinimized )
+ return;
+
+ float fScaleFactor = pFrameWin->GetDPIScaleFactor();
+
+ tools::Long nSplitSizeX = SPLIT_HANDLE_SIZE * fScaleFactor;
+ if ( aViewData.GetHSplitMode() == SC_SPLIT_FIX )
+ nSplitSizeX = 1;
+ tools::Long nSplitSizeY = SPLIT_HANDLE_SIZE * fScaleFactor;
+ if ( aViewData.GetVSplitMode() == SC_SPLIT_FIX )
+ nSplitSizeY = 1;
+
+ aBorderPos = rOffset;
+ aFrameSize = rSize;
+
+ const StyleSettings& rStyleSettings = pFrameWin->GetSettings().GetStyleSettings();
+
+
+ Size aFontSize = rStyleSettings.GetTabFont().GetFontSize();
+ MapMode aPtMapMode(MapUnit::MapPoint);
+ aFontSize = pFrameWin->LogicToPixel(aFontSize, aPtMapMode);
+ sal_Int32 nTabHeight = aFontSize.Height() + TAB_HEIGHT_MARGIN;
+
+ if ( aViewData.GetHSplitMode() != SC_SPLIT_NONE )
+ {
+ if ( aViewData.GetHSplitPos() > nSizeX - SPLIT_MARGIN )
+ {
+ aViewData.SetHSplitMode( SC_SPLIT_NONE );
+ if ( WhichH( aViewData.GetActivePart() ) == SC_SPLIT_RIGHT )
+ ActivatePart( SC_SPLIT_BOTTOMLEFT );
+ InvalidateSplit();
+ }
+ }
+ if ( aViewData.GetVSplitMode() != SC_SPLIT_NONE )
+ {
+ if ( aViewData.GetVSplitPos() > nSizeY - SPLIT_MARGIN )
+ {
+ aViewData.SetVSplitMode( SC_SPLIT_NONE );
+ if ( WhichV( aViewData.GetActivePart() ) == SC_SPLIT_TOP )
+ ActivatePart( SC_SPLIT_BOTTOMLEFT );
+ InvalidateSplit();
+ }
+ }
+
+ UpdateShow();
+
+ if (bHScroll || bVScroll) // Scrollbars horizontal or vertical
+ {
+ tools::Long nScrollBarSize = rStyleSettings.GetScrollBarSize();
+ if (bVScroll)
+ {
+ nBarX = nScrollBarSize;
+ nSizeX -= nBarX;
+ }
+ if (bHScroll)
+ {
+ nBarY = nTabHeight;
+
+ if (!mbInlineWithScrollbar)
+ nBarY += nScrollBarSize;
+
+ nSizeY -= nBarY;
+ }
+
+ if (bHScroll) // Scrollbars horizontal
+ {
+ tools::Long nSizeLt = 0; // left scroll bar
+ tools::Long nSizeRt = 0; // right scroll bar
+ tools::Long nSizeSp = 0; // splitter
+
+ switch (aViewData.GetHSplitMode())
+ {
+ case SC_SPLIT_NONE:
+ nSizeSp = nSplitSizeX;
+ nSizeLt = nSizeX - nSizeSp; // Convert the corner
+ break;
+ case SC_SPLIT_NORMAL:
+ nSizeSp = nSplitSizeX;
+ nSizeLt = aViewData.GetHSplitPos();
+ break;
+ case SC_SPLIT_FIX:
+ nSizeSp = 0;
+ nSizeLt = 0;
+ break;
+ }
+ nSizeRt = nSizeX - nSizeLt - nSizeSp;
+
+ tools::Long nTabSize = 0;
+
+ if (bTabControl)
+ {
+ // pending relative tab bar width from extended document options
+ if( mfPendingTabBarWidth >= 0.0 )
+ {
+ SetRelTabBarWidth( mfPendingTabBarWidth );
+ mfPendingTabBarWidth = -1.0;
+ }
+
+ if (mbInlineWithScrollbar)
+ {
+ nTabSize = pTabControl->GetSizePixel().Width();
+
+ if ( aViewData.GetHSplitMode() != SC_SPLIT_FIX ) // left Scrollbar
+ {
+ if (nTabSize > nSizeLt-SC_SCROLLBAR_MIN)
+ nTabSize = nSizeLt-SC_SCROLLBAR_MIN;
+ if (nTabSize < SC_TABBAR_MIN)
+ nTabSize = SC_TABBAR_MIN;
+ nSizeLt -= nTabSize;
+ }
+ else // right Scrollbar
+ {
+ if (nTabSize > nSizeRt-SC_SCROLLBAR_MIN)
+ nTabSize = nSizeRt-SC_SCROLLBAR_MIN;
+ if (nTabSize < SC_TABBAR_MIN)
+ nTabSize = SC_TABBAR_MIN;
+ nSizeRt -= nTabSize;
+ }
+ }
+ }
+
+ if (mbInlineWithScrollbar)
+ {
+ Point aTabPoint(nPosX, nPosY + nSizeY);
+ Size aTabSize(nTabSize, nBarY);
+ lcl_SetPosSize(*pTabControl, aTabPoint, aTabSize, nTotalWidth, bLayoutRTL);
+ pTabControl->SetSheetLayoutRTL(bLayoutRTL);
+
+ Point aHScrollLeftPoint(nPosX + nTabSize, nPosY + nSizeY);
+ Size aHScrollLeftSize(nSizeLt, nBarY);
+ lcl_SetPosSize(*aHScrollLeft, aHScrollLeftPoint, aHScrollLeftSize, nTotalWidth, bLayoutRTL);
+
+ Point aHSplitterPoint(nPosX + nTabSize + nSizeLt, nPosY + nSizeY);
+ Size aHSplitterSize(nSizeSp, nBarY);
+ lcl_SetPosSize(*pHSplitter, aHSplitterPoint, aHSplitterSize, nTotalWidth, bLayoutRTL);
+
+ Point aHScrollRightPoint(nPosX + nTabSize + nSizeLt + nSizeSp, nPosY + nSizeY);
+ Size aHScrollRightSize(nSizeRt, nBarY);
+ lcl_SetPosSize(*aHScrollRight, aHScrollRightPoint, aHScrollRightSize, nTotalWidth, bLayoutRTL);
+ }
+ else
+ {
+ Point aTabPoint(nPosX, nPosY + nSizeY + nScrollBarSize);
+ Size aTabSize(nSizeX, nTabHeight);
+ lcl_SetPosSize(*pTabControl, aTabPoint, aTabSize, nTotalWidth, bLayoutRTL);
+ pTabControl->SetSheetLayoutRTL(bLayoutRTL);
+
+ Point aHScrollLeftPoint(nPosX, nPosY + nSizeY);
+ Size aHScrollLeftSize(nSizeLt, nScrollBarSize);
+ lcl_SetPosSize(*aHScrollLeft, aHScrollLeftPoint, aHScrollLeftSize, nTotalWidth, bLayoutRTL);
+
+ Point aHSplitterPoint(nPosX + nSizeLt, nPosY + nSizeY);
+ Size aHSplitterSize(nSizeSp, nScrollBarSize);
+ lcl_SetPosSize(*pHSplitter, aHSplitterPoint, aHSplitterSize, nTotalWidth, bLayoutRTL);
+
+ Point aHScrollRightPoint(nPosX + nSizeLt + nSizeSp, nPosY + nSizeY);
+ Size aHScrollRightSize(nSizeRt, nScrollBarSize);
+ lcl_SetPosSize(*aHScrollRight, aHScrollRightPoint, aHScrollRightSize, nTotalWidth, bLayoutRTL);
+ }
+ // SetDragRectPixel is done below
+ }
+
+ if (bVScroll)
+ {
+ tools::Long nSizeUp = 0; // upper scroll bar
+ tools::Long nSizeSp = 0; // splitter
+ tools::Long nSizeDn; // lower scroll bar
+
+ switch (aViewData.GetVSplitMode())
+ {
+ case SC_SPLIT_NONE:
+ nSizeUp = 0;
+ nSizeSp = nSplitSizeY;
+ break;
+ case SC_SPLIT_NORMAL:
+ nSizeUp = aViewData.GetVSplitPos();
+ nSizeSp = nSplitSizeY;
+ break;
+ case SC_SPLIT_FIX:
+ nSizeUp = 0;
+ nSizeSp = 0;
+ break;
+ }
+ nSizeDn = nSizeY - nSizeUp - nSizeSp;
+
+ lcl_SetPosSize( *aVScrollTop, Point(nPosX + nSizeX, nPosY),
+ Size(nBarX, nSizeUp), nTotalWidth, bLayoutRTL );
+ lcl_SetPosSize( *pVSplitter, Point( nPosX + nSizeX, nPosY+nSizeUp ),
+ Size( nBarX, nSizeSp ), nTotalWidth, bLayoutRTL );
+ lcl_SetPosSize( *aVScrollBottom, Point(nPosX + nSizeX,
+ nPosY + nSizeUp + nSizeSp),
+ Size(nBarX, nSizeDn), nTotalWidth, bLayoutRTL );
+
+ // SetDragRectPixel is done below
+ }
+ }
+
+ // SetDragRectPixel also without Scrollbars etc., when already split
+ if ( bHScroll || aViewData.GetHSplitMode() != SC_SPLIT_NONE )
+ pHSplitter->SetDragRectPixel(
+ tools::Rectangle( nPosX, nPosY, nPosX+nSizeX, nPosY+nSizeY ), pFrameWin );
+ if ( bVScroll || aViewData.GetVSplitMode() != SC_SPLIT_NONE )
+ pVSplitter->SetDragRectPixel(
+ tools::Rectangle( nPosX, nPosY, nPosX+nSizeX, nPosY+nSizeY ), pFrameWin );
+
+ if (bTabControl && ! bHScroll )
+ {
+ nBarY = aHScrollLeft->GetSizePixel().Height();
+
+ tools::Long nSize1 = nSizeX;
+
+ tools::Long nTabSize = nSize1;
+ if (nTabSize < 0) nTabSize = 0;
+
+ lcl_SetPosSize( *pTabControl, Point(nPosX, nPosY+nSizeY-nBarY),
+ Size(nTabSize, nBarY), nTotalWidth, bLayoutRTL );
+ nSizeY -= nBarY;
+
+ if( bVScroll )
+ {
+ Size aVScrSize = aVScrollBottom->GetSizePixel();
+ aVScrSize.AdjustHeight( -nBarY );
+ aVScrollBottom->SetSizePixel( aVScrSize );
+ }
+ }
+
+ nOutPosX = nPosX;
+ nOutPosY = nPosY;
+
+ // Outline-Controls
+ if (bVOutline && pRowOutline[SC_SPLIT_BOTTOM])
+ {
+ nOutlineX = pRowOutline[SC_SPLIT_BOTTOM]->GetDepthSize();
+ nSizeX -= nOutlineX;
+ nPosX += nOutlineX;
+ }
+ if (bHOutline && pColOutline[SC_SPLIT_LEFT])
+ {
+ nOutlineY = pColOutline[SC_SPLIT_LEFT]->GetDepthSize();
+ nSizeY -= nOutlineY;
+ nPosY += nOutlineY;
+ }
+
+ if (bHeaders) // column/row header
+ {
+ nBarX = pRowBar[SC_SPLIT_BOTTOM]->GetSizePixel().Width();
+ nBarY = pColBar[SC_SPLIT_LEFT]->GetSizePixel().Height();
+ nSizeX -= nBarX;
+ nSizeY -= nBarY;
+ nPosX += nBarX;
+ nPosY += nBarY;
+ }
+ else
+ nBarX = nBarY = 0;
+
+ // evaluate splitter
+
+ tools::Long nLeftSize = nSizeX;
+ tools::Long nRightSize = 0;
+ tools::Long nTopSize = 0;
+ tools::Long nBottomSize = nSizeY;
+ tools::Long nSplitPosX = nPosX;
+ tools::Long nSplitPosY = nPosY;
+
+ if ( aViewData.GetHSplitMode() != SC_SPLIT_NONE )
+ {
+ tools::Long nSplitHeight = rSize.Height();
+ if ( aViewData.GetHSplitMode() == SC_SPLIT_FIX )
+ {
+ // Do not allow freeze splitter to overlap scroll bar/tab bar
+ if ( bHScroll )
+ nSplitHeight -= aHScrollLeft->GetSizePixel().Height();
+ else if ( bTabControl && pTabControl )
+ nSplitHeight -= pTabControl->GetSizePixel().Height();
+ }
+ nSplitPosX = aViewData.GetHSplitPos();
+ lcl_SetPosSize( *pHSplitter,
+ Point(nSplitPosX, nOutPosY),
+ Size(nSplitSizeX, nSplitHeight - nTabHeight), nTotalWidth, bLayoutRTL);
+ nLeftSize = nSplitPosX - nPosX;
+ nSplitPosX += nSplitSizeX;
+ nRightSize = nSizeX - nLeftSize - nSplitSizeX;
+ }
+ if ( aViewData.GetVSplitMode() != SC_SPLIT_NONE )
+ {
+ tools::Long nSplitWidth = rSize.Width();
+ if ( aViewData.GetVSplitMode() == SC_SPLIT_FIX && bVScroll )
+ nSplitWidth -= aVScrollBottom->GetSizePixel().Width();
+ nSplitPosY = aViewData.GetVSplitPos();
+ lcl_SetPosSize( *pVSplitter,
+ Point( nOutPosX, nSplitPosY ), Size( nSplitWidth, nSplitSizeY ), nTotalWidth, bLayoutRTL );
+ nTopSize = nSplitPosY - nPosY;
+ nSplitPosY += nSplitSizeY;
+ nBottomSize = nSizeY - nTopSize - nSplitSizeY;
+ }
+
+ // ShowHide for pColOutline / pRowOutline happens in UpdateShow
+
+ if (bHOutline) // Outline-Controls
+ {
+ if (pColOutline[SC_SPLIT_LEFT])
+ {
+ pColOutline[SC_SPLIT_LEFT]->SetHeaderSize( nBarX );
+ lcl_SetPosSize( *pColOutline[SC_SPLIT_LEFT],
+ Point(nPosX-nBarX,nOutPosY), Size(nLeftSize+nBarX,nOutlineY), nTotalWidth, bLayoutRTL );
+ }
+ if (pColOutline[SC_SPLIT_RIGHT])
+ {
+ pColOutline[SC_SPLIT_RIGHT]->SetHeaderSize( 0 ); // always call to update RTL flag
+ lcl_SetPosSize( *pColOutline[SC_SPLIT_RIGHT],
+ Point(nSplitPosX,nOutPosY), Size(nRightSize,nOutlineY), nTotalWidth, bLayoutRTL );
+ }
+ }
+ if (bVOutline)
+ {
+ if (nTopSize)
+ {
+ if (pRowOutline[SC_SPLIT_TOP] && pRowOutline[SC_SPLIT_BOTTOM])
+ {
+ pRowOutline[SC_SPLIT_TOP]->SetHeaderSize( nBarY );
+ lcl_SetPosSize( *pRowOutline[SC_SPLIT_TOP],
+ Point(nOutPosX,nPosY-nBarY), Size(nOutlineX,nTopSize+nBarY), nTotalWidth, bLayoutRTL );
+ pRowOutline[SC_SPLIT_BOTTOM]->SetHeaderSize( 0 );
+ lcl_SetPosSize( *pRowOutline[SC_SPLIT_BOTTOM],
+ Point(nOutPosX,nSplitPosY), Size(nOutlineX,nBottomSize), nTotalWidth, bLayoutRTL );
+ }
+ }
+ else if (pRowOutline[SC_SPLIT_BOTTOM])
+ {
+ pRowOutline[SC_SPLIT_BOTTOM]->SetHeaderSize( nBarY );
+ lcl_SetPosSize( *pRowOutline[SC_SPLIT_BOTTOM],
+ Point(nOutPosX,nSplitPosY-nBarY), Size(nOutlineX,nBottomSize+nBarY), nTotalWidth, bLayoutRTL );
+ }
+ }
+ if (bHOutline && bVOutline)
+ {
+ lcl_SetPosSize( *aTopButton, Point(nOutPosX,nOutPosY), Size(nOutlineX,nOutlineY), nTotalWidth, bLayoutRTL );
+ aTopButton->Show();
+ }
+ else
+ aTopButton->Hide();
+
+ if (bHeaders) // column/row header
+ {
+ lcl_SetPosSize( *pColBar[SC_SPLIT_LEFT],
+ Point(nPosX,nPosY-nBarY), Size(nLeftSize,nBarY), nTotalWidth, bLayoutRTL );
+ if (pColBar[SC_SPLIT_RIGHT])
+ lcl_SetPosSize( *pColBar[SC_SPLIT_RIGHT],
+ Point(nSplitPosX,nPosY-nBarY), Size(nRightSize,nBarY), nTotalWidth, bLayoutRTL );
+
+ if (pRowBar[SC_SPLIT_TOP])
+ lcl_SetPosSize( *pRowBar[SC_SPLIT_TOP],
+ Point(nPosX-nBarX,nPosY), Size(nBarX,nTopSize), nTotalWidth, bLayoutRTL );
+ lcl_SetPosSize( *pRowBar[SC_SPLIT_BOTTOM],
+ Point(nPosX-nBarX,nSplitPosY), Size(nBarX,nBottomSize), nTotalWidth, bLayoutRTL );
+
+ lcl_SetPosSize( *aCornerButton, Point(nPosX-nBarX,nPosY-nBarY), Size(nBarX,nBarY), nTotalWidth, bLayoutRTL );
+ aCornerButton->Show();
+ pColBar[SC_SPLIT_LEFT]->Show();
+ pRowBar[SC_SPLIT_BOTTOM]->Show();
+ }
+ else
+ {
+ aCornerButton->Hide();
+ pColBar[SC_SPLIT_LEFT]->Hide(); // always here
+ pRowBar[SC_SPLIT_BOTTOM]->Hide();
+ }
+
+ // Grid-Windows
+
+ if (bInner)
+ {
+ tools::Long nInnerPosX = bLayoutRTL ? ( nTotalWidth - nPosX - nLeftSize ) : nPosX;
+ pGridWin[SC_SPLIT_BOTTOMLEFT]->SetPosPixel( Point(nInnerPosX,nSplitPosY) );
+ }
+ else
+ {
+ lcl_SetPosSize( *pGridWin[SC_SPLIT_BOTTOMLEFT],
+ Point(nPosX,nSplitPosY), Size(nLeftSize,nBottomSize), nTotalWidth, bLayoutRTL );
+ if ( aViewData.GetHSplitMode() != SC_SPLIT_NONE )
+ lcl_SetPosSize( *pGridWin[SC_SPLIT_BOTTOMRIGHT],
+ Point(nSplitPosX,nSplitPosY), Size(nRightSize,nBottomSize), nTotalWidth, bLayoutRTL );
+ if ( aViewData.GetVSplitMode() != SC_SPLIT_NONE )
+ lcl_SetPosSize( *pGridWin[SC_SPLIT_TOPLEFT],
+ Point(nPosX,nPosY), Size(nLeftSize,nTopSize), nTotalWidth, bLayoutRTL );
+ if ( aViewData.GetHSplitMode() != SC_SPLIT_NONE && aViewData.GetVSplitMode() != SC_SPLIT_NONE )
+ lcl_SetPosSize( *pGridWin[SC_SPLIT_TOPRIGHT],
+ Point(nSplitPosX,nPosY), Size(nRightSize,nTopSize), nTotalWidth, bLayoutRTL );
+ }
+
+ // update scroll bars
+
+ if (!bInUpdateHeader)
+ {
+ UpdateScrollBars(); // don't reset scroll bars when scrolling
+ UpdateHeaderWidth();
+
+ InterpretVisible(); // have everything calculated before painting
+ }
+
+ if (bHasHint)
+ TestHintWindow(); // reposition
+
+ UpdateVarZoom(); // update variable zoom types (after resizing GridWindows)
+
+ if (aViewData.GetViewShell()->HasAccessibilityObjects())
+ aViewData.GetViewShell()->BroadcastAccessibility(SfxHint(SfxHintId::ScAccWindowResized));
+}
+
+void ScTabView::UpdateVarZoom()
+{
+ // update variable zoom types
+
+ SvxZoomType eZoomType = GetZoomType();
+ if (eZoomType == SvxZoomType::PERCENT || bInZoomUpdate)
+ return;
+
+ bInZoomUpdate = true;
+ const Fraction& rOldX = GetViewData().GetZoomX();
+ const Fraction& rOldY = GetViewData().GetZoomY();
+ tools::Long nOldPercent = tools::Long(rOldY * 100);
+ sal_uInt16 nNewZoom = CalcZoom( eZoomType, static_cast<sal_uInt16>(nOldPercent) );
+ Fraction aNew( nNewZoom, 100 );
+
+ if ( aNew != rOldX || aNew != rOldY )
+ {
+ SetZoom( aNew, aNew, false ); // always separately per sheet
+ PaintGrid();
+ PaintTop();
+ PaintLeft();
+ aViewData.GetViewShell()->GetViewFrame().GetBindings().Invalidate( SID_ATTR_ZOOM );
+ aViewData.GetViewShell()->GetViewFrame().GetBindings().Invalidate( SID_ATTR_ZOOMSLIDER );
+ aViewData.GetBindings().Invalidate(SID_ZOOM_IN);
+ aViewData.GetBindings().Invalidate(SID_ZOOM_OUT);
+ }
+ bInZoomUpdate = false;
+}
+
+void ScTabView::UpdateFixPos()
+{
+ bool bResize = false;
+ if ( aViewData.GetHSplitMode() == SC_SPLIT_FIX )
+ if (aViewData.UpdateFixX())
+ bResize = true;
+ if ( aViewData.GetVSplitMode() == SC_SPLIT_FIX )
+ if (aViewData.UpdateFixY())
+ bResize = true;
+ if (bResize)
+ RepeatResize(false);
+}
+
+void ScTabView::RepeatResize( bool bUpdateFix )
+{
+ if ( bUpdateFix )
+ {
+ ScSplitMode eHSplit = aViewData.GetHSplitMode();
+ ScSplitMode eVSplit = aViewData.GetVSplitMode();
+
+ // #i46796# UpdateFixX / UpdateFixY uses GetGridOffset, which requires the
+ // outline windows to be available. So UpdateShow has to be called before
+ // (also called from DoResize).
+ if ( eHSplit == SC_SPLIT_FIX || eVSplit == SC_SPLIT_FIX )
+ UpdateShow();
+
+ if ( eHSplit == SC_SPLIT_FIX )
+ aViewData.UpdateFixX();
+ if ( eVSplit == SC_SPLIT_FIX )
+ aViewData.UpdateFixY();
+ }
+
+ DoResize( aBorderPos, aFrameSize );
+
+ //! border must be reset ???
+}
+
+void ScTabView::GetBorderSize( SvBorder& rBorder, const Size& /* rSize */ )
+{
+ bool bScrollBars = aViewData.IsVScrollMode();
+ bool bHeaders = aViewData.IsHeaderMode();
+ bool bOutlMode = aViewData.IsOutlineMode();
+ bool bHOutline = bOutlMode && lcl_HasColOutline(aViewData);
+ bool bVOutline = bOutlMode && lcl_HasRowOutline(aViewData);
+ bool bLayoutRTL = aViewData.GetDocument().IsLayoutRTL( aViewData.GetTabNo() );
+
+ rBorder = SvBorder();
+
+ if (bScrollBars) // Scrollbars horizontal or vertical
+ {
+ rBorder.Right() += aVScrollBottom->GetSizePixel().Width();
+ rBorder.Bottom() += aHScrollLeft->GetSizePixel().Height();
+ }
+
+ // Outline-Controls
+ if (bVOutline && pRowOutline[SC_SPLIT_BOTTOM])
+ rBorder.Left() += pRowOutline[SC_SPLIT_BOTTOM]->GetDepthSize();
+ if (bHOutline && pColOutline[SC_SPLIT_LEFT])
+ rBorder.Top() += pColOutline[SC_SPLIT_LEFT]->GetDepthSize();
+
+ if (bHeaders) // column/row headers
+ {
+ rBorder.Left() += pRowBar[SC_SPLIT_BOTTOM]->GetSizePixel().Width();
+ rBorder.Top() += pColBar[SC_SPLIT_LEFT]->GetSizePixel().Height();
+ }
+
+ if ( bLayoutRTL )
+ ::std::swap( rBorder.Left(), rBorder.Right() );
+}
+
+IMPL_LINK_NOARG(ScTabView, TabBarResize, TabBar*, void)
+{
+ if (!aViewData.IsHScrollMode())
+ return;
+
+ tools::Long nSize = pTabControl->GetSplitSize();
+
+ if (aViewData.GetHSplitMode() != SC_SPLIT_FIX)
+ {
+ tools::Long nMax = pHSplitter->GetPosPixel().X();
+ if( pTabControl->IsEffectiveRTL() )
+ nMax = pFrameWin->GetSizePixel().Width() - nMax;
+ --nMax;
+ if (nSize>nMax) nSize = nMax;
+ }
+
+ if ( nSize != pTabControl->GetSizePixel().Width() )
+ {
+ pTabControl->SetSizePixel( Size( nSize,
+ pTabControl->GetSizePixel().Height() ) );
+ RepeatResize();
+ }
+}
+
+void ScTabView::SetTabBarWidth( tools::Long nNewWidth )
+{
+ Size aSize = pTabControl->GetSizePixel();
+
+ if ( aSize.Width() != nNewWidth )
+ {
+ aSize.setWidth( nNewWidth );
+ pTabControl->SetSizePixel( aSize );
+ }
+}
+
+void ScTabView::SetRelTabBarWidth( double fRelTabBarWidth )
+{
+ if( (0.0 <= fRelTabBarWidth) && (fRelTabBarWidth <= 1.0) )
+ if( tools::Long nFrameWidth = pFrameWin->GetSizePixel().Width() )
+ SetTabBarWidth( static_cast< tools::Long >( fRelTabBarWidth * nFrameWidth + 0.5 ) );
+}
+
+void ScTabView::SetPendingRelTabBarWidth( double fRelTabBarWidth )
+{
+ mfPendingTabBarWidth = fRelTabBarWidth;
+ SetRelTabBarWidth( fRelTabBarWidth );
+}
+
+tools::Long ScTabView::GetTabBarWidth() const
+{
+ return pTabControl->GetSizePixel().Width();
+}
+
+double ScTabView::GetRelTabBarWidth()
+{
+ return 0.5;
+}
+
+ScGridWindow* ScTabView::GetActiveWin()
+{
+ ScSplitPos ePos = aViewData.GetActivePart();
+ OSL_ENSURE(pGridWin[ePos],"no active window");
+ return pGridWin[ePos];
+}
+
+void ScTabView::SetActivePointer( PointerStyle nPointer )
+{
+ for (VclPtr<ScGridWindow> & pWin : pGridWin)
+ if (pWin)
+ pWin->SetPointer( nPointer );
+}
+
+void ScTabView::ActiveGrabFocus()
+{
+ ScSplitPos ePos = aViewData.GetActivePart();
+ if (pGridWin[ePos])
+ pGridWin[ePos]->GrabFocus();
+}
+
+ScSplitPos ScTabView::FindWindow( const vcl::Window* pWindow ) const
+{
+ ScSplitPos eVal = SC_SPLIT_BOTTOMLEFT; // Default
+ for (sal_uInt16 i=0; i<4; i++)
+ if ( pGridWin[i] == pWindow )
+ eVal = static_cast<ScSplitPos>(i);
+
+ return eVal;
+}
+
+Point ScTabView::GetGridOffset() const
+{
+ Point aPos;
+
+ // size as in DoResize
+
+ bool bHeaders = aViewData.IsHeaderMode();
+ bool bOutlMode = aViewData.IsOutlineMode();
+ bool bHOutline = bOutlMode && lcl_HasColOutline(aViewData);
+ bool bVOutline = bOutlMode && lcl_HasRowOutline(aViewData);
+
+ // Outline-Controls
+ if (bVOutline && pRowOutline[SC_SPLIT_BOTTOM])
+ aPos.AdjustX(pRowOutline[SC_SPLIT_BOTTOM]->GetDepthSize() );
+ if (bHOutline && pColOutline[SC_SPLIT_LEFT])
+ aPos.AdjustY(pColOutline[SC_SPLIT_LEFT]->GetDepthSize() );
+
+ if (bHeaders) // column/row headers
+ {
+ if (pRowBar[SC_SPLIT_BOTTOM])
+ aPos.AdjustX(pRowBar[SC_SPLIT_BOTTOM]->GetSizePixel().Width() );
+ if (pColBar[SC_SPLIT_LEFT])
+ aPos.AdjustY(pColBar[SC_SPLIT_LEFT]->GetSizePixel().Height() );
+ }
+
+ return aPos;
+}
+
+// --- Scroll-Bars --------------------------------------------------------
+
+void ScTabView::SetZoomPercentFromCommand(sal_uInt16 nZoomPercent)
+{
+ // scroll wheel doesn't set the AppOptions default
+
+ bool bSyncZoom = SC_MOD()->GetAppOptions().GetSynchronizeZoom();
+ SetZoomType(SvxZoomType::PERCENT, bSyncZoom);
+ Fraction aFract(nZoomPercent, 100);
+ SetZoom(aFract, aFract, bSyncZoom);
+ PaintGrid();
+ PaintTop();
+ PaintLeft();
+ aViewData.GetBindings().Invalidate( SID_ATTR_ZOOM);
+ aViewData.GetBindings().Invalidate( SID_ATTR_ZOOMSLIDER);
+ aViewData.GetBindings().Invalidate( SID_ZOOM_IN);
+ aViewData.GetBindings().Invalidate( SID_ZOOM_OUT);
+}
+
+bool ScTabView::ScrollCommand( const CommandEvent& rCEvt, ScSplitPos ePos )
+{
+ HideNoteMarker();
+
+ bool bDone = false;
+ const CommandWheelData* pData = rCEvt.GetWheelData();
+ if (pData && pData->GetMode() == CommandWheelMode::ZOOM)
+ {
+ if ( !aViewData.GetViewShell()->GetViewFrame().GetFrame().IsInPlace() )
+ {
+ // for ole inplace editing, the scale is defined by the visarea and client size
+ // and can't be changed directly
+
+ const Fraction& rOldY = aViewData.GetZoomY();
+ sal_uInt16 nOld = static_cast<tools::Long>( rOldY * 100 );
+ sal_uInt16 nNew;
+ if ( pData->GetDelta() < 0 )
+ nNew = std::max( MINZOOM, basegfx::zoomtools::zoomOut( nOld ));
+ else
+ nNew = std::min( MAXZOOM, basegfx::zoomtools::zoomIn( nOld ));
+ if ( nNew != nOld )
+ {
+ SetZoomPercentFromCommand(nNew);
+ }
+
+ bDone = true;
+ }
+ }
+ else
+ {
+ ScHSplitPos eHPos = WhichH(ePos);
+ ScVSplitPos eVPos = WhichV(ePos);
+ ScrollAdaptor* pHScroll = ( eHPos == SC_SPLIT_LEFT ) ? aHScrollLeft.get() : aHScrollRight.get();
+ ScrollAdaptor* pVScroll = ( eVPos == SC_SPLIT_TOP ) ? aVScrollTop.get() : aVScrollBottom.get();
+ if ( pGridWin[ePos] )
+ bDone = pGridWin[ePos]->HandleScrollCommand( rCEvt, pHScroll, pVScroll );
+ }
+ return bDone;
+}
+
+bool ScTabView::GestureZoomCommand(const CommandEvent& rCEvt)
+{
+ HideNoteMarker();
+
+ const CommandGestureZoomData* pData = rCEvt.GetGestureZoomData();
+ if (!pData)
+ return false;
+
+ if (aViewData.GetViewShell()->GetViewFrame().GetFrame().IsInPlace())
+ return false;
+
+ if (pData->meEventType == GestureEventZoomType::Begin)
+ {
+ mfLastZoomScale = pData->mfScaleDelta;
+ return true;
+ }
+
+ if (pData->meEventType == GestureEventZoomType::Update)
+ {
+ double deltaBetweenEvents = (pData->mfScaleDelta - mfLastZoomScale) / mfLastZoomScale;
+ mfLastZoomScale = pData->mfScaleDelta;
+
+ // Accumulate fractional zoom to avoid small zoom changes from being ignored
+ mfAccumulatedZoom += deltaBetweenEvents;
+ int nZoomChangePercent = mfAccumulatedZoom * 100;
+ mfAccumulatedZoom -= nZoomChangePercent / 100.0;
+
+ const Fraction& rOldY = aViewData.GetZoomY();
+ sal_uInt16 nOld = static_cast<tools::Long>(rOldY * 100);
+ sal_uInt16 nNew = nOld + nZoomChangePercent;
+ nNew = std::clamp<sal_uInt16>(nNew, MINZOOM, MAXZOOM);
+
+ if (nNew != nOld)
+ {
+ SetZoomPercentFromCommand(nNew);
+ }
+
+ return true;
+ }
+ return true;
+}
+
+IMPL_LINK_NOARG(ScTabView, HScrollLeftHdl, weld::Scrollbar&, void)
+{
+ ScrollHdl(aHScrollLeft.get());
+}
+
+IMPL_LINK_NOARG(ScTabView, HScrollRightHdl, weld::Scrollbar&, void)
+{
+ ScrollHdl(aHScrollRight.get());
+}
+
+IMPL_LINK_NOARG(ScTabView, VScrollTopHdl, weld::Scrollbar&, void)
+{
+ ScrollHdl(aVScrollTop.get());
+}
+
+IMPL_LINK_NOARG(ScTabView, VScrollBottomHdl, weld::Scrollbar&, void)
+{
+ ScrollHdl(aVScrollBottom.get());
+}
+
+IMPL_LINK_NOARG(ScTabView, EndScrollHdl, const MouseEvent&, bool)
+{
+ if (bDragging)
+ {
+ UpdateScrollBars();
+ bDragging = false;
+ }
+ return false;
+}
+
+void ScTabView::ScrollHdl(ScrollAdaptor* pScroll)
+{
+ bool bHoriz = ( pScroll == aHScrollLeft.get() || pScroll == aHScrollRight.get() );
+ tools::Long nViewPos;
+ if ( bHoriz )
+ nViewPos = aViewData.GetPosX( (pScroll == aHScrollLeft.get()) ?
+ SC_SPLIT_LEFT : SC_SPLIT_RIGHT );
+ else
+ nViewPos = aViewData.GetPosY( (pScroll == aVScrollTop.get()) ?
+ SC_SPLIT_TOP : SC_SPLIT_BOTTOM );
+
+ bool bLayoutRTL = aViewData.GetDocument().IsLayoutRTL( aViewData.GetTabNo() );
+
+ ScrollType eType = pScroll->GetScrollType();
+ if ( eType == ScrollType::Drag )
+ {
+ if (!bDragging)
+ {
+ bDragging = true;
+ nPrevDragPos = nViewPos;
+ }
+
+ // show scroll position
+ // (only QuickHelp, there is no entry for it in the status bar)
+
+ if (Help::IsQuickHelpEnabled())
+ {
+ Size aSize = pScroll->GetSizePixel();
+
+ /* Convert scrollbar mouse position to screen position. If RTL
+ mode of scrollbar differs from RTL mode of its parent, then the
+ direct call to Window::OutputToNormalizedScreenPixel() will
+ give unusable results, because calculation of screen position
+ is based on parent orientation and expects equal orientation of
+ the child position. Need to mirror mouse position before. */
+ Point aMousePos = pScroll->GetPointerPosPixel();
+ if( pScroll->IsRTLEnabled() != pScroll->GetParent()->IsRTLEnabled() )
+ aMousePos.setX( aSize.Width() - aMousePos.X() - 1 );
+ aMousePos = pScroll->OutputToNormalizedScreenPixel( aMousePos );
+
+ // convert top-left position of scrollbar to screen position
+ Point aPos = pScroll->OutputToNormalizedScreenPixel( Point() );
+
+ // get scrollbar scroll position for help text (row number/column name)
+ tools::Long nScrollMin = 0; // simulate RangeMin
+ if ( aViewData.GetHSplitMode()==SC_SPLIT_FIX && pScroll == aHScrollRight.get() )
+ nScrollMin = aViewData.GetFixPosX();
+ if ( aViewData.GetVSplitMode()==SC_SPLIT_FIX && pScroll == aVScrollBottom.get() )
+ nScrollMin = aViewData.GetFixPosY();
+ tools::Long nScrollPos = GetScrollBarPos( *pScroll ) + nScrollMin;
+
+ OUString aHelpStr;
+ tools::Rectangle aRect;
+ QuickHelpFlags nAlign;
+ if (bHoriz)
+ {
+ aHelpStr = ScResId(STR_COLUMN) +
+ " " + ScColToAlpha(static_cast<SCCOL>(nScrollPos));
+
+ aRect.SetLeft( aMousePos.X() );
+ aRect.SetTop( aPos.Y() - 4 );
+ nAlign = QuickHelpFlags::Bottom|QuickHelpFlags::Center;
+ }
+ else
+ {
+ aHelpStr = ScResId(STR_ROW) +
+ " " + OUString::number(nScrollPos + 1);
+
+ // show quicktext always inside sheet area
+ aRect.SetLeft( bLayoutRTL ? (aPos.X() + aSize.Width() + 8) : (aPos.X() - 8) );
+ aRect.SetTop( aMousePos.Y() );
+ nAlign = (bLayoutRTL ? QuickHelpFlags::Left : QuickHelpFlags::Right) | QuickHelpFlags::VCenter;
+ }
+ aRect.SetRight( aRect.Left() );
+ aRect.SetBottom( aRect.Top() );
+
+ Help::ShowQuickHelp(pScroll->GetParent(), aRect, aHelpStr, nAlign);
+ }
+ }
+ else
+ bDragging = false;
+
+ tools::Long nDelta(0);
+ switch ( eType )
+ {
+ case ScrollType::LineUp:
+ nDelta = -1;
+ break;
+ case ScrollType::LineDown:
+ nDelta = 1;
+ break;
+ case ScrollType::PageUp:
+ if ( pScroll == aHScrollLeft.get() ) nDelta = -static_cast<tools::Long>(aViewData.PrevCellsX( SC_SPLIT_LEFT ));
+ if ( pScroll == aHScrollRight.get() ) nDelta = -static_cast<tools::Long>(aViewData.PrevCellsX( SC_SPLIT_RIGHT ));
+ if ( pScroll == aVScrollTop.get() ) nDelta = -static_cast<tools::Long>(aViewData.PrevCellsY( SC_SPLIT_TOP ));
+ if ( pScroll == aVScrollBottom.get() ) nDelta = -static_cast<tools::Long>(aViewData.PrevCellsY( SC_SPLIT_BOTTOM ));
+ if (nDelta==0) nDelta=-1;
+ break;
+ case ScrollType::PageDown:
+ if ( pScroll == aHScrollLeft.get() ) nDelta = aViewData.VisibleCellsX( SC_SPLIT_LEFT );
+ if ( pScroll == aHScrollRight.get() ) nDelta = aViewData.VisibleCellsX( SC_SPLIT_RIGHT );
+ if ( pScroll == aVScrollTop.get() ) nDelta = aViewData.VisibleCellsY( SC_SPLIT_TOP );
+ if ( pScroll == aVScrollBottom.get() ) nDelta = aViewData.VisibleCellsY( SC_SPLIT_BOTTOM );
+ if (nDelta==0) nDelta=1;
+ break;
+ default:
+ {
+ // only scroll in the correct direction, do not jitter around hidden ranges
+ tools::Long nScrollMin = 0; // simulate RangeMin
+ if ( aViewData.GetHSplitMode()==SC_SPLIT_FIX && pScroll == aHScrollRight.get() )
+ nScrollMin = aViewData.GetFixPosX();
+ if ( aViewData.GetVSplitMode()==SC_SPLIT_FIX && pScroll == aVScrollBottom.get() )
+ nScrollMin = aViewData.GetFixPosY();
+
+ tools::Long nScrollPos = GetScrollBarPos( *pScroll ) + nScrollMin;
+ nDelta = nScrollPos - nViewPos;
+
+ // tdf#152406 Disable anti-jitter code for scroll wheel events
+ // After moving thousands of columns to the right via
+ // horizontal scroll wheel or trackpad swipe events, most
+ // vertical scroll wheel or trackpad swipe events will trigger
+ // the anti-jitter code because nScrollPos and nPrevDragPos
+ // will be equal and nDelta will be overridden and set to zero.
+ // So, only use the anti-jitter code for mouse drag events.
+ if ( eType == ScrollType::Drag )
+ {
+ if ( nScrollPos > nPrevDragPos )
+ {
+ if (nDelta<0) nDelta=0;
+ }
+ else if ( nScrollPos < nPrevDragPos )
+ {
+ if (nDelta>0) nDelta=0;
+ }
+ else
+ nDelta = 0;
+ }
+ else if ( bHoriz )
+ {
+ // tdf#135478 Reduce sensitivity of horizontal scrollwheel
+ // Problem: at least on macOS, swipe events are very
+ // precise. So, when swiping at a slight angle off of
+ // vertical, swipe events will include a small amount
+ // of horizontal movement. Since horizontal swipe units
+ // are measured in cell widths, these small amounts of
+ // horizontal movement results in shifting many columns
+ // to the right or left while swiping almost vertically.
+ // So my hacky fix is to reduce the amount of horizontal
+ // swipe events to roughly match the "visual distance"
+ // of vertical swipe events.
+ // The reduction factor is arbitrary but is set to
+ // roughly the ratio of default cell width divided by
+ // default cell height. This hacky fix isn't a perfect
+ // fix, but hopefully it reduces the amount of
+ // unexpected horizontal shifting while swiping
+ // vertically to a tolerable amount for most users.
+ // Note: the potential downside of doing this is that
+ // some users might find horizontal swiping to be
+ // slower than they are used to. If that becomes an
+ // issue for enough users, the reduction factor may
+ // need to be lowered to find a good balance point.
+ static const sal_uInt16 nHScrollReductionFactor = 8;
+ if ( pScroll == aHScrollLeft.get() )
+ {
+ mnPendingaHScrollLeftDelta += nDelta;
+ nDelta = 0;
+ if ( abs(mnPendingaHScrollLeftDelta) > nHScrollReductionFactor )
+ {
+ nDelta = mnPendingaHScrollLeftDelta / nHScrollReductionFactor;
+ mnPendingaHScrollLeftDelta = mnPendingaHScrollLeftDelta % nHScrollReductionFactor;
+ }
+ }
+ else if ( pScroll == aHScrollRight.get() )
+ {
+ mnPendingaHScrollRightDelta += nDelta;
+ nDelta = 0;
+ if ( abs(mnPendingaHScrollRightDelta) > nHScrollReductionFactor )
+ {
+ nDelta = mnPendingaHScrollRightDelta / nHScrollReductionFactor;
+ mnPendingaHScrollRightDelta = mnPendingaHScrollRightDelta % nHScrollReductionFactor;
+ }
+ }
+ }
+
+ nPrevDragPos = nScrollPos;
+ }
+ break;
+ }
+
+ if (nDelta)
+ {
+ bool bUpdate = ( eType != ScrollType::Drag ); // don't alter the ranges while dragging
+ if ( bHoriz )
+ ScrollX( nDelta, (pScroll == aHScrollLeft.get()) ? SC_SPLIT_LEFT : SC_SPLIT_RIGHT, bUpdate );
+ else
+ ScrollY( nDelta, (pScroll == aVScrollTop.get()) ? SC_SPLIT_TOP : SC_SPLIT_BOTTOM, bUpdate );
+ }
+}
+
+void ScTabView::ScrollX( tools::Long nDeltaX, ScHSplitPos eWhich, bool bUpdBars )
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCCOL nOldX = aViewData.GetPosX(eWhich);
+ SCCOL nNewX = nOldX + static_cast<SCCOL>(nDeltaX);
+ if ( nNewX < 0 )
+ {
+ nDeltaX -= nNewX;
+ nNewX = 0;
+ }
+ if ( nNewX > rDoc.MaxCol() )
+ {
+ nDeltaX -= nNewX - rDoc.MaxCol();
+ nNewX = rDoc.MaxCol();
+ }
+
+ SCCOL nDir = ( nDeltaX > 0 ) ? 1 : -1;
+ SCTAB nTab = aViewData.GetTabNo();
+ while ( rDoc.ColHidden(nNewX, nTab) &&
+ nNewX+nDir >= 0 && nNewX+nDir <= rDoc.MaxCol() )
+ nNewX = sal::static_int_cast<SCCOL>( nNewX + nDir );
+
+ // freeze
+
+ if (aViewData.GetHSplitMode() == SC_SPLIT_FIX)
+ {
+ if (eWhich == SC_SPLIT_LEFT)
+ nNewX = nOldX; // always keep the left part
+ else
+ {
+ SCCOL nFixX = aViewData.GetFixPosX();
+ if (nNewX < nFixX)
+ nNewX = nFixX;
+ }
+ }
+ if (nNewX == nOldX)
+ return;
+
+ HideAllCursors();
+
+ if ( nNewX >= 0 && nNewX <= rDoc.MaxCol() && nDeltaX )
+ {
+ SCCOL nTrackX = std::max( nOldX, nNewX );
+
+ // with VCL Update() affects all windows at the moment, that is why
+ // calling Update after scrolling of the GridWindow would possibly
+ // already have painted the column/row bar with updated position. -
+ // Therefore call Update once before on column/row bar
+ if (pColBar[eWhich])
+ pColBar[eWhich]->PaintImmediately();
+
+ tools::Long nOldPos = aViewData.GetScrPos( nTrackX, 0, eWhich ).X();
+ aViewData.SetPosX( eWhich, nNewX );
+ tools::Long nDiff = aViewData.GetScrPos( nTrackX, 0, eWhich ).X() - nOldPos;
+
+ if ( eWhich==SC_SPLIT_LEFT )
+ {
+ pGridWin[SC_SPLIT_BOTTOMLEFT]->ScrollPixel( nDiff, 0 );
+ if ( aViewData.GetVSplitMode() != SC_SPLIT_NONE )
+ pGridWin[SC_SPLIT_TOPLEFT]->ScrollPixel( nDiff, 0 );
+ }
+ else
+ {
+ pGridWin[SC_SPLIT_BOTTOMRIGHT]->ScrollPixel( nDiff, 0 );
+ if ( aViewData.GetVSplitMode() != SC_SPLIT_NONE )
+ pGridWin[SC_SPLIT_TOPRIGHT]->ScrollPixel( nDiff, 0 );
+ }
+ if (pColBar[eWhich]) { pColBar[eWhich]->Scroll( nDiff,0 ); pColBar[eWhich]->PaintImmediately(); }
+ if (pColOutline[eWhich]) pColOutline[eWhich]->ScrollPixel( nDiff );
+ if (bUpdBars)
+ UpdateScrollBars();
+ }
+
+ if (nDeltaX==1 || nDeltaX==-1)
+ pGridWin[aViewData.GetActivePart()]->PaintImmediately();
+
+ ShowAllCursors();
+
+ SetNewVisArea(); // MapMode must already be set
+
+ TestHintWindow();
+}
+
+void ScTabView::ScrollY( tools::Long nDeltaY, ScVSplitPos eWhich, bool bUpdBars )
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCROW nOldY = aViewData.GetPosY(eWhich);
+ SCROW nNewY = nOldY + static_cast<SCROW>(nDeltaY);
+ if ( nNewY < 0 )
+ {
+ nDeltaY -= nNewY;
+ nNewY = 0;
+ }
+ if ( nNewY > rDoc.MaxRow() )
+ {
+ nDeltaY -= nNewY - rDoc.MaxRow();
+ nNewY = rDoc.MaxRow();
+ }
+
+ SCROW nDir = ( nDeltaY > 0 ) ? 1 : -1;
+ SCTAB nTab = aViewData.GetTabNo();
+ while ( rDoc.RowHidden(nNewY, nTab) &&
+ nNewY+nDir >= 0 && nNewY+nDir <= rDoc.MaxRow() )
+ nNewY += nDir;
+
+ // freeze
+
+ if (aViewData.GetVSplitMode() == SC_SPLIT_FIX)
+ {
+ if (eWhich == SC_SPLIT_TOP)
+ nNewY = nOldY; // always keep the upper part
+ else
+ {
+ SCROW nFixY = aViewData.GetFixPosY();
+ if (nNewY < nFixY)
+ nNewY = nFixY;
+ }
+ }
+ if (nNewY == nOldY)
+ return;
+
+ HideAllCursors();
+
+ if ( nNewY >= 0 && nNewY <= rDoc.MaxRow() && nDeltaY )
+ {
+ SCROW nTrackY = std::max( nOldY, nNewY );
+
+ // adjust row headers before the actual scrolling, so it does not get painted twice
+ // PosY may then also not be set yet, pass on new value
+ SCROW nUNew = nNewY;
+ UpdateHeaderWidth( &eWhich, &nUNew ); // adjust row headers
+
+ if (pRowBar[eWhich])
+ pRowBar[eWhich]->PaintImmediately();
+
+ tools::Long nOldPos = aViewData.GetScrPos( 0, nTrackY, eWhich ).Y();
+ aViewData.SetPosY( eWhich, nNewY );
+ tools::Long nDiff = aViewData.GetScrPos( 0, nTrackY, eWhich ).Y() - nOldPos;
+
+ if ( eWhich==SC_SPLIT_TOP )
+ {
+ pGridWin[SC_SPLIT_TOPLEFT]->ScrollPixel( 0, nDiff );
+ if ( aViewData.GetHSplitMode() != SC_SPLIT_NONE )
+ pGridWin[SC_SPLIT_TOPRIGHT]->ScrollPixel( 0, nDiff );
+ }
+ else
+ {
+ pGridWin[SC_SPLIT_BOTTOMLEFT]->ScrollPixel( 0, nDiff );
+ if ( aViewData.GetHSplitMode() != SC_SPLIT_NONE )
+ pGridWin[SC_SPLIT_BOTTOMRIGHT]->ScrollPixel( 0, nDiff );
+ }
+ if (pRowBar[eWhich]) { pRowBar[eWhich]->Scroll( 0,nDiff ); pRowBar[eWhich]->PaintImmediately(); }
+ if (pRowOutline[eWhich]) pRowOutline[eWhich]->ScrollPixel( nDiff );
+ if (bUpdBars)
+ UpdateScrollBars();
+ }
+
+ if (nDeltaY==1 || nDeltaY==-1)
+ pGridWin[aViewData.GetActivePart()]->PaintImmediately();
+
+ ShowAllCursors();
+
+ SetNewVisArea(); // MapMode must already be set
+
+ TestHintWindow();
+}
+
+void ScTabView::ScrollLines( tools::Long nDeltaX, tools::Long nDeltaY )
+{
+ ScSplitPos eWhich = aViewData.GetActivePart();
+ if (nDeltaX)
+ ScrollX(nDeltaX,WhichH(eWhich));
+ if (nDeltaY)
+ ScrollY(nDeltaY,WhichV(eWhich));
+}
+
+namespace
+{
+
+SCROW lcl_LastVisible( const ScViewData& rViewData )
+{
+ // If many rows are hidden at end of the document,
+ // then there should not be a switch to wide row headers because of this
+ ScDocument& rDoc = rViewData.GetDocument();
+ SCTAB nTab = rViewData.GetTabNo();
+
+ SCROW nVis = rDoc.MaxRow();
+ SCROW startRow;
+ while ( nVis > 0 && rDoc.GetRowHeight( nVis, nTab, &startRow, nullptr ) == 0 )
+ nVis = std::max<SCROW>( startRow - 1, 0 );
+ return nVis;
+}
+
+} // anonymous namespace
+
+void ScTabView::UpdateHeaderWidth( const ScVSplitPos* pWhich, const SCROW* pPosY )
+{
+ if (!pRowBar[SC_SPLIT_BOTTOM])
+ return;
+
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCROW nEndPos = rDoc.MaxRow();
+ if ( !aViewData.GetViewShell()->GetViewFrame().GetFrame().IsInPlace() )
+ {
+ // for OLE Inplace always MAXROW
+
+ if ( pWhich && *pWhich == SC_SPLIT_BOTTOM && pPosY )
+ nEndPos = *pPosY;
+ else
+ nEndPos = aViewData.GetPosY( SC_SPLIT_BOTTOM );
+ nEndPos += aViewData.CellsAtY( nEndPos, 1, SC_SPLIT_BOTTOM ); // VisibleCellsY
+ if (nEndPos > rDoc.MaxRow())
+ nEndPos = lcl_LastVisible( aViewData );
+
+ if ( aViewData.GetVSplitMode() != SC_SPLIT_NONE )
+ {
+ SCROW nTopEnd;
+ if ( pWhich && *pWhich == SC_SPLIT_TOP && pPosY )
+ nTopEnd = *pPosY;
+ else
+ nTopEnd = aViewData.GetPosY( SC_SPLIT_TOP );
+ nTopEnd += aViewData.CellsAtY( nTopEnd, 1, SC_SPLIT_TOP );// VisibleCellsY
+ if (nTopEnd > rDoc.MaxRow())
+ nTopEnd = lcl_LastVisible( aViewData );
+
+ if ( nTopEnd > nEndPos )
+ nEndPos = nTopEnd;
+ }
+ }
+
+ tools::Long nSmall = pRowBar[SC_SPLIT_BOTTOM]->GetSmallWidth();
+ tools::Long nBig = pRowBar[SC_SPLIT_BOTTOM]->GetBigWidth();
+ tools::Long nDiff = nBig - nSmall;
+
+ if (nEndPos>10000)
+ nEndPos = 10000;
+ else if (nEndPos<1) // avoid extra step at 0 (when only one row is visible)
+ nEndPos = 1;
+ tools::Long nWidth = nBig - ( 10000 - nEndPos ) * nDiff / 10000;
+
+ if (nWidth == pRowBar[SC_SPLIT_BOTTOM]->GetWidth() || bInUpdateHeader)
+ return;
+
+ bInUpdateHeader = true;
+
+ pRowBar[SC_SPLIT_BOTTOM]->SetWidth( nWidth );
+ if (pRowBar[SC_SPLIT_TOP])
+ pRowBar[SC_SPLIT_TOP]->SetWidth( nWidth );
+
+ RepeatResize();
+
+ // on VCL there are endless updates (each Update is valid for all windows)
+ //aCornerButton->Update(); // otherwise this never gets an Update
+
+ bInUpdateHeader = false;
+}
+
+static void ShowHide( vcl::Window* pWin, bool bShow )
+{
+ OSL_ENSURE(pWin || !bShow, "window is not present");
+ if (pWin)
+ pWin->Show(bShow);
+}
+
+void ScTabView::UpdateShow()
+{
+ bool bHScrollMode = aViewData.IsHScrollMode();
+ bool bVScrollMode = aViewData.IsVScrollMode();
+ bool bTabMode = aViewData.IsTabMode();
+ bool bOutlMode = aViewData.IsOutlineMode();
+ bool bHOutline = bOutlMode && lcl_HasColOutline(aViewData);
+ bool bVOutline = bOutlMode && lcl_HasRowOutline(aViewData);
+ bool bHeader = aViewData.IsHeaderMode();
+
+ bool bShowH = ( aViewData.GetHSplitMode() != SC_SPLIT_NONE );
+ bool bShowV = ( aViewData.GetVSplitMode() != SC_SPLIT_NONE );
+
+ if ( aViewData.GetDocShell()->IsPreview() )
+ bHScrollMode = bVScrollMode = bTabMode = bHeader = bHOutline = bVOutline = false;
+
+ // create Windows
+
+ if (bShowH && !pGridWin[SC_SPLIT_BOTTOMRIGHT])
+ {
+ pGridWin[SC_SPLIT_BOTTOMRIGHT] = VclPtr<ScGridWindow>::Create( pFrameWin, aViewData, SC_SPLIT_BOTTOMRIGHT );
+ DoAddWin( pGridWin[SC_SPLIT_BOTTOMRIGHT] );
+ }
+ if (bShowV && !pGridWin[SC_SPLIT_TOPLEFT])
+ {
+ pGridWin[SC_SPLIT_TOPLEFT] = VclPtr<ScGridWindow>::Create( pFrameWin, aViewData, SC_SPLIT_TOPLEFT );
+ DoAddWin( pGridWin[SC_SPLIT_TOPLEFT] );
+ }
+ if (bShowH && bShowV && !pGridWin[SC_SPLIT_TOPRIGHT])
+ {
+ pGridWin[SC_SPLIT_TOPRIGHT] = VclPtr<ScGridWindow>::Create( pFrameWin, aViewData, SC_SPLIT_TOPRIGHT );
+ DoAddWin( pGridWin[SC_SPLIT_TOPRIGHT] );
+ }
+
+ if (bHOutline && !pColOutline[SC_SPLIT_LEFT])
+ pColOutline[SC_SPLIT_LEFT] = VclPtr<ScOutlineWindow>::Create( pFrameWin, SC_OUTLINE_HOR, &aViewData, SC_SPLIT_BOTTOMLEFT );
+ if (bShowH && bHOutline && !pColOutline[SC_SPLIT_RIGHT])
+ pColOutline[SC_SPLIT_RIGHT] = VclPtr<ScOutlineWindow>::Create( pFrameWin, SC_OUTLINE_HOR, &aViewData, SC_SPLIT_BOTTOMRIGHT );
+
+ if (bVOutline && !pRowOutline[SC_SPLIT_BOTTOM])
+ pRowOutline[SC_SPLIT_BOTTOM] = VclPtr<ScOutlineWindow>::Create( pFrameWin, SC_OUTLINE_VER, &aViewData, SC_SPLIT_BOTTOMLEFT );
+ if (bShowV && bVOutline && !pRowOutline[SC_SPLIT_TOP])
+ pRowOutline[SC_SPLIT_TOP] = VclPtr<ScOutlineWindow>::Create( pFrameWin, SC_OUTLINE_VER, &aViewData, SC_SPLIT_TOPLEFT );
+
+ if (bShowH && bHeader && !pColBar[SC_SPLIT_RIGHT])
+ pColBar[SC_SPLIT_RIGHT] = VclPtr<ScColBar>::Create( pFrameWin, SC_SPLIT_RIGHT,
+ &aHdrFunc, pHdrSelEng.get(), this );
+ if (bShowV && bHeader && !pRowBar[SC_SPLIT_TOP])
+ pRowBar[SC_SPLIT_TOP] = VclPtr<ScRowBar>::Create( pFrameWin, SC_SPLIT_TOP,
+ &aHdrFunc, pHdrSelEng.get(), this );
+
+ // show Windows
+
+ ShowHide( aHScrollLeft.get(), bHScrollMode );
+ ShowHide( aHScrollRight.get(), bShowH && bHScrollMode );
+ ShowHide( aVScrollBottom.get(), bVScrollMode );
+ ShowHide( aVScrollTop.get(), bShowV && bVScrollMode );
+
+ ShowHide( pHSplitter, bHScrollMode || bShowH ); // always generated
+ ShowHide( pVSplitter, bVScrollMode || bShowV );
+ ShowHide( pTabControl, bTabMode );
+
+ // from here dynamically generated
+
+ ShowHide( pGridWin[SC_SPLIT_BOTTOMRIGHT], bShowH );
+ ShowHide( pGridWin[SC_SPLIT_TOPLEFT], bShowV );
+ ShowHide( pGridWin[SC_SPLIT_TOPRIGHT], bShowH && bShowV );
+
+ ShowHide( pColOutline[SC_SPLIT_LEFT], bHOutline );
+ ShowHide( pColOutline[SC_SPLIT_RIGHT], bShowH && bHOutline );
+
+ ShowHide( pRowOutline[SC_SPLIT_BOTTOM], bVOutline );
+ ShowHide( pRowOutline[SC_SPLIT_TOP], bShowV && bVOutline );
+
+ ShowHide( pColBar[SC_SPLIT_RIGHT], bShowH && bHeader );
+ ShowHide( pRowBar[SC_SPLIT_TOP], bShowV && bHeader );
+
+ //! register new Gridwindows
+}
+
+bool ScTabView::UpdateVisibleRange()
+{
+ bool bChanged = false;
+ for (VclPtr<ScGridWindow> & pWin : pGridWin)
+ {
+ if (!pWin || !pWin->IsVisible())
+ continue;
+
+ if (pWin->UpdateVisibleRange())
+ bChanged = true;
+ }
+
+ return bChanged;
+}
+
+// --- Splitter --------------------------------------------------------
+
+IMPL_LINK( ScTabView, SplitHdl, Splitter*, pSplitter, void )
+{
+ if ( pSplitter == pHSplitter )
+ DoHSplit( pHSplitter->GetSplitPosPixel() );
+ else
+ DoVSplit( pVSplitter->GetSplitPosPixel() );
+
+ if ( aViewData.GetHSplitMode() == SC_SPLIT_FIX || aViewData.GetVSplitMode() == SC_SPLIT_FIX )
+ FreezeSplitters( true );
+
+ DoResize( aBorderPos, aFrameSize );
+}
+
+void ScTabView::DoHSplit(tools::Long nSplitPos)
+{
+ // nSplitPos is the real pixel position on the frame window,
+ // mirroring for RTL has to be done here.
+
+ bool bLayoutRTL = aViewData.GetDocument().IsLayoutRTL( aViewData.GetTabNo() );
+ if ( bLayoutRTL )
+ nSplitPos = pFrameWin->GetOutputSizePixel().Width() - nSplitPos - 1;
+
+ tools::Long nMinPos;
+ tools::Long nMaxPos;
+
+ nMinPos = SPLIT_MARGIN;
+ if ( pRowBar[SC_SPLIT_BOTTOM] && pRowBar[SC_SPLIT_BOTTOM]->GetSizePixel().Width() >= nMinPos )
+ nMinPos = pRowBar[SC_SPLIT_BOTTOM]->GetSizePixel().Width() + 1;
+ nMaxPos = aFrameSize.Width() - SPLIT_MARGIN;
+
+ ScSplitMode aOldMode = aViewData.GetHSplitMode();
+ ScSplitMode aNewMode = SC_SPLIT_NORMAL;
+
+ aViewData.SetHSplitPos( nSplitPos );
+ if ( nSplitPos < nMinPos || nSplitPos > nMaxPos )
+ aNewMode = SC_SPLIT_NONE;
+
+ aViewData.SetHSplitMode( aNewMode );
+
+ if ( aNewMode == aOldMode )
+ return;
+
+ UpdateShow(); // before ActivatePart !!
+
+ if ( aNewMode == SC_SPLIT_NONE )
+ {
+ if (aViewData.GetActivePart() == SC_SPLIT_TOPRIGHT)
+ ActivatePart( SC_SPLIT_TOPLEFT );
+ if (aViewData.GetActivePart() == SC_SPLIT_BOTTOMRIGHT)
+ ActivatePart( SC_SPLIT_BOTTOMLEFT );
+ }
+ else
+ {
+ SCCOL nOldDelta = aViewData.GetPosX( SC_SPLIT_LEFT );
+ tools::Long nLeftWidth = nSplitPos - pRowBar[SC_SPLIT_BOTTOM]->GetSizePixel().Width();
+ if ( nLeftWidth < 0 ) nLeftWidth = 0;
+ SCCOL nNewDelta = nOldDelta + aViewData.CellsAtX( nOldDelta, 1, SC_SPLIT_LEFT,
+ static_cast<sal_uInt16>(nLeftWidth) );
+ ScDocument& rDoc = aViewData.GetDocument();
+ if ( nNewDelta > rDoc.MaxCol() )
+ nNewDelta = rDoc.MaxCol();
+ aViewData.SetPosX( SC_SPLIT_RIGHT, nNewDelta );
+ if ( nNewDelta > aViewData.GetCurX() )
+ ActivatePart( (WhichV(aViewData.GetActivePart()) == SC_SPLIT_BOTTOM) ?
+ SC_SPLIT_BOTTOMLEFT : SC_SPLIT_TOPLEFT );
+ else
+ ActivatePart( (WhichV(aViewData.GetActivePart()) == SC_SPLIT_BOTTOM) ?
+ SC_SPLIT_BOTTOMRIGHT : SC_SPLIT_TOPRIGHT );
+ }
+
+ // Form Layer needs to know the visible part of all windows
+ // that is why MapMode must already be correct here
+ for (VclPtr<ScGridWindow> & pWin : pGridWin)
+ if (pWin)
+ pWin->SetMapMode( pWin->GetDrawMapMode() );
+ SetNewVisArea();
+
+ PaintGrid();
+ PaintTop();
+
+ InvalidateSplit();
+}
+
+void ScTabView::DoVSplit(tools::Long nSplitPos)
+{
+ tools::Long nMinPos;
+ tools::Long nMaxPos;
+ SCROW nOldDelta;
+
+ nMinPos = SPLIT_MARGIN;
+ if ( pColBar[SC_SPLIT_LEFT] && pColBar[SC_SPLIT_LEFT]->GetSizePixel().Height() >= nMinPos )
+ nMinPos = pColBar[SC_SPLIT_LEFT]->GetSizePixel().Height() + 1;
+ nMaxPos = aFrameSize.Height() - SPLIT_MARGIN;
+
+ ScSplitMode aOldMode = aViewData.GetVSplitMode();
+ ScSplitMode aNewMode = SC_SPLIT_NORMAL;
+
+ aViewData.SetVSplitPos( nSplitPos );
+ if ( nSplitPos < nMinPos || nSplitPos > nMaxPos )
+ aNewMode = SC_SPLIT_NONE;
+
+ aViewData.SetVSplitMode( aNewMode );
+
+ if ( aNewMode == aOldMode )
+ return;
+
+ UpdateShow(); // before ActivatePart !!
+
+ if ( aNewMode == SC_SPLIT_NONE )
+ {
+ nOldDelta = aViewData.GetPosY( SC_SPLIT_TOP );
+ aViewData.SetPosY( SC_SPLIT_BOTTOM, nOldDelta );
+
+ if (aViewData.GetActivePart() == SC_SPLIT_TOPLEFT)
+ ActivatePart( SC_SPLIT_BOTTOMLEFT );
+ if (aViewData.GetActivePart() == SC_SPLIT_TOPRIGHT)
+ ActivatePart( SC_SPLIT_BOTTOMRIGHT );
+ }
+ else
+ {
+ if ( aOldMode == SC_SPLIT_NONE )
+ nOldDelta = aViewData.GetPosY( SC_SPLIT_BOTTOM );
+ else
+ nOldDelta = aViewData.GetPosY( SC_SPLIT_TOP );
+
+ aViewData.SetPosY( SC_SPLIT_TOP, nOldDelta );
+ tools::Long nTopHeight = nSplitPos - pColBar[SC_SPLIT_LEFT]->GetSizePixel().Height();
+ if ( nTopHeight < 0 ) nTopHeight = 0;
+ SCROW nNewDelta = nOldDelta + aViewData.CellsAtY( nOldDelta, 1, SC_SPLIT_TOP,
+ static_cast<sal_uInt16>(nTopHeight) );
+ ScDocument& rDoc = aViewData.GetDocument();
+ if ( nNewDelta > rDoc.MaxRow() )
+ nNewDelta = rDoc.MaxRow();
+ aViewData.SetPosY( SC_SPLIT_BOTTOM, nNewDelta );
+ if ( nNewDelta > aViewData.GetCurY() )
+ ActivatePart( (WhichH(aViewData.GetActivePart()) == SC_SPLIT_LEFT) ?
+ SC_SPLIT_TOPLEFT : SC_SPLIT_TOPRIGHT );
+ else
+ ActivatePart( (WhichH(aViewData.GetActivePart()) == SC_SPLIT_LEFT) ?
+ SC_SPLIT_BOTTOMLEFT : SC_SPLIT_BOTTOMRIGHT );
+ }
+
+ // Form Layer needs to know the visible part of all windows
+ // that is why MapMode must already be correct here
+ for (VclPtr<ScGridWindow> & pWin : pGridWin)
+ if (pWin)
+ pWin->SetMapMode( pWin->GetDrawMapMode() );
+ SetNewVisArea();
+
+ PaintGrid();
+ PaintLeft();
+
+ InvalidateSplit();
+}
+
+Point ScTabView::GetInsertPos() const
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCCOL nCol = aViewData.GetCurX();
+ SCROW nRow = aViewData.GetCurY();
+ SCTAB nTab = aViewData.GetTabNo();
+ tools::Long nPosX = 0;
+ for (SCCOL i=0; i<nCol; i++)
+ nPosX += rDoc.GetColWidth(i,nTab);
+ nPosX = o3tl::convert(nPosX, o3tl::Length::twip, o3tl::Length::mm100);
+ if ( rDoc.IsNegativePage( nTab ) )
+ nPosX = -nPosX;
+ tools::Long nPosY = rDoc.GetRowHeight( 0, nRow-1, nTab);
+ nPosY = o3tl::convert(nPosY, o3tl::Length::twip, o3tl::Length::mm100);
+ return Point(nPosX,nPosY);
+}
+
+Point ScTabView::GetChartInsertPos( const Size& rSize, const ScRange& rCellRange )
+{
+ Point aInsertPos;
+ const tools::Long nBorder = 100; // leave 1mm for border
+ tools::Long nNeededWidth = rSize.Width() + 2 * nBorder;
+ tools::Long nNeededHeight = rSize.Height() + 2 * nBorder;
+
+ // use the active window, or lower/right if frozen (as in CalcZoom)
+ ScSplitPos eUsedPart = aViewData.GetActivePart();
+ if ( aViewData.GetHSplitMode() == SC_SPLIT_FIX )
+ eUsedPart = (WhichV(eUsedPart)==SC_SPLIT_TOP) ? SC_SPLIT_TOPRIGHT : SC_SPLIT_BOTTOMRIGHT;
+ if ( aViewData.GetVSplitMode() == SC_SPLIT_FIX )
+ eUsedPart = (WhichH(eUsedPart)==SC_SPLIT_LEFT) ? SC_SPLIT_BOTTOMLEFT : SC_SPLIT_BOTTOMRIGHT;
+
+ ScGridWindow* pWin = pGridWin[eUsedPart].get();
+ OSL_ENSURE( pWin, "Window not found" );
+ if (pWin)
+ {
+ ActivatePart( eUsedPart );
+
+ // get the visible rectangle in logic units
+ bool bLOKActive = comphelper::LibreOfficeKit::isActive();
+ MapMode aDrawMode = pWin->GetDrawMapMode();
+ tools::Rectangle aVisible(
+ bLOKActive ?
+ OutputDevice::LogicToLogic( aViewData.getLOKVisibleArea(), MapMode(MapUnit::MapTwip), MapMode(MapUnit::Map100thMM) )
+ : pWin->PixelToLogic( tools::Rectangle( Point(0,0), pWin->GetOutputSizePixel() ), aDrawMode ) );
+
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCTAB nTab = aViewData.GetTabNo();
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ tools::Long nDocX = o3tl::convert(rDoc.GetColOffset(rDoc.MaxCol() + 1, nTab), o3tl::Length::twip, o3tl::Length::mm100) * nLayoutSign;
+ tools::Long nDocY = o3tl::convert(rDoc.GetRowOffset( rDoc.MaxRow() + 1, nTab ), o3tl::Length::twip, o3tl::Length::mm100);
+
+ if ( aVisible.Left() * nLayoutSign > nDocX * nLayoutSign )
+ aVisible.SetLeft( nDocX );
+ if ( aVisible.Right() * nLayoutSign > nDocX * nLayoutSign )
+ aVisible.SetRight( nDocX );
+ if ( aVisible.Top() > nDocY )
+ aVisible.SetTop( nDocY );
+ if ( aVisible.Bottom() > nDocY )
+ aVisible.SetBottom( nDocY );
+
+ // get the logic position of the selection
+
+ tools::Rectangle aSelection = rDoc.GetMMRect( rCellRange.aStart.Col(), rCellRange.aStart.Row(),
+ rCellRange.aEnd.Col(), rCellRange.aEnd.Row(), nTab );
+
+ if (bLOKActive && bLayoutRTL)
+ {
+ // In this case we operate in negative X coordinates. The rectangle aSelection already
+ // has negative X coordinates. So the x coordinates in the rectangle aVisible(from getLOKVisibleArea)
+ // need be negated to match.
+ aVisible = tools::Rectangle(-aVisible.Right(), aVisible.Top(), -aVisible.Left(), aVisible.Bottom());
+ }
+
+ tools::Long nLeftSpace = aSelection.Left() - aVisible.Left();
+ tools::Long nRightSpace = aVisible.Right() - aSelection.Right();
+ tools::Long nTopSpace = aSelection.Top() - aVisible.Top();
+ tools::Long nBottomSpace = aVisible.Bottom() - aSelection.Bottom();
+
+ bool bFitLeft = ( nLeftSpace >= nNeededWidth );
+ bool bFitRight = ( nRightSpace >= nNeededWidth );
+
+ if ( bFitLeft || bFitRight )
+ {
+ // first preference: completely left or right of the selection
+
+ // if both fit, prefer left in RTL mode, right otherwise
+ bool bPutLeft = bFitLeft && ( bLayoutRTL || !bFitRight );
+
+ if ( bPutLeft )
+ aInsertPos.setX( aSelection.Left() - nNeededWidth );
+ else
+ aInsertPos.setX( aSelection.Right() + 1 );
+
+ // align with top of selection (is moved again if it doesn't fit)
+ aInsertPos.setY( std::max( aSelection.Top(), aVisible.Top() ) );
+ }
+ else if ( nTopSpace >= nNeededHeight || nBottomSpace >= nNeededHeight )
+ {
+ // second preference: completely above or below the selection
+ if ( nBottomSpace > nNeededHeight ) // bottom is preferred
+ aInsertPos.setY( aSelection.Bottom() + 1 );
+ else
+ aInsertPos.setY( aSelection.Top() - nNeededHeight );
+
+ // align with (logic) left edge of selection (moved again if it doesn't fit)
+ if ( bLayoutRTL )
+ aInsertPos.setX( std::min( aSelection.Right(), aVisible.Right() ) - nNeededWidth + 1 );
+ else
+ aInsertPos.setX( std::max( aSelection.Left(), aVisible.Left() ) );
+ }
+ else
+ {
+ // place to the (logic) right of the selection and move so it fits
+
+ if ( bLayoutRTL )
+ aInsertPos.setX( aSelection.Left() - nNeededWidth );
+ else
+ aInsertPos.setX( aSelection.Right() + 1 );
+ aInsertPos.setY( std::max( aSelection.Top(), aVisible.Top() ) );
+ }
+
+ // move the position if the object doesn't fit in the screen
+
+ tools::Rectangle aCompareRect( aInsertPos, Size( nNeededWidth, nNeededHeight ) );
+ if ( aCompareRect.Right() > aVisible.Right() )
+ aInsertPos.AdjustX( -(aCompareRect.Right() - aVisible.Right()) );
+ if ( aCompareRect.Bottom() > aVisible.Bottom() )
+ aInsertPos.AdjustY( -(aCompareRect.Bottom() - aVisible.Bottom()) );
+
+ if ( aInsertPos.X() < aVisible.Left() )
+ aInsertPos.setX( aVisible.Left() );
+ if ( aInsertPos.Y() < aVisible.Top() )
+ aInsertPos.setY( aVisible.Top() );
+
+ // nNeededWidth / nNeededHeight includes all borders - move aInsertPos to the
+ // object position, inside the border
+
+ aInsertPos.AdjustX(nBorder );
+ aInsertPos.AdjustY(nBorder );
+ }
+ return aInsertPos;
+}
+
+Point ScTabView::GetChartDialogPos( const Size& rDialogSize, const tools::Rectangle& rLogicChart )
+{
+ // rDialogSize must be in pixels, rLogicChart in 1/100 mm. Return value is in pixels.
+
+ Point aRet;
+
+ // use the active window, or lower/right if frozen (as in CalcZoom)
+ ScSplitPos eUsedPart = aViewData.GetActivePart();
+ if ( aViewData.GetHSplitMode() == SC_SPLIT_FIX )
+ eUsedPart = (WhichV(eUsedPart)==SC_SPLIT_TOP) ? SC_SPLIT_TOPRIGHT : SC_SPLIT_BOTTOMRIGHT;
+ if ( aViewData.GetVSplitMode() == SC_SPLIT_FIX )
+ eUsedPart = (WhichH(eUsedPart)==SC_SPLIT_LEFT) ? SC_SPLIT_BOTTOMLEFT : SC_SPLIT_BOTTOMRIGHT;
+
+ ScGridWindow* pWin = pGridWin[eUsedPart].get();
+ OSL_ENSURE( pWin, "Window not found" );
+ if (pWin)
+ {
+ MapMode aDrawMode = pWin->GetDrawMapMode();
+ tools::Rectangle aObjPixel = pWin->LogicToPixel( rLogicChart, aDrawMode );
+ AbsoluteScreenPixelRectangle aObjAbs( pWin->OutputToAbsoluteScreenPixel( aObjPixel.TopLeft() ),
+ pWin->OutputToAbsoluteScreenPixel( aObjPixel.BottomRight() ) );
+
+ AbsoluteScreenPixelRectangle aDesktop = pWin->GetDesktopRectPixel();
+ Size aSpace = pWin->LogicToPixel( Size(8, 12), MapMode(MapUnit::MapAppFont));
+
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCTAB nTab = aViewData.GetTabNo();
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+
+ bool bCenterHor = false;
+
+ if ( aDesktop.Bottom() - aObjAbs.Bottom() >= rDialogSize.Height() + aSpace.Height() )
+ {
+ // first preference: below the chart
+
+ aRet.setY( aObjAbs.Bottom() + aSpace.Height() );
+ bCenterHor = true;
+ }
+ else if ( aObjAbs.Top() - aDesktop.Top() >= rDialogSize.Height() + aSpace.Height() )
+ {
+ // second preference: above the chart
+
+ aRet.setY( aObjAbs.Top() - rDialogSize.Height() - aSpace.Height() );
+ bCenterHor = true;
+ }
+ else
+ {
+ bool bFitLeft = ( aObjAbs.Left() - aDesktop.Left() >= rDialogSize.Width() + aSpace.Width() );
+ bool bFitRight = ( aDesktop.Right() - aObjAbs.Right() >= rDialogSize.Width() + aSpace.Width() );
+
+ if ( bFitLeft || bFitRight )
+ {
+ // if both fit, prefer right in RTL mode, left otherwise
+ bool bPutRight = bFitRight && ( bLayoutRTL || !bFitLeft );
+ if ( bPutRight )
+ aRet.setX( aObjAbs.Right() + aSpace.Width() );
+ else
+ aRet.setX( aObjAbs.Left() - rDialogSize.Width() - aSpace.Width() );
+
+ // center vertically
+ aRet.setY( aObjAbs.Top() + ( aObjAbs.GetHeight() - rDialogSize.Height() ) / 2 );
+ }
+ else
+ {
+ // doesn't fit on any edge - put at the bottom of the screen
+ aRet.setY( aDesktop.Bottom() - rDialogSize.Height() );
+ bCenterHor = true;
+ }
+ }
+ if ( bCenterHor )
+ aRet.setX( aObjAbs.Left() + ( aObjAbs.GetWidth() - rDialogSize.Width() ) / 2 );
+
+ // limit to screen (centering might lead to invalid positions)
+ if ( aRet.X() + rDialogSize.Width() - 1 > aDesktop.Right() )
+ aRet.setX( aDesktop.Right() - rDialogSize.Width() + 1 );
+ if ( aRet.X() < aDesktop.Left() )
+ aRet.setX( aDesktop.Left() );
+ if ( aRet.Y() + rDialogSize.Height() - 1 > aDesktop.Bottom() )
+ aRet.setY( aDesktop.Bottom() - rDialogSize.Height() + 1 );
+ if ( aRet.Y() < aDesktop.Top() )
+ aRet.setY( aDesktop.Top() );
+ }
+
+ return aRet;
+}
+
+void ScTabView::LockModifiers( sal_uInt16 nModifiers )
+{
+ pSelEngine->LockModifiers( nModifiers );
+ pHdrSelEng->LockModifiers( nModifiers );
+}
+
+sal_uInt16 ScTabView::GetLockedModifiers() const
+{
+ return pSelEngine->GetLockedModifiers();
+}
+
+Point ScTabView::GetMousePosPixel()
+{
+ Point aPos;
+ ScGridWindow* pWin = GetActiveWin();
+
+ if ( pWin )
+ aPos = pWin->GetMousePosPixel();
+
+ return aPos;
+}
+
+void ScTabView::FreezeSplitters( bool bFreeze, SplitMethod eSplitMethod, SCCOLROW nFreezeIndex)
+{
+ if ((eSplitMethod == SC_SPLIT_METHOD_COL || eSplitMethod == SC_SPLIT_METHOD_ROW) && nFreezeIndex < 0)
+ nFreezeIndex = 0;
+
+ ScSplitMode eOldH = aViewData.GetHSplitMode();
+ ScSplitMode eOldV = aViewData.GetVSplitMode();
+
+ ScSplitPos ePos = SC_SPLIT_BOTTOMLEFT;
+ if ( eOldV != SC_SPLIT_NONE )
+ ePos = SC_SPLIT_TOPLEFT;
+ vcl::Window* pWin = pGridWin[ePos];
+
+ bool bLayoutRTL = aViewData.GetDocument().IsLayoutRTL( aViewData.GetTabNo() );
+ bool bUpdateFix = false;
+
+ if ( bFreeze )
+ {
+ Point aWinStart = pWin->GetPosPixel();
+ aViewData.GetDocShell()->SetDocumentModified();
+
+ Point aSplit;
+ SCCOL nPosX = 1;
+ SCROW nPosY = 1;
+ if (eOldV != SC_SPLIT_NONE || eOldH != SC_SPLIT_NONE)
+ {
+ if ( eOldV != SC_SPLIT_NONE && (eSplitMethod == SC_SPLIT_METHOD_ROW || eSplitMethod == SC_SPLIT_METHOD_CURSOR))
+ aSplit.setY( aViewData.GetVSplitPos() - aWinStart.Y() );
+
+ if ( eOldH != SC_SPLIT_NONE && (eSplitMethod == SC_SPLIT_METHOD_COL || eSplitMethod == SC_SPLIT_METHOD_CURSOR))
+ {
+ tools::Long nSplitPos = aViewData.GetHSplitPos();
+ if ( bLayoutRTL )
+ nSplitPos = pFrameWin->GetOutputSizePixel().Width() - nSplitPos - 1;
+ aSplit.setX( nSplitPos - aWinStart.X() );
+ }
+
+ aViewData.GetPosFromPixel( aSplit.X(), aSplit.Y(), ePos, nPosX, nPosY );
+ bool bLeft;
+ bool bTop;
+ aViewData.GetMouseQuadrant( aSplit, ePos, nPosX, nPosY, bLeft, bTop );
+ if (eSplitMethod == SC_SPLIT_METHOD_COL)
+ nPosX = static_cast<SCCOL>(nFreezeIndex);
+ else if (!bLeft)
+ ++nPosX;
+ if (eSplitMethod == SC_SPLIT_METHOD_ROW)
+ nPosY = static_cast<SCROW>(nFreezeIndex);
+ else if (!bTop)
+ ++nPosY;
+ }
+ else
+ {
+ switch(eSplitMethod)
+ {
+ case SC_SPLIT_METHOD_ROW:
+ {
+ nPosX = 0;
+ nPosY = static_cast<SCROW>(nFreezeIndex);
+ }
+ break;
+ case SC_SPLIT_METHOD_COL:
+ {
+ nPosX = static_cast<SCCOL>(nFreezeIndex);
+ nPosY = 0;
+ }
+ break;
+ case SC_SPLIT_METHOD_CURSOR:
+ {
+ nPosX = aViewData.GetCurX();
+ nPosY = aViewData.GetCurY();
+ }
+ break;
+ }
+ }
+
+ SCROW nTopPos = aViewData.GetPosY(SC_SPLIT_BOTTOM);
+ SCROW nBottomPos = nPosY;
+ SCCOL nLeftPos = aViewData.GetPosX(SC_SPLIT_LEFT);
+ SCCOL nRightPos = nPosX;
+
+ if (eSplitMethod == SC_SPLIT_METHOD_ROW || eSplitMethod == SC_SPLIT_METHOD_CURSOR)
+ {
+ if (eOldV != SC_SPLIT_NONE)
+ {
+ nTopPos = aViewData.GetPosY(SC_SPLIT_TOP);
+ if (aViewData.GetPosY(SC_SPLIT_BOTTOM) > nBottomPos)
+ nBottomPos = aViewData.GetPosY(SC_SPLIT_BOTTOM);
+ }
+ aSplit = aViewData.GetScrPos(nPosX, nPosY, ePos, true);
+ if (aSplit.Y() > 0)
+ {
+ aViewData.SetVSplitMode(SC_SPLIT_FIX);
+ aViewData.SetVSplitPos(aSplit.Y() + aWinStart.Y());
+ aViewData.SetFixPosY(nPosY);
+
+ aViewData.SetPosY(SC_SPLIT_TOP, nTopPos);
+ aViewData.SetPosY(SC_SPLIT_BOTTOM, nBottomPos);
+ }
+ else if (nPosY == 1 && eSplitMethod == SC_SPLIT_METHOD_ROW)
+ {
+ // Freeze first row, but row 1 is not visible on screen now == special handling
+ aViewData.SetVSplitMode(SC_SPLIT_FIX);
+ aViewData.SetFixPosY(nPosY);
+
+ aViewData.SetPosY(SC_SPLIT_TOP, 0);
+ bUpdateFix = true;
+ }
+ else
+ aViewData.SetVSplitMode(SC_SPLIT_NONE);
+ }
+
+ if (eSplitMethod == SC_SPLIT_METHOD_COL || eSplitMethod == SC_SPLIT_METHOD_CURSOR)
+ {
+ if (eOldH != SC_SPLIT_NONE)
+ {
+ if (aViewData.GetPosX(SC_SPLIT_RIGHT) > nRightPos)
+ nRightPos = aViewData.GetPosX(SC_SPLIT_RIGHT);
+ }
+ aSplit = aViewData.GetScrPos( nPosX, nPosY, ePos, true );
+ if (nPosX > aViewData.GetPosX(SC_SPLIT_LEFT)) // (aSplit.X() > 0) doesn't work for RTL
+ {
+ tools::Long nSplitPos = aSplit.X() + aWinStart.X();
+ if ( bLayoutRTL )
+ nSplitPos = pFrameWin->GetOutputSizePixel().Width() - nSplitPos - 1;
+
+ aViewData.SetHSplitMode( SC_SPLIT_FIX );
+ aViewData.SetHSplitPos( nSplitPos );
+ aViewData.SetFixPosX( nPosX );
+
+ aViewData.SetPosX(SC_SPLIT_LEFT, nLeftPos);
+ aViewData.SetPosX(SC_SPLIT_RIGHT, nRightPos);
+ }
+ else if (nPosX == 1 && eSplitMethod == SC_SPLIT_METHOD_COL)
+ {
+ // Freeze first column, but col A is not visible on screen now == special handling
+ aViewData.SetHSplitMode(SC_SPLIT_FIX);
+ aViewData.SetFixPosX(nPosX);
+
+ aViewData.SetPosX(SC_SPLIT_RIGHT, aViewData.GetPosX(SC_SPLIT_LEFT));
+ aViewData.SetPosX(SC_SPLIT_LEFT, 0);
+ bUpdateFix = true;
+ }
+ else
+ aViewData.SetHSplitMode( SC_SPLIT_NONE );
+ }
+ }
+ else // unfreeze
+ {
+ if ( eOldH == SC_SPLIT_FIX )
+ aViewData.SetHSplitMode( SC_SPLIT_NORMAL );
+ if ( eOldV == SC_SPLIT_FIX )
+ aViewData.SetVSplitMode( SC_SPLIT_NORMAL );
+ }
+
+ // Form Layer needs to know the visible part of all windows
+ // that is why MapMode must already be correct here
+ for (VclPtr<ScGridWindow> & p : pGridWin)
+ if (p)
+ p->SetMapMode( p->GetDrawMapMode() );
+ SetNewVisArea();
+
+ RepeatResize(bUpdateFix);
+
+ UpdateShow();
+ PaintLeft();
+ PaintTop();
+ PaintGrid();
+
+ // SC_FOLLOW_NONE: only update active part
+ AlignToCursor( aViewData.GetCurX(), aViewData.GetCurY(), SC_FOLLOW_NONE );
+ UpdateAutoFillMark();
+
+ InvalidateSplit();
+}
+
+void ScTabView::RemoveSplit()
+{
+ if (aViewData.GetHSplitMode() == SC_SPLIT_FIX || aViewData.GetVSplitMode() == SC_SPLIT_FIX)
+ aViewData.GetDocShell()->SetDocumentModified();
+ DoHSplit( 0 );
+ DoVSplit( 0 );
+ RepeatResize();
+}
+
+void ScTabView::SplitAtCursor()
+{
+ ScSplitPos ePos = SC_SPLIT_BOTTOMLEFT;
+ if ( aViewData.GetVSplitMode() != SC_SPLIT_NONE )
+ ePos = SC_SPLIT_TOPLEFT;
+ vcl::Window* pWin = pGridWin[ePos];
+ Point aWinStart = pWin->GetPosPixel();
+
+ SCCOL nPosX = aViewData.GetCurX();
+ SCROW nPosY = aViewData.GetCurY();
+ Point aSplit = aViewData.GetScrPos( nPosX, nPosY, ePos, true );
+ if ( nPosX > 0 )
+ DoHSplit( aSplit.X() + aWinStart.X() );
+ else
+ DoHSplit( 0 );
+ if ( nPosY > 0 )
+ DoVSplit( aSplit.Y() + aWinStart.Y() );
+ else
+ DoVSplit( 0 );
+ RepeatResize();
+}
+
+void ScTabView::SplitAtPixel( const Point& rPixel )
+{
+ // pixel is relative to the entire View, not to the first GridWin
+
+ if ( rPixel.X() > 0 )
+ DoHSplit( rPixel.X() );
+ else
+ DoHSplit( 0 );
+ if ( rPixel.Y() > 0 )
+ DoVSplit( rPixel.Y() );
+ else
+ DoVSplit( 0 );
+ RepeatResize();
+}
+
+void ScTabView::InvalidateSplit()
+{
+ SfxBindings& rBindings = aViewData.GetBindings();
+ rBindings.Invalidate( SID_WINDOW_SPLIT );
+ rBindings.Invalidate( SID_WINDOW_FIX );
+ rBindings.Invalidate( SID_WINDOW_FIX_COL );
+ rBindings.Invalidate( SID_WINDOW_FIX_ROW );
+
+ pHSplitter->SetFixed( aViewData.GetHSplitMode() == SC_SPLIT_FIX );
+ pVSplitter->SetFixed( aViewData.GetVSplitMode() == SC_SPLIT_FIX );
+}
+
+void ScTabView::SetNewVisArea()
+{
+ // Draw-MapMode must be set for Controls when VisAreaChanged
+ // (also when Edit-MapMode is set instead)
+ MapMode aOldMode[4];
+ MapMode aDrawMode[4];
+ sal_uInt16 i;
+ for (i=0; i<4; i++)
+ if (pGridWin[i])
+ {
+ aOldMode[i] = pGridWin[i]->GetMapMode();
+ aDrawMode[i] = pGridWin[i]->GetDrawMapMode();
+ if (aDrawMode[i] != aOldMode[i])
+ pGridWin[i]->SetMapMode(aDrawMode[i]);
+ }
+
+ vcl::Window* pActive = pGridWin[aViewData.GetActivePart()];
+ if (pActive)
+ aViewData.GetViewShell()->VisAreaChanged();
+ if (pDrawView)
+ pDrawView->VisAreaChanged(nullptr); // no window passed on -> for all windows
+
+ UpdateAllOverlays(); // #i79909# with drawing MapMode set
+
+ for (i=0; i<4; i++)
+ if (pGridWin[i] && aDrawMode[i] != aOldMode[i])
+ {
+ pGridWin[i]->flushOverlayManager(); // #i79909# flush overlays before switching to edit MapMode
+ pGridWin[i]->SetMapMode(aOldMode[i]);
+ }
+
+ SfxViewFrame& rViewFrame = aViewData.GetViewShell()->GetViewFrame();
+ SfxFrame& rFrame = rViewFrame.GetFrame();
+ css::uno::Reference<css::frame::XController> xController = rFrame.GetController();
+ if (xController.is())
+ {
+ ScTabViewObj* pImp = dynamic_cast<ScTabViewObj*>( xController.get() );
+ if (pImp)
+ pImp->VisAreaChanged();
+ }
+ if (aViewData.GetViewShell()->HasAccessibilityObjects())
+ aViewData.GetViewShell()->BroadcastAccessibility(SfxHint(SfxHintId::ScAccVisAreaChanged));
+}
+
+bool ScTabView::HasPageFieldDataAtCursor() const
+{
+ ScGridWindow* pWin = pGridWin[aViewData.GetActivePart()].get();
+ SCCOL nCol = aViewData.GetCurX();
+ SCROW nRow = aViewData.GetCurY();
+ if (pWin)
+ return pWin->GetDPFieldOrientation( nCol, nRow ) == sheet::DataPilotFieldOrientation_PAGE;
+
+ return false;
+}
+
+void ScTabView::StartDataSelect()
+{
+ ScGridWindow* pWin = pGridWin[aViewData.GetActivePart()].get();
+ SCCOL nCol = aViewData.GetCurX();
+ SCROW nRow = aViewData.GetCurY();
+
+ if (!pWin)
+ return;
+
+ switch (pWin->GetDPFieldOrientation(nCol, nRow))
+ {
+ case sheet::DataPilotFieldOrientation_PAGE:
+ // #i36598# If the cursor is on a page field's data cell,
+ // no meaningful input is possible anyway, so this function
+ // can be used to select a page field entry.
+ pWin->LaunchPageFieldMenu( nCol, nRow );
+ return;
+ case sheet::DataPilotFieldOrientation_COLUMN:
+ case sheet::DataPilotFieldOrientation_ROW:
+ pWin->LaunchDPFieldMenu( nCol, nRow );
+ return;
+ default:
+ ;
+ }
+
+ // Do autofilter if the current cell has autofilter button. Otherwise do
+ // a normal data select popup.
+ const ScMergeFlagAttr* pAttr =
+ aViewData.GetDocument().GetAttr(
+ nCol, nRow, aViewData.GetTabNo(), ATTR_MERGE_FLAG);
+
+ if (pAttr->HasAutoFilter())
+ pWin->LaunchAutoFilterMenu(nCol, nRow);
+ else
+ pWin->LaunchDataSelectMenu(nCol, nRow);
+}
+
+void ScTabView::EnableRefInput(bool bFlag)
+{
+ aHScrollLeft->EnableInput(bFlag);
+ aHScrollRight->EnableInput(bFlag);
+ aVScrollBottom->EnableInput(bFlag);
+ aVScrollTop->EnableInput(bFlag);
+
+ // from here on dynamically created ones
+
+ if(pTabControl!=nullptr) pTabControl->EnableInput(bFlag);
+
+ for (auto& p : pGridWin)
+ if (p)
+ p->EnableInput(bFlag, false);
+ for (auto& p : pColBar)
+ if (p)
+ p->EnableInput(bFlag, false);
+ for (auto& p : pRowBar)
+ if (p)
+ p->EnableInput(bFlag, false);
+}
+
+void ScTabView::EnableAutoSpell( bool bEnable )
+{
+ if (bEnable)
+ mpSpellCheckCxt =
+ std::make_shared<sc::SpellCheckContext>(&aViewData.GetDocument(),
+ aViewData.GetTabNo());
+ else
+ mpSpellCheckCxt.reset();
+
+ for (VclPtr<ScGridWindow> & pWin : pGridWin)
+ {
+ if (!pWin)
+ continue;
+
+ pWin->SetAutoSpellContext(mpSpellCheckCxt);
+ }
+}
+
+void ScTabView::ResetAutoSpell()
+{
+ for (VclPtr<ScGridWindow> & pWin : pGridWin)
+ {
+ if (!pWin)
+ continue;
+
+ pWin->ResetAutoSpell();
+ }
+}
+
+void ScTabView::ResetAutoSpellForContentChange()
+{
+ for (VclPtr<ScGridWindow> & pWin : pGridWin)
+ {
+ if (!pWin)
+ continue;
+
+ pWin->ResetAutoSpellForContentChange();
+ }
+}
+
+void ScTabView::SetAutoSpellData( SCCOL nPosX, SCROW nPosY, const std::vector<editeng::MisspellRanges>* pRanges )
+{
+ for (VclPtr<ScGridWindow> & pWin: pGridWin)
+ {
+ if (!pWin)
+ continue;
+
+ pWin->SetAutoSpellData(nPosX, nPosY, pRanges);
+ }
+}
+
+namespace
+{
+
+tools::Long lcl_GetRowHeightPx(const ScViewData &rViewData, SCROW nRow, SCTAB nTab)
+{
+ const sal_uInt16 nSize = rViewData.GetDocument().GetRowHeight(nRow, nTab);
+ return ScViewData::ToPixel(nSize, rViewData.GetPPTY());
+}
+
+tools::Long lcl_GetColWidthPx(const ScViewData &rViewData, SCCOL nCol, SCTAB nTab)
+{
+ const sal_uInt16 nSize = rViewData.GetDocument().GetColWidth(nCol, nTab);
+ return ScViewData::ToPixel(nSize, rViewData.GetPPTX());
+}
+
+void lcl_getGroupIndexes(const ScOutlineArray& rArray, SCCOLROW nStart, SCCOLROW nEnd, std::vector<size_t>& rGroupIndexes)
+{
+ rGroupIndexes.clear();
+ const size_t nGroupDepth = rArray.GetDepth();
+ rGroupIndexes.resize(nGroupDepth);
+
+ // Get first group per each level
+ for (size_t nLevel = 0; nLevel < nGroupDepth; ++nLevel)
+ {
+ if (rArray.GetCount(nLevel))
+ {
+ // look for a group inside the [nStartRow+1, nEndRow] range
+ size_t nIndex;
+ bool bFound = rArray.GetEntryIndexInRange(nLevel, nStart + 1, nEnd, nIndex);
+ if (bFound)
+ {
+ if (nIndex > 0)
+ {
+ // is there a previous group not inside the range
+ // but anyway intersecting it ?
+ const ScOutlineEntry* pPrevEntry = rArray.GetEntry(nLevel, nIndex - 1);
+ if (pPrevEntry && nStart < pPrevEntry->GetEnd())
+ {
+ --nIndex;
+ }
+ }
+ }
+ else
+ {
+ // look for a group which contains nStartRow+1
+ bFound = rArray.GetEntryIndex(nLevel, nStart + 1, nIndex);
+ if (!bFound)
+ {
+ // look for a group which contains nEndRow
+ bFound = rArray.GetEntryIndex(nLevel, nEnd, nIndex);
+ }
+ }
+
+ if (bFound)
+ {
+ // skip groups with no visible control
+ bFound = false;
+ while (nIndex < rArray.GetCount(nLevel))
+ {
+ const ScOutlineEntry* pEntry = rArray.GetEntry(nLevel, nIndex);
+ if (pEntry && pEntry->IsVisible())
+ {
+ bFound = true;
+ break;
+ }
+ if (pEntry && pEntry->GetStart() > nEnd)
+ {
+ break;
+ }
+ ++nIndex;
+ }
+ }
+
+ rGroupIndexes[nLevel] = bFound ? nIndex : -1;
+ }
+ }
+}
+
+void lcl_createGroupsData(
+ SCCOLROW nHeaderIndex, SCCOLROW nEnd, tools::Long nSizePx, tools::Long nTotalPx,
+ const ScOutlineArray& rArray, std::vector<size_t>& rGroupIndexes,
+ std::vector<tools::Long>& rGroupStartPositions, OStringBuffer& rGroupsBuffer)
+{
+ const size_t nGroupDepth = rArray.GetDepth();
+ // create string data for group controls
+ for (size_t nLevel = nGroupDepth - 1; nLevel != size_t(-1); --nLevel)
+ {
+ size_t nIndex = rGroupIndexes[nLevel];
+ if (nIndex == size_t(-1))
+ continue;
+ const ScOutlineEntry* pEntry = rArray.GetEntry(nLevel, nIndex);
+ if (pEntry)
+ {
+ if (nHeaderIndex < pEntry->GetStart())
+ {
+ continue;
+ }
+ else if (nHeaderIndex == pEntry->GetStart())
+ {
+ rGroupStartPositions[nLevel] = nTotalPx - nSizePx;
+ }
+ else if (nHeaderIndex > pEntry->GetStart() && (nHeaderIndex < nEnd && nHeaderIndex < pEntry->GetEnd()))
+ {
+ // for handling group started before the current view range
+ if (rGroupStartPositions[nLevel] < 0)
+ rGroupStartPositions[nLevel] *= -1;
+ break;
+ }
+ if (nHeaderIndex == pEntry->GetEnd() || (nHeaderIndex == nEnd && rGroupStartPositions[nLevel] != -1))
+ {
+ // nHeaderIndex is the end col/row of a group or is the last col/row and a group started and not yet ended
+
+ // append a new group control data
+ auto len = rGroupsBuffer.getLength();
+ if (len && rGroupsBuffer[len-1] == '}')
+ {
+ rGroupsBuffer.append(", ");
+ }
+
+ bool bGroupHidden = pEntry->IsHidden();
+
+ rGroupsBuffer.append(
+ "{ \"level\": " + OString::number(nLevel + 1) + ", "
+ "\"index\": " + OString::number(nIndex) + ", "
+ "\"startPos\": " + OString::number(rGroupStartPositions[nLevel]) + ", "
+ "\"endPos\": " + OString::number(nTotalPx) + ", "
+ "\"hidden\": " + OString::number(bGroupHidden ? 1 : 0) + " }");
+
+ // look for the next visible group control at level nLevel
+ bool bFound = false;
+ ++nIndex;
+ while (nIndex < rArray.GetCount(nLevel))
+ {
+ pEntry = rArray.GetEntry(nLevel, nIndex);
+ if (pEntry && pEntry->IsVisible())
+ {
+ bFound = true;
+ break;
+ }
+ if (pEntry && pEntry->GetStart() > nEnd)
+ {
+ break;
+ }
+ ++nIndex;
+ }
+ rGroupIndexes[nLevel] = bFound ? nIndex : -1;
+ rGroupStartPositions[nLevel] = -1;
+ }
+ }
+ }
+}
+
+class ScRangeProvider
+{
+public:
+ ScRangeProvider(const tools::Rectangle& rArea, bool bInPixels,
+ ScViewData& rViewData):
+ mrViewData(rViewData)
+ {
+ tools::Rectangle aAreaPx = bInPixels ? rArea :
+ tools::Rectangle(rArea.Left() * mrViewData.GetPPTX(),
+ rArea.Top() * mrViewData.GetPPTY(),
+ rArea.Right() * mrViewData.GetPPTX(),
+ rArea.Bottom() * mrViewData.GetPPTY());
+ calculateBounds(aAreaPx);
+ }
+
+ const ScRange& getCellRange() const
+ {
+ return maRange;
+ }
+
+ void getColPositions(tools::Long& rStartColPos, tools::Long& rEndColPos) const
+ {
+ rStartColPos = maBoundPositions.Left();
+ rEndColPos = maBoundPositions.Right();
+ }
+
+ void getRowPositions(tools::Long& rStartRowPos, tools::Long& rEndRowPos) const
+ {
+ rStartRowPos = maBoundPositions.Top();
+ rEndRowPos = maBoundPositions.Bottom();
+ }
+
+private:
+ void calculateBounds(const tools::Rectangle& rAreaPx)
+ {
+ tools::Long nLeftPx = 0, nRightPx = 0;
+ SCCOLROW nStartCol = -1, nEndCol = -1;
+ calculateDimensionBounds(rAreaPx.Left(), rAreaPx.Right(), true,
+ nStartCol, nEndCol, nLeftPx, nRightPx,
+ mnEnlargeX, mrViewData);
+ tools::Long nTopPx = 0, nBottomPx = 0;
+ SCCOLROW nStartRow = -1, nEndRow = -1;
+ calculateDimensionBounds(rAreaPx.Top(), rAreaPx.Bottom(), false,
+ nStartRow, nEndRow, nTopPx, nBottomPx,
+ mnEnlargeY, mrViewData);
+
+ maRange.aStart.Set(nStartCol, nStartRow, mrViewData.GetTabNo());
+ maRange.aEnd.Set(nEndCol, nEndRow, mrViewData.GetTabNo());
+
+ maBoundPositions.SetLeft(nLeftPx);
+ maBoundPositions.SetRight(nRightPx);
+ maBoundPositions.SetTop(nTopPx);
+ maBoundPositions.SetBottom(nBottomPx);
+ }
+
+ // All positions are in pixels.
+ static void calculateDimensionBounds(const tools::Long nStartPos, const tools::Long nEndPos,
+ bool bColumns, SCCOLROW& rStartIndex,
+ SCCOLROW& rEndIndex, tools::Long& rBoundStart,
+ tools::Long& rBoundEnd, SCCOLROW nEnlarge,
+ ScViewData& rViewData)
+ {
+ ScPositionHelper& rPosHelper = bColumns ? rViewData.GetLOKWidthHelper() :
+ rViewData.GetLOKHeightHelper();
+ const auto& rStartNearest = rPosHelper.getNearestByPosition(nStartPos);
+ const auto& rEndNearest = rPosHelper.getNearestByPosition(nEndPos);
+
+ ScBoundsProvider aBoundsProvider(rViewData, rViewData.GetTabNo(), bColumns);
+ aBoundsProvider.Compute(rStartNearest, rEndNearest, nStartPos, nEndPos);
+ aBoundsProvider.EnlargeBy(nEnlarge);
+ if (bColumns)
+ {
+ SCCOL nStartCol = -1, nEndCol = -1;
+ aBoundsProvider.GetStartIndexAndPosition(nStartCol, rBoundStart);
+ aBoundsProvider.GetEndIndexAndPosition(nEndCol, rBoundEnd);
+ rStartIndex = nStartCol;
+ rEndIndex = nEndCol;
+ }
+ else
+ {
+ SCROW nStartRow = -1, nEndRow = -1;
+ aBoundsProvider.GetStartIndexAndPosition(nStartRow, rBoundStart);
+ aBoundsProvider.GetEndIndexAndPosition(nEndRow, rBoundEnd);
+ rStartIndex = nStartRow;
+ rEndIndex = nEndRow;
+ }
+ }
+
+private:
+
+ ScRange maRange;
+ tools::Rectangle maBoundPositions;
+ ScViewData& mrViewData;
+ static const SCCOLROW mnEnlargeX = 2;
+ static const SCCOLROW mnEnlargeY = 2;
+};
+
+void lcl_ExtendTiledDimension(bool bColumn, const SCCOLROW nEnd, const SCCOLROW nExtra,
+ ScTabView& rTabView, ScViewData& rViewData)
+{
+ ScDocument& rDoc = rViewData.GetDocument();
+ // If we are approaching current max tiled row/col, signal a size changed event
+ // and invalidate the involved area
+ SCCOLROW nMaxTiledIndex = bColumn ? rViewData.GetMaxTiledCol() : rViewData.GetMaxTiledRow();
+ SCCOLROW nHardLimit = !bColumn ? MAXTILEDROW : rDoc.MaxCol();
+
+ if (nMaxTiledIndex >= nHardLimit)
+ return;
+
+ if (nEnd <= nMaxTiledIndex - nExtra) // No need to extend.
+ return;
+
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+ ScModelObj* pModelObj = pDocSh ? pDocSh->GetModel() : nullptr;
+ Size aOldSize(0, 0);
+ if (pModelObj)
+ aOldSize = pModelObj->getDocumentSize();
+
+ SCCOLROW nNewMaxTiledIndex = std::min(std::max(nEnd, nMaxTiledIndex) + nExtra, nHardLimit);
+
+ if (bColumn)
+ rViewData.SetMaxTiledCol(nNewMaxTiledIndex);
+ else
+ rViewData.SetMaxTiledRow(nNewMaxTiledIndex);
+
+ Size aNewSize(0, 0);
+ if (pModelObj)
+ aNewSize = pModelObj->getDocumentSize();
+
+ if (aOldSize == aNewSize)
+ return;
+
+ if (!pDocSh)
+ return;
+
+ // New area extended to the right/bottom of the sheet after last col/row
+ // excluding overlapping area with aNewArea
+ tools::Rectangle aNewArea = bColumn ?
+ tools::Rectangle(aOldSize.getWidth(), 0, aNewSize.getWidth(), aNewSize.getHeight()):
+ tools::Rectangle(0, aOldSize.getHeight(), aNewSize.getWidth(), aNewSize.getHeight());
+
+ // Only invalidate if spreadsheet has extended to the right or bottom
+ if ((bColumn && aNewArea.getOpenWidth()) || (!bColumn && aNewArea.getOpenHeight()))
+ {
+ rTabView.UpdateSelectionOverlay();
+ SfxLokHelper::notifyInvalidation(rViewData.GetViewShell(), &aNewArea);
+ }
+
+ // Provide size in the payload, so clients don't have to query for that.
+ std::stringstream ss;
+ ss << aNewSize.Width() << ", " << aNewSize.Height();
+ OString sSize( ss.str() );
+ ScModelObj* pModel = comphelper::getFromUnoTunnel<ScModelObj>(
+ rViewData.GetViewShell()->GetCurrentDocument());
+ SfxLokHelper::notifyDocumentSizeChanged(rViewData.GetViewShell(), sSize, pModel, false);
+}
+
+} // anonymous namespace
+
+void ScTabView::getRowColumnHeaders(const tools::Rectangle& rRectangle, tools::JsonWriter& rJsonWriter)
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+
+ if (rRectangle.IsEmpty())
+ return;
+
+ bool bRangeHeaderSupport = comphelper::LibreOfficeKit::isRangeHeaders();
+
+ rJsonWriter.put("commandName", ".uno:ViewRowColumnHeaders");
+
+ SCTAB nTab = aViewData.GetTabNo();
+ SCROW nStartRow = -1;
+ SCROW nEndRow = -1;
+ tools::Long nStartHeightPx = 0;
+ SCCOL nStartCol = -1;
+ SCCOL nEndCol = -1;
+ tools::Long nStartWidthPx = 0;
+
+ tools::Rectangle aOldVisArea(
+ mnLOKStartHeaderCol + 1, mnLOKStartHeaderRow + 1,
+ mnLOKEndHeaderCol, mnLOKEndHeaderRow);
+
+ ScRangeProvider aRangeProvider(rRectangle, /* bInPixels */ false, aViewData);
+ const ScRange& rCellRange = aRangeProvider.getCellRange();
+
+ /// *** start collecting ROWS ***
+
+ /// 1) compute start and end rows
+
+ if (rRectangle.Top() < rRectangle.Bottom())
+ {
+ SAL_INFO("sc.lok.header", "Row Header: compute start/end rows.");
+ tools::Long nEndHeightPx = 0;
+ nStartRow = rCellRange.aStart.Row();
+ nEndRow = rCellRange.aEnd.Row();
+ aRangeProvider.getRowPositions(nStartHeightPx, nEndHeightPx);
+
+ aViewData.GetLOKHeightHelper().removeByIndex(mnLOKStartHeaderRow);
+ aViewData.GetLOKHeightHelper().removeByIndex(mnLOKEndHeaderRow);
+ aViewData.GetLOKHeightHelper().insert(nStartRow, nStartHeightPx);
+ aViewData.GetLOKHeightHelper().insert(nEndRow, nEndHeightPx);
+
+ mnLOKStartHeaderRow = nStartRow;
+ mnLOKEndHeaderRow = nEndRow;
+ }
+
+ sal_Int32 nVisibleRows = nEndRow - nStartRow;
+ if (nVisibleRows < 25)
+ nVisibleRows = 25;
+
+ SAL_INFO("sc.lok.header", "Row Header: visible rows: " << nVisibleRows);
+
+
+ // Get row groups
+ // per each level store the index of the first group intersecting
+ // [nStartRow, nEndRow] range
+
+ const ScOutlineTable* pTable = rDoc.GetOutlineTable(nTab);
+ const ScOutlineArray* pRowArray = pTable ? &(pTable->GetRowArray()) : nullptr;
+ size_t nRowGroupDepth = 0;
+ std::vector<size_t> aRowGroupIndexes;
+ if (bRangeHeaderSupport && pTable)
+ {
+ nRowGroupDepth = pRowArray->GetDepth();
+ lcl_getGroupIndexes(*pRowArray, nStartRow, nEndRow, aRowGroupIndexes);
+ }
+
+ /// 2) if we are approaching current max tiled row, signal a size changed event
+ /// and invalidate the involved area
+ lcl_ExtendTiledDimension(/* bColumn */ false, nEndRow, nVisibleRows, *this, aViewData);
+
+ /// 3) create string data for rows
+
+ tools::Long nTotalPixels = nStartHeightPx;
+ tools::Long nPrevSizePx = -1;
+ OStringBuffer aRowGroupsBuffer = "\"rowGroups\": [\n";
+ {
+ auto rowsNode = rJsonWriter.startArray("rows");
+
+ SAL_INFO("sc.lok.header", "Row Header: [create string data for rows]: start row: "
+ << nStartRow << " start height: " << nTotalPixels);
+
+ if (nStartRow != nEndRow)
+ {
+ auto node = rJsonWriter.startStruct();
+ rJsonWriter.put("text", nStartRow + 1);
+ rJsonWriter.put("size", nTotalPixels);
+ rJsonWriter.put("groupLevels", static_cast<sal_Int64>(nRowGroupDepth));
+ }
+
+ std::vector<tools::Long> aRowGroupStartPositions(nRowGroupDepth, -nTotalPixels);
+ for (SCROW nRow = nStartRow + 1; nRow <= nEndRow; ++nRow)
+ {
+ // nSize will be 0 for hidden rows.
+ const tools::Long nSizePx = lcl_GetRowHeightPx(aViewData, nRow, nTab);
+ nTotalPixels += nSizePx;
+
+ if (bRangeHeaderSupport && nRowGroupDepth > 0)
+ {
+ lcl_createGroupsData(nRow, nEndRow, nSizePx, nTotalPixels,
+ *pRowArray, aRowGroupIndexes, aRowGroupStartPositions,
+ aRowGroupsBuffer);
+ }
+
+ if (bRangeHeaderSupport && nRow < nEndRow && nSizePx == nPrevSizePx)
+ continue;
+ nPrevSizePx = nSizePx;
+
+ auto node = rJsonWriter.startStruct();
+ rJsonWriter.put("text", pRowBar[SC_SPLIT_BOTTOM]->GetEntryText(nRow));
+ rJsonWriter.put("size", nTotalPixels);
+ }
+
+ aRowGroupsBuffer.append("]");
+ }
+ if (nRowGroupDepth > 0)
+ {
+ aRowGroupsBuffer.append(",\n");
+ rJsonWriter.putRaw(aRowGroupsBuffer);
+ }
+ /// end collecting ROWS
+
+
+ /// *** start collecting COLS ***
+
+ /// 1) compute start and end columns
+
+ if (rRectangle.Left() < rRectangle.Right())
+ {
+ SAL_INFO("sc.lok.header", "Column Header: compute start/end columns.");
+ tools::Long nEndWidthPx = 0;
+ nStartCol = rCellRange.aStart.Col();
+ nEndCol = rCellRange.aEnd.Col();
+ aRangeProvider.getColPositions(nStartWidthPx, nEndWidthPx);
+
+ aViewData.GetLOKWidthHelper().removeByIndex(mnLOKStartHeaderCol);
+ aViewData.GetLOKWidthHelper().removeByIndex(mnLOKEndHeaderCol);
+ aViewData.GetLOKWidthHelper().insert(nStartCol, nStartWidthPx);
+ aViewData.GetLOKWidthHelper().insert(nEndCol, nEndWidthPx);
+
+ mnLOKStartHeaderCol = nStartCol;
+ mnLOKEndHeaderCol = nEndCol;
+ }
+
+ sal_Int32 nVisibleCols = nEndCol - nStartCol;
+ if (nVisibleCols < 10)
+ nVisibleCols = 10;
+
+
+ // Get column groups
+ // per each level store the index of the first group intersecting
+ // [nStartCol, nEndCol] range
+
+ const ScOutlineArray* pColArray = pTable ? &(pTable->GetColArray()) : nullptr;
+ size_t nColGroupDepth = 0;
+ std::vector<size_t> aColGroupIndexes;
+ if (bRangeHeaderSupport && pTable)
+ {
+ nColGroupDepth = pColArray->GetDepth();
+ lcl_getGroupIndexes(*pColArray, nStartCol, nEndCol, aColGroupIndexes);
+ }
+
+ /// 2) if we are approaching current max tiled column, signal a size changed event
+ /// and invalidate the involved area
+ lcl_ExtendTiledDimension(/* bColumn */ true, nEndCol, nVisibleCols, *this, aViewData);
+
+ /// 3) create string data for columns
+ OStringBuffer aColGroupsBuffer = "\"columnGroups\": [\n";
+ {
+ auto columnsNode = rJsonWriter.startArray("columns");
+
+ nTotalPixels = nStartWidthPx;
+ SAL_INFO("sc.lok.header", "Col Header: [create string data for cols]: start col: "
+ << nStartRow << " start width: " << nTotalPixels);
+
+ if (nStartCol != nEndCol)
+ {
+ auto node = rJsonWriter.startStruct();
+ rJsonWriter.put("text", static_cast<sal_Int64>(nStartCol + 1));
+ rJsonWriter.put("size", nTotalPixels);
+ rJsonWriter.put("groupLevels", static_cast<sal_Int64>(nColGroupDepth));
+ }
+
+ std::vector<tools::Long> aColGroupStartPositions(nColGroupDepth, -nTotalPixels);
+ nPrevSizePx = -1;
+ for (SCCOL nCol = nStartCol + 1; nCol <= nEndCol; ++nCol)
+ {
+ // nSize will be 0 for hidden columns.
+ const tools::Long nSizePx = lcl_GetColWidthPx(aViewData, nCol, nTab);
+ nTotalPixels += nSizePx;
+
+ if (bRangeHeaderSupport && nColGroupDepth > 0)
+ lcl_createGroupsData(nCol, nEndCol, nSizePx, nTotalPixels,
+ *pColArray, aColGroupIndexes,
+ aColGroupStartPositions, aColGroupsBuffer);
+
+ if (bRangeHeaderSupport && nCol < nEndCol && nSizePx == nPrevSizePx)
+ continue;
+ nPrevSizePx = nSizePx;
+
+ OUString aText = bRangeHeaderSupport ?
+ OUString::number(nCol + 1) : pColBar[SC_SPLIT_LEFT]->GetEntryText(nCol);
+
+ auto node = rJsonWriter.startStruct();
+ rJsonWriter.put("text", aText);
+ rJsonWriter.put("size", nTotalPixels);
+ }
+
+ aColGroupsBuffer.append("]");
+ }
+ if (nColGroupDepth > 0)
+ {
+ aColGroupsBuffer.append(",\n");
+ rJsonWriter.putRaw(aColGroupsBuffer);
+ }
+ /// end collecting COLs
+
+ vcl::Region aNewVisArea(
+ tools::Rectangle(mnLOKStartHeaderCol + 1, mnLOKStartHeaderRow + 1,
+ mnLOKEndHeaderCol, mnLOKEndHeaderRow));
+ aNewVisArea.Exclude(aOldVisArea);
+ tools::Rectangle aChangedArea = aNewVisArea.GetBoundRect();
+ if (!aChangedArea.IsEmpty())
+ {
+ UpdateVisibleRange();
+ UpdateFormulas(aChangedArea.Left(), aChangedArea.Top(), aChangedArea.Right(), aChangedArea.Bottom());
+ }
+}
+
+OString ScTabView::getSheetGeometryData(bool bColumns, bool bRows, bool bSizes, bool bHidden,
+ bool bFiltered, bool bGroups)
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+
+ boost::property_tree::ptree aTree;
+ aTree.put("commandName", ".uno:SheetGeometryData");
+ aTree.put("maxtiledcolumn", rDoc.MaxCol());
+ aTree.put("maxtiledrow", MAXTILEDROW);
+
+ auto getJSONString = [](const boost::property_tree::ptree& rTree) {
+ std::stringstream aStream;
+ boost::property_tree::write_json(aStream, rTree);
+ return aStream.str();
+ };
+
+ if ((!bSizes && !bHidden && !bFiltered && !bGroups) ||
+ (!bColumns && !bRows))
+ {
+ return OString(getJSONString(aTree));
+ }
+
+ struct GeomEntry
+ {
+ SheetGeomType eType;
+ const char* pKey;
+ bool bEnabled;
+ };
+
+ const GeomEntry aGeomEntries[] = {
+ { SheetGeomType::SIZES, "sizes", bSizes },
+ { SheetGeomType::HIDDEN, "hidden", bHidden },
+ { SheetGeomType::FILTERED, "filtered", bFiltered },
+ { SheetGeomType::GROUPS, "groups", bGroups }
+ };
+
+ struct DimensionEntry
+ {
+ const char* pKey;
+ bool bDimIsCol;
+ bool bEnabled;
+ };
+
+ const DimensionEntry aDimEntries[] = {
+ { "columns", true, bColumns },
+ { "rows", false, bRows }
+ };
+
+ SCTAB nTab = aViewData.GetTabNo();
+
+ for (const auto& rDimEntry : aDimEntries)
+ {
+ if (!rDimEntry.bEnabled)
+ continue;
+
+ bool bDimIsCol = rDimEntry.bDimIsCol;
+
+ boost::property_tree::ptree aDimTree;
+ for (const auto& rGeomEntry : aGeomEntries)
+ {
+ if (!rGeomEntry.bEnabled)
+ continue;
+
+ OString aGeomDataEncoding = rDoc.dumpSheetGeomData(nTab, bDimIsCol, rGeomEntry.eType);
+ // TODO: Investigate if we can avoid the copy of the 'value' string in put().
+ aDimTree.put(rGeomEntry.pKey, aGeomDataEncoding.getStr());
+ }
+
+ aTree.add_child(rDimEntry.pKey, aDimTree);
+ }
+
+ return OString(getJSONString(aTree));
+}
+
+void ScTabView::extendTiledAreaIfNeeded()
+{
+ SAL_INFO("sc.lok.header",
+ "extendTiledAreaIfNeeded: START: ClientView: ColRange["
+ << mnLOKStartHeaderCol << "," << mnLOKEndHeaderCol
+ << "] RowRange[" << mnLOKStartHeaderRow << "," << mnLOKEndHeaderRow
+ << "] MaxTiledCol = " << aViewData.GetMaxTiledCol()
+ << " MaxTiledRow = " << aViewData.GetMaxTiledRow());
+
+ const tools::Rectangle rVisArea = aViewData.getLOKVisibleArea();
+ if (rVisArea.Top() >= rVisArea.Bottom() ||
+ rVisArea.Left() >= rVisArea.Right())
+ return;
+
+ // Needed for conditional updating of visible-range/formula.
+ tools::Rectangle aOldVisCellRange(mnLOKStartHeaderCol + 1, mnLOKStartHeaderRow + 1,
+ mnLOKEndHeaderCol, mnLOKEndHeaderRow);
+
+ ScRangeProvider aRangeProvider(rVisArea, /* bInPixels */ false, aViewData);
+ // Index bounds.
+ const ScRange& rCellRange = aRangeProvider.getCellRange();
+ const SCCOL nStartCol = rCellRange.aStart.Col();
+ const SCCOL nEndCol = rCellRange.aEnd.Col();
+ const SCROW nStartRow = rCellRange.aStart.Row();
+ const SCROW nEndRow = rCellRange.aEnd.Row();
+
+ // Column/Row positions.
+ tools::Long nStartColPos, nEndColPos, nStartRowPos, nEndRowPos;
+ aRangeProvider.getColPositions(nStartColPos, nEndColPos);
+ aRangeProvider.getRowPositions(nStartRowPos, nEndRowPos);
+
+ ScPositionHelper& rWidthHelper = aViewData.GetLOKWidthHelper();
+ ScPositionHelper& rHeightHelper = aViewData.GetLOKHeightHelper();
+
+ // Update mnLOKStartHeaderCol and mnLOKEndHeaderCol members.
+ // These are consulted in some ScGridWindow methods.
+ if (mnLOKStartHeaderCol != nStartCol)
+ {
+ rWidthHelper.removeByIndex(mnLOKStartHeaderCol);
+ rWidthHelper.insert(nStartCol, nStartColPos);
+ mnLOKStartHeaderCol = nStartCol;
+ }
+
+ if (mnLOKEndHeaderCol != nEndCol)
+ {
+ rWidthHelper.removeByIndex(mnLOKEndHeaderCol);
+ rWidthHelper.insert(nEndCol, nEndColPos);
+ mnLOKEndHeaderCol = nEndCol;
+ }
+
+ // Update mnLOKStartHeaderRow and mnLOKEndHeaderRow members.
+ // These are consulted in some ScGridWindow methods.
+ if (mnLOKStartHeaderRow != nStartRow)
+ {
+ rHeightHelper.removeByIndex(mnLOKStartHeaderRow);
+ rHeightHelper.insert(nStartRow, nStartRowPos);
+ mnLOKStartHeaderRow = nStartRow;
+ }
+
+ if (mnLOKEndHeaderRow != nEndRow)
+ {
+ rHeightHelper.removeByIndex(mnLOKEndHeaderRow);
+ rHeightHelper.insert(nEndRow, nEndRowPos);
+ mnLOKEndHeaderRow = nEndRow;
+ }
+
+ constexpr SCCOL nMinExtraCols = 10;
+ SCCOL nExtraCols = std::max<SCCOL>(nMinExtraCols, nEndCol - nStartCol);
+ // If we are approaching current max tiled column, signal a size changed event
+ // and invalidate the involved area.
+ lcl_ExtendTiledDimension(/* bColumn */ true, nEndCol, nExtraCols, *this, aViewData);
+
+ constexpr SCROW nMinExtraRows = 25;
+ SCROW nExtraRows = std::max(nMinExtraRows, nEndRow - nStartRow);
+ // If we are approaching current max tiled row, signal a size changed event
+ // and invalidate the involved area.
+ lcl_ExtendTiledDimension(/* bColumn */ false, nEndRow, nExtraRows, *this, aViewData);
+
+ vcl::Region aNewVisCellRange(
+ tools::Rectangle(mnLOKStartHeaderCol + 1, mnLOKStartHeaderRow + 1,
+ mnLOKEndHeaderCol, mnLOKEndHeaderRow));
+ aNewVisCellRange.Exclude(aOldVisCellRange);
+ tools::Rectangle aChangedCellRange = aNewVisCellRange.GetBoundRect();
+ if (!aChangedCellRange.IsEmpty())
+ {
+ UpdateVisibleRange();
+ UpdateFormulas(aChangedCellRange.Left(), aChangedCellRange.Top(),
+ aChangedCellRange.Right(), aChangedCellRange.Bottom());
+ }
+
+ SAL_INFO("sc.lok.header",
+ "extendTiledAreaIfNeeded: END: ClientView: ColRange["
+ << mnLOKStartHeaderCol << "," << mnLOKEndHeaderCol
+ << "] RowRange[" << mnLOKStartHeaderRow << "," << mnLOKEndHeaderRow
+ << "] MaxTiledCol = " << aViewData.GetMaxTiledCol()
+ << " MaxTiledRow = " << aViewData.GetMaxTiledRow());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabview2.cxx b/sc/source/ui/view/tabview2.cxx
new file mode 100644
index 0000000000..d5be3d5b59
--- /dev/null
+++ b/sc/source/ui/view/tabview2.cxx
@@ -0,0 +1,1709 @@
+/* -*- 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 .
+ */
+
+#include <scitems.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <sfx2/bindings.hxx>
+#include <osl/diagnose.h>
+
+#include <attrib.hxx>
+#include <pagedata.hxx>
+#include <tabview.hxx>
+#include <tabvwsh.hxx>
+#include <printfun.hxx>
+#include <stlpool.hxx>
+#include <docsh.hxx>
+#include <gridwin.hxx>
+#include <sc.hrc>
+#include <viewutil.hxx>
+#include <colrowba.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <scmod.hxx>
+#include <table.hxx>
+#include <tabprotection.hxx>
+#include <markdata.hxx>
+#include <inputopt.hxx>
+#include <comphelper/lok.hxx>
+
+namespace {
+
+bool isCellQualified(const ScDocument* pDoc, SCCOL nCol, SCROW nRow, SCTAB nTab, bool bSelectLocked, bool bSelectUnlocked)
+{
+ bool bCellProtected = pDoc->HasAttrib(
+ nCol, nRow, nTab, nCol, nRow, nTab, HasAttrFlags::Protected);
+
+ if (bCellProtected && !bSelectLocked)
+ return false;
+
+ if (!bCellProtected && !bSelectUnlocked)
+ return false;
+
+ return true;
+}
+
+bool areCellsQualified(const ScDocument* pDoc, SCCOL nColStart, SCROW nRowStart, SCCOL nColEnd,
+ SCROW nRowEnd, SCTAB nTab, bool bSelectLocked, bool bSelectUnlocked)
+{
+ PutInOrder(nColStart, nColEnd);
+ PutInOrder(nRowStart, nRowEnd);
+ for (SCCOL col = nColStart; col <= nColEnd; ++col)
+ for (SCROW row = nRowStart; row <= nRowEnd; ++row)
+ if (!isCellQualified(pDoc, col, row, nTab, bSelectLocked, bSelectUnlocked))
+ return false;
+
+ return true;
+}
+
+void moveCursorByProtRule(
+ SCCOL& rCol, SCROW& rRow, SCCOL nMovX, SCROW nMovY, SCTAB nTab, const ScDocument* pDoc)
+{
+ bool bSelectLocked = true;
+ bool bSelectUnlocked = true;
+ const ScTableProtection* pTabProtection = pDoc->GetTabProtection(nTab);
+ if (pTabProtection && pTabProtection->isProtected())
+ {
+ bSelectLocked = pTabProtection->isOptionEnabled(ScTableProtection::SELECT_LOCKED_CELLS);
+ bSelectUnlocked = pTabProtection->isOptionEnabled(ScTableProtection::SELECT_UNLOCKED_CELLS);
+ }
+
+ if (nMovX > 0)
+ {
+ for (SCCOL i = 0; i < nMovX && rCol < pDoc->MaxCol(); ++i)
+ {
+ SCCOL nNewUnhiddenCol = rCol + 1;
+ SCCOL nEndCol = 0;
+ while(pDoc->ColHidden(nNewUnhiddenCol, nTab, nullptr, &nEndCol))
+ {
+ if(nNewUnhiddenCol >= pDoc->MaxCol())
+ return;
+
+ i += nEndCol - nNewUnhiddenCol + 1;
+ nNewUnhiddenCol = nEndCol +1;
+ }
+
+ if (!isCellQualified(pDoc, nNewUnhiddenCol, rRow, nTab, bSelectLocked, bSelectUnlocked))
+ break;
+ rCol = nNewUnhiddenCol;
+ }
+ }
+ else if (nMovX < 0)
+ {
+ for (SCCOL i = 0; i > nMovX && rCol > 0; --i)
+ {
+ SCCOL nNewUnhiddenCol = rCol - 1;
+ SCCOL nStartCol = 0;
+ while(pDoc->ColHidden(nNewUnhiddenCol, nTab, &nStartCol))
+ {
+ if(nNewUnhiddenCol <= 0)
+ return;
+
+ i -= nNewUnhiddenCol - nStartCol + 1;
+ nNewUnhiddenCol = nStartCol - 1;
+ }
+
+ if (!isCellQualified(pDoc, nNewUnhiddenCol, rRow, nTab, bSelectLocked, bSelectUnlocked))
+ break;
+ rCol = nNewUnhiddenCol;
+ }
+ }
+
+ if (nMovY > 0)
+ {
+ for (SCROW i = 0; i < nMovY && rRow < pDoc->MaxRow(); ++i)
+ {
+ SCROW nNewUnhiddenRow = rRow + 1;
+ SCROW nEndRow = 0;
+ while(pDoc->RowHidden(nNewUnhiddenRow, nTab, nullptr, &nEndRow))
+ {
+ if(nNewUnhiddenRow >= pDoc->MaxRow())
+ return;
+
+ i += nEndRow - nNewUnhiddenRow + 1;
+ nNewUnhiddenRow = nEndRow + 1;
+ }
+
+ if (!isCellQualified(pDoc, rCol, nNewUnhiddenRow, nTab, bSelectLocked, bSelectUnlocked))
+ break;
+ rRow = nNewUnhiddenRow;
+ }
+ }
+ else if (nMovY < 0)
+ {
+ for (SCROW i = 0; i > nMovY && rRow > 0; --i)
+ {
+ SCROW nNewUnhiddenRow = rRow - 1;
+ SCROW nStartRow = 0;
+ while(pDoc->RowHidden(nNewUnhiddenRow, nTab, &nStartRow))
+ {
+ if(nNewUnhiddenRow <= 0)
+ return;
+
+ i -= nNewUnhiddenRow - nStartRow + 1;
+ nNewUnhiddenRow = nStartRow - 1;
+ }
+
+ if (!isCellQualified(pDoc, rCol, nNewUnhiddenRow, nTab, bSelectLocked, bSelectUnlocked))
+ break;
+ rRow = nNewUnhiddenRow;
+ }
+ }
+}
+
+bool checkBoundary(const ScDocument* pDoc, SCCOL& rCol, SCROW& rRow)
+{
+ bool bGood = true;
+ if (rCol < 0)
+ {
+ rCol = 0;
+ bGood = false;
+ }
+ else if (rCol > pDoc->MaxCol())
+ {
+ rCol = pDoc->MaxCol();
+ bGood = false;
+ }
+
+ if (rRow < 0)
+ {
+ rRow = 0;
+ bGood = false;
+ }
+ else if (rRow > pDoc->MaxRow())
+ {
+ rRow = pDoc->MaxRow();
+ bGood = false;
+ }
+ return bGood;
+}
+
+void moveRefByCell(SCCOL& rNewX, SCROW& rNewY,
+ SCCOL nMovX, SCROW nMovY, SCTAB nRefTab,
+ const ScDocument& rDoc)
+{
+ SCCOL nOldX = rNewX;
+ SCROW nOldY = rNewY;
+ bool bSelectLocked = true;
+ bool bSelectUnlocked = true;
+ const ScTableProtection* pTabProtection = rDoc.GetTabProtection(nRefTab);
+ if (pTabProtection && pTabProtection->isProtected())
+ {
+ bSelectLocked = pTabProtection->isOptionEnabled(ScTableProtection::SELECT_LOCKED_CELLS);
+ bSelectUnlocked = pTabProtection->isOptionEnabled(ScTableProtection::SELECT_UNLOCKED_CELLS);
+ }
+
+ moveCursorByProtRule(rNewX, rNewY, nMovX, nMovY, nRefTab, &rDoc);
+ checkBoundary(&rDoc, rNewX, rNewY);
+
+ if (nMovX)
+ {
+ SCCOL nTempX = rNewX;
+ while (rDoc.IsHorOverlapped(nTempX, rNewY, nRefTab))
+ {
+ nTempX = (nMovX > 0) ? nTempX + 1 : nTempX - 1;
+ if (!checkBoundary(&rDoc, nTempX, rNewY))
+ break;
+ }
+ if (isCellQualified(&rDoc, nTempX, rNewY, nRefTab, bSelectLocked, bSelectUnlocked))
+ rNewX = nTempX;
+
+ if (nMovX < 0 && rNewX > 0)
+ {
+ const ScMergeAttr* pMergeAttr = rDoc.GetAttr(rNewX, rNewY, nRefTab, ATTR_MERGE);
+ if (pMergeAttr && pMergeAttr->IsMerged() &&
+ nOldX >= rNewX &&
+ nOldX <= rNewX + pMergeAttr->GetRowMerge() - 1)
+ rNewX = rNewX - 1;
+ }
+ }
+
+ if (nMovY)
+ {
+ SCROW nTempY = rNewY;
+ while (rDoc.IsVerOverlapped(rNewX, nTempY, nRefTab))
+ {
+ nTempY = (nMovY > 0) ? nTempY + 1 : nTempY - 1;
+ if (!checkBoundary(&rDoc, rNewX, nTempY))
+ break;
+ }
+ if (isCellQualified(&rDoc, rNewX, nTempY, nRefTab, bSelectLocked, bSelectUnlocked))
+ rNewY = nTempY;
+
+ if (nMovY < 0 && rNewY > 0)
+ {
+ const ScMergeAttr* pMergeAttr = rDoc.GetAttr(rNewX, rNewY, nRefTab, ATTR_MERGE);
+ if (pMergeAttr && pMergeAttr->IsMerged() &&
+ nOldY >= rNewY &&
+ nOldY <= rNewY + pMergeAttr->GetRowMerge() - 1)
+ rNewY = rNewY - 1;
+ }
+ }
+
+ rDoc.SkipOverlapped(rNewX, rNewY, nRefTab);
+}
+
+void moveCursorByMergedCell(SCCOL& rCol, SCROW& rRow, SCCOL nMovX, SCROW nMovY, SCCOL nStartX,
+ SCROW nStartY, SCTAB nTab, const ScDocument* pDoc)
+{
+ const ScTableProtection* pTabProtection = pDoc->GetTabProtection(nTab);
+ bool bSelectLocked = true;
+ bool bSelectUnlocked = true;
+ if (pTabProtection && pTabProtection->isProtected())
+ {
+ bSelectLocked = pTabProtection->isOptionEnabled(ScTableProtection::SELECT_LOCKED_CELLS);
+ bSelectUnlocked = pTabProtection->isOptionEnabled(ScTableProtection::SELECT_UNLOCKED_CELLS);
+ }
+
+ if (nMovX > 0)
+ {
+ SCROW rowStart = std::min(rRow, nStartY);
+ SCROW rowEnd = std::max(rRow, nStartY);
+
+ for (SCROW i = rowStart; i <= rowEnd && rCol < nStartX;)
+ {
+ SCCOL tmpCol = rCol;
+ while (tmpCol < pDoc->MaxCol() && pDoc->IsHorOverlapped(tmpCol, i, nTab))
+ ++tmpCol;
+ if (tmpCol != rCol)
+ {
+ i = rowStart;
+ if (tmpCol > nStartX)
+ --tmpCol;
+ if (!areCellsQualified(pDoc, rCol + 1, rowStart, tmpCol, rowEnd, nTab,
+ bSelectLocked, bSelectUnlocked))
+ break;
+ rCol = tmpCol;
+ }
+ else
+ ++i;
+ }
+ }
+ else if (nMovX < 0)
+ {
+ SCROW rowStart = std::min(rRow, nStartY);
+ SCROW rowEnd = std::max(rRow, nStartY);
+
+ for (SCROW i = rowStart; i <= rowEnd && rCol > nStartX;)
+ {
+ SCCOL tmpCol = rCol;
+ while (tmpCol >= 0 && pDoc->IsHorOverlapped(tmpCol + 1, i, nTab))
+ --tmpCol;
+ if (tmpCol != rCol)
+ {
+ i = rowStart;
+ if (tmpCol < nStartX)
+ ++tmpCol;
+ if (!areCellsQualified(pDoc, rCol - 1, rowStart, tmpCol, rowEnd, nTab,
+ bSelectLocked, bSelectUnlocked))
+ break;
+ rCol = tmpCol;
+ }
+ else
+ ++i;
+ }
+ }
+
+ if (nMovY > 0)
+ {
+ SCCOL colStart = std::min(rCol, nStartX);
+ SCCOL colEnd = std::max(rCol, nStartX);
+
+ for (SCCOL i = colStart; i <= colEnd && rRow < nStartY;)
+ {
+ SCROW tmpRow = rRow;
+ while (tmpRow < pDoc->MaxRow() && pDoc->IsVerOverlapped(i, tmpRow, nTab))
+ ++tmpRow;
+ if (tmpRow != rRow)
+ {
+ i = colStart;
+ if (tmpRow > nStartY)
+ --tmpRow;
+ if (!areCellsQualified(pDoc, colStart, rRow + 1, colEnd, tmpRow, nTab,
+ bSelectLocked, bSelectUnlocked))
+ break;
+ rRow = tmpRow;
+ }
+ else
+ ++i;
+ }
+ }
+ else if (nMovY < 0)
+ {
+ SCCOL colStart = std::min(rCol, nStartX);
+ SCCOL colEnd = std::max(rCol, nStartX);
+
+ for (SCCOL i = colStart; i <= colEnd && rRow > nStartY;)
+ {
+ SCROW tmpRow = rRow;
+ while (tmpRow >= 0 && pDoc->IsVerOverlapped(i, tmpRow + 1, nTab))
+ --tmpRow;
+ if (tmpRow != rRow)
+ {
+ i = colStart;
+ if (tmpRow < nStartY)
+ ++tmpRow;
+ if (!areCellsQualified(pDoc, colStart, rRow - 1, colEnd, tmpRow, nTab,
+ bSelectLocked, bSelectUnlocked))
+ break;
+ rRow = tmpRow;
+ }
+ else
+ ++i;
+ }
+ }
+}
+
+void moveCursorToProperSide(SCCOL& rCol, SCROW& rRow, SCCOL nMovX, SCROW nMovY, SCCOL nStartX,
+ SCROW nStartY, SCTAB nTab, const ScDocument* pDoc)
+{
+ SCCOL tmpCol = rCol;
+ SCROW tmpRow = rRow;
+
+ if (nMovX > 0 && nStartX < pDoc->MaxCol() && rCol < nStartX)
+ {
+ SCROW rowStart = std::min(rRow, nStartY);
+ SCROW rowEnd = std::max(rRow, nStartY);
+ for (SCROW i = rowStart; i <= rowEnd && tmpCol < nStartX;)
+ {
+ if (pDoc->IsHorOverlapped(tmpCol + 1, i, nTab))
+ {
+ do
+ {
+ ++tmpCol;
+ } while (pDoc->IsHorOverlapped(tmpCol + 1, i, nTab));
+ i = rowStart;
+ }
+ else
+ ++i;
+ }
+ if (tmpCol < nStartX)
+ tmpCol = rCol;
+ }
+ else if (nMovX < 0 && nStartX > 0 && rCol > nStartX)
+ {
+ SCROW rowStart = std::min(rRow, nStartY);
+ SCROW rowEnd = std::max(rRow, nStartY);
+ for (SCROW i = rowStart; i <= rowEnd && tmpCol > nStartX;)
+ {
+ if (pDoc->IsHorOverlapped(tmpCol, i, nTab))
+ {
+ do
+ {
+ --tmpCol;
+ } while (pDoc->IsHorOverlapped(tmpCol, i, nTab));
+ i = rowStart;
+ }
+ else
+ ++i;
+ }
+ if (tmpCol > nStartX)
+ tmpCol = rCol;
+ }
+
+ if (nMovY > 0 && nStartY < pDoc->MaxRow() && rRow < nStartY)
+ {
+ SCCOL colStart = std::min(rCol, nStartX);
+ SCCOL colEnd = std::max(rCol, nStartX);
+ for (SCCOL i = colStart; i <= colEnd && tmpRow < nStartY;)
+ {
+ if (pDoc->IsVerOverlapped(i, tmpRow + 1, nTab))
+ {
+ do
+ {
+ ++tmpRow;
+ } while (pDoc->IsVerOverlapped(i, tmpRow + 1, nTab));
+ i = colStart;
+ }
+ else
+ ++i;
+ }
+ if (tmpRow < nStartY)
+ tmpRow = rRow;
+ }
+ else if (nMovY < 0 && nStartY > 0 && rRow > nStartY)
+ {
+ SCCOL colStart = std::min(rCol, nStartX);
+ SCCOL colEnd = std::max(rCol, nStartX);
+ for (SCCOL i = colStart; i <= colEnd && tmpRow > nStartY;)
+ {
+ if (pDoc->IsVerOverlapped(i, tmpRow, nTab))
+ {
+ do
+ {
+ --tmpRow;
+ } while (pDoc->IsVerOverlapped(i, tmpRow, nTab));
+ i = colStart;
+ }
+ else
+ ++i;
+ }
+ if (tmpRow > nStartY)
+ tmpRow = rRow;
+ }
+
+ if (tmpCol != rCol)
+ rCol = tmpCol;
+ if (tmpRow != rRow)
+ rRow = tmpRow;
+}
+}
+
+void ScTabView::PaintMarks(SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow )
+{
+ auto& rDoc = aViewData.GetDocument();
+ if (!rDoc.ValidCol(nStartCol)) nStartCol = rDoc.MaxCol();
+ if (!rDoc.ValidRow(nStartRow)) nStartRow = rDoc.MaxRow();
+ if (!rDoc.ValidCol(nEndCol)) nEndCol = rDoc.MaxCol();
+ if (!rDoc.ValidRow(nEndRow)) nEndRow = rDoc.MaxRow();
+
+ bool bLeft = (nStartCol==0 && nEndCol==rDoc.MaxCol());
+ bool bTop = (nStartRow==0 && nEndRow==rDoc.MaxRow());
+
+ if (bLeft)
+ PaintLeftArea( nStartRow, nEndRow );
+ if (bTop)
+ PaintTopArea( nStartCol, nEndCol );
+
+ aViewData.GetDocument().ExtendMerge( nStartCol, nStartRow, nEndCol, nEndRow,
+ aViewData.GetTabNo() );
+ PaintArea( nStartCol, nStartRow, nEndCol, nEndRow, ScUpdateMode::Marks );
+}
+
+bool ScTabView::IsMarking( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
+{
+ return IsBlockMode()
+ && nBlockStartX == nCol
+ && nBlockStartY == nRow
+ && nBlockStartZ == nTab;
+}
+
+void ScTabView::InitOwnBlockMode( const ScRange& rMarkRange )
+{
+ if (IsBlockMode())
+ return;
+
+ // when there is no (old) selection anymore, delete anchor in SelectionEngine:
+ ScMarkData& rMark = aViewData.GetMarkData();
+ if (!rMark.IsMarked() && !rMark.IsMultiMarked())
+ GetSelEngine()->CursorPosChanging( false, false );
+
+ meBlockMode = Own;
+ nBlockStartX = rMarkRange.aStart.Col();
+ nBlockStartY = rMarkRange.aStart.Row();
+ nBlockStartZ = rMarkRange.aStart.Tab();
+ nBlockEndX = rMarkRange.aEnd.Col();
+ nBlockEndY = rMarkRange.aEnd.Row();
+ nBlockEndZ = rMarkRange.aEnd.Tab();
+
+ SelectionChanged(); // status is checked with mark set
+}
+
+void ScTabView::InitBlockModeHighlight( SCCOL nCurX, SCROW nCurY, SCTAB nCurZ,
+ bool bCols, bool bRows )
+{
+ if (meHighlightBlockMode != None)
+ return;
+
+ auto& rDoc = aViewData.GetDocument();
+ if (!rDoc.ValidCol(nCurX)) nCurX = rDoc.MaxCol();
+ if (!rDoc.ValidRow(nCurY)) nCurY = rDoc.MaxRow();
+
+ ScMarkData& rMark = aViewData.GetHighlightData();
+ meHighlightBlockMode = Normal;
+
+ SCROW nStartY = nCurY;
+ SCCOL nStartX = nCurX;
+ SCROW nEndY = nCurY;
+ SCCOL nEndX = nCurX;
+
+ if (bCols)
+ {
+ nStartY = 0;
+ nEndY = rDoc.MaxRow();
+ }
+
+ if (bRows)
+ {
+ nStartX = 0;
+ nEndX = rDoc.MaxCol();
+ }
+
+ rMark.SetMarkArea( ScRange( nStartX, nStartY, nCurZ, nEndX, nEndY, nCurZ ) );
+ UpdateHighlightOverlay();
+}
+
+void ScTabView::InitBlockMode( SCCOL nCurX, SCROW nCurY, SCTAB nCurZ,
+ bool bTestNeg, bool bCols, bool bRows, bool bForceNeg )
+{
+ if (IsBlockMode())
+ return;
+
+ auto& rDoc = aViewData.GetDocument();
+ if (!rDoc.ValidCol(nCurX)) nCurX = rDoc.MaxCol();
+ if (!rDoc.ValidRow(nCurY)) nCurY = rDoc.MaxRow();
+
+ ScMarkData& rMark = aViewData.GetMarkData();
+ SCTAB nTab = aViewData.GetTabNo();
+
+ // unmark part?
+ if (bForceNeg)
+ bBlockNeg = true;
+ else if (bTestNeg)
+ {
+ if ( bCols )
+ bBlockNeg = rMark.IsColumnMarked( nCurX );
+ else if ( bRows )
+ bBlockNeg = rMark.IsRowMarked( nCurY );
+ else
+ bBlockNeg = rMark.IsCellMarked( nCurX, nCurY );
+ }
+ else
+ bBlockNeg = false;
+ rMark.SetMarkNegative(bBlockNeg);
+
+ meBlockMode = Normal;
+ bBlockCols = bCols;
+ bBlockRows = bRows;
+ nBlockStartX = nBlockStartXOrig = nCurX;
+ nBlockStartY = nBlockStartYOrig = nCurY;
+ nBlockStartZ = nCurZ;
+ nBlockEndX = nOldCurX = nBlockStartX;
+ nBlockEndY = nOldCurY = nBlockStartY;
+ nBlockEndZ = nBlockStartZ;
+
+ if (bBlockCols)
+ {
+ nBlockStartY = nBlockStartYOrig = 0;
+ nBlockEndY = rDoc.MaxRow();
+ }
+
+ if (bBlockRows)
+ {
+ nBlockStartX = nBlockStartXOrig = 0;
+ nBlockEndX = rDoc.MaxCol();
+ }
+
+ rMark.SetMarkArea( ScRange( nBlockStartX,nBlockStartY, nTab, nBlockEndX,nBlockEndY, nTab ) );
+
+ UpdateSelectionOverlay();
+}
+
+void ScTabView::DoneBlockModeHighlight( bool bContinue )
+{
+ if (meHighlightBlockMode == None)
+ return;
+
+ ScMarkData& rMark = aViewData.GetHighlightData();
+ bool bFlag = rMark.GetMarkingFlag();
+ rMark.SetMarking(false);
+
+ if (bContinue)
+ rMark.MarkToMulti();
+ else
+ {
+ SCTAB nTab = aViewData.GetTabNo();
+ ScDocument& rDoc = aViewData.GetDocument();
+ if ( rDoc.HasTable(nTab) )
+ rMark.ResetMark();
+ }
+ meHighlightBlockMode = None;
+
+ rMark.SetMarking(bFlag);
+ if (bContinue)
+ rMark.SetMarking(false);
+}
+
+void ScTabView::DoneBlockMode( bool bContinue )
+{
+ // When switching between sheet and header SelectionEngine DeselectAll may be called,
+ // because the other engine does not have any anchor.
+ // bMoveIsShift prevents the selection to be canceled.
+
+ if (!IsBlockMode() || bMoveIsShift)
+ return;
+
+ ScMarkData& rMark = aViewData.GetMarkData();
+ bool bFlag = rMark.GetMarkingFlag();
+ rMark.SetMarking(false);
+
+ if (bBlockNeg && !bContinue)
+ rMark.MarkToMulti();
+
+ if (bContinue)
+ rMark.MarkToMulti();
+ else
+ {
+ // the sheet may be invalid at this point because DoneBlockMode from SetTabNo is
+ // called (for example, when the current sheet is closed from another View)
+ SCTAB nTab = aViewData.GetTabNo();
+ ScDocument& rDoc = aViewData.GetDocument();
+ if ( rDoc.HasTable(nTab) )
+ PaintBlock( true ); // true -> delete block
+ else
+ rMark.ResetMark();
+ }
+ meBlockMode = None;
+
+ rMark.SetMarking(bFlag);
+ rMark.SetMarkNegative(false);
+}
+
+bool ScTabView::IsBlockMode() const
+{
+ return meBlockMode != None;
+}
+
+void ScTabView::MarkCursor( SCCOL nCurX, SCROW nCurY, SCTAB nCurZ,
+ bool bCols, bool bRows, bool bCellSelection )
+{
+ ScDocument& rDocument = aViewData.GetDocument();
+ if (!rDocument.ValidCol(nCurX)) nCurX = rDocument.MaxCol();
+ if (!rDocument.ValidRow(nCurY)) nCurY = rDocument.MaxRow();
+
+ if (!IsBlockMode())
+ {
+ OSL_FAIL( "MarkCursor not in BlockMode" );
+ InitBlockMode( nCurX, nCurY, nCurZ, false, bCols, bRows );
+ }
+
+ if (bCols)
+ nCurY = rDocument.MaxRow();
+ if (bRows)
+ nCurX = rDocument.MaxCol();
+
+ ScMarkData& rMark = aViewData.GetMarkData();
+ OSL_ENSURE(rMark.IsMarked() || rMark.IsMultiMarked(), "MarkCursor, !IsMarked()");
+ const ScRange& aMarkRange = rMark.GetMarkArea();
+ if (( aMarkRange.aStart.Col() != nBlockStartX && aMarkRange.aEnd.Col() != nBlockStartX ) ||
+ ( aMarkRange.aStart.Row() != nBlockStartY && aMarkRange.aEnd.Row() != nBlockStartY ) ||
+ ( meBlockMode == Own ))
+ {
+ // Mark has been changed
+ // (Eg MarkToSimple if by negative everything was erased, except for a rectangle)
+ // or after InitOwnBlockMode is further marked with shift-
+ bool bOldShift = bMoveIsShift;
+ bMoveIsShift = false; // really move
+ DoneBlockMode(); //! Set variables directly? (-> no flicker)
+ bMoveIsShift = bOldShift;
+
+ InitBlockMode( aMarkRange.aStart.Col(), aMarkRange.aStart.Row(),
+ nBlockStartZ, rMark.IsMarkNegative(), bCols, bRows );
+ }
+
+ if ( nCurX != nOldCurX || nCurY != nOldCurY )
+ {
+ // Current cursor has moved
+
+ SCTAB nTab = nCurZ;
+
+ if ( bCellSelection )
+ {
+ // Expand selection area accordingly when the current selection cuts
+ // through a merged cell.
+ ScRange cellSel(nBlockStartXOrig, nBlockStartYOrig, nTab, nCurX, nCurY, nTab);
+ cellSel.PutInOrder();
+ ScRange oldSel;
+ do
+ {
+ oldSel = cellSel;
+ rDocument.ExtendOverlapped(cellSel);
+ rDocument.ExtendMerge(cellSel);
+ } while (oldSel != cellSel);
+
+ // Preserve the directionality of the selection
+ if (nCurX >= nBlockStartXOrig)
+ {
+ nBlockStartX = cellSel.aStart.Col();
+ nBlockEndX = cellSel.aEnd.Col();
+ }
+ else
+ {
+ nBlockStartX = cellSel.aEnd.Col();
+ nBlockEndX = cellSel.aStart.Col();
+ }
+ if (nCurY >= nBlockStartYOrig)
+ {
+ nBlockStartY = cellSel.aStart.Row();
+ nBlockEndY = cellSel.aEnd.Row();
+ }
+ else
+ {
+ nBlockStartY = cellSel.aEnd.Row();
+ nBlockEndY = cellSel.aStart.Row();
+ }
+ }
+ else
+ {
+ nBlockEndX = nCurX;
+ nBlockEndY = nCurY;
+ }
+
+ // Set new selection area
+ rMark.SetMarkArea( ScRange( nBlockStartX, nBlockStartY, nTab, nBlockEndX, nBlockEndY, nTab ) );
+
+ UpdateSelectionOverlay();
+ SelectionChanged();
+
+ nOldCurX = nBlockEndX;
+ nOldCurY = nBlockEndY;
+
+ aViewData.GetViewShell()->UpdateInputHandler();
+ }
+
+ if ( !bCols && !bRows )
+ aHdrFunc.SetAnchorFlag( false );
+}
+
+void ScTabView::GetPageMoveEndPosition(SCCOL nMovX, SCROW nMovY, SCCOL& rPageX, SCROW& rPageY)
+{
+ SCCOL nCurX;
+ SCROW nCurY;
+ if (aViewData.IsRefMode())
+ {
+ nCurX = aViewData.GetRefEndX();
+ nCurY = aViewData.GetRefEndY();
+ }
+ else if (IsBlockMode())
+ {
+ // block end position.
+ nCurX = nBlockEndX;
+ nCurY = nBlockEndY;
+ }
+ else
+ {
+ // cursor position
+ nCurX = aViewData.GetCurX();
+ nCurY = aViewData.GetCurY();
+ }
+
+ ScSplitPos eWhich = aViewData.GetActivePart();
+ ScHSplitPos eWhichX = WhichH( eWhich );
+ ScVSplitPos eWhichY = WhichV( eWhich );
+
+ sal_uInt16 nScrSizeY = SC_SIZE_NONE;
+ if (comphelper::LibreOfficeKit::isActive() && aViewData.GetPageUpDownOffset() > 0) {
+ nScrSizeY = ScViewData::ToPixel( aViewData.GetPageUpDownOffset(), aViewData.GetPPTX() );
+ }
+
+ SCCOL nPageX;
+ SCROW nPageY;
+ if (nMovX >= 0)
+ nPageX = aViewData.CellsAtX( nCurX, 1, eWhichX ) * nMovX;
+ else
+ nPageX = aViewData.CellsAtX( nCurX, -1, eWhichX ) * nMovX;
+
+ if (nMovY >= 0)
+ nPageY = aViewData.CellsAtY( nCurY, 1, eWhichY, nScrSizeY ) * nMovY;
+ else
+ nPageY = aViewData.CellsAtY( nCurY, -1, eWhichY, nScrSizeY ) * nMovY;
+
+ if (nMovX != 0 && nPageX == 0) nPageX = (nMovX>0) ? 1 : -1;
+ if (nMovY != 0 && nPageY == 0) nPageY = (nMovY>0) ? 1 : -1;
+
+ rPageX = nPageX;
+ rPageY = nPageY;
+}
+
+void ScTabView::GetAreaMoveEndPosition(SCCOL nMovX, SCROW nMovY, ScFollowMode eMode,
+ SCCOL& rAreaX, SCROW& rAreaY, ScFollowMode& rMode,
+ bool bInteractiveByUser)
+{
+ SCCOL nNewX = -1;
+ SCROW nNewY = -1;
+
+ // current cursor position.
+ SCCOL nCurX = aViewData.GetCurX();
+ SCROW nCurY = aViewData.GetCurY();
+
+ ScModule* pScModule = SC_MOD();
+ bool bLegacyCellSelection = pScModule->GetInputOptions().GetLegacyCellSelection();
+ bool bIncrementallyExpandToDocLimits(false);
+
+ if (aViewData.IsRefMode())
+ {
+ nNewX = aViewData.GetRefEndX();
+ nNewY = aViewData.GetRefEndY();
+ nCurX = aViewData.GetRefStartX();
+ nCurY = aViewData.GetRefStartY();
+ }
+ else if (IsBlockMode())
+ {
+ // block end position.
+ nNewX = nBlockEndX;
+ nNewY = nBlockEndY;
+ }
+ else
+ {
+ nNewX = nCurX;
+ nNewY = nCurY;
+ // cool#6931 on ctrl+[right/down] don't immediately leap to the far limits of the document when no more data,
+ // instead jump a generous block of emptiness. Limit to direct interaction by user and the simple
+ // case.
+ bIncrementallyExpandToDocLimits = bInteractiveByUser && (nMovX == 1 || nMovY == 1) &&
+ !bLegacyCellSelection && comphelper::LibreOfficeKit::isActive();
+ }
+
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCTAB nTab = aViewData.GetTabNo();
+
+ // FindAreaPos knows only -1 or 1 as direction
+ SCCOL nVirtualX = bLegacyCellSelection ? nNewX : nCurX;
+ SCROW nVirtualY = bLegacyCellSelection ? nNewY : nCurY;
+
+ SCCOLROW i;
+ if ( nMovX > 0 )
+ for ( i=0; i<nMovX; i++ )
+ rDoc.FindAreaPos( nNewX, nVirtualY, nTab, SC_MOVE_RIGHT );
+ if ( nMovX < 0 )
+ for ( i=0; i<-nMovX; i++ )
+ rDoc.FindAreaPos( nNewX, nVirtualY, nTab, SC_MOVE_LEFT );
+ if ( nMovY > 0 )
+ for ( i=0; i<nMovY; i++ )
+ rDoc.FindAreaPos( nVirtualX, nNewY, nTab, SC_MOVE_DOWN );
+ if ( nMovY < 0 )
+ for ( i=0; i<-nMovY; i++ )
+ rDoc.FindAreaPos( nVirtualX, nNewY, nTab, SC_MOVE_UP );
+
+ if (eMode==SC_FOLLOW_JUMP) // bottom right do not show too much grey
+ {
+ if (nMovX != 0 && nNewX == rDoc.MaxCol())
+ {
+ eMode = SC_FOLLOW_LINE;
+ if (bIncrementallyExpandToDocLimits)
+ {
+ if (const ScTable* pTab = rDoc.FetchTable(nTab))
+ {
+ if (!pTab->HasData(nNewX, nCurY))
+ {
+ SCCOL nLastUsedCol(0);
+ SCROW nLastUsedRow(0);
+ rDoc.GetPrintArea(nTab, nLastUsedCol, nLastUsedRow);
+ SCCOL nJumpFrom = std::max(nCurX, nLastUsedCol);
+ nNewX = ((nJumpFrom / 13) + 2) * 13 - 1;
+ }
+ }
+ }
+ }
+ if (nMovY != 0 && nNewY == rDoc.MaxRow())
+ {
+ eMode = SC_FOLLOW_LINE;
+ if (bIncrementallyExpandToDocLimits)
+ {
+ if (const ScTable* pTab = rDoc.FetchTable(nTab))
+ {
+ if (!pTab->HasData(nCurX, nNewY))
+ {
+ SCCOL nLastUsedCol(0);
+ SCROW nLastUsedRow(0);
+ rDoc.GetPrintArea(nTab, nLastUsedCol, nLastUsedRow);
+ SCROW nJumpFrom = std::max(nCurY, nLastUsedRow);
+ nNewY = ((nJumpFrom / 500) + 2) * 500 - 1;
+ }
+ }
+ }
+ }
+ }
+
+ if (aViewData.IsRefMode())
+ {
+ rAreaX = nNewX - aViewData.GetRefEndX();
+ rAreaY = nNewY - aViewData.GetRefEndY();
+ }
+ else if (IsBlockMode())
+ {
+ rAreaX = nNewX - nBlockEndX;
+ rAreaY = nNewY - nBlockEndY;
+ }
+ else
+ {
+ rAreaX = nNewX - nCurX;
+ rAreaY = nNewY - nCurY;
+ }
+ rMode = eMode;
+}
+
+void ScTabView::SkipCursorHorizontal(SCCOL& rCurX, SCROW& rCurY, SCCOL nOldX, SCCOL nMovX)
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCTAB nTab = aViewData.GetTabNo();
+
+ bool bSkipProtected = false, bSkipUnprotected = false;
+ const ScTableProtection* pProtect = rDoc.GetTabProtection(nTab);
+ if (pProtect && pProtect->isProtected())
+ {
+ bSkipProtected = !pProtect->isOptionEnabled(ScTableProtection::SELECT_LOCKED_CELLS);
+ bSkipUnprotected = !pProtect->isOptionEnabled(ScTableProtection::SELECT_UNLOCKED_CELLS);
+ }
+
+ bool bSkipCell = false;
+ bool bHFlip = false;
+ // If a number of last columns are hidden, search up to and including the first of them,
+ // because after it nothing changes.
+ SCCOL nMaxCol;
+ if(rDoc.ColHidden(rDoc.MaxCol(), nTab, &nMaxCol))
+ ++nMaxCol;
+ else
+ nMaxCol = rDoc.MaxCol();
+ // Search also at least up to and including the first unallocated column (all unallocated columns
+ // share a set of attrs).
+ nMaxCol = std::max( nMaxCol, std::min<SCCOL>( rDoc.GetAllocatedColumnsCount(nTab) + 1, rDoc.MaxCol()));
+ do
+ {
+ bSkipCell = rDoc.ColHidden(rCurX, nTab) || rDoc.IsHorOverlapped(rCurX, rCurY, nTab);
+ if (bSkipProtected && !bSkipCell)
+ bSkipCell = rDoc.HasAttrib(rCurX, rCurY, nTab, rCurX, rCurY, nTab, HasAttrFlags::Protected);
+ if (bSkipUnprotected && !bSkipCell)
+ bSkipCell = !rDoc.HasAttrib(rCurX, rCurY, nTab, rCurX, rCurY, nTab, HasAttrFlags::Protected);
+
+ if (bSkipCell)
+ {
+ if (rCurX <= 0 || rCurX >= nMaxCol)
+ {
+ if (bHFlip)
+ {
+ rCurX = nOldX;
+ bSkipCell = false;
+ }
+ else
+ {
+ nMovX = -nMovX;
+ if (nMovX > 0)
+ ++rCurX;
+ else
+ --rCurX;
+ bHFlip = true;
+ }
+ }
+ else
+ if (nMovX > 0)
+ ++rCurX;
+ else
+ --rCurX;
+ }
+ }
+ while (bSkipCell);
+
+ if (rDoc.IsVerOverlapped(rCurX, rCurY, nTab))
+ {
+ aViewData.SetOldCursor(rCurX, rCurY);
+ while (rDoc.IsVerOverlapped(rCurX, rCurY, nTab))
+ --rCurY;
+ }
+}
+
+void ScTabView::SkipCursorVertical(SCCOL& rCurX, SCROW& rCurY, SCROW nOldY, SCROW nMovY)
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCTAB nTab = aViewData.GetTabNo();
+
+ bool bSkipProtected = false, bSkipUnprotected = false;
+ const ScTableProtection* pProtect = rDoc.GetTabProtection(nTab);
+ if (pProtect && pProtect->isProtected())
+ {
+ bSkipProtected = !pProtect->isOptionEnabled(ScTableProtection::SELECT_LOCKED_CELLS);
+ bSkipUnprotected = !pProtect->isOptionEnabled(ScTableProtection::SELECT_UNLOCKED_CELLS);
+ }
+
+ bool bSkipCell = false;
+ bool bVFlip = false;
+ // Avoid repeated calls to RowHidden(), IsVerOverlapped() and HasAttrib().
+ SCROW nFirstSameHiddenRow = -1;
+ SCROW nLastSameHiddenRow = -1;
+ bool bRowHidden = false;
+ SCROW nFirstSameIsVerOverlapped = -1;
+ SCROW nLastSameIsVerOverlapped = -1;
+ bool bIsVerOverlapped = false;
+ SCROW nFirstSameHasAttribRow = -1;
+ SCROW nLastSameHasAttribRow = -1;
+ bool bHasAttribProtected = false;
+ do
+ {
+ if( rCurY < nFirstSameHiddenRow || rCurY > nLastSameHiddenRow )
+ bRowHidden = rDoc.RowHidden(rCurY, nTab, &nFirstSameHiddenRow, &nLastSameHiddenRow);
+ bSkipCell = bRowHidden;
+ if( !bSkipCell )
+ {
+ if( rCurY < nFirstSameIsVerOverlapped || rCurY > nLastSameIsVerOverlapped )
+ bIsVerOverlapped = rDoc.IsVerOverlapped(rCurX, rCurY, nTab, &nFirstSameIsVerOverlapped, &nLastSameIsVerOverlapped);
+ bSkipCell = bIsVerOverlapped;
+ }
+ if (bSkipProtected && !bSkipCell)
+ {
+ if( rCurY < nFirstSameHasAttribRow || rCurY > nLastSameHasAttribRow )
+ bHasAttribProtected = rDoc.HasAttrib(rCurX, rCurY, nTab, HasAttrFlags::Protected,
+ &nFirstSameHasAttribRow, &nLastSameHasAttribRow);
+ bSkipCell = bHasAttribProtected;
+ }
+ if (bSkipUnprotected && !bSkipCell)
+ {
+ if( rCurY < nFirstSameHasAttribRow || rCurY > nLastSameHasAttribRow )
+ bHasAttribProtected = rDoc.HasAttrib(rCurX, rCurY, nTab, HasAttrFlags::Protected,
+ &nFirstSameHasAttribRow, &nLastSameHasAttribRow);
+ bSkipCell = !bHasAttribProtected;
+ }
+
+ if (bSkipCell)
+ {
+ if (rCurY <= 0 || rCurY >= rDoc.MaxRow())
+ {
+ if (bVFlip)
+ {
+ rCurY = nOldY;
+ bSkipCell = false;
+ }
+ else
+ {
+ nMovY = -nMovY;
+ if (nMovY > 0)
+ ++rCurY;
+ else
+ --rCurY;
+ bVFlip = true;
+ }
+ }
+ else
+ if (nMovY > 0)
+ ++rCurY;
+ else
+ --rCurY;
+ }
+ }
+ while (bSkipCell);
+
+ if (rDoc.IsHorOverlapped(rCurX, rCurY, nTab))
+ {
+ aViewData.SetOldCursor(rCurX, rCurY);
+ while (rDoc.IsHorOverlapped(rCurX, rCurY, nTab))
+ --rCurX;
+ }
+}
+
+void ScTabView::ExpandBlock(SCCOL nMovX, SCROW nMovY, ScFollowMode eMode)
+{
+ if (!nMovX && !nMovY)
+ // Nothing to do. Bail out.
+ return;
+
+ ScTabViewShell* pViewShell = aViewData.GetViewShell();
+ bool bRefInputMode = pViewShell && pViewShell->IsRefInputMode();
+ if (bRefInputMode && !aViewData.IsRefMode())
+ // initialize formula reference mode if it hasn't already.
+ InitRefMode(aViewData.GetCurX(), aViewData.GetCurY(), aViewData.GetTabNo(), SC_REFTYPE_REF);
+
+ ScDocument& rDoc = aViewData.GetDocument();
+
+ if (aViewData.IsRefMode())
+ {
+ // formula reference mode
+
+ SCCOL nNewX = aViewData.GetRefEndX();
+ SCROW nNewY = aViewData.GetRefEndY();
+ SCTAB nRefTab = aViewData.GetRefEndZ();
+
+ moveRefByCell(nNewX, nNewY, nMovX, nMovY, nRefTab, rDoc);
+
+ UpdateRef(nNewX, nNewY, nRefTab);
+ SCCOL nTargetCol = nNewX;
+ SCROW nTargetRow = nNewY;
+ if (((aViewData.GetRefStartX() == 0) || (aViewData.GetRefStartY() == 0)) &&
+ ((nNewX != rDoc.MaxCol()) || (nNewY != rDoc.MaxRow())))
+ {
+ // Row selection
+ if ((aViewData.GetRefStartX() == 0) && (nNewX == rDoc.MaxCol()))
+ nTargetCol = aViewData.GetCurX();
+ // Column selection
+ if ((aViewData.GetRefStartY() == 0) && (nNewY == rDoc.MaxRow()))
+ nTargetRow = aViewData.GetCurY();
+ }
+ AlignToCursor(nTargetCol, nTargetRow, eMode);
+ }
+ else
+ {
+ // normal selection mode
+
+ SCTAB nTab = aViewData.GetTabNo();
+ SCCOL nOrigX = aViewData.GetCurX();
+ SCROW nOrigY = aViewData.GetCurY();
+
+ // Note that the origin position *never* moves during selection.
+
+ if (!IsBlockMode())
+ {
+ InitBlockMode(nOrigX, nOrigY, nTab, true);
+ const ScMergeAttr* pMergeAttr = rDoc.GetAttr(nOrigX, nOrigY, nTab, ATTR_MERGE);
+ if (pMergeAttr && pMergeAttr->IsMerged())
+ {
+ nBlockEndX = nOrigX + pMergeAttr->GetColMerge() - 1;
+ nBlockEndY = nOrigY + pMergeAttr->GetRowMerge() - 1;
+ }
+ }
+
+ moveCursorToProperSide(nBlockEndX, nBlockEndY, nMovX, nMovY, nBlockStartX, nBlockStartY,
+ nTab, &rDoc);
+ moveCursorByProtRule(nBlockEndX, nBlockEndY, nMovX, nMovY, nTab, &rDoc);
+ checkBoundary(&rDoc, nBlockEndX, nBlockEndY);
+ moveCursorByMergedCell(nBlockEndX, nBlockEndY, nMovX, nMovY, nBlockStartX, nBlockStartY,
+ nTab, &rDoc);
+ checkBoundary(&rDoc, nBlockEndX, nBlockEndY);
+
+ MarkCursor(nBlockEndX, nBlockEndY, nTab, false, false, true);
+
+ // Check if the entire row(s) or column(s) are selected.
+ ScSplitPos eActive = aViewData.GetActivePart();
+ bool bRowSelected = (nBlockStartX == 0 && nBlockEndX == rDoc.MaxCol());
+ bool bColSelected = (nBlockStartY == 0 && nBlockEndY == rDoc.MaxRow());
+ SCCOL nAlignX = bRowSelected ? aViewData.GetPosX(WhichH(eActive)) : nBlockEndX;
+ SCROW nAlignY = bColSelected ? aViewData.GetPosY(WhichV(eActive)) : nBlockEndY;
+ AlignToCursor(nAlignX, nAlignY, eMode);
+
+ SelectionChanged();
+ }
+}
+
+void ScTabView::ExpandBlockPage(SCCOL nMovX, SCROW nMovY)
+{
+ SCCOL nPageX;
+ SCROW nPageY;
+ GetPageMoveEndPosition(nMovX, nMovY, nPageX, nPageY);
+ ExpandBlock(nPageX, nPageY, SC_FOLLOW_FIX);
+}
+
+void ScTabView::ExpandBlockArea(SCCOL nMovX, SCROW nMovY)
+{
+ SCCOL nAreaX;
+ SCROW nAreaY;
+ ScFollowMode eMode;
+ GetAreaMoveEndPosition(nMovX, nMovY, SC_FOLLOW_JUMP, nAreaX, nAreaY, eMode);
+ ExpandBlock(nAreaX, nAreaY, eMode);
+}
+
+void ScTabView::UpdateCopySourceOverlay()
+{
+ for (VclPtr<ScGridWindow> & pWin : pGridWin)
+ if (pWin && pWin->IsVisible())
+ pWin->UpdateCopySourceOverlay();
+}
+
+void ScTabView::UpdateSelectionOverlay()
+{
+ for (VclPtr<ScGridWindow> & pWin : pGridWin)
+ if ( pWin && pWin->IsVisible() )
+ pWin->UpdateSelectionOverlay();
+}
+
+void ScTabView::UpdateHighlightOverlay()
+{
+ for (VclPtr<ScGridWindow> & pWin : pGridWin)
+ if ( pWin && pWin->IsVisible() )
+ pWin->UpdateHighlightOverlay();
+}
+
+void ScTabView::UpdateShrinkOverlay()
+{
+ for (VclPtr<ScGridWindow> & pWin : pGridWin)
+ if ( pWin && pWin->IsVisible() )
+ pWin->UpdateShrinkOverlay();
+}
+
+void ScTabView::UpdateAllOverlays()
+{
+ for (VclPtr<ScGridWindow> & pWin : pGridWin)
+ if ( pWin && pWin->IsVisible() )
+ pWin->UpdateAllOverlays();
+}
+
+//!
+//! divide PaintBlock into two methods: RepaintBlock and RemoveBlock or similar
+//!
+
+void ScTabView::PaintBlock( bool bReset )
+{
+ ScMarkData& rMark = aViewData.GetMarkData();
+ SCTAB nTab = aViewData.GetTabNo();
+ bool bMulti = rMark.IsMultiMarked();
+ if (!(rMark.IsMarked() || bMulti))
+ return;
+
+ ScRange aMarkRange;
+ HideAllCursors();
+ if (bMulti)
+ {
+ bool bFlag = rMark.GetMarkingFlag();
+ rMark.SetMarking(false);
+ rMark.MarkToMulti();
+ aMarkRange = rMark.GetMultiMarkArea();
+ rMark.MarkToSimple();
+ rMark.SetMarking(bFlag);
+ }
+ else
+ aMarkRange = rMark.GetMarkArea();
+
+ nBlockStartX = aMarkRange.aStart.Col();
+ nBlockStartY = aMarkRange.aStart.Row();
+ nBlockStartZ = aMarkRange.aStart.Tab();
+ nBlockEndX = aMarkRange.aEnd.Col();
+ nBlockEndY = aMarkRange.aEnd.Row();
+ nBlockEndZ = aMarkRange.aEnd.Tab();
+
+ bool bDidReset = false;
+
+ if ( nTab>=nBlockStartZ && nTab<=nBlockEndZ )
+ {
+ if ( bReset )
+ {
+ // Inverting when deleting only on active View
+ if ( aViewData.IsActive() )
+ {
+ rMark.ResetMark();
+ UpdateSelectionOverlay();
+ bDidReset = true;
+ }
+ }
+ else
+ PaintMarks( nBlockStartX, nBlockStartY, nBlockEndX, nBlockEndY );
+ }
+
+ if ( bReset && !bDidReset )
+ rMark.ResetMark();
+
+ ShowAllCursors();
+}
+
+void ScTabView::SelectAll( bool bContinue )
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+ ScMarkData& rMark = aViewData.GetMarkData();
+ SCTAB nTab = aViewData.GetTabNo();
+
+ if (rMark.IsMarked())
+ {
+ if ( rMark.GetMarkArea() == ScRange( 0,0,nTab, rDoc.MaxCol(),rDoc.MaxRow(),nTab ) )
+ return;
+ }
+
+ DoneBlockMode( bContinue );
+ InitBlockMode( 0,0,nTab );
+ MarkCursor( rDoc.MaxCol(),rDoc.MaxRow(),nTab );
+
+ SelectionChanged();
+}
+
+void ScTabView::SelectAllTables()
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+ ScMarkData& rMark = aViewData.GetMarkData();
+ SCTAB nCount = rDoc.GetTableCount();
+
+ if (nCount>1)
+ {
+ for (SCTAB i=0; i<nCount; i++)
+ rMark.SelectTable( i, true );
+
+ aViewData.GetDocShell()->PostPaintExtras();
+ SfxBindings& rBind = aViewData.GetBindings();
+ rBind.Invalidate( FID_FILL_TAB );
+ rBind.Invalidate( FID_TAB_DESELECTALL );
+ }
+}
+
+void ScTabView::DeselectAllTables()
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+ ScMarkData& rMark = aViewData.GetMarkData();
+ SCTAB nTab = aViewData.GetTabNo();
+ SCTAB nCount = rDoc.GetTableCount();
+
+ for (SCTAB i=0; i<nCount; i++)
+ rMark.SelectTable( i, ( i == nTab ) );
+
+ aViewData.GetDocShell()->PostPaintExtras();
+ SfxBindings& rBind = aViewData.GetBindings();
+ rBind.Invalidate( FID_FILL_TAB );
+ rBind.Invalidate( FID_TAB_DESELECTALL );
+}
+
+static bool lcl_FitsInWindow( double fScaleX, double fScaleY, sal_uInt16 nZoom,
+ tools::Long nWindowX, tools::Long nWindowY, const ScDocument* pDoc, SCTAB nTab,
+ SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
+ SCCOL nFixPosX, SCROW nFixPosY )
+{
+ double fZoomFactor = static_cast<double>(Fraction(nZoom,100));
+ fScaleX *= fZoomFactor;
+ fScaleY *= fZoomFactor;
+
+ tools::Long nBlockX = 0;
+ SCCOL nCol;
+ for (nCol=0; nCol<nFixPosX; nCol++)
+ {
+ // for frozen panes, add both parts
+ sal_uInt16 nColTwips = pDoc->GetColWidth( nCol, nTab );
+ if (nColTwips)
+ {
+ nBlockX += static_cast<tools::Long>(nColTwips * fScaleX);
+ if (nBlockX > nWindowX)
+ return false;
+ }
+ }
+ for (nCol=nStartCol; nCol<=nEndCol; nCol++)
+ {
+ sal_uInt16 nColTwips = pDoc->GetColWidth( nCol, nTab );
+ if (nColTwips)
+ {
+ nBlockX += static_cast<tools::Long>(nColTwips * fScaleX);
+ if (nBlockX > nWindowX)
+ return false;
+ }
+ }
+
+ tools::Long nBlockY = 0;
+ for (SCROW nRow = 0; nRow <= nFixPosY-1; ++nRow)
+ {
+ if (pDoc->RowHidden(nRow, nTab))
+ continue;
+
+ // for frozen panes, add both parts
+ sal_uInt16 nRowTwips = pDoc->GetRowHeight(nRow, nTab);
+ if (nRowTwips)
+ {
+ nBlockY += static_cast<tools::Long>(nRowTwips * fScaleY);
+ if (nBlockY > nWindowY)
+ return false;
+ }
+ }
+ for (SCROW nRow = nStartRow; nRow <= nEndRow; ++nRow)
+ {
+ sal_uInt16 nRowTwips = pDoc->GetRowHeight(nRow, nTab);
+ if (nRowTwips)
+ {
+ nBlockY += static_cast<tools::Long>(nRowTwips * fScaleY);
+ if (nBlockY > nWindowY)
+ return false;
+ }
+ }
+
+ return true;
+}
+
+sal_uInt16 ScTabView::CalcZoom( SvxZoomType eType, sal_uInt16 nOldZoom )
+{
+ sal_uInt16 nZoom = 100;
+
+ switch ( eType )
+ {
+ case SvxZoomType::PERCENT: // rZoom is no particular percent value
+ nZoom = nOldZoom;
+ break;
+
+ case SvxZoomType::OPTIMAL: // nZoom corresponds to the optimal size
+ {
+ ScMarkData& rMark = aViewData.GetMarkData();
+ ScDocument& rDoc = aViewData.GetDocument();
+
+ if (!rMark.IsMarked() && !rMark.IsMultiMarked())
+ nZoom = 100; // nothing selected
+ else
+ {
+ SCTAB nTab = aViewData.GetTabNo();
+ ScRange aMarkRange;
+ if ( aViewData.GetSimpleArea( aMarkRange ) != SC_MARK_SIMPLE )
+ aMarkRange = rMark.GetMultiMarkArea();
+
+ SCCOL nStartCol = aMarkRange.aStart.Col();
+ SCROW nStartRow = aMarkRange.aStart.Row();
+ SCTAB nStartTab = aMarkRange.aStart.Tab();
+ SCCOL nEndCol = aMarkRange.aEnd.Col();
+ SCROW nEndRow = aMarkRange.aEnd.Row();
+ SCTAB nEndTab = aMarkRange.aEnd.Tab();
+
+ if ( nTab < nStartTab && nTab > nEndTab )
+ nTab = nStartTab;
+
+ ScSplitPos eUsedPart = aViewData.GetActivePart();
+
+ SCCOL nFixPosX = 0;
+ SCROW nFixPosY = 0;
+ if ( aViewData.GetHSplitMode() == SC_SPLIT_FIX )
+ {
+ // use right part
+ eUsedPart = (WhichV(eUsedPart)==SC_SPLIT_TOP) ? SC_SPLIT_TOPRIGHT : SC_SPLIT_BOTTOMRIGHT;
+ nFixPosX = aViewData.GetFixPosX();
+ if ( nStartCol < nFixPosX )
+ nStartCol = nFixPosX;
+ }
+ if ( aViewData.GetVSplitMode() == SC_SPLIT_FIX )
+ {
+ // use bottom part
+ eUsedPart = (WhichH(eUsedPart)==SC_SPLIT_LEFT) ? SC_SPLIT_BOTTOMLEFT : SC_SPLIT_BOTTOMRIGHT;
+ nFixPosY = aViewData.GetFixPosY();
+ if ( nStartRow < nFixPosY )
+ nStartRow = nFixPosY;
+ }
+
+ if (pGridWin[eUsedPart])
+ {
+ // Because scale is rounded to pixels, the only reliable way to find
+ // the right scale is to check if a zoom fits
+
+ Size aWinSize = pGridWin[eUsedPart]->GetOutputSizePixel();
+
+ // for frozen panes, use sum of both parts for calculation
+
+ if ( nFixPosX != 0 )
+ aWinSize.AdjustWidth(GetGridWidth( SC_SPLIT_LEFT ) );
+ if ( nFixPosY != 0 )
+ aWinSize.AdjustHeight(GetGridHeight( SC_SPLIT_TOP ) );
+
+ ScDocShell* pDocSh = aViewData.GetDocShell();
+ double nPPTX = ScGlobal::nScreenPPTX / pDocSh->GetOutputFactor();
+ double nPPTY = ScGlobal::nScreenPPTY;
+
+ sal_uInt16 nMin = MINZOOM;
+ sal_uInt16 nMax = MAXZOOM;
+ while ( nMax > nMin )
+ {
+ sal_uInt16 nTest = (nMin+nMax+1)/2;
+ if ( lcl_FitsInWindow(
+ nPPTX, nPPTY, nTest, aWinSize.Width(), aWinSize.Height(),
+ &rDoc, nTab, nStartCol, nStartRow, nEndCol, nEndRow,
+ nFixPosX, nFixPosY ) )
+ nMin = nTest;
+ else
+ nMax = nTest-1;
+ }
+ OSL_ENSURE( nMin == nMax, "Nesting is wrong" );
+ nZoom = nMin;
+
+ if ( nZoom != nOldZoom )
+ {
+ // scroll to block only in active split part
+ // (the part for which the size was calculated)
+
+ if ( nStartCol <= nEndCol )
+ aViewData.SetPosX( WhichH(eUsedPart), nStartCol );
+ if ( nStartRow <= nEndRow )
+ aViewData.SetPosY( WhichV(eUsedPart), nStartRow );
+ }
+ }
+ }
+ }
+ break;
+
+ case SvxZoomType::WHOLEPAGE: // nZoom corresponds to the whole page or
+ case SvxZoomType::PAGEWIDTH: // nZoom corresponds to the page width
+ {
+ SCTAB nCurTab = aViewData.GetTabNo();
+ ScDocument& rDoc = aViewData.GetDocument();
+ ScStyleSheetPool* pStylePool = rDoc.GetStyleSheetPool();
+ SfxStyleSheetBase* pStyleSheet =
+ pStylePool->Find( rDoc.GetPageStyle( nCurTab ),
+ SfxStyleFamily::Page );
+
+ OSL_ENSURE( pStyleSheet, "PageStyle not found :-/" );
+
+ if ( pStyleSheet )
+ {
+ ScPrintFunc aPrintFunc( aViewData.GetDocShell(),
+ aViewData.GetViewShell()->GetPrinter(true),
+ nCurTab );
+
+ Size aPageSize = aPrintFunc.GetDataSize();
+
+ // use the size of the largest GridWin for normal split,
+ // or both combined for frozen panes, with the (document) size
+ // of the frozen part added to the page size
+ // (with frozen panes, the size of the individual parts
+ // depends on the scale that is to be calculated)
+
+ if (!pGridWin[SC_SPLIT_BOTTOMLEFT])
+ return nZoom;
+
+ Size aWinSize = pGridWin[SC_SPLIT_BOTTOMLEFT]->GetOutputSizePixel();
+ ScSplitMode eHMode = aViewData.GetHSplitMode();
+ if ( eHMode != SC_SPLIT_NONE && pGridWin[SC_SPLIT_BOTTOMRIGHT] )
+ {
+ tools::Long nOtherWidth = pGridWin[SC_SPLIT_BOTTOMRIGHT]->
+ GetOutputSizePixel().Width();
+ if ( eHMode == SC_SPLIT_FIX )
+ {
+ aWinSize.AdjustWidth(nOtherWidth );
+ for ( SCCOL nCol = aViewData.GetPosX(SC_SPLIT_LEFT);
+ nCol < aViewData.GetFixPosX(); nCol++ )
+ aPageSize.AdjustWidth(rDoc.GetColWidth( nCol, nCurTab ) );
+ }
+ else if ( nOtherWidth > aWinSize.Width() )
+ aWinSize.setWidth( nOtherWidth );
+ }
+ ScSplitMode eVMode = aViewData.GetVSplitMode();
+ if ( eVMode != SC_SPLIT_NONE && pGridWin[SC_SPLIT_TOPLEFT] )
+ {
+ tools::Long nOtherHeight = pGridWin[SC_SPLIT_TOPLEFT]->
+ GetOutputSizePixel().Height();
+ if ( eVMode == SC_SPLIT_FIX )
+ {
+ aWinSize.AdjustHeight(nOtherHeight );
+ aPageSize.AdjustHeight(rDoc.GetRowHeight(
+ aViewData.GetPosY(SC_SPLIT_TOP),
+ aViewData.GetFixPosY()-1, nCurTab) );
+ }
+ else if ( nOtherHeight > aWinSize.Height() )
+ aWinSize.setHeight( nOtherHeight );
+ }
+
+ double nPPTX = ScGlobal::nScreenPPTX / aViewData.GetDocShell()->GetOutputFactor();
+ double nPPTY = ScGlobal::nScreenPPTY;
+
+ tools::Long nZoomX = static_cast<tools::Long>( aWinSize.Width() * 100 /
+ ( aPageSize.Width() * nPPTX ) );
+ tools::Long nZoomY = static_cast<tools::Long>( aWinSize.Height() * 100 /
+ ( aPageSize.Height() * nPPTY ) );
+
+ if (nZoomX > 0)
+ nZoom = static_cast<sal_uInt16>(nZoomX);
+
+ if (eType == SvxZoomType::WHOLEPAGE && nZoomY > 0 && nZoomY < nZoom)
+ nZoom = static_cast<sal_uInt16>(nZoomY);
+ }
+ }
+ break;
+
+ default:
+ OSL_FAIL("Unknown Zoom-Revision");
+ }
+
+ return nZoom;
+}
+
+// is called for instance when the view window is shifted:
+
+void ScTabView::StopMarking()
+{
+ ScSplitPos eActive = aViewData.GetActivePart();
+ if (pGridWin[eActive])
+ pGridWin[eActive]->StopMarking();
+
+ ScHSplitPos eH = WhichH(eActive);
+ if (pColBar[eH])
+ pColBar[eH]->StopMarking();
+
+ ScVSplitPos eV = WhichV(eActive);
+ if (pRowBar[eV])
+ pRowBar[eV]->StopMarking();
+}
+
+void ScTabView::HideNoteMarker()
+{
+ for (VclPtr<ScGridWindow> & pWin : pGridWin)
+ if (pWin && pWin->IsVisible())
+ pWin->HideNoteMarker();
+}
+
+void ScTabView::MakeDrawLayer()
+{
+ if (pDrawView)
+ return;
+
+ aViewData.GetDocShell()->MakeDrawLayer();
+
+ // pDrawView is set per Notify
+ OSL_ENSURE(pDrawView,"ScTabView::MakeDrawLayer does not work");
+
+ for(VclPtr<ScGridWindow> & pWin : pGridWin)
+ {
+ if(pWin)
+ {
+ pWin->DrawLayerCreated();
+ }
+ }
+}
+
+IMPL_STATIC_LINK_NOARG(ScTabView, InstallLOKNotifierHdl, void*, vcl::ILibreOfficeKitNotifier*)
+{
+ return GetpApp();
+}
+
+void ScTabView::ErrorMessage(TranslateId pGlobStrId)
+{
+ if ( SC_MOD()->IsInExecuteDrop() )
+ {
+ // #i28468# don't show error message when called from Drag&Drop, silently abort instead
+ return;
+ }
+
+ StopMarking(); // if called by Focus from MouseButtonDown
+
+ weld::Window* pParent = aViewData.GetDialogParent();
+ weld::WaitObject aWaitOff( pParent );
+ bool bFocus = pParent && pParent->has_focus();
+
+ if (pGlobStrId && pGlobStrId == STR_PROTECTIONERR)
+ {
+ if (aViewData.GetDocShell()->IsReadOnly())
+ {
+ pGlobStrId = STR_READONLYERR;
+ }
+ }
+
+ m_xMessageBox.reset(Application::CreateMessageDialog(pParent,
+ VclMessageType::Info, VclButtonsType::Ok,
+ ScResId(pGlobStrId)));
+
+ if (comphelper::LibreOfficeKit::isActive())
+ m_xMessageBox->SetInstallLOKNotifierHdl(LINK(this, ScTabView, InstallLOKNotifierHdl));
+
+ weld::Window* pGrabOnClose = bFocus ? pParent : nullptr;
+ m_xMessageBox->runAsync(m_xMessageBox, [this, pGrabOnClose](sal_Int32 /*nResult*/) {
+ m_xMessageBox.reset();
+ if (pGrabOnClose)
+ pGrabOnClose->grab_focus();
+ });
+}
+
+void ScTabView::UpdatePageBreakData( bool bForcePaint )
+{
+ std::unique_ptr<ScPageBreakData> pNewData;
+
+ if (aViewData.IsPagebreakMode())
+ {
+ ScDocShell* pDocSh = aViewData.GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTab = aViewData.GetTabNo();
+
+ sal_uInt16 nCount = rDoc.GetPrintRangeCount(nTab);
+ if (!nCount)
+ nCount = 1;
+ pNewData.reset( new ScPageBreakData(nCount) );
+
+ ScPrintFunc aPrintFunc( pDocSh, pDocSh->GetPrinter(), nTab, 0,0,nullptr, nullptr, pNewData.get() );
+ // ScPrintFunc fills the PageBreakData in ctor
+ if ( nCount > 1 )
+ {
+ aPrintFunc.ResetBreaks(nTab);
+ pNewData->AddPages();
+ }
+
+ // print area changed?
+ if ( bForcePaint || ( pPageBreakData && !( *pPageBreakData == *pNewData ) ) )
+ PaintGrid();
+ }
+
+ pPageBreakData = std::move(pNewData);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabview3.cxx b/sc/source/ui/view/tabview3.cxx
new file mode 100644
index 0000000000..4a78aa38e7
--- /dev/null
+++ b/sc/source/ui/view/tabview3.cxx
@@ -0,0 +1,3171 @@
+/* -*- 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 .
+ */
+
+#include <officecfg/Office/Calc.hxx>
+#include <rangelst.hxx>
+#include <scitems.hxx>
+
+#include <editeng/editview.hxx>
+#include <svx/fmshell.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <svx/svdoole2.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <vcl/cursor.hxx>
+#include <vcl/uitest/logger.hxx>
+#include <vcl/uitest/eventdescription.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+#include <IAnyRefDialog.hxx>
+#include <tabview.hxx>
+#include <tabvwsh.hxx>
+#include <docsh.hxx>
+#include <gridwin.hxx>
+#include <olinewin.hxx>
+#include <overlayobject.hxx>
+#include <colrowba.hxx>
+#include <tabcont.hxx>
+#include <scmod.hxx>
+#include <sc.hrc>
+#include <viewutil.hxx>
+#include <editutil.hxx>
+#include <inputhdl.hxx>
+#include <inputwin.hxx>
+#include <validat.hxx>
+#include <inputopt.hxx>
+#include <rfindlst.hxx>
+#include <hiranges.hxx>
+#include <viewuno.hxx>
+#include <dpobject.hxx>
+#include <seltrans.hxx>
+#include <fillinfo.hxx>
+#include <rangeutl.hxx>
+#include <client.hxx>
+#include <tabprotection.hxx>
+#include <spellcheckcontext.hxx>
+#include <markdata.hxx>
+#include <formula/FormulaCompiler.hxx>
+#include <comphelper/lok.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <output.hxx>
+
+#include <utility>
+
+#include <com/sun/star/chart2/data/HighlightedRange.hpp>
+
+namespace
+{
+
+ScRange lcl_getSubRangeByIndex( const ScRange& rRange, sal_Int32 nIndex )
+{
+ ScAddress aResult( rRange.aStart );
+
+ SCCOL nWidth = rRange.aEnd.Col() - rRange.aStart.Col() + 1;
+ SCROW nHeight = rRange.aEnd.Row() - rRange.aStart.Row() + 1;
+ SCTAB nDepth = rRange.aEnd.Tab() - rRange.aStart.Tab() + 1;
+ if( (nWidth > 0) && (nHeight > 0) && (nDepth > 0) )
+ {
+ // row by row from first to last sheet
+ sal_Int32 nArea = nWidth * nHeight;
+ aResult.IncCol( static_cast< SCCOL >( nIndex % nWidth ) );
+ aResult.IncRow( static_cast< SCROW >( (nIndex % nArea) / nWidth ) );
+ aResult.IncTab( static_cast< SCTAB >( nIndex / nArea ) );
+ if( !rRange.Contains( aResult ) )
+ aResult = rRange.aStart;
+ }
+
+ return ScRange( aResult );
+}
+
+} // anonymous namespace
+
+using namespace com::sun::star;
+
+ScExtraEditViewManager::~ScExtraEditViewManager()
+{
+ DBG_ASSERT(nTotalWindows == 0, "ScExtraEditViewManager dtor: some out window has not yet been removed!");
+}
+
+inline void ScExtraEditViewManager::Add(SfxViewShell* pViewShell, ScSplitPos eWhich)
+{
+ Apply<Adder>(pViewShell, eWhich);
+}
+
+inline void ScExtraEditViewManager::Remove(SfxViewShell* pViewShell, ScSplitPos eWhich)
+{
+ Apply<Remover>(pViewShell, eWhich);
+}
+
+
+template<ScExtraEditViewManager::ModifierTagType ModifierTag>
+void ScExtraEditViewManager::Apply(SfxViewShell* pViewShell, ScSplitPos eWhich)
+{
+ ScTabViewShell* pOtherViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
+ if (pOtherViewShell == nullptr || pOtherViewShell == mpThisViewShell)
+ return;
+
+ mpOtherEditView = pOtherViewShell->GetViewData().GetEditView(eWhich);
+ if (mpOtherEditView != nullptr)
+ {
+ DBG_ASSERT(mpOtherEditView->GetEditEngine(), "Edit view has no valid engine.");
+ for (int i = 0; i < 4; ++i)
+ {
+ ScGridWindow* pWin = mpGridWin[i].get();
+ if (pWin != nullptr)
+ {
+ Modifier<ModifierTag>(pWin);
+ }
+ }
+ }
+}
+
+template<ScExtraEditViewManager::ModifierTagType ModifierTag>
+void ScExtraEditViewManager::Modifier(ScGridWindow* /*pWin*/)
+{
+ (void)this;
+ SAL_WARN("sc", "ScExtraEditViewManager::Modifier<ModifierTag>: non-specialized version should not be invoked.");
+}
+
+template<>
+void ScExtraEditViewManager::Modifier<ScExtraEditViewManager::Adder>(ScGridWindow* pWin)
+{
+ if (mpOtherEditView->AddOtherViewWindow(pWin))
+ ++nTotalWindows;
+}
+
+template<>
+void ScExtraEditViewManager::Modifier<ScExtraEditViewManager::Remover>(ScGridWindow* pWin)
+{
+ if (mpOtherEditView->RemoveOtherViewWindow(pWin))
+ --nTotalWindows;
+}
+
+// --- public functions
+
+void ScTabView::ClickCursor( SCCOL nPosX, SCROW nPosY, bool bControl )
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCTAB nTab = aViewData.GetTabNo();
+ rDoc.SkipOverlapped(nPosX, nPosY, nTab);
+
+ bool bRefMode = SC_MOD()->IsFormulaMode();
+
+ if ( bRefMode )
+ {
+ DoneRefMode();
+
+ if (bControl)
+ SC_MOD()->AddRefEntry();
+
+ InitRefMode( nPosX, nPosY, nTab, SC_REFTYPE_REF );
+ }
+ else
+ {
+ DoneBlockMode( bControl );
+ aViewData.ResetOldCursor();
+ SetCursor( nPosX, nPosY );
+ }
+}
+
+void ScTabView::UpdateAutoFillMark(bool bFromPaste)
+{
+ // single selection or cursor
+ ScRange aMarkRange;
+ ScMarkType eMarkType = aViewData.GetSimpleArea(aMarkRange);
+ bool bMarked = eMarkType == SC_MARK_SIMPLE || eMarkType == SC_MARK_SIMPLE_FILTERED;
+
+ for (sal_uInt16 i = 0; i < 4; i++)
+ {
+ if (pGridWin[i] && pGridWin[i]->IsVisible())
+ pGridWin[i]->UpdateAutoFillMark( bMarked, aMarkRange );
+ }
+
+ for (sal_uInt16 i = 0; i < 2; i++)
+ {
+ if (pColBar[i] && pColBar[i]->IsVisible())
+ pColBar[i]->SetMark( bMarked, aMarkRange.aStart.Col(), aMarkRange.aEnd.Col() );
+ if (pRowBar[i] && pRowBar[i]->IsVisible())
+ pRowBar[i]->SetMark( bMarked, aMarkRange.aStart.Row(), aMarkRange.aEnd.Row() );
+ }
+
+ // selection transfer object is checked together with AutoFill marks,
+ // because it has the same requirement of a single continuous block.
+ if (!bFromPaste)
+ CheckSelectionTransfer(); // update selection transfer object
+}
+
+void ScTabView::FakeButtonUp( ScSplitPos eWhich )
+{
+ if (pGridWin[eWhich])
+ pGridWin[eWhich]->FakeButtonUp();
+}
+
+void ScTabView::HideAllCursors()
+{
+ for (VclPtr<ScGridWindow> & pWin : pGridWin)
+ {
+ if (pWin && pWin->IsVisible())
+ {
+ vcl::Cursor* pCur = pWin->GetCursor();
+ if (pCur && pCur->IsVisible())
+ pCur->Hide();
+ pWin->HideCursor();
+ }
+ }
+}
+
+void ScTabView::ShowAllCursors()
+{
+ for (VclPtr<ScGridWindow> & pWin : pGridWin)
+ {
+ if (pWin && pWin->IsVisible())
+ {
+ pWin->ShowCursor();
+ pWin->CursorChanged();
+ }
+ }
+}
+
+void ScTabView::ShowCursor()
+{
+ pGridWin[aViewData.GetActivePart()]->ShowCursor();
+ pGridWin[aViewData.GetActivePart()]->CursorChanged();
+}
+
+void ScTabView::InvalidateAttribs()
+{
+ SfxBindings& rBindings = aViewData.GetBindings();
+
+ rBindings.Invalidate( SID_STYLE_APPLY );
+ rBindings.Invalidate( SID_STYLE_FAMILY2 );
+ rBindings.Invalidate( SID_STYLE_FAMILY3 );
+
+ rBindings.Invalidate( SID_ATTR_CHAR_FONT );
+ rBindings.Invalidate( SID_ATTR_CHAR_FONTHEIGHT );
+ rBindings.Invalidate( SID_ATTR_CHAR_COLOR );
+
+ rBindings.Invalidate( SID_ATTR_CHAR_WEIGHT );
+ rBindings.Invalidate( SID_ATTR_CHAR_POSTURE );
+ rBindings.Invalidate( SID_ATTR_CHAR_UNDERLINE );
+ rBindings.Invalidate( SID_ULINE_VAL_NONE );
+ rBindings.Invalidate( SID_ULINE_VAL_SINGLE );
+ rBindings.Invalidate( SID_ULINE_VAL_DOUBLE );
+ rBindings.Invalidate( SID_ULINE_VAL_DOTTED );
+
+ rBindings.Invalidate( SID_ATTR_CHAR_OVERLINE );
+
+ rBindings.Invalidate( SID_ATTR_CHAR_KERNING );
+ rBindings.Invalidate( SID_SET_SUPER_SCRIPT );
+ rBindings.Invalidate( SID_SET_SUB_SCRIPT );
+ rBindings.Invalidate( SID_ATTR_CHAR_STRIKEOUT );
+ rBindings.Invalidate( SID_ATTR_CHAR_SHADOWED );
+
+ rBindings.Invalidate( SID_ATTR_PARA_ADJUST_LEFT );
+ rBindings.Invalidate( SID_ATTR_PARA_ADJUST_RIGHT );
+ rBindings.Invalidate( SID_ATTR_PARA_ADJUST_BLOCK );
+ rBindings.Invalidate( SID_ATTR_PARA_ADJUST_CENTER);
+ rBindings.Invalidate( SID_NUMBER_TYPE_FORMAT);
+
+ rBindings.Invalidate( SID_ALIGNLEFT );
+ rBindings.Invalidate( SID_ALIGNRIGHT );
+ rBindings.Invalidate( SID_ALIGNBLOCK );
+ rBindings.Invalidate( SID_ALIGNCENTERHOR );
+
+ rBindings.Invalidate( SID_ALIGNTOP );
+ rBindings.Invalidate( SID_ALIGNBOTTOM );
+ rBindings.Invalidate( SID_ALIGNCENTERVER );
+
+ rBindings.Invalidate( SID_SCATTR_CELLPROTECTION );
+
+ // stuff for sidebar panels
+ {
+ rBindings.Invalidate( SID_H_ALIGNCELL );
+ rBindings.Invalidate( SID_V_ALIGNCELL );
+ rBindings.Invalidate( SID_ATTR_ALIGN_INDENT );
+ rBindings.Invalidate( SID_FRAME_LINECOLOR );
+ rBindings.Invalidate( SID_FRAME_LINESTYLE );
+ rBindings.Invalidate( SID_ATTR_BORDER_OUTER );
+ rBindings.Invalidate( SID_ATTR_BORDER_INNER );
+ rBindings.Invalidate( SID_ATTR_BORDER_DIAG_TLBR );
+ rBindings.Invalidate( SID_ATTR_BORDER_DIAG_BLTR );
+ rBindings.Invalidate( SID_NUMBER_TYPE_FORMAT );
+ }
+
+ rBindings.Invalidate( SID_BACKGROUND_COLOR );
+
+ rBindings.Invalidate( SID_ATTR_ALIGN_LINEBREAK );
+ rBindings.Invalidate( SID_NUMBER_FORMAT );
+
+ rBindings.Invalidate( SID_TEXTDIRECTION_LEFT_TO_RIGHT );
+ rBindings.Invalidate( SID_TEXTDIRECTION_TOP_TO_BOTTOM );
+ rBindings.Invalidate( SID_ATTR_PARA_LEFT_TO_RIGHT );
+ rBindings.Invalidate( SID_ATTR_PARA_RIGHT_TO_LEFT );
+
+ // pseudo slots for Format menu
+ rBindings.Invalidate( SID_ALIGN_ANY_HDEFAULT );
+ rBindings.Invalidate( SID_ALIGN_ANY_LEFT );
+ rBindings.Invalidate( SID_ALIGN_ANY_HCENTER );
+ rBindings.Invalidate( SID_ALIGN_ANY_RIGHT );
+ rBindings.Invalidate( SID_ALIGN_ANY_JUSTIFIED );
+ rBindings.Invalidate( SID_ALIGN_ANY_VDEFAULT );
+ rBindings.Invalidate( SID_ALIGN_ANY_TOP );
+ rBindings.Invalidate( SID_ALIGN_ANY_VCENTER );
+ rBindings.Invalidate( SID_ALIGN_ANY_BOTTOM );
+
+ rBindings.Invalidate( SID_NUMBER_CURRENCY );
+ rBindings.Invalidate( SID_NUMBER_SCIENTIFIC );
+ rBindings.Invalidate( SID_NUMBER_DATE );
+ rBindings.Invalidate( SID_NUMBER_CURRENCY );
+ rBindings.Invalidate( SID_NUMBER_PERCENT );
+ rBindings.Invalidate( SID_NUMBER_TWODEC );
+ rBindings.Invalidate( SID_NUMBER_TIME );
+ rBindings.Invalidate( SID_NUMBER_STANDARD );
+ rBindings.Invalidate( SID_NUMBER_THOUSANDS );
+}
+
+namespace {
+
+void collectUIInformation(std::map<OUString, OUString>&& aParameters)
+{
+ EventDescription aDescription;
+ aDescription.aID = "grid_window";
+ aDescription.aAction = "SELECT";
+ aDescription.aParameters = std::move(aParameters);
+ aDescription.aParent = "MainWindow";
+ aDescription.aKeyWord = "ScGridWinUIObject";
+
+ UITestLogger::getInstance().logEvent(aDescription);
+}
+
+}
+
+// SetCursor - Cursor, set, draw, update InputWin
+// or send reference
+// Optimising breaks the functionality
+
+void ScTabView::SetCursor( SCCOL nPosX, SCROW nPosY, bool bNew )
+{
+ SCCOL nOldX = aViewData.GetCurX();
+ SCROW nOldY = aViewData.GetCurY();
+
+ // DeactivateIP only for MarkListHasChanged
+
+ // 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 = std::min(nPosY, MAXTILEDROW);
+
+ if ( !(nPosX != nOldX || nPosY != nOldY || bNew) )
+ {
+ HighlightOverlay();
+ return;
+ }
+
+ ScTabViewShell* pViewShell = aViewData.GetViewShell();
+ bool bRefMode = pViewShell && pViewShell->IsRefInputMode();
+ if ( aViewData.HasEditView( aViewData.GetActivePart() ) && !bRefMode ) // 23259 or so
+ {
+ UpdateInputLine();
+ }
+
+ HideAllCursors();
+
+ aViewData.SetCurX( nPosX );
+ aViewData.SetCurY( nPosY );
+
+ ShowAllCursors();
+
+ HighlightOverlay();
+
+ CursorPosChanged();
+
+ OUString aCurrAddress = ScAddress(nPosX,nPosY,0).GetColRowString();
+ collectUIInformation({{"CELL", aCurrAddress}});
+
+ if (!comphelper::LibreOfficeKit::isActive())
+ return;
+
+ if (nPosX <= aViewData.GetMaxTiledCol() - 10 && nPosY <= aViewData.GetMaxTiledRow() - 25)
+ return;
+
+ ScDocument& rDoc = aViewData.GetDocument();
+ ScDocShell* pDocSh = aViewData.GetDocShell();
+ ScModelObj* pModelObj = pDocSh ? pDocSh->GetModel() : nullptr;
+ Size aOldSize(0, 0);
+ if (pModelObj)
+ aOldSize = pModelObj->getDocumentSize();
+
+ if (nPosX > aViewData.GetMaxTiledCol() - 10)
+ aViewData.SetMaxTiledCol(std::min<SCCOL>(std::max(nPosX, aViewData.GetMaxTiledCol()) + 10, rDoc.MaxCol()));
+
+ if (nPosY > aViewData.GetMaxTiledRow() - 25)
+ aViewData.SetMaxTiledRow(std::min<SCROW>(std::max(nPosY, aViewData.GetMaxTiledRow()) + 25, MAXTILEDROW));
+
+ Size aNewSize(0, 0);
+ if (pModelObj)
+ aNewSize = pModelObj->getDocumentSize();
+
+ if (!pDocSh)
+ return;
+
+ // New area extended to the right of the sheet after last column
+ // including overlapping area with aNewRowArea
+ tools::Rectangle aNewColArea(aOldSize.getWidth(), 0, aNewSize.getWidth(), aNewSize.getHeight());
+ // New area extended to the bottom of the sheet after last row
+ // excluding overlapping area with aNewColArea
+ tools::Rectangle aNewRowArea(0, aOldSize.getHeight(), aOldSize.getWidth(), aNewSize.getHeight());
+
+ // Only invalidate if spreadsheet extended to the right
+ if (aNewColArea.getOpenWidth())
+ {
+ SfxLokHelper::notifyInvalidation(aViewData.GetViewShell(), &aNewColArea);
+ }
+
+ // Only invalidate if spreadsheet extended to the bottom
+ if (aNewRowArea.getOpenHeight())
+ {
+ SfxLokHelper::notifyInvalidation(aViewData.GetViewShell(), &aNewRowArea);
+ }
+
+ // Provide size in the payload, so clients don't have to
+ // call lok::Document::getDocumentSize().
+ std::stringstream ss;
+ ss << aNewSize.Width() << ", " << aNewSize.Height();
+ OString sSize( ss.str() );
+ ScModelObj* pModel = comphelper::getFromUnoTunnel<ScModelObj>(aViewData.GetViewShell()->GetCurrentDocument());
+ SfxLokHelper::notifyDocumentSizeChanged(aViewData.GetViewShell(), sSize, pModel, false);
+}
+
+static bool lcl_IsRefDlgActive(SfxViewFrame& rViewFrm)
+{
+ ScModule* pScMod = SC_MOD();
+ if (!pScMod->IsRefDialogOpen())
+ return false;
+
+ auto nDlgId = pScMod->GetCurRefDlgId();
+ if (!rViewFrm.HasChildWindow(nDlgId))
+ return false;
+
+ SfxChildWindow* pChild = rViewFrm.GetChildWindow(nDlgId);
+ if (!pChild)
+ return false;
+
+ auto xDlgController = pChild->GetController();
+ if (!xDlgController || !xDlgController->getDialog()->get_visible())
+ return false;
+
+ IAnyRefDialog* pRefDlg = dynamic_cast<IAnyRefDialog*>(xDlgController.get());
+ return pRefDlg && pRefDlg->IsRefInputMode();
+}
+
+void ScTabView::CheckSelectionTransfer()
+{
+ if ( !aViewData.IsActive() ) // only for active view
+ return;
+
+ ScModule* pScMod = SC_MOD();
+ ScSelectionTransferObj* pOld = pScMod->GetSelectionTransfer();
+ rtl::Reference<ScSelectionTransferObj> pNew = ScSelectionTransferObj::CreateFromView( this );
+ if ( !pNew )
+ return;
+
+ // create new selection
+
+ if (pOld)
+ pOld->ForgetView();
+
+ pScMod->SetSelectionTransfer( pNew.get() );
+
+ // tdf#124975/tdf#136242 changing the calc selection can trigger removal of the
+ // selection of an open RefDlg dialog, so don't inform the
+ // desktop clipboard of the changed selection if that dialog is open
+ if (!lcl_IsRefDlgActive(aViewData.GetViewShell()->GetViewFrame()))
+ pNew->CopyToPrimarySelection(); // may delete pOld
+
+ // Log the selection change
+ ScMarkData& rMark = aViewData.GetMarkData();
+ if (rMark.IsMarked())
+ {
+ const ScRange& aMarkRange = rMark.GetMarkArea();
+ OUString aStartAddress = aMarkRange.aStart.GetColRowString();
+ OUString aEndAddress = aMarkRange.aEnd.GetColRowString();
+ collectUIInformation({{"RANGE", aStartAddress + ":" + aEndAddress}});
+ }
+}
+
+// update input row / menus
+// CursorPosChanged calls SelectionChanged
+// SelectionChanged calls CellContentChanged
+
+void ScTabView::CellContentChanged()
+{
+ SfxBindings& rBindings = aViewData.GetBindings();
+
+ rBindings.Invalidate( SID_ATTR_SIZE ); // -> show error message
+ rBindings.Invalidate( SID_THESAURUS );
+ rBindings.Invalidate( SID_HYPERLINK_GETLINK );
+ rBindings.Invalidate( SID_ROWCOL_SELCOUNT );
+
+ InvalidateAttribs(); // attributes updates
+
+ aViewData.GetViewShell()->UpdateInputHandler();
+}
+
+void ScTabView::SetTabProtectionSymbol( SCTAB nTab, const bool bProtect )
+{
+ pTabControl->SetProtectionSymbol( static_cast<sal_uInt16>(nTab)+1, bProtect);
+}
+
+void ScTabView::SelectionChanged(bool bFromPaste)
+{
+ SfxViewFrame& rViewFrame = aViewData.GetViewShell()->GetViewFrame();
+ uno::Reference<frame::XController> xController = rViewFrame.GetFrame().GetController();
+ if (xController.is())
+ {
+ ScTabViewObj* pImp = dynamic_cast<ScTabViewObj*>( xController.get() );
+ if (pImp)
+ pImp->SelectionChanged();
+ }
+
+ UpdateAutoFillMark(bFromPaste); // also calls CheckSelectionTransfer
+
+ SfxBindings& rBindings = aViewData.GetBindings();
+
+ rBindings.Invalidate( SID_CURRENTCELL ); // -> Navigator
+ rBindings.Invalidate( SID_AUTO_FILTER ); // -> Menu
+ rBindings.Invalidate( FID_NOTE_VISIBLE );
+ rBindings.Invalidate( FID_SHOW_NOTE );
+ rBindings.Invalidate( FID_HIDE_NOTE );
+ rBindings.Invalidate( FID_SHOW_ALL_NOTES );
+ rBindings.Invalidate( FID_HIDE_ALL_NOTES );
+ rBindings.Invalidate( SID_TOGGLE_NOTES );
+ rBindings.Invalidate( SID_DELETE_NOTE );
+ rBindings.Invalidate( SID_ROWCOL_SELCOUNT );
+
+ // functions than may need to be disabled
+
+ rBindings.Invalidate( FID_INS_ROWBRK );
+ rBindings.Invalidate( FID_INS_COLBRK );
+ rBindings.Invalidate( FID_DEL_ROWBRK );
+ rBindings.Invalidate( FID_DEL_COLBRK );
+ rBindings.Invalidate( FID_MERGE_ON );
+ rBindings.Invalidate( FID_MERGE_OFF );
+ rBindings.Invalidate( FID_MERGE_TOGGLE );
+ rBindings.Invalidate( SID_AUTOFILTER_HIDE );
+ rBindings.Invalidate( SID_UNFILTER );
+ rBindings.Invalidate( SID_REIMPORT_DATA );
+ rBindings.Invalidate( SID_REFRESH_DBAREA );
+ rBindings.Invalidate( SID_OUTLINE_SHOW );
+ rBindings.Invalidate( SID_OUTLINE_HIDE );
+ rBindings.Invalidate( SID_OUTLINE_REMOVE );
+ rBindings.Invalidate( FID_FILL_TO_BOTTOM );
+ rBindings.Invalidate( FID_FILL_TO_RIGHT );
+ rBindings.Invalidate( FID_FILL_TO_TOP );
+ rBindings.Invalidate( FID_FILL_TO_LEFT );
+ rBindings.Invalidate( FID_FILL_SERIES );
+ rBindings.Invalidate( SID_SCENARIOS );
+ rBindings.Invalidate( SID_AUTOFORMAT );
+ rBindings.Invalidate( SID_OPENDLG_TABOP );
+ rBindings.Invalidate( SID_DATA_SELECT );
+
+ rBindings.Invalidate( SID_CUT );
+ rBindings.Invalidate( SID_COPY );
+ rBindings.Invalidate( SID_PASTE );
+ rBindings.Invalidate( SID_PASTE_SPECIAL );
+ rBindings.Invalidate( SID_PASTE_UNFORMATTED );
+
+ rBindings.Invalidate( FID_INS_ROW );
+ rBindings.Invalidate( FID_INS_COLUMN );
+ rBindings.Invalidate( FID_INS_ROWS_BEFORE );
+ rBindings.Invalidate( FID_INS_COLUMNS_BEFORE );
+ rBindings.Invalidate( FID_INS_ROWS_AFTER );
+ rBindings.Invalidate( FID_INS_COLUMNS_AFTER );
+ rBindings.Invalidate( FID_INS_CELL );
+ rBindings.Invalidate( FID_INS_CELLSDOWN );
+ rBindings.Invalidate( FID_INS_CELLSRIGHT );
+
+ rBindings.Invalidate( FID_CHG_COMMENT );
+
+ // only due to protect cell:
+
+ rBindings.Invalidate( SID_CELL_FORMAT_RESET );
+ rBindings.Invalidate( SID_DELETE );
+ rBindings.Invalidate( SID_DELETE_CONTENTS );
+ rBindings.Invalidate( FID_DELETE_CELL );
+ rBindings.Invalidate( FID_CELL_FORMAT );
+ rBindings.Invalidate( SID_ENABLE_HYPHENATION );
+ rBindings.Invalidate( SID_INSERT_POSTIT );
+ rBindings.Invalidate( SID_CHARMAP );
+ rBindings.Invalidate( SID_OPENDLG_FUNCTION );
+ rBindings.Invalidate( FID_VALIDATION );
+ rBindings.Invalidate( SID_EXTERNAL_SOURCE );
+ rBindings.Invalidate( SID_TEXT_TO_COLUMNS );
+ rBindings.Invalidate( SID_SORT_ASCENDING );
+ rBindings.Invalidate( SID_SORT_DESCENDING );
+ rBindings.Invalidate( SID_SELECT_UNPROTECTED_CELLS );
+
+ if (aViewData.GetViewShell()->HasAccessibilityObjects())
+ aViewData.GetViewShell()->BroadcastAccessibility(SfxHint(SfxHintId::ScAccCursorChanged));
+
+ CellContentChanged();
+}
+
+void ScTabView::CursorPosChanged()
+{
+ bool bRefMode = SC_MOD()->IsFormulaMode();
+ if ( !bRefMode ) // check that RefMode works when switching sheets
+ aViewData.GetDocShell()->Broadcast( SfxHint( SfxHintId::ScKillEditView ) );
+
+ // Broadcast, so that other Views of the document also switch
+
+ ScDocument& rDocument = aViewData.GetDocument();
+ bool bDataPilot = rDocument.HasDataPilotAtPosition(aViewData.GetCurPos());
+ aViewData.GetViewShell()->SetPivotShell(bDataPilot);
+
+ if (!bDataPilot)
+ {
+ bool bSparkline = rDocument.HasSparkline(aViewData.GetCurPos());
+ aViewData.GetViewShell()->SetSparklineShell(bSparkline);
+ }
+
+ // UpdateInputHandler now in CellContentChanged
+
+ SelectionChanged();
+
+ aViewData.SetTabStartCol( SC_TABSTART_NONE );
+}
+
+namespace {
+
+Point calcHintWindowPosition(
+ const Point& rCellPos, const Size& rCellSize, const Size& rFrameWndSize, const Size& rHintWndSize)
+{
+ const tools::Long nMargin = 20;
+
+ tools::Long nMLeft = rCellPos.X();
+ tools::Long nMRight = rFrameWndSize.Width() - rCellPos.X() - rCellSize.Width();
+ tools::Long nMTop = rCellPos.Y();
+ tools::Long nMBottom = rFrameWndSize.Height() - rCellPos.Y() - rCellSize.Height();
+
+ // First, see if we can fit the entire hint window in the visible region.
+
+ if (nMRight - nMargin >= rHintWndSize.Width())
+ {
+ // Right margin is wide enough.
+ if (rFrameWndSize.Height() >= rHintWndSize.Height())
+ {
+ // The frame has enough height. Take it.
+ Point aPos = rCellPos;
+ aPos.AdjustX(rCellSize.Width() + nMargin );
+ if (aPos.Y() + rHintWndSize.Height() > rFrameWndSize.Height())
+ {
+ // Push the hint window up a bit to make it fit.
+ aPos.setY( rFrameWndSize.Height() - rHintWndSize.Height() );
+ }
+ return aPos;
+ }
+ }
+
+ if (nMBottom - nMargin >= rHintWndSize.Height())
+ {
+ // Bottom margin is high enough.
+ if (rFrameWndSize.Width() >= rHintWndSize.Width())
+ {
+ // The frame has enough width. Take it.
+ Point aPos = rCellPos;
+ aPos.AdjustY(rCellSize.Height() + nMargin );
+ if (aPos.X() + rHintWndSize.Width() > rFrameWndSize.Width())
+ {
+ // Move the hint window to the left to make it fit.
+ aPos.setX( rFrameWndSize.Width() - rHintWndSize.Width() );
+ }
+ return aPos;
+ }
+ }
+
+ if (nMLeft - nMargin >= rHintWndSize.Width())
+ {
+ // Left margin is wide enough.
+ if (rFrameWndSize.Height() >= rHintWndSize.Height())
+ {
+ // The frame is high enough. Take it.
+ Point aPos = rCellPos;
+ aPos.AdjustX( -(rHintWndSize.Width() + nMargin) );
+ if (aPos.Y() + rHintWndSize.Height() > rFrameWndSize.Height())
+ {
+ // Push the hint window up a bit to make it fit.
+ aPos.setY( rFrameWndSize.Height() - rHintWndSize.Height() );
+ }
+ return aPos;
+ }
+ }
+
+ if (nMTop - nMargin >= rHintWndSize.Height())
+ {
+ // Top margin is high enough.
+ if (rFrameWndSize.Width() >= rHintWndSize.Width())
+ {
+ // The frame is wide enough. Take it.
+ Point aPos = rCellPos;
+ aPos.AdjustY( -(rHintWndSize.Height() + nMargin) );
+ if (aPos.X() + rHintWndSize.Width() > rFrameWndSize.Width())
+ {
+ // Move the hint window to the left to make it fit.
+ aPos.setX( rFrameWndSize.Width() - rHintWndSize.Width() );
+ }
+ return aPos;
+ }
+ }
+
+ // The popup doesn't fit in any direction in its entirety. Do our best.
+
+ if (nMRight - nMargin >= rHintWndSize.Width())
+ {
+ // Right margin is good enough.
+ Point aPos = rCellPos;
+ aPos.AdjustX(nMargin + rCellSize.Width() );
+ aPos.setY( 0 );
+ return aPos;
+ }
+
+ if (nMBottom - nMargin >= rHintWndSize.Height())
+ {
+ // Bottom margin is good enough.
+ Point aPos = rCellPos;
+ aPos.AdjustY(nMargin + rCellSize.Height() );
+ aPos.setX( 0 );
+ return aPos;
+ }
+
+ if (nMLeft - nMargin >= rHintWndSize.Width())
+ {
+ // Left margin is good enough.
+ Point aPos = rCellPos;
+ aPos.AdjustX( -(rHintWndSize.Width() + nMargin) );
+ aPos.setY( 0 );
+ return aPos;
+ }
+
+ if (nMTop - nMargin >= rHintWndSize.Height())
+ {
+ // Top margin is good enough.
+ Point aPos = rCellPos;
+ aPos.AdjustY( -(rHintWndSize.Height() + nMargin) );
+ aPos.setX( 0 );
+ return aPos;
+ }
+
+ // None of the above. Hopeless. At least try not to cover the current
+ // cell.
+ Point aPos = rCellPos;
+ aPos.AdjustX(rCellSize.Width() );
+ return aPos;
+}
+
+}
+
+void ScTabView::TestHintWindow()
+{
+ // show input help window and list drop-down button for validity
+
+ mxInputHintOO.reset();
+
+ bool bListValButton = false;
+ ScAddress aListValPos;
+
+ ScDocument& rDoc = aViewData.GetDocument();
+ const SfxUInt32Item* pItem = rDoc.GetAttr( aViewData.GetCurX(),
+ aViewData.GetCurY(),
+ aViewData.GetTabNo(),
+ ATTR_VALIDDATA );
+ if ( pItem->GetValue() )
+ {
+ const ScValidationData* pData = rDoc.GetValidationEntry( pItem->GetValue() );
+ OSL_ENSURE(pData,"ValidationData not found");
+ OUString aTitle, aMessage;
+
+ if ( pData && pData->GetInput( aTitle, aMessage ) && !aMessage.isEmpty() )
+ {
+ ScSplitPos eWhich = aViewData.GetActivePart();
+ ScGridWindow* pWin = pGridWin[eWhich].get();
+ SCCOL nCol = aViewData.GetCurX();
+ SCROW nRow = aViewData.GetCurY();
+ Point aPos = aViewData.GetScrPos( nCol, nRow, eWhich );
+ Size aWinSize = pWin->GetOutputSizePixel();
+ // cursor visible?
+ if ( nCol >= aViewData.GetPosX(WhichH(eWhich)) &&
+ nRow >= aViewData.GetPosY(WhichV(eWhich)) &&
+ aPos.X() < aWinSize.Width() && aPos.Y() < aWinSize.Height() )
+ {
+ const svtools::ColorConfig& rColorCfg = SC_MOD()->GetColorConfig();
+ Color aCommentColor = rColorCfg.GetColorValue(svtools::CALCNOTESBACKGROUND).nColor;
+ // create HintWindow, determines its size by itself
+ ScOverlayHint* pOverlay = new ScOverlayHint(aTitle, aMessage, aCommentColor, pFrameWin->GetFont());
+
+ mxInputHintOO.reset(new sdr::overlay::OverlayObjectList);
+ mxInputHintOO->append(std::unique_ptr<sdr::overlay::OverlayObject>(pOverlay));
+
+ Size aHintWndSize = pOverlay->GetSizePixel();
+ tools::Long nCellSizeX = 0;
+ tools::Long nCellSizeY = 0;
+ aViewData.GetMergeSizePixel(nCol, nRow, nCellSizeX, nCellSizeY);
+
+ Point aHintPos = calcHintWindowPosition(
+ aPos, Size(nCellSizeX,nCellSizeY), aWinSize, aHintWndSize);
+
+ pOverlay->SetPos(pWin->PixelToLogic(aHintPos, pWin->GetDrawMapMode()), pWin->GetDrawMapMode());
+ for (VclPtr<ScGridWindow> & pWindow : pGridWin)
+ {
+ if (!pWindow)
+ continue;
+ if (!pWindow->IsVisible())
+ continue;
+ rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = pWindow->getOverlayManager();
+ if (!xOverlayManager.is())
+ continue;
+ if (pWindow == pWin)
+ {
+ xOverlayManager->add(*pOverlay);
+ pWindow->updateLOKInputHelp(aTitle, aMessage);
+ }
+ else
+ {
+ //tdf#92530 if the help tip doesn't fit into its allocated area in a split window
+ //scenario, then because here we place it into the other split windows as well the
+ //missing portions will be displayed in the other split windows to form an apparent
+ //single tip, albeit "under" the split lines
+ Point aOtherPos(pWindow->ScreenToOutputPixel(pWin->OutputToScreenPixel(aHintPos)));
+ std::unique_ptr<ScOverlayHint> pOtherOverlay(new ScOverlayHint(aTitle, aMessage, aCommentColor, pFrameWin->GetFont()));
+ Point aFooPos(pWindow->PixelToLogic(aOtherPos, pWindow->GetDrawMapMode()));
+ pOtherOverlay->SetPos(aFooPos, pWindow->GetDrawMapMode());
+ xOverlayManager->add(*pOtherOverlay);
+ mxInputHintOO->append(std::move(pOtherOverlay));
+ }
+ }
+ }
+ }
+
+ // list drop-down button
+ if ( pData && pData->HasSelectionList() )
+ {
+ aListValPos.Set( aViewData.GetCurX(), aViewData.GetCurY(), aViewData.GetTabNo() );
+ bListValButton = true;
+ }
+ }
+
+ for (VclPtr<ScGridWindow> const & pWin : pGridWin)
+ {
+ if (pWin && pWin->IsVisible())
+ pWin->UpdateListValPos(bListValButton, aListValPos);
+ }
+}
+
+bool ScTabView::HasHintWindow() const { return mxInputHintOO != nullptr; }
+
+void ScTabView::RemoveHintWindow()
+{
+ mxInputHintOO.reset();
+}
+
+// find window that should not be over the cursor
+static weld::Window* lcl_GetCareWin(SfxViewFrame& rViewFrm)
+{
+ //! also spelling ??? (then set the member variables when calling)
+
+ // search & replace
+ if (rViewFrm.HasChildWindow(SID_SEARCH_DLG))
+ {
+ SfxChildWindow* pChild = rViewFrm.GetChildWindow(SID_SEARCH_DLG);
+ if (pChild)
+ {
+ auto xDlgController = pChild->GetController();
+ if (xDlgController && xDlgController->getDialog()->get_visible())
+ return xDlgController->getDialog();
+ }
+ }
+
+ // apply changes
+ if ( rViewFrm.HasChildWindow(FID_CHG_ACCEPT) )
+ {
+ SfxChildWindow* pChild = rViewFrm.GetChildWindow(FID_CHG_ACCEPT);
+ if (pChild)
+ {
+ auto xDlgController = pChild->GetController();
+ if (xDlgController && xDlgController->getDialog()->get_visible())
+ return xDlgController->getDialog();
+ }
+ }
+
+ return nullptr;
+}
+
+ // adjust screen with respect to cursor position
+
+void ScTabView::AlignToCursor( SCCOL nCurX, SCROW nCurY, ScFollowMode eMode,
+ const ScSplitPos* pWhich )
+{
+ // now switch active part here
+
+ ScSplitPos eActive = aViewData.GetActivePart();
+ ScHSplitPos eActiveX = WhichH(eActive);
+ ScVSplitPos eActiveY = WhichV(eActive);
+ bool bHFix = (aViewData.GetHSplitMode() == SC_SPLIT_FIX);
+ bool bVFix = (aViewData.GetVSplitMode() == SC_SPLIT_FIX);
+ if (bHFix && eActiveX == SC_SPLIT_LEFT && nCurX >= aViewData.GetFixPosX())
+ {
+ ActivatePart( (eActiveY==SC_SPLIT_TOP) ? SC_SPLIT_TOPRIGHT : SC_SPLIT_BOTTOMRIGHT );
+ eActiveX = SC_SPLIT_RIGHT;
+ }
+ if (bVFix && eActiveY == SC_SPLIT_TOP && nCurY >= aViewData.GetFixPosY())
+ {
+ ActivatePart( (eActiveX==SC_SPLIT_LEFT) ? SC_SPLIT_BOTTOMLEFT : SC_SPLIT_BOTTOMRIGHT );
+ eActiveY = SC_SPLIT_BOTTOM;
+ }
+
+ // actual align
+
+ if ( eMode != SC_FOLLOW_NONE )
+ {
+ ScSplitPos eAlign;
+ if (pWhich)
+ eAlign = *pWhich;
+ else
+ eAlign = aViewData.GetActivePart();
+ ScHSplitPos eAlignX = WhichH(eAlign);
+ ScVSplitPos eAlignY = WhichV(eAlign);
+
+ SCCOL nDeltaX = aViewData.GetPosX(eAlignX);
+ SCROW nDeltaY = aViewData.GetPosY(eAlignY);
+ SCCOL nSizeX = aViewData.VisibleCellsX(eAlignX);
+ SCROW nSizeY = aViewData.VisibleCellsY(eAlignY);
+
+ tools::Long nCellSizeX;
+ tools::Long nCellSizeY;
+ if ( nCurX >= 0 && nCurY >= 0 )
+ aViewData.GetMergeSizePixel( nCurX, nCurY, nCellSizeX, nCellSizeY );
+ else
+ nCellSizeX = nCellSizeY = 0;
+ Size aScrSize = aViewData.GetScrSize();
+
+ tools::Long nDenom;
+ if ( eMode == SC_FOLLOW_JUMP_END && nCurX > aViewData.GetRefStartX()
+ && nCurY > aViewData.GetRefStartY() )
+ nDenom = 1; // tdf#154271 Selected cell will be at the bottom corner
+ // to maximize the visible/usable area
+ else
+ nDenom = 2; // Selected cell will be at the center of the screen, so that
+ // it will be visible. This is useful for search results, etc.
+ tools::Long nSpaceX = ( aScrSize.Width() - nCellSizeX ) / nDenom;
+ tools::Long nSpaceY = ( aScrSize.Height() - nCellSizeY ) / nDenom;
+ // nSpaceY: desired start position of cell for FOLLOW_JUMP, modified if dialog interferes
+
+ bool bForceNew = false; // force new calculation of JUMP position (vertical only)
+
+ // VisibleCellsY == CellsAtY( GetPosY( eWhichY ), 1, eWhichY )
+
+ // when for instance a search dialog is open, don't put the cursor behind the dialog
+ // if possible, put the row with the cursor above or below the dialog
+ //! not if already completely visible
+
+ if ( eMode == SC_FOLLOW_JUMP || eMode == SC_FOLLOW_JUMP_END )
+ {
+ weld::Window* pCare = lcl_GetCareWin( aViewData.GetViewShell()->GetViewFrame() );
+ if (pCare)
+ {
+ bool bLimit = false;
+ tools::Rectangle aDlgPixel;
+ Size aWinSize;
+ vcl::Window* pWin = GetActiveWin();
+ weld::Window* pFrame = pWin ? pWin->GetFrameWeld() : nullptr;
+ int x, y, width, height;
+ if (pFrame && pCare->get_extents_relative_to(*pFrame, x, y, width, height))
+ {
+ aDlgPixel = tools::Rectangle(Point(x, y), Size(width, height));
+ aWinSize = pWin->GetOutputSizePixel();
+ // dos the dialog cover the GridWin?
+ if ( aDlgPixel.Right() >= 0 && aDlgPixel.Left() < aWinSize.Width() )
+ {
+ if ( nCurX < nDeltaX || nCurX >= nDeltaX+nSizeX ||
+ nCurY < nDeltaY || nCurY >= nDeltaY+nSizeY )
+ bLimit = true; // scroll anyway
+ else
+ {
+ // cursor is on the screen
+ Point aStart = aViewData.GetScrPos( nCurX, nCurY, eAlign );
+ tools::Long nCSX, nCSY;
+ aViewData.GetMergeSizePixel( nCurX, nCurY, nCSX, nCSY );
+ tools::Rectangle aCursor( aStart, Size( nCSX, nCSY ) );
+ if ( aCursor.Overlaps( aDlgPixel ) )
+ bLimit = true; // cell is covered by the dialog
+ }
+ }
+ }
+
+ if (bLimit)
+ {
+ bool bBottom = false;
+ tools::Long nTopSpace = aDlgPixel.Top();
+ tools::Long nBotSpace = aWinSize.Height() - aDlgPixel.Bottom();
+ if ( nBotSpace > 0 && nBotSpace > nTopSpace )
+ {
+ tools::Long nDlgBot = aDlgPixel.Bottom();
+ SCCOL nWPosX;
+ SCROW nWPosY;
+ aViewData.GetPosFromPixel( 0,nDlgBot, eAlign, nWPosX, nWPosY );
+ ++nWPosY; // below the last affected cell
+
+ SCROW nDiff = nWPosY - nDeltaY;
+ if ( nCurY >= nDiff ) // position can not be negative
+ {
+ nSpaceY = nDlgBot + ( nBotSpace - nCellSizeY ) / 2;
+ bBottom = true;
+ bForceNew = true;
+ }
+ }
+ if ( !bBottom && nTopSpace > 0 )
+ {
+ nSpaceY = ( nTopSpace - nCellSizeY ) / 2;
+ bForceNew = true;
+ }
+ }
+ }
+ }
+
+ SCCOL nNewDeltaX = nDeltaX;
+ SCROW nNewDeltaY = nDeltaY;
+ bool bDoLine = false;
+
+ switch (eMode)
+ {
+ case SC_FOLLOW_JUMP:
+ case SC_FOLLOW_JUMP_END:
+ if ( nCurX < nDeltaX || nCurX >= nDeltaX+nSizeX )
+ {
+ nNewDeltaX = nCurX - aViewData.CellsAtX( nCurX, -1, eAlignX, static_cast<sal_uInt16>(nSpaceX) );
+ if (nNewDeltaX < 0)
+ nNewDeltaX = 0;
+ nSizeX = aViewData.CellsAtX( nNewDeltaX, 1, eAlignX );
+ }
+ if ( nCurY < nDeltaY || nCurY >= nDeltaY+nSizeY || bForceNew )
+ {
+ nNewDeltaY = nCurY - aViewData.CellsAtY( nCurY, -1, eAlignY, static_cast<sal_uInt16>(nSpaceY) );
+ if (nNewDeltaY < 0)
+ nNewDeltaY = 0;
+ nSizeY = aViewData.CellsAtY( nNewDeltaY, 1, eAlignY );
+ }
+ bDoLine = true;
+ break;
+
+ case SC_FOLLOW_LINE:
+ bDoLine = true;
+ break;
+
+ case SC_FOLLOW_FIX:
+ if ( nCurX < nDeltaX || nCurX >= nDeltaX+nSizeX )
+ {
+ nNewDeltaX = nDeltaX + nCurX - aViewData.GetCurX();
+ if (nNewDeltaX < 0)
+ nNewDeltaX = 0;
+ nSizeX = aViewData.CellsAtX( nNewDeltaX, 1, eAlignX );
+ }
+ if ( nCurY < nDeltaY || nCurY >= nDeltaY+nSizeY )
+ {
+ nNewDeltaY = nDeltaY + nCurY - aViewData.GetCurY();
+ if (nNewDeltaY < 0)
+ nNewDeltaY = 0;
+ nSizeY = aViewData.CellsAtY( nNewDeltaY, 1, eAlignY );
+ }
+
+ // like old version of SC_FOLLOW_JUMP:
+
+ if ( nCurX < nNewDeltaX || nCurX >= nNewDeltaX+nSizeX )
+ {
+ nNewDeltaX = nCurX - (nSizeX / 2);
+ if (nNewDeltaX < 0)
+ nNewDeltaX = 0;
+ nSizeX = aViewData.CellsAtX( nNewDeltaX, 1, eAlignX );
+ }
+ if ( nCurY < nNewDeltaY || nCurY >= nNewDeltaY+nSizeY )
+ {
+ nNewDeltaY = nCurY - (nSizeY / 2);
+ if (nNewDeltaY < 0)
+ nNewDeltaY = 0;
+ nSizeY = aViewData.CellsAtY( nNewDeltaY, 1, eAlignY );
+ }
+
+ bDoLine = true;
+ break;
+
+ case SC_FOLLOW_NONE:
+ break;
+ default:
+ OSL_FAIL("Wrong cursor mode");
+ break;
+ }
+
+ ScDocument& rDoc = aViewData.GetDocument();
+ if (bDoLine)
+ {
+ while ( nCurX >= nNewDeltaX+nSizeX )
+ {
+ nNewDeltaX = nCurX-nSizeX+1;
+ SCTAB nTab = aViewData.GetTabNo();
+ while ( nNewDeltaX < rDoc.MaxCol() && !rDoc.GetColWidth( nNewDeltaX, nTab ) )
+ ++nNewDeltaX;
+ nSizeX = aViewData.CellsAtX( nNewDeltaX, 1, eAlignX );
+ }
+ while ( nCurY >= nNewDeltaY+nSizeY )
+ {
+ nNewDeltaY = nCurY-nSizeY+1;
+ SCTAB nTab = aViewData.GetTabNo();
+ while ( nNewDeltaY < rDoc.MaxRow() && !rDoc.GetRowHeight( nNewDeltaY, nTab ) )
+ ++nNewDeltaY;
+ nSizeY = aViewData.CellsAtY( nNewDeltaY, 1, eAlignY );
+ }
+ if ( nCurX < nNewDeltaX )
+ nNewDeltaX = nCurX;
+ if ( nCurY < nNewDeltaY )
+ nNewDeltaY = nCurY;
+ }
+
+ if ( nNewDeltaX != nDeltaX )
+ nSizeX = aViewData.CellsAtX( nNewDeltaX, 1, eAlignX );
+ if (nNewDeltaX+nSizeX-1 > rDoc.MaxCol())
+ nNewDeltaX = rDoc.MaxCol()-nSizeX+1;
+ if (nNewDeltaX < 0)
+ nNewDeltaX = 0;
+
+ if ( nNewDeltaY != nDeltaY )
+ nSizeY = aViewData.CellsAtY( nNewDeltaY, 1, eAlignY );
+ if (nNewDeltaY+nSizeY-1 > rDoc.MaxRow())
+ nNewDeltaY = rDoc.MaxRow()-nSizeY+1;
+ if (nNewDeltaY < 0)
+ nNewDeltaY = 0;
+
+ if ( nNewDeltaX != nDeltaX )
+ ScrollX( nNewDeltaX - nDeltaX, eAlignX );
+ if ( nNewDeltaY != nDeltaY )
+ ScrollY( nNewDeltaY - nDeltaY, eAlignY );
+ }
+
+ // switch active part again
+
+ if (bHFix)
+ if (eActiveX == SC_SPLIT_RIGHT && nCurX < aViewData.GetFixPosX())
+ {
+ ActivatePart( (eActiveY==SC_SPLIT_TOP) ? SC_SPLIT_TOPLEFT : SC_SPLIT_BOTTOMLEFT );
+ eActiveX = SC_SPLIT_LEFT;
+ }
+ if (bVFix)
+ if (eActiveY == SC_SPLIT_BOTTOM && nCurY < aViewData.GetFixPosY())
+ {
+ ActivatePart( (eActiveX==SC_SPLIT_LEFT) ? SC_SPLIT_TOPLEFT : SC_SPLIT_TOPRIGHT );
+ }
+}
+
+bool ScTabView::SelMouseButtonDown( const MouseEvent& rMEvt )
+{
+ bool bRet = false;
+
+ // #i3875# *Hack*
+ bool bMod1Locked = (aViewData.GetViewShell()->GetLockedModifiers() & KEY_MOD1) != 0;
+ aViewData.SetSelCtrlMouseClick( rMEvt.IsMod1() || bMod1Locked );
+
+ if ( pSelEngine )
+ {
+ bMoveIsShift = rMEvt.IsShift();
+ bRet = pSelEngine->SelMouseButtonDown( rMEvt );
+ bMoveIsShift = false;
+ }
+
+ aViewData.SetSelCtrlMouseClick( false ); // #i3875# *Hack*
+
+ return bRet;
+}
+
+ // MoveCursor - with adjustment of the view section
+
+void ScTabView::MoveCursorAbs( SCCOL nCurX, SCROW nCurY, ScFollowMode eMode,
+ bool bShift, bool bControl, bool bKeepOld, bool bKeepSel )
+{
+ if (!bKeepOld)
+ aViewData.ResetOldCursor();
+
+ ScDocument& rDoc = aViewData.GetDocument();
+ // #i123629#
+ if( aViewData.GetViewShell()->GetForceFocusOnCurCell() )
+ aViewData.GetViewShell()->SetForceFocusOnCurCell( !rDoc.ValidColRow(nCurX, nCurY) );
+
+ if (nCurX < 0) nCurX = 0;
+ if (nCurY < 0) nCurY = 0;
+ if (nCurX > rDoc.MaxCol()) nCurX = rDoc.MaxCol();
+ if (nCurY > rDoc.MaxRow()) nCurY = rDoc.MaxRow();
+
+ // 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())
+ nCurY = std::min(nCurY, MAXTILEDROW);
+
+ HideAllCursors();
+
+ // switch of active now in AlignToCursor
+
+ AlignToCursor( nCurX, nCurY, eMode );
+
+ if (bKeepSel)
+ {
+ SetCursor( nCurX, nCurY ); // keep selection
+
+ // If the cursor is in existing selection, it's a cursor movement by
+ // ENTER or TAB. If not, then it's a new selection during ADD
+ // selection mode.
+
+ const ScMarkData& rMark = aViewData.GetMarkData();
+ ScRangeList aSelList;
+ rMark.FillRangeListWithMarks(&aSelList, false);
+ if (!aSelList.Contains(ScRange(nCurX, nCurY, aViewData.GetTabNo())))
+ // Cursor not in existing selection. Start a new selection.
+ DoneBlockMode(true);
+ }
+ else
+ {
+ if (!bShift)
+ {
+ // Remove all marked data on cursor movement unless the Shift is
+ // locked or while editing a formula. It is cheaper to check for
+ // marks first and then formula mode.
+ ScMarkData& rMark = aViewData.GetMarkData();
+ bool bMarked = rMark.IsMarked() || rMark.IsMultiMarked();
+ if (bMarked && !SC_MOD()->IsFormulaMode())
+ {
+ rMark.ResetMark();
+ DoneBlockMode();
+ InitOwnBlockMode( ScRange( nCurX, nCurY, aViewData.GetTabNo()));
+ MarkDataChanged();
+ }
+ }
+
+ bool bSame = ( nCurX == aViewData.GetCurX() && nCurY == aViewData.GetCurY() );
+ bMoveIsShift = bShift;
+ pSelEngine->CursorPosChanging( bShift, bControl );
+ bMoveIsShift = false;
+ aFunctionSet.SetCursorAtCell( nCurX, nCurY, false );
+
+ // If the cursor has not been moved, the SelectionChanged for canceling the
+ // selection has to happen here individually:
+ if (bSame)
+ SelectionChanged();
+ }
+
+ ShowAllCursors();
+ TestHintWindow();
+}
+
+void ScTabView::MoveCursorRel( SCCOL nMovX, SCROW nMovY, ScFollowMode eMode,
+ bool bShift, bool bKeepSel )
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCTAB nTab = aViewData.GetTabNo();
+
+ bool bSkipProtected = false, bSkipUnprotected = false;
+ const ScTableProtection* pProtect = rDoc.GetTabProtection(nTab);
+ if ( pProtect && pProtect->isProtected() )
+ {
+ bSkipProtected = !pProtect->isOptionEnabled(ScTableProtection::SELECT_LOCKED_CELLS);
+ bSkipUnprotected = !pProtect->isOptionEnabled(ScTableProtection::SELECT_UNLOCKED_CELLS);
+ }
+
+ if ( bSkipProtected && bSkipUnprotected )
+ return;
+
+ SCCOL nOldX;
+ SCROW nOldY;
+ SCCOL nCurX;
+ SCROW nCurY;
+ if ( aViewData.IsRefMode() )
+ {
+ nOldX = aViewData.GetRefEndX();
+ nOldY = aViewData.GetRefEndY();
+ nCurX = nOldX + nMovX;
+ nCurY = nOldY + nMovY;
+ }
+ else
+ {
+ nOldX = aViewData.GetCurX();
+ nOldY = aViewData.GetCurY();
+ nCurX = (nMovX != 0) ? nOldX+nMovX : aViewData.GetOldCurX();
+ nCurY = (nMovY != 0) ? nOldY+nMovY : aViewData.GetOldCurY();
+ }
+
+ if (nMovX < 0 && nOldX == 0)
+ { // trying to go left from 1st column
+ if (nMovY == 0) // done, because no vertical move is requested
+ return;
+ }
+ if (nMovY < 0 && nOldY == 0)
+ { // trying to go up from 1st row
+ if (nMovX == 0) // done, because no horizontal move is requested
+ return;
+ }
+
+ aViewData.ResetOldCursor();
+
+ if (nMovX != 0 && rDoc.ValidColRow(nCurX,nCurY))
+ SkipCursorHorizontal(nCurX, nCurY, nOldX, nMovX);
+
+ if (nMovY != 0 && rDoc.ValidColRow(nCurX,nCurY))
+ SkipCursorVertical(nCurX, nCurY, nOldY, nMovY);
+
+ MoveCursorAbs( nCurX, nCurY, eMode, bShift, false, true, bKeepSel );
+}
+
+void ScTabView::MoveCursorPage( SCCOL nMovX, SCROW nMovY, ScFollowMode eMode, bool bShift, bool bKeepSel )
+{
+ SCCOL nPageX;
+ SCROW nPageY;
+ GetPageMoveEndPosition(nMovX, nMovY, nPageX, nPageY);
+ MoveCursorRel( nPageX, nPageY, eMode, bShift, bKeepSel );
+}
+
+void ScTabView::MoveCursorArea( SCCOL nMovX, SCROW nMovY, ScFollowMode eMode, bool bShift, bool bKeepSel, bool bInteractiveByUser )
+{
+ SCCOL nNewX;
+ SCROW nNewY;
+ GetAreaMoveEndPosition(nMovX, nMovY, eMode, nNewX, nNewY, eMode, bInteractiveByUser);
+ MoveCursorRel(nNewX, nNewY, eMode, bShift, bKeepSel);
+}
+
+void ScTabView::MoveCursorEnd( SCCOL nMovX, SCROW nMovY, ScFollowMode eMode, bool bShift, bool bKeepSel )
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCTAB nTab = aViewData.GetTabNo();
+
+ SCCOL nCurX;
+ SCROW nCurY;
+ aViewData.GetMoveCursor( nCurX,nCurY );
+ SCCOL nNewX = nCurX;
+ SCROW nNewY = nCurY;
+
+ SCCOL nUsedX = 0;
+ SCROW nUsedY = 0;
+ if ( nMovX > 0 || nMovY > 0 )
+ rDoc.GetPrintArea( nTab, nUsedX, nUsedY ); // get end
+
+ if (nMovX<0)
+ nNewX=0;
+ else if (nMovX>0)
+ nNewX=nUsedX; // last used range
+
+ if (nMovY<0)
+ nNewY=0;
+ else if (nMovY>0)
+ nNewY=nUsedY;
+
+ aViewData.ResetOldCursor();
+ MoveCursorRel( nNewX-nCurX, nNewY-nCurY, eMode, bShift, bKeepSel );
+}
+
+void ScTabView::MoveCursorScreen( SCCOL nMovX, SCROW nMovY, ScFollowMode eMode, bool bShift )
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCTAB nTab = aViewData.GetTabNo();
+
+ SCCOL nCurX;
+ SCROW nCurY;
+ aViewData.GetMoveCursor( nCurX,nCurY );
+ SCCOL nNewX = nCurX;
+ SCROW nNewY = nCurY;
+
+ ScSplitPos eWhich = aViewData.GetActivePart();
+ SCCOL nPosX = aViewData.GetPosX( WhichH(eWhich) );
+ SCROW nPosY = aViewData.GetPosY( WhichV(eWhich) );
+
+ SCCOL nAddX = aViewData.VisibleCellsX( WhichH(eWhich) );
+ if (nAddX != 0)
+ --nAddX;
+ SCROW nAddY = aViewData.VisibleCellsY( WhichV(eWhich) );
+ if (nAddY != 0)
+ --nAddY;
+
+ if (nMovX<0)
+ nNewX=nPosX;
+ else if (nMovX>0)
+ nNewX=nPosX+nAddX;
+
+ if (nMovY<0)
+ nNewY=nPosY;
+ else if (nMovY>0)
+ nNewY=nPosY+nAddY;
+
+ aViewData.SetOldCursor( nNewX,nNewY );
+ rDoc.SkipOverlapped(nNewX, nNewY, nTab);
+ MoveCursorAbs( nNewX, nNewY, eMode, bShift, false, true );
+}
+
+void ScTabView::MoveCursorEnter( bool bShift ) // bShift -> up/down
+{
+ const ScInputOptions& rOpt = SC_MOD()->GetInputOptions();
+ if (!rOpt.GetMoveSelection())
+ {
+ aViewData.UpdateInputHandler(true);
+ return;
+ }
+
+ SCCOL nMoveX = 0;
+ SCROW nMoveY = 0;
+ switch (static_cast<ScDirection>(rOpt.GetMoveDir()))
+ {
+ case DIR_BOTTOM:
+ nMoveY = bShift ? -1 : 1;
+ break;
+ case DIR_RIGHT:
+ nMoveX = bShift ? -1 : 1;
+ break;
+ case DIR_TOP:
+ nMoveY = bShift ? 1 : -1;
+ break;
+ case DIR_LEFT:
+ nMoveX = bShift ? 1 : -1;
+ break;
+ }
+
+ SCCOL nCurX;
+ SCROW nCurY;
+ aViewData.GetMoveCursor( nCurX,nCurY );
+ SCCOL nNewX = nCurX;
+ SCROW nNewY = nCurY;
+ SCTAB nTab = aViewData.GetTabNo();
+
+ ScMarkData& rMark = aViewData.GetMarkData();
+ ScDocument& rDoc = aViewData.GetDocument();
+
+ if (rMark.IsMarked() || rMark.IsMultiMarked())
+ {
+ rDoc.GetNextPos( nNewX, nNewY, nTab, nMoveX, nMoveY, true, false, rMark );
+
+ MoveCursorRel( nNewX - nCurX, nNewY - nCurY, SC_FOLLOW_LINE, false, true );
+
+ // update input line even if cursor was not moved
+ if ( nNewX == nCurX && nNewY == nCurY )
+ aViewData.UpdateInputHandler(true);
+ }
+ else
+ {
+ // After Tab and Enter back to the starting column again.
+ const SCCOL nTabStartCol = ((nMoveY != 0 && !nMoveX) ? aViewData.GetTabStartCol() : SC_TABSTART_NONE);
+ rDoc.GetNextPos( nNewX, nNewY, nTab, nMoveX, nMoveY, false, true, rMark, nTabStartCol );
+
+ MoveCursorRel( nNewX - nCurX, nNewY - nCurY, SC_FOLLOW_LINE, false);
+ }
+}
+
+bool ScTabView::MoveCursorKeyInput( const KeyEvent& rKeyEvent )
+{
+ const vcl::KeyCode& rKCode = rKeyEvent.GetKeyCode();
+
+ enum { MOD_NONE, MOD_CTRL, MOD_ALT, MOD_BOTH } eModifier =
+ rKCode.IsMod1() ?
+ (rKCode.IsMod2() ? MOD_BOTH : MOD_CTRL) :
+ (rKCode.IsMod2() ? MOD_ALT : MOD_NONE);
+
+ bool bSel = rKCode.IsShift();
+ sal_uInt16 nCode = rKCode.GetCode();
+
+ // CURSOR keys
+ SCCOL nDX = 0;
+ SCROW nDY = 0;
+ switch( nCode )
+ {
+ case KEY_LEFT: nDX = -1; break;
+ case KEY_RIGHT: nDX = 1; break;
+ case KEY_UP: nDY = -1; break;
+ case KEY_DOWN: nDY = 1; break;
+ }
+ if( nDX != 0 || nDY != 0 )
+ {
+ switch( eModifier )
+ {
+ case MOD_NONE: MoveCursorRel( nDX, nDY, SC_FOLLOW_LINE, bSel ); break;
+ case MOD_CTRL: MoveCursorArea( nDX, nDY, SC_FOLLOW_JUMP, bSel ); break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ // always true to suppress changes of col/row size (ALT+CURSOR)
+ return true;
+ }
+
+ // PAGEUP/PAGEDOWN
+ if( (nCode == KEY_PAGEUP) || (nCode == KEY_PAGEDOWN) )
+ {
+ nDX = (nCode == KEY_PAGEUP) ? -1 : 1;
+ switch( eModifier )
+ {
+ case MOD_NONE: MoveCursorPage( 0, static_cast<SCCOLROW>(nDX), SC_FOLLOW_FIX, bSel ); break;
+ case MOD_ALT: MoveCursorPage( nDX, 0, SC_FOLLOW_FIX, bSel ); break;
+ case MOD_CTRL: SelectNextTab( nDX, false ); break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ return true;
+ }
+
+ // HOME/END
+ if( (nCode == KEY_HOME) || (nCode == KEY_END) )
+ {
+ nDX = (nCode == KEY_HOME) ? -1 : 1;
+ ScFollowMode eMode = (nCode == KEY_HOME) ? SC_FOLLOW_LINE : SC_FOLLOW_JUMP_END;
+ switch( eModifier )
+ {
+ case MOD_NONE: MoveCursorEnd( nDX, 0, eMode, bSel ); break;
+ case MOD_CTRL: MoveCursorEnd( nDX, static_cast<SCCOLROW>(nDX), eMode, bSel ); break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ return true;
+ }
+
+ return false;
+}
+
+ // next/previous unprotected cell
+void ScTabView::FindNextUnprot( bool bShift, bool bInSelection )
+{
+ short nMove = bShift ? -1 : 1;
+
+ ScMarkData& rMark = aViewData.GetMarkData();
+ bool bMarked = bInSelection && (rMark.IsMarked() || rMark.IsMultiMarked());
+
+ SCCOL nCurX;
+ SCROW nCurY;
+ aViewData.GetMoveCursor( nCurX,nCurY );
+ SCCOL nNewX = nCurX;
+ SCROW nNewY = nCurY;
+ SCTAB nTab = aViewData.GetTabNo();
+
+ ScDocument& rDoc = aViewData.GetDocument();
+ rDoc.GetNextPos( nNewX,nNewY, nTab, nMove,0, bMarked, true, rMark );
+
+ SCCOL nTabCol = aViewData.GetTabStartCol();
+ if ( nTabCol == SC_TABSTART_NONE )
+ nTabCol = nCurX; // back to this column after Enter
+
+ MoveCursorRel( nNewX-nCurX, nNewY-nCurY, SC_FOLLOW_LINE, false, true );
+
+ // TabCol is reset in MoveCursorRel...
+ aViewData.SetTabStartCol( nTabCol );
+}
+
+void ScTabView::MarkColumns()
+{
+ SCCOL nStartCol;
+ SCCOL nEndCol;
+
+ ScMarkData& rMark = aViewData.GetMarkData();
+ if (rMark.IsMarked())
+ {
+ const ScRange& aMarkRange = rMark.GetMarkArea();
+ nStartCol = aMarkRange.aStart.Col();
+ nEndCol = aMarkRange.aEnd.Col();
+ }
+ else
+ {
+ SCROW nDummy;
+ aViewData.GetMoveCursor( nStartCol, nDummy );
+ nEndCol=nStartCol;
+ }
+
+ SCTAB nTab = aViewData.GetTabNo();
+ ScDocument& rDoc = aViewData.GetDocument();
+ DoneBlockMode();
+ InitBlockMode( nStartCol,0, nTab );
+ MarkCursor( nEndCol, rDoc.MaxRow(), nTab );
+ SelectionChanged();
+}
+
+void ScTabView::MarkRows()
+{
+ SCROW nStartRow;
+ SCROW nEndRow;
+
+ ScMarkData& rMark = aViewData.GetMarkData();
+ if (rMark.IsMarked())
+ {
+ const ScRange& aMarkRange = rMark.GetMarkArea();
+ nStartRow = aMarkRange.aStart.Row();
+ nEndRow = aMarkRange.aEnd.Row();
+ }
+ else
+ {
+ SCCOL nDummy;
+ aViewData.GetMoveCursor( nDummy, nStartRow );
+ nEndRow=nStartRow;
+ }
+
+ SCTAB nTab = aViewData.GetTabNo();
+ ScDocument& rDoc = aViewData.GetDocument();
+ DoneBlockMode();
+ InitBlockMode( 0,nStartRow, nTab );
+ MarkCursor( rDoc.MaxCol(), nEndRow, nTab );
+ SelectionChanged();
+}
+
+
+void ScTabView::MarkColumns(SCCOL nCol, sal_Int16 nModifier)
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCCOL nStartCol = nCol;
+ SCTAB nTab = aViewData.GetTabNo();
+
+ if ((nModifier & KEY_SHIFT) == KEY_SHIFT)
+ bMoveIsShift = true;
+
+ if ( SC_MOD()->IsFormulaMode() )
+ {
+ DoneRefMode( nModifier != 0 );
+ InitRefMode( nCol, 0, nTab, SC_REFTYPE_REF );
+ UpdateRef( nCol, rDoc.MaxRow(), nTab );
+ bMoveIsShift = false;
+ }
+ else
+ {
+ DoneBlockMode( nModifier != 0 );
+ InitBlockMode( nStartCol, 0, nTab, true, true);
+ MarkCursor( nCol, rDoc.MaxRow(), nTab );
+ bMoveIsShift = false;
+ SetCursor( nCol, 0 );
+ SelectionChanged();
+ }
+}
+
+void ScTabView::MarkRows(SCROW nRow, sal_Int16 nModifier)
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCROW nStartRow = nRow;
+ SCTAB nTab = aViewData.GetTabNo();
+
+ if ((nModifier & KEY_SHIFT) == KEY_SHIFT)
+ bMoveIsShift = true;
+
+ if ( SC_MOD()->IsFormulaMode() )
+ {
+ DoneRefMode( nModifier != 0 );
+ InitRefMode( 0, nRow, nTab, SC_REFTYPE_REF );
+ UpdateRef( rDoc.MaxCol(), nRow, nTab );
+ bMoveIsShift = false;
+ }
+ else
+ {
+ DoneBlockMode( nModifier != 0 );
+ InitBlockMode( 0, nStartRow, nTab, true, false, true );
+ MarkCursor( rDoc.MaxCol(), nRow, nTab );
+ bMoveIsShift = false;
+ SetCursor( 0, nRow );
+ SelectionChanged();
+ }
+}
+
+void ScTabView::HighlightOverlay()
+{
+ if (!officecfg::Office::Calc::Content::Display::ColumnRowHighlighting::get())
+ {
+ aViewData.GetHighlightData().ResetMark();
+ UpdateHighlightOverlay();
+ return;
+ }
+
+ ScAddress aCell = GetViewData().GetCurPos();
+ SCROW nRow = aCell.Row();
+ SCCOL nCol = aCell.Col();
+
+ bool nModifier = false; // modifier key pressed?
+ DoneBlockModeHighlight( nModifier );
+ InitBlockModeHighlight( nCol, 0, aCell.Tab(), true, false);
+ nModifier = true;
+ DoneBlockModeHighlight( nModifier );
+ InitBlockModeHighlight( 0, nRow, aCell.Tab(), false, true );
+}
+
+void ScTabView::MarkDataArea( bool bIncludeCursor )
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCTAB nTab = aViewData.GetTabNo();
+ SCCOL nStartCol = aViewData.GetCurX();
+ SCROW nStartRow = aViewData.GetCurY();
+ SCCOL nEndCol = nStartCol;
+ SCROW nEndRow = nStartRow;
+
+ rDoc.GetDataArea( nTab, nStartCol, nStartRow, nEndCol, nEndRow, bIncludeCursor, false );
+
+ HideAllCursors();
+ DoneBlockMode();
+ InitBlockMode( nStartCol, nStartRow, nTab );
+ MarkCursor( nEndCol, nEndRow, nTab );
+ ShowAllCursors();
+
+ SelectionChanged();
+}
+
+void ScTabView::MarkMatrixFormula()
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+ ScAddress aCursor( aViewData.GetCurX(), aViewData.GetCurY(), aViewData.GetTabNo() );
+ ScRange aMatrix;
+ if ( rDoc.GetMatrixFormulaRange( aCursor, aMatrix ) )
+ {
+ MarkRange( aMatrix, false ); // cursor is already within the range
+ }
+}
+
+void ScTabView::MarkRange( const ScRange& rRange, bool bSetCursor, bool bContinue )
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCTAB nTab = rRange.aStart.Tab();
+ SetTabNo( nTab );
+
+ HideAllCursors();
+ DoneBlockMode( bContinue ); // bContinue==true -> clear old mark
+ if (bSetCursor) // if Cursor is set, also always align
+ {
+ SCCOL nAlignX = rRange.aStart.Col();
+ SCROW nAlignY = rRange.aStart.Row();
+ bool bCol = ( rRange.aStart.Col() == 0 && rRange.aEnd.Col() == rDoc.MaxCol() ) && !aViewData.GetDocument().IsInVBAMode();
+ bool bRow = ( rRange.aStart.Row() == 0 && rRange.aEnd.Row() == rDoc.MaxRow() );
+ if ( bCol )
+ nAlignX = aViewData.GetPosX(WhichH(aViewData.GetActivePart()));
+ if ( bRow )
+ nAlignY = aViewData.GetPosY(WhichV(aViewData.GetActivePart()));
+ AlignToCursor( nAlignX, nAlignY, SC_FOLLOW_JUMP );
+ }
+ InitBlockMode( rRange.aStart.Col(), rRange.aStart.Row(), nTab );
+ MarkCursor( rRange.aEnd.Col(), rRange.aEnd.Row(), nTab );
+ if (bSetCursor)
+ {
+ SCCOL nPosX = rRange.aStart.Col();
+ SCROW nPosY = rRange.aStart.Row();
+ rDoc.SkipOverlapped(nPosX, nPosY, nTab);
+
+ aViewData.ResetOldCursor();
+ SetCursor( nPosX, nPosY );
+ }
+ ShowAllCursors();
+
+ SelectionChanged();
+}
+
+void ScTabView::Unmark()
+{
+ ScMarkData& rMark = aViewData.GetMarkData();
+ if ( rMark.IsMarked() || rMark.IsMultiMarked() )
+ {
+ SCCOL nCurX;
+ SCROW nCurY;
+ aViewData.GetMoveCursor( nCurX,nCurY );
+ MoveCursorAbs( nCurX, nCurY, SC_FOLLOW_NONE, false, false );
+
+ SelectionChanged();
+ }
+}
+
+void ScTabView::SetMarkData( const ScMarkData& rNew )
+{
+ DoneBlockMode();
+ InitOwnBlockMode( rNew.GetMarkArea());
+ aViewData.GetMarkData() = rNew;
+
+ MarkDataChanged();
+}
+
+void ScTabView::MarkDataChanged()
+{
+ // has to be called after making direct changes to mark data (not via MarkCursor etc)
+
+ UpdateSelectionOverlay();
+}
+
+void ScTabView::SelectNextTab( short nDir, bool bExtendSelection )
+{
+ if (!nDir)
+ return;
+ OSL_ENSURE( nDir==-1 || nDir==1, "SelectNextTab: invalid value");
+
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCTAB nTab = aViewData.GetTabNo();
+ SCTAB nNextTab = nTab;
+ if (nDir < 0)
+ {
+ do
+ {
+ --nNextTab;
+ if (nNextTab < 0)
+ nNextTab = rDoc.GetTableCount();
+ if (rDoc.IsVisible(nNextTab))
+ break;
+ } while (nNextTab != nTab);
+ }
+ if (nDir > 0)
+ {
+ SCTAB nCount = rDoc.GetTableCount();
+ do
+ {
+ ++nNextTab;
+ if (nNextTab >= nCount)
+ nNextTab = 0;
+ if (rDoc.IsVisible(nNextTab))
+ break;
+ } while (nNextTab != nTab);
+ }
+ if (nNextTab == nTab)
+ return;
+
+ SetTabNo(nNextTab, false, bExtendSelection);
+ PaintExtras();
+}
+
+void ScTabView::SelectTabPage( const sal_uInt16 nTab )
+{
+ pTabControl->SwitchToPageId( nTab );
+}
+
+// SetTabNo - set the displayed sheet
+
+void ScTabView::SetTabNo( SCTAB nTab, bool bNew, bool bExtendSelection, bool bSameTabButMoved )
+{
+ if ( !ValidTab(nTab) )
+ {
+ OSL_FAIL("SetTabNo: invalid sheet");
+ return;
+ }
+
+ if (!bNew && nTab == aViewData.GetTabNo())
+ return;
+
+ // FormShell would like to be informed before the switch
+ FmFormShell* pFormSh = aViewData.GetViewShell()->GetFormShell();
+ if (pFormSh)
+ {
+ bool bAllowed = pFormSh->PrepareClose();
+ if (!bAllowed)
+ {
+ //! error message? or does FormShell do it?
+ //! return error flag and cancel actions
+
+ return; // FormShell says that it can not be switched
+ }
+ }
+
+ // not InputEnterHandler due to reference input
+
+ ScDocument& rDoc = aViewData.GetDocument();
+
+ rDoc.MakeTable( nTab );
+
+ // Update pending row heights before switching the sheet, so Reschedule from the progress bar
+ // doesn't paint the new sheet with old heights
+ aViewData.GetDocShell()->UpdatePendingRowHeights( nTab );
+
+ SCTAB nTabCount = rDoc.GetTableCount();
+ SCTAB nOldPos = nTab;
+ while (!rDoc.IsVisible(nTab)) // search for next visible
+ {
+ bool bUp = (nTab>=nOldPos);
+ if (bUp)
+ {
+ ++nTab;
+ if (nTab>=nTabCount)
+ {
+ nTab = nOldPos;
+ bUp = false;
+ }
+ }
+
+ if (!bUp)
+ {
+ if (nTab != 0)
+ --nTab;
+ else
+ {
+ OSL_FAIL("no visible sheets");
+ rDoc.SetVisible( 0, true );
+ }
+ }
+ }
+
+ // #i71490# Deselect drawing objects before changing the sheet number in view data,
+ // so the handling of notes still has the sheet selected on which the notes are.
+ DrawDeselectAll();
+
+ ScModule* pScMod = SC_MOD();
+ bool bRefMode = pScMod->IsFormulaMode();
+ if ( !bRefMode ) // query, so that RefMode works when switching sheet
+ {
+ DoneBlockMode();
+ pSelEngine->Reset(); // reset all flags, including locked modifiers
+ aViewData.SetRefTabNo( nTab );
+ }
+
+ ScSplitPos eOldActive = aViewData.GetActivePart(); // before switching
+ bool bFocus = pGridWin[eOldActive] && pGridWin[eOldActive]->HasFocus();
+
+ aViewData.SetTabNo( nTab );
+ if (mpSpellCheckCxt)
+ mpSpellCheckCxt->setTabNo( nTab );
+ // UpdateShow before SetCursor, so that UpdateAutoFillMark finds the correct
+ // window (is called from SetCursor)
+ UpdateShow();
+ aViewData.GetView()->TestHintWindow();
+
+ SfxBindings& rBindings = aViewData.GetBindings();
+ ScMarkData& rMark = aViewData.GetMarkData();
+
+ bool bAllSelected = true;
+ for (SCTAB nSelTab = 0; nSelTab < nTabCount; ++nSelTab)
+ {
+ if (!rDoc.IsVisible(nSelTab) || rMark.GetTableSelect(nSelTab))
+ {
+ if (nTab == nSelTab)
+ // This tab is already in selection. Keep the current
+ // selection.
+ bExtendSelection = true;
+ }
+ else
+ {
+ bAllSelected = false;
+ if (bExtendSelection)
+ // We got what we need. No need to stay in the loop.
+ break;
+ }
+ }
+ if (bAllSelected && !bNew)
+ // #i6327# if all tables are selected, a selection event (#i6330#) will deselect all
+ // (not if called with bNew to update settings)
+ bExtendSelection = false;
+
+ if (bExtendSelection)
+ rMark.SelectTable( nTab, true );
+ else
+ {
+ rMark.SelectOneTable( nTab );
+ rBindings.Invalidate( FID_FILL_TAB );
+ rBindings.Invalidate( FID_TAB_DESELECTALL );
+ }
+
+ bool bUnoRefDialog = pScMod->IsRefDialogOpen() && pScMod->GetCurRefDlgId() == WID_SIMPLE_REF;
+
+ // recalc zoom-dependent values (before TabChanged, before UpdateEditViewPos)
+ RefreshZoom();
+ UpdateVarZoom();
+
+ if ( bRefMode ) // hide EditView if necessary (after aViewData.SetTabNo !)
+ {
+ for (VclPtr<ScGridWindow> & pWin : pGridWin)
+ {
+ if (pWin && pWin->IsVisible())
+ pWin->UpdateEditViewPos();
+ }
+ }
+
+ TabChanged(bSameTabButMoved); // DrawView
+ collectUIInformation({{"TABLE", OUString::number(nTab)}});
+ UpdateVisibleRange();
+
+ aViewData.GetViewShell()->WindowChanged(); // if the active window has changed
+ aViewData.ResetOldCursor();
+ SetCursor( aViewData.GetCurX(), aViewData.GetCurY(), true );
+
+ if ( !bUnoRefDialog )
+ aViewData.GetViewShell()->DisconnectAllClients(); // important for floating frames
+ else
+ {
+ // hide / show inplace client
+ ScClient* pClient = static_cast<ScClient*>(aViewData.GetViewShell()->GetIPClient());
+ if ( pClient && pClient->IsObjectInPlaceActive() )
+ {
+ tools::Rectangle aObjArea = pClient->GetObjArea();
+ if ( nTab == aViewData.GetRefTabNo() )
+ {
+ // move to its original position
+
+ SdrOle2Obj* pDrawObj = pClient->GetDrawObj();
+ if ( pDrawObj )
+ {
+ tools::Rectangle aRect = pDrawObj->GetLogicRect();
+ MapMode aMapMode( MapUnit::Map100thMM );
+ Size aOleSize = pDrawObj->GetOrigObjSize( &aMapMode );
+ aRect.SetSize( aOleSize );
+ aObjArea = aRect;
+ }
+ }
+ else
+ {
+ // move to an invisible position
+
+ aObjArea.SetPos( Point( 0, -2*aObjArea.GetHeight() ) );
+ }
+ pClient->SetObjArea( aObjArea );
+ }
+ }
+
+ if ( bFocus && aViewData.GetActivePart() != eOldActive && !bRefMode )
+ ActiveGrabFocus(); // grab focus to the pane that's active now
+
+ // freeze
+
+ bool bResize = false;
+ if ( aViewData.GetHSplitMode() == SC_SPLIT_FIX )
+ if (aViewData.UpdateFixX())
+ bResize = true;
+ if ( aViewData.GetVSplitMode() == SC_SPLIT_FIX )
+ if (aViewData.UpdateFixY())
+ bResize = true;
+ if (bResize)
+ RepeatResize();
+ InvalidateSplit();
+
+ if ( aViewData.IsPagebreakMode() )
+ UpdatePageBreakData(); //! asynchronously ??
+
+ // Form Layer must know the visible area of the new sheet
+ // that is why MapMode must already be correct here
+ for (VclPtr<ScGridWindow> & pWin : pGridWin)
+ {
+ if (pWin)
+ pWin->SetMapMode(pWin->GetDrawMapMode());
+ }
+ SetNewVisArea();
+
+ PaintGrid();
+ PaintTop();
+ PaintLeft();
+ PaintExtras();
+
+ DoResize( aBorderPos, aFrameSize );
+ rBindings.Invalidate( SID_DELETE_PRINTAREA ); // Menu
+ rBindings.Invalidate( FID_DEL_MANUALBREAKS );
+ rBindings.Invalidate( FID_RESET_PRINTZOOM );
+ rBindings.Invalidate( SID_STATUS_DOCPOS ); // Status bar
+ rBindings.Invalidate( SID_ROWCOL_SELCOUNT ); // Status bar
+ rBindings.Invalidate( SID_STATUS_PAGESTYLE ); // Status bar
+ rBindings.Invalidate( SID_CURRENTTAB ); // Navigator
+ rBindings.Invalidate( SID_STYLE_FAMILY2 ); // Designer
+ rBindings.Invalidate( SID_STYLE_FAMILY4 ); // Designer
+ rBindings.Invalidate( SID_TABLES_COUNT );
+
+ if (pScMod->IsRefDialogOpen())
+ {
+ sal_uInt16 nCurRefDlgId=pScMod->GetCurRefDlgId();
+ SfxViewFrame& rViewFrm = aViewData.GetViewShell()->GetViewFrame();
+ SfxChildWindow* pChildWnd = rViewFrm.GetChildWindow( nCurRefDlgId );
+ if (pChildWnd)
+ {
+ if (pChildWnd->GetController())
+ {
+ IAnyRefDialog* pRefDlg = dynamic_cast<IAnyRefDialog*>(pChildWnd->GetController().get());
+ if (pRefDlg)
+ pRefDlg->ViewShellChanged();
+ }
+ }
+ }
+
+ OnLibreOfficeKitTabChanged();
+}
+
+void ScTabView::AddWindowToForeignEditView(SfxViewShell* pViewShell, ScSplitPos eWhich)
+{
+ aExtraEditViewManager.Add(pViewShell, eWhich);
+}
+
+void ScTabView::RemoveWindowFromForeignEditView(SfxViewShell* pViewShell, ScSplitPos eWhich)
+{
+ aExtraEditViewManager.Remove(pViewShell, eWhich);
+}
+
+void ScTabView::OnLibreOfficeKitTabChanged()
+{
+ if (!comphelper::LibreOfficeKit::isActive())
+ return;
+
+ ScTabViewShell* pThisViewShell = aViewData.GetViewShell();
+ SCTAB nThisTabNo = pThisViewShell->GetViewData().GetTabNo();
+ auto lTabSwitch = [pThisViewShell, nThisTabNo] (ScTabViewShell* pOtherViewShell)
+ {
+ ScViewData& rOtherViewData = pOtherViewShell->GetViewData();
+ SCTAB nOtherTabNo = rOtherViewData.GetTabNo();
+ if (nThisTabNo == nOtherTabNo)
+ {
+ for (int i = 0; i < 4; ++i)
+ {
+ if (rOtherViewData.HasEditView(ScSplitPos(i)))
+ {
+ pThisViewShell->AddWindowToForeignEditView(pOtherViewShell, ScSplitPos(i));
+ }
+ }
+ }
+ else
+ {
+ for (int i = 0; i < 4; ++i)
+ {
+ if (rOtherViewData.HasEditView(ScSplitPos(i)))
+ {
+ pThisViewShell->RemoveWindowFromForeignEditView(pOtherViewShell, ScSplitPos(i));
+ }
+ }
+ }
+ };
+
+ SfxLokHelper::forEachOtherView(pThisViewShell, lTabSwitch);
+
+ pThisViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_INVALIDATE_HEADER, "all"_ostr);
+
+ if (pThisViewShell->GetInputHandler())
+ pThisViewShell->GetInputHandler()->UpdateLokReferenceMarks();
+}
+
+// paint functions - only for this View
+
+void ScTabView::MakeEditView( ScEditEngineDefaulter* pEngine, SCCOL nCol, SCROW nRow )
+{
+ DrawDeselectAll();
+
+ if (pDrawView)
+ DrawEnableAnim( false );
+
+ EditView* pSpellingView = aViewData.GetSpellingView();
+
+ for (sal_uInt16 i = 0; i < 4; i++)
+ {
+ if (pGridWin[i] && pGridWin[i]->IsVisible() && !aViewData.HasEditView(ScSplitPos(i)))
+ {
+ ScHSplitPos eHWhich = WhichH( static_cast<ScSplitPos>(i) );
+ ScVSplitPos eVWhich = WhichV( static_cast<ScSplitPos>(i) );
+ SCCOL nScrX = aViewData.GetPosX( eHWhich );
+ SCROW nScrY = aViewData.GetPosY( eVWhich );
+
+ bool bPosVisible =
+ ( nCol >= nScrX && nCol <= nScrX + aViewData.VisibleCellsX(eHWhich) - 1 &&
+ nRow >= nScrY && nRow <= nScrY + aViewData.VisibleCellsY(eVWhich) - 1 );
+
+ // for the active part, create edit view even if outside the visible area,
+ // so input isn't lost (and the edit view may be scrolled into the visible area)
+
+ // #i26433# during spelling, the spelling view must be active
+ if ( bPosVisible || aViewData.GetActivePart() == static_cast<ScSplitPos>(i) ||
+ ( pSpellingView && aViewData.GetEditView(static_cast<ScSplitPos>(i)) == pSpellingView ) )
+ {
+ pGridWin[i]->HideCursor();
+
+ pGridWin[i]->DeleteCursorOverlay();
+ pGridWin[i]->DeleteAutoFillOverlay();
+ pGridWin[i]->DeleteCopySourceOverlay();
+
+ // flush OverlayManager before changing MapMode to text edit
+ pGridWin[i]->flushOverlayManager();
+
+ // MapMode must be set after HideCursor
+ pGridWin[i]->SetMapMode(aViewData.GetLogicMode());
+
+ aViewData.SetEditEngine( static_cast<ScSplitPos>(i), pEngine, pGridWin[i], nCol, nRow );
+
+ if ( !bPosVisible )
+ {
+ // move the edit view area to the real (possibly negative) position,
+ // or hide if completely above or left of the window
+ pGridWin[i]->UpdateEditViewPos();
+ }
+ }
+ }
+ }
+
+ if (aViewData.GetViewShell()->HasAccessibilityObjects())
+ aViewData.GetViewShell()->BroadcastAccessibility(SfxHint(SfxHintId::ScAccEnterEditMode));
+}
+
+void ScTabView::UpdateEditView()
+{
+ if (aViewData.GetTabNo() != aViewData.GetRefTabNo() && SC_MOD()->IsFormulaMode())
+ return;
+
+ ScSplitPos eActive = aViewData.GetActivePart();
+ for (sal_uInt16 i = 0; i < 4; i++)
+ {
+ ScSplitPos eCurrent = ScSplitPos(i);
+ if (aViewData.HasEditView(eCurrent))
+ {
+ EditView* pEditView = aViewData.GetEditView(eCurrent);
+
+ tools::Long nRefTabNo = GetViewData().GetRefTabNo();
+ tools::Long nX = GetViewData().GetCurXForTab(nRefTabNo);
+ tools::Long nY = GetViewData().GetCurYForTab(nRefTabNo);
+
+ aViewData.SetEditEngine(eCurrent,
+ static_cast<ScEditEngineDefaulter*>(pEditView->GetEditEngine()),
+ pGridWin[i], nX, nY );
+ if (eCurrent == eActive)
+ pEditView->ShowCursor( false );
+ }
+ }
+}
+
+void ScTabView::KillEditView( bool bNoPaint )
+{
+ SCCOL nCol1 = aViewData.GetEditStartCol();
+ SCROW nRow1 = aViewData.GetEditStartRow();
+ SCCOL nCol2 = aViewData.GetEditEndCol();
+ SCROW nRow2 = aViewData.GetEditEndRow();
+ SCTAB nTab = aViewData.GetTabNo();
+ bool bPaint[4];
+ bool bNotifyAcc = false;
+ tools::Rectangle aRectangle[4];
+
+ bool bExtended = nRow1 != nRow2; // column is painted to the end anyway
+
+ bool bAtCursor = nCol1 <= aViewData.GetCurX() &&
+ nCol2 >= aViewData.GetCurX() &&
+ nRow1 == aViewData.GetCurY();
+ for (sal_uInt16 i = 0; i < 4; i++)
+ {
+ bPaint[i] = aViewData.HasEditView( static_cast<ScSplitPos>(i) );
+ if (bPaint[i])
+ {
+ bNotifyAcc = true;
+
+ EditView* pView = aViewData.GetEditView( static_cast<ScSplitPos>(i) );
+ aRectangle[i] = pView->GetInvalidateRect();
+ }
+ }
+
+ // notify accessibility before all things happen
+ if (bNotifyAcc && aViewData.GetViewShell()->HasAccessibilityObjects())
+ aViewData.GetViewShell()->BroadcastAccessibility(SfxHint(SfxHintId::ScAccLeaveEditMode));
+
+ aViewData.ResetEditView();
+ for (sal_uInt16 i = 0; i < 4; i++)
+ {
+ if (pGridWin[i] && bPaint[i] && pGridWin[i]->IsVisible())
+ {
+ pGridWin[i]->ShowCursor();
+
+ pGridWin[i]->SetMapMode(pGridWin[i]->GetDrawMapMode());
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ const tools::Rectangle& rInvRect = aRectangle[i];
+ pGridWin[i]->LogicInvalidatePart(&rInvRect, nTab);
+
+ // invalidate other views
+ auto lInvalidateWindows =
+ [nTab, &rInvRect] (ScTabView* pTabView)
+ {
+ for (VclPtr<ScGridWindow> const & pWin: pTabView->pGridWin)
+ {
+ if (pWin)
+ pWin->LogicInvalidatePart(&rInvRect, nTab);
+ }
+ };
+
+ SfxLokHelper::forEachOtherView(GetViewData().GetViewShell(), lInvalidateWindows);
+ }
+ // #i73567# the cell still has to be repainted
+ else if (bExtended || ( bAtCursor && !bNoPaint ))
+ {
+ pGridWin[i]->Draw( nCol1, nRow1, nCol2, nRow2, ScUpdateMode::All );
+ pGridWin[i]->UpdateSelectionOverlay();
+ }
+ }
+ }
+
+ if (pDrawView)
+ DrawEnableAnim( true );
+
+ // GrabFocus always when this View is active and
+ // when the input row has the focus
+
+ bool bGrabFocus = false;
+ if (aViewData.IsActive())
+ {
+ ScInputHandler* pInputHdl = SC_MOD()->GetInputHdl();
+ if ( pInputHdl )
+ {
+ ScInputWindow* pInputWin = pInputHdl->GetInputWindow();
+ if (pInputWin && pInputWin->IsInputActive())
+ bGrabFocus = true;
+ }
+ }
+
+ if (bGrabFocus)
+ {
+// should be done like this, so that Sfx notice it, but it does not work:
+//! aViewData.GetViewShell()->GetViewFrame().GetWindow().GrabFocus();
+// therefore first like this:
+ GetActiveWin()->GrabFocus();
+ }
+
+ // cursor query only after GrabFocus
+
+ for (sal_uInt16 i = 0; i < 4; i++)
+ {
+ if (pGridWin[i] && pGridWin[i]->IsVisible())
+ {
+ vcl::Cursor* pCur = pGridWin[i]->GetCursor();
+ if (pCur && pCur->IsVisible())
+ pCur->Hide();
+
+ if (bPaint[i])
+ {
+ pGridWin[i]->UpdateCursorOverlay();
+ pGridWin[i]->UpdateAutoFillOverlay();
+ }
+ }
+ }
+}
+
+void ScTabView::UpdateFormulas(SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow)
+{
+ if ( aViewData.GetDocument().IsAutoCalcShellDisabled() )
+ return;
+
+ for (sal_uInt16 i = 0; i < 4; i++)
+ {
+ if (pGridWin[i] && pGridWin[i]->IsVisible())
+ pGridWin[i]->UpdateFormulas(nStartCol, nStartRow, nEndCol, nEndRow);
+ }
+
+ if ( aViewData.IsPagebreakMode() )
+ UpdatePageBreakData(); //! asynchronous
+
+ UpdateHeaderWidth();
+
+ // if in edit mode, adjust edit view area because widths/heights may have changed
+ if ( aViewData.HasEditView( aViewData.GetActivePart() ) )
+ UpdateEditView();
+}
+
+// PaintArea - repaint block
+
+void ScTabView::PaintArea( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
+ ScUpdateMode eMode )
+{
+ SCCOL nCol1;
+ SCROW nRow1;
+ SCCOL nCol2;
+ SCROW nRow2;
+ bool bIsTiledRendering = comphelper::LibreOfficeKit::isActive();
+ ScDocument& rDoc = aViewData.GetDocument();
+
+ PutInOrder( nStartCol, nEndCol );
+ PutInOrder( nStartRow, nEndRow );
+
+ for (size_t i = 0; i < 4; ++i)
+ {
+ if (!pGridWin[i] || !pGridWin[i]->IsVisible())
+ continue;
+
+ ScHSplitPos eHWhich = WhichH( static_cast<ScSplitPos>(i) );
+ ScVSplitPos eVWhich = WhichV( static_cast<ScSplitPos>(i) );
+ bool bOut = false;
+
+ nCol1 = nStartCol;
+ nRow1 = nStartRow;
+ nCol2 = nEndCol;
+ nRow2 = nEndRow;
+
+ SCCOL nLastX = 0;
+ SCROW nLastY = 0;
+
+ if (bIsTiledRendering)
+ {
+ nLastX = aViewData.GetMaxTiledCol();
+ nLastY = aViewData.GetMaxTiledRow();
+ }
+ else
+ {
+
+ SCCOL nScrX = aViewData.GetPosX( eHWhich );
+ SCROW nScrY = aViewData.GetPosY( eVWhich );
+
+ if (nCol1 < nScrX)
+ nCol1 = nScrX;
+ if (nCol2 < nScrX)
+ {
+ if ( eMode == ScUpdateMode::All ) // for UPDATE_ALL, paint anyway
+ nCol2 = nScrX; // (because of extending strings to the right)
+ else
+ bOut = true; // completely outside the window
+ }
+ if (nRow1 < nScrY)
+ nRow1 = nScrY;
+ if (nRow2 < nScrY)
+ bOut = true;
+
+ nLastX = nScrX + aViewData.VisibleCellsX( eHWhich ) + 1;
+ nLastY = nScrY + aViewData.VisibleCellsY( eVWhich ) + 1;
+ }
+
+ if (nCol1 > nLastX)
+ bOut = true;
+ if (nCol2 > nLastX)
+ nCol2 = nLastX;
+ if (nRow1 > nLastY)
+ bOut = true;
+ if (nRow2 > nLastY)
+ nRow2 = nLastY;
+
+ if (bOut)
+ continue;
+
+ bool bLayoutRTL = aViewData.GetDocument().IsLayoutRTL( aViewData.GetTabNo() );
+ tools::Long nLayoutSign = (!bIsTiledRendering && bLayoutRTL) ? -1 : 1;
+
+ Point aStart = aViewData.GetScrPos( nCol1, nRow1, static_cast<ScSplitPos>(i) );
+ Point aEnd = aViewData.GetScrPos( nCol2+1, nRow2+1, static_cast<ScSplitPos>(i) );
+
+ if ( eMode == ScUpdateMode::All )
+ {
+ if (bIsTiledRendering)
+ {
+ // When a cell content is deleted we have no clue about
+ // the width of the embedded text.
+ // Anyway, clients will ask only for tiles that overlaps
+ // the visible area.
+ // Remember that wsd expects int and that aEnd.X() is
+ // in pixels and will be converted in twips, before performing
+ // the lok callback, so we need to avoid that an overflow occurs.
+ aEnd.setX( std::numeric_limits<int>::max() / 1000 );
+ }
+ else
+ {
+ aEnd.setX( bLayoutRTL ? 0 : pGridWin[i]->GetOutputSizePixel().Width() );
+ }
+ }
+ aEnd.AdjustX( -nLayoutSign );
+ aEnd.AdjustY( -1 );
+
+ // #i85232# include area below cells (could be done in GetScrPos?)
+ if ( eMode == ScUpdateMode::All && nRow2 >= rDoc.MaxRow() && !bIsTiledRendering )
+ aEnd.setY( pGridWin[i]->GetOutputSizePixel().Height() );
+
+ aStart.AdjustX( -nLayoutSign ); // include change marks
+ aStart.AdjustY( -1 );
+
+ bool bMarkClipped = SC_MOD()->GetColorConfig().GetColorValue(svtools::CALCTEXTOVERFLOW).bIsVisible;
+ if (bMarkClipped)
+ {
+ // ScColumn::IsEmptyData has to be optimized for this
+ // (switch to Search() )
+ //!if ( nCol1 > 0 && !aViewData.GetDocument()->IsBlockEmpty(
+ //! 0, nRow1, nCol1-1, nRow2.
+ //! aViewData.GetTabNo() ) )
+ tools::Long nMarkPixel = static_cast<tools::Long>( SC_CLIPMARK_SIZE * aViewData.GetPPTX() );
+ aStart.AdjustX( -(nMarkPixel * nLayoutSign) );
+ }
+
+ pGridWin[i]->Invalidate( pGridWin[i]->PixelToLogic( tools::Rectangle( aStart,aEnd ) ) );
+ }
+
+ // #i79909# Calling UpdateAllOverlays here isn't necessary and would lead to overlay calls from a timer,
+ // with a wrong MapMode if editing in a cell (reference input).
+ // #i80499# Overlays need updates in a lot of cases, e.g. changing row/column size,
+ // or showing/hiding outlines. TODO: selections in inactive windows are vanishing.
+ // #i84689# With relative conditional formats, PaintArea may be called often (for each changed cell),
+ // so UpdateAllOverlays was moved to ScTabViewShell::Notify and is called only if PaintPartFlags::Left/PaintPartFlags::Top
+ // is set (width or height changed).
+}
+
+void ScTabView::PaintRangeFinderEntry (const ScRangeFindData* pData, const SCTAB nTab)
+{
+ ScRange aRef = pData->aRef;
+ aRef.PutInOrder(); // PutInOrder for the queries below
+
+ if ( aRef.aStart == aRef.aEnd ) //! ignore sheet?
+ aViewData.GetDocument().ExtendMerge(aRef);
+
+ if (aRef.aStart.Tab() < nTab || aRef.aEnd.Tab() > nTab)
+ return;
+
+ SCCOL nCol1 = aRef.aStart.Col();
+ SCROW nRow1 = aRef.aStart.Row();
+ SCCOL nCol2 = aRef.aEnd.Col();
+ SCROW nRow2 = aRef.aEnd.Row();
+
+ // remove -> repaint
+ // ScUpdateMode::Marks: Invalidate, nothing until end of row
+
+ bool bHiddenEdge = false;
+ SCROW nTmp;
+ ScDocument& rDoc = aViewData.GetDocument();
+ while ( nCol1 > 0 && rDoc.ColHidden(nCol1, nTab) )
+ {
+ --nCol1;
+ bHiddenEdge = true;
+ }
+ while ( nCol2 < rDoc.MaxCol() && rDoc.ColHidden(nCol2, nTab) )
+ {
+ ++nCol2;
+ bHiddenEdge = true;
+ }
+ nTmp = rDoc.LastVisibleRow(0, nRow1, nTab);
+ if (!rDoc.ValidRow(nTmp))
+ nTmp = 0;
+ if (nTmp < nRow1)
+ {
+ nRow1 = nTmp;
+ bHiddenEdge = true;
+ }
+ nTmp = rDoc.FirstVisibleRow(nRow2, rDoc.MaxRow(), nTab);
+ if (!rDoc.ValidRow(nTmp))
+ nTmp = rDoc.MaxRow();
+ if (nTmp > nRow2)
+ {
+ nRow2 = nTmp;
+ bHiddenEdge = true;
+ }
+
+ if ( nCol2 - nCol1 > 1 && nRow2 - nRow1 > 1 && !bHiddenEdge )
+ {
+ // only along the edges
+ PaintArea( nCol1, nRow1, nCol2, nRow1, ScUpdateMode::Marks );
+ PaintArea( nCol1, nRow1+1, nCol1, nRow2-1, ScUpdateMode::Marks );
+ PaintArea( nCol2, nRow1+1, nCol2, nRow2-1, ScUpdateMode::Marks );
+ PaintArea( nCol1, nRow2, nCol2, nRow2, ScUpdateMode::Marks );
+ }
+ else // all in one
+ PaintArea( nCol1, nRow1, nCol2, nRow2, ScUpdateMode::Marks );
+}
+
+void ScTabView::PaintRangeFinder( tools::Long nNumber )
+{
+ ScInputHandler* pHdl = SC_MOD()->GetInputHdl( aViewData.GetViewShell() );
+ if (!pHdl)
+ return;
+
+ ScRangeFindList* pRangeFinder = pHdl->GetRangeFindList();
+ if ( !(pRangeFinder && pRangeFinder->GetDocName() == aViewData.GetDocShell()->GetTitle()) )
+ return;
+
+ SCTAB nTab = aViewData.GetTabNo();
+ sal_uInt16 nCount = static_cast<sal_uInt16>(pRangeFinder->Count());
+
+ if (nNumber < 0)
+ {
+ for (sal_uInt16 i=0; i<nCount; i++)
+ PaintRangeFinderEntry(&pRangeFinder->GetObject(i),nTab);
+ }
+ else
+ {
+ sal_uInt16 idx = nNumber;
+ if (idx < nCount)
+ PaintRangeFinderEntry(&pRangeFinder->GetObject(idx),nTab);
+ }
+}
+
+// for chart data selection
+
+void ScTabView::AddHighlightRange( const ScRange& rRange, const Color& rColor )
+{
+ maHighlightRanges.emplace_back( rRange, rColor );
+
+ SCTAB nTab = aViewData.GetTabNo();
+ if ( nTab >= rRange.aStart.Tab() && nTab <= rRange.aEnd.Tab() )
+ PaintArea( rRange.aStart.Col(), rRange.aStart.Row(),
+ rRange.aEnd.Col(), rRange.aEnd.Row(), ScUpdateMode::Marks );
+}
+
+void ScTabView::ClearHighlightRanges()
+{
+ SCTAB nTab = aViewData.GetTabNo();
+ for (ScHighlightEntry const & rEntry : maHighlightRanges)
+ {
+ ScRange aRange = rEntry.aRef;
+ if ( nTab >= aRange.aStart.Tab() && nTab <= aRange.aEnd.Tab() )
+ PaintArea( aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row(), ScUpdateMode::Marks );
+ }
+
+ maHighlightRanges.clear();
+}
+
+void ScTabView::DoChartSelection(
+ const uno::Sequence< chart2::data::HighlightedRange > & rHilightRanges )
+{
+ ClearHighlightRanges();
+ const sal_Unicode sep = ::formula::FormulaCompiler::GetNativeSymbolChar(ocSep);
+ size_t nSize = 0;
+ size_t nIndex = 0;
+ std::vector<ReferenceMark> aReferenceMarks( nSize );
+
+ for (chart2::data::HighlightedRange const & rHighlightedRange : rHilightRanges)
+ {
+ Color aSelColor(ColorTransparency, rHighlightedRange.PreferredColor);
+ ScRangeList aRangeList;
+ ScDocument& rDoc = aViewData.GetDocShell()->GetDocument();
+ if( ScRangeStringConverter::GetRangeListFromString(
+ aRangeList, rHighlightedRange.RangeRepresentation, rDoc, rDoc.GetAddressConvention(), sep ))
+ {
+ size_t nListSize = aRangeList.size();
+ nSize += nListSize;
+ aReferenceMarks.resize(nSize);
+
+ for ( size_t j = 0; j < nListSize; ++j )
+ {
+ ScRange& p = aRangeList[j];
+ ScRange aTargetRange;
+ if( rHighlightedRange.Index == - 1 )
+ {
+ aTargetRange = p;
+ AddHighlightRange( aTargetRange, aSelColor );
+ }
+ else
+ {
+ aTargetRange = lcl_getSubRangeByIndex( p, rHighlightedRange.Index );
+ AddHighlightRange( aTargetRange, aSelColor );
+ }
+
+ if ( comphelper::LibreOfficeKit::isActive() && aViewData.GetViewShell() )
+ {
+ aTargetRange.PutInOrder();
+
+ tools::Long nX1 = aTargetRange.aStart.Col();
+ tools::Long nX2 = aTargetRange.aEnd.Col();
+ tools::Long nY1 = aTargetRange.aStart.Row();
+ tools::Long nY2 = aTargetRange.aEnd.Row();
+ tools::Long nTab = aTargetRange.aStart.Tab();
+
+ aReferenceMarks[nIndex++] = ScInputHandler::GetReferenceMark( aViewData, aViewData.GetDocShell(),
+ nX1, nX2, nY1, nY2,
+ nTab, aSelColor );
+ }
+ }
+ }
+ }
+
+ if ( comphelper::LibreOfficeKit::isActive() && aViewData.GetViewShell() )
+ ScInputHandler::SendReferenceMarks( aViewData.GetViewShell(), aReferenceMarks );
+}
+
+void ScTabView::DoDPFieldPopup(std::u16string_view rPivotTableName, sal_Int32 nDimensionIndex, Point aPoint, Size aSize)
+{
+ ScDocument& rDocument = aViewData.GetDocShell()->GetDocument();
+ ScGridWindow* pWin = pGridWin[aViewData.GetActivePart()].get();
+
+ if (!pWin)
+ return;
+
+ ScDPCollection* pDPCollection = rDocument.GetDPCollection();
+ ScDPObject* pDPObject = pDPCollection->GetByName(rPivotTableName);
+ if (!pDPObject)
+ return;
+
+ pDPObject->BuildAllDimensionMembers();
+
+ Point aPos = pWin->LogicToPixel(aPoint);
+ bool bLOK = comphelper::LibreOfficeKit::isActive();
+ Point aScreenPoint = bLOK ? aPos : pWin->OutputToScreenPixel(aPos);
+ Size aScreenSize = pWin->LogicToPixel(aSize);
+
+ pWin->DPLaunchFieldPopupMenu(aScreenPoint, aScreenSize, nDimensionIndex, pDPObject);
+}
+
+// PaintGrid - repaint data range
+
+void ScTabView::PaintGrid()
+{
+ for (sal_uInt16 i = 0; i < 4; i++)
+ {
+ if (pGridWin[i] && pGridWin[i]->IsVisible())
+ pGridWin[i]->Invalidate();
+ }
+}
+
+// PaintTop - repaint top control elements
+
+void ScTabView::PaintTop()
+{
+ for (sal_uInt16 i = 0; i < 2; i++)
+ {
+ if (pColBar[i])
+ pColBar[i]->Invalidate();
+ if (pColOutline[i])
+ pColOutline[i]->Invalidate();
+ }
+}
+
+void ScTabView::CreateAnchorHandles(SdrHdlList& rHdl, const ScAddress& rAddress)
+{
+ for (sal_uInt16 i = 0; i < 4; i++)
+ {
+ if(pGridWin[i] && pGridWin[i]->IsVisible())
+ pGridWin[i]->CreateAnchorHandle(rHdl, rAddress);
+ }
+}
+
+void ScTabView::PaintTopArea( SCCOL nStartCol, SCCOL nEndCol )
+{
+ // pixel position of the left edge
+
+ if ( nStartCol < aViewData.GetPosX(SC_SPLIT_LEFT) ||
+ nStartCol < aViewData.GetPosX(SC_SPLIT_RIGHT) )
+ aViewData.RecalcPixPos();
+
+ // adjust freeze (UpdateFixX resets HSplitPos)
+
+ if ( aViewData.GetHSplitMode() == SC_SPLIT_FIX && nStartCol < aViewData.GetFixPosX() )
+ if (aViewData.UpdateFixX())
+ RepeatResize();
+
+ // paint
+
+ if (nStartCol>0)
+ --nStartCol; //! general ?
+
+ ScDocument& rDoc = aViewData.GetDocument();
+ bool bLayoutRTL = rDoc.IsLayoutRTL( aViewData.GetTabNo() );
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ for (sal_uInt16 i = 0; i < 2; i++)
+ {
+ ScHSplitPos eWhich = ScHSplitPos(i);
+ if (pColBar[eWhich])
+ {
+ Size aWinSize = pColBar[eWhich]->GetSizePixel();
+ tools::Long nStartX = aViewData.GetScrPos( nStartCol, 0, eWhich ).X();
+ tools::Long nEndX;
+ if (nEndCol >= rDoc.MaxCol())
+ nEndX = bLayoutRTL ? 0 : ( aWinSize.Width()-1 );
+ else
+ nEndX = aViewData.GetScrPos( nEndCol+1, 0, eWhich ).X() - nLayoutSign;
+ if (nStartX > nEndX)
+ std::swap(nStartX, nEndX);
+ pColBar[eWhich]->Invalidate(
+ tools::Rectangle( nStartX, 0, nEndX, aWinSize.Height()-1 ) );
+ }
+ if (pColOutline[eWhich])
+ pColOutline[eWhich]->Invalidate();
+ }
+}
+
+// PaintLeft - repaint left control elements
+
+void ScTabView::PaintLeft()
+{
+ for (sal_uInt16 i = 0; i < 2; i++)
+ {
+ if (pRowBar[i])
+ pRowBar[i]->Invalidate();
+ if (pRowOutline[i])
+ pRowOutline[i]->Invalidate();
+ }
+}
+
+void ScTabView::PaintLeftArea( SCROW nStartRow, SCROW nEndRow )
+{
+ // pixel position of the upper edge
+
+ if ( nStartRow < aViewData.GetPosY(SC_SPLIT_TOP) ||
+ nStartRow < aViewData.GetPosY(SC_SPLIT_BOTTOM) )
+ aViewData.RecalcPixPos();
+
+ // adjust freeze (UpdateFixY reset VSplitPos)
+
+ if ( aViewData.GetVSplitMode() == SC_SPLIT_FIX && nStartRow < aViewData.GetFixPosY() )
+ if (aViewData.UpdateFixY())
+ RepeatResize();
+
+ // paint
+
+ if (nStartRow>0)
+ --nStartRow;
+
+ ScDocument& rDoc = aViewData.GetDocument();
+ for (sal_uInt16 i = 0; i < 2; i++)
+ {
+ ScVSplitPos eWhich = ScVSplitPos(i);
+ if (pRowBar[eWhich])
+ {
+ Size aWinSize = pRowBar[eWhich]->GetSizePixel();
+ tools::Long nStartY = aViewData.GetScrPos( 0, nStartRow, eWhich ).Y();
+ tools::Long nEndY;
+ if (nEndRow >= rDoc.MaxRow())
+ nEndY = aWinSize.Height() - 1;
+ else
+ nEndY = aViewData.GetScrPos( 0, nEndRow+1, eWhich ).Y() - 1;
+ if (nStartY > nEndY)
+ std::swap(nStartY, nEndY);
+ pRowBar[eWhich]->Invalidate(
+ tools::Rectangle( 0, nStartY, aWinSize.Width()-1, nEndY ) );
+ }
+ if (pRowOutline[eWhich])
+ pRowOutline[eWhich]->Invalidate();
+ }
+}
+
+bool ScTabView::PaintExtras()
+{
+ bool bRet = false;
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCTAB nTab = aViewData.GetTabNo();
+ if (!rDoc.HasTable(nTab)) // sheet is deleted?
+ {
+ SCTAB nCount = rDoc.GetTableCount();
+ aViewData.SetTabNo(nCount-1);
+ bRet = true;
+ }
+ pTabControl->UpdateStatus(); // true = active
+ return bRet;
+}
+
+void ScTabView::RecalcPPT()
+{
+ // called after changes that require the PPT values to be recalculated
+ // (currently from detective operations)
+
+ double nOldX = aViewData.GetPPTX();
+ double nOldY = aViewData.GetPPTY();
+
+ aViewData.RefreshZoom(); // pre-calculate new PPT values
+
+ bool bChangedX = ( aViewData.GetPPTX() != nOldX );
+ bool bChangedY = ( aViewData.GetPPTY() != nOldY );
+ if ( !(bChangedX || bChangedY) )
+ return;
+
+ // call view SetZoom (including draw scale, split update etc)
+ // and paint only if values changed
+
+ Fraction aZoomX = aViewData.GetZoomX();
+ Fraction aZoomY = aViewData.GetZoomY();
+ SetZoom( aZoomX, aZoomY, false );
+
+ PaintGrid();
+ if (bChangedX)
+ PaintTop();
+ if (bChangedY)
+ PaintLeft();
+}
+
+void ScTabView::ActivateView( bool bActivate, bool bFirst )
+{
+ if ( bActivate == aViewData.IsActive() && !bFirst )
+ {
+ // no assertion anymore - occurs when previously in Drag&Drop switching over
+ // to another document
+ return;
+ }
+
+ // is only called for MDI-(De)Activate
+ // aViewData.Activate behind due to cursor show for KillEditView
+ // don't delete selection - if Activate(false) is set in ViewData,
+ // then the selection is not displayed
+
+ if (!bActivate)
+ {
+ ScModule* pScMod = SC_MOD();
+ bool bRefMode = pScMod->IsFormulaMode();
+
+ // don't cancel reference input, to allow reference
+ // to other document
+
+ if (!bRefMode)
+ {
+ // pass view to GetInputHdl, this view may not be current anymore
+ ScInputHandler* pHdl = SC_MOD()->GetInputHdl(aViewData.GetViewShell());
+ if (pHdl)
+ pHdl->EnterHandler();
+ }
+ }
+
+ PaintExtras();
+
+ aViewData.Activate(bActivate);
+
+ PaintBlock(false); // repaint, selection after active status
+
+ if (!bActivate)
+ HideAllCursors(); // Cursor
+ else if (!bFirst)
+ ShowAllCursors();
+
+ if (bActivate)
+ {
+ if ( bFirst )
+ {
+ ScSplitPos eWin = aViewData.GetActivePart();
+ OSL_ENSURE( pGridWin[eWin], "Corrupted document, not all SplitPos in GridWin" );
+ if ( !pGridWin[eWin] )
+ {
+ eWin = SC_SPLIT_BOTTOMLEFT;
+ if ( !pGridWin[eWin] )
+ {
+ short i;
+ for ( i=0; i<4; i++ )
+ {
+ if ( pGridWin[i] )
+ {
+ eWin = static_cast<ScSplitPos>(i);
+ break; // for
+ }
+ }
+ OSL_ENSURE( i<4, "and BOOM" );
+ }
+ aViewData.SetActivePart( eWin );
+ }
+ }
+ // do not call GrabFocus from here!
+ // if the document is processed, then Sfx calls GrabFocus in the window of the shell.
+ // if it is a mail body for instance, then it can't get the focus
+ UpdateInputContext();
+ }
+ else
+ pGridWin[aViewData.GetActivePart()]->ClickExtern();
+}
+
+void ScTabView::ActivatePart( ScSplitPos eWhich )
+{
+ ScSplitPos eOld = aViewData.GetActivePart();
+ if ( eOld == eWhich )
+ return;
+
+ bInActivatePart = true;
+
+ bool bRefMode = SC_MOD()->IsFormulaMode();
+
+ // the HasEditView call during SetCursor would fail otherwise
+ if ( aViewData.HasEditView(eOld) && !bRefMode )
+ UpdateInputLine();
+
+ ScHSplitPos eOldH = WhichH(eOld);
+ ScVSplitPos eOldV = WhichV(eOld);
+ ScHSplitPos eNewH = WhichH(eWhich);
+ ScVSplitPos eNewV = WhichV(eWhich);
+ bool bTopCap = pColBar[eOldH] && pColBar[eOldH]->IsMouseCaptured();
+ bool bLeftCap = pRowBar[eOldV] && pRowBar[eOldV]->IsMouseCaptured();
+
+ bool bFocus = pGridWin[eOld]->HasFocus();
+ bool bCapture = pGridWin[eOld]->IsMouseCaptured();
+ if (bCapture)
+ pGridWin[eOld]->ReleaseMouse();
+ pGridWin[eOld]->ClickExtern();
+ pGridWin[eOld]->HideCursor();
+ pGridWin[eWhich]->HideCursor();
+ aViewData.SetActivePart( eWhich );
+
+ ScTabViewShell* pShell = aViewData.GetViewShell();
+ pShell->WindowChanged();
+
+ pSelEngine->SetWindow(pGridWin[eWhich]);
+ pSelEngine->SetWhich(eWhich);
+ pSelEngine->SetVisibleArea( tools::Rectangle(Point(), pGridWin[eWhich]->GetOutputSizePixel()) );
+
+ pGridWin[eOld]->MoveMouseStatus(*pGridWin[eWhich]);
+
+ if ( bCapture || pGridWin[eWhich]->IsMouseCaptured() )
+ {
+ // tracking instead of CaptureMouse, so it can be cancelled cleanly
+ // (SelectionEngine calls CaptureMouse for SetWindow)
+ //! someday SelectionEngine itself should call StartTracking!?!
+ pGridWin[eWhich]->ReleaseMouse();
+ pGridWin[eWhich]->StartTracking();
+ }
+
+ if ( bTopCap && pColBar[eNewH] )
+ {
+ pColBar[eOldH]->SetIgnoreMove(true);
+ pColBar[eNewH]->SetIgnoreMove(false);
+ pHdrSelEng->SetWindow( pColBar[eNewH] );
+ tools::Long nWidth = pColBar[eNewH]->GetOutputSizePixel().Width();
+ pHdrSelEng->SetVisibleArea( tools::Rectangle( 0, LONG_MIN, nWidth-1, LONG_MAX ) );
+ pColBar[eNewH]->CaptureMouse();
+ }
+ if ( bLeftCap && pRowBar[eNewV] )
+ {
+ pRowBar[eOldV]->SetIgnoreMove(true);
+ pRowBar[eNewV]->SetIgnoreMove(false);
+ pHdrSelEng->SetWindow( pRowBar[eNewV] );
+ tools::Long nHeight = pRowBar[eNewV]->GetOutputSizePixel().Height();
+ pHdrSelEng->SetVisibleArea( tools::Rectangle( LONG_MIN, 0, LONG_MAX, nHeight-1 ) );
+ pRowBar[eNewV]->CaptureMouse();
+ }
+ aHdrFunc.SetWhich(eWhich);
+
+ pGridWin[eOld]->ShowCursor();
+ pGridWin[eWhich]->ShowCursor();
+
+ SfxInPlaceClient* pClient = aViewData.GetViewShell()->GetIPClient();
+ bool bOleActive = ( pClient && pClient->IsObjectInPlaceActive() );
+
+ // don't switch ViewShell's active window during RefInput, because the focus
+ // might change, and subsequent SetReference calls wouldn't find the right EditView
+ if ( !bRefMode && !bOleActive )
+ aViewData.GetViewShell()->SetWindow( pGridWin[eWhich] );
+
+ if ( bFocus && !aViewData.IsAnyFillMode() && !bRefMode )
+ {
+ // GrabFocus only if previously the other GridWindow had the focus
+ // (for instance due to search and replace)
+ pGridWin[eWhich]->GrabFocus();
+ }
+
+ bInActivatePart = false;
+}
+
+void ScTabView::HideListBox()
+{
+ for (VclPtr<ScGridWindow> & pWin : pGridWin)
+ {
+ if (pWin)
+ pWin->ClickExtern();
+ }
+}
+
+void ScTabView::UpdateInputContext()
+{
+ ScGridWindow* pWin = pGridWin[aViewData.GetActivePart()].get();
+ if (pWin)
+ pWin->UpdateInputContext();
+
+ if (pTabControl)
+ pTabControl->UpdateInputContext();
+}
+
+// GetGridWidth - width of an output range (for ViewData)
+
+tools::Long ScTabView::GetGridWidth( ScHSplitPos eWhich )
+{
+ // at present only the size of the current pane is synchronized with
+ // the size of the visible area in Online;
+ // as a workaround we use the same width for all panes independently
+ // from the eWhich value
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ ScGridWindow* pGridWindow = aViewData.GetActiveWin();
+ if (pGridWindow)
+ return pGridWindow->GetSizePixel().Width();
+ }
+
+ ScSplitPos eGridWhich = ( eWhich == SC_SPLIT_LEFT ) ? SC_SPLIT_BOTTOMLEFT : SC_SPLIT_BOTTOMRIGHT;
+ if (pGridWin[eGridWhich])
+ return pGridWin[eGridWhich]->GetSizePixel().Width();
+ else
+ return 0;
+}
+
+// GetGridHeight - height of an output range (for ViewData)
+
+tools::Long ScTabView::GetGridHeight( ScVSplitPos eWhich )
+{
+ // at present only the size of the current pane is synchronized with
+ // the size of the visible area in Online;
+ // as a workaround we use the same height for all panes independently
+ // from the eWhich value
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ ScGridWindow* pGridWindow = aViewData.GetActiveWin();
+ if (pGridWindow)
+ return pGridWindow->GetSizePixel().Height();
+ }
+
+ ScSplitPos eGridWhich = ( eWhich == SC_SPLIT_TOP ) ? SC_SPLIT_TOPLEFT : SC_SPLIT_BOTTOMLEFT;
+ if (pGridWin[eGridWhich])
+ return pGridWin[eGridWhich]->GetSizePixel().Height();
+ else
+ return 0;
+}
+
+void ScTabView::UpdateInputLine()
+{
+ SC_MOD()->InputEnterHandler();
+}
+
+void ScTabView::ZoomChanged()
+{
+ ScInputHandler* pHdl = SC_MOD()->GetInputHdl(aViewData.GetViewShell());
+ if (pHdl)
+ pHdl->SetRefScale( aViewData.GetZoomX(), aViewData.GetZoomY() );
+
+ UpdateFixPos();
+
+ UpdateScrollBars();
+
+ // VisArea...
+ // AW: Discussed with NN if there is a reason that new map mode was only set for one window,
+ // but is not. Setting only on one window causes the first repaint to have the old mapMode
+ // in three of four views, so the overlay will save the wrong content e.g. when zooming out.
+ // Changing to setting map mode at all windows.
+
+ for (sal_uInt32 i = 0; i < 4; i++)
+ {
+ if (pGridWin[i])
+ pGridWin[i]->SetMapMode(pGridWin[i]->GetDrawMapMode());
+ }
+
+ SetNewVisArea();
+
+ InterpretVisible(); // have everything calculated before painting
+
+ SfxBindings& rBindings = aViewData.GetBindings();
+ rBindings.Invalidate( SID_ATTR_ZOOM );
+ rBindings.Invalidate( SID_ATTR_ZOOMSLIDER );
+ rBindings.Invalidate(SID_ZOOM_IN);
+ rBindings.Invalidate(SID_ZOOM_OUT);
+
+ HideNoteMarker();
+
+ // To not change too much, use pWin here
+ ScGridWindow* pWin = pGridWin[aViewData.GetActivePart()].get();
+
+ if ( pWin && aViewData.HasEditView( aViewData.GetActivePart() ) )
+ {
+ // flush OverlayManager before changing the MapMode
+ pWin->flushOverlayManager();
+
+ // make sure the EditView's position and size are updated
+ // with the right (logic, not drawing) MapMode
+ pWin->SetMapMode( aViewData.GetLogicMode() );
+ UpdateEditView();
+ }
+}
+
+void ScTabView::CheckNeedsRepaint()
+{
+ for (sal_uInt16 i = 0; i < 4; i++)
+ {
+ if (pGridWin[i] && pGridWin[i]->IsVisible())
+ pGridWin[i]->CheckNeedsRepaint();
+ }
+}
+
+bool ScTabView::NeedsRepaint()
+{
+ for (VclPtr<ScGridWindow> & pWin : pGridWin)
+ {
+ if (pWin && pWin->IsVisible() && pWin->NeedsRepaint())
+ return true;
+ }
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabview4.cxx b/sc/source/ui/view/tabview4.cxx
new file mode 100644
index 0000000000..a7de6bdf67
--- /dev/null
+++ b/sc/source/ui/view/tabview4.cxx
@@ -0,0 +1,538 @@
+/* -*- 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 .
+ */
+
+#include <vcl/help.hxx>
+
+#include <tabview.hxx>
+#include <tabvwsh.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <scmod.hxx>
+#include <gridwin.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <inputhdl.hxx>
+
+// --- Referenz-Eingabe / Fill-Cursor
+
+void ScTabView::HideTip()
+{
+ if ( nTipVisible )
+ {
+ ScSplitPos eWhich = aViewData.GetActivePart();
+ vcl::Window* pWin = pGridWin[eWhich];
+ Help::HidePopover(pWin, nTipVisible);
+ nTipVisible = nullptr;
+ aTipRectangle = tools::Rectangle();
+ nTipAlign = QuickHelpFlags::NONE;
+ sTipString.clear();
+ sTopParent.clear();
+ }
+}
+
+void ScTabView::ShowRefTip()
+{
+ bool bDone = false;
+ if ( aViewData.GetRefType() == SC_REFTYPE_REF && Help::IsQuickHelpEnabled() )
+ {
+ SCCOL nStartX = aViewData.GetRefStartX();
+ SCROW nStartY = aViewData.GetRefStartY();
+ SCCOL nEndX = aViewData.GetRefEndX();
+ SCROW nEndY = aViewData.GetRefEndY();
+ if ( nEndX != nStartX || nEndY != nStartY ) // not for a single cell
+ {
+ bool bLeft = ( nEndX < nStartX );
+ bool bTop = ( nEndY < nStartY );
+ PutInOrder( nStartX, nEndX );
+ PutInOrder( nStartY, nEndY );
+ SCCOL nCols = nEndX+1-nStartX;
+ SCROW nRows = nEndY+1-nStartY;
+
+ OUString aHelp = ScResId( STR_QUICKHELP_REF );
+ aHelp = aHelp.replaceFirst("%1", OUString::number(nRows) );
+ aHelp = aHelp.replaceFirst("%2", OUString::number(nCols) );
+
+ ScSplitPos eWhich = aViewData.GetActivePart();
+ vcl::Window* pWin = pGridWin[eWhich];
+ if ( pWin )
+ {
+ Point aStart = aViewData.GetScrPos( nStartX, nStartY, eWhich );
+ Point aEnd = aViewData.GetScrPos( nEndX+1, nEndY+1, eWhich );
+
+ Point aPos( bLeft ? aStart.X() : ( aEnd.X() + 3 ),
+ bTop ? aStart.Y() : ( aEnd.Y() + 3 ) );
+ QuickHelpFlags nFlags = ( bLeft ? QuickHelpFlags::Right : QuickHelpFlags::Left ) |
+ ( bTop ? QuickHelpFlags::Bottom : QuickHelpFlags::Top );
+
+ // not over the edited formula
+ if ( !bTop && aViewData.HasEditView( eWhich ) &&
+ nEndY+1 == aViewData.GetEditViewRow() )
+ {
+ // then align at the upper border of edited cell
+ aPos.AdjustY( -2 ); // the three from above
+ nFlags = ( nFlags & ~QuickHelpFlags::Top ) | QuickHelpFlags::Bottom;
+ }
+
+ tools::Rectangle aRect( pWin->OutputToScreenPixel( aPos ), Size(1,1) );
+
+ // Test if changed.
+ if (!nTipVisible || nFlags != nTipAlign || aRect != aTipRectangle || sTipString != aHelp || sTopParent != pWin)
+ {
+ HideTip();
+ nTipVisible = Help::ShowPopover(pWin, aRect, aHelp, nFlags);
+ aTipRectangle = aRect;
+ nTipAlign = nFlags;
+ sTipString = aHelp;
+ sTopParent = pWin;
+ }
+ bDone = true;
+ }
+ }
+ }
+
+ if (!bDone)
+ HideTip();
+}
+
+void ScTabView::StopRefMode()
+{
+ if (aViewData.IsRefMode())
+ {
+ aViewData.SetRefMode( false, SC_REFTYPE_NONE );
+
+ HideTip();
+ UpdateShrinkOverlay();
+
+ if ( aViewData.GetTabNo() >= aViewData.GetRefStartZ() &&
+ aViewData.GetTabNo() <= aViewData.GetRefEndZ() )
+ {
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCCOL nStartX = aViewData.GetRefStartX();
+ SCROW nStartY = aViewData.GetRefStartY();
+ SCCOL nEndX = aViewData.GetRefEndX();
+ SCROW nEndY = aViewData.GetRefEndY();
+ if ( nStartX == nEndX && nStartY == nEndY )
+ rDoc.ExtendMerge( nStartX, nStartY, nEndX, nEndY, aViewData.GetTabNo() );
+
+ PaintArea( nStartX,nStartY,nEndX,nEndY, ScUpdateMode::Marks );
+ }
+
+ pSelEngine->Reset();
+ pSelEngine->SetAddMode( false ); //! shouldn't that happen during reset?
+
+ ScSplitPos eOld = pSelEngine->GetWhich();
+ ScSplitPos eNew = aViewData.GetActivePart();
+ if ( eNew != eOld )
+ {
+ pSelEngine->SetWindow( pGridWin[ eNew ] );
+ pSelEngine->SetWhich( eNew );
+ pSelEngine->SetVisibleArea( tools::Rectangle(Point(),
+ pGridWin[eNew]->GetOutputSizePixel()) );
+ pGridWin[eOld]->MoveMouseStatus(*pGridWin[eNew]);
+ }
+ }
+
+ // AlignToCursor(SC_FOLLOW_NONE): Only switch active part.
+ // This must also be done if no RefMode was active (for RangeFinder dragging),
+ // but if RefMode was set, AlignToCursor must be after SelectionEngine reset,
+ // so the SelectionEngine SetWindow call from AlignToCursor doesn't capture
+ // the mouse again when called from Tracking/MouseButtonUp (#94562#).
+ AlignToCursor( aViewData.GetCurX(), aViewData.GetCurY(), SC_FOLLOW_NONE );
+}
+
+void ScTabView::DoneRefMode( bool bContinue )
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+ if ( aViewData.GetRefType() == SC_REFTYPE_REF && bContinue )
+ SC_MOD()->AddRefEntry();
+
+ bool bWasRef = aViewData.IsRefMode();
+ aViewData.SetRefMode( false, SC_REFTYPE_NONE );
+
+ HideTip();
+ UpdateShrinkOverlay();
+
+ // Paint:
+ if ( bWasRef && aViewData.GetTabNo() >= aViewData.GetRefStartZ() &&
+ aViewData.GetTabNo() <= aViewData.GetRefEndZ() )
+ {
+ SCCOL nStartX = aViewData.GetRefStartX();
+ SCROW nStartY = aViewData.GetRefStartY();
+ SCCOL nEndX = aViewData.GetRefEndX();
+ SCROW nEndY = aViewData.GetRefEndY();
+ if ( nStartX == nEndX && nStartY == nEndY )
+ rDoc.ExtendMerge( nStartX, nStartY, nEndX, nEndY, aViewData.GetTabNo() );
+
+ PaintArea( nStartX,nStartY,nEndX,nEndY, ScUpdateMode::Marks );
+ }
+}
+
+void ScTabView::UpdateRef( SCCOL nCurX, SCROW nCurY, SCTAB nCurZ )
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+
+ if (!aViewData.IsRefMode())
+ {
+ // This will happen, when first at a reference dialog with Control in
+ // the table is clicked. Then append the new reference to the old content:
+
+ ScModule* pScMod = SC_MOD();
+ if (pScMod->IsFormulaMode())
+ pScMod->AddRefEntry();
+
+ InitRefMode( nCurX, nCurY, nCurZ, SC_REFTYPE_REF );
+ }
+
+ if ( nCurX != aViewData.GetRefEndX() || nCurY != aViewData.GetRefEndY() ||
+ nCurZ != aViewData.GetRefEndZ() )
+ {
+ ScMarkData& rMark = aViewData.GetMarkData();
+ SCTAB nTab = aViewData.GetTabNo();
+
+ SCCOL nStartX = aViewData.GetRefStartX();
+ SCROW nStartY = aViewData.GetRefStartY();
+ SCCOL nEndX = aViewData.GetRefEndX();
+ SCROW nEndY = aViewData.GetRefEndY();
+ if ( nStartX == nEndX && nStartY == nEndY )
+ rDoc.ExtendMerge( nStartX, nStartY, nEndX, nEndY, nTab );
+ ScUpdateRect aRect( nStartX, nStartY, nEndX, nEndY );
+
+ if (rDoc.HasAttrib(nCurX, nCurY, nCurZ, HasAttrFlags::Merged))
+ rDoc.ExtendMerge(nStartX, nStartY, nCurX, nCurY, nCurZ);
+
+ aViewData.SetRefEnd( nCurX, nCurY, nCurZ );
+
+ nStartX = aViewData.GetRefStartX();
+ nStartY = aViewData.GetRefStartY();
+ nEndX = aViewData.GetRefEndX();
+ nEndY = aViewData.GetRefEndY();
+ if ( nStartX == nEndX && nStartY == nEndY )
+ rDoc.ExtendMerge( nStartX, nStartY, nEndX, nEndY, nTab );
+ aRect.SetNew( nStartX, nStartY, nEndX, nEndY );
+
+ ScRefType eType = aViewData.GetRefType();
+ if ( eType == SC_REFTYPE_REF )
+ {
+ if ((nStartX > nEndX || nStartY > nEndY) &&
+ rDoc.HasAttrib(nStartX, nStartY, nTab, HasAttrFlags::Merged))
+ rDoc.ExtendMerge( nStartX, nStartY, nStartX, nStartY, nTab );
+
+ ScRange aRef(
+ nStartX, nStartY, aViewData.GetRefStartZ(),
+ nEndX, nEndY, aViewData.GetRefEndZ() );
+ SC_MOD()->SetReference( aRef, rDoc, &rMark );
+ ShowRefTip();
+ }
+ else if ( eType == SC_REFTYPE_EMBED_LT || eType == SC_REFTYPE_EMBED_RB )
+ {
+ PutInOrder(nStartX,nEndX);
+ PutInOrder(nStartY,nEndY);
+ rDoc.SetEmbedded( ScRange(nStartX,nStartY,nTab, nEndX,nEndY,nTab) );
+ ScDocShell* pDocSh = aViewData.GetDocShell();
+ pDocSh->UpdateOle( aViewData, true );
+ pDocSh->SetDocumentModified();
+ }
+
+ SCCOL nPaintStartX;
+ SCROW nPaintStartY;
+ SCCOL nPaintEndX;
+ SCROW nPaintEndY;
+ if (aRect.GetDiff( nPaintStartX, nPaintStartY, nPaintEndX, nPaintEndY ))
+ PaintArea( nPaintStartX, nPaintStartY, nPaintEndX, nPaintEndY, ScUpdateMode::Marks );
+
+ ScInputHandler* pInputHandler = SC_MOD()->GetInputHdl();
+ if (pInputHandler)
+ {
+ pInputHandler->UpdateLokReferenceMarks();
+ }
+ }
+
+ // autocomplete for Auto-Fill
+ if ( !(aViewData.GetRefType() == SC_REFTYPE_FILL && Help::IsQuickHelpEnabled()) )
+ return;
+
+ vcl::Window* pWin = GetActiveWin();
+ if ( !pWin )
+ return;
+
+ OUString aHelpStr;
+ ScRange aMarkRange;
+ aViewData.GetSimpleArea( aMarkRange );
+ SCCOL nEndX = aViewData.GetRefEndX();
+ SCROW nEndY = aViewData.GetRefEndY();
+ ScRange aDelRange;
+ if ( aViewData.GetFillMode() == ScFillMode::MATRIX && !(nScFillModeMouseModifier & KEY_MOD1) )
+ {
+ aHelpStr = ScResId( STR_TIP_RESIZEMATRIX );
+ SCCOL nCols = nEndX + 1 - aViewData.GetRefStartX(); // order is right
+ SCROW nRows = nEndY + 1 - aViewData.GetRefStartY();
+ aHelpStr = aHelpStr.replaceFirst("%1", OUString::number(nRows) );
+ aHelpStr = aHelpStr.replaceFirst("%2", OUString::number(nCols) );
+ }
+ else if ( aViewData.GetDelMark( aDelRange ) )
+ aHelpStr = ScResId( STR_QUICKHELP_DELETE );
+ else if ( nEndX != aMarkRange.aEnd.Col() || nEndY != aMarkRange.aEnd.Row() )
+ aHelpStr = rDoc.GetAutoFillPreview( aMarkRange, nEndX, nEndY );
+
+ if (!aHelpStr.getLength())
+ return;
+
+ // depending on direction the upper or lower corner
+ SCCOL nAddX = ( nEndX >= aMarkRange.aEnd.Col() ) ? 1 : 0;
+ SCROW nAddY = ( nEndY >= aMarkRange.aEnd.Row() ) ? 1 : 0;
+ Point aPos = aViewData.GetScrPos( nEndX+nAddX, nEndY+nAddY, aViewData.GetActivePart() );
+ aPos.AdjustX(8 );
+ aPos.AdjustY(4 );
+ aPos = pWin->OutputToScreenPixel( aPos );
+ tools::Rectangle aRect( aPos, aPos );
+ QuickHelpFlags nAlign = QuickHelpFlags::Left|QuickHelpFlags::Top;
+ if (!nTipVisible || nAlign != nTipAlign || aRect != aTipRectangle || sTipString != aHelpStr || sTopParent != pWin)
+ {
+ HideTip();
+ nTipVisible = Help::ShowPopover(pWin, aRect, aHelpStr, nAlign);
+ aTipRectangle = aRect;
+ nTipAlign = nAlign;
+ sTipString = aHelpStr;
+ sTopParent = pWin;
+ }
+}
+
+void ScTabView::InitRefMode( SCCOL nCurX, SCROW nCurY, SCTAB nCurZ, ScRefType eType )
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+ ScMarkData& rMark = aViewData.GetMarkData();
+ if (aViewData.IsRefMode())
+ return;
+
+ aViewData.SetRefMode( true, eType );
+ aViewData.SetRefStart( nCurX, nCurY, nCurZ );
+ aViewData.SetRefEnd( nCurX, nCurY, nCurZ );
+
+ if (nCurZ == aViewData.GetTabNo())
+ {
+ SCCOL nStartX = nCurX;
+ SCROW nStartY = nCurY;
+ SCCOL nEndX = nCurX;
+ SCROW nEndY = nCurY;
+ rDoc.ExtendMerge( nStartX, nStartY, nEndX, nEndY, aViewData.GetTabNo() );
+
+ //! draw only markings over content!
+ PaintArea( nStartX,nStartY,nEndX,nEndY, ScUpdateMode::Marks );
+
+ // SetReference without Merge-Adjustment
+ ScRange aRef( nCurX,nCurY,nCurZ, nCurX,nCurY,nCurZ );
+ SC_MOD()->SetReference( aRef, rDoc, &rMark );
+ }
+
+ ScInputHandler* pInputHandler = SC_MOD()->GetInputHdl();
+ if (pInputHandler)
+ {
+ pInputHandler->UpdateLokReferenceMarks();
+ }
+}
+
+void ScTabView::SetScrollBar( ScrollAdaptor& rScroll, tools::Long nRangeMax, tools::Long nVisible, tools::Long nPos, bool bLayoutRTL )
+{
+ if ( nVisible == 0 )
+ nVisible = 1; // #i59893# don't use visible size 0
+
+ rScroll.SetRange( Range( 0, nRangeMax ) );
+ rScroll.SetVisibleSize( nVisible );
+ rScroll.SetThumbPos( nPos );
+
+ rScroll.EnableRTL( bLayoutRTL );
+}
+
+tools::Long ScTabView::GetScrollBarPos( const ScrollAdaptor& rScroll )
+{
+ return rScroll.GetThumbPos();
+}
+
+// UpdateScrollBars - set visible area and scroll width of scroll bars
+static tools::Long lcl_UpdateBar( ScrollAdaptor& rScroll, SCCOLROW nSize ) // Size = (complete) cells
+{
+ tools::Long nOldPos;
+ tools::Long nNewPos;
+
+ nOldPos = rScroll.GetThumbPos();
+ rScroll.SetPageSize( static_cast<tools::Long>(nSize) );
+ nNewPos = rScroll.GetThumbPos();
+#ifndef UNX
+ rScroll.SetPageSize( 1 ); // always possible !
+#endif
+
+ return nNewPos - nOldPos;
+}
+
+static tools::Long lcl_GetScrollRange( SCCOLROW nDocEnd, SCCOLROW nPos, SCCOLROW nVis, SCCOLROW nMax, SCCOLROW nStart )
+{
+ // get the end (positive) of a scroll bar range that always starts at 0
+
+ ++nVis;
+ ++nMax; // for partially visible cells
+ SCCOLROW nEnd = std::max(nDocEnd, static_cast<SCCOLROW>(nPos+nVis)) + nVis;
+ if (nEnd > nMax)
+ nEnd = nMax;
+
+ return ( nEnd - nStart ); // for range starting at 0
+}
+
+void ScTabView::UpdateScrollBars( HeaderType eHeaderType )
+{
+ ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), eHeaderType, GetViewData().GetTabNo());
+
+ tools::Long nDiff;
+ bool bTop = ( aViewData.GetVSplitMode() != SC_SPLIT_NONE );
+ bool bRight = ( aViewData.GetHSplitMode() != SC_SPLIT_NONE );
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCTAB nTab = aViewData.GetTabNo();
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+ SCCOL nUsedX;
+ SCROW nUsedY;
+ rDoc.GetTableArea( nTab, nUsedX, nUsedY ); //! cached !!!!!!!!!!!!!!!
+
+ SCCOL nVisXL = 0;
+ SCCOL nVisXR = 0;
+ SCROW nVisYB = 0;
+ SCROW nVisYT = 0;
+
+ SCCOL nStartX = 0;
+ SCROW nStartY = 0;
+ if (aViewData.GetHSplitMode()==SC_SPLIT_FIX)
+ nStartX = aViewData.GetFixPosX();
+ if (aViewData.GetVSplitMode()==SC_SPLIT_FIX)
+ nStartY = aViewData.GetFixPosY();
+
+ nVisXL = aViewData.VisibleCellsX( SC_SPLIT_LEFT );
+ tools::Long nMaxXL = lcl_GetScrollRange( nUsedX, aViewData.GetPosX(SC_SPLIT_LEFT), nVisXL, rDoc.MaxCol(), 0 );
+ SetScrollBar( *aHScrollLeft, nMaxXL, nVisXL, aViewData.GetPosX( SC_SPLIT_LEFT ), bLayoutRTL );
+
+ nVisYB = aViewData.VisibleCellsY( SC_SPLIT_BOTTOM );
+ tools::Long nMaxYB = lcl_GetScrollRange( nUsedY, aViewData.GetPosY(SC_SPLIT_BOTTOM), nVisYB, rDoc.MaxRow(), nStartY );
+ SetScrollBar( *aVScrollBottom, nMaxYB, nVisYB, aViewData.GetPosY( SC_SPLIT_BOTTOM ) - nStartY, bLayoutRTL );
+
+ if (bRight)
+ {
+ nVisXR = aViewData.VisibleCellsX( SC_SPLIT_RIGHT );
+ tools::Long nMaxXR = lcl_GetScrollRange( nUsedX, aViewData.GetPosX(SC_SPLIT_RIGHT), nVisXR, rDoc.MaxCol(), nStartX );
+ SetScrollBar( *aHScrollRight, nMaxXR, nVisXR, aViewData.GetPosX( SC_SPLIT_RIGHT ) - nStartX, bLayoutRTL );
+ }
+
+ if (bTop)
+ {
+ nVisYT = aViewData.VisibleCellsY( SC_SPLIT_TOP );
+ tools::Long nMaxYT = lcl_GetScrollRange( nUsedY, aViewData.GetPosY(SC_SPLIT_TOP), nVisYT, rDoc.MaxRow(), 0 );
+ SetScrollBar( *aVScrollTop, nMaxYT, nVisYT, aViewData.GetPosY( SC_SPLIT_TOP ), bLayoutRTL );
+ }
+
+ // test the range
+
+ nDiff = lcl_UpdateBar( *aHScrollLeft, nVisXL );
+ if (nDiff) ScrollX( nDiff, SC_SPLIT_LEFT );
+ if (bRight)
+ {
+ nDiff = lcl_UpdateBar( *aHScrollRight, nVisXR );
+ if (nDiff) ScrollX( nDiff, SC_SPLIT_RIGHT );
+ }
+
+ nDiff = lcl_UpdateBar( *aVScrollBottom, nVisYB );
+ if (nDiff) ScrollY( nDiff, SC_SPLIT_BOTTOM );
+ if (bTop)
+ {
+ nDiff = lcl_UpdateBar( *aVScrollTop, nVisYT );
+ if (nDiff) ScrollY( nDiff, SC_SPLIT_TOP );
+ }
+
+ // set visible area for online spelling
+
+ if ( aViewData.IsActive() )
+ {
+ if (UpdateVisibleRange())
+ SC_MOD()->AnythingChanged(); // if visible area has changed
+ }
+}
+
+#ifndef HDR_SLIDERSIZE
+#define HDR_SLIDERSIZE 2
+#endif
+
+void ScTabView::InvertHorizontal( ScVSplitPos eWhich, tools::Long nDragPos )
+{
+ for (sal_uInt16 i=0; i<4; i++)
+ if (WhichV(static_cast<ScSplitPos>(i))==eWhich)
+ {
+ ScGridWindow* pWin = pGridWin[i].get();
+ if (pWin)
+ {
+ tools::Rectangle aRect( 0,nDragPos, pWin->GetOutputSizePixel().Width()-1,nDragPos+HDR_SLIDERSIZE-1 );
+ pWin->PaintImmediately();
+ pWin->DoInvertRect( aRect ); // Pixel
+ }
+ }
+}
+
+void ScTabView::InvertVertical( ScHSplitPos eWhich, tools::Long nDragPos )
+{
+ for (sal_uInt16 i=0; i<4; i++)
+ if (WhichH(static_cast<ScSplitPos>(i))==eWhich)
+ {
+ ScGridWindow* pWin = pGridWin[i].get();
+ if (pWin)
+ {
+ tools::Rectangle aRect( nDragPos,0, nDragPos+HDR_SLIDERSIZE-1,pWin->GetOutputSizePixel().Height()-1 );
+ pWin->PaintImmediately();
+ pWin->DoInvertRect( aRect ); // Pixel
+ }
+ }
+}
+
+void ScTabView::InterpretVisible()
+{
+ // make sure all visible cells are interpreted,
+ // so the next paint will not execute a macro function
+
+ ScDocument& rDoc = aViewData.GetDocument();
+ if ( !rDoc.GetAutoCalc() )
+ return;
+
+ SCTAB nTab = aViewData.GetTabNo();
+ for (sal_uInt16 i=0; i<4; i++)
+ {
+ // rely on gridwin pointers to find used panes
+ // no IsVisible test in case the whole view is not yet shown
+
+ if (pGridWin[i])
+ {
+ ScHSplitPos eHWhich = WhichH( ScSplitPos(i) );
+ ScVSplitPos eVWhich = WhichV( ScSplitPos(i) );
+
+ SCCOL nX1 = rDoc.SanitizeCol( aViewData.GetPosX( eHWhich ));
+ SCROW nY1 = rDoc.SanitizeRow( aViewData.GetPosY( eVWhich ));
+ SCCOL nX2 = rDoc.SanitizeCol( nX1 + aViewData.VisibleCellsX( eHWhich ));
+ SCROW nY2 = rDoc.SanitizeRow( nY1 + aViewData.VisibleCellsY( eVWhich ));
+
+ rDoc.InterpretDirtyCells(ScRange(nX1, nY1, nTab, nX2, nY2, nTab));
+ }
+ }
+
+ // #i65047# repaint during the above loop may have set the bNeedsRepaint flag
+ CheckNeedsRepaint();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabview5.cxx b/sc/source/ui/view/tabview5.cxx
new file mode 100644
index 0000000000..c9b65bb58c
--- /dev/null
+++ b/sc/source/ui/view/tabview5.cxx
@@ -0,0 +1,704 @@
+/* -*- 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 .
+ */
+
+#include <svx/fmshell.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdocapt.hxx>
+#include <svx/svdoutl.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <osl/diagnose.h>
+
+#include <tabview.hxx>
+#include <tabvwsh.hxx>
+#include <document.hxx>
+#include <gridwin.hxx>
+#include <olinewin.hxx>
+#include <tabsplit.hxx>
+#include <colrowba.hxx>
+#include <tabcont.hxx>
+#include <sc.hrc>
+#include <pagedata.hxx>
+#include <hiranges.hxx>
+#include <drawview.hxx>
+#include <drwlayer.hxx>
+#include <fusel.hxx>
+#include <seltrans.hxx>
+#include <scmod.hxx>
+#include <docsh.hxx>
+#include <viewuno.hxx>
+#include <postit.hxx>
+#include <spellcheckcontext.hxx>
+
+#include <vcl/settings.hxx>
+
+#include <comphelper/lok.hxx>
+#include <officecfg/Office/Calc.hxx>
+
+using namespace com::sun::star;
+
+void ScTabView::Init()
+{
+ /* RTL layout of the view windows is done manually, because it depends on
+ the sheet orientation, not the UI setting. Note: controls that are
+ already constructed (e.g. scroll bars) have the RTL setting of the GUI.
+ Eventually this has to be disabled manually (see below). */
+ pFrameWin->EnableRTL( false );
+
+ sal_uInt16 i;
+
+ mbInlineWithScrollbar = officecfg::Office::Calc::Layout::Other::TabbarInlineWithScrollbar::get();
+
+ aScrollTimer.SetTimeout(10);
+ aScrollTimer.SetInvokeHandler( LINK( this, ScTabView, TimerHdl ) );
+
+ for (i=0; i<4; i++)
+ pGridWin[i] = nullptr;
+ pGridWin[SC_SPLIT_BOTTOMLEFT] = VclPtr<ScGridWindow>::Create( pFrameWin, aViewData, SC_SPLIT_BOTTOMLEFT );
+
+ pSelEngine.reset( new ScViewSelectionEngine( pGridWin[SC_SPLIT_BOTTOMLEFT], this,
+ SC_SPLIT_BOTTOMLEFT ) );
+ aFunctionSet.SetSelectionEngine( pSelEngine.get() );
+
+ pHdrSelEng.reset( new ScHeaderSelectionEngine( pFrameWin, &aHdrFunc ) );
+
+ pColBar[SC_SPLIT_LEFT] = VclPtr<ScColBar>::Create( pFrameWin, SC_SPLIT_LEFT,
+ &aHdrFunc, pHdrSelEng.get(), this );
+ pColBar[SC_SPLIT_RIGHT] = nullptr;
+ pRowBar[SC_SPLIT_BOTTOM] = VclPtr<ScRowBar>::Create( pFrameWin, SC_SPLIT_BOTTOM,
+ &aHdrFunc, pHdrSelEng.get(), this );
+ pRowBar[SC_SPLIT_TOP] = nullptr;
+ for (i=0; i<2; i++)
+ pColOutline[i] = pRowOutline[i] = nullptr;
+
+ pHSplitter = VclPtr<ScTabSplitter>::Create( pFrameWin, WinBits( WB_HSCROLL ), &aViewData );
+ pVSplitter = VclPtr<ScTabSplitter>::Create( pFrameWin, WinBits( WB_VSCROLL ), &aViewData );
+
+ // SSA: override default keyboard step size to allow snap to row/column
+ pHSplitter->SetKeyboardStepSize( 1 );
+ pVSplitter->SetKeyboardStepSize( 1 );
+
+ pTabControl = VclPtr<ScTabControl>::Create(pFrameWin, &aViewData);
+ if (mbInlineWithScrollbar)
+ pTabControl->SetStyle(pTabControl->GetStyle() | WB_SIZEABLE);
+
+ /* #i97900# The tab control has to remain in RTL mode if GUI is RTL, this
+ is needed to draw the 3D effect correctly. The base TabBar implements
+ mirroring independent from the GUI direction. Have to set RTL mode
+ explicitly because the parent frame window is already RTL disabled. */
+ pTabControl->EnableRTL( AllSettings::GetLayoutRTL() );
+
+ InitScrollBar( *aHScrollLeft, aViewData.GetDocument().MaxCol()+1, LINK(this, ScTabView, HScrollLeftHdl) );
+ InitScrollBar( *aHScrollRight, aViewData.GetDocument().MaxCol()+1, LINK(this, ScTabView, HScrollRightHdl) );
+ InitScrollBar( *aVScrollTop, aViewData.GetDocument().MaxRow()+1, LINK(this, ScTabView, VScrollTopHdl) );
+ InitScrollBar( *aVScrollBottom, aViewData.GetDocument().MaxRow()+1, LINK(this, ScTabView, VScrollBottomHdl) );
+ /* #i97900# scrollbars remain in correct RTL mode, needed mirroring etc.
+ is now handled correctly at the respective places. */
+
+ // Don't show anything here, because still in wrong order
+ // Show is received from UpdateShow during first resize
+ // pTabControl, pGridWin, aHScrollLeft, aVScrollBottom,
+ // aCornerButton, pHSplitter, pVSplitter
+
+ // fragment
+
+ pHSplitter->SetSplitHdl( LINK( this, ScTabView, SplitHdl ) );
+ pVSplitter->SetSplitHdl( LINK( this, ScTabView, SplitHdl ) );
+
+ // UpdateShow is done during resize or a copy of an existing view from ctor
+
+ pDrawActual = nullptr;
+ pDrawOld = nullptr;
+
+ // DrawView cannot be create in the TabView - ctor
+ // when the ViewShell isn't constructed yet...
+ // The also applies to ViewOptionsHasChanged()
+
+ TestHintWindow();
+}
+
+ScTabView::~ScTabView()
+{
+ sal_uInt16 i;
+
+ // remove selection object
+ ScModule* pScMod = SC_MOD();
+ ScSelectionTransferObj* pOld = pScMod->GetSelectionTransfer();
+ if ( pOld && pOld->GetView() == this )
+ {
+ pOld->ForgetView();
+ pScMod->SetSelectionTransfer( nullptr );
+ TransferableHelper::ClearPrimarySelection(); // may delete pOld
+ }
+
+ pBrushDocument.reset();
+ pDrawBrushSet.reset();
+
+ pPageBreakData.reset();
+
+ delete pDrawActual;
+ pDrawActual = nullptr;
+ delete pDrawOld;
+ pDrawOld = nullptr;
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ ScTabViewShell* pThisViewShell = GetViewData().GetViewShell();
+
+ auto lRemoveWindows =
+ [pThisViewShell] (ScTabViewShell* pOtherViewShell)
+ {
+ ScViewData& rOtherViewData = pOtherViewShell->GetViewData();
+ for (int k = 0; k < 4; ++k)
+ {
+ if (rOtherViewData.HasEditView(static_cast<ScSplitPos>(k)))
+ pThisViewShell->RemoveWindowFromForeignEditView(pOtherViewShell, static_cast<ScSplitPos>(k));
+ }
+ };
+
+ SfxLokHelper::forEachOtherView(pThisViewShell, lRemoveWindows);
+ }
+
+ aViewData.KillEditView(); // as long as GridWins still exist
+
+ if (pDrawView)
+ {
+ for (i=0; i<4; i++)
+ if (pGridWin[i])
+ {
+ pDrawView->DeleteDeviceFromPaintView(*pGridWin[i]->GetOutDev());
+ }
+
+ pDrawView->HideSdrPage();
+ pDrawView.reset();
+ }
+
+ pSelEngine.reset();
+
+ if (mpSpellCheckCxt)
+ mpSpellCheckCxt->dispose();
+ mpSpellCheckCxt.reset();
+
+ mxInputHintOO.reset();
+ for (i=0; i<4; i++)
+ pGridWin[i].disposeAndClear();
+
+ pHdrSelEng.reset();
+
+ for (i=0; i<2; i++)
+ {
+ pColBar[i].disposeAndClear();
+ pRowBar[i].disposeAndClear();
+ pColOutline[i].disposeAndClear();
+ pRowOutline[i].disposeAndClear();
+ }
+
+ aCornerButton.disposeAndClear();
+ aTopButton.disposeAndClear();
+ aHScrollLeft.disposeAndClear();
+ aHScrollRight.disposeAndClear();
+ aVScrollTop.disposeAndClear();
+ aVScrollBottom.disposeAndClear();
+
+ pHSplitter.disposeAndClear();
+ pVSplitter.disposeAndClear();
+ pTabControl.disposeAndClear();
+}
+
+void ScTabView::MakeDrawView( TriState nForceDesignMode )
+{
+ if (pDrawView)
+ return;
+
+ ScDrawLayer* pLayer = aViewData.GetDocument().GetDrawLayer();
+ OSL_ENSURE(pLayer, "Where is the Draw Layer ??");
+
+ sal_uInt16 i;
+ pDrawView.reset( new ScDrawView( pGridWin[SC_SPLIT_BOTTOMLEFT]->GetOutDev(), &aViewData ) );
+ for (i=0; i<4; i++)
+ if (pGridWin[i])
+ {
+ if ( SC_SPLIT_BOTTOMLEFT != static_cast<ScSplitPos>(i) )
+ pDrawView->AddDeviceToPaintView(*pGridWin[i]->GetOutDev(), nullptr);
+ }
+ pDrawView->RecalcScale();
+ for (i=0; i<4; i++)
+ if (pGridWin[i])
+ {
+ pGridWin[i]->SetMapMode(pGridWin[i]->GetDrawMapMode());
+
+ pGridWin[i]->PaintImmediately(); // because of Invalidate in DrawView ctor (ShowPage),
+ // so that immediately can be drawn
+ }
+ SfxRequest aSfxRequest(SID_OBJECT_SELECT, SfxCallMode::SLOT, aViewData.GetViewShell()->GetPool());
+ SetDrawFuncPtr(new FuSelection(*aViewData.GetViewShell(), GetActiveWin(), pDrawView.get(),
+ pLayer,aSfxRequest));
+
+ // used when switching back from page preview: restore saved design mode state
+ // (otherwise, keep the default from the draw view ctor)
+ if ( nForceDesignMode != TRISTATE_INDET )
+ pDrawView->SetDesignMode( nForceDesignMode != TRISTATE_FALSE );
+
+ // register at FormShell
+ FmFormShell* pFormSh = aViewData.GetViewShell()->GetFormShell();
+ if (pFormSh)
+ pFormSh->SetView(pDrawView.get());
+
+ if (aViewData.GetViewShell()->HasAccessibilityObjects())
+ aViewData.GetViewShell()->BroadcastAccessibility(SfxHint(SfxHintId::ScAccMakeDrawLayer));
+}
+
+void ScTabView::DoAddWin( ScGridWindow* pWin )
+{
+ if (pDrawView)
+ {
+ pDrawView->AddDeviceToPaintView(*pWin->GetOutDev(), nullptr);
+ pWin->DrawLayerCreated();
+ }
+ pWin->SetAutoSpellContext(mpSpellCheckCxt);
+}
+
+void ScTabView::TabChanged( bool bSameTabButMoved )
+{
+ if (pDrawView)
+ {
+ DrawDeselectAll(); // end also text edit mode
+
+ SCTAB nTab = aViewData.GetTabNo();
+ pDrawView->HideSdrPage();
+ pDrawView->ShowSdrPage(pDrawView->GetModel().GetPage(nTab));
+
+ UpdateLayerLocks();
+
+ pDrawView->RecalcScale();
+ pDrawView->UpdateWorkArea(); // PageSize is different per page
+ }
+
+ SfxBindings& rBindings = aViewData.GetBindings();
+
+ // There is no easy way to invalidate all slots of the FormShell
+ // (for disabled slots on protected tables), therefore simply everything...
+ rBindings.InvalidateAll(false);
+
+ if (aViewData.GetViewShell()->HasAccessibilityObjects())
+ {
+ SfxHint aAccHint(SfxHintId::ScAccTableChanged);
+ aViewData.GetViewShell()->BroadcastAccessibility(aAccHint);
+ }
+
+ // notification for XActivationBroadcaster
+ SfxViewFrame& rViewFrame = aViewData.GetViewShell()->GetViewFrame();
+ uno::Reference<frame::XController> xController = rViewFrame.GetFrame().GetController();
+ if (xController.is())
+ {
+ ScTabViewObj* pImp = dynamic_cast<ScTabViewObj*>( xController.get() );
+ if (pImp)
+ pImp->SheetChanged( bSameTabButMoved );
+ }
+
+ for (int i = 0; i < 4; i++)
+ {
+ if (pGridWin[i])
+ {
+ pGridWin[i]->initiatePageBreaks();
+ // Trigger calculating page breaks only once.
+ break;
+ }
+ }
+
+ if (!comphelper::LibreOfficeKit::isActive())
+ return;
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScModelObj* pModelObj = pDocSh ? pDocSh->GetModel() : nullptr;
+
+ if (!pModelObj)
+ return;
+
+ Size aDocSize = pModelObj->getDocumentSize();
+ std::stringstream ss;
+ ss << aDocSize.Width() << ", " << aDocSize.Height();
+ OString sRect(ss.str());
+ ScTabViewShell* pViewShell = aViewData.GetViewShell();
+
+ // Invalidate first
+ tools::Rectangle aRectangle(0, 0, 1000000000, 1000000000);
+ pViewShell->libreOfficeKitViewInvalidateTilesCallback(&aRectangle, aViewData.GetTabNo(), 0);
+
+ ScModelObj* pModel = comphelper::getFromUnoTunnel<ScModelObj>(pViewShell->GetCurrentDocument());
+ SfxLokHelper::notifyDocumentSizeChanged(pViewShell, sRect, pModel, false);
+}
+
+void ScTabView::UpdateLayerLocks()
+{
+ if (!pDrawView)
+ return;
+
+ SCTAB nTab = aViewData.GetTabNo();
+ bool bEx = aViewData.GetViewShell()->IsDrawSelMode();
+ bool bProt = aViewData.GetDocument().IsTabProtected( nTab ) ||
+ aViewData.GetSfxDocShell()->IsReadOnly();
+ bool bShared = aViewData.GetDocShell()->IsDocShared();
+
+ SdrLayer* pLayer;
+ SdrLayerAdmin& rAdmin = pDrawView->GetModel().GetLayerAdmin();
+ pLayer = rAdmin.GetLayerPerID(SC_LAYER_BACK);
+ if (pLayer)
+ pDrawView->SetLayerLocked( pLayer->GetName(), bProt || !bEx || bShared );
+ pLayer = rAdmin.GetLayerPerID(SC_LAYER_INTERN);
+ if (pLayer)
+ pDrawView->SetLayerLocked( pLayer->GetName() );
+ pLayer = rAdmin.GetLayerPerID(SC_LAYER_FRONT);
+ if (pLayer)
+ pDrawView->SetLayerLocked( pLayer->GetName(), bProt || bShared );
+ pLayer = rAdmin.GetLayerPerID(SC_LAYER_CONTROLS);
+ if (pLayer)
+ pDrawView->SetLayerLocked( pLayer->GetName(), bProt || bShared );
+ pLayer = rAdmin.GetLayerPerID(SC_LAYER_HIDDEN);
+ if (pLayer)
+ {
+ pDrawView->SetLayerLocked( pLayer->GetName(), bProt || bShared );
+ pDrawView->SetLayerVisible( pLayer->GetName(), false);
+ }
+ pTabControl->SetAddButtonEnabled(aViewData.GetDocument().IsDocEditable());
+}
+
+void ScTabView::DrawDeselectAll()
+{
+ if (!pDrawView)
+ return;
+
+ ScTabViewShell* pViewSh = aViewData.GetViewShell();
+ if ( pDrawActual &&
+ ( pViewSh->IsDrawTextShell() || pDrawActual->GetSlotID() == SID_DRAW_NOTEEDIT ) )
+ {
+ // end text edit (as if escape pressed, in FuDraw)
+ aViewData.GetDispatcher().Execute( pDrawActual->GetSlotID(),
+ SfxCallMode::SLOT | SfxCallMode::RECORD );
+ }
+
+ pDrawView->ScEndTextEdit();
+ pDrawView->UnmarkAll();
+
+ if (!pViewSh->IsDrawSelMode())
+ pViewSh->SetDrawShell( false );
+}
+
+bool ScTabView::IsDrawTextEdit() const
+{
+ if (pDrawView)
+ return pDrawView->IsTextEdit();
+ else
+ return false;
+}
+
+SvxZoomType ScTabView::GetZoomType() const
+{
+ return aViewData.GetZoomType();
+}
+
+void ScTabView::SetZoomType( SvxZoomType eNew, bool bAll )
+{
+ aViewData.SetZoomType( eNew, bAll );
+}
+
+void ScTabView::SetZoom( const Fraction& rNewX, const Fraction& rNewY, bool bAll )
+{
+ aViewData.SetZoom( rNewX, rNewY, bAll );
+ if (pDrawView)
+ pDrawView->RecalcScale();
+ ZoomChanged();
+}
+
+void ScTabView::RefreshZoom()
+{
+ aViewData.RefreshZoom();
+ if (pDrawView)
+ pDrawView->RecalcScale();
+ ZoomChanged();
+}
+
+void ScTabView::SetPagebreakMode( bool bSet )
+{
+ aViewData.SetPagebreakMode(bSet);
+ if (pDrawView)
+ pDrawView->RecalcScale();
+ ZoomChanged();
+}
+
+void ScTabView::ResetDrawDragMode()
+{
+ if (pDrawView)
+ pDrawView->SetDragMode( SdrDragMode::Move );
+}
+
+void ScTabView::ViewOptionsHasChanged( bool bHScrollChanged, bool bGraphicsChanged )
+{
+ // create DrawView when grid should be displayed
+ if ( !pDrawView && aViewData.GetOptions().GetGridOptions().GetGridVisible() )
+ MakeDrawLayer();
+
+ if (pDrawView)
+ pDrawView->UpdateUserViewOptions();
+
+ if (bGraphicsChanged)
+ DrawEnableAnim(true); // DrawEnableAnim checks the options state
+
+ // if TabBar is set to visible, make sure its size is not 0
+ bool bGrow = ( aViewData.IsTabMode() && pTabControl->GetSizePixel().Width() <= 0 );
+
+ // if ScrollBar is set to visible, TabBar must make room
+ bool bShrink = ( bHScrollChanged && aViewData.IsTabMode() && aViewData.IsHScrollMode() &&
+ pTabControl->GetSizePixel().Width() > SC_TABBAR_DEFWIDTH );
+
+ if ( bGrow || bShrink )
+ {
+ Size aSize = pTabControl->GetSizePixel();
+ aSize.setWidth( SC_TABBAR_DEFWIDTH ); // initial size
+ pTabControl->SetSizePixel(aSize); // DoResize is called later...
+ }
+}
+
+// helper function against including the drawing layer
+
+void ScTabView::DrawMarkListHasChanged()
+{
+ if ( pDrawView )
+ pDrawView->MarkListHasChanged();
+}
+
+void ScTabView::UpdateAnchorHandles()
+{
+ if ( pDrawView )
+ pDrawView->AdjustMarkHdl();
+}
+
+void ScTabView::UpdateIMap( SdrObject* pObj )
+{
+ if ( pDrawView )
+ pDrawView->UpdateIMap( pObj );
+}
+
+void ScTabView::DrawEnableAnim(bool bSet)
+{
+ sal_uInt16 i;
+ if ( !pDrawView )
+ return;
+
+ // don't start animations if display of graphics is disabled
+ // graphics are controlled by VOBJ_TYPE_OLE
+ if ( bSet && aViewData.GetOptions().GetObjMode(VOBJ_TYPE_OLE) == VOBJ_MODE_SHOW )
+ {
+ if ( !pDrawView->IsAnimationEnabled() )
+ {
+ pDrawView->SetAnimationEnabled();
+
+ // animated GIFs must be restarted:
+ ScDocument& rDoc = aViewData.GetDocument();
+ for (i=0; i<4; i++)
+ if ( pGridWin[i] && pGridWin[i]->IsVisible() )
+ rDoc.StartAnimations( aViewData.GetTabNo() );
+ }
+ }
+ else
+ {
+ pDrawView->SetAnimationEnabled(false);
+ }
+}
+
+void ScTabView::UpdateDrawTextOutliner()
+{
+ if ( pDrawView )
+ {
+ Outliner* pOL = pDrawView->GetTextEditOutliner();
+ if (pOL)
+ aViewData.UpdateOutlinerFlags( *pOL );
+ }
+}
+
+void ScTabView::DigitLanguageChanged()
+{
+ LanguageType eNewLang = ScModule::GetOptDigitLanguage();
+ for (VclPtr<ScGridWindow> & pWin : pGridWin)
+ if ( pWin )
+ pWin->GetOutDev()->SetDigitLanguage( eNewLang );
+}
+
+void ScTabView::ScrollToObject( const SdrObject* pDrawObj )
+{
+ if ( pDrawObj )
+ {
+ // #i118524# use the BoundRect, this defines the visible area
+ MakeVisible(pDrawObj->GetCurrentBoundRect());
+ }
+}
+
+void ScTabView::MakeVisible( const tools::Rectangle& rHMMRect )
+{
+ vcl::Window* pWin = GetActiveWin();
+ Size aWinSize = pWin->GetOutputSizePixel();
+ SCTAB nTab = aViewData.GetTabNo();
+
+ tools::Rectangle aRect = pWin->LogicToPixel( rHMMRect );
+
+ tools::Long nScrollX=0, nScrollY=0; // pixel
+
+ if ( aRect.Right() >= aWinSize.Width() ) // right out
+ {
+ nScrollX = aRect.Right() - aWinSize.Width() + 1; // right border visible
+ if ( aRect.Left() < nScrollX )
+ nScrollX = aRect.Left(); // left visible (if too big)
+ }
+ if ( aRect.Bottom() >= aWinSize.Height() ) // bottom out
+ {
+ nScrollY = aRect.Bottom() - aWinSize.Height() + 1; // bottom border visible
+ if ( aRect.Top() < nScrollY )
+ nScrollY = aRect.Top(); // top visible (if too big)
+ }
+
+ if ( aRect.Left() < 0 ) // left out
+ nScrollX = aRect.Left(); // left border visible
+ if ( aRect.Top() < 0 ) // top out
+ nScrollY = aRect.Top(); // top border visible
+
+ if (!(nScrollX || nScrollY))
+ return;
+
+ ScDocument& rDoc = aViewData.GetDocument();
+ if ( rDoc.IsNegativePage( nTab ) )
+ nScrollX = -nScrollX;
+
+ double nPPTX = aViewData.GetPPTX();
+ double nPPTY = aViewData.GetPPTY();
+ ScSplitPos eWhich = aViewData.GetActivePart();
+ SCCOL nPosX = aViewData.GetPosX(WhichH(eWhich));
+ SCROW nPosY = aViewData.GetPosY(WhichV(eWhich));
+
+ tools::Long nLinesX=0, nLinesY=0; // columns/rows - scroll at least nScrollX/Y
+
+ if (nScrollX > 0)
+ while (nScrollX > 0 && nPosX < rDoc.MaxCol())
+ {
+ nScrollX -= static_cast<tools::Long>( rDoc.GetColWidth(nPosX, nTab) * nPPTX );
+ ++nPosX;
+ ++nLinesX;
+ }
+ else if (nScrollX < 0)
+ while (nScrollX < 0 && nPosX > 0)
+ {
+ --nPosX;
+ nScrollX += static_cast<tools::Long>( rDoc.GetColWidth(nPosX, nTab) * nPPTX );
+ --nLinesX;
+ }
+
+ if (nScrollY > 0)
+ while (nScrollY > 0 && nPosY < rDoc.MaxRow())
+ {
+ nScrollY -= static_cast<tools::Long>( rDoc.GetRowHeight(nPosY, nTab) * nPPTY );
+ ++nPosY;
+ ++nLinesY;
+ }
+ else if (nScrollY < 0)
+ while (nScrollY < 0 && nPosY > 0)
+ {
+ --nPosY;
+ nScrollY += static_cast<tools::Long>( rDoc.GetRowHeight(nPosY, nTab) * nPPTY );
+ --nLinesY;
+ }
+
+ ScrollLines( nLinesX, nLinesY ); // execute
+}
+
+void ScTabView::SetBrushDocument( ScDocumentUniquePtr pNew, bool bLock )
+{
+ pDrawBrushSet.reset();
+ pBrushDocument = std::move(pNew);
+
+ bLockPaintBrush = bLock;
+
+ aViewData.GetBindings().Invalidate(SID_FORMATPAINTBRUSH);
+}
+
+void ScTabView::SetDrawBrushSet( std::unique_ptr<SfxItemSet> pNew, bool bLock )
+{
+ pBrushDocument.reset();
+ pDrawBrushSet = std::move(pNew);
+
+ bLockPaintBrush = bLock;
+
+ aViewData.GetBindings().Invalidate(SID_FORMATPAINTBRUSH);
+}
+
+void ScTabView::ResetBrushDocument()
+{
+ if ( HasPaintBrush() )
+ {
+ SetBrushDocument( nullptr, false );
+ SetActivePointer( aViewData.IsThemedCursor() ? PointerStyle::FatCross : PointerStyle::Arrow ); // switch pointers also when ended with escape key
+ }
+}
+
+void ScTabView::OnLOKNoteStateChanged(const ScPostIt* pNote)
+{
+ if (!comphelper::LibreOfficeKit::isActive())
+ return;
+
+ const SdrCaptionObj* pCaption = pNote->GetCaption();
+ if (!pCaption) return;
+
+ tools::Rectangle aRect = pCaption->GetLogicRect();
+ basegfx::B2DRange aTailRange = pCaption->getTailPolygon().getB2DRange();
+ tools::Rectangle aTailRect(aTailRange.getMinX(), aTailRange.getMinY(),
+ aTailRange.getMaxX(), aTailRange.getMaxY());
+ aRect.Union( aTailRect );
+
+ // This is a temporary workaround: sometime in tiled rendering mode
+ // the tip of the note arrow is misplaced by a fixed offset.
+ // The value used below is enough to get the tile, where the arrow tip is
+ // placed, invalidated.
+ const int nBorderSize = 200;
+ tools::Rectangle aInvalidRect = aRect;
+ aInvalidRect.AdjustLeft( -nBorderSize );
+ aInvalidRect.AdjustRight( nBorderSize );
+ aInvalidRect.AdjustTop( -nBorderSize );
+ aInvalidRect.AdjustBottom( nBorderSize );
+
+ SfxViewShell* pCurrentViewShell = SfxViewShell::Current();
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
+ if (pTabViewShell && pViewShell->GetDocId() == pCurrentViewShell->GetDocId())
+ {
+ for (auto& pWin: pTabViewShell->pGridWin)
+ {
+ if (pWin && pWin->IsVisible())
+ {
+ pWin->Invalidate(aInvalidRect);
+ }
+ }
+ }
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabvwsh.cxx b/sc/source/ui/view/tabvwsh.cxx
new file mode 100644
index 0000000000..f64b960485
--- /dev/null
+++ b/sc/source/ui/view/tabvwsh.cxx
@@ -0,0 +1,121 @@
+/* -*- 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 .
+ */
+
+#include <svx/imapdlg.hxx>
+#include <svx/srchdlg.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/infobar.hxx>
+#include <sfx2/sidebar/SidebarChildWindow.hxx>
+#include <sfx2/devtools/DevelopmentToolChildWindow.hxx>
+#include <sfx2/viewfac.hxx>
+
+#include <cellvalue.hxx>
+
+#include <tabvwsh.hxx>
+#include <docsh.hxx>
+#include <reffact.hxx>
+#include <sc.hrc>
+#include <spelldialog.hxx>
+#include <formulacell.hxx>
+#include <searchresults.hxx>
+
+ // needed for -fsanitize=function visibility of typeinfo for functions of
+ // type void(SfxShell*,SfxRequest&) defined in scslots.hxx
+#define ShellClass_ScTabViewShell
+#include <scslots.hxx>
+
+
+SFX_IMPL_INTERFACE(ScTabViewShell, SfxViewShell)
+
+void ScTabViewShell::InitInterface_Impl()
+{
+ GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_TOOLS,
+ SfxVisibilityFlags::Standard | SfxVisibilityFlags::FullScreen | SfxVisibilityFlags::Server,
+ ToolbarId::Objectbar_Tools);
+
+ GetStaticInterface()->RegisterChildWindow(FID_INPUTLINE_STATUS);
+ GetStaticInterface()->RegisterChildWindow(SfxInfoBarContainerChild::GetChildWindowId());
+
+ GetStaticInterface()->RegisterChildWindow(SID_NAVIGATOR, true);
+
+ GetStaticInterface()->RegisterChildWindow(::sfx2::sidebar::SidebarChildWindow::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(DevelopmentToolChildWindow::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScNameDlgWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScNameDefDlgWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScSolverDlgWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScOptSolverDlgWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScXMLSourceDlgWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScPivotLayoutWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScTabOpDlgWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScFilterDlgWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScSpecialFilterDlgWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScDbNameDlgWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScConsolidateDlgWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScPrintAreasDlgWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScColRowNameRangesDlgWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScFormulaDlgWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(SvxIMapDlgChildWindow::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScFormulaDlgWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScAcceptChgDlgWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScHighlightChgDlgWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScSimpleRefDlgWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(SvxSearchDialogWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(SID_HYPERLINK_DIALOG);
+ GetStaticInterface()->RegisterChildWindow(ScSpellDialogChildWindow::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScValidityRefChildWin::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(sc::SearchResultsDlgWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(sc::ConditionalFormatEasyDialogWrapper::GetChildWindowId());
+
+ GetStaticInterface()->RegisterChildWindow(ScRandomNumberGeneratorDialogWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScSamplingDialogWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScDescriptiveStatisticsDialogWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScAnalysisOfVarianceDialogWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScCorrelationDialogWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScCovarianceDialogWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScExponentialSmoothingDialogWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScMovingAverageDialogWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScRegressionDialogWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScTTestDialogWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScFTestDialogWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScZTestDialogWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScChiSquareTestDialogWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScFourierAnalysisDialogWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScCondFormatDlgWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(sc::SparklineDialogWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(sc::SparklineDataRangeDialogWrapper::GetChildWindowId());
+}
+
+SFX_IMPL_NAMED_VIEWFACTORY( ScTabViewShell, "Default" )
+{
+ SFX_VIEW_REGISTRATION(ScDocShell);
+}
+
+OUString ScTabViewShell::GetFormula(const ScAddress& rAddress)
+{
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScRefCellValue aCell(rDoc, rAddress);
+ if (!aCell.isEmpty() && aCell.getType() == CELLTYPE_FORMULA)
+ {
+ return aCell.getFormula()->GetFormula();
+ }
+ return OUString();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabvwsh2.cxx b/sc/source/ui/view/tabvwsh2.cxx
new file mode 100644
index 0000000000..b2fef44d96
--- /dev/null
+++ b/sc/source/ui/view/tabvwsh2.cxx
@@ -0,0 +1,472 @@
+/* -*- 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 .
+ */
+
+#include <comphelper/lok.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <svl/whiter.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <svl/cjkoptions.hxx>
+#include <sfx2/dispatch.hxx>
+#include <tools/UnitConversion.hxx>
+
+#include <tabvwsh.hxx>
+#include <drawview.hxx>
+#include <fupoor.hxx>
+#include <fuconrec.hxx>
+#include <fuconpol.hxx>
+#include <fuconarc.hxx>
+#include <fuconuno.hxx>
+#include <fusel.hxx>
+#include <futext.hxx>
+#include <fuinsert.hxx>
+#include <sc.hrc>
+#include <scmod.hxx>
+#include <appoptio.hxx>
+#include <gridwin.hxx>
+
+// Create default drawing objects via keyboard
+#include <svx/svdpagv.hxx>
+#include <svl/stritem.hxx>
+#include <fuconcustomshape.hxx>
+
+SdrView* ScTabViewShell::GetDrawView() const
+{
+ return const_cast<ScTabViewShell*>(this)->GetScDrawView(); // GetScDrawView is non-const
+}
+
+void ScTabViewShell::WindowChanged()
+{
+ vcl::Window* pWin = GetActiveWin();
+
+ ScDrawView* pDrView = GetScDrawView();
+ if (pDrView)
+ pDrView->SetActualWin(pWin->GetOutDev());
+
+ FuPoor* pFunc = GetDrawFuncPtr();
+ if (pFunc)
+ pFunc->SetWindow(pWin);
+
+ // when font from InputContext is used,
+ // this must be moved to change of cursor position:
+ UpdateInputContext();
+}
+
+void ScTabViewShell::ExecDraw(SfxRequest& rReq)
+{
+ SC_MOD()->InputEnterHandler();
+ UpdateInputHandler();
+
+ MakeDrawLayer();
+
+ ScTabView* pTabView = GetViewData().GetView();
+ SfxBindings& rBindings = GetViewFrame().GetBindings();
+
+ vcl::Window* pWin = pTabView->GetActiveWin();
+ ScDrawView* pView = pTabView->GetScDrawView();
+ SdrModel& rModel = pView->GetModel();
+
+ const SfxItemSet *pArgs = rReq.GetArgs();
+ sal_uInt16 nNewId = rReq.GetSlot();
+
+ if ( nNewId == SID_DRAW_CHART )
+ {
+ // #i71254# directly insert a chart instead of drawing its output rectangle
+ FuInsertChart(*this, pWin, pView, &rModel, rReq, LINK( this, ScTabViewShell, DialogClosedHdl ));
+ return;
+ }
+
+ if ( nNewId == SID_DRAW_SELECT )
+ nNewId = SID_OBJECT_SELECT;
+
+ SdrObjKind eNewFormObjKind = SdrObjKind::NONE;
+ if (nNewId == SID_FM_CREATE_CONTROL)
+ {
+ const SfxUInt16Item* pIdentifierItem = rReq.GetArg<SfxUInt16Item>(SID_FM_CONTROL_IDENTIFIER);
+ if (pIdentifierItem)
+ eNewFormObjKind = static_cast<SdrObjKind>(pIdentifierItem->GetValue());
+ }
+
+ OUString sStringItemValue;
+ if ( pArgs )
+ {
+ const SfxPoolItem* pItem;
+ if ( pArgs->GetItemState( nNewId, true, &pItem ) == SfxItemState::SET )
+ if (auto pStringItem = dynamic_cast<const SfxStringItem*>(pItem) )
+ sStringItemValue = pStringItem->GetValue();
+ }
+ bool bSwitchCustom = ( !sStringItemValue.isEmpty() && !sDrawCustom.isEmpty() && sStringItemValue != sDrawCustom );
+
+ if (nNewId == SID_INSERT_FRAME) // from Tbx button
+ nNewId = SID_DRAW_TEXT;
+
+ // CTRL-SID_OBJECT_SELECT is used to select the first object,
+ // but not if SID_OBJECT_SELECT is the result of clicking a create function again,
+ // so this must be tested before changing nNewId below.
+ bool bSelectFirst = ( nNewId == SID_OBJECT_SELECT && (rReq.GetModifier() & KEY_MOD1) );
+
+ bool bEx = IsDrawSelMode();
+ if ( rReq.GetModifier() & KEY_MOD1 )
+ {
+ // always allow keyboard selection also on background layer
+ // also allow creation of default objects if the same object type
+ // was already active
+ bEx = true;
+ }
+ else if ( nNewId == nDrawSfxId && ( nNewId != SID_FM_CREATE_CONTROL ||
+ eNewFormObjKind == eFormObjKind || eNewFormObjKind == SdrObjKind::NONE ) && !bSwitchCustom )
+ {
+ // #i52871# if a different custom shape is selected, the slot id can be the same,
+ // so the custom shape type string has to be compared, too.
+
+ // SID_FM_CREATE_CONTROL with eNewFormObjKind==OBJ_NONE (without parameter) comes
+ // from FuConstruct::SimpleMouseButtonUp when deactivating
+ // Execute for the form shell, to deselect the controller
+ if ( nNewId == SID_FM_CREATE_CONTROL )
+ {
+ GetViewData().GetDispatcher().Execute(SID_FM_LEAVE_CREATE);
+ GetViewFrame().GetBindings().InvalidateAll(false);
+ //! what kind of slot does the weird controller really need to display this????
+ }
+
+ bEx = !bEx;
+ nNewId = SID_OBJECT_SELECT;
+ }
+ else
+ bEx = true;
+
+ if ( nDrawSfxId == SID_FM_CREATE_CONTROL && nNewId != nDrawSfxId )
+ {
+ // switching from control- to paint function -> deselect in control-controller
+ GetViewData().GetDispatcher().Execute(SID_FM_LEAVE_CREATE);
+ GetViewFrame().GetBindings().InvalidateAll(false);
+ //! what kind of slot does the weird controller really need to display this????
+ }
+
+ SetDrawSelMode(bEx);
+
+ pView->LockBackgroundLayer( !bEx );
+
+ if ( bSelectFirst )
+ {
+ // select first draw object if none is selected yet
+ if(!pView->AreObjectsMarked())
+ {
+ // select first object
+ pView->UnmarkAllObj();
+ pView->MarkNextObj(true);
+
+ // ...and make it visible
+ if(pView->AreObjectsMarked())
+ pView->MakeVisible(pView->GetAllMarkedRect(), *pWin);
+ }
+ }
+
+ nDrawSfxId = nNewId;
+ sDrawCustom.clear(); // value is set below for custom shapes
+
+ if (nNewId == SID_DRAW_TEXT || nNewId == SID_DRAW_TEXT_VERTICAL
+ || nNewId == SID_DRAW_TEXT_MARQUEE || nNewId == SID_DRAW_NOTEEDIT)
+ SetDrawTextShell(true);
+ else
+ {
+ if (bEx || pView->GetMarkedObjectList().GetMarkCount() != 0)
+ SetDrawShellOrSub();
+ else
+ SetDrawShell(false);
+ }
+
+ if (pTabView->GetDrawFuncPtr())
+ {
+ if (pTabView->GetDrawFuncOldPtr() != pTabView->GetDrawFuncPtr())
+ delete pTabView->GetDrawFuncOldPtr();
+
+ pTabView->GetDrawFuncPtr()->Deactivate();
+ pTabView->SetDrawFuncOldPtr(pTabView->GetDrawFuncPtr());
+ pTabView->SetDrawFuncPtr(nullptr);
+ }
+
+ SfxRequest aNewReq(rReq);
+ aNewReq.SetSlot(nDrawSfxId);
+
+ assert(nNewId != SID_DRAW_CHART); //#i71254# handled already above
+
+ // for LibreOfficeKit - choosing a shape should construct it directly
+ bool bCreateDirectly = false;
+
+ switch (nNewId)
+ {
+ case SID_OBJECT_SELECT:
+ // not always switch back
+ if(pView->GetMarkedObjectList().GetMarkCount() == 0) SetDrawShell(bEx);
+ pTabView->SetDrawFuncPtr(new FuSelection(*this, pWin, pView, &rModel, aNewReq));
+ break;
+
+ case SID_DRAW_LINE:
+ case SID_DRAW_XLINE:
+ case SID_LINE_ARROW_END:
+ case SID_LINE_ARROW_CIRCLE:
+ case SID_LINE_ARROW_SQUARE:
+ case SID_LINE_ARROW_START:
+ case SID_LINE_CIRCLE_ARROW:
+ case SID_LINE_SQUARE_ARROW:
+ case SID_LINE_ARROWS:
+ case SID_DRAW_RECT:
+ case SID_DRAW_ELLIPSE:
+ case SID_DRAW_MEASURELINE:
+ pTabView->SetDrawFuncPtr(new FuConstRectangle(*this, pWin, pView, &rModel, aNewReq));
+ bCreateDirectly = comphelper::LibreOfficeKit::isActive();
+ break;
+
+ case SID_DRAW_CAPTION:
+ case SID_DRAW_CAPTION_VERTICAL:
+ pTabView->SetDrawFuncPtr(new FuConstRectangle(*this, pWin, pView, &rModel, aNewReq));
+ pView->SetFrameDragSingles( false );
+ rBindings.Invalidate( SID_BEZIER_EDIT );
+ break;
+
+ case SID_DRAW_XPOLYGON:
+ case SID_DRAW_XPOLYGON_NOFILL:
+ case SID_DRAW_POLYGON:
+ case SID_DRAW_POLYGON_NOFILL:
+ case SID_DRAW_BEZIER_NOFILL:
+ case SID_DRAW_BEZIER_FILL:
+ case SID_DRAW_FREELINE:
+ case SID_DRAW_FREELINE_NOFILL:
+ pTabView->SetDrawFuncPtr(new FuConstPolygon(*this, pWin, pView, &rModel, aNewReq));
+ break;
+
+ case SID_DRAW_ARC:
+ case SID_DRAW_PIE:
+ case SID_DRAW_CIRCLECUT:
+ pTabView->SetDrawFuncPtr(new FuConstArc(*this, pWin, pView, &rModel, aNewReq));
+ break;
+
+ case SID_DRAW_TEXT:
+ case SID_DRAW_TEXT_VERTICAL:
+ case SID_DRAW_TEXT_MARQUEE:
+ case SID_DRAW_NOTEEDIT:
+ pTabView->SetDrawFuncPtr(new FuText(*this, pWin, pView, &rModel, aNewReq));
+ bCreateDirectly = comphelper::LibreOfficeKit::isActive();
+ break;
+
+ case SID_FM_CREATE_CONTROL:
+ SetDrawFormShell(true);
+ pTabView->SetDrawFuncPtr(new FuConstUnoControl(*this, pWin, pView, &rModel, aNewReq));
+ eFormObjKind = eNewFormObjKind;
+ break;
+
+ case SID_DRAWTBX_CS_BASIC :
+ case SID_DRAWTBX_CS_SYMBOL :
+ case SID_DRAWTBX_CS_ARROW :
+ case SID_DRAWTBX_CS_FLOWCHART :
+ case SID_DRAWTBX_CS_CALLOUT :
+ case SID_DRAWTBX_CS_STAR :
+ case SID_DRAW_CS_ID :
+ {
+ pTabView->SetDrawFuncPtr(new FuConstCustomShape(*this, pWin, pView, &rModel, aNewReq));
+
+ bCreateDirectly = comphelper::LibreOfficeKit::isActive();
+
+ if ( nNewId != SID_DRAW_CS_ID )
+ {
+ const SfxStringItem* pEnumCommand = rReq.GetArg<SfxStringItem>(nNewId);
+ if ( pEnumCommand )
+ {
+ SfxBindings& rBind = GetViewFrame().GetBindings();
+ rBind.Invalidate( nNewId );
+ rBind.Update( nNewId );
+
+ sDrawCustom = pEnumCommand->GetValue(); // to detect when a different shape type is selected
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (pTabView->GetDrawFuncPtr())
+ pTabView->GetDrawFuncPtr()->Activate();
+
+ rReq.Done();
+
+ Invalidate();
+
+ // Create default drawing objects via keyboard
+ // with qualifier construct directly
+ FuPoor* pFuActual = GetDrawFuncPtr();
+
+ if(!(pFuActual && ((rReq.GetModifier() & KEY_MOD1) || bCreateDirectly)))
+ return;
+
+ // Create default drawing objects via keyboard
+ const ScAppOptions& rAppOpt = SC_MOD()->GetAppOptions();
+ sal_uInt32 nDefaultObjectSizeWidth = rAppOpt.GetDefaultObjectSizeWidth();
+ sal_uInt32 nDefaultObjectSizeHeight = rAppOpt.GetDefaultObjectSizeHeight();
+
+ // calc position and size
+ bool bLOKIsActive = comphelper::LibreOfficeKit::isActive();
+ Point aInsertPos;
+ if(!bLOKIsActive)
+ {
+ tools::Rectangle aVisArea = pWin->PixelToLogic(tools::Rectangle(Point(0,0), pWin->GetOutputSizePixel()));
+ aInsertPos = aVisArea.Center();
+ aInsertPos.AdjustX( -sal_Int32(nDefaultObjectSizeWidth / 2) );
+ aInsertPos.AdjustY( -sal_Int32(nDefaultObjectSizeHeight / 2) );
+ }
+ else
+ {
+ ScViewData& rViewData = GetViewData();
+ tools::Long nLayoutSign = rViewData.GetDocument().IsLayoutRTL(rViewData.GetTabNo()) ? -1 : 1;
+ aInsertPos = rViewData.getLOKVisibleArea().Center();
+ if (comphelper::LibreOfficeKit::isCompatFlagSet(
+ comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
+ aInsertPos = rViewData.GetPrintTwipsPosFromTileTwips(aInsertPos);
+
+ aInsertPos.setX(nLayoutSign * convertTwipToMm100(aInsertPos.X()));
+ aInsertPos.setY(convertTwipToMm100(aInsertPos.Y()));
+
+ aInsertPos.AdjustX( -sal_Int32(nDefaultObjectSizeWidth / 2) );
+ aInsertPos.AdjustY( -sal_Int32(nDefaultObjectSizeHeight / 2) );
+ }
+
+ tools::Rectangle aNewObjectRectangle(aInsertPos, Size(nDefaultObjectSizeWidth, nDefaultObjectSizeHeight));
+
+ ScDrawView* pDrView = GetScDrawView();
+
+ if(!pDrView)
+ return;
+
+ SdrPageView* pPageView = pDrView->GetSdrPageView();
+
+ if(!pPageView)
+ return;
+
+ // create the default object
+ rtl::Reference<SdrObject> pObj = pFuActual->CreateDefaultObject(nNewId, aNewObjectRectangle);
+
+ if(!pObj)
+ return;
+
+ // insert into page
+ pView->InsertObjectAtView(pObj.get(), *pPageView);
+
+ switch ( nNewId )
+ {
+ case SID_DRAW_CAPTION:
+ case SID_DRAW_CAPTION_VERTICAL:
+ case SID_DRAW_TEXT:
+ case SID_DRAW_TEXT_VERTICAL:
+ // use KeyInput to start edit mode (FuText is created).
+ // For FuText objects, edit mode is handled within CreateDefaultObject.
+ // KEY_F2 is handled in FuDraw::KeyInput.
+
+ pFuActual->KeyInput( KeyEvent( 0, vcl::KeyCode( KEY_F2 ) ) );
+ break;
+ default:
+ break;
+ }
+}
+
+void ScTabViewShell::GetDrawState(SfxItemSet &rSet)
+{
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+
+ while ( nWhich )
+ {
+ switch ( nWhich )
+ {
+ case SID_DRAW_CHART:
+ {
+ bool bOle = GetViewFrame().GetFrame().IsInPlace();
+ if ( bOle || !SvtModuleOptions().IsChart() )
+ rSet.DisableItem( nWhich );
+ }
+ break;
+
+ case SID_DRAW_LINE:
+ case SID_DRAW_XLINE:
+ case SID_LINE_ARROW_END:
+ case SID_LINE_ARROW_CIRCLE:
+ case SID_LINE_ARROW_SQUARE:
+ case SID_LINE_ARROW_START:
+ case SID_LINE_CIRCLE_ARROW:
+ case SID_LINE_SQUARE_ARROW:
+ case SID_LINE_ARROWS:
+ case SID_DRAW_MEASURELINE:
+ case SID_DRAW_RECT:
+ case SID_DRAW_ELLIPSE:
+ case SID_DRAW_POLYGON:
+ case SID_DRAW_POLYGON_NOFILL:
+ case SID_DRAW_XPOLYGON:
+ case SID_DRAW_XPOLYGON_NOFILL:
+ case SID_DRAW_BEZIER_FILL:
+ case SID_DRAW_BEZIER_NOFILL:
+ case SID_DRAW_FREELINE:
+ case SID_DRAW_FREELINE_NOFILL:
+ case SID_DRAW_ARC:
+ case SID_DRAW_PIE:
+ case SID_DRAW_CIRCLECUT:
+ case SID_DRAW_TEXT:
+ case SID_DRAW_TEXT_MARQUEE:
+ case SID_DRAW_CAPTION:
+ rSet.Put( SfxBoolItem( nWhich, nDrawSfxId == nWhich ) );
+ break;
+
+ case SID_DRAW_TEXT_VERTICAL:
+ case SID_DRAW_CAPTION_VERTICAL:
+ if ( !SvtCJKOptions::IsVerticalTextEnabled() )
+ rSet.DisableItem( nWhich );
+ else
+ rSet.Put( SfxBoolItem( nWhich, nDrawSfxId == nWhich ) );
+ break;
+
+ case SID_OBJECT_SELECT: // important for the old control-controller
+ rSet.Put( SfxBoolItem( nWhich, nDrawSfxId == SID_OBJECT_SELECT && IsDrawSelMode() ) );
+ break;
+
+ case SID_DRAWTBX_CS_BASIC:
+ case SID_DRAWTBX_CS_SYMBOL:
+ case SID_DRAWTBX_CS_ARROW:
+ case SID_DRAWTBX_CS_FLOWCHART:
+ case SID_DRAWTBX_CS_CALLOUT:
+ case SID_DRAWTBX_CS_STAR:
+ rSet.Put( SfxStringItem( nWhich, nDrawSfxId == nWhich ? sDrawCustom : OUString() ) );
+ break;
+ }
+ nWhich = aIter.NextWhich();
+ }
+}
+
+bool ScTabViewShell::SelectObject( std::u16string_view rName )
+{
+ ScDrawView* pView = GetViewData().GetScDrawView();
+ if (!pView)
+ return false;
+
+ bool bFound = pView->SelectObject( rName );
+ // DrawShell etc. is handled in MarkListHasChanged
+
+ return bFound;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabvwsh3.cxx b/sc/source/ui/view/tabvwsh3.cxx
new file mode 100644
index 0000000000..208748b711
--- /dev/null
+++ b/sc/source/ui/view/tabvwsh3.cxx
@@ -0,0 +1,1398 @@
+/* -*- 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 .
+ */
+
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/passwd.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/sidebar/Sidebar.hxx>
+#include <svl/ptitem.hxx>
+#include <svl/stritem.hxx>
+#include <tools/urlobj.hxx>
+#include <sfx2/objface.hxx>
+#include <vcl/vclenum.hxx>
+#include <vcl/uitest/logger.hxx>
+#include <vcl/uitest/eventdescription.hxx>
+
+#include <globstr.hrc>
+#include <strings.hrc>
+#include <scmod.hxx>
+#include <appoptio.hxx>
+#include <tabvwsh.hxx>
+#include <document.hxx>
+#include <sc.hrc>
+#include <helpids.h>
+#include <inputwin.hxx>
+#include <scresid.hxx>
+#include <docsh.hxx>
+#include <rangeutl.hxx>
+#include <reffact.hxx>
+#include <tabprotection.hxx>
+#include <protectiondlg.hxx>
+#include <markdata.hxx>
+
+#include <svl/ilstitem.hxx>
+#include <vector>
+
+#include <svx/zoomslideritem.hxx>
+#include <svx/svxdlg.hxx>
+#include <comphelper/lok.hxx>
+#include <comphelper/string.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <scabstdlg.hxx>
+#include <officecfg/Office/Calc.hxx>
+
+#include <basegfx/utils/zoomtools.hxx>
+
+#include <svx/svdpagv.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/dialog/ThemeDialog.hxx>
+#include <ThemeColorChanger.hxx>
+
+namespace
+{
+ void collectUIInformation(const OUString& aZoom)
+ {
+ EventDescription aDescription;
+ aDescription.aID = "grid_window";
+ aDescription.aParameters = {{"ZOOM", aZoom}};
+ aDescription.aAction = "SET";
+ aDescription.aKeyWord = "ScGridWinUIObject";
+ aDescription.aParent = "MainWindow";
+ UITestLogger::getInstance().logEvent(aDescription);
+ }
+
+ enum class DetectFlags
+ {
+ NONE,
+ RANGE,
+ ADDRESS
+ };
+
+ struct ScRefFlagsAndType
+ {
+ ScRefFlags nResult;
+ DetectFlags eDetected;
+ };
+
+ ScRefFlagsAndType lcl_ParseRangeOrAddress(ScRange& rScRange, ScAddress& rScAddress,
+ const OUString& aAddress, const ScDocument& rDoc,
+ SCCOL nCurCol, SCROW nCurRow)
+ {
+ ScRefFlagsAndType aRet;
+
+ // Relative address parsing needs current position.
+ // row,col parameters, not col,row!
+ ScAddress::Details aDetails( rDoc.GetAddressConvention(), nCurRow, nCurCol);
+
+ // start with the address convention set in the document
+ aRet.nResult = rScRange.Parse(aAddress, rDoc, aDetails);
+ if (aRet.nResult & ScRefFlags::VALID)
+ {
+ aRet.eDetected = DetectFlags::RANGE;
+ return aRet;
+ }
+
+ aRet.nResult = rScAddress.Parse(aAddress, rDoc, aDetails);
+ if (aRet.nResult & ScRefFlags::VALID)
+ {
+ aRet.eDetected = DetectFlags::ADDRESS;
+ return aRet;
+ }
+
+ // try the default Calc (A1) address convention
+ aRet.nResult = rScRange.Parse(aAddress, rDoc);
+ if (aRet.nResult & ScRefFlags::VALID)
+ {
+ aRet.eDetected = DetectFlags::RANGE;
+ return aRet;
+ }
+
+ aRet.nResult = rScAddress.Parse(aAddress, rDoc);
+ if (aRet.nResult & ScRefFlags::VALID)
+ {
+ aRet.eDetected = DetectFlags::ADDRESS;
+ return aRet;
+ }
+
+ // try the Excel A1 address convention
+ aRet.nResult = rScRange.Parse(aAddress, rDoc, formula::FormulaGrammar::CONV_XL_A1);
+ if (aRet.nResult & ScRefFlags::VALID)
+ {
+ aRet.eDetected = DetectFlags::RANGE;
+ return aRet;
+ }
+
+ // try the Excel A1 address convention
+ aRet.nResult = rScAddress.Parse(aAddress, rDoc, formula::FormulaGrammar::CONV_XL_A1);
+ if (aRet.nResult & ScRefFlags::VALID)
+ {
+ aRet.eDetected = DetectFlags::ADDRESS;
+ return aRet;
+ }
+
+ // try Excel R1C1 address convention
+ aDetails.eConv = formula::FormulaGrammar::CONV_XL_R1C1;
+ aRet.nResult = rScRange.Parse(aAddress, rDoc, aDetails);
+ if (aRet.nResult & ScRefFlags::VALID)
+ {
+ aRet.eDetected = DetectFlags::RANGE;
+ return aRet;
+ }
+
+ aRet.nResult = rScAddress.Parse(aAddress, rDoc, aDetails);
+ if (aRet.nResult & ScRefFlags::VALID)
+ {
+ aRet.eDetected = DetectFlags::ADDRESS;
+ return aRet;
+ }
+
+ aRet.nResult = ScRefFlags::ZERO;
+ aRet.eDetected = DetectFlags::NONE;
+
+ return aRet;
+ }
+}
+
+void ScTabViewShell::Execute( SfxRequest& rReq )
+{
+ SfxViewFrame& rThisFrame = GetViewFrame();
+ SfxBindings& rBindings = rThisFrame.GetBindings();
+ ScModule* pScMod = SC_MOD();
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+ sal_uInt16 nSlot = rReq.GetSlot();
+
+ if (nSlot != SID_CURRENTCELL) // comes with MouseButtonUp
+ HideListBox(); // Autofilter-DropDown-Listbox
+
+ switch ( nSlot )
+ {
+ case FID_INSERT_FILE:
+ {
+ const SfxPoolItem* pItem;
+ if ( pReqArgs &&
+ pReqArgs->GetItemState(FID_INSERT_FILE,true,&pItem) == SfxItemState::SET )
+ {
+ OUString aFileName = static_cast<const SfxStringItem*>(pItem)->GetValue();
+
+ // insert position
+
+ Point aInsertPos;
+ if ( pReqArgs->GetItemState(FN_PARAM_1,true,&pItem) == SfxItemState::SET )
+ aInsertPos = static_cast<const SfxPointItem*>(pItem)->GetValue();
+ else
+ aInsertPos = GetInsertPos();
+
+ // as Link?
+
+ bool bAsLink = false;
+ if ( pReqArgs->GetItemState(FN_PARAM_2,true,&pItem) == SfxItemState::SET )
+ bAsLink = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+
+ // execute
+
+ PasteFile( aInsertPos, aFileName, bAsLink );
+ }
+ }
+ break;
+
+ case SID_OPENDLG_EDIT_PRINTAREA:
+ {
+ sal_uInt16 nId = ScPrintAreasDlgWrapper::GetChildWindowId();
+ SfxChildWindow* pWnd = rThisFrame.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ }
+ break;
+
+ case SID_CHANGE_PRINTAREA:
+ {
+ if ( pReqArgs ) // OK from dialog
+ {
+ OUString aPrintStr;
+ OUString aRowStr;
+ OUString aColStr;
+ bool bEntire = false;
+ const SfxPoolItem* pItem;
+ if ( pReqArgs->GetItemState( SID_CHANGE_PRINTAREA, true, &pItem ) == SfxItemState::SET )
+ aPrintStr = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ if ( pReqArgs->GetItemState( FN_PARAM_2, true, &pItem ) == SfxItemState::SET )
+ aRowStr = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ if ( pReqArgs->GetItemState( FN_PARAM_3, true, &pItem ) == SfxItemState::SET )
+ aColStr = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ if ( pReqArgs->GetItemState( FN_PARAM_4, true, &pItem ) == SfxItemState::SET )
+ bEntire = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+
+ SetPrintRanges( bEntire, &aPrintStr, &aColStr, &aRowStr, false );
+
+ rReq.Done();
+ }
+ }
+ break;
+
+ case SID_ADD_PRINTAREA:
+ case SID_DEFINE_PRINTAREA: // menu or basic
+ {
+ bool bAdd = ( nSlot == SID_ADD_PRINTAREA );
+ if ( pReqArgs )
+ {
+ OUString aPrintStr;
+ const SfxPoolItem* pItem;
+ if ( pReqArgs->GetItemState( SID_DEFINE_PRINTAREA, true, &pItem ) == SfxItemState::SET )
+ aPrintStr = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ SetPrintRanges( false, &aPrintStr, nullptr, nullptr, bAdd );
+ }
+ else
+ {
+ SetPrintRanges( false, nullptr, nullptr, nullptr, bAdd ); // from selection
+ rReq.Done();
+ }
+ }
+ break;
+
+ case SID_DELETE_PRINTAREA:
+ {
+ // Clear currently defined print range if any, and reset it to
+ // print entire sheet which is the default.
+ OUString aEmpty;
+ SetPrintRanges(true, &aEmpty, nullptr, nullptr, false);
+ rReq.Done();
+ }
+ break;
+
+ case FID_DEL_MANUALBREAKS:
+ RemoveManualBreaks();
+ rReq.Done();
+ break;
+
+ case FID_ADJUST_PRINTZOOM:
+ AdjustPrintZoom();
+ rReq.Done();
+ break;
+
+ case FID_RESET_PRINTZOOM:
+ SetPrintZoom( 100 ); // 100%, not on pages
+ rReq.Done();
+ break;
+
+ case SID_FORMATPAGE:
+ case SID_STATUS_PAGESTYLE:
+ case SID_HFEDIT:
+ GetViewData().GetDocShell()->
+ ExecutePageStyle( *this, rReq, GetViewData().GetTabNo() );
+ break;
+
+ case SID_JUMPTOMARK:
+ case SID_CURRENTCELL:
+ if ( pReqArgs )
+ {
+ OUString aAddress;
+ const SfxPoolItem* pItem;
+ if ( pReqArgs->GetItemState( nSlot, true, &pItem ) == SfxItemState::SET )
+ aAddress = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ else if ( nSlot == SID_JUMPTOMARK && pReqArgs->GetItemState(
+ SID_JUMPTOMARK, true, &pItem ) == SfxItemState::SET )
+ aAddress = static_cast<const SfxStringItem*>(pItem)->GetValue();
+
+ // #i14927# SID_CURRENTCELL with a single cell must unmark if FN_PARAM_1
+ // isn't set (for recorded macros, because IsAPI is no longer available).
+ // ScGridWindow::MouseButtonUp no longer executes the slot for a single
+ // cell if there is a multi selection.
+ bool bUnmark = ( nSlot == SID_CURRENTCELL );
+ if ( pReqArgs->GetItemState( FN_PARAM_1, true, &pItem ) == SfxItemState::SET )
+ bUnmark = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+
+ bool bAlignToCursor = true;
+ if (pReqArgs->GetItemState(FN_PARAM_2, true, &pItem) == SfxItemState::SET)
+ bAlignToCursor = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+
+ bool bForceGlobalName = false;
+ if (pReqArgs->GetItemState(FN_PARAM_3, true, &pItem) == SfxItemState::SET)
+ bForceGlobalName = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+
+ if ( nSlot == SID_JUMPTOMARK )
+ {
+ // URL has to be decoded for escaped characters (%20)
+ aAddress = INetURLObject::decode( aAddress,
+ INetURLObject::DecodeMechanism::WithCharset );
+ }
+
+ bool bFound = false;
+ ScViewData& rViewData = GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ ScMarkData& rMark = rViewData.GetMarkData();
+ ScRange aScRange;
+ ScAddress aScAddress;
+ ScRefFlagsAndType aResult = lcl_ParseRangeOrAddress(aScRange, aScAddress, aAddress, rDoc,
+ rViewData.GetCurX(), rViewData.GetCurY());
+ ScRefFlags nResult = aResult.nResult;
+ SCTAB nTab = rViewData.GetTabNo();
+ bool bMark = true;
+
+ // Is this a range ?
+ if (aResult.eDetected == DetectFlags::RANGE)
+ {
+ if ( nResult & ScRefFlags::TAB_3D )
+ {
+ if( aScRange.aStart.Tab() != nTab )
+ {
+ nTab = aScRange.aStart.Tab();
+ SetTabNo( nTab );
+ }
+ }
+ else
+ {
+ aScRange.aStart.SetTab( nTab );
+ aScRange.aEnd.SetTab( nTab );
+ }
+ }
+ // Is this a cell ?
+ else if (aResult.eDetected == DetectFlags::ADDRESS)
+ {
+ if ( nResult & ScRefFlags::TAB_3D )
+ {
+ if( aScAddress.Tab() != nTab )
+ {
+ nTab = aScAddress.Tab();
+ SetTabNo( nTab );
+ }
+ }
+ else
+ aScAddress.SetTab( nTab );
+
+ aScRange = ScRange( aScAddress, aScAddress );
+ // cells should not be marked
+ bMark = false;
+ }
+ // Is it a named area (first named ranges then database ranges)?
+ else
+ {
+ const RutlNameScope eScope = (bForceGlobalName ? RUTL_NAMES_GLOBAL : RUTL_NAMES);
+ ScAddress::Details aDetails( rDoc.GetAddressConvention(), rViewData.GetCurY(), rViewData.GetCurX());
+ if (ScRangeUtil::MakeRangeFromName( aAddress, rDoc, nTab, aScRange, eScope, aDetails, true) ||
+ ScRangeUtil::MakeRangeFromName( aAddress, rDoc, nTab, aScRange, RUTL_DBASE, aDetails, true))
+ {
+ nResult |= ScRefFlags::VALID;
+ if( aScRange.aStart.Tab() != nTab )
+ {
+ nTab = aScRange.aStart.Tab();
+ SetTabNo( nTab );
+ }
+ }
+ }
+
+ if ( !(nResult & ScRefFlags::VALID) && comphelper::string::isdigitAsciiString(aAddress) )
+ {
+ sal_Int32 nNumeric = aAddress.toInt32();
+ if ( nNumeric > 0 && nNumeric <= rDoc.MaxRow()+1 )
+ {
+ // one-based row numbers
+
+ aScAddress.SetRow( static_cast<SCROW>(nNumeric - 1) );
+ aScAddress.SetCol( rViewData.GetCurX() );
+ aScAddress.SetTab( nTab );
+ aScRange = ScRange( aScAddress, aScAddress );
+ bMark = false;
+ nResult = ScRefFlags::VALID;
+ }
+ }
+
+ if ( !rDoc.ValidRow(aScRange.aStart.Row()) || !rDoc.ValidRow(aScRange.aEnd.Row()) )
+ nResult = ScRefFlags::ZERO;
+
+ // we have found something
+ if( nResult & ScRefFlags::VALID )
+ {
+ bFound = true;
+ SCCOL nCol = aScRange.aStart.Col();
+ SCROW nRow = aScRange.aStart.Row();
+ bool bNothing = ( rViewData.GetCurX()==nCol && rViewData.GetCurY()==nRow );
+
+ // mark
+ if( bMark )
+ {
+ if (rMark.IsMarked()) // is the same range already marked?
+ {
+ ScRange aOldMark = rMark.GetMarkArea();
+ aOldMark.PutInOrder();
+ ScRange aCurrent = aScRange;
+ aCurrent.PutInOrder();
+ bNothing = ( aCurrent == aOldMark );
+ }
+ else
+ bNothing = false;
+
+ if (!bNothing)
+ MarkRange( aScRange, false ); // cursor comes after...
+ }
+ else
+ {
+ // remove old selection, unless bUnmark argument is sal_False (from navigator)
+ if( bUnmark )
+ {
+ MoveCursorAbs( nCol, nRow,
+ SC_FOLLOW_NONE, false, false );
+ }
+ }
+
+ // and set cursor
+
+ // consider merged cells:
+ rDoc.SkipOverlapped(nCol, nRow, nTab);
+
+ // navigator calls are not part of the API!!!
+
+ if( bNothing )
+ {
+ if (rReq.IsAPI())
+ rReq.Ignore(); // if macro, then nothing
+ else
+ rReq.Done(); // then at least paint it
+ }
+ else
+ {
+ rViewData.ResetOldCursor();
+ SetCursor( nCol, nRow );
+ rBindings.Invalidate( SID_CURRENTCELL );
+ rBindings.Update( nSlot );
+
+ if (!rReq.IsAPI())
+ rReq.Done();
+ }
+
+ if (bAlignToCursor)
+ {
+ // align to cursor even if the cursor position hasn't changed,
+ // because the cursor may be set outside the visible area.
+ AlignToCursor( nCol, nRow, SC_FOLLOW_JUMP );
+ if ( nSlot == SID_JUMPTOMARK && comphelper::LibreOfficeKit::isActive() )
+ rViewData.GetActiveWin()->notifyKitCellFollowJump();
+ }
+
+ rReq.SetReturnValue( SfxStringItem( SID_CURRENTCELL, aAddress ) );
+ }
+
+ if (!bFound) // no valid range
+ {
+ // if it is a sheet name, then switch (for Navigator/URL)
+
+ SCTAB nNameTab;
+ if ( rDoc.GetTable( aAddress, nNameTab ) )
+ {
+ bFound = true;
+ if ( nNameTab != nTab )
+ SetTabNo( nNameTab );
+ }
+ }
+
+ if ( !bFound && nSlot == SID_JUMPTOMARK )
+ {
+ // test graphics objects (only for URL)
+
+ bFound = SelectObject( aAddress );
+ }
+
+ if (!bFound && !rReq.IsAPI())
+ ErrorMessage( STR_ERR_INVALID_AREA );
+ }
+ break;
+
+ case SID_CURRENTOBJECT:
+ if ( pReqArgs )
+ {
+ OUString aName = static_cast<const SfxStringItem&>(pReqArgs->Get(nSlot)).GetValue();
+ SelectObject( aName );
+ }
+ break;
+
+ case SID_CURRENTTAB:
+ {
+ SCTAB nTab;
+ ScViewData& rViewData = GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ SCTAB nTabCount = rDoc.GetTableCount();
+ if ( pReqArgs ) // command from Navigator with nTab
+ {
+ // sheet for basic is one-based
+ nTab = static_cast<const SfxUInt16Item&>(pReqArgs->Get(nSlot)).GetValue() - 1;
+ }
+ else // command from Menu: ask for nTab
+ {
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScGoToTabDlg> pDlg(pFact->CreateScGoToTabDlg(GetFrameWeld()));
+ pDlg->SetDescription(
+ ScResId( STR_DLG_SELECTTABLE_TITLE ),
+ ScResId( STR_DLG_SELECTTABLE_MASK ),
+ ScResId( STR_DLG_SELECTTABLE_LBNAME ),
+ GetStaticInterface()->GetSlot(SID_CURRENTTAB)->GetCommand(), HID_GOTOTABLEMASK, HID_GOTOTABLE );
+
+ // fill all table names and select current tab
+ OUString aTabName;
+ for( nTab = 0; nTab < nTabCount; ++nTab )
+ {
+ if( rDoc.IsVisible( nTab ) )
+ {
+ rDoc.GetName( nTab, aTabName );
+ pDlg->Insert( aTabName, rViewData.GetTabNo() == nTab );
+ }
+ }
+
+ if( pDlg->Execute() == RET_OK )
+ {
+ if( !rDoc.GetTable( pDlg->GetSelectedEntry(), nTab ) )
+ nTab = nTabCount;
+ pDlg.disposeAndClear();
+ }
+ else
+ {
+ rReq.Ignore();
+ }
+ }
+ if ( nTab < nTabCount )
+ {
+ SetTabNo( nTab );
+ rBindings.Update( nSlot );
+
+ if( ! rReq.IsAPI() )
+ rReq.Done();
+ }
+ //! otherwise an error ?
+ }
+ break;
+
+ case SID_CURRENTDOC:
+ if ( pReqArgs )
+ {
+ OUString aStrDocName( static_cast<const SfxStringItem&>(pReqArgs->
+ Get(nSlot)).GetValue() );
+
+ SfxViewFrame* pViewFrame = nullptr;
+ ScDocShell* pDocSh = static_cast<ScDocShell*>(SfxObjectShell::GetFirst());
+ bool bFound = false;
+
+ // search for ViewFrame to be activated
+
+ while ( pDocSh && !bFound )
+ {
+ if ( pDocSh->GetTitle() == aStrDocName )
+ {
+ pViewFrame = SfxViewFrame::GetFirst( pDocSh );
+ bFound = ( nullptr != pViewFrame );
+ }
+
+ pDocSh = static_cast<ScDocShell*>(SfxObjectShell::GetNext( *pDocSh ));
+ }
+
+ if ( bFound )
+ pViewFrame->GetFrame().Appear();
+
+ rReq.Ignore();//XXX is handled by SFX
+ }
+ break;
+
+ case SID_PRINTPREVIEW:
+ {
+ if ( !rThisFrame.GetFrame().IsInPlace() ) // not for OLE
+ {
+ // print preview is now always in the same frame as the tab view
+ // -> always switch this frame back to normal view
+ // (ScPreviewShell ctor reads view data)
+
+ // #102785#; finish input
+ pScMod->InputEnterHandler();
+
+ rThisFrame.GetDispatcher()->Execute( SID_VIEWSHELL1, SfxCallMode::ASYNCHRON );
+ }
+ // else error (e.g. Ole)
+ }
+ break;
+
+ case SID_DETECTIVE_DEL_ALL:
+ DetectiveDelAll();
+ rReq.Done();
+ break;
+
+ // SID_TABLE_ACTIVATE and SID_MARKAREA are called by basic for the
+ // hidden View, to mark/switch on the visible View:
+
+ case SID_TABLE_ACTIVATE:
+ OSL_FAIL("old slot SID_TABLE_ACTIVATE");
+ break;
+
+ case SID_REPAINT:
+ PaintGrid();
+ PaintTop();
+ PaintLeft();
+ PaintExtras();
+ rReq.Done();
+ break;
+
+ case FID_NORMALVIEWMODE:
+ case FID_PAGEBREAKMODE:
+ {
+ bool bWantPageBreak = nSlot == FID_PAGEBREAKMODE;
+
+ // check whether there is an explicit argument, use it
+ const SfxPoolItem* pItem;
+ if ( pReqArgs && pReqArgs->GetItemState(nSlot, true, &pItem) == SfxItemState::SET )
+ {
+ bool bItemValue = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+ bWantPageBreak = (nSlot == FID_PAGEBREAKMODE) == bItemValue;
+ }
+
+ if( GetViewData().IsPagebreakMode() != bWantPageBreak )
+ {
+ SetPagebreakMode( bWantPageBreak );
+ UpdatePageBreakData();
+ SetCurSubShell( GetCurObjectSelectionType(), true );
+ PaintGrid();
+ PaintTop();
+ PaintLeft();
+ rBindings.Invalidate( nSlot );
+ rReq.AppendItem( SfxBoolItem( nSlot, true ) );
+ rReq.Done();
+ }
+ }
+ break;
+
+ case FID_FUNCTION_BOX:
+ {
+ // First make sure that the sidebar is visible
+ rThisFrame.ShowChildWindow(SID_SIDEBAR);
+
+ ::sfx2::sidebar::Sidebar::ShowPanel(u"ScFunctionsPanel",
+ rThisFrame.GetFrame().GetFrameInterface(),
+ true);
+ rReq.Done ();
+ }
+ break;
+
+ case FID_TOGGLESYNTAX:
+ {
+ bool bSet = !GetViewData().IsSyntaxMode();
+ const SfxPoolItem* pItem;
+ if ( pReqArgs && pReqArgs->GetItemState(nSlot, true, &pItem) == SfxItemState::SET )
+ bSet = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+ GetViewData().SetSyntaxMode( bSet );
+ PaintGrid();
+ rBindings.Invalidate( FID_TOGGLESYNTAX );
+ rReq.AppendItem( SfxBoolItem( nSlot, bSet ) );
+ rReq.Done();
+ }
+ break;
+ case FID_TOGGLECOLROWHIGHLIGHTING:
+ {
+ bool bNewVal = !officecfg::Office::Calc::Content::Display::ColumnRowHighlighting::get();
+
+ auto pChange(comphelper::ConfigurationChanges::create());
+ officecfg::Office::Calc::Content::Display::ColumnRowHighlighting::set(bNewVal, pChange);
+ pChange->commit();
+
+ rReq.AppendItem(SfxBoolItem(nSlot, bNewVal));
+ rReq.Done();
+ }
+ break;
+ case FID_TOGGLEHEADERS:
+ {
+ bool bSet = !GetViewData().IsHeaderMode();
+ const SfxPoolItem* pItem;
+ if ( pReqArgs && pReqArgs->GetItemState(nSlot, true, &pItem) == SfxItemState::SET )
+ bSet = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+ GetViewData().SetHeaderMode( bSet );
+ RepeatResize();
+ rBindings.Invalidate( FID_TOGGLEHEADERS );
+ rReq.AppendItem( SfxBoolItem( nSlot, bSet ) );
+ rReq.Done();
+ }
+ break;
+
+ case FID_TOGGLEFORMULA:
+ {
+ ScViewData& rViewData = GetViewData();
+ const ScViewOptions& rOpts = rViewData.GetOptions();
+ bool bFormulaMode = !rOpts.GetOption( VOPT_FORMULAS );
+ const SfxPoolItem *pItem;
+ if( pReqArgs && pReqArgs->GetItemState(nSlot, true, &pItem) == SfxItemState::SET )
+ bFormulaMode = static_cast<const SfxBoolItem *>(pItem)->GetValue();
+
+ ScViewOptions aSetOpts = rOpts;
+ aSetOpts.SetOption( VOPT_FORMULAS, bFormulaMode );
+ rViewData.SetOptions( aSetOpts );
+ ScDocument& rDoc = rViewData.GetDocument();
+ rDoc.SetViewOptions(aSetOpts);
+
+ rViewData.GetDocShell()->PostPaintGridAll();
+
+ rBindings.Invalidate( FID_TOGGLEFORMULA );
+ rReq.AppendItem( SfxBoolItem( nSlot, bFormulaMode ) );
+ rReq.Done();
+ }
+ break;
+
+ case FID_TOGGLEINPUTLINE:
+ {
+ sal_uInt16 nId = ScInputWindowWrapper::GetChildWindowId();
+ SfxChildWindow* pWnd = rThisFrame.GetChildWindow( nId );
+ bool bSet = ( pWnd == nullptr );
+ const SfxPoolItem* pItem;
+ if ( pReqArgs && pReqArgs->GetItemState(nSlot, true, &pItem) == SfxItemState::SET )
+ bSet = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+
+ rThisFrame.SetChildWindow( nId, bSet );
+ rBindings.Invalidate( FID_TOGGLEINPUTLINE );
+ rReq.AppendItem( SfxBoolItem( nSlot, bSet ) );
+ rReq.Done();
+ }
+ break;
+
+ // handling for SID_ZOOM_IN and SID_ZOOM_OUT is ScTabView::ScrollCommand
+ // CommandWheelMode::ZOOM inspired
+ case SID_ZOOM_IN:
+ case SID_ZOOM_OUT:
+ {
+ HideNoteMarker();
+
+ if (!GetViewData().GetViewShell()->GetViewFrame().GetFrame().IsInPlace())
+ {
+ // for ole inplace editing, the scale is defined by the visarea and client size
+ // and can't be changed directly
+
+ const Fraction& rOldY = GetViewData().GetZoomY();
+ sal_uInt16 nOld = tools::Long(rOldY * 100);
+ sal_uInt16 nNew;
+ if (SID_ZOOM_OUT == nSlot)
+ nNew = std::max(MINZOOM, basegfx::zoomtools::zoomOut(nOld));
+ else
+ nNew = std::min(MAXZOOM, basegfx::zoomtools::zoomIn(nOld));
+ if ( nNew != nOld)
+ {
+ bool bSyncZoom = SC_MOD()->GetAppOptions().GetSynchronizeZoom();
+ SetZoomType(SvxZoomType::PERCENT, bSyncZoom);
+ Fraction aFract(nNew, 100);
+ SetZoom(aFract, aFract, bSyncZoom);
+ PaintGrid();
+ PaintTop();
+ PaintLeft();
+ rBindings.Invalidate(SID_ATTR_ZOOM);
+ rBindings.Invalidate(SID_ATTR_ZOOMSLIDER);
+ rBindings.Invalidate(SID_ZOOM_IN);
+ rBindings.Invalidate(SID_ZOOM_OUT);
+ rReq.Done();
+ }
+ }
+ }
+ break;
+
+ case SID_ATTR_ZOOM: // status row
+ case FID_SCALE:
+ {
+ bool bSyncZoom = SC_MOD()->GetAppOptions().GetSynchronizeZoom();
+ SvxZoomType eOldZoomType = GetZoomType();
+ SvxZoomType eNewZoomType = eOldZoomType;
+ const Fraction& rOldY = GetViewData().GetZoomY(); // Y is shown
+ sal_uInt16 nOldZoom = static_cast<sal_uInt16>(tools::Long( rOldY * 100 ));
+ sal_uInt16 nZoom = nOldZoom;
+ bool bCancel = false;
+
+ if ( pReqArgs )
+ {
+ const SvxZoomItem& rZoomItem = pReqArgs->Get(SID_ATTR_ZOOM);
+
+ eNewZoomType = rZoomItem.GetType();
+ nZoom = rZoomItem.GetValue();
+ }
+ else
+ {
+ SfxItemSetFixed<SID_ATTR_ZOOM, SID_ATTR_ZOOM> aSet( GetPool() );
+ SvxZoomItem aZoomItem( eOldZoomType, nOldZoom, SID_ATTR_ZOOM );
+ ScopedVclPtr<AbstractSvxZoomDialog> pDlg;
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ SvxZoomEnableFlags nBtnFlags = SvxZoomEnableFlags::N50
+ | SvxZoomEnableFlags::N75
+ | SvxZoomEnableFlags::N100
+ | SvxZoomEnableFlags::N150
+ | SvxZoomEnableFlags::N200
+ | SvxZoomEnableFlags::WHOLEPAGE
+ | SvxZoomEnableFlags::PAGEWIDTH;
+
+ if ( rMark.IsMarked() || rMark.IsMultiMarked() )
+ nBtnFlags = nBtnFlags | SvxZoomEnableFlags::OPTIMAL;
+
+ aZoomItem.SetValueSet( nBtnFlags );
+ aSet.Put( aZoomItem );
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ pDlg.disposeAndReset(pFact->CreateSvxZoomDialog(GetFrameWeld(), aSet));
+ pDlg->SetLimits( MINZOOM, MAXZOOM );
+
+ bCancel = ( RET_CANCEL == pDlg->Execute() );
+
+ // bCancel is True only if we were in the previous if block,
+ // so no need to check again pDlg
+ if ( !bCancel )
+ {
+ const SvxZoomItem& rZoomItem = pDlg->GetOutputItemSet()->
+ Get( SID_ATTR_ZOOM );
+
+ eNewZoomType = rZoomItem.GetType();
+ nZoom = rZoomItem.GetValue();
+ }
+ }
+
+ if ( !bCancel )
+ {
+ if ( eNewZoomType == SvxZoomType::PERCENT )
+ {
+ if ( nZoom < MINZOOM ) nZoom = MINZOOM;
+ if ( nZoom > MAXZOOM ) nZoom = MAXZOOM;
+ }
+ else
+ {
+ nZoom = CalcZoom( eNewZoomType, nOldZoom );
+ bCancel = nZoom == 0;
+ }
+
+ switch ( eNewZoomType )
+ {
+ case SvxZoomType::WHOLEPAGE:
+ case SvxZoomType::PAGEWIDTH:
+ SetZoomType( eNewZoomType, bSyncZoom );
+ break;
+
+ default:
+ SetZoomType( SvxZoomType::PERCENT, bSyncZoom );
+ }
+ }
+
+ if ( nZoom != nOldZoom && !bCancel )
+ {
+ if (!GetViewData().IsPagebreakMode())
+ {
+ ScAppOptions aNewOpt = pScMod->GetAppOptions();
+ aNewOpt.SetZoom( nZoom );
+ aNewOpt.SetZoomType( GetZoomType() );
+ pScMod->SetAppOptions( aNewOpt );
+ }
+ Fraction aFract( nZoom, 100 );
+ SetZoom( aFract, aFract, bSyncZoom );
+ PaintGrid();
+ PaintTop();
+ PaintLeft();
+ rBindings.Invalidate( SID_ATTR_ZOOM );
+ rReq.AppendItem( SvxZoomItem( GetZoomType(), nZoom, TypedWhichId<SvxZoomItem>(nSlot) ) );
+ rReq.Done();
+ }
+ }
+ break;
+
+ case SID_ATTR_ZOOMSLIDER:
+ {
+ const SfxPoolItem* pItem = nullptr;
+ bool bSyncZoom = SC_MOD()->GetAppOptions().GetSynchronizeZoom();
+ if ( pReqArgs && pReqArgs->GetItemState(SID_ATTR_ZOOMSLIDER, true, &pItem) == SfxItemState::SET )
+ {
+ const sal_uInt16 nCurrentZoom = static_cast<const SvxZoomSliderItem *>(pItem)->GetValue();
+ if( nCurrentZoom )
+ {
+ SetZoomType( SvxZoomType::PERCENT, bSyncZoom );
+ if (!GetViewData().IsPagebreakMode())
+ {
+ ScAppOptions aNewOpt = pScMod->GetAppOptions();
+ aNewOpt.SetZoom( nCurrentZoom );
+ collectUIInformation(OUString::number(nCurrentZoom));
+ aNewOpt.SetZoomType( GetZoomType() );
+ pScMod->SetAppOptions( aNewOpt );
+ }
+ Fraction aFract( nCurrentZoom,100 );
+ SetZoom( aFract, aFract, bSyncZoom );
+ PaintGrid();
+ PaintTop();
+ PaintLeft();
+ rBindings.Invalidate( SID_ATTR_ZOOMSLIDER );
+ rBindings.Invalidate( SID_ZOOM_IN );
+ rBindings.Invalidate( SID_ZOOM_OUT );
+ rReq.Done();
+ }
+ }
+ }
+ break;
+
+ case FID_TAB_SELECTALL:
+ SelectAllTables();
+ rReq.Done();
+ break;
+
+ case FID_TAB_DESELECTALL:
+ DeselectAllTables();
+ rReq.Done();
+ break;
+
+ case SID_SELECT_TABLES:
+ {
+ ScViewData& rViewData = GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ ScMarkData& rMark = rViewData.GetMarkData();
+ SCTAB nTabCount = rDoc.GetTableCount();
+ SCTAB nTab;
+
+ ::std::vector < sal_Int32 > aIndexList;
+ const SfxIntegerListItem* pItem = rReq.GetArg<SfxIntegerListItem>(SID_SELECT_TABLES);
+ if ( pItem )
+ aIndexList = pItem->GetList();
+ else
+ {
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScShowTabDlg> pDlg(pFact->CreateScShowTabDlg(GetFrameWeld()));
+ pDlg->SetDescription(
+ ScResId( STR_DLG_SELECTTABLES_TITLE ),
+ ScResId( STR_DLG_SELECTTABLES_LBNAME ),
+ GetStaticInterface()->GetSlot(SID_SELECT_TABLES)->GetCommand(), HID_SELECTTABLES );
+
+ // fill all table names with selection state
+ OUString aTabName;
+ for( nTab = 0; nTab < nTabCount; ++nTab )
+ {
+ rDoc.GetName( nTab, aTabName );
+ pDlg->Insert( aTabName, rMark.GetTableSelect( nTab ) );
+ }
+
+ if( pDlg->Execute() == RET_OK )
+ {
+ aIndexList = pDlg->GetSelectedRows();
+ pDlg.disposeAndClear();
+ rReq.AppendItem( SfxIntegerListItem( SID_SELECT_TABLES, std::vector(aIndexList) ) );
+ }
+ else
+ rReq.Ignore();
+ }
+
+ if ( !aIndexList.empty() )
+ {
+ sal_uInt16 nSelCount = aIndexList.size();
+ sal_uInt16 nSelIx;
+ SCTAB nFirstVisTab = 0;
+
+ // special case: only hidden tables selected -> do nothing
+ bool bVisSelected = false;
+ for( nSelIx = 0; !bVisSelected && (nSelIx < nSelCount); ++nSelIx )
+ {
+ nFirstVisTab = static_cast<SCTAB>(aIndexList[nSelIx]);
+ bVisSelected = rDoc.IsVisible( nFirstVisTab );
+ }
+ if( !bVisSelected )
+ nSelCount = 0;
+
+ // select the tables
+ if( nSelCount )
+ {
+ for( nTab = 0; nTab < nTabCount; ++nTab )
+ rMark.SelectTable( nTab, false );
+
+ for( nSelIx = 0; nSelIx < nSelCount; ++nSelIx )
+ rMark.SelectTable( static_cast<SCTAB>(aIndexList[nSelIx]), true );
+
+ // activate another table, if current is deselected
+ if( !rMark.GetTableSelect( rViewData.GetTabNo() ) )
+ {
+ rMark.SelectTable( nFirstVisTab, true );
+ SetTabNo( nFirstVisTab );
+ }
+
+ rViewData.GetDocShell()->PostPaintExtras();
+ SfxBindings& rBind = rViewData.GetBindings();
+ rBind.Invalidate( FID_FILL_TAB );
+ rBind.Invalidate( FID_TAB_DESELECTALL );
+ }
+
+ rReq.Done();
+ }
+ }
+ break;
+
+ case SID_OUTLINE_DELETEALL:
+ RemoveAllOutlines();
+ rReq.Done();
+ break;
+
+ case SID_AUTO_OUTLINE:
+ AutoOutline();
+ rReq.Done();
+ break;
+
+ case SID_WINDOW_SPLIT:
+ {
+ ScSplitMode eHSplit = GetViewData().GetHSplitMode();
+ ScSplitMode eVSplit = GetViewData().GetVSplitMode();
+ if ( eHSplit == SC_SPLIT_NORMAL || eVSplit == SC_SPLIT_NORMAL ) // remove
+ RemoveSplit();
+ else if ( eHSplit == SC_SPLIT_FIX || eVSplit == SC_SPLIT_FIX ) // normal
+ FreezeSplitters( false );
+ else // create
+ SplitAtCursor();
+ rReq.Done();
+
+ InvalidateSplit();
+ }
+ break;
+
+ case SID_WINDOW_FIX:
+ {
+ if (!comphelper::LibreOfficeKit::isActive())
+ {
+ ScSplitMode eHSplit = GetViewData().GetHSplitMode();
+ ScSplitMode eVSplit = GetViewData().GetVSplitMode();
+ if ( eHSplit == SC_SPLIT_FIX || eVSplit == SC_SPLIT_FIX ) // remove
+ RemoveSplit();
+ else
+ FreezeSplitters( true, SC_SPLIT_METHOD_CURSOR); // create or fixate
+ rReq.Done();
+ InvalidateSplit();
+ }
+ else
+ {
+ ScViewData& rViewData = GetViewData();
+ SCTAB nThisTab = rViewData.GetTabNo();
+ bool bChangedX = false, bChangedY = false;
+ if (rViewData.GetLOKSheetFreezeIndex(true) > 0 ||
+ rViewData.GetLOKSheetFreezeIndex(false) > 0 ) // remove freeze
+ {
+ bChangedX = rViewData.RemoveLOKFreeze();
+ } // create or fixate
+ else
+ {
+ bChangedX = rViewData.SetLOKSheetFreezeIndex(rViewData.GetCurX(), true); // Freeze column
+ bChangedY = rViewData.SetLOKSheetFreezeIndex(rViewData.GetCurY(), false); // Freeze row
+ }
+
+ rReq.Done();
+ if (bChangedX || bChangedY)
+ {
+ rBindings.Invalidate( SID_WINDOW_FIX );
+ rBindings.Invalidate( SID_WINDOW_FIX_COL );
+ rBindings.Invalidate( SID_WINDOW_FIX_ROW );
+ // Invalidate the slot for all views on the same tab of the document.
+ SfxLokHelper::forEachOtherView(this, [nThisTab](ScTabViewShell* pOther) {
+ ScViewData& rOtherViewData = pOther->GetViewData();
+ if (rOtherViewData.GetTabNo() != nThisTab)
+ return;
+
+ SfxBindings& rOtherBind = rOtherViewData.GetBindings();
+ rOtherBind.Invalidate( SID_WINDOW_FIX );
+ rOtherBind.Invalidate( SID_WINDOW_FIX_COL );
+ rOtherBind.Invalidate( SID_WINDOW_FIX_ROW );
+ });
+ if (!GetViewData().GetDocShell()->IsReadOnly())
+ GetViewData().GetDocShell()->SetDocumentModified();
+ }
+ }
+ }
+ break;
+
+ case SID_WINDOW_FIX_COL:
+ case SID_WINDOW_FIX_ROW:
+ {
+ bool bIsCol = (nSlot == SID_WINDOW_FIX_COL);
+ sal_Int32 nFreezeIndex = 1;
+ if (const SfxInt32Item* pItem = rReq.GetArg<SfxInt32Item>(nSlot))
+ {
+ nFreezeIndex = pItem->GetValue();
+ if (nFreezeIndex < 0)
+ nFreezeIndex = 0;
+ }
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ ScViewData& rViewData = GetViewData();
+ SCTAB nThisTab = rViewData.GetTabNo();
+ bool bChanged = rViewData.SetLOKSheetFreezeIndex(nFreezeIndex, bIsCol);
+ rReq.Done();
+ if (bChanged)
+ {
+ rBindings.Invalidate( SID_WINDOW_FIX );
+ rBindings.Invalidate(nSlot);
+ // Invalidate the slot for all views on the same tab of the document.
+ SfxLokHelper::forEachOtherView(this, [nSlot, nThisTab](ScTabViewShell* pOther) {
+ ScViewData& rOtherViewData = pOther->GetViewData();
+ if (rOtherViewData.GetTabNo() != nThisTab)
+ return;
+
+ SfxBindings& rOtherBind = rOtherViewData.GetBindings();
+ rOtherBind.Invalidate( SID_WINDOW_FIX );
+ rOtherBind.Invalidate(nSlot);
+ });
+ if (!GetViewData().GetDocShell()->IsReadOnly())
+ GetViewData().GetDocShell()->SetDocumentModified();
+ }
+ }
+ else
+ {
+ FreezeSplitters( true, bIsCol ? SC_SPLIT_METHOD_COL : SC_SPLIT_METHOD_ROW, nFreezeIndex);
+ rReq.Done();
+ InvalidateSplit();
+ }
+ }
+ break;
+
+ case FID_CHG_SHOW:
+ {
+ sal_uInt16 nId = ScHighlightChgDlgWrapper::GetChildWindowId();
+ SfxChildWindow* pWnd = rThisFrame.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ }
+ break;
+
+ case FID_CHG_ACCEPT:
+ {
+ rThisFrame.ToggleChildWindow(ScAcceptChgDlgWrapper::GetChildWindowId());
+ GetViewFrame().GetBindings().Invalidate(FID_CHG_ACCEPT);
+ rReq.Done ();
+
+ /*
+ sal_uInt16 nId = ScAcceptChgDlgWrapper::GetChildWindowId();
+ SfxChildWindow* pWnd = rThisFrame.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd ? sal_False : sal_True );
+ */
+ }
+ break;
+
+ case FID_CHG_COMMENT:
+ {
+ ScViewData& rData = GetViewData();
+ ScAddress aCursorPos( rData.GetCurX(), rData.GetCurY(), rData.GetTabNo() );
+ ScDocShell* pDocSh = rData.GetDocShell();
+
+ ScChangeAction* pAction = pDocSh->GetChangeAction( aCursorPos );
+ if ( pAction )
+ {
+ const SfxPoolItem* pItem;
+ if ( pReqArgs &&
+ pReqArgs->GetItemState( nSlot, true, &pItem ) == SfxItemState::SET &&
+ dynamic_cast<const SfxStringItem*>( pItem) != nullptr )
+ {
+ OUString aComment = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ pDocSh->SetChangeComment( pAction, aComment );
+ rReq.Done();
+ }
+ else
+ {
+ pDocSh->ExecuteChangeCommentDialog(pAction, GetFrameWeld());
+ rReq.Done();
+ }
+ }
+ }
+ break;
+
+ case SID_CREATE_SW_DRAWVIEW:
+ // is called by Forms, when the DrawView has to be created with all
+ // the extras
+ if (!GetScDrawView())
+ {
+ GetViewData().GetDocShell()->MakeDrawLayer();
+ rBindings.InvalidateAll(false);
+ }
+ break;
+
+ case FID_PROTECT_DOC:
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+
+ if( pReqArgs )
+ {
+ const SfxPoolItem* pItem;
+ if( pReqArgs->HasItem( FID_PROTECT_DOC, &pItem ) &&
+ static_cast<const SfxBoolItem*>(pItem)->GetValue() == rDoc.IsDocProtected() )
+ {
+ rReq.Ignore();
+ break;
+ }
+ }
+
+ ScDocProtection* pProtect = rDoc.GetDocProtection();
+ if (pProtect && pProtect->isProtected())
+ {
+ bool bCancel = false;
+ OUString aPassword;
+
+ if (pProtect->isProtectedWithPass())
+ {
+ OUString aText(ScResId(SCSTR_PASSWORD));
+
+ SfxPasswordDialog aDlg(GetFrameWeld(), &aText);
+ aDlg.set_title(ScResId(SCSTR_UNPROTECTDOC));
+ aDlg.SetMinLen(0);
+ aDlg.set_help_id(GetStaticInterface()->GetSlot(FID_PROTECT_DOC)->GetCommand());
+ aDlg.SetEditHelpId(HID_PASSWD_DOC);
+
+ if (aDlg.run() == RET_OK)
+ aPassword = aDlg.GetPassword();
+ else
+ bCancel = true;
+ }
+ if (!bCancel)
+ {
+ Unprotect( TABLEID_DOC, aPassword );
+ rReq.AppendItem( SfxBoolItem( FID_PROTECT_DOC, false ) );
+ rReq.Done();
+ }
+ }
+ else
+ {
+ OUString aText(ScResId(SCSTR_PASSWORDOPT));
+
+ SfxPasswordDialog aDlg(GetFrameWeld(), &aText);
+ aDlg.set_title(ScResId(SCSTR_PROTECTDOC));
+ aDlg.SetMinLen( 0 );
+ aDlg.set_help_id(GetStaticInterface()->GetSlot(FID_PROTECT_DOC)->GetCommand());
+ aDlg.SetEditHelpId(HID_PASSWD_DOC);
+ aDlg.ShowExtras(SfxShowExtras::CONFIRM);
+ aDlg.SetConfirmHelpId(HID_PASSWD_DOC_CONFIRM);
+
+ if (aDlg.run() == RET_OK)
+ {
+ OUString aPassword = aDlg.GetPassword();
+ ProtectDoc( aPassword );
+ rReq.AppendItem( SfxBoolItem( FID_PROTECT_DOC, true ) );
+ rReq.Done();
+ }
+ }
+ rBindings.Invalidate( FID_PROTECT_DOC );
+ }
+ break;
+
+ case FID_PROTECT_TABLE:
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+ SCTAB nTab = GetViewData().GetTabNo();
+ bool bOldProtection = rDoc.IsTabProtected(nTab);
+
+ if( pReqArgs )
+ {
+ const SfxPoolItem* pItem;
+ bool bNewProtection = !bOldProtection;
+ if( pReqArgs->HasItem( FID_PROTECT_TABLE, &pItem ) )
+ bNewProtection = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+ if( bNewProtection == bOldProtection )
+ {
+ rReq.Ignore();
+ break;
+ }
+ }
+
+ if (bOldProtection)
+ {
+ // Unprotect a protected sheet.
+
+ const ScTableProtection* pProtect = rDoc.GetTabProtection(nTab);
+ if (pProtect && pProtect->isProtectedWithPass())
+ {
+ OUString aText( ScResId(SCSTR_PASSWORDOPT) );
+ SfxPasswordDialog aDlg(GetFrameWeld(), &aText);
+ aDlg.set_title(ScResId(SCSTR_UNPROTECTTAB));
+ aDlg.SetMinLen(0);
+ aDlg.set_help_id(GetStaticInterface()->GetSlot(FID_PROTECT_TABLE)->GetCommand());
+ aDlg.SetEditHelpId(HID_PASSWD_TABLE);
+
+ if (aDlg.run() == RET_OK)
+ {
+ OUString aPassword = aDlg.GetPassword();
+ Unprotect(nTab, aPassword);
+ }
+ }
+ else
+ // this sheet is not password-protected.
+ Unprotect(nTab, OUString());
+
+ if (!pReqArgs)
+ {
+ rReq.AppendItem( SfxBoolItem(FID_PROTECT_TABLE, false) );
+ rReq.Done();
+ }
+ }
+ else
+ {
+ // Protect a current sheet.
+
+ ScTableProtectionDlg aDlg(GetFrameWeld());
+
+ const ScTableProtection* pProtect = rDoc.GetTabProtection(nTab);
+ if (pProtect)
+ aDlg.SetDialogData(*pProtect);
+
+ if (aDlg.run() == RET_OK)
+ {
+ pScMod->InputEnterHandler();
+
+ ScTableProtection aNewProtect;
+ aDlg.WriteData(aNewProtect);
+ ProtectSheet(nTab, aNewProtect);
+ if (!pReqArgs)
+ {
+ rReq.AppendItem( SfxBoolItem(FID_PROTECT_TABLE, true) );
+ rReq.Done();
+ }
+ }
+ }
+ TabChanged();
+ UpdateInputHandler(true); // to immediately enable input again
+ SelectionChanged();
+ }
+ break;
+ case SID_THEME_DIALOG:
+ {
+ MakeDrawLayer();
+ ScViewData& rViewData = GetViewData();
+ ScDocument& rDocument = rViewData.GetDocument();
+ ScDrawLayer* pModel = rDocument.GetDrawLayer();
+ auto const& pTheme = pModel->getTheme();
+ if (pTheme)
+ {
+ vcl::Window* pWin = rViewData.GetActiveWin();
+ auto pDialog = std::make_shared<svx::ThemeDialog>(pWin ? pWin->GetFrameWeld() : nullptr, pTheme.get());
+ weld::DialogController::runAsync(pDialog, [this, pDialog](sal_uInt32 nResult) {
+ if (RET_OK != nResult)
+ return;
+
+ auto pColorSet = pDialog->getCurrentColorSet();
+ if (pColorSet)
+ {
+ sc::ThemeColorChanger aChanger(*GetViewData().GetDocShell());
+ aChanger.apply(pColorSet);
+ }
+ });
+ }
+ rReq.Done();
+ }
+ break;
+ case SID_OPT_LOCALE_CHANGED :
+ { // locale changed, SYSTEM number formats changed => repaint cell contents
+ PaintGrid();
+ rReq.Done();
+ }
+ break;
+
+ default:
+ OSL_FAIL("Unknown Slot at ScTabViewShell::Execute");
+ break;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabvwsh4.cxx b/sc/source/ui/view/tabvwsh4.cxx
new file mode 100644
index 0000000000..345a33534d
--- /dev/null
+++ b/sc/source/ui/view/tabvwsh4.cxx
@@ -0,0 +1,1946 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include <formdata.hxx>
+
+#include <sfx2/app.hxx>
+#include <svx/dialogs.hrc>
+#include <svx/extrusionbar.hxx>
+#include <svx/fontworkbar.hxx>
+#include <editeng/borderline.hxx>
+#include <svx/fmshell.hxx>
+#include <svx/sidebar/ContextChangeEventMultiplexer.hxx>
+#include <sfx2/printer.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/ipclient.hxx>
+#include <tools/urlobj.hxx>
+#include <sfx2/docfile.hxx>
+#include <tools/svborder.hxx>
+
+#include <IAnyRefDialog.hxx>
+#include <tabvwsh.hxx>
+#include <sc.hrc>
+#include <globstr.hrc>
+#include <docsh.hxx>
+#include <scmod.hxx>
+#include <appoptio.hxx>
+#include <drawsh.hxx>
+#include <drformsh.hxx>
+#include <editsh.hxx>
+#include <pivotsh.hxx>
+#include <SparklineShell.hxx>
+#include <auditsh.hxx>
+#include <drtxtob.hxx>
+#include <inputhdl.hxx>
+#include <editutil.hxx>
+#include <inputopt.hxx>
+#include <inputwin.hxx>
+#include <dbdata.hxx>
+#include <reffact.hxx>
+#include <viewuno.hxx>
+#include <dispuno.hxx>
+#include <chgtrack.hxx>
+#include <cellsh.hxx>
+#include <oleobjsh.hxx>
+#include <chartsh.hxx>
+#include <graphsh.hxx>
+#include <mediash.hxx>
+#include <pgbrksh.hxx>
+#include <dpobject.hxx>
+#include <prevwsh.hxx>
+#include <scextopt.hxx>
+#include <drawview.hxx>
+#include <fupoor.hxx>
+#include <navsett.hxx>
+#include <scabstdlg.hxx>
+#include <externalrefmgr.hxx>
+#include <defaultsoptions.hxx>
+#include <markdata.hxx>
+#include <preview.hxx>
+#include <docoptio.hxx>
+#include <documentlinkmgr.hxx>
+#include <gridwin.hxx>
+
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <sfx2/lokhelper.hxx>
+#include <comphelper/flagguard.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <comphelper/lok.hxx>
+#include <sfx2/sidebar/SidebarController.hxx>
+
+using namespace com::sun::star;
+using namespace sfx2::sidebar;
+
+namespace {
+
+bool inChartOrMathContext(const ScTabViewShell* pViewShell)
+{
+ SidebarController* pSidebar = SidebarController::GetSidebarControllerForView(pViewShell);
+ if (pSidebar)
+ return pSidebar->hasChartOrMathContextCurrently();
+
+ return false;
+}
+
+} // anonymous namespace
+
+void ScTabViewShell::Activate(bool bMDI)
+{
+ SfxViewShell::Activate(bMDI);
+ bIsActive = true;
+ // here no GrabFocus, otherwise there will be problems when something is edited inplace!
+
+ if ( bMDI )
+ {
+ // for input row (ClearCache)
+ ScModule* pScMod = SC_MOD();
+ pScMod->ViewShellChanged(/*bStopEditing=*/ !comphelper::LibreOfficeKit::isActive());
+
+ ActivateView( true, bFirstActivate );
+
+ // update AutoCorrect, if Writer has newly created this
+ UpdateDrawTextOutliner();
+
+ // RegisterNewTargetNames does not exist anymore
+
+ SfxViewFrame& rThisFrame = GetViewFrame();
+ if ( mpInputHandler && rThisFrame.HasChildWindow(FID_INPUTLINE_STATUS) )
+ {
+ // actually only required for Reload (last version):
+ // The InputWindow remains, but the View along with the InputHandler is newly created,
+ // that is why the InputHandler must be set at the InputWindow.
+ SfxChildWindow* pChild = rThisFrame.GetChildWindow(FID_INPUTLINE_STATUS);
+ if (pChild)
+ {
+ ScInputWindow* pWin = static_cast<ScInputWindow*>(pChild->GetWindow());
+ if (pWin && pWin->IsVisible())
+ {
+ pWin->NumLinesChanged(); // tdf#150664
+ ScInputHandler* pOldHdl=pWin->GetInputHandler();
+
+ SfxViewShell* pSh = SfxViewShell::GetFirst( true, checkSfxViewShell<ScTabViewShell> );
+ while ( pSh!=nullptr && pOldHdl!=nullptr)
+ {
+ // Hmm, what if pSh is a shell for a different document? But as this code
+ // does not seem to be LibreOfficeKit-specific, probably that doesn't
+ // happen, because having multiple documents open simultaneously has of
+ // course not been a problem at all in traditional desktop LibreOffice.
+ // (Unlike in a LibreOfficeKit-based process where it has been a problem.)
+ if (static_cast<ScTabViewShell*>(pSh)->GetInputHandler() == pOldHdl)
+ {
+ pOldHdl->ResetDelayTimer();
+ break;
+ }
+ pSh = SfxViewShell::GetNext( *pSh, true, checkSfxViewShell<ScTabViewShell> );
+ }
+
+ pWin->SetInputHandler( mpInputHandler.get() );
+ }
+ }
+ }
+
+ bool isLOK = comphelper::LibreOfficeKit::isActive();
+ UpdateInputHandler( /*bForce=*/ !isLOK, /*bStopEditing=*/ !isLOK );
+
+ if ( bFirstActivate )
+ {
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScNavigatorUpdateAll ) );
+ bFirstActivate = false;
+
+ // ReadExtOptions (view settings from Excel import) must also be done
+ // after the ctor, because of the potential calls to Window::Show.
+ // Even after a bugfix (Window::Show no longer notifies the access
+ // bridge, it's done in ImplSetReallyVisible), there are problems if Window::Show
+ // is called during the ViewShell ctor and reschedules asynchronous calls
+ // (for example from the FmFormShell ctor).
+ ScExtDocOptions* pExtOpt = GetViewData().GetDocument().GetExtDocOptions();
+ if ( pExtOpt && pExtOpt->IsChanged() )
+ {
+ GetViewData().ReadExtOptions(*pExtOpt); // Excel view settings
+ SetTabNo( GetViewData().GetTabNo(), true );
+ pExtOpt->SetChanged( false );
+ }
+ }
+
+ pScActiveViewShell = this;
+
+ ScInputHandler* pHdl = pScMod->GetInputHdl(this);
+ if (pHdl)
+ {
+ pHdl->SetRefScale( GetViewData().GetZoomX(), GetViewData().GetZoomY() );
+ }
+
+ // update change dialog
+
+ if ( rThisFrame.HasChildWindow(FID_CHG_ACCEPT) )
+ {
+ SfxChildWindow* pChild = rThisFrame.GetChildWindow(FID_CHG_ACCEPT);
+ if (pChild)
+ {
+ static_cast<ScAcceptChgDlgWrapper*>(pChild)->ReInitDlg();
+ }
+ }
+
+ if(pScMod->IsRefDialogOpen())
+ {
+ sal_uInt16 nModRefDlgId=pScMod->GetCurRefDlgId();
+ SfxChildWindow* pChildWnd = rThisFrame.GetChildWindow( nModRefDlgId );
+ if ( pChildWnd )
+ {
+ if (auto pController = pChildWnd->GetController())
+ {
+ IAnyRefDialog* pRefDlg = dynamic_cast<IAnyRefDialog*>(pController.get());
+ if (pRefDlg)
+ pRefDlg->ViewShellChanged();
+ }
+ }
+ }
+ }
+
+ // don't call CheckSelectionTransfer here - activating a view should not change the
+ // primary selection (may be happening just because the mouse was moved over the window)
+
+ if (!inChartOrMathContext(this))
+ {
+ ContextChangeEventMultiplexer::NotifyContextChange(
+ GetController(),
+ vcl::EnumContext::Context::Default);
+ }
+}
+
+void ScTabViewShell::Deactivate(bool bMDI)
+{
+ HideTip();
+
+ ScDocument& rDoc = GetViewData().GetDocument();
+
+ ScChangeTrack* pChanges = rDoc.GetChangeTrack();
+
+ if(pChanges!=nullptr)
+ {
+ Link<ScChangeTrack&,void> aLink;
+ pChanges->SetModifiedLink(aLink);
+ }
+
+ SfxViewShell::Deactivate(bMDI);
+ bIsActive = false;
+ ScInputHandler* pHdl = SC_MOD()->GetInputHdl(this);
+
+ if( bMDI && !comphelper::LibreOfficeKit::isActive())
+ {
+ // during shell deactivation, shells must not be switched, or the loop
+ // through the shell stack (in SfxDispatcher::DoDeactivate_Impl) will not work
+ bool bOldDontSwitch = bDontSwitch;
+ bDontSwitch = true;
+
+ ActivateView( false, false );
+
+ if ( GetViewFrame().GetFrame().IsInPlace() ) // inplace
+ GetViewData().GetDocShell()->UpdateOle(GetViewData(), true);
+
+ if ( pHdl )
+ pHdl->NotifyChange( nullptr, true ); // timer-delayed due to document switching
+
+ if (pScActiveViewShell == this)
+ pScActiveViewShell = nullptr;
+
+ bDontSwitch = bOldDontSwitch;
+ }
+ else
+ {
+ HideNoteMarker(); // note marker
+
+ if ( pHdl )
+ pHdl->HideTip(); // Hide formula auto input tip
+ }
+}
+
+void ScTabViewShell::SetActive()
+{
+ // SFX-View would like to activate itself, since then magical things would happen
+ // (eg else the designer may crash)
+ ActiveGrabFocus();
+}
+
+bool ScTabViewShell::PrepareClose(bool bUI)
+{
+ comphelper::FlagRestorationGuard aFlagGuard(bInPrepareClose, true);
+
+ // Call EnterHandler even in formula mode here,
+ // so a formula change in an embedded object isn't lost
+ // (ScDocShell::PrepareClose isn't called then).
+ ScInputHandler* pHdl = SC_MOD()->GetInputHdl( this );
+ if ( pHdl && pHdl->IsInputMode() )
+ {
+ pHdl->EnterHandler();
+ }
+
+ // draw text edit mode must be closed
+ FuPoor* pPoor = GetDrawFuncPtr();
+ if (pPoor && IsDrawTextShell())
+ {
+ // "clean" end of text edit, including note handling, subshells and draw func switching,
+ // as in FuDraw and ScTabView::DrawDeselectAll
+ GetViewData().GetDispatcher().Execute( pPoor->GetSlotID(), SfxCallMode::SLOT | SfxCallMode::RECORD );
+ }
+ ScDrawView* pDrView = GetScDrawView();
+ if ( pDrView )
+ {
+ // force end of text edit, to be safe
+ // ScEndTextEdit must always be used, to ensure correct UndoManager
+ pDrView->ScEndTextEdit();
+ }
+
+ if ( pFormShell )
+ {
+ bool bRet = pFormShell->PrepareClose(bUI);
+ if (!bRet)
+ return bRet;
+ }
+ return SfxViewShell::PrepareClose(bUI);
+}
+
+// calculate zoom for in-place
+// from the ratio of VisArea and window size of GridWin
+
+void ScTabViewShell::UpdateOleZoom()
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ if ( pDocSh->GetCreateMode() == SfxObjectCreateMode::EMBEDDED )
+ {
+ //TODO/LATER: is there a difference between the two GetVisArea methods?
+ Size aObjSize = static_cast<const SfxObjectShell*>(pDocSh)->GetVisArea().GetSize();
+ if ( !aObjSize.IsEmpty() )
+ {
+ vcl::Window* pWin = GetActiveWin();
+ Size aWinHMM = pWin->PixelToLogic(pWin->GetOutputSizePixel(), MapMode(MapUnit::Map100thMM));
+ SetZoomFactor( Fraction( aWinHMM.Width(),aObjSize.Width() ),
+ Fraction( aWinHMM.Height(),aObjSize.Height() ) );
+ }
+ }
+}
+
+void ScTabViewShell::InnerResizePixel( const Point &rOfs, const Size &rSize, bool inplaceEditModeChange )
+{
+ Size aNewSize( rSize );
+ if ( GetViewFrame().GetFrame().IsInPlace() )
+ {
+ SvBorder aBorder;
+ GetBorderSize( aBorder, rSize );
+ SetBorderPixel( aBorder );
+
+ Size aObjSize = GetObjectShell()->GetVisArea().GetSize();
+
+ Size aSize( rSize );
+ aSize.AdjustWidth( -(aBorder.Left() + aBorder.Right()) );
+ aSize.AdjustHeight( -(aBorder.Top() + aBorder.Bottom()) );
+
+ if ( !aObjSize.IsEmpty() )
+ {
+ Size aLogicSize = GetWindow()->PixelToLogic(aSize, MapMode(MapUnit::Map100thMM));
+ SfxViewShell::SetZoomFactor( Fraction( aLogicSize.Width(),aObjSize.Width() ),
+ Fraction( aLogicSize.Height(),aObjSize.Height() ) );
+ }
+
+ Point aPos( rOfs );
+ aPos.AdjustX(aBorder.Left() );
+ aPos.AdjustY(aBorder.Top() );
+ GetWindow()->SetPosSizePixel( aPos, aSize );
+ }
+ else
+ {
+ SvBorder aBorder;
+ GetBorderSize( aBorder, rSize );
+ SetBorderPixel( aBorder );
+ aNewSize.AdjustWidth(aBorder.Left() + aBorder.Right() );
+ aNewSize.AdjustHeight(aBorder.Top() + aBorder.Bottom() );
+ }
+
+ DoResize( rOfs, aNewSize, true ); // rSize = size of gridwin
+
+ UpdateOleZoom(); // calculate zoom for in-place
+
+ if (!inplaceEditModeChange)
+ {
+ GetViewData().GetDocShell()->SetDocumentModified();
+ }
+}
+
+void ScTabViewShell::OuterResizePixel( const Point &rOfs, const Size &rSize )
+{
+ SvBorder aBorder;
+ GetBorderSize( aBorder, rSize );
+ SetBorderPixel( aBorder );
+
+ DoResize( rOfs, rSize ); // position and size of tabview as passed
+
+ // ForceMove as replacement for Sfx-Move mechanism
+ // (aWinPos must be kept current, so that ForceMove works for Ole deactivation)
+
+ ForceMove();
+}
+
+void ScTabViewShell::SetZoomFactor( const Fraction &rZoomX, const Fraction &rZoomY )
+{
+ // for OLE...
+
+ Fraction aFrac20( 1,5 );
+ Fraction aFrac400( 4,1 );
+
+ Fraction aNewX( rZoomX );
+ if ( aNewX < aFrac20 )
+ aNewX = aFrac20;
+ if ( aNewX > aFrac400 )
+ aNewX = aFrac400;
+ Fraction aNewY( rZoomY );
+ if ( aNewY < aFrac20 )
+ aNewY = aFrac20;
+ if ( aNewY > aFrac400 )
+ aNewY = aFrac400;
+
+ GetViewData().UpdateScreenZoom( aNewX, aNewY );
+ SetZoom( aNewX, aNewY, true );
+
+ PaintGrid();
+ PaintTop();
+ PaintLeft();
+
+ SfxViewShell::SetZoomFactor( rZoomX, rZoomY );
+}
+
+void ScTabViewShell::QueryObjAreaPixel( tools::Rectangle& rRect ) const
+{
+ // adjust to entire cells (in 1/100 mm)
+
+ Size aPixelSize = rRect.GetSize();
+ vcl::Window* pWin = const_cast<ScTabViewShell*>(this)->GetActiveWin();
+ Size aLogicSize = pWin->PixelToLogic( aPixelSize );
+
+ const ScViewData& rViewData = GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ ScSplitPos ePos = rViewData.GetActivePart();
+ SCCOL nCol = rViewData.GetPosX(WhichH(ePos));
+ SCROW nRow = rViewData.GetPosY(WhichV(ePos));
+ SCTAB nTab = rViewData.GetTabNo();
+ bool bNegativePage = rDoc.IsNegativePage( nTab );
+
+ tools::Rectangle aLogicRect = rDoc.GetMMRect( nCol, nRow, nCol, nRow, nTab );
+ if ( bNegativePage )
+ {
+ // use right edge of aLogicRect, and aLogicSize
+ aLogicRect.SetLeft( aLogicRect.Right() - aLogicSize.Width() + 1 ); // Right() is set below
+ }
+ aLogicRect.SetSize( aLogicSize );
+
+ rViewData.GetDocShell()->SnapVisArea( aLogicRect );
+
+ rRect.SetSize( pWin->LogicToPixel( aLogicRect.GetSize() ) );
+}
+
+void ScTabViewShell::Move()
+{
+ Point aNewPos = GetViewFrame().GetWindow().OutputToScreenPixel(Point());
+
+ if (aNewPos != aWinPos)
+ {
+ StopMarking();
+ aWinPos = aNewPos;
+ }
+}
+
+void ScTabViewShell::ShowCursor(bool /* bOn */)
+{
+/*!!! ShowCursor is not called as a pair as in gridwin.
+ here the CursorLockCount for Gridwin must be set directly to 0
+
+ if (bOn)
+ ShowAllCursors();
+ else
+ HideAllCursors();
+*/
+}
+
+void ScTabViewShell::WriteUserData(OUString& rData, bool /* bBrowse */)
+{
+ GetViewData().WriteUserData(rData);
+}
+
+void ScTabViewShell::WriteUserDataSequence (uno::Sequence < beans::PropertyValue >& rSettings )
+{
+ GetViewData().WriteUserDataSequence(rSettings);
+}
+
+void ScTabViewShell::ReadUserData(const OUString& rData, bool /* bBrowse */)
+{
+ if ( !GetViewData().GetDocShell()->IsPreview() )
+ DoReadUserData( rData );
+}
+
+void ScTabViewShell::ReadUserDataSequence (const uno::Sequence < beans::PropertyValue >& rSettings )
+{
+ if ( !GetViewData().GetDocShell()->IsPreview() )
+ DoReadUserDataSequence( rSettings );
+}
+
+void ScTabViewShell::DoReadUserDataSequence( const uno::Sequence < beans::PropertyValue >& rSettings )
+{
+ vcl::Window* pOldWin = GetActiveWin();
+ bool bFocus = pOldWin && pOldWin->HasFocus();
+
+ GetViewData().ReadUserDataSequence(rSettings);
+ SetTabNo( GetViewData().GetTabNo(), true );
+
+ if ( GetViewData().IsPagebreakMode() )
+ SetCurSubShell( GetCurObjectSelectionType(), true );
+
+ vcl::Window* pNewWin = GetActiveWin();
+ if (pNewWin && pNewWin != pOldWin)
+ {
+ SetWindow( pNewWin ); //! is this ViewShell always active???
+ if (bFocus)
+ pNewWin->GrabFocus();
+ WindowChanged(); // drawing layer (for instance #56771#)
+ }
+
+ if (GetViewData().GetHSplitMode() == SC_SPLIT_FIX ||
+ GetViewData().GetVSplitMode() == SC_SPLIT_FIX)
+ {
+ InvalidateSplit();
+ }
+
+ ZoomChanged();
+
+ TestHintWindow();
+
+ //! if ViewData has more tables than document, remove tables in ViewData
+}
+
+// DoReadUserData is also called from ctor when switching from print preview
+
+void ScTabViewShell::DoReadUserData( std::u16string_view rData )
+{
+ vcl::Window* pOldWin = GetActiveWin();
+ bool bFocus = pOldWin && pOldWin->HasFocus();
+
+ GetViewData().ReadUserData(rData);
+ SetTabNo( GetViewData().GetTabNo(), true );
+
+ if ( GetViewData().IsPagebreakMode() )
+ SetCurSubShell( GetCurObjectSelectionType(), true );
+
+ vcl::Window* pNewWin = GetActiveWin();
+ if (pNewWin && pNewWin != pOldWin)
+ {
+ SetWindow( pNewWin ); //! is this ViewShell always active???
+ if (bFocus)
+ pNewWin->GrabFocus();
+ WindowChanged(); // drawing layer (for instance #56771#)
+ }
+
+ if (GetViewData().GetHSplitMode() == SC_SPLIT_FIX ||
+ GetViewData().GetVSplitMode() == SC_SPLIT_FIX)
+ {
+ InvalidateSplit();
+ }
+
+ ZoomChanged();
+
+ TestHintWindow();
+
+ //! if ViewData has more tables than document, remove tables in ViewData
+}
+
+void ScTabViewShell::UpdateDrawShell()
+{
+ // Called after user interaction that may delete the selected drawing object.
+ // Remove DrawShell if nothing is selected.
+
+ SdrView* pDrView = GetScDrawView();
+ if ( pDrView && !pDrView->AreObjectsMarked() && !IsDrawSelMode() )
+ SetDrawShell( false );
+}
+
+void ScTabViewShell::SetDrawShellOrSub()
+{
+ bActiveDrawSh = true;
+
+ if(bActiveDrawFormSh)
+ {
+ SetCurSubShell(OST_DrawForm);
+ }
+ else if(bActiveGraphicSh)
+ {
+ SetCurSubShell(OST_Graphic);
+ }
+ else if(bActiveMediaSh)
+ {
+ SetCurSubShell(OST_Media);
+ }
+ else if(bActiveChartSh)
+ {
+ SetCurSubShell(OST_Chart);
+ }
+ else if(bActiveOleObjectSh)
+ {
+ SetCurSubShell(OST_OleObject);
+ }
+ else
+ {
+ SetCurSubShell(OST_Drawing, true /* force: different toolbars are
+ visible concerning shape type
+ and shape state */);
+ }
+}
+
+void ScTabViewShell::SetDrawShell( bool bActive )
+{
+ if(bActive)
+ {
+ SetCurSubShell(OST_Drawing, true /* force: different toolbars are
+ visible concerning shape type
+ and shape state */);
+ }
+ else
+ {
+ if(bActiveDrawFormSh || bActiveDrawSh ||
+ bActiveGraphicSh || bActiveMediaSh || bActiveOleObjectSh||
+ bActiveChartSh || bActiveDrawTextSh)
+ {
+ SetCurSubShell(OST_Cell);
+ }
+ bActiveDrawFormSh=false;
+ bActiveGraphicSh=false;
+ bActiveMediaSh=false;
+ bActiveOleObjectSh=false;
+ bActiveChartSh=false;
+ }
+
+ bool bWasDraw = bActiveDrawSh || bActiveDrawTextSh;
+
+ bActiveDrawSh = bActive;
+ bActiveDrawTextSh = false;
+
+ if ( !bActive )
+ {
+ ResetDrawDragMode(); // switch off Mirror / Rotate
+
+ if (bWasDraw && (GetViewData().GetHSplitMode() == SC_SPLIT_FIX ||
+ GetViewData().GetVSplitMode() == SC_SPLIT_FIX))
+ {
+ // adjust active part to cursor, etc.
+ MoveCursorAbs( GetViewData().GetCurX(), GetViewData().GetCurY(),
+ SC_FOLLOW_NONE, false, false, true );
+ }
+ }
+}
+
+void ScTabViewShell::SetDrawTextShell( bool bActive )
+{
+ bActiveDrawTextSh = bActive;
+ if ( bActive )
+ {
+ bActiveDrawFormSh=false;
+ bActiveGraphicSh=false;
+ bActiveMediaSh=false;
+ bActiveOleObjectSh=false;
+ bActiveChartSh=false;
+ bActiveDrawSh = false;
+ SetCurSubShell(OST_DrawText);
+ }
+ else
+ SetCurSubShell(OST_Cell);
+
+}
+
+void ScTabViewShell::SetPivotShell( bool bActive )
+{
+ // SetPivotShell is called from CursorPosChanged every time
+ // -> don't change anything except switching between cell and pivot shell
+
+ if (eCurOST != OST_Pivot && eCurOST != OST_Cell)
+ return;
+
+ if ( bActive )
+ {
+ bActiveDrawTextSh = bActiveDrawSh = false;
+ bActiveDrawFormSh=false;
+ bActiveGraphicSh=false;
+ bActiveMediaSh=false;
+ bActiveOleObjectSh=false;
+ bActiveChartSh=false;
+ SetCurSubShell(OST_Pivot);
+ }
+ else
+ SetCurSubShell(OST_Cell);
+}
+
+void ScTabViewShell::SetSparklineShell(bool bActive)
+{
+ if (eCurOST != OST_Sparkline && eCurOST != OST_Cell)
+ return;
+
+ if (bActive)
+ {
+ bActiveDrawTextSh = bActiveDrawSh = false;
+ bActiveDrawFormSh=false;
+ bActiveGraphicSh=false;
+ bActiveMediaSh=false;
+ bActiveOleObjectSh=false;
+ bActiveChartSh=false;
+ SetCurSubShell(OST_Sparkline);
+ }
+ else
+ SetCurSubShell(OST_Cell);
+}
+
+void ScTabViewShell::SetAuditShell( bool bActive )
+{
+ if ( bActive )
+ {
+ bActiveDrawTextSh = bActiveDrawSh = false;
+ bActiveDrawFormSh=false;
+ bActiveGraphicSh=false;
+ bActiveMediaSh=false;
+ bActiveOleObjectSh=false;
+ bActiveChartSh=false;
+ SetCurSubShell(OST_Auditing);
+ }
+ else
+ SetCurSubShell(OST_Cell);
+}
+
+void ScTabViewShell::SetDrawFormShell( bool bActive )
+{
+ bActiveDrawFormSh = bActive;
+
+ if(bActiveDrawFormSh)
+ SetCurSubShell(OST_DrawForm);
+}
+void ScTabViewShell::SetChartShell( bool bActive )
+{
+ bActiveChartSh = bActive;
+
+ if(bActiveChartSh)
+ SetCurSubShell(OST_Chart);
+}
+
+void ScTabViewShell::SetGraphicShell( bool bActive )
+{
+ bActiveGraphicSh = bActive;
+
+ if(bActiveGraphicSh)
+ SetCurSubShell(OST_Graphic);
+}
+
+void ScTabViewShell::SetMediaShell( bool bActive )
+{
+ bActiveMediaSh = bActive;
+
+ if(bActiveMediaSh)
+ SetCurSubShell(OST_Media);
+}
+
+void ScTabViewShell::SetOleObjectShell( bool bActive )
+{
+ bActiveOleObjectSh = bActive;
+
+ if(bActiveOleObjectSh)
+ SetCurSubShell(OST_OleObject);
+ else
+ SetCurSubShell(OST_Cell);
+}
+
+void ScTabViewShell::SetEditShell(EditView* pView, bool bActive )
+{
+ if(bActive)
+ {
+ if (pEditShell)
+ pEditShell->SetEditView( pView );
+ else
+ pEditShell.reset( new ScEditShell(pView, GetViewData()) );
+
+ SetCurSubShell(OST_Editing);
+ }
+ else if(bActiveEditSh)
+ {
+ SetCurSubShell(OST_Cell);
+ }
+ bActiveEditSh = bActive;
+}
+
+void ScTabViewShell::SetCurSubShell(ObjectSelectionType eOST, bool bForce)
+{
+ ScViewData& rViewData = GetViewData();
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+
+ if(bDontSwitch) return;
+
+ if(!pCellShell) // is anyway always used
+ {
+ pCellShell.reset(new ScCellShell(GetViewData(), GetFrameWin()));
+ pCellShell->SetRepeatTarget( &aTarget );
+ }
+
+ bool bPgBrk = rViewData.IsPagebreakMode();
+
+ if(bPgBrk && !pPageBreakShell)
+ {
+ pPageBreakShell.reset( new ScPageBreakShell( this ) );
+ pPageBreakShell->SetRepeatTarget( &aTarget );
+ }
+
+ if ( !(eOST!=eCurOST || bForce) )
+ return;
+
+ bool bCellBrush = false; // "format paint brush" allowed for cells
+ bool bDrawBrush = false; // "format paint brush" allowed for drawing objects
+
+ if(eCurOST!=OST_NONE) RemoveSubShell();
+
+ if (pFormShell && !bFormShellAtTop)
+ AddSubShell(*pFormShell); // add below own subshells
+
+ switch(eOST)
+ {
+ case OST_Cell:
+ {
+ AddSubShell(*pCellShell);
+ if(bPgBrk) AddSubShell(*pPageBreakShell);
+ bCellBrush = true;
+ }
+ break;
+ case OST_Editing:
+ {
+ AddSubShell(*pCellShell);
+ if(bPgBrk) AddSubShell(*pPageBreakShell);
+
+ if(pEditShell)
+ {
+ AddSubShell(*pEditShell);
+ }
+ }
+ break;
+ case OST_DrawText:
+ {
+ if ( !pDrawTextShell )
+ {
+ pDocSh->MakeDrawLayer();
+ pDrawTextShell.reset( new ScDrawTextObjectBar(GetViewData()) );
+ }
+ AddSubShell(*pDrawTextShell);
+ }
+ break;
+ case OST_Drawing:
+ {
+ if (svx::checkForSelectedCustomShapes(
+ GetScDrawView(), true /* bOnlyExtruded */ )) {
+ if (pExtrusionBarShell == nullptr)
+ pExtrusionBarShell.reset( new svx::ExtrusionBar(this) );
+ AddSubShell( *pExtrusionBarShell );
+ }
+
+ if (svx::checkForSelectedFontWork(
+ GetScDrawView() )) {
+ if (pFontworkBarShell == nullptr)
+ pFontworkBarShell.reset( new svx::FontworkBar(this) );
+ AddSubShell( *pFontworkBarShell );
+ }
+
+ if ( !pDrawShell )
+ {
+ pDocSh->MakeDrawLayer();
+ pDrawShell.reset(new ScDrawShell(GetViewData()));
+ pDrawShell->SetRepeatTarget( &aTarget );
+ }
+ AddSubShell(*pDrawShell);
+ bDrawBrush = true;
+ }
+ break;
+
+ case OST_DrawForm:
+ {
+ if ( !pDrawFormShell )
+ {
+ pDocSh->MakeDrawLayer();
+ pDrawFormShell.reset( new ScDrawFormShell(GetViewData()) );
+ pDrawFormShell->SetRepeatTarget( &aTarget );
+ }
+ AddSubShell(*pDrawFormShell);
+ bDrawBrush = true;
+ }
+ break;
+
+ case OST_Chart:
+ {
+ if ( !pChartShell )
+ {
+ pDocSh->MakeDrawLayer();
+ pChartShell.reset( new ScChartShell(GetViewData()) );
+ pChartShell->SetRepeatTarget( &aTarget );
+ }
+ AddSubShell(*pChartShell);
+ bDrawBrush = true;
+ }
+ break;
+
+ case OST_OleObject:
+ {
+ if ( !pOleObjectShell )
+ {
+ pDocSh->MakeDrawLayer();
+ pOleObjectShell.reset( new ScOleObjectShell(GetViewData()) );
+ pOleObjectShell->SetRepeatTarget( &aTarget );
+ }
+ AddSubShell(*pOleObjectShell);
+ bDrawBrush = true;
+ }
+ break;
+
+ case OST_Graphic:
+ {
+ if ( !pGraphicShell)
+ {
+ pDocSh->MakeDrawLayer();
+ pGraphicShell.reset( new ScGraphicShell(GetViewData()) );
+ pGraphicShell->SetRepeatTarget( &aTarget );
+ }
+ AddSubShell(*pGraphicShell);
+ bDrawBrush = true;
+ }
+ break;
+
+ case OST_Media:
+ {
+ if ( !pMediaShell)
+ {
+ pDocSh->MakeDrawLayer();
+ pMediaShell.reset( new ScMediaShell(GetViewData()) );
+ pMediaShell->SetRepeatTarget( &aTarget );
+ }
+ AddSubShell(*pMediaShell);
+ }
+ break;
+
+ case OST_Pivot:
+ {
+ AddSubShell(*pCellShell);
+ if(bPgBrk) AddSubShell(*pPageBreakShell);
+
+ if ( !pPivotShell )
+ {
+ pPivotShell.reset( new ScPivotShell( this ) );
+ pPivotShell->SetRepeatTarget( &aTarget );
+ }
+ AddSubShell(*pPivotShell);
+ bCellBrush = true;
+ }
+ break;
+ case OST_Auditing:
+ {
+ AddSubShell(*pCellShell);
+ if(bPgBrk) AddSubShell(*pPageBreakShell);
+
+ if ( !pAuditingShell )
+ {
+ pDocSh->MakeDrawLayer(); // the waiting time rather now as on the click
+
+ pAuditingShell.reset( new ScAuditingShell(GetViewData()) );
+ pAuditingShell->SetRepeatTarget( &aTarget );
+ }
+ AddSubShell(*pAuditingShell);
+ bCellBrush = true;
+ }
+ break;
+ case OST_Sparkline:
+ {
+ AddSubShell(*pCellShell);
+ if(bPgBrk) AddSubShell(*pPageBreakShell);
+
+ if (!m_pSparklineShell)
+ {
+ m_pSparklineShell.reset(new sc::SparklineShell(this));
+ m_pSparklineShell->SetRepeatTarget(&aTarget);
+ }
+ AddSubShell(*m_pSparklineShell);
+ bCellBrush = true;
+ }
+ break;
+ default:
+ OSL_FAIL("wrong shell requested");
+ break;
+ }
+
+ if (pFormShell && bFormShellAtTop)
+ AddSubShell(*pFormShell); // add on top of own subshells
+
+ eCurOST=eOST;
+
+ // abort "format paint brush" when switching to an incompatible shell
+ if ( ( GetBrushDocument() && !bCellBrush ) || ( GetDrawBrushSet() && !bDrawBrush ) )
+ ResetBrushDocument();
+}
+
+void ScTabViewShell::SetFormShellAtTop( bool bSet )
+{
+ if ( pFormShell && !bSet )
+ pFormShell->ForgetActiveControl(); // let the FormShell know it no longer has the focus
+
+ if ( bFormShellAtTop != bSet )
+ {
+ bFormShellAtTop = bSet;
+ SetCurSubShell( GetCurObjectSelectionType(), true );
+ }
+}
+
+IMPL_LINK_NOARG(ScTabViewShell, FormControlActivated, LinkParamNone*, void)
+{
+ // a form control got the focus, so the form shell has to be on top
+ SetFormShellAtTop( true );
+}
+
+// GetMySubShell / SetMySubShell: simulate old behavior,
+// so that there is only one SubShell (only within the 5 own SubShells)
+
+SfxShell* ScTabViewShell::GetMySubShell() const
+{
+ // GetSubShell() was const before, and GetSubShell(sal_uInt16) should also be const...
+
+ sal_uInt16 nPos = 0;
+ SfxShell* pSub = const_cast<ScTabViewShell*>(this)->GetSubShell(nPos);
+ while (pSub)
+ {
+ if (pSub == pDrawShell.get() || pSub == pDrawTextShell.get() || pSub == pEditShell.get() ||
+ pSub == pPivotShell.get() || pSub == pAuditingShell.get() || pSub == pDrawFormShell.get() ||
+ pSub == pCellShell.get() || pSub == pOleObjectShell.get() || pSub == pChartShell.get() ||
+ pSub == pGraphicShell.get() || pSub == pMediaShell.get() || pSub == pPageBreakShell.get() ||
+ pSub == m_pSparklineShell.get())
+ {
+ return pSub; // found
+ }
+
+ pSub = const_cast<ScTabViewShell*>(this)->GetSubShell(++nPos);
+ }
+ return nullptr; // none from mine present
+}
+
+bool ScTabViewShell::IsDrawTextShell() const
+{
+ return ( pDrawTextShell && ( GetMySubShell() == pDrawTextShell.get() ) );
+}
+
+bool ScTabViewShell::IsAuditShell() const
+{
+ return ( pAuditingShell && ( GetMySubShell() == pAuditingShell.get() ) );
+}
+
+void ScTabViewShell::SetDrawTextUndo( SfxUndoManager* pNewUndoMgr )
+{
+ // Default: undo manager for DocShell
+ if (!pNewUndoMgr)
+ pNewUndoMgr = GetViewData().GetDocShell()->GetUndoManager();
+
+ if (pDrawTextShell)
+ {
+ pDrawTextShell->SetUndoManager(pNewUndoMgr);
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ if ( pNewUndoMgr == pDocSh->GetUndoManager() &&
+ !pDocSh->GetDocument().IsUndoEnabled() )
+ {
+ pNewUndoMgr->SetMaxUndoActionCount( 0 );
+ }
+ }
+ else
+ {
+ OSL_FAIL("SetDrawTextUndo without DrawTextShell");
+ }
+}
+
+ScTabViewShell* ScTabViewShell::GetActiveViewShell()
+{
+ return dynamic_cast< ScTabViewShell *>( Current() );
+}
+
+SfxPrinter* ScTabViewShell::GetPrinter( bool bCreate )
+{
+ // printer is always present (is created for the FontList already on start-up)
+ return GetViewData().GetDocShell()->GetPrinter(bCreate);
+}
+
+sal_uInt16 ScTabViewShell::SetPrinter( SfxPrinter *pNewPrinter, SfxPrinterChangeFlags nDiffFlags )
+{
+ return GetViewData().GetDocShell()->SetPrinter( pNewPrinter, nDiffFlags );
+}
+
+bool ScTabViewShell::HasPrintOptionsPage() const
+{
+ return true;
+}
+
+std::unique_ptr<SfxTabPage> ScTabViewShell::CreatePrintOptionsPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rOptions )
+{
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+ ::CreateTabPage ScTpPrintOptionsCreate = pFact->GetTabPageCreatorFunc(RID_SC_TP_PRINT);
+ if ( ScTpPrintOptionsCreate )
+ return ScTpPrintOptionsCreate(pPage, pController, &rOptions);
+ return nullptr;
+}
+
+void ScTabViewShell::StopEditShell()
+{
+ if ( pEditShell != nullptr && !bDontSwitch )
+ SetEditShell(nullptr, false );
+}
+
+// close handler to ensure function of dialog:
+
+IMPL_LINK_NOARG(ScTabViewShell, SimpleRefClose, const OUString*, void)
+{
+ SfxInPlaceClient* pClient = GetIPClient();
+ if ( pClient && pClient->IsObjectInPlaceActive() )
+ {
+ // If range selection was started with an active embedded object,
+ // switch back to original sheet (while the dialog is still open).
+
+ SetTabNo( GetViewData().GetRefTabNo() );
+ }
+
+ ScSimpleRefDlgWrapper::SetAutoReOpen( true );
+}
+
+// handlers to call UNO listeners:
+
+static ScTabViewObj* lcl_GetViewObj( const ScTabViewShell& rShell )
+{
+ ScTabViewObj* pRet = nullptr;
+ SfxViewFrame& rViewFrame = rShell.GetViewFrame();
+ SfxFrame& rFrame = rViewFrame.GetFrame();
+ uno::Reference<frame::XController> xController = rFrame.GetController();
+ if (xController.is())
+ pRet = dynamic_cast<ScTabViewObj*>( xController.get() );
+ return pRet;
+}
+
+IMPL_LINK( ScTabViewShell, SimpleRefDone, const OUString&, aResult, void )
+{
+ ScTabViewObj* pImpObj = lcl_GetViewObj( *this );
+ if ( pImpObj )
+ pImpObj->RangeSelDone( aResult );
+}
+
+IMPL_LINK( ScTabViewShell, SimpleRefAborted, const OUString&, rResult, void )
+{
+ ScTabViewObj* pImpObj = lcl_GetViewObj( *this );
+ if ( pImpObj )
+ pImpObj->RangeSelAborted( rResult );
+}
+
+IMPL_LINK( ScTabViewShell, SimpleRefChange, const OUString&, rResult, void )
+{
+ ScTabViewObj* pImpObj = lcl_GetViewObj( *this );
+ if ( pImpObj )
+ pImpObj->RangeSelChanged( rResult );
+}
+
+void ScTabViewShell::StartSimpleRefDialog(
+ const OUString& rTitle, const OUString& rInitVal,
+ bool bCloseOnButtonUp, bool bSingleCell, bool bMultiSelection )
+{
+ SfxViewFrame& rViewFrm = GetViewFrame();
+
+ if ( GetActiveViewShell() != this )
+ {
+ // #i18833# / #i34499# The API method can be called for a view that's not active.
+ // Then the view has to be activated first, the same way as in Execute for SID_CURRENTDOC.
+ // Can't use GrabFocus here, because it needs to take effect immediately.
+
+ rViewFrm.GetFrame().Appear();
+ }
+
+ sal_uInt16 nId = ScSimpleRefDlgWrapper::GetChildWindowId();
+
+ SC_MOD()->SetRefDialog( nId, true, &rViewFrm );
+
+ ScSimpleRefDlgWrapper* pWnd = static_cast<ScSimpleRefDlgWrapper*>(rViewFrm.GetChildWindow( nId ));
+ if (!pWnd)
+ return;
+
+ pWnd->SetCloseHdl( LINK( this, ScTabViewShell, SimpleRefClose ) );
+ pWnd->SetUnoLinks( LINK( this, ScTabViewShell, SimpleRefDone ),
+ LINK( this, ScTabViewShell, SimpleRefAborted ),
+ LINK( this, ScTabViewShell, SimpleRefChange ) );
+ pWnd->SetRefString( rInitVal );
+ pWnd->SetFlags( bCloseOnButtonUp, bSingleCell, bMultiSelection );
+ ScSimpleRefDlgWrapper::SetAutoReOpen( false );
+ if (auto xWin = pWnd->GetController())
+ xWin->set_title(rTitle);
+ pWnd->StartRefInput();
+}
+
+void ScTabViewShell::StopSimpleRefDialog()
+{
+ SfxViewFrame& rViewFrm = GetViewFrame();
+ sal_uInt16 nId = ScSimpleRefDlgWrapper::GetChildWindowId();
+
+ ScSimpleRefDlgWrapper* pWnd = static_cast<ScSimpleRefDlgWrapper*>(rViewFrm.GetChildWindow( nId ));
+ if (pWnd)
+ {
+ if (auto pWin = pWnd->GetController())
+ pWin->response(RET_CLOSE);
+ }
+}
+
+bool ScTabViewShell::TabKeyInput(const KeyEvent& rKEvt)
+{
+ ScModule* pScMod = SC_MOD();
+
+ SfxViewFrame& rThisFrame = GetViewFrame();
+ if ( rThisFrame.GetChildWindow( SID_OPENDLG_FUNCTION ) )
+ return false;
+
+ vcl::KeyCode aCode = rKEvt.GetKeyCode();
+ bool bShift = aCode.IsShift();
+ bool bControl = aCode.IsMod1();
+ bool bAlt = aCode.IsMod2();
+ sal_uInt16 nCode = aCode.GetCode();
+ bool bUsed = false;
+ bool bInPlace = pScMod->IsEditMode(); // Editengine gets all
+ bool bAnyEdit = pScMod->IsInputMode(); // only characters & backspace
+ bool bDraw = IsDrawTextEdit();
+
+ HideNoteMarker(); // note marker
+
+ // don't do extra HideCursor/ShowCursor calls if EnterHandler will switch to a different sheet
+ bool bOnRefSheet = ( GetViewData().GetRefTabNo() == GetViewData().GetTabNo() );
+ bool bHideCursor = ( ( nCode == KEY_RETURN && bInPlace ) || nCode == KEY_TAB ) && bOnRefSheet;
+
+ if (bHideCursor)
+ HideAllCursors();
+
+ ScDocument& rDoc = GetViewData().GetDocument();
+ rDoc.KeyInput(); // TimerDelays etc.
+
+ if( bInPlace )
+ {
+ bUsed = pScMod->InputKeyEvent( rKEvt ); // input
+ if( !bUsed )
+ bUsed = SfxViewShell::KeyInput( rKEvt ); // accelerators
+ }
+ else if( bAnyEdit )
+ {
+ bool bIsType = false;
+ sal_uInt16 nModi = aCode.GetModifier();
+ sal_uInt16 nGroup = aCode.GetGroup();
+
+ if ( nGroup == KEYGROUP_NUM || nGroup == KEYGROUP_ALPHA || nGroup == 0 )
+ if ( !bControl && !bAlt )
+ bIsType = true;
+
+ if ( nGroup == KEYGROUP_MISC )
+ switch ( nCode )
+ {
+ case KEY_RETURN:
+ bIsType = bControl && !bAlt; // Control, Shift-Control-Return
+ if ( !bIsType && nModi == 0 )
+ {
+ // Does the Input Handler also want a simple Return?
+
+ ScInputHandler* pHdl = pScMod->GetInputHdl(this);
+ bIsType = pHdl && pHdl->TakesReturn();
+ }
+ break;
+ case KEY_SPACE:
+ bIsType = !bControl && !bAlt; // without modifier or Shift-Space
+ break;
+ case KEY_ESCAPE:
+ bIsType = (nModi == 0); // only without modifier
+ break;
+ default:
+ bIsType = true;
+ }
+ else if (nCode == KEY_RIGHT && !bControl && !bShift && !bAlt)
+ {
+ ScInputHandler* pHdl = pScMod->GetInputHdl(this);
+ bIsType = pHdl && pHdl->HasPartialComplete();
+ }
+
+ if( bIsType )
+ bUsed = pScMod->InputKeyEvent( rKEvt ); // input
+
+ if( !bUsed )
+ bUsed = SfxViewShell::KeyInput( rKEvt ); // accelerators
+
+ if ( !bUsed && !bIsType && nCode != KEY_RETURN ) // input once again afterwards
+ bUsed = pScMod->InputKeyEvent( rKEvt );
+ }
+ else
+ {
+ // special case: copy/cut for multiselect -> error message
+ // (Slot is disabled, so SfxViewShell::KeyInput would be swallowed without a comment)
+ KeyFuncType eFunc = aCode.GetFunction();
+ if ( eFunc == KeyFuncType::CUT )
+ {
+ ScRange aDummy;
+ ScMarkType eMarkType = GetViewData().GetSimpleArea( aDummy );
+ if (eMarkType != SC_MARK_SIMPLE)
+ {
+ ErrorMessage(STR_NOMULTISELECT);
+ bUsed = true;
+ }
+ }
+ if (!bUsed)
+ bUsed = SfxViewShell::KeyInput( rKEvt ); // accelerators
+
+ // during inplace editing, some slots are handled by the
+ // container app and are executed during Window::KeyInput.
+ // -> don't pass keys to input handler that would be used there
+ // but should call slots instead.
+ bool bParent = ( GetViewFrame().GetFrame().IsInPlace() && eFunc != KeyFuncType::DONTKNOW );
+
+ if( !bUsed && !bDraw && nCode != KEY_RETURN && !bParent )
+ bUsed = pScMod->InputKeyEvent( rKEvt, true ); // input
+ }
+
+ if (!bInPlace && !bUsed && !bDraw)
+ {
+ switch (nCode)
+ {
+ case KEY_RETURN:
+ {
+ bool bNormal = !bControl && !bAlt;
+ if ( !bAnyEdit && bNormal )
+ {
+ // Depending on options, Enter switches to edit mode.
+ const ScInputOptions& rOpt = pScMod->GetInputOptions();
+ if ( rOpt.GetEnterEdit() )
+ {
+ pScMod->SetInputMode( SC_INPUT_TABLE );
+ bUsed = true;
+ }
+ }
+
+ bool bEditReturn = bControl && !bShift; // pass on to edit engine
+ if ( !bUsed && !bEditReturn )
+ {
+ if ( bOnRefSheet )
+ HideAllCursors();
+
+ ScEnterMode nMode = ScEnterMode::NORMAL;
+ if ( bShift && bControl )
+ nMode = ScEnterMode::MATRIX;
+ else if ( bAlt )
+ nMode = ScEnterMode::BLOCK;
+ pScMod->InputEnterHandler(nMode);
+
+ if (nMode == ScEnterMode::NORMAL)
+ {
+ if( bShift )
+ GetViewData().GetDispatcher().Execute( SID_CURSORENTERUP,
+ SfxCallMode::SLOT | SfxCallMode::RECORD );
+ else
+ GetViewData().GetDispatcher().Execute( SID_CURSORENTERDOWN,
+ SfxCallMode::SLOT | SfxCallMode::RECORD );
+ }
+ else
+ UpdateInputHandler(true);
+
+ if ( bOnRefSheet )
+ ShowAllCursors();
+
+ // here no UpdateInputHandler, since during reference input on another
+ // document this ViewShell is not the one that is used for input.
+
+ bUsed = true;
+ }
+ }
+ break;
+ }
+ }
+
+ // hard-code Alt-Cursor key, since Alt is not configurable
+
+ if ( !bUsed && bAlt && !bControl )
+ {
+ sal_uInt16 nSlotId = 0;
+ switch (nCode)
+ {
+ case KEY_UP:
+ ModifyCellSize( DIR_TOP, bShift );
+ bUsed = true;
+ break;
+ case KEY_DOWN:
+ ModifyCellSize( DIR_BOTTOM, bShift );
+ bUsed = true;
+ break;
+ case KEY_LEFT:
+ ModifyCellSize( DIR_LEFT, bShift );
+ bUsed = true;
+ break;
+ case KEY_RIGHT:
+ ModifyCellSize( DIR_RIGHT, bShift );
+ bUsed = true;
+ break;
+ case KEY_PAGEUP:
+ nSlotId = bShift ? SID_CURSORPAGELEFT_SEL : SID_CURSORPAGELEFT_;
+ break;
+ case KEY_PAGEDOWN:
+ nSlotId = bShift ? SID_CURSORPAGERIGHT_SEL : SID_CURSORPAGERIGHT_;
+ break;
+ case KEY_EQUAL:
+ {
+ // #tdf39302: Use "Alt + =" for autosum
+ if ( !bAnyEdit ) // Ignore shortcut if currently editing a cell
+ {
+ ScInputHandler* pHdl = pScMod->GetInputHdl(this);
+ if ( pHdl )
+ {
+ ScInputWindow* pWin = pHdl->GetInputWindow();
+ if ( pWin )
+ {
+ bool bRangeFinder = false;
+ bool bSubTotal = false;
+ pWin->AutoSum( bRangeFinder, bSubTotal, ocSum );
+ }
+ }
+
+ bUsed = true;
+ break;
+ }
+ }
+ }
+ if ( nSlotId )
+ {
+ GetViewData().GetDispatcher().Execute( nSlotId, SfxCallMode::SLOT | SfxCallMode::RECORD );
+ bUsed = true;
+ }
+ }
+
+ // use Ctrl+Alt+Shift+arrow keys to move the cursor in cells
+ // while keeping the last selection
+ if ( !bUsed && bAlt && bControl && bShift)
+ {
+ sal_uInt16 nSlotId = 0;
+ switch (nCode)
+ {
+ case KEY_UP:
+ nSlotId = SID_CURSORUP;
+ break;
+ case KEY_DOWN:
+ nSlotId = SID_CURSORDOWN;
+ break;
+ case KEY_LEFT:
+ nSlotId = SID_CURSORLEFT;
+ break;
+ case KEY_RIGHT:
+ nSlotId = SID_CURSORRIGHT;
+ break;
+ case KEY_PAGEUP:
+ nSlotId = SID_CURSORPAGEUP;
+ break;
+ case KEY_PAGEDOWN:
+ nSlotId = SID_CURSORPAGEDOWN;
+ break;
+ case KEY_HOME:
+ nSlotId = SID_CURSORHOME;
+ break;
+ case KEY_END:
+ nSlotId = SID_CURSOREND;
+ break;
+ default:
+ nSlotId = 0;
+ break;
+ }
+ if ( nSlotId )
+ {
+ sal_uInt16 nMode = GetLockedModifiers();
+ LockModifiers(KEY_MOD1);
+ GetViewData().GetDispatcher().Execute( nSlotId, SfxCallMode::SLOT | SfxCallMode::RECORD );
+ LockModifiers(nMode);
+ bUsed = true;
+ }
+ }
+ if (bHideCursor)
+ ShowAllCursors();
+
+ return bUsed;
+}
+
+bool ScTabViewShell::SfxKeyInput(const KeyEvent& rKeyEvent)
+{
+ return SfxViewShell::KeyInput( rKeyEvent );
+}
+
+bool ScTabViewShell::KeyInput( const KeyEvent &rKeyEvent )
+{
+ return TabKeyInput( rKeyEvent );
+}
+
+void ScTabViewShell::Construct( TriState nForceDesignMode )
+{
+ SfxApplication* pSfxApp = SfxGetpApp();
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ bReadOnly = pDocSh->IsReadOnly();
+ bIsActive = false;
+
+ EnableAutoSpell(rDoc.GetDocOptions().IsAutoSpell());
+
+ SetName("View"); // for SBX
+ Color aColBlack( COL_BLACK );
+ SetPool( &SC_MOD()->GetPool() );
+ SetWindow( GetActiveWin() );
+
+ pCurFrameLine.reset( new ::editeng::SvxBorderLine(&aColBlack, 20, SvxBorderLineStyle::SOLID) );
+ StartListening(*GetViewData().GetDocShell(), DuplicateHandling::Prevent);
+ StartListening(GetViewFrame(), DuplicateHandling::Prevent);
+ StartListening(*pSfxApp, DuplicateHandling::Prevent); // #i62045# #i62046# application is needed for Calc's own hints
+
+ SfxViewFrame* pFirst = SfxViewFrame::GetFirst(pDocSh);
+ bool bFirstView = !pFirst
+ || (pFirst == &GetViewFrame() && !SfxViewFrame::GetNext(*pFirst,pDocSh));
+
+ if ( pDocSh->GetCreateMode() == SfxObjectCreateMode::EMBEDDED )
+ {
+ //TODO/LATER: is there a difference between the two GetVisArea methods?
+ tools::Rectangle aVisArea = static_cast<const SfxObjectShell*>(pDocSh)->GetVisArea();
+
+ SCTAB nVisTab = rDoc.GetVisibleTab();
+ if (!rDoc.HasTable(nVisTab))
+ {
+ nVisTab = 0;
+ rDoc.SetVisibleTab(nVisTab);
+ }
+ SetTabNo( nVisTab );
+ bool bNegativePage = rDoc.IsNegativePage( nVisTab );
+ // show the right cells
+ GetViewData().SetScreenPos( bNegativePage ? aVisArea.TopRight() : aVisArea.TopLeft() );
+
+ if ( GetViewFrame().GetFrame().IsInPlace() ) // inplace
+ {
+ pDocSh->SetInplace( true ); // already initiated like this
+ if (rDoc.IsEmbedded())
+ rDoc.ResetEmbedded(); // no blue mark
+ }
+ else if ( bFirstView )
+ {
+ pDocSh->SetInplace( false );
+ GetViewData().RefreshZoom(); // recalculate PPT
+ if (!rDoc.IsEmbedded())
+ rDoc.SetEmbedded( rDoc.GetVisibleTab(), aVisArea ); // mark VisArea
+ }
+ }
+
+ // ViewInputHandler
+ // Each task now has its own InputWindow,
+ // therefore either should each task get its own InputHandler,
+ // or the InputWindow should create its own InputHandler
+ // (then always search via InputWindow and only if not found
+ // use the InputHandler of the App).
+ // As an intermediate solution each View gets its own InputHandler,
+ // which only yields problems if two Views are in one task window.
+ mpInputHandler.reset(new ScInputHandler);
+
+ // old version:
+ // if ( !GetViewFrame().ISA(SfxTopViewFrame) ) // OLE or Plug-In
+ // pInputHandler = new ScInputHandler;
+
+ // create FormShell before MakeDrawView, so that DrawView can be registered at the
+ // FormShell in every case
+ // the FormShell is pushed in the first activate
+ pFormShell.reset( new FmFormShell(this) );
+ pFormShell->SetControlActivationHandler( LINK( this, ScTabViewShell, FormControlActivated ) );
+
+ // DrawView must not be created in TabView - ctor,
+ // if the ViewShell is not yet constructed...
+ if (rDoc.GetDrawLayer())
+ MakeDrawView( nForceDesignMode );
+ ViewOptionsHasChanged(false, false); // possibly also creates DrawView
+
+ SfxUndoManager* pMgr = pDocSh->GetUndoManager();
+ SetUndoManager( pMgr );
+ pFormShell->SetUndoManager( pMgr );
+ if ( !rDoc.IsUndoEnabled() )
+ {
+ pMgr->SetMaxUndoActionCount( 0 );
+ }
+ SetRepeatTarget( &aTarget );
+ pFormShell->SetRepeatTarget( &aTarget );
+
+ if ( bFirstView ) // first view?
+ {
+ rDoc.SetDocVisible( true ); // used when creating new sheets
+ if ( pDocSh->IsEmpty() )
+ {
+ // set first sheet's RTL flag (following will already be initialized because of SetDocVisible)
+ rDoc.SetLayoutRTL( 0, ScGlobal::IsSystemRTL() );
+
+ // append additional sheets (not for OLE object)
+ if ( pDocSh->GetCreateMode() != SfxObjectCreateMode::EMBEDDED )
+ {
+ // Get the customized initial tab count
+ const ScDefaultsOptions& rOpt = SC_MOD()->GetDefaultsOptions();
+ SCTAB nInitTabCount = rOpt.GetInitTabCount();
+
+ for (SCTAB i=1; i<nInitTabCount; i++)
+ rDoc.MakeTable(i,false);
+ }
+
+ pDocSh->SetEmpty( false ); // #i6232# make sure this is done only once
+ }
+
+ // ReadExtOptions is now in Activate
+
+ // link update no nesting
+ if ( pDocSh->GetCreateMode() != SfxObjectCreateMode::INTERNAL &&
+ pDocSh->IsUpdateEnabled() ) // #105575#; update only in the first creation of the ViewShell
+ {
+ // Check if there are any external data.
+ bool bLink = rDoc.GetExternalRefManager()->hasExternalData();
+ if (!bLink)
+ {
+ // #i100042# sheet links can still exist independently from external formula references
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (SCTAB i=0; i<nTabCount && !bLink; i++)
+ if (rDoc.IsLinked(i))
+ bLink = true;
+ }
+ if (!bLink)
+ {
+ const sc::DocumentLinkManager& rMgr = rDoc.GetDocLinkManager();
+ if (rDoc.HasLinkFormulaNeedingCheck() || rDoc.HasAreaLinks() || rMgr.hasDdeOrOleOrWebServiceLinks())
+ bLink = true;
+ }
+ if (bLink)
+ {
+ if ( !pFirst )
+ pFirst = &GetViewFrame();
+
+ if(SC_MOD()->GetCurRefDlgId()==0)
+ {
+ pFirst->GetDispatcher()->Execute( SID_UPDATETABLINKS,
+ SfxCallMode::ASYNCHRON | SfxCallMode::RECORD );
+ }
+ }
+ else
+ {
+ // No links yet, but loading an existing document may have
+ // disabled link update but there's no "Allow updating" infobar
+ // that could enable it again. So in order to enable the user
+ // to add formulas with external references allow link updates
+ // again.
+ pDocSh->AllowLinkUpdate();
+ }
+
+ bool bReImport = false; // update imported data
+ ScDBCollection* pDBColl = rDoc.GetDBCollection();
+ if ( pDBColl )
+ {
+ const ScDBCollection::NamedDBs& rDBs = pDBColl->getNamedDBs();
+ bReImport = std::any_of(rDBs.begin(), rDBs.end(),
+ [](const std::unique_ptr<ScDBData>& rxDB) { return rxDB->IsStripData() && rxDB->HasImportParam() && !rxDB->HasImportSelection(); });
+ }
+ if (bReImport)
+ {
+ if ( !pFirst )
+ pFirst = &GetViewFrame();
+ if(SC_MOD()->GetCurRefDlgId()==0)
+ {
+ pFirst->GetDispatcher()->Execute( SID_REIMPORT_AFTER_LOAD,
+ SfxCallMode::ASYNCHRON | SfxCallMode::RECORD );
+ }
+ }
+ }
+ }
+
+ UpdateAutoFillMark();
+
+ // ScDispatchProviderInterceptor registers itself in ctor
+ xDisProvInterceptor = new ScDispatchProviderInterceptor( this );
+
+ bFirstActivate = true; // delay NavigatorUpdate until Activate()
+
+ // #105575#; update only in the first creation of the ViewShell
+ pDocSh->SetUpdateEnabled(false);
+
+ if ( GetViewFrame().GetFrame().IsInPlace() )
+ UpdateHeaderWidth(); // The inplace activation requires headers to be calculated
+
+ SvBorder aBorder;
+ GetBorderSize( aBorder, Size() );
+ SetBorderPixel( aBorder );
+}
+
+ScTabViewShell::ScTabViewShell( SfxViewFrame& rViewFrame,
+ SfxViewShell* pOldSh ) :
+ SfxViewShell(rViewFrame, SfxViewShellFlags::HAS_PRINTOPTIONS),
+ ScDBFunc( &rViewFrame.GetWindow(), static_cast<ScDocShell&>(*rViewFrame.GetObjectShell()), this ),
+ eCurOST(OST_NONE),
+ nDrawSfxId(0),
+ aTarget(this),
+ bActiveDrawSh(false),
+ bActiveDrawTextSh(false),
+ bActiveDrawFormSh(false),
+ bActiveOleObjectSh(false),
+ bActiveChartSh(false),
+ bActiveGraphicSh(false),
+ bActiveMediaSh(false),
+ bFormShellAtTop(false),
+ bDontSwitch(false),
+ bInFormatDialog(false),
+ bReadOnly(false),
+ bForceFocusOnCurCell(false),
+ bInPrepareClose(false),
+ bInDispose(false),
+ nCurRefDlgId(0),
+ mbInSwitch(false),
+ m_pDragData(new ScDragData)
+{
+ const ScAppOptions& rAppOpt = SC_MOD()->GetAppOptions();
+
+ // if switching back from print preview,
+ // restore the view settings that were active when creating the preview
+ // ReadUserData must not happen from ctor, because the view's edit window
+ // has to be shown by the sfx. ReadUserData is deferred until the first Activate call.
+ // old DesignMode state from form layer must be restored, too
+
+ TriState nForceDesignMode = TRISTATE_INDET;
+ if ( auto pPreviewShell = dynamic_cast<ScPreviewShell*>( pOldSh) )
+ {
+ nForceDesignMode = pPreviewShell->GetSourceDesignMode();
+ ScPreview* p = pPreviewShell->GetPreview();
+ if (p)
+ GetViewData().GetMarkData().SetSelectedTabs(p->GetSelectedTabs());
+ }
+
+ Construct( nForceDesignMode );
+
+ // make Controller known to SFX
+ new ScTabViewObj( this );
+
+ // Resolves: tdf#53899 if there is no controller, register the above
+ // ScTabViewObj as the current controller for the duration of the first
+ // round of calculations triggered here by SetZoom. That way any StarBasic
+ // macros triggered while the document is loading have a CurrentController
+ // available to them.
+ bool bInstalledScTabViewObjAsTempController = false;
+ uno::Reference<frame::XController> xCurrentController(GetViewData().GetDocShell()->GetModel()->getCurrentController());
+ if (!xCurrentController)
+ {
+ //GetController here returns the ScTabViewObj above
+ GetViewData().GetDocShell()->GetModel()->setCurrentController(GetController());
+ bInstalledScTabViewObjAsTempController = true;
+ }
+ xCurrentController.clear();
+
+ if ( GetViewData().GetDocShell()->IsPreview() )
+ {
+ // preview for template dialog: always show whole page
+ SetZoomType( SvxZoomType::WHOLEPAGE, true ); // zoom value is recalculated at next Resize
+ }
+ else
+ {
+ Fraction aFract( rAppOpt.GetZoom(), 100 );
+ SetZoom( aFract, aFract, true );
+ SetZoomType( rAppOpt.GetZoomType(), true );
+ }
+
+ SetCurSubShell(OST_Cell);
+ SvBorder aBorder;
+ GetBorderSize( aBorder, Size() );
+ SetBorderPixel( aBorder );
+
+ MakeDrawLayer();
+
+ //put things back as we found them
+ if (bInstalledScTabViewObjAsTempController)
+ GetViewData().GetDocShell()->GetModel()->setCurrentController(nullptr);
+
+ // formula mode in online is not usable in collaborative mode,
+ // this is a workaround for disabling formula mode in online
+ // when there is more than a single view
+ if (!comphelper::LibreOfficeKit::isActive())
+ return;
+
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ // have we already one view ?
+ if (!pViewShell)
+ return;
+
+ // this view is not yet visible at this stage, so we look for not visible views, too, for this same document
+ SfxViewShell* pViewShell2 = pViewShell;
+ do
+ {
+ pViewShell2 = SfxViewShell::GetNext(*pViewShell2, /*only visible shells*/ false);
+ } while (pViewShell2 && pViewShell2->GetDocId() != pViewShell->GetDocId());
+ // if the second view is not this one, it means that there is
+ // already more than one active view and so the formula mode
+ // has already been disabled
+ if (pViewShell2 && pViewShell2 == this)
+ {
+ ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
+ assert(pTabViewShell);
+ ScInputHandler* pInputHdl = pTabViewShell->GetInputHandler();
+ if (pInputHdl && pInputHdl->IsFormulaMode())
+ {
+ pInputHdl->SetMode(SC_INPUT_NONE);
+ }
+ }
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ ScModelObj* pModel = comphelper::getFromUnoTunnel<ScModelObj>(GetCurrentDocument());
+ SfxLokHelper::notifyViewRenderState(this, pModel);
+ }
+}
+
+ScTabViewShell::~ScTabViewShell()
+{
+ bInDispose = true;
+
+ // Notify other LOK views that we are going away.
+ SfxLokHelper::notifyOtherViews(this, LOK_CALLBACK_VIEW_CURSOR_VISIBLE, "visible", "false"_ostr);
+ SfxLokHelper::notifyOtherViews(this, LOK_CALLBACK_TEXT_VIEW_SELECTION, "selection", ""_ostr);
+ SfxLokHelper::notifyOtherViews(this, LOK_CALLBACK_GRAPHIC_VIEW_SELECTION, "selection", "EMPTY"_ostr);
+ SfxLokHelper::notifyOtherViews(this, LOK_CALLBACK_CELL_VIEW_CURSOR, "rectangle", "EMPTY"_ostr);
+
+ // all to NULL, in case the TabView-dtor tries to access them
+ //! (should not really! ??!?!)
+ if (mpInputHandler)
+ {
+ mpInputHandler->SetDocumentDisposing(true);
+ }
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ EndListening(*pDocSh);
+ EndListening(GetViewFrame());
+ EndListening(*SfxGetpApp()); // #i62045# #i62046# needed now - SfxViewShell no longer does it
+
+ SC_MOD()->ViewShellGone(this);
+
+ RemoveSubShell(); // all
+ SetWindow(nullptr);
+
+ // need kill editview or we will touch the editengine after it has been freed by the ScInputHandler
+ KillEditView(true);
+
+ pFontworkBarShell.reset();
+ pExtrusionBarShell.reset();
+ pCellShell.reset();
+ pPageBreakShell.reset();
+ pDrawShell.reset();
+ pDrawFormShell.reset();
+ pOleObjectShell.reset();
+ pChartShell.reset();
+ pGraphicShell.reset();
+ pMediaShell.reset();
+ pDrawTextShell.reset();
+ pEditShell.reset();
+ pPivotShell.reset();
+ m_pSparklineShell.reset();
+ pAuditingShell.reset();
+ pCurFrameLine.reset();
+ mpFormEditData.reset();
+ mpInputHandler.reset();
+ pDialogDPObject.reset();
+ pNavSettings.reset();
+
+ pFormShell.reset();
+ pAccessibilityBroadcaster.reset();
+}
+
+void ScTabViewShell::SetDialogDPObject( std::unique_ptr<ScDPObject> pObj )
+{
+ pDialogDPObject = std::move(pObj);
+}
+
+void ScTabViewShell::FillFieldData( ScHeaderFieldData& rData )
+{
+ ScDocShell* pDocShell = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocShell->GetDocument();
+ SCTAB nTab = GetViewData().GetTabNo();
+ OUString aTmp;
+ rDoc.GetName(nTab, aTmp);
+ rData.aTabName = aTmp;
+
+ if( pDocShell->getDocProperties()->getTitle().getLength() != 0 )
+ rData.aTitle = pDocShell->getDocProperties()->getTitle();
+ else
+ rData.aTitle = pDocShell->GetTitle();
+
+ const INetURLObject& rURLObj = pDocShell->GetMedium()->GetURLObject();
+ rData.aLongDocName = rURLObj.GetMainURL( INetURLObject::DecodeMechanism::Unambiguous );
+ if ( !rData.aLongDocName.isEmpty() )
+ rData.aShortDocName = rURLObj.GetLastName(INetURLObject::DecodeMechanism::Unambiguous);
+ else
+ rData.aShortDocName = rData.aLongDocName = rData.aTitle;
+ rData.nPageNo = 1;
+ rData.nTotalPages = 99;
+
+ // eNumType is known by the dialog
+}
+
+ScNavigatorSettings* ScTabViewShell::GetNavigatorSettings()
+{
+ if( !pNavSettings )
+ pNavSettings.reset(new ScNavigatorSettings);
+ return pNavSettings.get();
+}
+
+tools::Rectangle ScTabViewShell::getLOKVisibleArea() const
+{
+ return GetViewData().getLOKVisibleArea();
+}
+
+void ScTabViewShell::SetDragObject(ScTransferObj* pCellObj, ScDrawTransferObj* pDrawObj)
+{
+ ResetDragObject();
+ m_pDragData->pCellTransfer = pCellObj;
+ m_pDragData->pDrawTransfer = pDrawObj;
+}
+
+void ScTabViewShell::ResetDragObject()
+{
+ m_pDragData->pCellTransfer = nullptr;
+ m_pDragData->pDrawTransfer = nullptr;
+ m_pDragData->pJumpLocalDoc = nullptr;
+ m_pDragData->aLinkDoc.clear();
+ m_pDragData->aLinkTable.clear();
+ m_pDragData->aLinkArea.clear();
+ m_pDragData->aJumpTarget.clear();
+ m_pDragData->aJumpText.clear();
+}
+
+void ScTabViewShell::SetDragLink(const OUString& rDoc, const OUString& rTab, const OUString& rArea)
+{
+ ResetDragObject();
+ m_pDragData->aLinkDoc = rDoc;
+ m_pDragData->aLinkTable = rTab;
+ m_pDragData->aLinkArea = rArea;
+}
+
+void ScTabViewShell::SetDragJump(ScDocument* pLocalDoc, const OUString& rTarget, const OUString& rText)
+{
+ ResetDragObject();
+ m_pDragData->pJumpLocalDoc = pLocalDoc;
+ m_pDragData->aJumpTarget = rTarget;
+ m_pDragData->aJumpText = rText;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabvwsh5.cxx b/sc/source/ui/view/tabvwsh5.cxx
new file mode 100644
index 0000000000..fab96304f3
--- /dev/null
+++ b/sc/source/ui/view/tabvwsh5.cxx
@@ -0,0 +1,390 @@
+/* -*- 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 .
+ */
+
+#include <svl/hint.hxx>
+#include <comphelper/lok.hxx>
+#include <svx/numfmtsh.hxx>
+#include <svx/numinf.hxx>
+#include <svx/svxids.hrc>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <osl/diagnose.h>
+
+#include <tabvwsh.hxx>
+#include <global.hxx>
+#include <docsh.hxx>
+#include <document.hxx>
+#include <formulacell.hxx>
+#include <scmod.hxx>
+#include <uiitems.hxx>
+#include <hints.hxx>
+#include <cellvalue.hxx>
+#include <svl/numformat.hxx>
+#include <svl/sharedstring.hxx>
+
+void ScTabViewShell::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ if (const ScPaintHint* pPaintHint = dynamic_cast<const ScPaintHint*>(&rHint)) // draw new
+ {
+ PaintPartFlags nParts = pPaintHint->GetParts();
+ SCTAB nTab = GetViewData().GetTabNo();
+ if (pPaintHint->GetStartTab() <= nTab && pPaintHint->GetEndTab() >= nTab)
+ {
+ if (nParts & PaintPartFlags::Extras) // first if table vanished !!!
+ if (PaintExtras())
+ nParts = PaintPartFlags::All;
+
+ // if the current sheet has pending row height updates (sheet links refreshed),
+ // execute them before invalidating the window
+ GetViewData().GetDocShell()->UpdatePendingRowHeights( GetViewData().GetTabNo() );
+
+ if (nParts & PaintPartFlags::Size)
+ RepeatResize(); //! InvalidateBorder ???
+ if (nParts & PaintPartFlags::Grid)
+ PaintArea( pPaintHint->GetStartCol(), pPaintHint->GetStartRow(),
+ pPaintHint->GetEndCol(), pPaintHint->GetEndRow() );
+ if (nParts & PaintPartFlags::Marks)
+ PaintArea( pPaintHint->GetStartCol(), pPaintHint->GetStartRow(),
+ pPaintHint->GetEndCol(), pPaintHint->GetEndRow(), ScUpdateMode::Marks );
+ if (nParts & PaintPartFlags::Left)
+ PaintLeftArea( pPaintHint->GetStartRow(), pPaintHint->GetEndRow() );
+ if (nParts & PaintPartFlags::Top)
+ PaintTopArea( pPaintHint->GetStartCol(), pPaintHint->GetEndCol() );
+
+ // #i84689# call UpdateAllOverlays here instead of in ScTabView::PaintArea
+ if (nParts & ( PaintPartFlags::Left | PaintPartFlags::Top )) // only if widths or heights changed
+ UpdateAllOverlays();
+
+ HideNoteMarker();
+ }
+ }
+ else if (auto pEditViewHint = dynamic_cast<const ScEditViewHint*>(&rHint)) // create Edit-View
+ {
+ // ScEditViewHint is only received at active view
+
+ SCTAB nTab = GetViewData().GetTabNo();
+ if ( pEditViewHint->GetTab() == nTab )
+ {
+ SCCOL nCol = pEditViewHint->GetCol();
+ SCROW nRow = pEditViewHint->GetRow();
+ {
+ HideNoteMarker();
+
+ MakeEditView( pEditViewHint->GetEngine(), nCol, nRow );
+
+ StopEditShell(); // shouldn't be set
+
+ ScSplitPos eActive = GetViewData().GetActivePart();
+ if ( GetViewData().HasEditView(eActive) )
+ {
+ // MakeEditView will fail, if the cursor is outside the screen.
+ // Then GetEditView will return a none-active view, therefore
+ // calling HasEditView.
+
+ EditView* pView = GetViewData().GetEditView(eActive); // isn't zero
+
+ SetEditShell(pView, true);
+ }
+ }
+ }
+ }
+ else if (auto pTablesHint = dynamic_cast<const ScTablesHint*>(&rHint)) // table insert / deleted
+ {
+ // first fetch current table (can be changed during DeleteTab on ViewData)
+ SCTAB nActiveTab = GetViewData().GetTabNo();
+
+ SCTAB nTab1 = pTablesHint->GetTab1();
+ SCTAB nTab2 = pTablesHint->GetTab2();
+ sal_uInt16 nId = pTablesHint->GetTablesHintId();
+ switch (nId)
+ {
+ case SC_TAB_INSERTED:
+ GetViewData().InsertTab( nTab1 );
+ break;
+ case SC_TAB_DELETED:
+ GetViewData().DeleteTab( nTab1 );
+ break;
+ case SC_TAB_MOVED:
+ GetViewData().MoveTab( nTab1, nTab2 );
+ break;
+ case SC_TAB_COPIED:
+ GetViewData().CopyTab( nTab1, nTab2 );
+ break;
+ case SC_TAB_HIDDEN:
+ break;
+ case SC_TABS_INSERTED:
+ GetViewData().InsertTabs( nTab1, nTab2 );
+ break;
+ case SC_TABS_DELETED:
+ GetViewData().DeleteTabs( nTab1, nTab2 );
+ break;
+ default:
+ OSL_FAIL("unknown ScTablesHint");
+ }
+
+ // No calling of IsActive() here, because the actions can be coming from Basic
+ // and then also the active view has to be switched.
+
+ SCTAB nNewTab = nActiveTab;
+ bool bStayOnActiveTab = true;
+ switch (nId)
+ {
+ case SC_TAB_INSERTED:
+ if ( nTab1 <= nNewTab ) // insert before
+ ++nNewTab;
+ break;
+ case SC_TAB_DELETED:
+ if ( nTab1 < nNewTab ) // deleted before
+ --nNewTab;
+ else if ( nTab1 == nNewTab ) // deleted current
+ bStayOnActiveTab = false;
+ break;
+ case SC_TAB_MOVED:
+ if ( nNewTab == nTab1 ) // moved table
+ nNewTab = nTab2;
+ else if ( nTab1 < nTab2 ) // moved back
+ {
+ if ( nNewTab > nTab1 && nNewTab <= nTab2 ) // succeeding area
+ --nNewTab;
+ }
+ else // move in front
+ {
+ if ( nNewTab >= nTab2 && nNewTab < nTab1 ) // succeeding area
+ ++nNewTab;
+ }
+ break;
+ case SC_TAB_COPIED:
+ if ( nNewTab >= nTab2 ) // insert before
+ ++nNewTab;
+ break;
+ case SC_TAB_HIDDEN:
+ if ( nTab1 == nNewTab ) // current is hidden
+ bStayOnActiveTab = false;
+ break;
+ case SC_TABS_INSERTED:
+ if ( nTab1 <= nNewTab )
+ nNewTab += nTab2;
+ break;
+ case SC_TABS_DELETED:
+ if ( nTab1 < nNewTab )
+ nNewTab -= nTab2;
+ break;
+ }
+
+ ScDocument& rDoc = GetViewData().GetDocument();
+ if ( nNewTab >= rDoc.GetTableCount() )
+ nNewTab = rDoc.GetTableCount() - 1;
+
+ bool bForce = !bStayOnActiveTab;
+ SetTabNo( nNewTab, bForce, false, bStayOnActiveTab );
+ }
+ else if (const ScIndexHint* pIndexHint = dynamic_cast<const ScIndexHint*>(&rHint))
+ {
+ SfxHintId nId = pIndexHint->GetId();
+ sal_uInt16 nIndex = pIndexHint->GetIndex();
+ switch (nId)
+ {
+ case SfxHintId::ScShowRangeFinder:
+ PaintRangeFinder( nIndex );
+ break;
+ default: break;
+ }
+ }
+ else // without parameter
+ {
+ const SfxHintId nSlot = rHint.GetId();
+ switch ( nSlot )
+ {
+ case SfxHintId::ScDataChanged:
+ UpdateFormulas();
+ break;
+
+ case SfxHintId::ScRefModeChanged:
+ {
+ bool bRefMode = SC_MOD()->IsFormulaMode();
+ if (!bRefMode)
+ StopRefMode();
+ else
+ GetSelEngine()->Reset();
+ }
+ break;
+
+ case SfxHintId::ScKillEditView:
+ case SfxHintId::ScKillEditViewNoPaint:
+ if (!comphelper::LibreOfficeKit::isActive()
+ || this == SfxViewShell::Current()
+ || bInPrepareClose
+ || bInDispose)
+ {
+ StopEditShell();
+ KillEditView( nSlot == SfxHintId::ScKillEditViewNoPaint );
+ }
+ break;
+
+ case SfxHintId::DocChanged:
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+ if (!rDoc.HasTable( GetViewData().GetTabNo() ))
+ {
+ SetTabNo(0);
+ }
+ }
+ break;
+
+ case SfxHintId::ScDrawLayerNew:
+ MakeDrawView(TRISTATE_INDET);
+ break;
+
+ case SfxHintId::ScDocSaved:
+ {
+ // "Save as" can make a write-protected document writable,
+ // therefore the Layer-Locks anew (#39884#)
+ // (Invalidate etc. is happening already from Sfx)
+ // by SID_EDITDOC no SfxHintId::TitleChanged will occur, that
+ // is why the own hint from DoSaveCompleted
+ //! what is with SfxHintId::SAVECOMPLETED ?
+
+ UpdateLayerLocks();
+
+ // Would be too much to change Design-Mode with every save
+ // (when saving under the name, it should remain unchanged)
+ // Therefore only by SfxHintId::ModeChanged (from ViewFrame)
+ }
+ break;
+
+ case SfxHintId::ModeChanged:
+ // Since you can no longer rely on it where this hint was coming
+ // from, always switch the design mode when the ReadOnly state
+ // really was changed:
+
+ if ( GetViewData().GetSfxDocShell()->IsReadOnly() != bReadOnly )
+ {
+ bReadOnly = GetViewData().GetSfxDocShell()->IsReadOnly();
+
+ SfxBoolItem aItem( SID_FM_DESIGN_MODE, !bReadOnly);
+ GetViewData().GetDispatcher().ExecuteList(SID_FM_DESIGN_MODE,
+ SfxCallMode::ASYNCHRON, { &aItem });
+
+ UpdateInputContext();
+ }
+ break;
+
+ case SfxHintId::ScShowRangeFinder:
+ PaintRangeFinder(-1);
+ break;
+
+ case SfxHintId::ScForceSetTab:
+ SetTabNo( GetViewData().GetTabNo(), true );
+ break;
+
+ case SfxHintId::LanguageChanged:
+ {
+ GetViewFrame().GetBindings().Invalidate(SID_LANGUAGE_STATUS);
+ if ( ScGridWindow* pWin = GetViewData().GetActiveWin() )
+ pWin->ResetAutoSpell();
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ SfxViewShell::Notify( rBC, rHint );
+}
+
+std::unique_ptr<SvxNumberInfoItem> ScTabViewShell::MakeNumberInfoItem( ScDocument& rDoc, const ScViewData& rViewData )
+{
+
+ // construct NumberInfo item
+
+ SvxNumberValueType eValType = SvxNumberValueType::Undefined;
+ double nCellValue = 0;
+ OUString aCellString;
+
+ ScRefCellValue aCell(rDoc, rViewData.GetCurPos());
+
+ switch (aCell.getType())
+ {
+ case CELLTYPE_VALUE:
+ {
+ nCellValue = aCell.getDouble();
+ eValType = SvxNumberValueType::Number;
+ }
+ break;
+
+ case CELLTYPE_STRING:
+ {
+ aCellString = aCell.getSharedString()->getString();
+ eValType = SvxNumberValueType::String;
+ }
+ break;
+
+ case CELLTYPE_FORMULA:
+ {
+ if (aCell.getFormula()->IsValue())
+ {
+ nCellValue = aCell.getFormula()->GetValue();
+ eValType = SvxNumberValueType::Number;
+ }
+ else
+ {
+ nCellValue = 0;
+ eValType = SvxNumberValueType::Undefined;
+ }
+ }
+ break;
+
+ default:
+ nCellValue = 0;
+ eValType = SvxNumberValueType::Undefined;
+ }
+
+ switch ( eValType )
+ {
+ case SvxNumberValueType::String:
+ return std::make_unique<SvxNumberInfoItem>(
+ rDoc.GetFormatTable(),
+ aCellString,
+ SID_ATTR_NUMBERFORMAT_INFO );
+
+ case SvxNumberValueType::Number:
+ return std::make_unique<SvxNumberInfoItem>(
+ rDoc.GetFormatTable(),
+ nCellValue,
+ SID_ATTR_NUMBERFORMAT_INFO );
+
+ case SvxNumberValueType::Undefined:
+ default:
+ ;
+ }
+
+ return std::make_unique<SvxNumberInfoItem>(
+ rDoc.GetFormatTable(), SID_ATTR_NUMBERFORMAT_INFO);
+}
+
+void ScTabViewShell::UpdateNumberFormatter(
+ const SvxNumberInfoItem& rInfoItem )
+{
+ for ( sal_uInt32 key : rInfoItem.GetDelFormats() )
+ rInfoItem.GetNumberFormatter()->DeleteEntry( key );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabvwsh8.cxx b/sc/source/ui/view/tabvwsh8.cxx
new file mode 100644
index 0000000000..9a743c295b
--- /dev/null
+++ b/sc/source/ui/view/tabvwsh8.cxx
@@ -0,0 +1,76 @@
+/* -*- 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 .
+ */
+
+#include <editeng/borderline.hxx>
+
+#include <tabvwsh.hxx>
+#include <document.hxx>
+
+void ScTabViewShell::SetDefaultFrameLine( const ::editeng::SvxBorderLine* pLine )
+{
+ if ( pLine )
+ {
+ pCurFrameLine.reset( new ::editeng::SvxBorderLine( &pLine->GetColor(),
+ pLine->GetWidth(),
+ pLine->GetBorderLineStyle() ) );
+ }
+ else
+ pCurFrameLine.reset();
+}
+
+bool ScTabViewShell::HasSelection( bool bText ) const
+{
+ bool bHas = false;
+ ScViewData& rData = const_cast<ScViewData&>(GetViewData());
+ if ( bText )
+ {
+ // Content contained: Count2 >= 1
+ ScDocument& rDoc = rData.GetDocument();
+ ScMarkData& rMark = rData.GetMarkData();
+ ScAddress aCursor( rData.GetCurX(), rData.GetCurY(), rData.GetTabNo() );
+ double fVal = 0.0;
+ if ( rDoc.GetSelectionFunction( SUBTOTAL_FUNC_CNT2, aCursor, rMark, fVal ) )
+ bHas = ( fVal > 0.5 );
+ }
+ else
+ {
+ ScRange aRange;
+ ScMarkType eMarkType = rData.GetSimpleArea( aRange );
+ if ( eMarkType == SC_MARK_SIMPLE )
+ bHas = ( aRange.aStart != aRange.aEnd ); // more than 1 cell
+ else
+ bHas = true; // multiple selection or filtered
+ }
+ return bHas;
+}
+
+void ScTabViewShell::UIDeactivated( SfxInPlaceClient* pClient )
+{
+ ClearHighlightRanges();
+
+ // Move in the ViewShell should really be called from Sfx, when the
+ // frame window is moved due to different toolboxes or other things
+ // (to not move painted objects by mistake, #56515#).
+ // this mechanism does however not work at the moment, that is why this
+ // call is here (in Move it is checked if the position has really changed).
+ ForceMove();
+ SfxViewShell::UIDeactivated( pClient );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabvwsh9.cxx b/sc/source/ui/view/tabvwsh9.cxx
new file mode 100644
index 0000000000..9127bb598a
--- /dev/null
+++ b/sc/source/ui/view/tabvwsh9.cxx
@@ -0,0 +1,203 @@
+/* -*- 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 .
+ */
+
+#include <svx/imapdlg.hxx>
+#include <svx/svdmark.hxx>
+#include <svx/svdview.hxx>
+#include <svx/ImageMapInfo.hxx>
+#include <svx/svxids.hrc>
+#include <sfx2/bindings.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/sidebar/Sidebar.hxx>
+#include <svl/whiter.hxx>
+#include <svl/stritem.hxx>
+
+#include "imapwrap.hxx"
+#include <tabvwsh.hxx>
+#include <viewdata.hxx>
+#include <docsh.hxx>
+
+#include <svx/galleryitem.hxx>
+#include <com/sun/star/gallery/GalleryItemType.hpp>
+
+class SvxIMapDlg;
+
+void ScTabViewShell::ExecChildWin(const SfxRequest& rReq)
+{
+ sal_uInt16 nSlot = rReq.GetSlot();
+ switch(nSlot)
+ {
+ case SID_GALLERY:
+ {
+ // First make sure that the sidebar is visible
+ GetViewFrame().ShowChildWindow(SID_SIDEBAR);
+
+ ::sfx2::sidebar::Sidebar::ShowPanel(
+ u"GalleryPanel",
+ GetViewFrame().GetFrame().GetFrameInterface());
+ }
+ break;
+ }
+}
+
+void ScTabViewShell::ExecGallery( const SfxRequest& rReq )
+{
+ const SfxItemSet* pArgs = rReq.GetArgs();
+
+ const SvxGalleryItem* pGalleryItem = SfxItemSet::GetItem<SvxGalleryItem>(pArgs, SID_GALLERY_FORMATS, false);
+ if ( !pGalleryItem )
+ return;
+
+ sal_Int8 nType( pGalleryItem->GetType() );
+ if ( nType == css::gallery::GalleryItemType::GRAPHIC )
+ {
+ MakeDrawLayer();
+
+ Graphic aGraphic( pGalleryItem->GetGraphic() );
+ Point aPos = GetInsertPos();
+
+ PasteGraphic( aPos, aGraphic, OUString() );
+ }
+ else if ( nType == css::gallery::GalleryItemType::MEDIA )
+ {
+ // for sounds (linked or not), insert a hyperlink button,
+ // like in Impress and Writer
+ const SfxStringItem aMediaURLItem( SID_INSERT_AVMEDIA, pGalleryItem->GetURL() );
+ GetViewFrame().GetDispatcher()->ExecuteList(SID_INSERT_AVMEDIA,
+ SfxCallMode::SYNCHRON, { &aMediaURLItem });
+ }
+}
+
+void ScTabViewShell::ExecImageMap( SfxRequest& rReq )
+{
+ sal_uInt16 nSlot = rReq.GetSlot();
+ switch(nSlot)
+ {
+ case SID_IMAP:
+ {
+ SfxViewFrame& rThisFrame = GetViewFrame();
+ sal_uInt16 nId = ScIMapChildWindowId();
+ rThisFrame.ToggleChildWindow( nId );
+ GetViewFrame().GetBindings().Invalidate( SID_IMAP );
+
+ if ( rThisFrame.HasChildWindow( nId ) )
+ {
+ SvxIMapDlg* pDlg = GetIMapDlg();
+ if ( pDlg )
+ {
+ SdrView* pDrView = GetScDrawView();
+ if ( pDrView )
+ {
+ const SdrMarkList& rMarkList = pDrView->GetMarkedObjectList();
+ if ( rMarkList.GetMarkCount() == 1 )
+ UpdateIMap( rMarkList.GetMark( 0 )->GetMarkedSdrObj() );
+ }
+ }
+ }
+
+ rReq.Ignore();
+ }
+ break;
+
+ case SID_IMAP_EXEC:
+ {
+ SdrView* pDrView = GetScDrawView();
+ SdrMark* pMark = pDrView ? pDrView->GetMarkedObjectList().GetMark(0) : nullptr;
+
+ if ( pMark )
+ {
+ SdrObject* pSdrObj = pMark->GetMarkedSdrObj();
+ SvxIMapDlg* pDlg = GetIMapDlg();
+
+ if ( ScIMapDlgGetObj(pDlg) == static_cast<void*>(pSdrObj) )
+ {
+ const ImageMap& rImageMap = ScIMapDlgGetMap(pDlg);
+ SvxIMapInfo* pIMapInfo = SvxIMapInfo::GetIMapInfo( pSdrObj );
+
+ if ( !pIMapInfo )
+ pSdrObj->AppendUserData( std::unique_ptr<SdrObjUserData>(new SvxIMapInfo( rImageMap )) );
+ else
+ pIMapInfo->SetImageMap( rImageMap );
+
+ GetViewData().GetDocShell()->SetDrawModified();
+ }
+ }
+ }
+ break;
+ }
+}
+
+void ScTabViewShell::GetImageMapState( SfxItemSet& rSet )
+{
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while ( nWhich )
+ {
+ switch ( nWhich )
+ {
+ case SID_IMAP:
+ {
+ // We don't disable this anymore
+
+ bool bThere = false;
+ SfxViewFrame& rThisFrame = GetViewFrame();
+ sal_uInt16 nId = ScIMapChildWindowId();
+ if ( rThisFrame.KnowsChildWindow(nId) )
+ if ( rThisFrame.HasChildWindow(nId) )
+ bThere = true;
+
+ ObjectSelectionType eType=GetCurObjectSelectionType();
+ bool bEnable=(eType==OST_OleObject) ||(eType==OST_Graphic);
+ if(!bThere && !bEnable)
+ {
+ rSet.DisableItem( nWhich );
+ }
+ else
+ {
+ rSet.Put( SfxBoolItem( nWhich, bThere ) );
+ }
+ }
+ break;
+
+ case SID_IMAP_EXEC:
+ {
+ bool bDisable = true;
+
+ SdrView* pDrView = GetScDrawView();
+ if ( pDrView )
+ {
+ const SdrMarkList& rMarkList = pDrView->GetMarkedObjectList();
+ if ( rMarkList.GetMarkCount() == 1 )
+ if ( ScIMapDlgGetObj(GetIMapDlg()) ==
+ static_cast<void*>(rMarkList.GetMark(0)->GetMarkedSdrObj()) )
+ bDisable = false;
+ }
+
+ rSet.Put( SfxBoolItem( SID_IMAP_EXEC, bDisable ) );
+ }
+ break;
+ }
+
+ nWhich = aIter.NextWhich();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabvwsha.cxx b/sc/source/ui/view/tabvwsha.cxx
new file mode 100644
index 0000000000..c332c9542a
--- /dev/null
+++ b/sc/source/ui/view/tabvwsha.cxx
@@ -0,0 +1,1817 @@
+/* -*- 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 .
+ */
+
+#include <com/sun/star/table/BorderLineStyle.hpp>
+#include <officecfg/Office/Calc.hxx>
+
+#include <comphelper/lok.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/langitem.hxx>
+#include <o3tl/temporary.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/sfxdlg.hxx>
+#include <sfx2/sidebar/Sidebar.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/newstyle.hxx>
+#include <sfx2/tplpitem.hxx>
+#include <svl/ilstitem.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zformat.hxx>
+#include <svl/int64item.hxx>
+#include <svl/ptitem.hxx>
+#include <svl/srchitem.hxx>
+#include <svl/srchdefs.hxx>
+#include <svl/stritem.hxx>
+#include <svl/whiter.hxx>
+#include <svx/numinf.hxx>
+#include <svx/xbtmpit.hxx>
+#include <svx/xlndsit.hxx>
+#include <svx/xlnstit.hxx>
+#include <svx/xlnedit.hxx>
+#include <svx/xflgrit.hxx>
+#include <svx/xflftrit.hxx>
+#include <svx/xflhtit.hxx>
+#include <svx/zoomslideritem.hxx>
+
+#include <global.hxx>
+#include <appoptio.hxx>
+#include <attrib.hxx>
+#include <cellform.hxx>
+#include <cellvalue.hxx>
+#include <compiler.hxx>
+#include <docsh.hxx>
+#include <document.hxx>
+#include <formulacell.hxx>
+#include <globstr.hrc>
+#include <inputhdl.hxx>
+#include <inputwin.hxx>
+#include <markdata.hxx>
+#include <patattr.hxx>
+#include <sc.hrc>
+#include <scabstdlg.hxx>
+#include <scitems.hxx>
+#include <scmod.hxx>
+#include <scresid.hxx>
+#include <stlpool.hxx>
+#include <tabvwsh.hxx>
+#include <tokenarray.hxx>
+#include <viewdata.hxx>
+#include <docpool.hxx>
+#include <printfun.hxx>
+#include <undostyl.hxx>
+#include <futext.hxx>
+
+#include <memory>
+
+using namespace com::sun::star;
+
+bool ScTabViewShell::GetFunction( OUString& rFuncStr, FormulaError nErrCode )
+{
+ sal_uInt32 nFuncs = SC_MOD()->GetAppOptions().GetStatusFunc();
+ ScViewData& rViewData = GetViewData();
+ ScMarkData& rMark = rViewData.GetMarkData();
+ bool bIgnoreError = (rMark.IsMarked() || rMark.IsMultiMarked());
+ bool bFirst = true;
+ for ( sal_uInt16 nFunc = 0; nFunc < 32; nFunc++ )
+ {
+ if ( !(nFuncs & (1U << nFunc)) )
+ continue;
+ ScSubTotalFunc eFunc = static_cast<ScSubTotalFunc>(nFunc);
+
+ if (bIgnoreError && (eFunc == SUBTOTAL_FUNC_CNT || eFunc == SUBTOTAL_FUNC_CNT2))
+ nErrCode = FormulaError::NONE;
+
+ if (nErrCode != FormulaError::NONE)
+ {
+ rFuncStr = ScGlobal::GetLongErrorString(nErrCode);
+ return true;
+ }
+
+ TranslateId pGlobStrId;
+ switch (eFunc)
+ {
+ case SUBTOTAL_FUNC_AVE: pGlobStrId = STR_FUN_TEXT_AVG; break;
+ case SUBTOTAL_FUNC_CNT: pGlobStrId = STR_FUN_TEXT_COUNT; break;
+ case SUBTOTAL_FUNC_CNT2: pGlobStrId = STR_FUN_TEXT_COUNT2; break;
+ case SUBTOTAL_FUNC_MAX: pGlobStrId = STR_FUN_TEXT_MAX; break;
+ case SUBTOTAL_FUNC_MIN: pGlobStrId = STR_FUN_TEXT_MIN; break;
+ case SUBTOTAL_FUNC_SUM: pGlobStrId = STR_FUN_TEXT_SUM; break;
+ case SUBTOTAL_FUNC_SELECTION_COUNT: pGlobStrId = STR_FUN_TEXT_SELECTION_COUNT; break;
+
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ if (pGlobStrId)
+ {
+ ScDocument& rDoc = rViewData.GetDocument();
+ SCCOL nPosX = rViewData.GetCurX();
+ SCROW nPosY = rViewData.GetCurY();
+ SCTAB nTab = rViewData.GetTabNo();
+
+ OUString aStr = ScResId(pGlobStrId) + ": ";
+
+ ScAddress aCursor( nPosX, nPosY, nTab );
+ double nVal;
+ if ( rDoc.GetSelectionFunction( eFunc, aCursor, rMark, nVal ) )
+ {
+ if ( nVal == 0.0 )
+ aStr += "0";
+ else
+ {
+ // Number in the standard format, the other on the cursor position
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ sal_uInt32 nNumFmt = 0;
+ if ( eFunc != SUBTOTAL_FUNC_CNT && eFunc != SUBTOTAL_FUNC_CNT2 && eFunc != SUBTOTAL_FUNC_SELECTION_COUNT)
+ {
+ // number format from attributes or formula
+ nNumFmt = rDoc.GetNumberFormat( nPosX, nPosY, nTab );
+ // If the number format is time (without date) and the
+ // result is not within 24 hours, use a duration
+ // format. Summing date+time doesn't make much sense
+ // otherwise but we also don't want to display duration
+ // for a single date+time value.
+ if (nVal < 0.0 || nVal >= 1.0)
+ {
+ const SvNumberformat* pFormat = pFormatter->GetEntry(nNumFmt);
+ if (pFormat && (pFormat->GetType() == SvNumFormatType::TIME))
+ nNumFmt = pFormatter->GetTimeFormat( nVal, pFormat->GetLanguage(), true);
+ }
+ }
+
+ OUString aValStr;
+ const Color* pDummy;
+ pFormatter->GetOutputString( nVal, nNumFmt, aValStr, &pDummy );
+ aStr += aValStr;
+ }
+ }
+ if ( bFirst )
+ {
+ rFuncStr += aStr;
+ bFirst = false;
+ }
+ else
+ rFuncStr += "; " + aStr;
+ }
+ }
+
+ return !rFuncStr.isEmpty();
+}
+
+// Functions that are disabled, depending on the selection
+// Default:
+// SID_DELETE,
+// SID_DELETE_CONTENTS,
+// FID_DELETE_CELL
+// FID_VALIDATION
+
+void ScTabViewShell::GetState( SfxItemSet& rSet )
+{
+ ScViewData& rViewData = GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ ScDocShell* pDocShell = rViewData.GetDocShell();
+ ScMarkData& rMark = rViewData.GetMarkData();
+ SCCOL nPosX = rViewData.GetCurX();
+ SCROW nPosY = rViewData.GetCurY();
+ SCTAB nTab = rViewData.GetTabNo();
+
+ SfxViewFrame& rThisFrame = GetViewFrame();
+ bool bOle = GetViewFrame().GetFrame().IsInPlace();
+
+ SCTAB nTabSelCount = rMark.GetSelectCount();
+
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+
+ while ( nWhich )
+ {
+ switch ( nWhich )
+ {
+ case FID_CHG_COMMENT:
+ {
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScAddress aPos( nPosX, nPosY, nTab );
+ if ( pDocSh->IsReadOnly() || !pDocSh->GetChangeAction(aPos) || pDocSh->IsDocShared() )
+ rSet.DisableItem( nWhich );
+ }
+ break;
+
+ case SID_OPENDLG_EDIT_PRINTAREA:
+ case SID_ADD_PRINTAREA:
+ case SID_DEFINE_PRINTAREA:
+ {
+ if ( pDocShell && pDocShell->IsDocShared() )
+ {
+ rSet.DisableItem( nWhich );
+ }
+ }
+ break;
+
+ case SID_DELETE_PRINTAREA:
+ if ( pDocShell && pDocShell->IsDocShared() )
+ {
+ rSet.DisableItem( nWhich );
+ }
+ else if (rDoc.IsPrintEntireSheet(nTab))
+ rSet.DisableItem(nWhich);
+ break;
+
+ case SID_STATUS_PAGESTYLE:
+ case SID_HFEDIT:
+ GetViewData().GetDocShell()->GetStatePageStyle( rSet, nTab );
+ break;
+
+ case SID_SEARCH_ITEM:
+ {
+ SvxSearchItem aItem(ScGlobal::GetSearchItem()); // make a copy.
+ // Search on current selection if a range is marked.
+ aItem.SetSelection(rMark.IsMarked());
+ rSet.Put(aItem);
+ break;
+ }
+
+ case SID_SEARCH_OPTIONS:
+ {
+ // Anything goes
+ SearchOptionFlags nOptions = SearchOptionFlags::ALL;
+
+ // No replacement if ReadOnly
+ if (GetViewData().GetDocShell()->IsReadOnly())
+ nOptions &= ~SearchOptionFlags( SearchOptionFlags::REPLACE | SearchOptionFlags::REPLACE_ALL );
+ rSet.Put( SfxUInt16Item( nWhich, static_cast<sal_uInt16>(nOptions) ) );
+ }
+ break;
+
+ case SID_CURRENTCELL:
+ {
+ ScAddress aScAddress( GetViewData().GetCurX(), GetViewData().GetCurY(), 0 );
+ OUString aAddr(aScAddress.Format(ScRefFlags::ADDR_ABS, nullptr, rDoc.GetAddressConvention()));
+ SfxStringItem aPosItem( SID_CURRENTCELL, aAddr );
+
+ rSet.Put( aPosItem );
+ }
+ break;
+
+ case SID_CURRENTTAB:
+ // Table for Basic is 1-based
+ rSet.Put( SfxUInt16Item( nWhich, static_cast<sal_uInt16>(GetViewData().GetTabNo()) + 1 ) );
+ break;
+
+ case SID_CURRENTDOC:
+ rSet.Put( SfxStringItem( nWhich, GetViewData().GetDocShell()->GetTitle() ) );
+ break;
+
+ case FID_TOGGLEINPUTLINE:
+ {
+ sal_uInt16 nId = ScInputWindowWrapper::GetChildWindowId();
+
+ if ( rThisFrame.KnowsChildWindow( nId ) )
+ {
+ SfxChildWindow* pWnd = rThisFrame.GetChildWindow( nId );
+ rSet.Put( SfxBoolItem( nWhich, pWnd != nullptr ) );
+ }
+ else
+ rSet.DisableItem( nWhich );
+ }
+ break;
+
+ case FID_DEL_MANUALBREAKS:
+ if (!rDoc.HasManualBreaks(nTab))
+ rSet.DisableItem( nWhich );
+ break;
+
+ case FID_RESET_PRINTZOOM:
+ {
+ // disable if already set to default
+
+ OUString aStyleName = rDoc.GetPageStyle( nTab );
+ ScStyleSheetPool* pStylePool = rDoc.GetStyleSheetPool();
+ SfxStyleSheetBase* pStyleSheet = pStylePool->Find( aStyleName,
+ SfxStyleFamily::Page );
+ OSL_ENSURE( pStyleSheet, "PageStyle not found" );
+ if ( pStyleSheet )
+ {
+ SfxItemSet& rStyleSet = pStyleSheet->GetItemSet();
+ sal_uInt16 nScale = rStyleSet.Get(ATTR_PAGE_SCALE).GetValue();
+ sal_uInt16 nPages = rStyleSet.Get(ATTR_PAGE_SCALETOPAGES).GetValue();
+ if ( nScale == 100 && nPages == 0 )
+ rSet.DisableItem( nWhich );
+ }
+ }
+ break;
+
+ case SID_ZOOM_IN:
+ {
+ const Fraction& rZoomY = GetViewData().GetZoomY();
+ tools::Long nZoom = tools::Long(rZoomY * 100);
+ if (nZoom >= MAXZOOM)
+ rSet.DisableItem(nWhich);
+ }
+ break;
+ case SID_ZOOM_OUT:
+ {
+ const Fraction& rZoomY = GetViewData().GetZoomY();
+ tools::Long nZoom = tools::Long(rZoomY * 100);
+ if (nZoom <= MINZOOM)
+ rSet.DisableItem(nWhich);
+ }
+ break;
+
+ case FID_SCALE:
+ case SID_ATTR_ZOOM:
+ if ( bOle )
+ rSet.DisableItem( nWhich );
+ else
+ {
+ const Fraction& rOldY = GetViewData().GetZoomY();
+ sal_uInt16 nZoom = static_cast<sal_uInt16>(tools::Long( rOldY * 100 ));
+ rSet.Put( SvxZoomItem( SvxZoomType::PERCENT, nZoom, TypedWhichId<SvxZoomItem>(nWhich) ) );
+ }
+ break;
+
+ case SID_ATTR_ZOOMSLIDER:
+ {
+ if ( bOle )
+ rSet.DisableItem( nWhich );
+ else
+ {
+ const Fraction& rOldY = GetViewData().GetZoomY();
+ sal_uInt16 nCurrentZoom = static_cast<sal_uInt16>(tools::Long( rOldY * 100 ));
+
+ if( nCurrentZoom )
+ {
+ SvxZoomSliderItem aZoomSliderItem( nCurrentZoom, MINZOOM, MAXZOOM, SID_ATTR_ZOOMSLIDER );
+ aZoomSliderItem.AddSnappingPoint( 100 );
+ rSet.Put( aZoomSliderItem );
+ }
+ }
+ }
+ break;
+
+ case FID_FUNCTION_BOX:
+ {
+ const bool bBoxOpen = ::sfx2::sidebar::Sidebar::IsPanelVisible(u"ScFunctionsPanel",
+ rThisFrame.GetFrame().GetFrameInterface());
+ rSet.Put(SfxBoolItem(nWhich, bBoxOpen));
+ break;
+ }
+
+ case FID_TOGGLESYNTAX:
+ rSet.Put(SfxBoolItem(nWhich, GetViewData().IsSyntaxMode()));
+ break;
+
+ case FID_TOGGLECOLROWHIGHLIGHTING:
+ rSet.Put(SfxBoolItem(
+ nWhich,
+ officecfg::Office::Calc::Content::Display::ColumnRowHighlighting::get()));
+ break;
+
+ case FID_TOGGLEHEADERS:
+ rSet.Put(SfxBoolItem(nWhich, GetViewData().IsHeaderMode()));
+ break;
+
+ case FID_TOGGLEFORMULA:
+ {
+ const ScViewOptions& rOpts = rViewData.GetOptions();
+ bool bFormulaMode = rOpts.GetOption( VOPT_FORMULAS );
+ rSet.Put(SfxBoolItem(nWhich, bFormulaMode ));
+ }
+ break;
+
+ case FID_NORMALVIEWMODE:
+ case FID_PAGEBREAKMODE:
+ // always handle both slots - they exclude each other
+ if ( bOle )
+ {
+ rSet.DisableItem( FID_NORMALVIEWMODE );
+ rSet.DisableItem( FID_PAGEBREAKMODE );
+ }
+ else
+ {
+ rSet.Put(SfxBoolItem(FID_NORMALVIEWMODE, !GetViewData().IsPagebreakMode()));
+ rSet.Put(SfxBoolItem(FID_PAGEBREAKMODE, GetViewData().IsPagebreakMode()));
+ }
+ break;
+
+ case FID_PROTECT_DOC:
+ {
+ if ( pDocShell && pDocShell->IsDocShared() )
+ {
+ rSet.DisableItem( nWhich );
+ }
+ else
+ {
+ rSet.Put( SfxBoolItem( nWhich, rDoc.IsDocProtected() ) );
+ }
+ }
+ break;
+
+ case FID_PROTECT_TABLE:
+ {
+ if ( pDocShell && pDocShell->IsDocShared() )
+ {
+ rSet.DisableItem( nWhich );
+ }
+ else
+ {
+ rSet.Put( SfxBoolItem( nWhich, rDoc.IsTabProtected( nTab ) ) );
+ }
+ }
+ break;
+
+ case SID_AUTO_OUTLINE:
+ {
+ if (rDoc.GetChangeTrack()!=nullptr || GetViewData().IsMultiMarked())
+ {
+ rSet.DisableItem( nWhich );
+ }
+ }
+ break;
+
+ case SID_OUTLINE_DELETEALL:
+ {
+ SCTAB nOlTab = GetViewData().GetTabNo();
+ ScOutlineTable* pOlTable = rDoc.GetOutlineTable( nOlTab );
+ if (pOlTable == nullptr)
+ rSet.DisableItem( nWhich );
+ }
+ break;
+
+ case SID_WINDOW_SPLIT:
+ rSet.Put(SfxBoolItem(nWhich,
+ rViewData.GetHSplitMode() == SC_SPLIT_NORMAL ||
+ rViewData.GetVSplitMode() == SC_SPLIT_NORMAL ));
+ break;
+
+ case SID_WINDOW_FIX:
+ if(!comphelper::LibreOfficeKit::isActive())
+ {
+ rSet.Put(SfxBoolItem(nWhich,
+ rViewData.GetHSplitMode() == SC_SPLIT_FIX ||
+ rViewData.GetVSplitMode() == SC_SPLIT_FIX ));
+ }
+ else
+ {
+ rSet.Put(SfxBoolItem(nWhich,
+ rViewData.GetLOKSheetFreezeIndex(true) > 0 ||
+ rViewData.GetLOKSheetFreezeIndex(false) > 0 ));
+ }
+ break;
+
+ case SID_WINDOW_FIX_COL:
+ case SID_WINDOW_FIX_ROW:
+ {
+ Point aPos;
+ bool bIsCol = (nWhich == SID_WINDOW_FIX_COL);
+ aPos.setX(rViewData.GetLOKSheetFreezeIndex(bIsCol));
+ aPos.setY(rViewData.GetTabNo());
+ rSet.Put(SfxPointItem(nWhich, aPos));
+ }
+ break;
+
+ case FID_CHG_SHOW:
+ {
+ if ( rDoc.GetChangeTrack() == nullptr || ( pDocShell && pDocShell->IsDocShared() ) )
+ rSet.DisableItem( nWhich );
+ }
+ break;
+ case FID_CHG_ACCEPT:
+ {
+ if(
+ ( !rDoc.GetChangeTrack() && !rThisFrame.HasChildWindow(FID_CHG_ACCEPT) )
+ ||
+ ( pDocShell && pDocShell->IsDocShared() )
+ )
+ {
+ rSet.DisableItem( nWhich);
+ }
+ else
+ {
+ rSet.Put(SfxBoolItem(FID_CHG_ACCEPT,
+ rThisFrame.HasChildWindow(FID_CHG_ACCEPT)));
+ }
+ }
+ break;
+
+ case SID_FORMATPAGE:
+ // in protected tables
+ if ( pDocShell && ( pDocShell->IsReadOnly() || pDocShell->IsDocShared() ) )
+ rSet.DisableItem( nWhich );
+ break;
+
+ case SID_PRINTPREVIEW:
+ // Toggle slot needs a State
+ rSet.Put( SfxBoolItem( nWhich, false ) );
+ break;
+
+ case SID_READONLY_MODE:
+ rSet.Put( SfxBoolItem( nWhich, GetViewData().GetDocShell()->IsReadOnly() ) );
+ break;
+
+ case FID_TAB_DESELECTALL:
+ if ( nTabSelCount == 1 )
+ rSet.DisableItem( nWhich ); // enabled only if several sheets are selected
+ break;
+
+ case FID_TOGGLEHIDDENCOLROW:
+ const svtools::ColorConfig& rColorCfg = SC_MOD()->GetColorConfig();
+ rSet.Put( SfxBoolItem( nWhich, rColorCfg.GetColorValue(svtools::CALCHIDDENROWCOL).bIsVisible) );
+ break;
+
+ } // switch ( nWitch )
+ nWhich = aIter.NextWhich();
+ } // while ( nWitch )
+}
+
+void ScTabViewShell::ExecuteCellFormatDlg(SfxRequest& rReq, const OUString &rName)
+{
+ ScDocument& rDoc = GetViewData().GetDocument();
+
+ std::shared_ptr<SvxBoxItem> aLineOuter(std::make_shared<SvxBoxItem>(ATTR_BORDER));
+ std::shared_ptr<SvxBoxInfoItem> aLineInner(std::make_shared<SvxBoxInfoItem>(ATTR_BORDER_INNER));
+
+ const ScPatternAttr* pOldAttrs = GetSelectionPattern();
+
+ auto pOldSet = std::make_shared<SfxItemSet>(pOldAttrs->GetItemSet());
+
+ pOldSet->MergeRange(XATTR_FILLSTYLE, XATTR_FILLCOLOR);
+
+ pOldSet->MergeRange(SID_ATTR_BORDER_STYLES, SID_ATTR_BORDER_DEFAULT_WIDTH);
+
+ // We only allow these border line types.
+ std::vector<sal_Int32> aBorderStyles{
+ table::BorderLineStyle::SOLID,
+ table::BorderLineStyle::DOTTED,
+ table::BorderLineStyle::DASHED,
+ table::BorderLineStyle::FINE_DASHED,
+ table::BorderLineStyle::DASH_DOT,
+ table::BorderLineStyle::DASH_DOT_DOT,
+ table::BorderLineStyle::DOUBLE_THIN };
+
+ pOldSet->Put(SfxIntegerListItem(SID_ATTR_BORDER_STYLES, std::move(aBorderStyles)));
+
+ // Set the default border width to 0.75 points.
+ SfxInt64Item aBorderWidthItem(SID_ATTR_BORDER_DEFAULT_WIDTH, 75);
+ pOldSet->Put(aBorderWidthItem);
+
+ // Get border items and put them in the set:
+ GetSelectionFrame( aLineOuter, aLineInner );
+
+ //Fix border incorrect for RTL fdo#62399
+ if( rDoc.IsLayoutRTL( GetViewData().GetTabNo() ) )
+ {
+ std::unique_ptr<SvxBoxItem> aNewFrame(aLineOuter->Clone());
+ std::unique_ptr<SvxBoxInfoItem> aTempInfo(aLineInner->Clone());
+
+ if ( aLineInner->IsValid(SvxBoxInfoItemValidFlags::LEFT) )
+ aNewFrame->SetLine( aLineOuter->GetLeft(), SvxBoxItemLine::RIGHT );
+ if ( aLineInner->IsValid(SvxBoxInfoItemValidFlags::RIGHT) )
+ aNewFrame->SetLine( aLineOuter->GetRight(), SvxBoxItemLine::LEFT );
+
+ aLineInner->SetValid( SvxBoxInfoItemValidFlags::LEFT, aTempInfo->IsValid(SvxBoxInfoItemValidFlags::RIGHT));
+ aLineInner->SetValid( SvxBoxInfoItemValidFlags::RIGHT, aTempInfo->IsValid(SvxBoxInfoItemValidFlags::LEFT));
+
+ pOldSet->Put( std::move(aNewFrame) );
+ }
+ else
+ {
+ pOldSet->Put( *aLineOuter );
+ }
+
+ pOldSet->Put( *aLineInner );
+
+ // Generate NumberFormat Value from Value and Language and box it.
+ pOldSet->Put( SfxUInt32Item( ATTR_VALUE_FORMAT,
+ pOldAttrs->GetNumberFormat( rDoc.GetFormatTable() ) ) );
+
+ std::unique_ptr<SvxNumberInfoItem> pNumberInfoItem = MakeNumberInfoItem(rDoc, GetViewData());
+ pOldSet->MergeRange( SID_ATTR_NUMBERFORMAT_INFO, SID_ATTR_NUMBERFORMAT_INFO );
+ pOldSet->Put( std::move(pNumberInfoItem) );
+
+ bInFormatDialog = true;
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ VclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateScAttrDlg(GetFrameWeld(), pOldSet.get()));
+
+ if (!rName.isEmpty())
+ pDlg->SetCurPageId(rName);
+
+ auto pRequest = std::make_shared<SfxRequest>(rReq);
+ rReq.Ignore(); // the 'old' request is not relevant any more
+
+ pDlg->StartExecuteAsync([pDlg, pOldSet, pRequest, this](sal_Int32 nResult){
+ bInFormatDialog = false;
+
+ if ( nResult == RET_OK )
+ {
+ const SfxItemSet* pOutSet = pDlg->GetOutputItemSet();
+ if(const SvxNumberInfoItem* pItem = pOutSet->GetItemIfSet(SID_ATTR_NUMBERFORMAT_INFO))
+ {
+ UpdateNumberFormatter(*pItem);
+ }
+
+ ApplyAttributes(*pOutSet, *pOldSet);
+
+ pRequest->Done(*pOutSet);
+ }
+
+ pDlg->disposeOnce();
+ });
+}
+
+const OUString* ScTabViewShell::GetEditString() const
+{
+ if (mpInputHandler)
+ return &mpInputHandler->GetEditString();
+
+ return nullptr;
+}
+
+bool ScTabViewShell::IsRefInputMode() const
+{
+ ScModule* pScMod = SC_MOD();
+ if ( pScMod )
+ {
+ if( pScMod->IsRefDialogOpen() )
+ return pScMod->IsFormulaMode();
+ if( pScMod->IsFormulaMode() )
+ {
+ ScInputHandler* pHdl = pScMod->GetInputHdl();
+ if ( pHdl )
+ {
+ const ScViewData& rViewData = GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ const ScAddress aPos( rViewData.GetCurPos() );
+ const sal_uInt32 nIndex = rDoc.GetAttr(aPos, ATTR_VALUE_FORMAT )->GetValue();
+ const SvNumFormatType nType = rDoc.GetFormatTable()->GetType(nIndex);
+ if (nType == SvNumFormatType::TEXT)
+ {
+ return false;
+ }
+ OUString aString = pHdl->GetEditString();
+ if ( !pHdl->GetSelIsRef() && aString.getLength() > 1 &&
+ ( aString[0] == '+' || aString[0] == '-' ) )
+ {
+ ScCompiler aComp( rDoc, aPos, rDoc.GetGrammar() );
+ aComp.SetCloseBrackets( false );
+ std::unique_ptr<ScTokenArray> pArr(aComp.CompileString(aString));
+ if ( pArr && pArr->MayReferenceFollow() )
+ {
+ return true;
+ }
+ }
+ else
+ {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+void ScTabViewShell::ExecuteInputDirect()
+{
+ if ( !IsRefInputMode() )
+ {
+ ScModule* pScMod = SC_MOD();
+ if ( pScMod )
+ {
+ pScMod->InputEnterHandler();
+ }
+ }
+}
+
+void ScTabViewShell::UpdateInputHandler( bool bForce /* = sal_False */, bool bStopEditing /* = sal_True */ )
+{
+ ScInputHandler* pHdl = mpInputHandler ? mpInputHandler.get() : SC_MOD()->GetInputHdl();
+
+ if ( pHdl )
+ {
+ OUString aString;
+ const EditTextObject* pObject = nullptr;
+ ScViewData& rViewData = GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ SCCOL nPosX = rViewData.GetCurX();
+ SCROW nPosY = rViewData.GetCurY();
+ SCTAB nTab = rViewData.GetTabNo();
+ SCTAB nStartTab = 0;
+ SCTAB nEndTab = 0;
+ SCCOL nStartCol = 0;
+ SCROW nStartRow = 0;
+ SCCOL nEndCol = 0;
+ SCROW nEndRow = 0;
+ ScAddress aPos = rViewData.GetCurPos();
+
+ rViewData.GetSimpleArea( nStartCol, nStartRow, nStartTab,
+ nEndCol, nEndRow, nEndTab );
+
+ PutInOrder( nStartCol, nEndCol );
+ PutInOrder( nStartRow, nEndRow );
+ PutInOrder( nStartTab, nEndTab );
+
+ bool bHideFormula = false;
+ bool bHideAll = false;
+
+ if (rDoc.IsTabProtected(nTab))
+ {
+ const ScProtectionAttr* pProt = rDoc.GetAttr( nPosX,nPosY,nTab,
+ ATTR_PROTECTION);
+ bHideFormula = pProt->GetHideFormula();
+ bHideAll = pProt->GetHideCell();
+ }
+
+ if (!bHideAll)
+ {
+ ScRefCellValue rCell(rDoc, aPos);
+ if (rCell.getType() == CELLTYPE_FORMULA)
+ {
+ if (!bHideFormula)
+ aString = rCell.getFormula()->GetFormula();
+ }
+ else if (rCell.getType() == CELLTYPE_EDIT)
+ {
+ pObject = rCell.getEditText();
+ }
+ else
+ {
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ sal_uInt32 nNumFmt = rDoc.GetNumberFormat( aPos );
+
+ aString = ScCellFormat::GetInputString( rCell, nNumFmt, *pFormatter, rDoc );
+ if (rCell.getType() == CELLTYPE_STRING)
+ {
+ // Put a ' in front if necessary, so that the string is not
+ // unintentionally interpreted as a number, and to show the
+ // user that it is a string (#35060#).
+ // If cell is not formatted as Text, a leading apostrophe
+ // needs another prepended, also '=' or '+' or '-'
+ // otherwise starting a formula.
+ // NOTE: this corresponds with
+ // sc/source/core/data/column3.cxx ScColumn::ParseString()
+ // removing one apostrophe.
+ // For number format Text IsNumberFormat() would never
+ // result in numeric anyway.
+ if (!pFormatter->IsTextFormat(nNumFmt) && (aString.startsWith("'")
+ || aString.startsWith("=") || aString.startsWith("+") || aString.startsWith("-")
+ || pFormatter->IsNumberFormat(aString, nNumFmt, o3tl::temporary(double()))))
+ aString = "'" + aString;
+ }
+ }
+ }
+
+ ScInputHdlState aState( ScAddress( nPosX, nPosY, nTab ),
+ ScAddress( nStartCol, nStartRow, nTab ),
+ ScAddress( nEndCol, nEndRow, nTab ),
+ aString,
+ pObject );
+
+ // if using the view's local input handler, this view can always be set
+ // as current view inside NotifyChange.
+ ScTabViewShell* pSourceSh = mpInputHandler ? this : nullptr;
+
+ pHdl->NotifyChange( &aState, bForce, pSourceSh, bStopEditing );
+ }
+
+ SfxBindings& rBindings = GetViewFrame().GetBindings();
+ rBindings.Invalidate( SID_STATUS_SUM ); // always together with the input row
+ rBindings.Invalidate( SID_ATTR_SIZE );
+ rBindings.Invalidate( SID_TABLE_CELL );
+}
+
+void ScTabViewShell::UpdateInputHandlerCellAdjust( SvxCellHorJustify eJust )
+{
+ if( ScInputHandler* pHdl = mpInputHandler ? mpInputHandler.get() : SC_MOD()->GetInputHdl() )
+ pHdl->UpdateCellAdjust( eJust );
+}
+
+void ScTabViewShell::ExecuteSave( SfxRequest& rReq )
+{
+ // only SID_SAVEDOC / SID_SAVEASDOC
+ bool bCommitChanges = true;
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+ const SfxPoolItem* pItem;
+
+ if (pReqArgs && pReqArgs->HasItem(FN_PARAM_1, &pItem))
+ bCommitChanges = !static_cast<const SfxBoolItem*>(pItem)->GetValue();
+
+ // Finish entering unless 'DontTerminateEdit' is specified, even if a formula is being processed
+ if (bCommitChanges)
+ {
+ bool bLOKActive = comphelper::LibreOfficeKit::isActive();
+
+ // Disable error dialog box when about to save in lok mode as
+ // this ultimately invokes SvpSalInstance::DoYield() when we want
+ // to save immediately without committing any erroneous input in possibly
+ // a cell with validation rules. After save is complete the user
+ // can continue editing.
+ SC_MOD()->InputEnterHandler(ScEnterMode::NORMAL, bLOKActive /* bBeforeSavingInLOK */);
+
+ if (bLOKActive)
+ {
+ // Normally this isn't needed, but in Calc when editing a cell formula
+ // and manually saving (without changing cells or hitting enter), while
+ // InputEnterHandler will mark the doc as modified (when it is), because
+ // we will save the doc immediately afterwards, the modified state event
+ // is clobbered. To avoid that, we need to update SID_DOC_MODIFIED so that
+ // a possible state of "true" after "InputEnterHandler" will be sent
+ // as a notification. It is important that the notification goes through
+ // normal process (cache) rather than directly notifying the views.
+ // Otherwise, because there is a previous state of "false" in cache, the
+ // "false" state after saving will be ignored.
+ // This will work only if .uno:ModifiedStatus message will be removed from
+ // the mechanism that keeps in the message queue only last message of
+ // a particular status even if the values are different.
+ GetViewData().GetDocShell()->GetViewBindings()->Update(SID_DOC_MODIFIED);
+ }
+ }
+
+ if ( GetViewData().GetDocShell()->IsDocShared() )
+ {
+ GetViewData().GetDocShell()->SetDocumentModified();
+ }
+
+ // otherwise as normal
+ GetViewData().GetDocShell()->ExecuteSlot( rReq );
+}
+
+void ScTabViewShell::GetSaveState( SfxItemSet& rSet )
+{
+ SfxShell* pDocSh = GetViewData().GetDocShell();
+
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while( nWhich )
+ {
+ if ( nWhich != SID_SAVEDOC || !GetViewData().GetDocShell()->IsDocShared() )
+ {
+ // get state from DocShell
+ pDocSh->GetSlotState( nWhich, nullptr, &rSet );
+ }
+ nWhich = aIter.NextWhich();
+ }
+}
+
+void ScTabViewShell::ExecDrawOpt( const SfxRequest& rReq )
+{
+ ScViewOptions aViewOptions = GetViewData().GetOptions();
+ ScGridOptions aGridOptions = aViewOptions.GetGridOptions();
+
+ SfxBindings& rBindings = GetViewFrame().GetBindings();
+ const SfxItemSet* pArgs = rReq.GetArgs();
+ const SfxPoolItem* pItem;
+ sal_uInt16 nSlotId = rReq.GetSlot();
+ switch (nSlotId)
+ {
+ case SID_GRID_VISIBLE:
+ if ( pArgs && pArgs->GetItemState(nSlotId,true,&pItem) == SfxItemState::SET )
+ {
+ aGridOptions.SetGridVisible( static_cast<const SfxBoolItem*>(pItem)->GetValue() );
+ aViewOptions.SetGridOptions(aGridOptions);
+ rBindings.Invalidate(SID_GRID_VISIBLE);
+ }
+ break;
+
+ case SID_GRID_USE:
+ if ( pArgs && pArgs->GetItemState(nSlotId,true,&pItem) == SfxItemState::SET )
+ {
+ aGridOptions.SetUseGridSnap( static_cast<const SfxBoolItem*>(pItem)->GetValue() );
+ aViewOptions.SetGridOptions(aGridOptions);
+ rBindings.Invalidate(SID_GRID_USE);
+ }
+ break;
+
+ case SID_HELPLINES_MOVE:
+ if ( pArgs && pArgs->GetItemState(nSlotId,true,&pItem) == SfxItemState::SET )
+ {
+ aViewOptions.SetOption( VOPT_HELPLINES, static_cast<const SfxBoolItem*>(pItem)->GetValue() );
+ rBindings.Invalidate(SID_HELPLINES_MOVE);
+ }
+ break;
+ }
+
+ GetViewData().SetOptions(aViewOptions);
+}
+
+void ScTabViewShell::GetDrawOptState( SfxItemSet& rSet )
+{
+ SfxBoolItem aBool;
+
+ const ScViewOptions& rViewOptions = GetViewData().GetOptions();
+ const ScGridOptions& rGridOptions = rViewOptions.GetGridOptions();
+
+ aBool.SetValue(rGridOptions.GetGridVisible());
+ aBool.SetWhich( SID_GRID_VISIBLE );
+ rSet.Put( aBool );
+
+ aBool.SetValue(rGridOptions.GetUseGridSnap());
+ aBool.SetWhich( SID_GRID_USE );
+ rSet.Put( aBool );
+
+ aBool.SetValue(rViewOptions.GetOption( VOPT_HELPLINES ));
+ aBool.SetWhich( SID_HELPLINES_MOVE );
+ rSet.Put( aBool );
+}
+
+void ScTabViewShell::ExecStyle( SfxRequest& rReq )
+{
+ const SfxItemSet* pArgs = rReq.GetArgs();
+ const sal_uInt16 nSlotId = rReq.GetSlot();
+ if ( !pArgs && nSlotId != SID_STYLE_NEW_BY_EXAMPLE && nSlotId != SID_STYLE_UPDATE_BY_EXAMPLE )
+ {
+ // in case of vertical toolbar
+ GetDispatcher()->Execute( SID_STYLE_DESIGNER, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD );
+ return;
+ }
+
+ SfxBindings& rBindings = GetViewData().GetBindings();
+ const SCTAB nCurTab = GetViewData().GetTabNo();
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ ScModule* pScMod = SC_MOD();
+ SdrObject* pEditObject = GetDrawView()->GetTextEditObject();
+ OutlinerView* pOLV = GetDrawView()->GetTextEditOutlinerView();
+ ESelection aSelection = pOLV ? pOLV->GetSelection() : ESelection();
+ OUString aRefName;
+ bool bUndo = rDoc.IsUndoEnabled();
+
+ SfxStyleSheetBasePool* pStylePool = rDoc.GetStyleSheetPool();
+ SfxStyleSheetBase* pStyleSheet = nullptr;
+
+ bool bStyleToMarked = false;
+ bool bListAction = false;
+ bool bAddUndo = false; // add ScUndoModifyStyle (style modified)
+ ScStyleSaveData aOldData; // for undo/redo
+ ScStyleSaveData aNewData;
+
+ SfxStyleFamily eFamily = SfxStyleFamily::Para;
+ const SfxUInt16Item* pFamItem;
+ const SfxStringItem* pFamilyNameItem;
+ if ( pArgs && (pFamItem = pArgs->GetItemIfSet( SID_STYLE_FAMILY )) )
+ eFamily = static_cast<SfxStyleFamily>(pFamItem->GetValue());
+ else if ( pArgs && (pFamilyNameItem = pArgs->GetItemIfSet( SID_STYLE_FAMILYNAME )) )
+ {
+ OUString sFamily = pFamilyNameItem->GetValue();
+ if (sFamily == "CellStyles")
+ eFamily = SfxStyleFamily::Para;
+ else if (sFamily == "PageStyles")
+ eFamily = SfxStyleFamily::Page;
+ else if (sFamily == "GraphicStyles")
+ eFamily = SfxStyleFamily::Frame;
+ }
+
+ OUString aStyleName;
+ sal_uInt16 nRetMask = 0xffff;
+
+ switch ( nSlotId )
+ {
+ case SID_STYLE_NEW:
+ {
+ const SfxPoolItem* pNameItem;
+ if (pArgs && SfxItemState::SET == pArgs->GetItemState( nSlotId, true, &pNameItem ))
+ aStyleName = static_cast<const SfxStringItem*>(pNameItem)->GetValue();
+
+ const SfxStringItem* pRefItem=nullptr;
+ if (pArgs && (pRefItem = pArgs->GetItemIfSet( SID_STYLE_REFERENCE )))
+ {
+ aRefName = pRefItem->GetValue();
+ }
+
+ pStyleSheet = &(pStylePool->Make( aStyleName, eFamily,
+ SfxStyleSearchBits::UserDefined ) );
+
+ if (pStyleSheet->HasParentSupport())
+ pStyleSheet->SetParent(aRefName);
+ }
+ break;
+
+ case SID_STYLE_APPLY:
+ {
+ const SfxStringItem* pNameItem = rReq.GetArg<SfxStringItem>(SID_APPLY_STYLE);
+ const SfxStringItem* pFamilyItem = rReq.GetArg<SfxStringItem>(SID_STYLE_FAMILYNAME);
+ if ( pFamilyItem && pNameItem )
+ {
+ try
+ {
+ css::uno::Reference< css::container::XNameAccess > xStyles;
+ css::uno::Reference< css::container::XNameAccess > xCont = pDocSh->GetModel()->getStyleFamilies();
+ xCont->getByName(pFamilyItem->GetValue()) >>= xStyles;
+ css::uno::Reference< css::beans::XPropertySet > xInfo;
+ xStyles->getByName( pNameItem->GetValue() ) >>= xInfo;
+ OUString aUIName;
+ xInfo->getPropertyValue("DisplayName") >>= aUIName;
+ if ( !aUIName.isEmpty() )
+ rReq.AppendItem( SfxStringItem( SID_STYLE_APPLY, aUIName ) );
+ }
+ catch( css::uno::Exception& )
+ {
+ }
+ }
+ [[fallthrough]];
+ }
+ case SID_STYLE_EDIT:
+ case SID_STYLE_DELETE:
+ case SID_STYLE_HIDE:
+ case SID_STYLE_SHOW:
+ case SID_STYLE_NEW_BY_EXAMPLE:
+ {
+ const SfxPoolItem* pNameItem;
+ if (pArgs && SfxItemState::SET == pArgs->GetItemState( nSlotId, true, &pNameItem ))
+ aStyleName = static_cast<const SfxStringItem*>(pNameItem)->GetValue();
+ else if ( nSlotId == SID_STYLE_NEW_BY_EXAMPLE )
+ {
+ weld::Window* pDialogParent = rReq.GetFrameWeld();
+ if (!pDialogParent)
+ pDialogParent = GetFrameWeld();
+ SfxNewStyleDlg aDlg(pDialogParent, *pStylePool, eFamily);
+ if (aDlg.run() != RET_OK)
+ return;
+ aStyleName = aDlg.GetName();
+ }
+
+ pStyleSheet = pStylePool->Find( aStyleName, eFamily );
+
+ aOldData.InitFromStyle( pStyleSheet );
+ }
+ break;
+
+ case SID_STYLE_WATERCAN:
+ {
+ bool bWaterCan = pScMod->GetIsWaterCan();
+
+ if( !bWaterCan )
+ {
+ const SfxPoolItem* pItem;
+
+ if ( SfxItemState::SET ==
+ pArgs->GetItemState( nSlotId, true, &pItem ) )
+ {
+ const SfxStringItem* pStrItem = dynamic_cast< const SfxStringItem *>( pItem );
+ if ( pStrItem )
+ {
+ aStyleName = pStrItem->GetValue();
+ pStyleSheet = pStylePool->Find( aStyleName, eFamily );
+
+ if ( pStyleSheet )
+ {
+ static_cast<ScStyleSheetPool*>(pStylePool)->
+ SetActualStyleSheet( pStyleSheet );
+ rReq.Done();
+ }
+ }
+ }
+ }
+
+ if ( !bWaterCan && pStyleSheet )
+ {
+ pScMod->SetWaterCan( true );
+ SetActivePointer( PointerStyle::Fill );
+ rReq.Done();
+ }
+ else
+ {
+ pScMod->SetWaterCan( false );
+ SetActivePointer( PointerStyle::Arrow );
+ rReq.Done();
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ // set new style for paintbrush format mode
+ if ( nSlotId == SID_STYLE_APPLY && pScMod->GetIsWaterCan() && pStyleSheet )
+ static_cast<ScStyleSheetPool*>(pStylePool)->SetActualStyleSheet( pStyleSheet );
+
+ switch ( eFamily )
+ {
+ case SfxStyleFamily::Para:
+ {
+ switch ( nSlotId )
+ {
+ case SID_STYLE_DELETE:
+ {
+ if ( pStyleSheet )
+ {
+ RemoveStyleSheetInUse( pStyleSheet );
+ pStylePool->Remove( pStyleSheet );
+ InvalidateAttribs();
+ nRetMask = sal_uInt16(true);
+ bAddUndo = true;
+ rReq.Done();
+ }
+ else
+ nRetMask = sal_uInt16(false);
+ }
+ break;
+
+ case SID_STYLE_HIDE:
+ case SID_STYLE_SHOW:
+ {
+ if ( pStyleSheet )
+ {
+ pStyleSheet->SetHidden( nSlotId == SID_STYLE_HIDE );
+ InvalidateAttribs();
+ rReq.Done();
+ }
+ else
+ nRetMask = sal_uInt16(false);
+ }
+ break;
+
+ case SID_STYLE_APPLY:
+ {
+ if ( pStyleSheet && !pScMod->GetIsWaterCan() )
+ {
+ // apply style sheet to document
+ SetStyleSheetToMarked( static_cast<SfxStyleSheet*>(pStyleSheet) );
+ InvalidateAttribs();
+ rReq.Done();
+ }
+ }
+ break;
+
+ case SID_STYLE_NEW_BY_EXAMPLE:
+ case SID_STYLE_UPDATE_BY_EXAMPLE:
+ {
+ // create/replace style sheet by attributes
+ // at cursor position:
+
+ const ScPatternAttr* pAttrItem = nullptr;
+
+ // The query if marked, was always wrong here,
+ // so now no more, and just from the cursor.
+ // If attributes are to be removed from the selection, still need to be
+ // cautious not to adopt items from templates
+ // (GetSelectionPattern also collects items from originals) (# 44748 #)
+ SCCOL nCol = GetViewData().GetCurX();
+ SCROW nRow = GetViewData().GetCurY();
+ pAttrItem = rDoc.GetPattern( nCol, nRow, nCurTab );
+
+ SfxItemSet aAttrSet = pAttrItem->GetItemSet();
+ aAttrSet.ClearItem( ATTR_MERGE );
+ aAttrSet.ClearItem( ATTR_MERGE_FLAG );
+
+ // Do not adopt conditional formatting and validity,
+ // because they can not be edited in the template
+ aAttrSet.ClearItem( ATTR_VALIDDATA );
+ aAttrSet.ClearItem( ATTR_CONDITIONAL );
+
+ if ( SID_STYLE_NEW_BY_EXAMPLE == nSlotId )
+ {
+ if ( bUndo )
+ {
+ OUString aUndo = ScResId( STR_UNDO_EDITCELLSTYLE );
+ pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, GetViewShellId() );
+ bListAction = true;
+ }
+
+ bool bConvertBack = false;
+ SfxStyleSheet* pSheetInUse = const_cast<SfxStyleSheet*>(GetStyleSheetFromMarked());
+
+ // when a new style is present and is used in the selection,
+ // then the parent can not be adopted:
+ if ( pStyleSheet && pSheetInUse && pStyleSheet == pSheetInUse )
+ pSheetInUse = nullptr;
+
+ // if already present, first remove ...
+ if ( pStyleSheet )
+ {
+ // style pointer to names before erase,
+ // otherwise cells will get invalid pointer
+ //!!! As it happens, a method that does it for a particular style
+ rDoc.StylesToNames();
+ bConvertBack = true;
+ pStylePool->Remove(pStyleSheet);
+ }
+
+ // ...and create new
+ pStyleSheet = &pStylePool->Make( aStyleName, eFamily,
+ SfxStyleSearchBits::UserDefined );
+
+ // when a style is present, then this will become
+ // the parent of the new style:
+ if ( pSheetInUse && pStyleSheet->HasParentSupport() )
+ pStyleSheet->SetParent( pSheetInUse->GetName() );
+
+ if ( bConvertBack )
+ // Name to style pointer
+ rDoc.UpdStlShtPtrsFrmNms();
+ else
+ rDoc.GetPool()->CellStyleCreated( aStyleName, rDoc );
+
+ // Adopt attribute and use style
+ pStyleSheet->GetItemSet().Put( aAttrSet );
+ UpdateStyleSheetInUse( pStyleSheet );
+
+ // call SetStyleSheetToMarked after adding the ScUndoModifyStyle
+ // (pStyleSheet pointer is used!)
+ bStyleToMarked = true;
+ }
+ else // ( nSlotId == SID_STYLE_UPDATE_BY_EXAMPLE )
+ {
+ pStyleSheet = const_cast<SfxStyleSheet*>(GetStyleSheetFromMarked());
+
+ if ( pStyleSheet )
+ {
+ aOldData.InitFromStyle( pStyleSheet );
+
+ if ( bUndo )
+ {
+ OUString aUndo = ScResId( STR_UNDO_EDITCELLSTYLE );
+ pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, GetViewShellId() );
+ bListAction = true;
+ }
+
+ pStyleSheet->GetItemSet().Put( aAttrSet );
+ UpdateStyleSheetInUse( pStyleSheet );
+
+ // call SetStyleSheetToMarked after adding the ScUndoModifyStyle
+ // (pStyleSheet pointer is used!)
+ bStyleToMarked = true;
+ }
+ }
+
+ aNewData.InitFromStyle( pStyleSheet );
+ bAddUndo = true;
+ rReq.Done();
+ }
+ break;
+
+ default:
+ break;
+ }
+ } // case SfxStyleFamily::Para:
+ break;
+
+ case SfxStyleFamily::Page:
+ {
+ switch ( nSlotId )
+ {
+ case SID_STYLE_DELETE:
+ {
+ nRetMask = sal_uInt16( nullptr != pStyleSheet );
+ if ( pStyleSheet )
+ {
+ if ( rDoc.RemovePageStyleInUse( pStyleSheet->GetName() ) )
+ {
+ ScPrintFunc( pDocSh, GetPrinter(true), nCurTab ).UpdatePages();
+ rBindings.Invalidate( SID_STATUS_PAGESTYLE );
+ rBindings.Invalidate( FID_RESET_PRINTZOOM );
+ }
+ pStylePool->Remove( pStyleSheet );
+ rBindings.Invalidate( SID_STYLE_FAMILY4 );
+ pDocSh->SetDocumentModified();
+ bAddUndo = true;
+ rReq.Done();
+ }
+ }
+ break;
+
+ case SID_STYLE_HIDE:
+ case SID_STYLE_SHOW:
+ {
+ nRetMask = sal_uInt16( nullptr != pStyleSheet );
+ if ( pStyleSheet )
+ {
+ pStyleSheet->SetHidden( nSlotId == SID_STYLE_HIDE );
+ rBindings.Invalidate( SID_STYLE_FAMILY4 );
+ pDocSh->SetDocumentModified();
+ rReq.Done();
+ }
+ }
+ break;
+
+ case SID_STYLE_APPLY:
+ {
+ nRetMask = sal_uInt16( nullptr != pStyleSheet );
+ if ( pStyleSheet && !pScMod->GetIsWaterCan() )
+ {
+ std::unique_ptr<ScUndoApplyPageStyle> pUndoAction;
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+ OUString aOldName = rDoc.GetPageStyle( rTab );
+ if ( aOldName != aStyleName )
+ {
+ rDoc.SetPageStyle( rTab, aStyleName );
+ ScPrintFunc( pDocSh, GetPrinter(true), rTab ).UpdatePages();
+ if( !pUndoAction )
+ pUndoAction.reset(new ScUndoApplyPageStyle( pDocSh, aStyleName ));
+ pUndoAction->AddSheetAction( rTab, aOldName );
+ }
+ }
+ if( pUndoAction )
+ {
+ pDocSh->GetUndoManager()->AddUndoAction( std::move(pUndoAction) );
+ pDocSh->SetDocumentModified();
+ rBindings.Invalidate( SID_STYLE_FAMILY4 );
+ rBindings.Invalidate( SID_STATUS_PAGESTYLE );
+ rBindings.Invalidate( FID_RESET_PRINTZOOM );
+ }
+ rReq.Done();
+ }
+ }
+ break;
+
+ case SID_STYLE_NEW_BY_EXAMPLE:
+ {
+ const OUString& rStrCurStyle = rDoc.GetPageStyle( nCurTab );
+
+ if ( rStrCurStyle != aStyleName )
+ {
+ SfxStyleSheetBase* pCurStyle = pStylePool->Find( rStrCurStyle, eFamily );
+ SfxItemSet aAttrSet = pCurStyle->GetItemSet();
+ SCTAB nInTab;
+ bool bUsed = rDoc.IsPageStyleInUse( aStyleName, &nInTab );
+
+ // if already present, first remove...
+ if ( pStyleSheet )
+ pStylePool->Remove( pStyleSheet );
+
+ // ...and create new
+ pStyleSheet = &pStylePool->Make( aStyleName, eFamily,
+ SfxStyleSearchBits::UserDefined );
+
+ // Adopt attribute
+ pStyleSheet->GetItemSet().Put( aAttrSet );
+ pDocSh->SetDocumentModified();
+
+ // If being used -> Update
+ if ( bUsed )
+ ScPrintFunc( pDocSh, GetPrinter(true), nInTab ).UpdatePages();
+
+ aNewData.InitFromStyle( pStyleSheet );
+ bAddUndo = true;
+ rReq.Done();
+ nRetMask = sal_uInt16(true);
+ }
+ }
+ break;
+
+ default:
+ break;
+ } // switch ( nSlotId )
+ } // case SfxStyleFamily::Page:
+ break;
+
+ case SfxStyleFamily::Frame:
+ {
+ switch ( nSlotId )
+ {
+ case SID_STYLE_DELETE:
+ {
+ if ( pStyleSheet )
+ {
+ pStylePool->Remove( pStyleSheet );
+ InvalidateAttribs();
+ pDocSh->SetDocumentModified();
+ nRetMask = sal_uInt16(true);
+ bAddUndo = true;
+ rReq.Done();
+ }
+ else
+ nRetMask = sal_uInt16(false);
+ }
+ break;
+
+ case SID_STYLE_HIDE:
+ case SID_STYLE_SHOW:
+ {
+ if ( pStyleSheet )
+ {
+ pStyleSheet->SetHidden( nSlotId == SID_STYLE_HIDE );
+ InvalidateAttribs();
+ rReq.Done();
+ }
+ else
+ nRetMask = sal_uInt16(false);
+ }
+ break;
+
+ case SID_STYLE_APPLY:
+ {
+ if ( pStyleSheet && !pScMod->GetIsWaterCan() )
+ {
+ GetScDrawView()->ScEndTextEdit();
+ GetScDrawView()->SetStyleSheet(static_cast<SfxStyleSheet*>(pStyleSheet), false);
+
+ GetScDrawView()->InvalidateAttribs();
+ InvalidateAttribs();
+ rReq.Done();
+ }
+ }
+ break;
+
+ case SID_STYLE_NEW_BY_EXAMPLE:
+ case SID_STYLE_UPDATE_BY_EXAMPLE:
+ {
+ if (nSlotId == SID_STYLE_NEW_BY_EXAMPLE)
+ {
+ pStyleSheet = &pStylePool->Make( aStyleName, eFamily, SfxStyleSearchBits::UserDefined );
+
+ // when a style is present, then this will become
+ // the parent of the new style:
+ if (SfxStyleSheet* pOldStyle = GetDrawView()->GetStyleSheet())
+ pStyleSheet->SetParent(pOldStyle->GetName());
+ }
+ else
+ {
+ pStyleSheet = GetDrawView()->GetStyleSheet();
+ aOldData.InitFromStyle( pStyleSheet );
+ }
+
+ if ( bUndo )
+ {
+ OUString aUndo = ScResId( STR_UNDO_EDITGRAPHICSTYLE );
+ pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, GetViewShellId() );
+ bListAction = true;
+ }
+
+ SfxItemSet aCoreSet(GetDrawView()->GetModel().GetItemPool());
+ GetDrawView()->GetAttributes(aCoreSet, true);
+
+ SfxItemSet* pStyleSet = &pStyleSheet->GetItemSet();
+ pStyleSet->Put(aCoreSet);
+ static_cast<SfxStyleSheet*>(pStyleSheet)->Broadcast(SfxHint(SfxHintId::DataChanged));
+
+ aNewData.InitFromStyle( pStyleSheet );
+ bAddUndo = true;
+
+ // call SetStyleSheet after adding the ScUndoModifyStyle
+ // (pStyleSheet pointer is used!)
+ bStyleToMarked = true;
+ rReq.Done();
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ default:
+ break;
+ } // switch ( eFamily )
+
+ // create new or process through Dialog:
+ if ( nSlotId == SID_STYLE_NEW || nSlotId == SID_STYLE_EDIT )
+ {
+ if ( pStyleSheet )
+ {
+ SfxStyleFamily eFam = pStyleSheet->GetFamily();
+ ScopedVclPtr<SfxAbstractTabDialog> pDlg;
+ bool bPage = false;
+
+ // Store old Items from the style
+ SfxItemSet aOldSet = pStyleSheet->GetItemSet();
+ OUString aOldName = pStyleSheet->GetName();
+
+ switch ( eFam )
+ {
+ case SfxStyleFamily::Page:
+ bPage = true;
+ break;
+
+ case SfxStyleFamily::Para:
+ {
+ SfxItemSet& rSet = pStyleSheet->GetItemSet();
+
+ if ( const SfxUInt32Item* pItem = rSet.GetItemIfSet( ATTR_VALUE_FORMAT,
+ false ) )
+ {
+ // Produce and format NumberFormat Value from Value and Language
+ sal_uLong nFormat = pItem->GetValue();
+ LanguageType eLang =
+ rSet.Get(ATTR_LANGUAGE_FORMAT ).GetLanguage();
+ sal_uLong nLangFormat = rDoc.GetFormatTable()->
+ GetFormatForLanguageIfBuiltIn( nFormat, eLang );
+ if ( nLangFormat != nFormat )
+ {
+ SfxUInt32Item aNewItem( ATTR_VALUE_FORMAT, nLangFormat );
+ rSet.Put( aNewItem );
+ aOldSet.Put( aNewItem );
+ // Also in aOldSet for comparison after the dialog,
+ // Otherwise might miss a language change
+ }
+ }
+
+ std::unique_ptr<SvxNumberInfoItem> pNumberInfoItem(
+ ScTabViewShell::MakeNumberInfoItem(rDoc, GetViewData()));
+
+ pDocSh->PutItem( *pNumberInfoItem );
+ bPage = false;
+
+ // Definitely a SvxBoxInfoItem with Table = sal_False in set:
+ // (If there is no item, the dialogue will also delete the
+ // BORDER_OUTER SvxBoxItem from the Template Set)
+ if ( rSet.GetItemState( ATTR_BORDER_INNER, false ) != SfxItemState::SET )
+ {
+ SvxBoxInfoItem aBoxInfoItem( ATTR_BORDER_INNER );
+ aBoxInfoItem.SetTable(false); // no inner lines
+ aBoxInfoItem.SetDist(true);
+ aBoxInfoItem.SetMinDist(false);
+ rSet.Put( aBoxInfoItem );
+ }
+ }
+ break;
+
+ case SfxStyleFamily::Frame:
+ default:
+ break;
+ }
+
+ SetInFormatDialog(true);
+
+ SfxItemSet& rStyleSet = pStyleSheet->GetItemSet();
+ rStyleSet.MergeRange( XATTR_FILL_FIRST, XATTR_FILL_LAST );
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ weld::Window* pDialogParent = rReq.GetFrameWeld();
+ if (!pDialogParent)
+ pDialogParent = GetFrameWeld();
+
+ if (eFam == SfxStyleFamily::Frame)
+ pDlg.disposeAndReset(pFact->CreateScDrawStyleDlg(pDialogParent, *pStyleSheet, GetDrawView()));
+ else
+ pDlg.disposeAndReset(pFact->CreateScStyleDlg(pDialogParent, *pStyleSheet, bPage));
+
+ short nResult = pDlg->Execute();
+ SetInFormatDialog(false);
+
+ if ( nResult == RET_OK )
+ {
+ const SfxItemSet* pOutSet = pDlg->GetOutputItemSet();
+
+ if ( pOutSet )
+ {
+ nRetMask = sal_uInt16(pStyleSheet->GetMask());
+
+ // Attribute comparisons (earlier in ModifyStyleSheet) now here
+ // with the old values (the style is already changed)
+ if ( SfxStyleFamily::Para == eFam )
+ {
+ SfxItemSet& rNewSet = pStyleSheet->GetItemSet();
+ bool bNumFormatChanged;
+ if ( ScGlobal::CheckWidthInvalidate(
+ bNumFormatChanged, rNewSet, aOldSet ) )
+ rDoc.InvalidateTextWidth( nullptr, nullptr, bNumFormatChanged );
+
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (SCTAB nTab=0; nTab<nTabCount; nTab++)
+ rDoc.SetStreamValid(nTab, false);
+
+ sal_uLong nOldFormat = aOldSet.Get( ATTR_VALUE_FORMAT ).GetValue();
+ sal_uLong nNewFormat = rNewSet.Get( ATTR_VALUE_FORMAT ).GetValue();
+ if ( nNewFormat != nOldFormat )
+ {
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ const SvNumberformat* pOld = pFormatter->GetEntry( nOldFormat );
+ const SvNumberformat* pNew = pFormatter->GetEntry( nNewFormat );
+ if ( pOld && pNew && pOld->GetLanguage() != pNew->GetLanguage() )
+ rNewSet.Put( SvxLanguageItem(
+ pNew->GetLanguage(), ATTR_LANGUAGE_FORMAT ) );
+ }
+
+ rDoc.GetPool()->CellStyleCreated( pStyleSheet->GetName(), rDoc );
+ }
+ else if ( SfxStyleFamily::Page == eFam )
+ {
+ //! Here also queries for Page Styles
+
+ OUString aNewName = pStyleSheet->GetName();
+ if ( aNewName != aOldName &&
+ rDoc.RenamePageStyleInUse( aOldName, aNewName ) )
+ {
+ rBindings.Invalidate( SID_STATUS_PAGESTYLE );
+ rBindings.Invalidate( FID_RESET_PRINTZOOM );
+ }
+
+ rDoc.ModifyStyleSheet( *pStyleSheet, *pOutSet );
+ rBindings.Invalidate( FID_RESET_PRINTZOOM );
+ }
+ else
+ {
+ SfxItemSet& rAttr = pStyleSheet->GetItemSet();
+ sdr::properties::CleanupFillProperties(rAttr);
+
+ // check for unique names of named items for xml
+ auto checkForUniqueItem = [&] (auto nWhichId)
+ {
+ if (auto pOldItem = rAttr.GetItemIfSet(nWhichId, false))
+ {
+ if (auto pNewItem = pOldItem->checkForUniqueItem(&GetDrawView()->GetModel()))
+ rAttr.Put(std::move(pNewItem));
+ }
+ };
+
+ checkForUniqueItem(XATTR_FILLBITMAP);
+ checkForUniqueItem(XATTR_LINEDASH);
+ checkForUniqueItem(XATTR_LINESTART);
+ checkForUniqueItem(XATTR_LINEEND);
+ checkForUniqueItem(XATTR_FILLGRADIENT);
+ checkForUniqueItem(XATTR_FILLFLOATTRANSPARENCE);
+ checkForUniqueItem(XATTR_FILLHATCH);
+
+ static_cast<SfxStyleSheet*>(pStyleSheet)->Broadcast(SfxHint(SfxHintId::DataChanged));
+ GetScDrawView()->InvalidateAttribs();
+ }
+
+ pDocSh->SetDocumentModified();
+
+ if ( SfxStyleFamily::Para == eFam )
+ {
+ ScTabViewShell::UpdateNumberFormatter(
+ *( pDocSh->GetItem(SID_ATTR_NUMBERFORMAT_INFO) ));
+
+ UpdateStyleSheetInUse( pStyleSheet );
+ InvalidateAttribs();
+ }
+
+ aNewData.InitFromStyle( pStyleSheet );
+ bAddUndo = true;
+ }
+ }
+ else
+ {
+ if ( nSlotId == SID_STYLE_NEW )
+ pStylePool->Remove( pStyleSheet );
+ else
+ {
+ // If in the meantime something was painted with the
+ // temporary changed item set
+ pDocSh->PostPaintGridAll();
+ }
+ }
+ }
+ }
+
+ rReq.SetReturnValue( SfxUInt16Item( nSlotId, nRetMask ) );
+
+ if ( bAddUndo && bUndo)
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoModifyStyle>( pDocSh, eFamily, aOldData, aNewData ) );
+
+ if ( bStyleToMarked )
+ {
+ // call SetStyleSheetToMarked after adding the ScUndoModifyStyle,
+ // so redo will find the modified style
+ if (eFamily == SfxStyleFamily::Para)
+ {
+ SetStyleSheetToMarked( static_cast<SfxStyleSheet*>(pStyleSheet) );
+ }
+ else if (eFamily == SfxStyleFamily::Frame)
+ {
+ GetScDrawView()->ScEndTextEdit();
+ GetScDrawView()->SetStyleSheet( static_cast<SfxStyleSheet*>(pStyleSheet), false );
+ }
+ InvalidateAttribs();
+ }
+
+ if ( bListAction )
+ pDocSh->GetUndoManager()->LeaveListAction();
+
+ // The above call to ScEndTextEdit left us in an inconsistent state:
+ // Text editing isn't active, but the text edit shell still is. And we
+ // couldn't just deactivate it fully, because in case of editing a
+ // comment, that will make the comment disappear. So let's try to
+ // reactivate text editing instead:
+ auto pFuText = dynamic_cast<FuText*>(GetDrawFuncPtr());
+ if (pFuText && pEditObject != GetDrawView()->GetTextEditObject())
+ {
+ pFuText->SetInEditMode(pEditObject);
+ if (GetDrawView()->GetTextEditOutlinerView())
+ GetDrawView()->GetTextEditOutlinerView()->SetSelection(aSelection);
+ }
+}
+
+void ScTabViewShell::GetStyleState( SfxItemSet& rSet )
+{
+ ScDocument& rDoc = GetViewData().GetDocument();
+ SfxStyleSheetBasePool* pStylePool = rDoc.GetStyleSheetPool();
+
+ bool bProtected = false;
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (SCTAB i=0; i<nTabCount && !bProtected; i++)
+ if (rDoc.IsTabProtected(i)) // look after protected table
+ bProtected = true;
+
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ sal_uInt16 nSlotId = 0;
+
+ while ( nWhich )
+ {
+ nSlotId = SfxItemPool::IsWhich( nWhich )
+ ? GetPool().GetSlotId( nWhich )
+ : nWhich;
+
+ switch ( nSlotId )
+ {
+ case SID_STYLE_APPLY:
+ if ( !pStylePool )
+ rSet.DisableItem( nSlotId );
+ break;
+
+ case SID_STYLE_FAMILY2: // cell style sheets
+ {
+ SfxStyleSheet* pStyleSheet = const_cast<SfxStyleSheet*>(GetStyleSheetFromMarked());
+
+ if ( pStyleSheet )
+ rSet.Put( SfxTemplateItem( nSlotId, pStyleSheet->GetName() ) );
+ else
+ rSet.Put( SfxTemplateItem( nSlotId, OUString() ) );
+ }
+ break;
+
+ case SID_STYLE_FAMILY3: // drawing style sheets
+ {
+ SfxStyleSheet* pStyleSheet = GetDrawView()->GetStyleSheet();
+
+ if ( pStyleSheet )
+ rSet.Put( SfxTemplateItem( nSlotId, pStyleSheet->GetName() ) );
+ else
+ rSet.Put( SfxTemplateItem( nSlotId, OUString() ) );
+ }
+ break;
+
+ case SID_STYLE_FAMILY4: // page style sheets
+ {
+ SCTAB nCurTab = GetViewData().GetTabNo();
+ OUString aPageStyle = rDoc.GetPageStyle( nCurTab );
+ SfxStyleSheet* pStyleSheet = pStylePool ? static_cast<SfxStyleSheet*>(pStylePool->
+ Find( aPageStyle, SfxStyleFamily::Page )) : nullptr;
+
+ if ( pStyleSheet )
+ rSet.Put( SfxTemplateItem( nSlotId, aPageStyle ) );
+ else
+ rSet.Put( SfxTemplateItem( nSlotId, OUString() ) );
+ }
+ break;
+
+ case SID_STYLE_WATERCAN:
+ {
+ rSet.Put( SfxBoolItem( nSlotId, SC_MOD()->GetIsWaterCan() ) );
+ }
+ break;
+
+ case SID_STYLE_UPDATE_BY_EXAMPLE:
+ {
+ std::unique_ptr<SfxUInt16Item> pFamilyItem;
+ GetViewFrame().GetBindings().QueryState(SID_STYLE_FAMILY, pFamilyItem);
+
+ bool bPage = pFamilyItem && SfxStyleFamily::Page == static_cast<SfxStyleFamily>(pFamilyItem->GetValue());
+
+ if ( bProtected || bPage )
+ rSet.DisableItem( nSlotId );
+ }
+ break;
+
+ case SID_STYLE_EDIT:
+ case SID_STYLE_DELETE:
+ case SID_STYLE_HIDE:
+ case SID_STYLE_SHOW:
+ {
+ std::unique_ptr<SfxUInt16Item> pFamilyItem;
+ GetViewFrame().GetBindings().QueryState(SID_STYLE_FAMILY, pFamilyItem);
+ bool bPage = pFamilyItem && SfxStyleFamily::Page == static_cast<SfxStyleFamily>(pFamilyItem->GetValue());
+
+ if ( bProtected && !bPage )
+ rSet.DisableItem( nSlotId );
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ nWhich = aIter.NextWhich();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabvwshb.cxx b/sc/source/ui/view/tabvwshb.cxx
new file mode 100644
index 0000000000..ad0e757ce0
--- /dev/null
+++ b/sc/source/ui/view/tabvwshb.cxx
@@ -0,0 +1,919 @@
+/* -*- 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 .
+ */
+
+#include <com/sun/star/chart2/data/XDataReceiver.hpp>
+#include <com/sun/star/awt/XRequestCallback.hpp>
+#include <com/sun/star/awt/Rectangle.hpp>
+
+#include <com/sun/star/embed/EmbedMisc.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <vcl/errinf.hxx>
+#include <sfx2/app.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <svx/svxdlg.hxx>
+#include <svx/dataaccessdescriptor.hxx>
+#include <svx/svditer.hxx>
+#include <svx/svdmark.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdouno.hxx>
+#include <svx/svdview.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <svx/fontworkbar.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <svtools/soerr.hxx>
+#include <svl/rectitem.hxx>
+#include <svl/stritem.hxx>
+#include <svl/slstitm.hxx>
+#include <svl/whiter.hxx>
+#include <svtools/strings.hrc>
+#include <unotools/moduleoptions.hxx>
+#include <sot/exchange.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+#include <tabvwsh.hxx>
+#include <scmod.hxx>
+#include <document.hxx>
+#include <sc.hrc>
+#include <client.hxx>
+#include <fuinsert.hxx>
+#include <docsh.hxx>
+#include <drawview.hxx>
+#include <ChartRangeSelectionListener.hxx>
+#include <gridwin.hxx>
+#include <undomanager.hxx>
+#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
+#include <svx/svdpagv.hxx>
+#include <o3tl/temporary.hxx>
+#include <officecfg/Office/Common.hxx>
+
+#include <comphelper/lok.hxx>
+
+using namespace com::sun::star;
+
+void ScTabViewShell::ConnectObject( const SdrOle2Obj* pObj )
+{
+ // is called from paint
+
+ uno::Reference < embed::XEmbeddedObject > xObj = pObj->GetObjRef();
+ vcl::Window* pWin = GetActiveWin();
+
+ // when already connected do not execute SetObjArea/SetSizeScale again
+
+ SfxInPlaceClient* pClient = FindIPClient( xObj, pWin );
+ if ( pClient )
+ return;
+
+ pClient = new ScClient( this, pWin, &GetScDrawView()->GetModel(), pObj );
+ ScViewData& rViewData = GetViewData();
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ bool bNegativeX = comphelper::LibreOfficeKit::isActive() && rDoc.IsNegativePage(rViewData.GetTabNo());
+ if (bNegativeX)
+ pClient->SetNegativeX(true);
+
+ tools::Rectangle aRect = pObj->GetLogicRect();
+ Size aDrawSize = aRect.GetSize();
+
+ Size aOleSize = pObj->GetOrigObjSize();
+
+ Fraction aScaleWidth (aDrawSize.Width(), aOleSize.Width() );
+ Fraction aScaleHeight(aDrawSize.Height(), aOleSize.Height() );
+ aScaleWidth.ReduceInaccurate(10); // compatible with SdrOle2Obj
+ aScaleHeight.ReduceInaccurate(10);
+ pClient->SetSizeScale(aScaleWidth,aScaleHeight);
+
+ // visible section is only changed inplace!
+ // the object area must be set after the scaling since it triggers the resizing
+ aRect.SetSize( aOleSize );
+ pClient->SetObjArea( aRect );
+}
+
+namespace {
+
+class PopupCallback : public cppu::WeakImplHelper<css::awt::XCallback>
+{
+ ScTabViewShell* m_pViewShell;
+ SdrOle2Obj* m_pObject;
+
+public:
+ explicit PopupCallback(ScTabViewShell* pViewShell, SdrOle2Obj* pObject)
+ : m_pViewShell(pViewShell)
+ , m_pObject(pObject)
+ {}
+
+ // XCallback
+ virtual void SAL_CALL notify(const css::uno::Any& aData) override
+ {
+ uno::Sequence<beans::PropertyValue> aProperties;
+ if (!(aData >>= aProperties))
+ return;
+
+ awt::Rectangle xRectangle;
+ sal_Int32 dimensionIndex = 0;
+ OUString sPivotTableName("DataPilot1");
+
+ for (beans::PropertyValue const & rProperty : std::as_const(aProperties))
+ {
+ if (rProperty.Name == "Rectangle")
+ rProperty.Value >>= xRectangle;
+ if (rProperty.Name == "DimensionIndex")
+ rProperty.Value >>= dimensionIndex;
+ if (rProperty.Name == "PivotTableName")
+ rProperty.Value >>= sPivotTableName;
+ }
+
+ tools::Rectangle aChartRect = m_pObject->GetLogicRect();
+
+ Point aPoint(xRectangle.X + aChartRect.Left(), xRectangle.Y + aChartRect.Top());
+ Size aSize(xRectangle.Width, xRectangle.Height);
+
+ m_pViewShell->DoDPFieldPopup(sPivotTableName, dimensionIndex, aPoint, aSize);
+ }
+};
+
+}
+
+void ScTabViewShell::ActivateObject(SdrOle2Obj* pObj, sal_Int32 nVerb)
+{
+ // Do not leave the hint message box on top of the object
+ RemoveHintWindow();
+
+ uno::Reference < embed::XEmbeddedObject > xObj = pObj->GetObjRef();
+ vcl::Window* pWin = GetActiveWin();
+ ErrCodeMsg nErr = ERRCODE_NONE;
+ bool bErrorShown = false;
+
+ {
+ ScViewData& rViewData = GetViewData();
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ bool bNegativeX = comphelper::LibreOfficeKit::isActive() && rDoc.IsNegativePage(rViewData.GetTabNo());
+ SfxInPlaceClient* pClient = FindIPClient( xObj, pWin );
+ if ( !pClient )
+ pClient = new ScClient( this, pWin, &GetScDrawView()->GetModel(), pObj );
+
+ if (bNegativeX)
+ pClient->SetNegativeX(true);
+
+ if ( (sal_uInt32(nErr.GetCode()) & ERRCODE_ERROR_MASK) == 0 && xObj.is() )
+ {
+ tools::Rectangle aRect = pObj->GetLogicRect();
+
+ {
+ // #i118485# center on BoundRect for activation,
+ // OLE may be sheared/rotated now
+ const tools::Rectangle& rBoundRect = pObj->GetCurrentBoundRect();
+ const Point aDelta(rBoundRect.Center() - aRect.Center());
+ aRect.Move(aDelta.X(), aDelta.Y());
+ }
+
+ Size aDrawSize = aRect.GetSize();
+
+ MapMode aMapMode( MapUnit::Map100thMM );
+ Size aOleSize = pObj->GetOrigObjSize( &aMapMode );
+
+ if ( pClient->GetAspect() != embed::Aspects::MSOLE_ICON
+ && ( xObj->getStatus( pClient->GetAspect() ) & embed::EmbedMisc::MS_EMBED_RECOMPOSEONRESIZE ) )
+ {
+ // scale must always be 1 - change VisArea if different from client size
+
+ if ( aDrawSize != aOleSize )
+ {
+ MapUnit aUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( pClient->GetAspect() ) );
+ aOleSize = OutputDevice::LogicToLogic( aDrawSize,
+ MapMode(MapUnit::Map100thMM), MapMode(aUnit));
+ awt::Size aSz( aOleSize.Width(), aOleSize.Height() );
+ xObj->setVisualAreaSize( pClient->GetAspect(), aSz );
+ }
+ Fraction aOne( 1, 1 );
+ pClient->SetSizeScale( aOne, aOne );
+ }
+ else
+ {
+ // calculate scale from client and VisArea size
+
+ Fraction aScaleWidth (aDrawSize.Width(), aOleSize.Width() );
+ Fraction aScaleHeight(aDrawSize.Height(), aOleSize.Height() );
+ aScaleWidth.ReduceInaccurate(10); // compatible with SdrOle2Obj
+ aScaleHeight.ReduceInaccurate(10);
+ pClient->SetSizeScale(aScaleWidth,aScaleHeight);
+ }
+
+ // visible section is only changed inplace!
+ // the object area must be set after the scaling since it triggers the resizing
+ aRect.SetSize( aOleSize );
+ pClient->SetObjArea( aRect );
+
+ nErr = pClient->DoVerb( nVerb );
+ bErrorShown = true;
+ // SfxViewShell::DoVerb shows its error messages
+
+ // attach listener to selection changes in chart that affect cell
+ // ranges, so those can be highlighted
+ // note: do that after DoVerb, so that the chart controller exists
+ if ( SvtModuleOptions().IsChart() )
+ {
+ SvGlobalName aObjClsId ( xObj->getClassID() );
+ if (SotExchange::IsChart( aObjClsId ))
+ {
+ try
+ {
+ uno::Reference < embed::XComponentSupplier > xSup( xObj, uno::UNO_QUERY_THROW );
+ uno::Reference< chart2::data::XDataReceiver > xDataReceiver(
+ xSup->getComponent(), uno::UNO_QUERY_THROW );
+ uno::Reference< chart2::data::XRangeHighlighter > xRangeHighlighter(
+ xDataReceiver->getRangeHighlighter());
+ if (xRangeHighlighter.is())
+ {
+ uno::Reference< view::XSelectionChangeListener > xListener(
+ new ScChartRangeSelectionListener( this ));
+ xRangeHighlighter->addSelectionChangeListener( xListener );
+ }
+ uno::Reference<awt::XRequestCallback> xPopupRequest(xDataReceiver->getPopupRequest());
+ if (xPopupRequest.is())
+ {
+ uno::Reference<awt::XCallback> xCallback(new PopupCallback(this, pObj));
+ uno::Any aAny;
+ xPopupRequest->addCallback(xCallback, aAny);
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ TOOLS_WARN_EXCEPTION( "sc", "Exception caught while querying chart" );
+ }
+ }
+ }
+ }
+ }
+ if (nErr != ERRCODE_NONE && !bErrorShown)
+ ErrorHandler::HandleError(nErr);
+
+ // #i118524# refresh handles to suppress for activated OLE
+ if(GetScDrawView())
+ {
+ GetScDrawView()->AdjustMarkHdl();
+ }
+ //! SetDocumentName should already happen in Sfx ???
+ //TODO/LATER: how "SetDocumentName"?
+ //xIPObj->SetDocumentName( GetViewData().GetDocShell()->GetTitle() );
+}
+
+ErrCode ScTabViewShell::DoVerb(sal_Int32 nVerb)
+{
+ SdrView* pView = GetScDrawView();
+ if (!pView)
+ return ERRCODE_SO_NOTIMPL; // should not be
+
+ SdrOle2Obj* pOle2Obj = nullptr;
+
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+ if (rMarkList.GetMarkCount() == 1)
+ {
+ SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
+ if (pObj->GetObjIdentifier() == SdrObjKind::OLE2)
+ pOle2Obj = static_cast<SdrOle2Obj*>(pObj);
+ }
+
+ if (pOle2Obj)
+ {
+ ActivateObject( pOle2Obj, nVerb );
+ }
+ else
+ {
+ OSL_FAIL("no object for Verb found");
+ }
+
+ return ERRCODE_NONE;
+}
+
+void ScTabViewShell::DeactivateOle()
+{
+ // deactivate inplace editing if currently active
+
+ ScModule* pScMod = SC_MOD();
+ bool bUnoRefDialog = pScMod->IsRefDialogOpen() && pScMod->GetCurRefDlgId() == WID_SIMPLE_REF;
+
+ ScClient* pClient = static_cast<ScClient*>(GetIPClient());
+ if ( pClient && pClient->IsObjectInPlaceActive() && !bUnoRefDialog )
+ pClient->DeactivateObject();
+}
+
+IMPL_LINK( ScTabViewShell, DialogClosedHdl, css::ui::dialogs::DialogClosedEvent*, pEvent, void )
+{
+ if( pEvent->DialogResult == ui::dialogs::ExecutableDialogResults::CANCEL )
+ {
+ ScTabView* pTabView = GetViewData().GetView();
+ ScDrawView* pView = pTabView->GetScDrawView();
+ ScViewData& rData = GetViewData();
+ ScDocShell* pScDocSh = rData.GetDocShell();
+ ScDocument& rScDoc = pScDocSh->GetDocument();
+ // leave OLE inplace mode and unmark
+ OSL_ASSERT( pView );
+ DeactivateOle();
+ pView->UnMarkAll();
+
+ rScDoc.GetUndoManager()->Undo();
+ rScDoc.GetUndoManager()->ClearRedo();
+
+ // leave the draw shell
+ SetDrawShell( false );
+
+ // reset marked cell area
+ ScMarkData aMark = GetViewData().GetMarkData();
+ GetViewData().GetViewShell()->SetMarkData(aMark);
+ }
+ else
+ {
+ OSL_ASSERT( pEvent->DialogResult == ui::dialogs::ExecutableDialogResults::OK );
+ //@todo maybe move chart to different table
+ }
+}
+
+void ScTabViewShell::ExecDrawIns(SfxRequest& rReq)
+{
+ sal_uInt16 nSlot = rReq.GetSlot();
+ if (nSlot != SID_OBJECTRESIZE )
+ {
+ SC_MOD()->InputEnterHandler();
+ UpdateInputHandler();
+ }
+
+ // insertion of border for Chart is cancelled:
+ FuPoor* pPoor = GetDrawFuncPtr();
+ if ( pPoor && pPoor->GetSlotID() == SID_DRAW_CHART )
+ GetViewData().GetDispatcher().Execute(SID_DRAW_CHART, SfxCallMode::SLOT | SfxCallMode::RECORD);
+
+ MakeDrawLayer();
+
+ SfxBindings& rBindings = GetViewFrame().GetBindings();
+ ScTabView* pTabView = GetViewData().GetView();
+ vcl::Window* pWin = pTabView->GetActiveWin();
+ ScDrawView* pView = pTabView->GetScDrawView();
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SdrModel& rModel = pView->GetModel();
+
+ switch ( nSlot )
+ {
+ case SID_INSERT_GRAPHIC:
+ FuInsertGraphic(*this, pWin, pView, &rModel, rReq);
+ // shell is set in MarkListHasChanged
+ break;
+
+ case SID_INSERT_AVMEDIA:
+ FuInsertMedia(*this, pWin, pView, &rModel, rReq);
+ // shell is set in MarkListHasChanged
+ break;
+
+ case SID_INSERT_DIAGRAM:
+ FuInsertChart(*this, pWin, pView, &rModel, rReq, LINK( this, ScTabViewShell, DialogClosedHdl ));
+ if (comphelper::LibreOfficeKit::isActive())
+ pDocSh->SetModified();
+ break;
+
+ case SID_INSERT_OBJECT:
+ case SID_INSERT_SMATH:
+ case SID_INSERT_FLOATINGFRAME:
+ FuInsertOLE(*this, pWin, pView, &rModel, rReq);
+ break;
+
+ case SID_INSERT_SIGNATURELINE:
+ case SID_EDIT_SIGNATURELINE:
+ {
+ const uno::Reference<frame::XModel> xModel( GetViewData().GetDocShell()->GetBaseModel() );
+
+ VclAbstractDialogFactory* pFact = VclAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractSignatureLineDialog> pDialog(pFact->CreateSignatureLineDialog(
+ pWin->GetFrameWeld(), xModel, rReq.GetSlot() == SID_EDIT_SIGNATURELINE));
+ pDialog->Execute();
+ break;
+ }
+
+ case SID_SIGN_SIGNATURELINE:
+ {
+ const uno::Reference<frame::XModel> xModel(
+ GetViewData().GetDocShell()->GetBaseModel());
+
+ VclAbstractDialogFactory* pFact = VclAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractSignSignatureLineDialog> pDialog(
+ pFact->CreateSignSignatureLineDialog(GetFrameWeld(), xModel));
+ pDialog->Execute();
+ break;
+ }
+
+ case SID_INSERT_QRCODE:
+ case SID_EDIT_QRCODE:
+ {
+ const uno::Reference<frame::XModel> xModel( GetViewData().GetDocShell()->GetBaseModel() );
+
+ VclAbstractDialogFactory* pFact = VclAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractQrCodeGenDialog> pDialog(pFact->CreateQrCodeGenDialog(
+ pWin->GetFrameWeld(), xModel, rReq.GetSlot() == SID_EDIT_QRCODE));
+ pDialog->Execute();
+ break;
+ }
+
+ case SID_ADDITIONS_DIALOG:
+ {
+ OUString sAdditionsTag = "";
+
+ const SfxStringItem* pStringArg = rReq.GetArg<SfxStringItem>(FN_PARAM_ADDITIONS_TAG);
+ if (pStringArg)
+ sAdditionsTag = pStringArg->GetValue();
+
+ VclAbstractDialogFactory* pFact = VclAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractAdditionsDialog> pDialog(
+ pFact->CreateAdditionsDialog(pWin->GetFrameWeld(), sAdditionsTag));
+ pDialog->Execute();
+ break;
+ }
+
+ case SID_OBJECTRESIZE:
+ {
+ // the server would like to change the client size
+
+ SfxInPlaceClient* pClient = GetIPClient();
+
+ if ( pClient && pClient->IsObjectInPlaceActive() )
+ {
+ const SfxRectangleItem& rRect = rReq.GetArgs()->Get(SID_OBJECTRESIZE);
+ tools::Rectangle aRect( pWin->PixelToLogic( rRect.GetValue() ) );
+
+ if ( pView->AreObjectsMarked() )
+ {
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+
+ if (rMarkList.GetMarkCount() == 1)
+ {
+ SdrMark* pMark = rMarkList.GetMark(0);
+ SdrObject* pObj = pMark->GetMarkedSdrObj();
+
+ SdrObjKind nSdrObjKind = pObj->GetObjIdentifier();
+
+ if (nSdrObjKind == SdrObjKind::OLE2)
+ {
+ if ( static_cast<SdrOle2Obj*>(pObj)->GetObjRef().is() )
+ {
+ pObj->SetLogicRect(aRect);
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case SID_LINKS:
+ {
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ if (officecfg::Office::Common::Security::Scripting::DisableActiveContent::get())
+ {
+ std::unique_ptr<weld::MessageDialog> xError(Application::CreateMessageDialog(
+ nullptr, VclMessageType::Warning, VclButtonsType::Ok,
+ SvtResId(STR_WARNING_EXTERNAL_LINK_EDIT_DISABLED)));
+ xError->run();
+ break;
+ }
+
+ ScopedVclPtr<SfxAbstractLinksDialog> pDlg(pFact->CreateLinksDialog(pWin->GetFrameWeld(), rDoc.GetLinkManager()));
+ pDlg->Execute();
+ rBindings.Invalidate( nSlot );
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) ); // Navigator
+ rReq.Done();
+ }
+ break;
+
+ case SID_FM_CREATE_FIELDCONTROL:
+ {
+ const SfxUnoAnyItem* pDescriptorItem = rReq.GetArg<SfxUnoAnyItem>(SID_FM_DATACCESS_DESCRIPTOR);
+ OSL_ENSURE( pDescriptorItem, "SID_FM_CREATE_FIELDCONTROL: invalid request args!" );
+
+ if(pDescriptorItem)
+ {
+ //! merge with ScViewFunc::PasteDataFormat (SotClipboardFormatId::SBA_FIELDDATAEXCHANGE)?
+
+ ScDrawView* pDrView = GetScDrawView();
+ SdrPageView* pPageView = pDrView ? pDrView->GetSdrPageView() : nullptr;
+ if(pPageView)
+ {
+ svx::ODataAccessDescriptor aDescriptor(pDescriptorItem->GetValue());
+ rtl::Reference<SdrObject> pNewDBField = pDrView->CreateFieldControl(aDescriptor);
+
+ if(pNewDBField)
+ {
+ tools::Rectangle aVisArea = pWin->PixelToLogic(tools::Rectangle(Point(0,0), pWin->GetOutputSizePixel()));
+ Point aObjPos(aVisArea.Center());
+ Size aObjSize(pNewDBField->GetLogicRect().GetSize());
+ aObjPos.AdjustX( -(aObjSize.Width() / 2) );
+ aObjPos.AdjustY( -(aObjSize.Height() / 2) );
+ tools::Rectangle aNewObjectRectangle(aObjPos, aObjSize);
+
+ pNewDBField->SetLogicRect(aNewObjectRectangle);
+
+ // controls must be on control layer, groups on front layer
+ if ( dynamic_cast<const SdrUnoObj*>( pNewDBField.get() ) != nullptr )
+ pNewDBField->NbcSetLayer(SC_LAYER_CONTROLS);
+ else
+ pNewDBField->NbcSetLayer(SC_LAYER_FRONT);
+ if (dynamic_cast<const SdrObjGroup*>( pNewDBField.get() ) != nullptr)
+ {
+ SdrObjListIter aIter( *pNewDBField, SdrIterMode::DeepWithGroups );
+ SdrObject* pSubObj = aIter.Next();
+ while (pSubObj)
+ {
+ if ( dynamic_cast<const SdrUnoObj*>( pSubObj) != nullptr )
+ pSubObj->NbcSetLayer(SC_LAYER_CONTROLS);
+ else
+ pSubObj->NbcSetLayer(SC_LAYER_FRONT);
+ pSubObj = aIter.Next();
+ }
+ }
+
+ pView->InsertObjectAtView(pNewDBField.get(), *pPageView);
+ }
+ }
+ }
+ rReq.Done();
+ }
+ break;
+
+ case SID_FONTWORK_GALLERY_FLOATER:
+ svx::FontworkBar::execute(*pView, rReq, GetViewFrame().GetBindings());
+ rReq.Ignore();
+ break;
+ }
+}
+
+void ScTabViewShell::GetDrawInsState(SfxItemSet &rSet)
+{
+ bool bOle = GetViewFrame().GetFrame().IsInPlace();
+ bool bTabProt = GetViewData().GetDocument().IsTabProtected(GetViewData().GetTabNo());
+ ScDocShell* pDocShell = GetViewData().GetDocShell();
+ bool bShared = pDocShell && pDocShell->IsDocShared();
+ SdrView* pSdrView = GetScDrawView();
+
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while ( nWhich )
+ {
+ switch ( nWhich )
+ {
+ case SID_INSERT_DIAGRAM:
+ if ( bOle || bTabProt || !SvtModuleOptions().IsChart() || bShared )
+ rSet.DisableItem( nWhich );
+ break;
+
+ case SID_INSERT_SMATH:
+ if ( bOle || bTabProt || !SvtModuleOptions().IsMath() || bShared )
+ rSet.DisableItem( nWhich );
+ break;
+
+ case SID_INSERT_OBJECT:
+ case SID_INSERT_FLOATINGFRAME:
+ if ( bOle || bTabProt || bShared )
+ rSet.DisableItem( nWhich );
+ break;
+
+ case SID_INSERT_AVMEDIA:
+ case SID_FONTWORK_GALLERY_FLOATER:
+ if ( bTabProt || bShared )
+ rSet.DisableItem( nWhich );
+ break;
+
+ case SID_INSERT_SIGNATURELINE:
+ if ( bTabProt || bShared || (pSdrView && pSdrView->GetMarkedObjectCount() != 0))
+ rSet.DisableItem( nWhich );
+ break;
+ case SID_EDIT_SIGNATURELINE:
+ case SID_SIGN_SIGNATURELINE:
+ if (!IsSignatureLineSelected() || IsSignatureLineSigned())
+ rSet.DisableItem(nWhich);
+ break;
+
+ case SID_INSERT_QRCODE:
+ if ( bTabProt || bShared || (pSdrView && pSdrView->GetMarkedObjectCount() != 0))
+ rSet.DisableItem( nWhich );
+ break;
+ case SID_EDIT_QRCODE:
+ if (!IsQRCodeSelected())
+ rSet.DisableItem(nWhich);
+ break;
+
+ case SID_INSERT_GRAPHIC:
+ if (bTabProt || bShared)
+ {
+ // do not disable 'insert graphic' item if the currently marked area is editable (not protected)
+ // if there is no marked area, check the current cell
+ bool bDisableInsertImage = true;
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ if (!rMark.GetMarkedRanges().empty() && GetViewData().GetDocument().IsSelectionEditable(rMark))
+ bDisableInsertImage = false;
+ else
+ {
+ if (GetViewData().GetDocument().IsBlockEditable
+ (GetViewData().GetTabNo(), GetViewData().GetCurX(), GetViewData().GetCurY(), GetViewData().GetCurX(), GetViewData().GetCurY()))
+ {
+ bDisableInsertImage = false;
+ }
+ }
+
+ if (bDisableInsertImage)
+ rSet.DisableItem(nWhich);
+ }
+ break;
+
+ case SID_LINKS:
+ {
+ if (GetViewData().GetDocument().GetLinkManager()->GetLinks().empty())
+ rSet.DisableItem( SID_LINKS );
+ }
+ break;
+ }
+ nWhich = aIter.NextWhich();
+ }
+}
+
+bool ScTabViewShell::IsSignatureLineSelected()
+{
+ SdrView* pSdrView = GetScDrawView();
+ if (!pSdrView)
+ return false;
+
+ if (pSdrView->GetMarkedObjectCount() != 1)
+ return false;
+
+ SdrObject* pPickObj = pSdrView->GetMarkedObjectByIndex(0);
+ if (!pPickObj)
+ return false;
+
+ SdrGrafObj* pGraphic = dynamic_cast<SdrGrafObj*>(pPickObj);
+ if (!pGraphic)
+ return false;
+
+ return pGraphic->isSignatureLine();
+}
+
+bool ScTabViewShell::IsQRCodeSelected()
+{
+ SdrView* pSdrView = GetScDrawView();
+ if (!pSdrView)
+ return false;
+
+ if (pSdrView->GetMarkedObjectCount() != 1)
+ return false;
+
+ SdrObject* pPickObj = pSdrView->GetMarkedObjectByIndex(0);
+ if (!pPickObj)
+ return false;
+
+ SdrGrafObj* pGraphic = dynamic_cast<SdrGrafObj*>(pPickObj);
+ if (!pGraphic)
+ return false;
+
+ if(pGraphic->getQrCode())
+ {
+ return true;
+ }
+ else{
+ return false;
+ }
+}
+
+bool ScTabViewShell::IsSignatureLineSigned()
+{
+ SdrView* pSdrView = GetScDrawView();
+ if (!pSdrView)
+ return false;
+
+ if (pSdrView->GetMarkedObjectCount() != 1)
+ return false;
+
+ SdrObject* pPickObj = pSdrView->GetMarkedObjectByIndex(0);
+ if (!pPickObj)
+ return false;
+
+ SdrGrafObj* pGraphic = dynamic_cast<SdrGrafObj*>(pPickObj);
+ if (!pGraphic)
+ return false;
+
+ return pGraphic->isSignatureLineSigned();
+}
+
+void ScTabViewShell::ExecuteUndo(SfxRequest& rReq)
+{
+ SfxShell* pSh = GetViewData().GetDispatcher().GetShell(0);
+ if (!pSh)
+ return;
+
+ ScUndoManager* pUndoManager = static_cast<ScUndoManager*>(pSh->GetUndoManager());
+
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+
+ sal_uInt16 nSlot = rReq.GetSlot();
+ switch ( nSlot )
+ {
+ case SID_UNDO:
+ case SID_REDO:
+ if ( pUndoManager )
+ {
+ bool bIsUndo = ( nSlot == SID_UNDO );
+
+ sal_uInt16 nCount = 1;
+ const SfxPoolItem* pItem;
+ if ( pReqArgs && pReqArgs->GetItemState( nSlot, true, &pItem ) == SfxItemState::SET )
+ nCount = static_cast<const SfxUInt16Item*>(pItem)->GetValue();
+
+ // Repair mode: allow undo/redo of all undo actions, even if access would
+ // be limited based on the view shell ID.
+ bool bRepair = false;
+ if (pReqArgs && pReqArgs->GetItemState(SID_REPAIRPACKAGE, false, &pItem) == SfxItemState::SET)
+ bRepair = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+
+ sal_uInt16 nUndoOffset = 0;
+ if (comphelper::LibreOfficeKit::isActive() && !bRepair)
+ {
+ SfxUndoAction* pAction = nullptr;
+ if (bIsUndo)
+ {
+ if (pUndoManager->GetUndoActionCount() != 0)
+ pAction = pUndoManager->GetUndoAction();
+ }
+ else
+ {
+ if (pUndoManager->GetRedoActionCount() != 0)
+ pAction = pUndoManager->GetRedoAction();
+ }
+ if (pAction)
+ {
+ // If another view created the undo action, prevent undoing it from this view.
+ // Unless we know that the other view's undo action is independent from us.
+ ViewShellId nViewShellId = GetViewShellId();
+ if (pAction->GetViewShellId() != nViewShellId)
+ {
+ sal_uInt16 nOffset = 0;
+ if (pUndoManager->IsViewUndoActionIndependent(this, nOffset))
+ {
+ // Execute the undo with an offset: don't undo the top action, but an
+ // earlier one, since it's independent and that belongs to our view.
+ nUndoOffset += nOffset;
+ }
+ else
+ {
+ rReq.SetReturnValue(SfxUInt32Item(SID_UNDO, static_cast<sal_uInt32>(SID_REPAIRPACKAGE)));
+ return;
+ }
+ }
+ }
+ }
+
+ // lock paint for more than one cell undo action (not for editing within a cell)
+ bool bLockPaint = ( nCount > 1 && pUndoManager == GetUndoManager() );
+ if ( bLockPaint )
+ pDocSh->LockPaint();
+
+ try
+ {
+ ScUndoRedoContext aUndoRedoContext;
+ aUndoRedoContext.SetUndoOffset(nUndoOffset);
+
+ for (sal_uInt16 i=0; i<nCount; i++)
+ {
+ if ( bIsUndo )
+ pUndoManager->UndoWithContext(aUndoRedoContext);
+ else
+ pUndoManager->RedoWithContext(aUndoRedoContext);
+ }
+ }
+ catch ( const uno::Exception& )
+ {
+ // no need to handle. By definition, the UndoManager handled this by clearing the
+ // Undo/Redo stacks
+ }
+
+ if ( bLockPaint )
+ pDocSh->UnlockPaint();
+
+ GetViewFrame().GetBindings().InvalidateAll(false);
+ }
+ break;
+// default:
+// GetViewFrame().ExecuteSlot( rReq );
+ }
+}
+
+void ScTabViewShell::GetUndoState(SfxItemSet &rSet)
+{
+ SfxShell* pSh = GetViewData().GetDispatcher().GetShell(0);
+ if (!pSh)
+ return;
+
+ SfxUndoManager* pUndoManager = pSh->GetUndoManager();
+ ScUndoManager* pScUndoManager = dynamic_cast<ScUndoManager*>(pUndoManager);
+
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while ( nWhich )
+ {
+ switch (nWhich)
+ {
+ case SID_GETUNDOSTRINGS:
+ case SID_GETREDOSTRINGS:
+ {
+ SfxStringListItem aStrLst( nWhich );
+ if ( pUndoManager )
+ {
+ std::vector<OUString> &aList = aStrLst.GetList();
+ bool bIsUndo = ( nWhich == SID_GETUNDOSTRINGS );
+ size_t nCount = bIsUndo ? pUndoManager->GetUndoActionCount() : pUndoManager->GetRedoActionCount();
+ for (size_t i=0; i<nCount; ++i)
+ {
+ aList.push_back( bIsUndo ? pUndoManager->GetUndoActionComment(i) :
+ pUndoManager->GetRedoActionComment(i) );
+ }
+ }
+ rSet.Put( aStrLst );
+ }
+ break;
+
+ case SID_UNDO:
+ {
+ if (pScUndoManager)
+ {
+ if (pScUndoManager->GetUndoActionCount())
+ {
+ const SfxUndoAction* pAction = pScUndoManager->GetUndoAction();
+ SfxViewShell *pViewSh = GetViewShell();
+ if (pViewSh && pAction->GetViewShellId() != pViewSh->GetViewShellId()
+ && !pScUndoManager->IsViewUndoActionIndependent(this, o3tl::temporary(sal_uInt16())))
+ {
+ rSet.Put(SfxUInt32Item(SID_UNDO, static_cast<sal_uInt32>(SID_REPAIRPACKAGE)));
+ }
+ else
+ {
+ rSet.Put( SfxStringItem( SID_UNDO, SvtResId(STR_UNDO)+pScUndoManager->GetUndoActionComment() ) );
+ }
+ }
+ else
+ rSet.DisableItem( SID_UNDO );
+ }
+ else
+ // get state from sfx view frame
+ GetViewFrame().GetSlotState( nWhich, nullptr, &rSet );
+ break;
+ }
+ case SID_REDO:
+ {
+ if (pScUndoManager)
+ {
+ if (pScUndoManager->GetRedoActionCount())
+ {
+ const SfxUndoAction* pAction = pScUndoManager->GetRedoAction();
+ SfxViewShell *pViewSh = GetViewShell();
+ if (pViewSh && pAction->GetViewShellId() != pViewSh->GetViewShellId())
+ {
+ rSet.Put(SfxUInt32Item(SID_REDO, static_cast<sal_uInt32>(SID_REPAIRPACKAGE)));
+ }
+ else
+ {
+ rSet.Put(SfxStringItem(SID_REDO, SvtResId(STR_REDO) + pScUndoManager->GetRedoActionComment()));
+ }
+ }
+ else
+ rSet.DisableItem( SID_REDO );
+ }
+ else
+ // get state from sfx view frame
+ GetViewFrame().GetSlotState( nWhich, nullptr, &rSet );
+ break;
+ }
+ default:
+ // get state from sfx view frame
+ GetViewFrame().GetSlotState( nWhich, nullptr, &rSet );
+ }
+
+ nWhich = aIter.NextWhich();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabvwshc.cxx b/sc/source/ui/view/tabvwshc.cxx
new file mode 100644
index 0000000000..8e2e74e8e4
--- /dev/null
+++ b/sc/source/ui/view/tabvwshc.cxx
@@ -0,0 +1,775 @@
+/* -*- 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 .
+ */
+
+#include <scitems.hxx>
+#include <sfx2/childwin.hxx>
+#include <sfx2/dispatch.hxx>
+#include <editeng/editview.hxx>
+#include <inputhdl.hxx>
+
+#include <tabvwsh.hxx>
+#include <sc.hrc>
+#include <scres.hrc>
+#include <global.hxx>
+#include <scmod.hxx>
+#include <document.hxx>
+#include <uiitems.hxx>
+#include <namedlg.hxx>
+#include <namedefdlg.hxx>
+#include <solvrdlg.hxx>
+#include <optsolver.hxx>
+#include <tabopdlg.hxx>
+#include <consdlg.hxx>
+#include <filtdlg.hxx>
+#include <dbnamdlg.hxx>
+#include <areasdlg.hxx>
+#include <crnrdlg.hxx>
+#include <formula.hxx>
+#include <highred.hxx>
+#include <simpref.hxx>
+#include <funcdesc.hxx>
+#include <dpobject.hxx>
+#include <markdata.hxx>
+#include <reffact.hxx>
+#include <condformatdlg.hxx>
+#include <condformateasydlg.hxx>
+#include <xmlsourcedlg.hxx>
+#include <condformatdlgitem.hxx>
+#include <formdata.hxx>
+#include <inputwin.hxx>
+
+#include <RandomNumberGeneratorDialog.hxx>
+#include <SamplingDialog.hxx>
+#include <DescriptiveStatisticsDialog.hxx>
+#include <AnalysisOfVarianceDialog.hxx>
+#include <CorrelationDialog.hxx>
+#include <CovarianceDialog.hxx>
+#include <ExponentialSmoothingDialog.hxx>
+#include <MovingAverageDialog.hxx>
+#include <RegressionDialog.hxx>
+#include <TTestDialog.hxx>
+#include <FTestDialog.hxx>
+#include <ZTestDialog.hxx>
+#include <ChiSquareTestDialog.hxx>
+#include <FourierAnalysisDialog.hxx>
+
+#include <PivotLayoutDialog.hxx>
+#include <SparklineDialog.hxx>
+#include <SparklineDataRangeDialog.hxx>
+
+#include <svtools/colorcfg.hxx>
+#include <comphelper/lok.hxx>
+#include <o3tl/unreachable.hxx>
+#include <o3tl/make_shared.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+
+void ScTabViewShell::SetCurRefDlgId( sal_uInt16 nNew )
+{
+ // CurRefDlgId is stored in ScModule to find if a ref dialog is open,
+ // and in the view to identify the view that has opened the dialog
+ nCurRefDlgId = nNew;
+}
+
+//ugly hack to call Define Name from Manage Names
+void ScTabViewShell::SwitchBetweenRefDialogs(SfxModelessDialogController* pDialog)
+{
+ sal_uInt16 nSlotId = SC_MOD()->GetCurRefDlgId();
+ if( nSlotId == FID_ADD_NAME )
+ {
+ static_cast<ScNameDefDlg*>(pDialog)->GetNewData(maName, maScope);
+ static_cast<ScNameDefDlg*>(pDialog)->Close();
+ sal_uInt16 nId = ScNameDlgWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ SC_MOD()->SetRefDialog( nId, pWnd == nullptr );
+ }
+ else if (nSlotId == FID_DEFINE_NAME)
+ {
+ mbInSwitch = true;
+ static_cast<ScNameDlg*>(pDialog)->GetRangeNames(m_RangeMap);
+ static_cast<ScNameDlg*>(pDialog)->Close();
+ sal_uInt16 nId = ScNameDefDlgWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ SC_MOD()->SetRefDialog( nId, pWnd == nullptr );
+ }
+}
+
+std::shared_ptr<SfxModelessDialogController> ScTabViewShell::CreateRefDialogController(
+ SfxBindings* pB, SfxChildWindow* pCW,
+ const SfxChildWinInfo* pInfo,
+ weld::Window* pParent, sal_uInt16 nSlotId)
+{
+ // only open dialog when called through ScModule::SetRefDialog,
+ // so that it does not re appear for instance after a crash (#42341#).
+
+ if ( SC_MOD()->GetCurRefDlgId() != nSlotId )
+ return nullptr;
+
+ if ( nCurRefDlgId != nSlotId )
+ {
+ if (!(comphelper::LibreOfficeKit::isActive() && nSlotId == SID_OPENDLG_FUNCTION))
+ {
+ // the dialog has been opened in a different view
+ // -> lock the dispatcher for this view (modal mode)
+
+ GetViewData().GetDispatcher().Lock( true ); // lock is reset when closing dialog
+ }
+ return nullptr;
+ }
+
+ std::shared_ptr<SfxModelessDialogController> xResult;
+
+ if(pCW)
+ pCW->SetHideNotDelete(true);
+
+ ScDocument& rDoc = GetViewData().GetDocument();
+
+ switch( nSlotId )
+ {
+ case SID_CORRELATION_DIALOG:
+ xResult = std::make_shared<ScCorrelationDialog>(pB, pCW, pParent, GetViewData());
+ break;
+ case SID_SAMPLING_DIALOG:
+ xResult = std::make_shared<ScSamplingDialog>(pB, pCW, pParent, GetViewData());
+ break;
+ case SID_DESCRIPTIVE_STATISTICS_DIALOG:
+ xResult = std::make_shared<ScDescriptiveStatisticsDialog>(pB, pCW, pParent, GetViewData());
+ break;
+ case SID_ANALYSIS_OF_VARIANCE_DIALOG:
+ xResult = std::make_shared<ScAnalysisOfVarianceDialog>(pB, pCW, pParent, GetViewData());
+ break;
+ case SID_COVARIANCE_DIALOG:
+ xResult = std::make_shared<ScCovarianceDialog>(pB, pCW, pParent, GetViewData());
+ break;
+ case SID_EXPONENTIAL_SMOOTHING_DIALOG:
+ xResult = std::make_shared<ScExponentialSmoothingDialog>(pB, pCW, pParent, GetViewData());
+ break;
+ case SID_MOVING_AVERAGE_DIALOG:
+ xResult = std::make_shared<ScMovingAverageDialog>(pB, pCW, pParent, GetViewData());
+ break;
+ case SID_REGRESSION_DIALOG:
+ xResult = std::make_shared<ScRegressionDialog>(pB, pCW, pParent, GetViewData());
+ break;
+ case SID_FTEST_DIALOG:
+ xResult = std::make_shared<ScFTestDialog>(pB, pCW, pParent, GetViewData());
+ break;
+ case SID_TTEST_DIALOG:
+ xResult = std::make_shared<ScTTestDialog>(pB, pCW, pParent, GetViewData());
+ break;
+ case SID_ZTEST_DIALOG:
+ xResult = std::make_shared<ScZTestDialog>(pB, pCW, pParent, GetViewData());
+ break;
+ case SID_CHI_SQUARE_TEST_DIALOG:
+ xResult = std::make_shared<ScChiSquareTestDialog>(pB, pCW, pParent, GetViewData());
+ break;
+ case SID_FOURIER_ANALYSIS_DIALOG:
+ xResult = std::make_shared<ScFourierAnalysisDialog>(pB, pCW, pParent, GetViewData());
+ break;
+ case WID_SIMPLE_REF:
+ {
+ // dialog checks, what is in the cell
+
+ ScViewData& rViewData = GetViewData();
+ rViewData.SetRefTabNo( rViewData.GetTabNo() );
+ xResult = std::make_shared<ScSimpleRefDlg>(pB, pCW, pParent);
+ break;
+ }
+ case FID_DEFINE_NAME:
+ {
+ if (!mbInSwitch)
+ {
+ xResult = std::make_shared<ScNameDlg>(pB, pCW, pParent, GetViewData(),
+ ScAddress( GetViewData().GetCurX(),
+ GetViewData().GetCurY(),
+ GetViewData().GetTabNo() ) );
+ }
+ else
+ {
+ xResult = std::make_shared<ScNameDlg>( pB, pCW, pParent, GetViewData(),
+ ScAddress( GetViewData().GetCurX(),
+ GetViewData().GetCurY(),
+ GetViewData().GetTabNo() ), &m_RangeMap);
+ static_cast<ScNameDlg*>(xResult.get())->SetEntry(maName, maScope);
+ mbInSwitch = false;
+ }
+ break;
+ }
+ case FID_ADD_NAME:
+ {
+ if (!mbInSwitch)
+ {
+ std::map<OUString, ScRangeName*> aRangeMap;
+ rDoc.GetRangeNameMap(aRangeMap);
+ xResult = std::make_shared<ScNameDefDlg>(pB, pCW, pParent, GetViewData(), std::move(aRangeMap),
+ ScAddress(GetViewData().GetCurX(),
+ GetViewData().GetCurY(),
+ GetViewData().GetTabNo()), true);
+ }
+ else
+ {
+ std::map<OUString, ScRangeName*> aRangeMap;
+ for (auto& itr : m_RangeMap)
+ {
+ aRangeMap.insert(std::pair<OUString, ScRangeName*>(itr.first, &itr.second));
+ }
+ xResult = std::make_shared<ScNameDefDlg>(pB, pCW, pParent, GetViewData(), std::move(aRangeMap),
+ ScAddress(GetViewData().GetCurX(),
+ GetViewData().GetCurY(),
+ GetViewData().GetTabNo()), false);
+ }
+ break;
+ }
+ case SID_RANDOM_NUMBER_GENERATOR_DIALOG:
+ xResult = std::make_shared<ScRandomNumberGeneratorDialog>(pB, pCW, pParent, GetViewData());
+ break;
+ case SID_SPARKLINE_DIALOG:
+ {
+ xResult = std::make_shared<sc::SparklineDialog>(pB, pCW, pParent, GetViewData());
+ break;
+ }
+ case SID_SPARKLINE_DATA_RANGE_DIALOG:
+ {
+ xResult = std::make_shared<sc::SparklineDataRangeDialog>(pB, pCW, pParent, GetViewData());
+ break;
+ }
+ case SID_DEFINE_DBNAME:
+ {
+ // when called for an existing range, then mark
+ GetDBData( true, SC_DB_OLD );
+ const ScMarkData& rMark = GetViewData().GetMarkData();
+ if ( !rMark.IsMarked() && !rMark.IsMultiMarked() )
+ MarkDataArea( false );
+
+ xResult = std::make_shared<ScDbNameDlg>(pB, pCW, pParent, GetViewData());
+ break;
+ }
+ case SID_OPENDLG_EDIT_PRINTAREA:
+ xResult = std::make_shared<ScPrintAreasDlg>(pB, pCW, pParent);
+ break;
+ case SID_DEFINE_COLROWNAMERANGES:
+ xResult = std::make_shared<ScColRowNameRangesDlg>(pB, pCW, pParent, GetViewData());
+ break;
+ case SID_OPENDLG_SOLVE:
+ {
+ ScViewData& rViewData = GetViewData();
+ ScAddress aCurPos( rViewData.GetCurX(),
+ rViewData.GetCurY(),
+ rViewData.GetTabNo());
+ xResult = std::make_shared<ScSolverDlg>(pB, pCW, pParent, &rViewData.GetDocument(), aCurPos);
+ break;
+ }
+ case SID_OPENDLG_TABOP:
+ {
+ ScViewData& rViewData = GetViewData();
+ ScRefAddress aCurPos ( rViewData.GetCurX(),
+ rViewData.GetCurY(),
+ rViewData.GetTabNo());
+
+ xResult = std::make_shared<ScTabOpDlg>(pB, pCW, pParent, &rViewData.GetDocument(), aCurPos);
+ break;
+ }
+ case SID_OPENDLG_CONSOLIDATE:
+ {
+ SfxItemSetFixed<SCITEM_CONSOLIDATEDATA,
+ SCITEM_CONSOLIDATEDATA> aArgSet( GetPool() );
+
+ const ScConsolidateParam* pDlgData =
+ rDoc.GetConsolidateDlgData();
+
+ if ( !pDlgData )
+ {
+ ScConsolidateParam aConsParam;
+ SCCOL nStartCol, nEndCol;
+ SCROW nStartRow, nEndRow;
+ SCTAB nStartTab, nEndTab;
+
+ GetViewData().GetSimpleArea( nStartCol, nStartRow, nStartTab,
+ nEndCol, nEndRow, nEndTab );
+
+ PutInOrder( nStartCol, nEndCol );
+ PutInOrder( nStartRow, nEndRow );
+ PutInOrder( nStartTab, nEndTab );
+
+ aConsParam.nCol = nStartCol;
+ aConsParam.nRow = nStartRow;
+ aConsParam.nTab = nStartTab;
+
+ aArgSet.Put( ScConsolidateItem( SCITEM_CONSOLIDATEDATA,
+ &aConsParam ) );
+ }
+ else
+ {
+ aArgSet.Put( ScConsolidateItem( SCITEM_CONSOLIDATEDATA, pDlgData ) );
+ }
+ xResult = std::make_shared<ScConsolidateDlg>(pB, pCW, pParent, aArgSet);
+ break;
+ }
+ case SID_EASY_CONDITIONAL_FORMAT_DIALOG:
+ {
+ xResult = std::make_shared<sc::ConditionalFormatEasyDialog>(pB, pCW, pParent, &GetViewData());
+ break;
+ }
+ case SID_FILTER:
+ {
+
+ ScQueryParam aQueryParam;
+ SfxItemSetFixed<SCITEM_QUERYDATA, SCITEM_QUERYDATA> aArgSet( GetPool() );
+
+ ScDBData* pDBData = GetDBData(false, SC_DB_MAKE, ScGetDBSelection::RowDown);
+ pDBData->ExtendDataArea(rDoc);
+ pDBData->ExtendBackColorArea(rDoc);
+ pDBData->GetQueryParam( aQueryParam );
+
+ ScRange aArea;
+ pDBData->GetArea(aArea);
+ MarkRange(aArea, false);
+
+ aArgSet.Put( ScQueryItem( SCITEM_QUERYDATA,
+ &GetViewData(),
+ &aQueryParam ) );
+
+ // mark current sheet (due to RefInput in dialog)
+ GetViewData().SetRefTabNo( GetViewData().GetTabNo() );
+
+ xResult = std::make_shared<ScFilterDlg>(pB, pCW, pParent, aArgSet);
+ break;
+ }
+ case SID_SPECIAL_FILTER:
+ {
+ ScQueryParam aQueryParam;
+ SfxItemSetFixed<SCITEM_QUERYDATA,
+ SCITEM_QUERYDATA> aArgSet( GetPool() );
+
+ ScDBData* pDBData = GetDBData(false, SC_DB_MAKE, ScGetDBSelection::RowDown);
+ pDBData->ExtendDataArea(rDoc);
+ pDBData->GetQueryParam( aQueryParam );
+
+ ScRange aArea;
+ pDBData->GetArea(aArea);
+ MarkRange(aArea, false);
+
+ ScQueryItem aItem( SCITEM_QUERYDATA, &GetViewData(), &aQueryParam );
+ ScRange aAdvSource;
+ if (pDBData->GetAdvancedQuerySource(aAdvSource))
+ aItem.SetAdvancedQuerySource( &aAdvSource );
+
+ aArgSet.Put( aItem );
+
+ // mark current sheet (due to RefInput in dialog)
+ GetViewData().SetRefTabNo( GetViewData().GetTabNo() );
+
+ xResult = std::make_shared<ScSpecialFilterDlg>(pB, pCW, pParent, aArgSet);
+ break;
+ }
+ case SID_OPENDLG_OPTSOLVER:
+ {
+ ScViewData& rViewData = GetViewData();
+ ScAddress aCurPos( rViewData.GetCurX(), rViewData.GetCurY(), rViewData.GetTabNo());
+ xResult = std::make_shared<ScOptSolverDlg>(pB, pCW, pParent, rViewData.GetDocShell(), aCurPos);
+ break;
+ }
+ case FID_CHG_SHOW:
+ {
+ // dialog checks, what is in the cell
+ xResult = std::make_shared<ScHighlightChgDlg>(pB, pCW, pParent, GetViewData());
+ break;
+ }
+ case SID_MANAGE_XML_SOURCE:
+ {
+ xResult = std::make_shared<ScXMLSourceDlg>(pB, pCW, pParent, &rDoc);
+ break;
+ }
+ case SID_OPENDLG_PIVOTTABLE:
+ {
+ // all settings must be in pDialogDPObject
+
+ if( pDialogDPObject )
+ {
+ // Check for an existing datapilot output.
+ ScViewData& rViewData = GetViewData();
+ rViewData.SetRefTabNo( rViewData.GetTabNo() );
+ ScDPObject* pObj = rDoc.GetDPAtCursor(rViewData.GetCurX(), rViewData.GetCurY(), rViewData.GetTabNo());
+ xResult = std::make_shared<ScPivotLayoutDialog>(pB, pCW, pParent, &rViewData, pDialogDPObject.get(), pObj == nullptr);
+ }
+
+ break;
+ }
+ case SID_OPENDLG_FUNCTION:
+ {
+ if (!isLOKMobilePhone())
+ {
+ // dialog checks, what is in the cell
+ xResult = o3tl::make_shared<ScFormulaDlg>(pB, pCW, pParent, GetViewData(), ScGlobal::GetStarCalcFunctionMgr());
+ }
+ break;
+ }
+ case WID_CONDFRMT_REF:
+ {
+ const ScCondFormatDlgItem* pDlgItem = nullptr;
+ // Get the pool item stored by Conditional Format Manager Dialog.
+ auto itemsRange = GetPool().GetItemSurrogates(SCITEM_CONDFORMATDLGDATA);
+ if (itemsRange.begin() != itemsRange.end())
+ {
+ const SfxPoolItem* pItem = *itemsRange.begin();
+ pDlgItem = static_cast<const ScCondFormatDlgItem*>(pItem);
+ }
+
+ if (pDlgItem)
+ {
+ ScViewData& rViewData = GetViewData();
+ rViewData.SetRefTabNo( rViewData.GetTabNo() );
+
+ xResult = std::make_shared<ScCondFormatDlg>(pB, pCW, pParent, &rViewData, pDlgItem);
+
+ // Remove the pool item stored by Conditional Format Manager Dialog.
+ GetPool().DirectRemoveItemFromPool(*pDlgItem);
+ }
+
+ break;
+ }
+ }
+
+ if (xResult)
+ xResult->Initialize( pInfo );
+ return xResult;
+}
+
+int ScTabViewShell::getPart() const
+{
+ return GetViewData().GetTabNo();
+}
+
+void ScTabViewShell::afterCallbackRegistered()
+{
+ // common tasks
+ SfxViewShell::afterCallbackRegistered();
+
+ UpdateInputHandler(true, false);
+
+ ScInputHandler* pHdl = mpInputHandler ? mpInputHandler.get() : SC_MOD()->GetInputHdl();
+ if (pHdl)
+ {
+ ScInputWindow* pInputWindow = pHdl->GetInputWindow();
+ if (pInputWindow)
+ {
+ pInputWindow->NotifyLOKClient();
+ }
+ }
+}
+
+void ScTabViewShell::NotifyCursor(SfxViewShell* pOtherShell) const
+{
+ ScDrawView* pDrView = const_cast<ScTabViewShell*>(this)->GetScDrawView();
+ if (pDrView)
+ {
+ if (pDrView->GetTextEditObject())
+ {
+ // Blinking cursor.
+ EditView& rEditView = pDrView->GetTextEditOutlinerView()->GetEditView();
+ rEditView.RegisterOtherShell(pOtherShell);
+ rEditView.ShowCursor();
+ rEditView.RegisterOtherShell(nullptr);
+ // Text selection, if any.
+ rEditView.DrawSelectionXOR(pOtherShell);
+ }
+ else
+ {
+ // Graphic selection.
+ pDrView->AdjustMarkHdl(pOtherShell);
+ }
+ }
+
+ const ScGridWindow* pWin = GetViewData().GetActiveWin();
+ if (pWin)
+ pWin->updateKitCellCursor(pOtherShell);
+}
+
+::Color ScTabViewShell::GetColorConfigColor(svtools::ColorConfigEntry nColorType) const
+{
+ const ScViewOptions& rViewOptions = GetViewData().GetOptions();
+
+ switch (nColorType)
+ {
+ case svtools::ColorConfigEntry::DOCCOLOR:
+ {
+ return rViewOptions.GetDocColor();
+ }
+ // Should never be called for an unimplemented color type
+ default:
+ {
+ O3TL_UNREACHABLE;
+ }
+ }
+}
+
+css::uno::Reference<css::datatransfer::XTransferable2> ScTabViewShell::GetClipData(vcl::Window* pWin)
+{
+ SfxViewFrame* pViewFrame = nullptr;
+ css::uno::Reference<css::datatransfer::XTransferable2> xTransferable;
+ css::uno::Reference<css::datatransfer::clipboard::XClipboard> xClipboard;
+
+ if (pWin)
+ xClipboard = pWin->GetClipboard();
+ else if ((pViewFrame = SfxViewFrame::GetFirst(nullptr, false)))
+ xClipboard = pViewFrame->GetWindow().GetClipboard();
+
+ xTransferable.set(xClipboard.is() ? xClipboard->getContents() : nullptr, css::uno::UNO_QUERY);
+
+ return xTransferable;
+}
+
+void ScTabViewShell::notifyAllViewsHeaderInvalidation(const SfxViewShell* pForViewShell, HeaderType eHeaderType, SCTAB nCurrentTabIndex)
+{
+ if (!comphelper::LibreOfficeKit::isActive())
+ return;
+
+ OString aPayload;
+ switch (eHeaderType)
+ {
+ case COLUMN_HEADER:
+ aPayload = "column"_ostr;
+ break;
+ case ROW_HEADER:
+ aPayload = "row"_ostr;
+ break;
+ case BOTH_HEADERS:
+ default:
+ aPayload = "all"_ostr;
+ break;
+ }
+
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
+ if (pTabViewShell && pViewShell->GetDocId() == pForViewShell->GetDocId()
+ && (nCurrentTabIndex == -1 || pTabViewShell->getPart() == nCurrentTabIndex))
+ {
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_INVALIDATE_HEADER, aPayload);
+ }
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+}
+
+bool ScTabViewShell::isAnyEditViewInRange(const SfxViewShell* pForViewShell, bool bColumns, SCCOLROW nStart, SCCOLROW nEnd)
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
+ if (pTabViewShell && pTabViewShell->GetDocId() == pForViewShell->GetDocId())
+ {
+ ScInputHandler* pInputHandler = pTabViewShell->GetInputHandler();
+ if (pInputHandler && pInputHandler->GetActiveView())
+ {
+ const ScViewData& rViewData = pTabViewShell->GetViewData();
+ SCCOLROW nPos = bColumns ? rViewData.GetCurX() : rViewData.GetCurY();
+ if (nStart <= nPos && nPos <= nEnd)
+ return true;
+ }
+ }
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+ }
+ return false;
+}
+
+void ScTabViewShell::notifyAllViewsSheetGeomInvalidation(const SfxViewShell* pForViewShell, bool bColumns,
+ bool bRows, bool bSizes, bool bHidden, bool bFiltered,
+ bool bGroups, SCTAB nCurrentTabIndex)
+{
+ if (!comphelper::LibreOfficeKit::isActive() ||
+ !comphelper::LibreOfficeKit::isCompatFlagSet(
+ comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
+ return;
+
+ if (!bColumns && !bRows)
+ return;
+
+ bool bAllTypes = bSizes && bHidden && bFiltered && bGroups;
+ bool bAllDims = bColumns && bRows;
+ OString aPayload = bAllDims ? "all" : bColumns ? "columns" : "rows";
+
+ if (!bAllTypes)
+ {
+ if (bSizes)
+ aPayload += " sizes";
+
+ if (bHidden)
+ aPayload += " hidden";
+
+ if (bFiltered)
+ aPayload += " filtered";
+
+ if (bGroups)
+ aPayload += " groups";
+ }
+
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
+ if (pTabViewShell && pViewShell->GetDocId() == pForViewShell->GetDocId() &&
+ (nCurrentTabIndex == -1 || pTabViewShell->getPart() == nCurrentTabIndex))
+ {
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_INVALIDATE_SHEET_GEOMETRY, aPayload);
+ }
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+}
+
+bool ScTabViewShell::UseSubTotal(ScRangeList* pRangeList)
+{
+ bool bSubTotal = false;
+ ScDocument& rDoc = GetViewData().GetDocument();
+ size_t nRangeCount (pRangeList->size());
+ size_t nRangeIndex (0);
+ while (!bSubTotal && nRangeIndex < nRangeCount)
+ {
+ const ScRange& rRange = (*pRangeList)[nRangeIndex];
+ SCTAB nTabEnd(rRange.aEnd.Tab());
+ SCTAB nTab(rRange.aStart.Tab());
+ while (!bSubTotal && nTab <= nTabEnd)
+ {
+ SCROW nRowEnd(rRange.aEnd.Row());
+ SCROW nRow(rRange.aStart.Row());
+ while (!bSubTotal && nRow <= nRowEnd)
+ {
+ if (rDoc.RowFiltered(nRow, nTab))
+ bSubTotal = true;
+ else
+ ++nRow;
+ }
+ ++nTab;
+ }
+ ++nRangeIndex;
+ }
+
+ if (!bSubTotal)
+ {
+ const ScDBCollection::NamedDBs& rDBs = rDoc.GetDBCollection()->getNamedDBs();
+ for (const auto& rxDB : rDBs)
+ {
+ const ScDBData& rDB = *rxDB;
+ if (!rDB.HasAutoFilter())
+ continue;
+
+ nRangeIndex = 0;
+ while (!bSubTotal && nRangeIndex < nRangeCount)
+ {
+ const ScRange & rRange = (*pRangeList)[nRangeIndex];
+ ScRange aDBArea;
+ rDB.GetArea(aDBArea);
+ if (aDBArea.Intersects(rRange))
+ bSubTotal = true;
+ ++nRangeIndex;
+ }
+
+ if (bSubTotal)
+ break;
+ }
+ }
+ return bSubTotal;
+}
+
+OUString ScTabViewShell::DoAutoSum(bool& rRangeFinder, bool& rSubTotal, const OpCode eCode)
+{
+ OUString aFormula;
+ const ScMarkData& rMark = GetViewData().GetMarkData();
+ if ( rMark.IsMarked() || rMark.IsMultiMarked() )
+ {
+ ScRangeList aMarkRangeList;
+ rRangeFinder = rSubTotal = false;
+ rMark.FillRangeListWithMarks( &aMarkRangeList, false );
+ ScDocument& rDoc = GetViewData().GetDocument();
+
+ // check if one of the marked ranges is empty
+ bool bEmpty = false;
+ const size_t nCount = aMarkRangeList.size();
+ for ( size_t i = 0; i < nCount; ++i )
+ {
+ const ScRange & rRange( aMarkRangeList[i] );
+ if ( rDoc.IsBlockEmpty( rRange.aStart.Col(), rRange.aStart.Row(),
+ rRange.aEnd.Col(), rRange.aEnd.Row(), rRange.aStart.Tab() ) )
+ {
+ bEmpty = true;
+ break;
+ }
+ }
+
+ if ( bEmpty )
+ {
+ ScRangeList aRangeList;
+ const bool bDataFound = GetAutoSumArea( aRangeList );
+ if ( bDataFound )
+ {
+ ScAddress aAddr = aRangeList.back().aEnd;
+ aAddr.IncRow();
+ const bool bSubTotal( UseSubTotal( &aRangeList ) );
+ EnterAutoSum( aRangeList, bSubTotal, aAddr, eCode );
+ }
+ }
+ else
+ {
+ const bool bSubTotal( UseSubTotal( &aMarkRangeList ) );
+ for ( size_t i = 0; i < nCount; ++i )
+ {
+ const ScRange & rRange = aMarkRangeList[i];
+ const bool bSetCursor = ( i == nCount - 1 );
+ const bool bContinue = ( i != 0 );
+ if ( !AutoSum( rRange, bSubTotal, bSetCursor, bContinue, eCode ) )
+ {
+ MarkRange( rRange, false );
+ SetCursor( rRange.aEnd.Col(), rRange.aEnd.Row() );
+ const ScRangeList aRangeList;
+ ScAddress aAddr = rRange.aEnd;
+ aAddr.IncRow();
+ aFormula = GetAutoSumFormula( aRangeList, bSubTotal, aAddr , eCode);
+ break;
+ }
+ }
+ }
+ }
+ else // Only insert into input row
+ {
+ ScRangeList aRangeList;
+ rRangeFinder = GetAutoSumArea( aRangeList );
+ rSubTotal = UseSubTotal( &aRangeList );
+ ScAddress aAddr = GetViewData().GetCurPos();
+ aFormula = GetAutoSumFormula( aRangeList, rSubTotal, aAddr , eCode);
+ }
+ return aFormula;
+}
+
+void ScTabViewShell::InitFormEditData()
+{
+ mpFormEditData.reset(new ScFormEditData);
+}
+
+void ScTabViewShell::ClearFormEditData()
+{
+ mpFormEditData.reset();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabvwshd.cxx b/sc/source/ui/view/tabvwshd.cxx
new file mode 100644
index 0000000000..52d97d1a25
--- /dev/null
+++ b/sc/source/ui/view/tabvwshd.cxx
@@ -0,0 +1,67 @@
+/* -*- 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 .
+ */
+
+#include <sfx2/childwin.hxx>
+#include <sfx2/viewfrm.hxx>
+
+#include <tabvwsh.hxx>
+#include <scmod.hxx>
+#include <docsh.hxx>
+#include <gridwin.hxx>
+
+//! parent window for dialogs
+//! Problem: OLE Server!
+
+weld::Window* ScTabViewShell::GetDialogParent()
+{
+ // if a ref-input dialog is open, use it as parent
+ // (necessary when a slot is executed from the dialog's OK handler)
+ if (nCurRefDlgId && nCurRefDlgId == SC_MOD()->GetCurRefDlgId())
+ {
+ SfxViewFrame& rViewFrm = GetViewFrame();
+ if (rViewFrm.HasChildWindow(nCurRefDlgId))
+ {
+ SfxChildWindow* pChild = rViewFrm.GetChildWindow(nCurRefDlgId);
+ if (pChild)
+ {
+ auto xController = pChild->GetController();
+ weld::Window* pRet = xController ? xController->getDialog() : nullptr;
+ if (pRet && pRet->get_visible())
+ return pRet;
+ }
+ }
+ }
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ if (pDocSh->IsOle())
+ {
+ // TODO/LATER: how to GetEditWindow in embedded document?!
+ // It should be OK to return the ViewShell Window!
+ vcl::Window* pWin = GetWindow();
+ return pWin ? pWin->GetFrameWeld() : nullptr;
+ // SvInPlaceEnvironment* pEnv = pDocSh->GetIPEnv();
+ // if (pEnv)
+ // return pEnv->GetEditWin();
+ }
+
+ vcl::Window* pWin = GetActiveWin(); // for normal views, too
+ return pWin ? pWin->GetFrameWeld() : nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabvwshe.cxx b/sc/source/ui/view/tabvwshe.cxx
new file mode 100644
index 0000000000..0c809b31b3
--- /dev/null
+++ b/sc/source/ui/view/tabvwshe.cxx
@@ -0,0 +1,322 @@
+/* -*- 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 .
+ */
+
+#include <comphelper/string.hxx>
+#include <comphelper/lok.hxx>
+#include <editeng/eeitem.hxx>
+#include <osl/diagnose.h>
+
+#include <editeng/editview.hxx>
+#include <editeng/flditem.hxx>
+#include <svx/hlnkitem.hxx>
+#include <svl/srchitem.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/request.hxx>
+#include <svl/stritem.hxx>
+
+#include <tabvwsh.hxx>
+#include <sc.hrc>
+#include <scmod.hxx>
+#include <impex.hxx>
+#include <editsh.hxx>
+#include <dociter.hxx>
+#include <inputhdl.hxx>
+#include <document.hxx>
+
+OUString ScTabViewShell::GetSelectionText( bool bWholeWord, bool bOnlyASample )
+{
+ OUString aStrSelection;
+
+ if ( pEditShell && pEditShell.get() == GetMySubShell() )
+ {
+ aStrSelection = pEditShell->GetSelectionText( bWholeWord );
+ }
+ else
+ {
+ ScRange aRange;
+
+ if ( GetViewData().GetSimpleArea( aRange ) == SC_MARK_SIMPLE )
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+ if ( (bOnlyASample || bInFormatDialog) && aRange.aStart.Row() != aRange.aEnd.Row() )
+ {
+ // limit range to one data row
+ // (only when the call comes from a format dialog)
+ ScHorizontalCellIterator aIter( rDoc, aRange.aStart.Tab(),
+ aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row() );
+ SCCOL nCol;
+ SCROW nRow;
+ if ( aIter.GetNext( nCol, nRow ) )
+ {
+ aRange.aStart.SetCol( nCol );
+ aRange.aStart.SetRow( nRow );
+ aRange.aEnd.SetRow( nRow );
+ }
+ else
+ aRange.aEnd = aRange.aStart;
+ }
+ else
+ {
+ // #i111531# with 1M rows it was necessary to limit the range
+ // to the actually used data area.
+ SCCOL nCol1, nCol2;
+ SCROW nRow1, nRow2;
+ SCTAB nTab1, nTab2;
+ aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+ bool bShrunk;
+ rDoc.ShrinkToUsedDataArea( bShrunk, nTab1, nCol1, nRow1, nCol2, nRow2, false);
+ if (bShrunk)
+ {
+ aRange.aStart.SetCol( nCol1 );
+ aRange.aStart.SetRow( nRow1 );
+ aRange.aEnd.SetCol( nCol2 );
+ aRange.aEnd.SetRow( nRow2 );
+ }
+ }
+
+ ScImportExport aObj( rDoc, aRange );
+ // tdf#148437 - if cell contains a formula, overwrite entire content of the cell
+ aObj.SetFormulas(true);
+ OUString aExportOUString;
+ /* TODO: STRING_TSVC under some circumstances? */
+ aObj.ExportString( aExportOUString, SotClipboardFormatId::STRING );
+ aStrSelection = convertLineEnd(aExportOUString, LINEEND_CR);
+
+ // replace Tab/CR with space, if for dialog or through Basic/SelectionTextExt,
+ // or when it is a single row.
+ // Otherwise keep Tabs in multi-row (for instance mail or Basic/SelectionText).
+ // for mail the Tabs are then later changed into (multiple) spaces.
+
+ if ( bInFormatDialog || bWholeWord || aRange.aEnd.Row() == aRange.aStart.Row() )
+ {
+ aStrSelection = aStrSelection.replaceAll("\r", " ");
+ aStrSelection = aStrSelection.replaceAll("\t", " ");
+ aStrSelection = comphelper::string::stripEnd(aStrSelection, ' ');
+ }
+ }
+ }
+
+ return aStrSelection;
+}
+
+void ScTabViewShell::InsertURL( const OUString& rName, const OUString& rURL, const OUString& rTarget,
+ sal_uInt16 nMode )
+{
+ SvxLinkInsertMode eMode = static_cast<SvxLinkInsertMode>(nMode);
+ bool bAsText = ( eMode != HLINK_BUTTON ); // default is now text
+
+ if ( bAsText )
+ {
+ if ( GetViewData().IsActive() )
+ {
+ // if the view is active, always use InsertURLField, which starts EditMode
+ // and selects the URL, so it can be changed from the URL bar / dialog
+
+ InsertURLField( rName, rURL, rTarget );
+ }
+ else
+ {
+ // if the view is not active, InsertURLField doesn't work
+ // -> use InsertBookmark to directly manipulate cell content
+ // bTryReplace=sal_True -> if cell contains only one URL, replace it
+
+ SCCOL nPosX = GetViewData().GetCurX();
+ SCROW nPosY = GetViewData().GetCurY();
+ InsertBookmark( rName, rURL, nPosX, nPosY, &rTarget, true );
+ }
+ }
+ else
+ {
+ SC_MOD()->InputEnterHandler();
+ InsertURLButton( rName, rURL, rTarget, nullptr );
+ }
+}
+
+static void lcl_SelectFieldAfterInsert( EditView& rView )
+{
+ ESelection aSel = rView.GetSelection();
+ if ( aSel.nStartPos == aSel.nEndPos && aSel.nStartPos > 0 )
+ {
+ // Cursor is behind the inserted field -> extend selection to the left
+
+ --aSel.nStartPos;
+ rView.SetSelection( aSel );
+ }
+}
+
+void ScTabViewShell::InsertURLField( const OUString& rName, const OUString& rURL, const OUString& rTarget )
+{
+ SvxURLField aURLField( rURL, rName, SvxURLFormat::Repr );
+ aURLField.SetTargetFrame( rTarget );
+ SvxFieldItem aURLItem( aURLField, EE_FEATURE_FIELD );
+
+ ScViewData& rViewData = GetViewData();
+ ScModule* pScMod = SC_MOD();
+ ScInputHandler* pHdl = pScMod->GetInputHdl( rViewData.GetViewShell() );
+
+ bool bSelectFirst = false;
+ bool bIsEditMode = pScMod->IsEditMode();
+ int nSelInd = 1;
+ OUString sSeltext(GetSelectionText());
+
+ if ( !bIsEditMode )
+ {
+ if ( !SelectionEditable() )
+ {
+ // no error message (may be called from drag&drop)
+ return;
+ }
+
+ // single url in cell is shown in the dialog and replaced
+ bSelectFirst = HasBookmarkAtCursor( nullptr );
+ pScMod->SetInputMode( SC_INPUT_TABLE );
+ }
+
+ EditView* pTopView = pHdl->GetTopView();
+ EditView* pTableView = pHdl->GetTableView();
+ OSL_ENSURE( pTopView || pTableView, "No EditView" );
+
+ // Check if user selected a whole cell by single click, and cell has content.
+ // tdf#80043 - if true, replace the entire content of the selected cell instead of
+ // inserting a duplicate, or appending the url.
+ if (!bIsEditMode && !bSelectFirst && pTableView && !sSeltext.isEmpty())
+ {
+ nSelInd = sSeltext.getLength();
+ bSelectFirst = true;
+ }
+
+ if ( bSelectFirst )
+ {
+ if ( pTopView )
+ pTopView->SetSelection( ESelection(0,0,0,1) );
+ if ( pTableView )
+ pTableView->SetSelection( ESelection(0,0,0,nSelInd) );
+ }
+
+ pHdl->DataChanging();
+
+ if ( pTopView )
+ {
+ pTopView->InsertField( aURLItem );
+ lcl_SelectFieldAfterInsert( *pTopView );
+ }
+ if ( pTableView )
+ {
+ pTableView->InsertField( aURLItem );
+ lcl_SelectFieldAfterInsert( *pTableView );
+ }
+
+ pHdl->DataChanged();
+}
+
+void ScTabViewShell::ExecSearch( SfxRequest& rReq )
+{
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+ sal_uInt16 nSlot = rReq.GetSlot();
+ const SfxPoolItem* pItem;
+
+ switch ( nSlot )
+ {
+ case FID_SEARCH_NOW:
+ {
+ const SvxSearchItem* pSearchItem;
+ if ( pReqArgs &&
+ (pSearchItem = pReqArgs->GetItemIfSet(SID_SEARCH_ITEM, false)) )
+ {
+ ScGlobal::SetSearchItem( *pSearchItem );
+ SearchAndReplace( pSearchItem, true, rReq.IsAPI() );
+ rReq.Done();
+ }
+ }
+ break;
+
+ case SID_SEARCH_ITEM:
+ {
+ const SvxSearchItem* pSearchItem;
+ if (pReqArgs && (pSearchItem =
+ pReqArgs->GetItemIfSet(SID_SEARCH_ITEM, false)))
+ {
+ // remember search item
+ ScGlobal::SetSearchItem( *pSearchItem );
+ }
+ else
+ {
+ OSL_FAIL("SID_SEARCH_ITEM without Parameter");
+ }
+ break;
+ }
+ case FID_SEARCH:
+ case FID_REPLACE:
+ case FID_REPLACE_ALL:
+ case FID_SEARCH_ALL:
+ {
+ if (pReqArgs && SfxItemState::SET == pReqArgs->GetItemState(nSlot, false, &pItem))
+ {
+ // get search item
+
+ SvxSearchItem aSearchItem = ScGlobal::GetSearchItem();
+
+ // fill search item
+
+ aSearchItem.SetSearchString(static_cast<const SfxStringItem*>(pItem)->GetValue());
+ if(SfxItemState::SET == pReqArgs->GetItemState(FN_PARAM_1, false, &pItem))
+ aSearchItem.SetReplaceString(static_cast<const SfxStringItem*>(pItem)->GetValue());
+
+ if (nSlot == FID_SEARCH)
+ aSearchItem.SetCommand(SvxSearchCmd::FIND);
+ else if(nSlot == FID_REPLACE)
+ aSearchItem.SetCommand(SvxSearchCmd::REPLACE);
+ else if(nSlot == FID_REPLACE_ALL)
+ aSearchItem.SetCommand(SvxSearchCmd::REPLACE_ALL);
+ else
+ aSearchItem.SetCommand(SvxSearchCmd::FIND_ALL);
+
+ // execute request (which stores the SearchItem)
+
+ aSearchItem.SetWhich(SID_SEARCH_ITEM);
+ GetViewData().GetDispatcher().ExecuteList(FID_SEARCH_NOW,
+ rReq.IsAPI() ? SfxCallMode::API|SfxCallMode::SYNCHRON :
+ SfxCallMode::RECORD,
+ { &aSearchItem });
+ }
+ else
+ {
+ GetViewData().GetDispatcher().Execute(
+ SID_SEARCH_DLG, SfxCallMode::ASYNCHRON|SfxCallMode::RECORD );
+ }
+ }
+ break;
+ case FID_REPEAT_SEARCH:
+ {
+ // once more with ScGlobal::GetSearchItem()
+
+ SvxSearchItem aSearchItem = ScGlobal::GetSearchItem();
+ aSearchItem.SetWhich(SID_SEARCH_ITEM);
+ GetViewData().GetDispatcher().ExecuteList( FID_SEARCH_NOW,
+ rReq.IsAPI() ? SfxCallMode::API|SfxCallMode::SYNCHRON :
+ SfxCallMode::RECORD,
+ { &aSearchItem });
+ }
+ break;
+// case FID_SEARCH_COUNT:
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabvwshf.cxx b/sc/source/ui/view/tabvwshf.cxx
new file mode 100644
index 0000000000..2ac3b93760
--- /dev/null
+++ b/sc/source/ui/view/tabvwshf.cxx
@@ -0,0 +1,1094 @@
+/* -*- 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 .
+ */
+
+#include <config_features.h>
+
+#include <memory>
+
+#include <sfx2/request.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <basic/sbstar.hxx>
+#include <basic/sberrors.hxx>
+#include <svl/ctloptions.hxx>
+#include <svl/stritem.hxx>
+#include <svl/whiter.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <sfx2/objface.hxx>
+#include <svx/svxdlg.hxx>
+#include <editeng/colritem.hxx>
+
+#include <tabvwsh.hxx>
+#include <sc.hrc>
+#include <helpids.h>
+#include <docsh.hxx>
+#include <document.hxx>
+#include <scresid.hxx>
+#include <globstr.hrc>
+#include <strings.hrc>
+#include <docfunc.hxx>
+#include <eventuno.hxx>
+#include <dpobject.hxx>
+#include <dpshttab.hxx>
+
+#include <scabstdlg.hxx>
+
+#include <tabbgcolor.hxx>
+#include <markdata.hxx>
+
+#include <vector>
+
+using std::unique_ptr;
+using namespace com::sun::star;
+
+void ScTabViewShell::ExecuteTable( SfxRequest& rReq )
+{
+ ScViewData& rViewData = GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+
+ SCTAB nCurrentTab = rViewData.GetTabNo();
+ SCTAB nTabCount = rDoc.GetTableCount();
+ sal_uInt16 nSlot = rReq.GetSlot();
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+
+ HideListBox(); // Autofilter-DropDown-Listbox
+
+ switch ( nSlot )
+ {
+ case FID_TABLE_VISIBLE:
+ {
+ OUString aName;
+ rDoc.GetName( nCurrentTab, aName );
+
+ bool bVisible=true;
+ if( pReqArgs != nullptr )
+ {
+ const SfxPoolItem* pItem;
+ if( pReqArgs->HasItem( FID_TABLE_VISIBLE, &pItem ) )
+ bVisible = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+ }
+
+ if( ! bVisible ) // fade out
+ {
+ if ( rDoc.IsDocEditable() )
+ {
+ ScMarkData& rMark = rViewData.GetMarkData();
+ HideTable( rMark );
+ }
+ }
+ else // fade in
+ {
+ std::vector<OUString> rNames { aName };
+ ShowTable( rNames );
+ }
+ }
+ break;
+
+ case FID_TABLE_HIDE:
+ {
+ if ( rDoc.IsDocEditable() )
+ {
+ ScMarkData& rMark = rViewData.GetMarkData();
+ SCTAB nActiveTab = -1;
+ // For the cases when user right clicks on a non-active tab and hides it. This case is possible for Online.
+ if (pReqArgs)
+ {
+ const SfxPoolItem *pItem;
+ if( pReqArgs->HasItem( FID_TABLE_HIDE, &pItem ) )
+ {
+ SCTAB nTabNumber = static_cast<const SfxInt16Item*>(pItem)->GetValue();
+ // Does selected sheets (tabs) list include the sheet to be hidden?
+ std::set<SCTAB>::iterator it = rMark.GetSelectedTabs().find(nTabNumber);
+ if (it == rMark.GetSelectedTabs().end())
+ {
+ // No it doesn't, so we won't shift the selected tab. Let's remember its position.
+ nActiveTab = GetViewData().GetTabNo();
+ }
+ rMark.SelectOneTable(nTabNumber);
+ }
+ }
+ HideTable( rMark, nActiveTab );
+ }
+ }
+ break;
+
+ case FID_TABLE_SHOW:
+ {
+ std::vector<OUString> rNames;
+ if ( pReqArgs )
+ {
+ const SfxPoolItem* pItem;
+ if( pReqArgs->HasItem( FID_TABLE_SHOW, &pItem ) )
+ {
+ OUString aName = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ rNames.push_back(aName);
+ ShowTable( rNames );
+
+ if( ! rReq.IsAPI() )
+ rReq.Done();
+ }
+ }
+ else
+ {
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ VclPtr<AbstractScShowTabDlg> pDlg(pFact->CreateScShowTabDlg(GetFrameWeld()));
+
+ OUString aTabName;
+ bool bFirst = true;
+ for ( SCTAB i=0; i != nTabCount; i++ )
+ {
+ if (!rDoc.IsVisible(i))
+ {
+ rDoc.GetName( i, aTabName );
+ pDlg->Insert( aTabName, bFirst );
+ bFirst = false;
+ }
+ }
+
+ std::shared_ptr<SfxRequest> pReq = std::make_shared<SfxRequest>(rReq);
+ pDlg->StartExecuteAsync([this, pDlg, pReq](sal_Int32 nResult){
+ std::vector<OUString> sTables;
+ if (RET_OK == nResult)
+ {
+ std::vector<sal_Int32> aSelectedRows = pDlg->GetSelectedRows();
+ for (auto a : aSelectedRows)
+ {
+ OUString sTable = pDlg->GetEntry(a);
+ pReq->AppendItem( SfxStringItem( FID_TABLE_SHOW, sTable ) );
+ sTables.push_back(sTable);
+ }
+ ShowTable( sTables );
+ pReq->Done();
+ }
+ pDlg->disposeOnce();
+ });
+ rReq.Ignore();
+ }
+ }
+ break;
+
+ case FID_INS_TABLE:
+ case FID_INS_TABLE_EXT:
+ {
+ ScMarkData& rMark = rViewData.GetMarkData();
+ SCTAB nTabSelCount = rMark.GetSelectCount();
+ SCTAB nTabNr = nCurrentTab;
+
+ if ( !rDoc.IsDocEditable() )
+ break; // locked
+
+ if ( pReqArgs != nullptr ) // from basic
+ {
+ bool bOk = false;
+ const SfxPoolItem* pTabItem;
+ const SfxPoolItem* pNameItem;
+
+ if ( pReqArgs->HasItem( FN_PARAM_1, &pTabItem ) &&
+ pReqArgs->HasItem( nSlot, &pNameItem ) )
+ {
+ OUString aName = static_cast<const SfxStringItem*>(pNameItem)->GetValue();
+ rDoc.CreateValidTabName(aName);
+
+ // sheet number from basic: 1-based
+ // 0 is special, means adding at the end
+ nTabNr = static_cast<const SfxUInt16Item*>(pTabItem)->GetValue();
+ if (nTabNr == 0)
+ nTabNr = nTabCount;
+ else
+ --nTabNr;
+
+ if (nTabNr > nTabCount)
+ nTabNr = nTabCount;
+
+ bOk = InsertTable(aName, nTabNr);
+ }
+
+ if (bOk)
+ rReq.Done( *pReqArgs );
+ //! else set error
+ }
+ else // dialog
+ {
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScInsertTableDlg> pDlg(pFact->CreateScInsertTableDlg(GetFrameWeld(), rViewData,
+ nTabSelCount, nSlot == FID_INS_TABLE_EXT));
+ if ( RET_OK == pDlg->Execute() )
+ {
+ if (pDlg->GetTablesFromFile())
+ {
+ std::vector<SCTAB> nTabs;
+ sal_uInt16 n = 0;
+ const OUString* pStr = pDlg->GetFirstTable( &n );
+ while ( pStr )
+ {
+ nTabs.push_back( static_cast<SCTAB>(n) );
+ pStr = pDlg->GetNextTable( &n );
+ }
+ bool bLink = pDlg->GetTablesAsLink();
+ if (!nTabs.empty())
+ {
+ if(pDlg->IsTableBefore())
+ {
+ ImportTables( pDlg->GetDocShellTables(), nTabs.size(), nTabs.data(),
+ bLink,nTabNr );
+ }
+ else
+ {
+ SCTAB nTabAfter = nTabNr+1;
+
+ for(SCTAB j=nCurrentTab+1;j<nTabCount;j++)
+ {
+ if(!rDoc.IsScenario(j))
+ {
+ nTabAfter=j;
+ break;
+ }
+ }
+
+ ImportTables( pDlg->GetDocShellTables(), nTabs.size(), nTabs.data(),
+ bLink,nTabAfter );
+ }
+ }
+ }
+ else
+ {
+ SCTAB nCount=pDlg->GetTableCount();
+ if(pDlg->IsTableBefore())
+ {
+ if(nCount==1 && !pDlg->GetFirstTable()->isEmpty())
+ {
+ rReq.AppendItem( SfxStringItem( FID_INS_TABLE, *pDlg->GetFirstTable() ) );
+ rReq.AppendItem( SfxUInt16Item( FN_PARAM_1, static_cast<sal_uInt16>(nTabNr) + 1 ) ); // 1-based
+ rReq.Done();
+
+ InsertTable( *pDlg->GetFirstTable(), nTabNr );
+ }
+ else
+ {
+ std::vector<OUString> aNames(0);
+ InsertTables( aNames, nTabNr,nCount );
+ }
+ }
+ else
+ {
+ SCTAB nTabAfter = nTabNr+1;
+ SCTAB nSelHigh = rMark.GetLastSelected();
+
+ for(SCTAB j=nSelHigh+1;j<nTabCount;j++)
+ {
+ if(!rDoc.IsScenario(j))
+ {
+ nTabAfter=j;
+ break;
+ }
+ else // #101672#; increase nTabAfter, because it is possible that the scenario tables are the last
+ nTabAfter = j + 1;
+ }
+
+ if(nCount==1 && !pDlg->GetFirstTable()->isEmpty())
+ {
+ rReq.AppendItem( SfxStringItem( FID_INS_TABLE, *pDlg->GetFirstTable() ) );
+ rReq.AppendItem( SfxUInt16Item( FN_PARAM_1, static_cast<sal_uInt16>(nTabAfter) + 1 ) ); // 1-based
+ rReq.Done();
+
+ InsertTable( *pDlg->GetFirstTable(), nTabAfter);
+ }
+ else
+ {
+ std::vector<OUString> aNames(0);
+ InsertTables( aNames, nTabAfter,nCount);
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case FID_TAB_APPEND:
+ case FID_TAB_RENAME:
+ case FID_TAB_MENU_RENAME:
+ {
+ // FID_TAB_MENU_RENAME - "rename" in menu
+ // FID_TAB_RENAME - "name"-property for basic
+ // equal execute, but MENU_RENAME may be disabled inside GetState
+
+ if ( nSlot == FID_TAB_MENU_RENAME )
+ nSlot = FID_TAB_RENAME; // equal execute
+
+ SCTAB nTabNr = rViewData.GetTabNo();
+ ScMarkData& rMark = rViewData.GetMarkData();
+ SCTAB nTabSelCount = rMark.GetSelectCount();
+
+ if ( !rDoc.IsDocEditable() )
+ break; // everything locked
+
+ if ( nSlot != FID_TAB_APPEND &&
+ ( rDoc.IsTabProtected( nTabNr ) || nTabSelCount > 1 ) )
+ break; // no rename
+
+ if( pReqArgs != nullptr )
+ {
+ bool bDone = false;
+ const SfxPoolItem* pItem;
+ OUString aName;
+
+ if( pReqArgs->HasItem( FN_PARAM_1, &pItem ) )
+ {
+ nTabNr = static_cast<const SfxUInt16Item*>(pItem)->GetValue();
+
+ // inserting is 1-based, let's be consistent
+ if (nTabNr > 0)
+ --nTabNr;
+ }
+
+ if( pReqArgs->HasItem( nSlot, &pItem ) )
+ aName = static_cast<const SfxStringItem*>(pItem)->GetValue();
+
+ switch ( nSlot )
+ {
+ case FID_TAB_APPEND:
+ bDone = AppendTable( aName );
+ break;
+ case FID_TAB_RENAME:
+ bDone = RenameTable( aName, nTabNr );
+ break;
+ }
+
+ if( bDone )
+ {
+ rReq.Done( *pReqArgs );
+ }
+ }
+ else
+ {
+ sal_uInt16 nRet = RET_OK;
+ bool bDone = false;
+ OUString aErrMsg ( ScResId( STR_INVALIDTABNAME ) );
+ OUString aName;
+ OUString aDlgTitle;
+ OUString sHelpId;
+
+ switch ( nSlot )
+ {
+ case FID_TAB_APPEND:
+ aDlgTitle = ScResId(SCSTR_APDTABLE);
+ rDoc.CreateValidTabName( aName );
+ sHelpId = HID_SC_APPEND_NAME;
+ break;
+
+ case FID_TAB_RENAME:
+ aDlgTitle = ScResId(SCSTR_RENAMETAB);
+ rDoc.GetName( rViewData.GetTabNo(), aName );
+ sHelpId = HID_SC_RENAME_NAME;
+ break;
+ }
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScStringInputDlg> pDlg(pFact->CreateScStringInputDlg(
+ GetFrameWeld(), aDlgTitle, ScResId(SCSTR_NAME),
+ aName, GetStaticInterface()->GetSlot(nSlot)->GetCommand(),
+ sHelpId));
+
+
+ while ( !bDone && nRet == RET_OK )
+ {
+ nRet = pDlg->Execute();
+
+ if ( nRet == RET_OK )
+ {
+ aName = pDlg->GetInputString();
+
+ switch ( nSlot )
+ {
+ case FID_TAB_APPEND:
+ bDone = AppendTable( aName );
+ break;
+ case FID_TAB_RENAME:
+ bDone = RenameTable( aName, nTabNr );
+ break;
+ }
+
+ if ( bDone )
+ {
+ rReq.AppendItem( SfxStringItem( nSlot, aName ) );
+ rReq.Done();
+ }
+ else
+ {
+ if( rReq.IsAPI() )
+ {
+#if HAVE_FEATURE_SCRIPTING
+ StarBASIC::Error( ERRCODE_BASIC_SETPROP_FAILED ); // XXX error handling???
+#endif
+ }
+ else
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetFrameWeld(),
+ VclMessageType::Warning, VclButtonsType::Ok, aErrMsg));
+ nRet = xBox->run();
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case FID_TAB_MOVE:
+ {
+ if ( rDoc.GetChangeTrack() != nullptr )
+ break; // if ChangeTracking is active, then no TabMove
+
+ bool bDoIt = false;
+ sal_uInt16 nDoc = 0;
+ SCTAB nTab = rViewData.GetTabNo();
+ bool bCpy = false, bUseCurrentDocument = false;
+ OUString aDocName;
+ OUString aTabName;
+
+ if( pReqArgs != nullptr )
+ {
+ SCTAB nTableCount = rDoc.GetTableCount();
+ const SfxPoolItem* pItem;
+
+ // if UseCurrentDocument(FN_PARAM_3) is true ignore the document name provided and use current document
+ if( pReqArgs->HasItem( FN_PARAM_3, &pItem ) )
+ bUseCurrentDocument = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+
+ if (bUseCurrentDocument)
+ aDocName = GetViewData().GetDocShell()->GetTitle();
+ else if(pReqArgs->HasItem( FID_TAB_MOVE, &pItem ))
+ aDocName = static_cast<const SfxStringItem*>(pItem)->GetValue();
+
+ if( pReqArgs->HasItem( FN_PARAM_1, &pItem ) )
+ {
+ // table is 1-based
+ nTab = static_cast<const SfxUInt16Item*>(pItem)->GetValue() - 1;
+ if ( nTab >= nTableCount )
+ nTab = SC_TAB_APPEND;
+ }
+ if( pReqArgs->HasItem( FN_PARAM_2, &pItem ) )
+ bCpy = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+
+ if (!aDocName.isEmpty())
+ {
+ SfxObjectShell* pSh = SfxObjectShell::GetFirst();
+ ScDocShell* pScSh = nullptr;
+ sal_uInt16 i=0;
+
+ while ( pSh )
+ {
+ pScSh = dynamic_cast<ScDocShell*>( pSh );
+
+ if( pScSh )
+ {
+ pScSh->GetTitle();
+
+ if (aDocName == pScSh->GetTitle())
+ {
+ nDoc = i;
+ ScDocument& rDestDoc = pScSh->GetDocument();
+ nTableCount = rDestDoc.GetTableCount();
+ bDoIt = rDestDoc.IsDocEditable();
+ break;
+ }
+
+ i++; // only count ScDocShell
+ }
+ pSh = SfxObjectShell::GetNext( *pSh );
+ }
+ }
+ else // no doc-name -> new doc
+ {
+ nDoc = SC_DOC_NEW;
+ bDoIt = true;
+ }
+
+ if ( bDoIt && nTab >= nTableCount ) // if necessary append
+ nTab = SC_TAB_APPEND;
+ }
+ else
+ {
+ OUString aDefaultName;
+ rDoc.GetName( rViewData.GetTabNo(), aDefaultName );
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScMoveTableDlg> pDlg(pFact->CreateScMoveTableDlg(GetFrameWeld(),
+ aDefaultName));
+
+ SCTAB nTableCount = rDoc.GetTableCount();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ SCTAB nTabSelCount = rMark.GetSelectCount();
+
+ if(nTableCount==nTabSelCount)
+ {
+ pDlg->SetForceCopyTable();
+ }
+
+ // We support direct renaming of sheet only when one sheet
+ // is selected.
+ pDlg->EnableRenameTable(nTabSelCount == 1);
+
+ if ( pDlg->Execute() == RET_OK )
+ {
+ nDoc = pDlg->GetSelectedDocument();
+ nTab = pDlg->GetSelectedTable();
+ bCpy = pDlg->GetCopyTable();
+ bool bRna = pDlg->GetRenameTable();
+ // Leave aTabName string empty, when Rename is FALSE.
+ if( bRna )
+ {
+ pDlg->GetTabNameString( aTabName );
+ }
+ bDoIt = true;
+
+ OUString aFoundDocName;
+ if ( nDoc != SC_DOC_NEW )
+ {
+ ScDocShell* pSh = ScDocShell::GetShellByNum( nDoc );
+ if (pSh)
+ {
+ aFoundDocName = pSh->GetTitle();
+ if ( !pSh->GetDocument().IsDocEditable() )
+ {
+ ErrorMessage(STR_READONLYERR);
+ bDoIt = false;
+ }
+ }
+ }
+ rReq.AppendItem( SfxStringItem( FID_TAB_MOVE, aFoundDocName ) );
+ // 1-based table, if not APPEND
+ SCTAB nBasicTab = ( nTab <= MAXTAB ) ? (nTab+1) : nTab;
+ rReq.AppendItem( SfxUInt16Item( FN_PARAM_1, static_cast<sal_uInt16>(nBasicTab) ) );
+ rReq.AppendItem( SfxBoolItem( FN_PARAM_2, bCpy ) );
+ }
+ }
+
+ if( bDoIt )
+ {
+ rReq.Done(); // record, while doc is active
+
+ MoveTable( nDoc, nTab, bCpy, &aTabName );
+ }
+ }
+ break;
+
+ case FID_TAB_DUPLICATE:
+ {
+ // Get info about current document and selected tab
+ SCTAB nTab = rViewData.GetTabNo();
+ OUString aDocName = GetViewData().GetDocShell()->GetTitle();
+ sal_uInt16 nDoc = 0;
+ bool bCpy = true;
+
+ SfxObjectShell* pSh = SfxObjectShell::GetFirst();
+ ScDocShell* pScSh = nullptr;
+ sal_uInt16 i = 0;
+
+ // Determine the index of the current document
+ while ( pSh )
+ {
+ pScSh = dynamic_cast<ScDocShell*>( pSh );
+
+ if( pScSh )
+ {
+ pScSh->GetTitle();
+
+ if (aDocName == pScSh->GetTitle())
+ {
+ nDoc = i;
+ break;
+ }
+ // Only count ScDocShell
+ i++;
+ }
+ pSh = SfxObjectShell::GetNext( *pSh );
+ }
+
+ MoveTable( nDoc, nTab + 1, bCpy );
+ }
+ break;
+
+ case FID_DELETE_TABLE:
+ {
+ bool bHasIndex = (pReqArgs != nullptr);
+
+ // allow removing via the Index/FID_DELETE_TABLE parameter
+ SCTAB nTabNr = nCurrentTab;
+ if (bHasIndex)
+ {
+ const SfxPoolItem* pItem;
+ if (pReqArgs->HasItem(FID_DELETE_TABLE, &pItem))
+ {
+ nTabNr = static_cast<const SfxUInt16Item*>(pItem)->GetValue();
+
+ // inserting is 1-based, let's be consistent
+ if (nTabNr > 0)
+ --nTabNr;
+ }
+ }
+
+ bool bDoIt = bHasIndex;
+ if (!bDoIt)
+ {
+ bool bTabWithPivotTable = false;
+ if (rDoc.HasPivotTable())
+ {
+ const ScDPCollection* pDPs = rDoc.GetDPCollection();
+ if (pDPs)
+ {
+ const ScMarkData::MarkedTabsType& rSelectedTabs = rViewData.GetMarkData().GetSelectedTabs();
+ const size_t nCount = pDPs->GetCount();
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ const ScDPObject& rDPObj = (*pDPs)[i];
+ const ScSheetSourceDesc* pSheetSourceDesc = rDPObj.GetSheetDesc();
+ if (pSheetSourceDesc)
+ {
+ SCTAB nTabOut = rDPObj.GetOutRange().aStart.Tab();
+ SCTAB nTabSource = pSheetSourceDesc->GetSourceRange().aStart.Tab();
+ bool bTabOutSel = false;
+ for (const SCTAB nSelTab : rSelectedTabs)
+ {
+ if (nSelTab == nTabSource)
+ bTabWithPivotTable = true;
+ if (nSelTab == nTabOut)
+ bTabOutSel = true;
+ if (bTabWithPivotTable && bTabOutSel)
+ break;
+ }
+ // if both pivot table and data are selected
+ // no need to warn for source data losing
+ if (bTabWithPivotTable && bTabOutSel)
+ bTabWithPivotTable = false;
+ if (bTabWithPivotTable)
+ break;
+ }
+ }
+ }
+ }
+
+ SCTAB nTabSelCnt = rViewData.GetMarkData().GetSelectCount();
+ OUString aTabSelCnt = Application::GetSettings().GetUILocaleDataWrapper().getNum( nTabSelCnt, 0 );
+ OUString aQueryDeleteTab = ScResId( STR_QUERY_DELTAB, nTabSelCnt )
+ .replaceAll( "%d", aTabSelCnt );
+ if (bTabWithPivotTable)
+ {
+ OUString aStr = ScResId( STR_QUERY_PIVOTTABLE_DELTAB, nTabSelCnt )
+ .replaceAll( "%d", aTabSelCnt )
+ + " " + aQueryDeleteTab;
+
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(GetFrameWeld(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ aStr));
+ xQueryBox->set_default_response(RET_NO);
+
+ // Hard warning as there is potential of data loss on deletion
+ bDoIt = (RET_YES == xQueryBox->run());
+ }
+ else
+ {
+ bool bHasData = false;
+ ScMarkData& rMark = rViewData.GetMarkData();
+ for ( SCTAB i = 0; i < nTabCount && !bHasData; i++ )
+ {
+ if ( rMark.GetTableSelect(i) && !rDoc.IsTabProtected(i) )
+ {
+ SCCOL nStartCol;
+ SCROW nStartRow;
+ bHasData = rDoc.GetDataStart( i, nStartCol, nStartRow );
+ }
+ }
+ // Do not ask for confirmation if all selected tabs are empty
+ if (bHasData)
+ {
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(GetFrameWeld(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ aQueryDeleteTab));
+ xQueryBox->set_default_response(RET_YES);
+
+ // no parameter given, ask for confirmation
+ bDoIt = (RET_YES == xQueryBox->run());
+ }
+ else
+ bDoIt = true;
+ }
+ }
+
+ if (bDoIt)
+ {
+ SCTAB nNewTab = nCurrentTab;
+ std::vector<SCTAB> TheTabs;
+
+ if (bHasIndex)
+ {
+ // sheet no. provided by the parameter
+ TheTabs.push_back(nTabNr);
+ if (nNewTab > nTabNr && nNewTab > 0)
+ --nNewTab;
+ }
+ else
+ {
+ SCTAB nFirstTab = 0;
+ bool bTabFlag = false;
+ ScMarkData& rMark = rViewData.GetMarkData();
+ for (SCTAB i = 0; i < nTabCount; i++)
+ {
+ if (rMark.GetTableSelect(i) && !rDoc.IsTabProtected(i))
+ {
+ TheTabs.push_back(i);
+ bTabFlag = true;
+ if (nNewTab == i && i+1 < nTabCount)
+ nNewTab++;
+ }
+ if (!bTabFlag)
+ nFirstTab = i;
+ }
+ if (nNewTab >= nTabCount - static_cast<SCTAB>(TheTabs.size()))
+ nNewTab = nFirstTab;
+ }
+
+ rViewData.SetTabNo(nNewTab);
+ DeleteTables(TheTabs);
+ TheTabs.clear();
+ rReq.Done();
+ }
+ }
+ break;
+
+ case FID_TAB_RTL:
+ {
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+ ScDocFunc &rFunc = pDocSh->GetDocFunc();
+ bool bSet = !rDoc.IsLayoutRTL( nCurrentTab );
+
+ const ScMarkData& rMark = rViewData.GetMarkData();
+ if ( rMark.GetSelectCount() != 0 )
+ {
+ // handle several sheets
+
+ SfxUndoManager* pUndoManager = pDocSh->GetUndoManager();
+ OUString aUndo = ScResId( STR_UNDO_TAB_RTL );
+ pUndoManager->EnterListAction( aUndo, aUndo, 0, rViewData.GetViewShell()->GetViewShellId() );
+
+ for (const auto& rTab : rMark)
+ rFunc.SetLayoutRTL( rTab, bSet );
+
+ pUndoManager->LeaveListAction();
+ }
+ else
+ rFunc.SetLayoutRTL( nCurrentTab, bSet );
+ }
+ break;
+
+ case FID_TAB_TOGGLE_GRID:
+ {
+ bool bShowGrid = rViewData.GetShowGrid();
+ rViewData.SetShowGrid(!bShowGrid);
+ SfxBindings& rBindings = GetViewFrame().GetBindings();
+ rBindings.Invalidate( FID_TAB_TOGGLE_GRID );
+ ScDocShellModificator aModificator(*rViewData.GetDocShell());
+ aModificator.SetDocumentModified();
+ PaintGrid();
+ rReq.Done();
+ }
+ break;
+
+ case FID_TAB_SET_TAB_BG_COLOR:
+ case FID_TAB_MENU_SET_TAB_BG_COLOR:
+ {
+ if ( nSlot == FID_TAB_MENU_SET_TAB_BG_COLOR )
+ nSlot = FID_TAB_SET_TAB_BG_COLOR;
+ SCTAB nTabNr = rViewData.GetTabNo();
+ ScMarkData& rMark = rViewData.GetMarkData();
+ SCTAB nTabSelCount = rMark.GetSelectCount();
+ if ( !rDoc.IsDocEditable() )
+ break;
+
+ if ( rDoc.IsTabProtected( nTabNr ) ) // ||nTabSelCount > 1
+ break;
+
+ if( pReqArgs != nullptr )
+ {
+ bool bDone = false;
+ const SfxPoolItem* pItem;
+ Color aColor;
+
+ if( pReqArgs->HasItem( nSlot, &pItem ) )
+ aColor = static_cast<const SvxColorItem*>(pItem)->GetValue();
+
+ if ( nTabSelCount > 1 )
+ {
+ std::unique_ptr<ScUndoTabColorInfo::List>
+ pTabColorList(new ScUndoTabColorInfo::List);
+ for (const auto& rTab : rMark)
+ {
+ if ( !rDoc.IsTabProtected(rTab) )
+ {
+ ScUndoTabColorInfo aTabColorInfo(rTab);
+ aTabColorInfo.maNewTabBgColor = aColor;
+ pTabColorList->push_back(aTabColorInfo);
+ }
+ }
+ bDone = SetTabBgColor( *pTabColorList );
+ }
+ else
+ {
+ bDone = SetTabBgColor( aColor, nCurrentTab ); //ScViewFunc.SetTabBgColor
+ }
+ if( bDone )
+ {
+ rReq.Done( *pReqArgs );
+ }
+ }
+ else
+ {
+ sal_uInt16 nRet = RET_OK; /// temp
+ bool bDone = false; /// temp
+
+ Color aTabBgColor = rDoc.GetTabBgColor( nCurrentTab );
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractScTabBgColorDlg> pDlg(pFact->CreateScTabBgColorDlg(
+ GetFrameWeld(),
+ ScResId(SCSTR_SET_TAB_BG_COLOR),
+ ScResId(SCSTR_NO_TAB_BG_COLOR),
+ aTabBgColor));
+ while ( !bDone && nRet == RET_OK )
+ {
+ nRet = pDlg->Execute();
+ if( nRet == RET_OK )
+ {
+ Color aSelectedColor;
+ pDlg->GetSelectedColor(aSelectedColor);
+ std::unique_ptr<ScUndoTabColorInfo::List>
+ pTabColorList(new ScUndoTabColorInfo::List);
+ if ( nTabSelCount > 1 )
+ {
+ for (const auto& rTab : rMark)
+ {
+ if ( !rDoc.IsTabProtected(rTab) )
+ {
+ ScUndoTabColorInfo aTabColorInfo(rTab);
+ aTabColorInfo.maNewTabBgColor = aSelectedColor;
+ pTabColorList->push_back(aTabColorInfo);
+ }
+ }
+ bDone = SetTabBgColor( *pTabColorList );
+ }
+ else
+ {
+ bDone = SetTabBgColor( aSelectedColor, nCurrentTab ); //ScViewFunc.SetTabBgColor
+ }
+
+ if ( bDone )
+ {
+ rReq.AppendItem( SvxColorItem( aTabBgColor, nSlot ) );
+ rReq.Done();
+ }
+ else
+ {
+ if( rReq.IsAPI() )
+ {
+#if HAVE_FEATURE_SCRIPTING
+ StarBASIC::Error( ERRCODE_BASIC_SETPROP_FAILED );
+#endif
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case FID_TAB_EVENTS:
+ {
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+ uno::Reference<container::XNameReplace> xEvents( new ScSheetEventsObj( pDocSh, nCurrentTab ) );
+ uno::Reference<frame::XFrame> xFrame = GetViewFrame().GetFrame().GetFrameInterface();
+ SvxAbstractDialogFactory* pDlgFactory = SvxAbstractDialogFactory::Create();
+ ScopedVclPtr<VclAbstractDialog> pDialog( pDlgFactory->CreateSvxMacroAssignDlg(
+ GetFrameWeld(), xFrame, false, xEvents, 0 ) );
+ if ( pDialog->Execute() == RET_OK )
+ {
+ // the dialog modifies the settings directly
+ }
+ }
+ break;
+ case FID_TOGGLEHIDDENCOLROW:
+ {
+ svtools::EditableColorConfig aEditableConfig;
+ svtools::ColorConfigValue aValue = aEditableConfig.GetColorValue(svtools::CALCHIDDENROWCOL);
+ aValue.bIsVisible = !aValue.bIsVisible;
+ aEditableConfig.SetColorValue(svtools::CALCHIDDENROWCOL, aValue);
+ }
+ break;
+ default:
+ OSL_FAIL("unknown message for ViewShell");
+ break;
+ }
+}
+
+void ScTabViewShell::GetStateTable( SfxItemSet& rSet )
+{
+ ScViewData& rViewData = GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ ScDocShell* pDocShell = rViewData.GetDocShell();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ SCTAB nTab = rViewData.GetTabNo();
+
+ SCTAB nTabCount = rDoc.GetTableCount();
+ SCTAB nTabSelCount = rMark.GetSelectCount();
+
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+
+ while ( nWhich )
+ {
+ switch ( nWhich )
+ {
+
+ case FID_TABLE_VISIBLE:
+ rSet.Put( SfxBoolItem( nWhich, rDoc.IsVisible(nTab) ));
+ break;
+
+ case FID_TABLE_HIDE:
+ {
+ sal_uInt16 nVis = 0;
+ // enable menu : check to make sure we won't hide all sheets. we need at least one visible at all times.
+ for ( SCTAB i=0; i < nTabCount && nVis<nTabSelCount + 1; i++ )
+ if (rDoc.IsVisible(i))
+ ++nVis;
+ if ( nVis<=nTabSelCount || !rDoc.IsDocEditable() )
+ rSet.DisableItem( nWhich );
+ }
+ break;
+
+ case FID_TABLE_SHOW:
+ {
+ bool bHasHidden = false;
+ for ( SCTAB i=0; i < nTabCount && !bHasHidden; i++ )
+ if (!rDoc.IsVisible(i))
+ bHasHidden = true;
+ if ( !bHasHidden || rDoc.IsDocProtected() || nTabSelCount > 1 )
+ rSet.DisableItem( nWhich );
+ }
+ break;
+
+ case FID_DELETE_TABLE:
+ {
+ if ( rDoc.GetChangeTrack() )
+ rSet.DisableItem( nWhich );
+ else
+ {
+ sal_uInt16 nVis = 0;
+ for ( SCTAB i=0; i < nTabCount && nVis<2; i++ )
+ if (rDoc.IsVisible(i))
+ ++nVis;
+ if ( rDoc.IsTabProtected(nTab)
+ || !rDoc.IsDocEditable()
+ || nVis < 2
+ || nTabSelCount == nTabCount)
+ rSet.DisableItem( nWhich );
+ }
+ }
+ break;
+
+ case FID_INS_TABLE:
+ case FID_INS_TABLE_EXT:
+ case FID_TAB_APPEND:
+ if ( !rDoc.IsDocEditable() ||
+ nTabCount > MAXTAB ||
+ ( nWhich == FID_INS_TABLE_EXT && pDocShell && pDocShell->IsDocShared() ) )
+ rSet.DisableItem( nWhich );
+ break;
+
+ case FID_TAB_MOVE:
+ if ( !rDoc.IsDocEditable()
+ || rDoc.GetChangeTrack() != nullptr
+ || nTabCount > MAXTAB)
+ rSet.DisableItem( nWhich );
+ break;
+
+ case FID_TAB_DUPLICATE:
+ if ( !rDoc.IsDocEditable()
+ || rDoc.GetChangeTrack() != nullptr
+ || nTabCount > MAXTAB)
+ rSet.DisableItem( nWhich );
+ break;
+
+ // FID_TAB_MENU_RENAME - "rename" from Menu
+ // FID_TAB_RENAME - "name"-property for Basic
+
+ case FID_TAB_MENU_RENAME:
+ if ( !rDoc.IsDocEditable() ||
+ rDoc.IsTabProtected(nTab) ||nTabSelCount > 1 ||
+ ( pDocShell && pDocShell->IsDocShared() ) )
+ rSet.DisableItem( nWhich );
+ break;
+
+ case FID_TAB_RENAME:
+ {
+ OUString aTabName;
+ rDoc.GetName( nTab, aTabName );
+
+ rSet.Put( SfxStringItem( nWhich, aTabName ));
+
+ }
+ break;
+
+ case FID_TAB_RTL:
+ {
+ if ( !SvtCTLOptions::IsCTLFontEnabled() )
+ rSet.DisableItem( nWhich );
+ else
+ rSet.Put( SfxBoolItem( nWhich, rDoc.IsLayoutRTL( nTab ) ) );
+ }
+ break;
+
+ case FID_TAB_MENU_SET_TAB_BG_COLOR:
+ {
+ if ( !rDoc.IsDocEditable()
+ || ( pDocShell && pDocShell->IsDocShared() )
+ || rDoc.IsTabProtected(nTab) )
+ rSet.DisableItem( nWhich );
+ }
+ break;
+
+ case FID_TAB_SET_TAB_BG_COLOR:
+ {
+ Color aColor = rDoc.GetTabBgColor( nTab );
+ rSet.Put( SvxColorItem( aColor, nWhich ) );
+ }
+ break;
+
+ case FID_TAB_TOGGLE_GRID:
+ rSet.Put( SfxBoolItem(nWhich, rViewData.GetShowGrid()) );
+ break;
+ }
+ nWhich = aIter.NextWhich();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabvwshg.cxx b/sc/source/ui/view/tabvwshg.cxx
new file mode 100644
index 0000000000..6b6820b9a3
--- /dev/null
+++ b/sc/source/ui/view/tabvwshg.cxx
@@ -0,0 +1,120 @@
+/* -*- 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 .
+ */
+
+#include <config_features.h>
+
+#include <tools/urlobj.hxx>
+#include <svx/svdobjkind.hxx>
+#include <svx/svdouno.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/docfile.hxx>
+#include <osl/diagnose.h>
+
+#include <com/sun/star/form/FormButtonType.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/awt/XControlModel.hpp>
+
+#include <tabvwsh.hxx>
+#include <document.hxx>
+#include <drawview.hxx>
+#include <globstr.hrc>
+#include <gridwin.hxx>
+#include <avmedia/mediawindow.hxx>
+
+using namespace com::sun::star;
+
+void ScTabViewShell::InsertURLButton( const OUString& rName, const OUString& rURL,
+ const OUString& rTarget,
+ const Point* pInsPos )
+{
+ // protected sheet ?
+
+ ScViewData& rViewData = GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ SCTAB nTab = rViewData.GetTabNo();
+ if ( rDoc.IsTabProtected(nTab) )
+ {
+ ErrorMessage(STR_PROTECTIONERR);
+ return;
+ }
+
+ MakeDrawLayer();
+
+ ScTabView* pView = rViewData.GetView();
+ ScDrawView* pDrView = pView->GetScDrawView();
+ SdrModel& rModel = pDrView->GetModel();
+
+ rtl::Reference<SdrObject> pObj = SdrObjFactory::MakeNewObject(
+ rModel,
+ SdrInventor::FmForm,
+ SdrObjKind::FormButton);
+
+ SdrUnoObj* pUnoCtrl = dynamic_cast<SdrUnoObj*>( pObj.get() );
+ OSL_ENSURE( pUnoCtrl, "no SdrUnoObj");
+ if( !pUnoCtrl )
+ return;
+
+ uno::Reference<awt::XControlModel> xControlModel = pUnoCtrl->GetUnoControlModel();
+ OSL_ENSURE( xControlModel.is(), "UNO control without model" );
+ if( !xControlModel.is() )
+ return;
+
+ uno::Reference< beans::XPropertySet > xPropSet( xControlModel, uno::UNO_QUERY );
+
+ xPropSet->setPropertyValue("Label", uno::Any(rName) );
+
+ OUString aTmp = INetURLObject::GetAbsURL( rDoc.GetDocumentShell()->GetMedium()->GetBaseURL(), rURL );
+ xPropSet->setPropertyValue("TargetURL", uno::Any(aTmp) );
+
+ if( !rTarget.isEmpty() )
+ {
+ xPropSet->setPropertyValue("TargetFrame", uno::Any(rTarget) );
+ }
+
+ xPropSet->setPropertyValue("ButtonType", uno::Any(form::FormButtonType_URL) );
+
+#if HAVE_FEATURE_AVMEDIA
+ if ( ::avmedia::MediaWindow::isMediaURL( rURL, ""/*TODO?*/ ) )
+ {
+ xPropSet->setPropertyValue("DispatchURLInternal", uno::Any(true) );
+ }
+#endif
+
+ Point aPos;
+ if (pInsPos)
+ aPos = *pInsPos;
+ else
+ aPos = GetInsertPos();
+
+ // Size as in 3.1:
+ Size aSize = GetActiveWin()->PixelToLogic(Size(140, 20));
+
+ if ( rDoc.IsNegativePage(nTab) )
+ aPos.AdjustX( -(aSize.Width()) );
+
+ pObj->SetLogicRect(tools::Rectangle(aPos, aSize));
+
+ // for the old VC-Button the position/size had to be set explicitly once more
+ // that seems not to be needed with UnoControls
+
+ // do not mark when Ole
+ pDrView->InsertObjectSafe( pObj.get(), *pDrView->GetSdrPageView() );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabvwshh.cxx b/sc/source/ui/view/tabvwshh.cxx
new file mode 100644
index 0000000000..d1263590a8
--- /dev/null
+++ b/sc/source/ui/view/tabvwshh.cxx
@@ -0,0 +1,261 @@
+/* -*- 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 .
+ */
+
+#include <config_features.h>
+
+#include <basic/sberrors.hxx>
+#include <svx/svdmark.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdview.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/request.hxx>
+#include <basic/sbxcore.hxx>
+#include <svl/stritem.hxx>
+#include <svl/whiter.hxx>
+#include <vcl/svapp.hxx>
+#include <osl/diagnose.h>
+
+#include <tabvwsh.hxx>
+#include <document.hxx>
+#include <sc.hrc>
+#include <drwlayer.hxx>
+#include <retypepassdlg.hxx>
+#include <tabprotection.hxx>
+
+#include <com/sun/star/embed/EmbedVerbs.hpp>
+
+using namespace com::sun::star;
+
+void ScTabViewShell::ExecuteObject( const SfxRequest& rReq )
+{
+ sal_uInt16 nSlotId = rReq.GetSlot();
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+
+ // Always activate/deactivate object in the visible View
+
+ ScTabViewShell* pVisibleSh = this;
+ if ( nSlotId == SID_OLE_SELECT || nSlotId == SID_OLE_ACTIVATE || nSlotId == SID_OLE_DEACTIVATE )
+ {
+ OSL_FAIL("old slot SID_OLE...");
+ }
+
+ switch (nSlotId)
+ {
+ case SID_OLE_SELECT:
+ case SID_OLE_ACTIVATE:
+ {
+ // In both cases, first select in the visible View
+
+ OUString aName;
+ SdrView* pDrView = GetScDrawView();
+ if (pDrView)
+ {
+ const SdrMarkList& rMarkList = pDrView->GetMarkedObjectList();
+ if (rMarkList.GetMarkCount() == 1)
+ aName = ScDrawLayer::GetVisibleName( rMarkList.GetMark(0)->GetMarkedSdrObj() );
+ }
+ pVisibleSh->SelectObject( aName );
+
+ // activate
+
+ if ( nSlotId == SID_OLE_ACTIVATE )
+ pVisibleSh->DoVerb(css::embed::EmbedVerbs::MS_OLEVERB_PRIMARY);
+ }
+ break;
+ case SID_OLE_DEACTIVATE:
+ pVisibleSh->DeactivateOle();
+ break;
+
+ case SID_OBJECT_LEFT:
+ case SID_OBJECT_TOP:
+ case SID_OBJECT_WIDTH:
+ case SID_OBJECT_HEIGHT:
+ {
+ bool bDone = false;
+ const SfxPoolItem* pItem;
+ if ( pReqArgs && pReqArgs->GetItemState( nSlotId, true, &pItem ) == SfxItemState::SET )
+ {
+ tools::Long nNewVal = static_cast<const SfxInt32Item*>(pItem)->GetValue();
+ if ( nNewVal < 0 )
+ nNewVal = 0;
+
+ //! convert from something into 1/100mm ??????
+
+ SdrView* pDrView = GetScDrawView();
+ if ( pDrView )
+ {
+ const SdrMarkList& rMarkList = pDrView->GetMarkedObjectList();
+ if (rMarkList.GetMarkCount() == 1)
+ {
+ SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
+ tools::Rectangle aRect = pObj->GetLogicRect();
+
+ if ( nSlotId == SID_OBJECT_LEFT )
+ pDrView->MoveMarkedObj( Size( nNewVal - aRect.Left(), 0 ) );
+ else if ( nSlotId == SID_OBJECT_TOP )
+ pDrView->MoveMarkedObj( Size( 0, nNewVal - aRect.Top() ) );
+ else if ( nSlotId == SID_OBJECT_WIDTH )
+ pDrView->ResizeMarkedObj( aRect.TopLeft(),
+ Fraction( nNewVal, aRect.GetWidth() ),
+ Fraction( 1, 1 ) );
+ else // if ( nSlotId == SID_OBJECT_HEIGHT )
+ pDrView->ResizeMarkedObj( aRect.TopLeft(),
+ Fraction( 1, 1 ),
+ Fraction( nNewVal, aRect.GetHeight() ) );
+ bDone = true;
+ }
+ }
+ }
+#if HAVE_FEATURE_SCRIPTING
+ if (!bDone)
+ SbxBase::SetError( ERRCODE_BASIC_BAD_PARAMETER ); // basic error
+#endif
+ }
+ break;
+
+ }
+}
+
+static uno::Reference < embed::XEmbeddedObject > lcl_GetSelectedObj( const SdrView* pDrView ) //! member of ScDrawView?
+{
+ uno::Reference < embed::XEmbeddedObject > xRet;
+ if (pDrView)
+ {
+ const SdrMarkList& rMarkList = pDrView->GetMarkedObjectList();
+ if (rMarkList.GetMarkCount() == 1)
+ {
+ SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
+ if (pObj->GetObjIdentifier() == SdrObjKind::OLE2)
+ {
+ SdrOle2Obj* pOle2Obj = static_cast<SdrOle2Obj*>(pObj);
+ xRet = pOle2Obj->GetObjRef();
+ }
+ }
+ }
+
+ return xRet;
+}
+
+void ScTabViewShell::GetObjectState( SfxItemSet& rSet )
+{
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while ( nWhich )
+ {
+ switch (nWhich)
+ {
+ case SID_ACTIVE_OBJ_NAME:
+ {
+ OUString aName;
+ uno::Reference < embed::XEmbeddedObject > xOLE = lcl_GetSelectedObj( GetScDrawView() );
+ if (xOLE.is())
+ {
+ aName = GetViewData().GetSfxDocShell()->GetEmbeddedObjectContainer().GetEmbeddedObjectName( xOLE );
+ }
+ rSet.Put( SfxStringItem( nWhich, aName ) );
+ }
+ break;
+ case SID_OBJECT_LEFT:
+ case SID_OBJECT_TOP:
+ case SID_OBJECT_WIDTH:
+ case SID_OBJECT_HEIGHT:
+ {
+ SdrView* pDrView = GetScDrawView();
+ if ( pDrView )
+ {
+ const SdrMarkList& rMarkList = pDrView->GetMarkedObjectList();
+ if (rMarkList.GetMarkCount() == 1)
+ {
+ SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
+ tools::Rectangle aRect = pObj->GetLogicRect();
+
+ tools::Long nVal;
+ if ( nWhich == SID_OBJECT_LEFT )
+ nVal = aRect.Left();
+ else if ( nWhich == SID_OBJECT_TOP )
+ nVal = aRect.Top();
+ else if ( nWhich == SID_OBJECT_WIDTH )
+ nVal = aRect.GetWidth();
+ else // if ( nWhich == SID_OBJECT_HEIGHT )
+ nVal = aRect.GetHeight();
+
+ //! convert from 1/100mm to something else ??????
+
+ rSet.Put( SfxInt32Item( TypedWhichId<SfxInt32Item>(nWhich), nVal ) );
+ }
+ }
+ }
+ break;
+ }
+ nWhich = aIter.NextWhich();
+ }
+}
+
+void ScTabViewShell::AddAccessibilityObject( SfxListener& rObject )
+{
+ if (!pAccessibilityBroadcaster)
+ pAccessibilityBroadcaster.reset( new SfxBroadcaster );
+
+ rObject.StartListening( *pAccessibilityBroadcaster );
+ ScDocument& rDoc = GetViewData().GetDocument();
+ rDoc.AddUnoObject(rObject);
+}
+
+void ScTabViewShell::RemoveAccessibilityObject( SfxListener& rObject )
+{
+ SolarMutexGuard g;
+
+ if (pAccessibilityBroadcaster)
+ {
+ rObject.EndListening( *pAccessibilityBroadcaster );
+ ScDocument& rDoc = GetViewData().GetDocument();
+ rDoc.RemoveUnoObject(rObject);
+ }
+ else
+ {
+ OSL_FAIL("no accessibility broadcaster?");
+ }
+}
+
+void ScTabViewShell::BroadcastAccessibility( const SfxHint &rHint )
+{
+ if (pAccessibilityBroadcaster)
+ pAccessibilityBroadcaster->Broadcast( rHint );
+}
+
+bool ScTabViewShell::HasAccessibilityObjects() const
+{
+ return pAccessibilityBroadcaster && pAccessibilityBroadcaster->HasListeners();
+}
+
+bool ScTabViewShell::ExecuteRetypePassDlg(ScPasswordHash eDesiredHash)
+{
+ ScDocument& rDoc = GetViewData().GetDocument();
+
+ ScRetypePassDlg aDlg(GetFrameWeld());
+ aDlg.SetDataFromDocument(rDoc);
+ aDlg.SetDesiredHash(eDesiredHash);
+ if (aDlg.run() != RET_OK)
+ return false;
+
+ aDlg.WriteNewDataToDocument(rDoc);
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/viewdata.cxx b/sc/source/ui/view/viewdata.cxx
new file mode 100644
index 0000000000..25e602669e
--- /dev/null
+++ b/sc/source/ui/view/viewdata.cxx
@@ -0,0 +1,4365 @@
+/* -*- 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 .
+ */
+
+#include <scitems.hxx>
+#include <editeng/eeitem.hxx>
+#include <o3tl/safeint.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <o3tl/string_view.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <editeng/adjustitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <svtools/colorcfg.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/editstat.hxx>
+#include <editeng/outliner.hxx>
+#include <editeng/unolingu.hxx>
+#include <editeng/justifyitem.hxx>
+
+#include <vcl/svapp.hxx>
+#include <rtl/math.hxx>
+#include <sal/log.hxx>
+
+#include <viewdata.hxx>
+#include <docoptio.hxx>
+#include <scmod.hxx>
+#include <global.hxx>
+#include <document.hxx>
+#include <drwlayer.hxx>
+#include <attrib.hxx>
+#include <tabview.hxx>
+#include <tabvwsh.hxx>
+#include <docsh.hxx>
+#include <patattr.hxx>
+#include <editutil.hxx>
+#include <scextopt.hxx>
+#include <miscuno.hxx>
+#include <unonames.hxx>
+#include <inputopt.hxx>
+#include <inputhdl.hxx>
+#include <inputwin.hxx>
+#include <viewutil.hxx>
+#include <markdata.hxx>
+#include <ViewSettingsSequenceDefines.hxx>
+#include <gridwin.hxx>
+#include <transobj.hxx>
+#include <clipparam.hxx>
+#include <comphelper/flagguard.hxx>
+#include <comphelper/lok.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/string.hxx>
+
+#include <vcl/uitest/logger.hxx>
+#include <vcl/uitest/eventdescription.hxx>
+
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/document/NamedPropertyValues.hpp>
+
+using namespace com::sun::star;
+
+#define SC_GROWY_SMALL_EXTRA 100
+#define SC_GROWY_BIG_EXTRA 200
+
+constexpr OUString TAG_TABBARWIDTH = u"tw:"_ustr;
+
+namespace {
+
+void lcl_LOKRemoveWindow(ScTabViewShell* pTabViewShell, ScSplitPos eWhich)
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ auto lRemoveWindows =
+ [pTabViewShell, eWhich] (ScTabViewShell* pOtherViewShell)
+ { pOtherViewShell->RemoveWindowFromForeignEditView(pTabViewShell, eWhich); };
+
+ SfxLokHelper::forEachOtherView(pTabViewShell, lRemoveWindows);
+ }
+}
+
+} // anonymous namespace
+
+namespace {
+
+void collectUIInformation(std::map<OUString, OUString>&& aParameters, const OUString& rAction)
+{
+ EventDescription aDescription;
+ aDescription.aID = "grid_window";
+ aDescription.aAction = rAction;
+ aDescription.aParameters = std::move(aParameters);
+ aDescription.aParent = "MainWindow";
+ aDescription.aKeyWord = "ScGridWinUIObject";
+
+ UITestLogger::getInstance().logEvent(aDescription);
+}
+}
+
+const ScPositionHelper::index_type ScPositionHelper::null; // definition
+
+bool ScPositionHelper::Comp::operator() (const value_type& rValue1, const value_type& rValue2) const
+{
+ if (rValue1.first == null || rValue2.first == null)
+ {
+ return rValue1.second < rValue2.second;
+ }
+ else
+ {
+ return rValue1.first < rValue2.first;
+ }
+}
+
+ScPositionHelper::ScPositionHelper(const ScDocument *pDoc, bool bColumn)
+ : MAX_INDEX(bColumn ? (pDoc ? pDoc->MaxCol() : -1) : MAXTILEDROW)
+{
+ mData.insert(std::make_pair(-1, 0));
+}
+
+void ScPositionHelper::setDocument(const ScDocument& rDoc, bool bColumn)
+{
+ MAX_INDEX = bColumn ? rDoc.MaxCol() : MAXTILEDROW;
+}
+
+void ScPositionHelper::insert(index_type nIndex, tools::Long nPos)
+{
+ if (nIndex < 0) return;
+ SAL_INFO("sc.lok.poshelper", "ScPositionHelper::insert: nIndex: "
+ << nIndex << ", nPos: " << nPos << ", size: " << mData.size());
+ value_type aValue = std::make_pair(nIndex, nPos);
+ mData.erase(aValue);
+ mData.insert(aValue);
+ SAL_INFO("sc.lok.poshelper",
+ "ScPositionHelper::insert: after insert: size: " << mData.size());
+}
+
+void ScPositionHelper::removeByIndex(index_type nIndex)
+{
+ if (nIndex < 0)
+ return;
+ SAL_INFO("sc.lok.poshelper", "ScPositionHelper::remove: nIndex: " << nIndex
+ << ", size: " << mData.size());
+ auto it = mData.find(std::make_pair(nIndex, 0));
+ if (it == mData.end()) return;
+ mData.erase(it);
+ SAL_INFO("sc.lok.poshelper",
+ "ScPositionHelper::remove: after erase: size: " << mData.size());
+}
+
+void ScPositionHelper::invalidateByIndex(index_type nIndex)
+{
+ SAL_INFO("sc.lok.poshelper", "ScPositionHelper::invalidate: nIndex: " << nIndex);
+ if (nIndex < 0)
+ {
+ mData.clear();
+ mData.insert(std::make_pair(-1, 0));
+ }
+ else
+ {
+ auto it = mData.lower_bound(std::make_pair(nIndex, 0));
+ mData.erase(it, mData.end());
+ }
+}
+
+void ScPositionHelper::invalidateByPosition(tools::Long nPos)
+{
+ SAL_INFO("sc.lok.poshelper", "ScPositionHelper::invalidate: nPos: " << nPos);
+ if (nPos <= 0)
+ {
+ mData.clear();
+ mData.insert(std::make_pair(-1, 0));
+ }
+ else
+ {
+ auto it = mData.lower_bound(std::make_pair(null, nPos));
+ mData.erase(it, mData.end());
+ }
+}
+
+const ScPositionHelper::value_type&
+ScPositionHelper::getNearestByIndex(index_type nIndex) const
+{
+ SAL_INFO("sc.lok.poshelper",
+ "ScPositionHelper::getNearest: nIndex: " << nIndex << ", size: " << mData.size());
+ auto posUB = mData.upper_bound(std::make_pair(nIndex, 0));
+ if (posUB == mData.begin())
+ {
+ return *posUB;
+ }
+
+ auto posLB = std::prev(posUB);
+ // coverity[copy_paste_error : FALSE] - posUB is correct
+ if (posUB == mData.end())
+ {
+ return *posLB;
+ }
+
+ tools::Long nDiffUB = posUB->first - nIndex;
+ tools::Long nDiffLB = posLB->first - nIndex;
+ if (nDiffUB < -nDiffLB)
+ {
+ return *posUB;
+ }
+ else
+ {
+ return *posLB;
+ }
+}
+
+const ScPositionHelper::value_type&
+ScPositionHelper::getNearestByPosition(tools::Long nPos) const
+{
+ SAL_INFO("sc.lok.poshelper",
+ "ScPositionHelper::getNearest: nPos: " << nPos << ", size: " << mData.size());
+ auto posUB = mData.upper_bound(std::make_pair(null, nPos));
+
+ if (posUB == mData.begin())
+ {
+ return *posUB;
+ }
+
+ auto posLB = std::prev(posUB);
+ // coverity[copy_paste_error : FALSE] - posUB is correct
+ if (posUB == mData.end())
+ {
+ return *posLB;
+ }
+
+ tools::Long nDiffUB = posUB->second - nPos;
+ tools::Long nDiffLB = posLB->second - nPos;
+
+ if (nDiffUB < -nDiffLB)
+ {
+ return *posUB;
+ }
+ else
+ {
+ return *posLB;
+ }
+}
+
+tools::Long ScPositionHelper::getPosition(index_type nIndex) const
+{
+ auto it = mData.find(std::make_pair(nIndex, 0));
+ if (it == mData.end()) return -1;
+ return it->second;
+}
+
+tools::Long ScPositionHelper::computePosition(index_type nIndex, const std::function<long (index_type)>& getSizePx)
+{
+ assert(MAX_INDEX > 0);
+ if (nIndex < 0) nIndex = 0;
+ if (nIndex > MAX_INDEX) nIndex = MAX_INDEX;
+
+ const auto& rNearest = getNearestByIndex(nIndex);
+ index_type nStartIndex = rNearest.first;
+ tools::Long nTotalPixels = rNearest.second;
+
+ if (nStartIndex < nIndex)
+ {
+ for (index_type nIdx = nStartIndex + 1; nIdx <= nIndex; ++nIdx)
+ {
+ nTotalPixels += getSizePx(nIdx);
+ }
+ }
+ else
+ {
+ for (index_type nIdx = nStartIndex; nIdx > nIndex; --nIdx)
+ {
+ nTotalPixels -= getSizePx(nIdx);
+ }
+ }
+ return nTotalPixels;
+}
+
+ScBoundsProvider::ScBoundsProvider(const ScViewData &rView, SCTAB nT, bool bColHeader)
+ : rDoc(rView.GetDocument())
+ , nTab(nT)
+ , bColumnHeader(bColHeader)
+ , MAX_INDEX(bColHeader ? rDoc.MaxCol() : MAXTILEDROW)
+ , mfPPTX(rView.GetPPTX())
+ , mfPPTY(rView.GetPPTY())
+ , nFirstIndex(-1)
+ , nSecondIndex(-1)
+ , nFirstPositionPx(-1)
+ , nSecondPositionPx(-1)
+{}
+
+void ScBoundsProvider::GetStartIndexAndPosition(SCCOL& nIndex, tools::Long& nPosition) const
+{
+ assert(bColumnHeader);
+ nIndex = nFirstIndex;
+ nPosition = nFirstPositionPx;
+}
+
+void ScBoundsProvider::GetEndIndexAndPosition(SCCOL& nIndex, tools::Long& nPosition) const
+{
+ assert(bColumnHeader);
+ nIndex = nSecondIndex;
+ nPosition = nSecondPositionPx;
+}
+
+void ScBoundsProvider::GetStartIndexAndPosition(SCROW& nIndex, tools::Long& nPosition) const
+{
+ assert(!bColumnHeader);
+ nIndex = nFirstIndex;
+ nPosition = nFirstPositionPx;
+}
+
+void ScBoundsProvider::GetEndIndexAndPosition(SCROW& nIndex, tools::Long& nPosition) const
+{
+ assert(!bColumnHeader);
+ nIndex = nSecondIndex;
+ nPosition = nSecondPositionPx;
+}
+
+tools::Long ScBoundsProvider::GetSize(index_type nIndex) const
+{
+ const sal_uInt16 nSize = bColumnHeader ? rDoc.GetColWidth(nIndex, nTab) : rDoc.GetRowHeight(nIndex, nTab);
+ return ScViewData::ToPixel(nSize, bColumnHeader ? mfPPTX : mfPPTY);
+}
+
+void ScBoundsProvider::GetIndexAndPos(index_type nNearestIndex, tools::Long nNearestPosition,
+ tools::Long nBound, index_type& nFoundIndex, tools::Long& nPosition,
+ bool bTowards, tools::Long nDiff)
+{
+ if (nDiff > 0) // nBound < nNearestPosition
+ GeIndexBackwards(nNearestIndex, nNearestPosition, nBound,
+ nFoundIndex, nPosition, bTowards);
+ else
+ GetIndexTowards(nNearestIndex, nNearestPosition, nBound,
+ nFoundIndex, nPosition, bTowards);
+}
+
+void ScBoundsProvider::Compute(
+ value_type aFirstNearest, value_type aSecondNearest,
+ tools::Long nFirstBound, tools::Long nSecondBound)
+{
+ SAL_INFO("sc.lok.header", "BoundsProvider: nFirstBound: " << nFirstBound
+ << ", nSecondBound: " << nSecondBound);
+
+ tools::Long nFirstDiff = aFirstNearest.second - nFirstBound;
+ tools::Long nSecondDiff = aSecondNearest.second - nSecondBound;
+ SAL_INFO("sc.lok.header", "BoundsProvider: rTopNearest: index: " << aFirstNearest.first
+ << ", pos: " << aFirstNearest.second << ", diff: " << nFirstDiff);
+ SAL_INFO("sc.lok.header", "BoundsProvider: rBottomNearest: index: " << aSecondNearest.first
+ << ", pos: " << aSecondNearest.second << ", diff: " << nSecondDiff);
+
+ bool bReverse = (std::abs(nFirstDiff) >= std::abs(nSecondDiff));
+
+ if(bReverse)
+ {
+ std::swap(aFirstNearest, aSecondNearest);
+ std::swap(nFirstBound, nSecondBound);
+ std::swap(nFirstDiff, nSecondDiff);
+ }
+
+ index_type nNearestIndex = aFirstNearest.first;
+ tools::Long nNearestPosition = aFirstNearest.second;
+ SAL_INFO("sc.lok.header", "BoundsProvider: nearest to first bound: nNearestIndex: "
+ << nNearestIndex << ", nNearestPosition: " << nNearestPosition);
+
+ GetIndexAndPos(nNearestIndex, nNearestPosition, nFirstBound,
+ nFirstIndex, nFirstPositionPx, !bReverse, nFirstDiff);
+ SAL_INFO("sc.lok.header", "BoundsProvider: nFirstIndex: " << nFirstIndex
+ << ", nFirstPositionPx: " << nFirstPositionPx);
+
+ if (std::abs(nSecondDiff) < std::abs(nSecondBound - nFirstPositionPx))
+ {
+ nNearestIndex = aSecondNearest.first;
+ nNearestPosition = aSecondNearest.second;
+ }
+ else
+ {
+ nNearestPosition = nFirstPositionPx;
+ nNearestIndex = nFirstIndex;
+ nSecondDiff = !bReverse ? -1 : 1;
+ }
+ SAL_INFO("sc.lok.header", "BoundsProvider: nearest to second bound: nNearestIndex: "
+ << nNearestIndex << ", nNearestPosition: " << nNearestPosition
+ << ", diff: " << nSecondDiff);
+
+ GetIndexAndPos(nNearestIndex, nNearestPosition, nSecondBound,
+ nSecondIndex, nSecondPositionPx, bReverse, nSecondDiff);
+ SAL_INFO("sc.lok.header", "BoundsProvider: nSecondIndex: " << nSecondIndex
+ << ", nSecondPositionPx: " << nSecondPositionPx);
+
+ if (bReverse)
+ {
+ std::swap(nFirstIndex, nSecondIndex);
+ std::swap(nFirstPositionPx, nSecondPositionPx);
+ }
+}
+
+void ScBoundsProvider::EnlargeStartBy(tools::Long nOffset)
+{
+ const index_type nNewFirstIndex =
+ std::max(static_cast<index_type>(-1),
+ static_cast<index_type>(nFirstIndex - nOffset));
+ for (index_type nIndex = nFirstIndex; nIndex > nNewFirstIndex; --nIndex)
+ {
+ const tools::Long nSizePx = GetSize(nIndex);
+ nFirstPositionPx -= nSizePx;
+ }
+ nFirstIndex = nNewFirstIndex;
+ SAL_INFO("sc.lok.header", "BoundsProvider: added offset: nFirstIndex: " << nFirstIndex
+ << ", nFirstPositionPx: " << nFirstPositionPx);
+}
+
+void ScBoundsProvider::EnlargeEndBy(tools::Long nOffset)
+{
+ const index_type nNewSecondIndex = std::min(MAX_INDEX, static_cast<index_type>(nSecondIndex + nOffset));
+ for (index_type nIndex = nSecondIndex + 1; nIndex <= nNewSecondIndex; ++nIndex)
+ {
+ const tools::Long nSizePx = GetSize(nIndex);
+ nSecondPositionPx += nSizePx;
+ }
+ nSecondIndex = nNewSecondIndex;
+ SAL_INFO("sc.lok.header", "BoundsProvider: added offset: nSecondIndex: " << nSecondIndex
+ << ", nSecondPositionPx: " << nSecondPositionPx);
+}
+
+void ScBoundsProvider::GeIndexBackwards(
+ index_type nNearestIndex, tools::Long nNearestPosition,
+ tools::Long nBound, index_type& nFoundIndex, tools::Long& nPosition, bool bTowards)
+{
+ nFoundIndex = -1;
+ for (index_type nIndex = nNearestIndex; nIndex >= 0; --nIndex)
+ {
+ if (nBound >= nNearestPosition)
+ {
+ nFoundIndex = nIndex; // last index whose nPosition is less than nBound
+ nPosition = nNearestPosition;
+ break;
+ }
+
+ const tools::Long nSizePx = GetSize(nIndex);
+ nNearestPosition -= nSizePx;
+ }
+ if (!bTowards && nFoundIndex != -1)
+ {
+ nFoundIndex += 1;
+ nPosition += GetSize(nFoundIndex);
+ }
+}
+
+void ScBoundsProvider::GetIndexTowards(
+ index_type nNearestIndex, tools::Long nNearestPosition,
+ tools::Long nBound, index_type& nFoundIndex, tools::Long& nPosition, bool bTowards)
+{
+ nFoundIndex = -2;
+ for (index_type nIndex = nNearestIndex + 1; nIndex <= MAX_INDEX; ++nIndex)
+ {
+ const tools::Long nSizePx = GetSize(nIndex);
+ nNearestPosition += nSizePx;
+
+ if (nNearestPosition > nBound)
+ {
+ nFoundIndex = nIndex; // first index whose nPosition is greater than nBound
+ nPosition = nNearestPosition;
+ break;
+ }
+ }
+ if (nFoundIndex == -2)
+ {
+ nFoundIndex = MAX_INDEX;
+ nPosition = nNearestPosition;
+ }
+ else if (bTowards)
+ {
+ nPosition -= GetSize(nFoundIndex);
+ nFoundIndex -= 1;
+ }
+}
+
+ScViewDataTable::ScViewDataTable(const ScDocument *pDoc) :
+ eZoomType( SvxZoomType::PERCENT ),
+ aZoomX( 1,1 ),
+ aZoomY( 1,1 ),
+ aPageZoomX( 3,5 ), // Page-Default: 60%
+ aPageZoomY( 3,5 ),
+ nHSplitPos( 0 ),
+ nVSplitPos( 0 ),
+ eHSplitMode( SC_SPLIT_NONE ),
+ eVSplitMode( SC_SPLIT_NONE ),
+ eWhichActive( SC_SPLIT_BOTTOMLEFT ),
+ nFixPosX( 0 ),
+ nFixPosY( 0 ),
+ nCurX( 0 ),
+ nCurY( 0 ),
+ nOldCurX( 0 ),
+ nOldCurY( 0 ),
+ aWidthHelper(pDoc, true),
+ aHeightHelper(pDoc, false),
+ nMaxTiledCol( 20 ),
+ nMaxTiledRow( 50 ),
+ bShowGrid( true ),
+ mbOldCursorValid( false )
+{
+ nPosX[0]=nPosX[1]=0;
+ nPosY[0]=nPosY[1]=0;
+ nTPosX[0]=nTPosX[1]=0;
+ nTPosY[0]=nTPosY[1]=0;
+ nMPosX[0]=nMPosX[1]=0;
+ nMPosY[0]=nMPosY[1]=0;
+ nPixPosX[0]=nPixPosX[1]=0;
+ nPixPosY[0]=nPixPosY[1]=0;
+}
+
+void ScViewDataTable::InitData(const ScDocument& rDoc)
+{
+ aWidthHelper.setDocument(rDoc, true);
+ aHeightHelper.setDocument(rDoc, false);
+}
+
+void ScViewDataTable::WriteUserDataSequence(uno::Sequence <beans::PropertyValue>& rSettings, const ScViewData& rViewData, SCTAB nTab) const
+{
+ rSettings.realloc(SC_TABLE_VIEWSETTINGS_COUNT);
+ beans::PropertyValue* pSettings = rSettings.getArray();
+
+ ScSplitMode eExHSplitMode = eHSplitMode;
+ ScSplitMode eExVSplitMode = eVSplitMode;
+ SCCOL nExFixPosX = nFixPosX;
+ SCROW nExFixPosY = nFixPosY;
+ tools::Long nExHSplitPos = nHSplitPos;
+ tools::Long nExVSplitPos = nVSplitPos;
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ rViewData.OverrideWithLOKFreeze(eExHSplitMode, eExVSplitMode,
+ nExFixPosX, nExFixPosY,
+ nExHSplitPos, nExVSplitPos, nTab);
+ }
+
+ pSettings[SC_CURSOR_X].Name = SC_CURSORPOSITIONX;
+ pSettings[SC_CURSOR_X].Value <<= sal_Int32(nCurX);
+ pSettings[SC_CURSOR_Y].Name = SC_CURSORPOSITIONY;
+ pSettings[SC_CURSOR_Y].Value <<= sal_Int32(nCurY);
+
+ // Write freezepan data only when freeze pans are set
+ if(nExFixPosX != 0 || nExFixPosY != 0 || nExHSplitPos != 0 || nExVSplitPos != 0)
+ {
+ pSettings[SC_HORIZONTAL_SPLIT_MODE].Name = SC_HORIZONTALSPLITMODE;
+ pSettings[SC_HORIZONTAL_SPLIT_MODE].Value <<= sal_Int16(eExHSplitMode);
+ pSettings[SC_VERTICAL_SPLIT_MODE].Name = SC_VERTICALSPLITMODE;
+ pSettings[SC_VERTICAL_SPLIT_MODE].Value <<= sal_Int16(eExVSplitMode);
+ pSettings[SC_HORIZONTAL_SPLIT_POSITION].Name = SC_HORIZONTALSPLITPOSITION;
+ if (eExHSplitMode == SC_SPLIT_FIX)
+ pSettings[SC_HORIZONTAL_SPLIT_POSITION].Value <<= sal_Int32(nExFixPosX);
+ else
+ pSettings[SC_HORIZONTAL_SPLIT_POSITION].Value <<= sal_Int32(nExHSplitPos);
+ pSettings[SC_VERTICAL_SPLIT_POSITION].Name = SC_VERTICALSPLITPOSITION;
+ if (eExVSplitMode == SC_SPLIT_FIX)
+ pSettings[SC_VERTICAL_SPLIT_POSITION].Value <<= sal_Int32(nExFixPosY);
+ else
+ pSettings[SC_VERTICAL_SPLIT_POSITION].Value <<= sal_Int32(nExVSplitPos);
+ }
+
+ // Prevent writing odd settings that would make crash versions that
+ // don't apply SanitizeWhichActive() when reading the settings.
+ // See tdf#117093
+ const ScSplitPos eActiveSplitRange = SanitizeWhichActive();
+ // And point out to give us a chance to inspect weird things (if anyone
+ // remembers what s/he did).
+ assert(eWhichActive == eActiveSplitRange);
+ pSettings[SC_ACTIVE_SPLIT_RANGE].Name = SC_ACTIVESPLITRANGE;
+ pSettings[SC_ACTIVE_SPLIT_RANGE].Value <<= sal_Int16(eActiveSplitRange);
+ pSettings[SC_POSITION_LEFT].Name = SC_POSITIONLEFT;
+ pSettings[SC_POSITION_LEFT].Value <<= sal_Int32(nPosX[SC_SPLIT_LEFT]);
+ pSettings[SC_POSITION_RIGHT].Name = SC_POSITIONRIGHT;
+ pSettings[SC_POSITION_RIGHT].Value <<= sal_Int32(nPosX[SC_SPLIT_RIGHT]);
+ pSettings[SC_POSITION_TOP].Name = SC_POSITIONTOP;
+ pSettings[SC_POSITION_TOP].Value <<= sal_Int32(nPosY[SC_SPLIT_TOP]);
+ pSettings[SC_POSITION_BOTTOM].Name = SC_POSITIONBOTTOM;
+ pSettings[SC_POSITION_BOTTOM].Value <<= sal_Int32(nPosY[SC_SPLIT_BOTTOM]);
+
+ sal_Int32 nZoomValue = tools::Long(aZoomY * 100);
+ sal_Int32 nPageZoomValue = tools::Long(aPageZoomY * 100);
+ pSettings[SC_TABLE_ZOOM_TYPE].Name = SC_ZOOMTYPE;
+ pSettings[SC_TABLE_ZOOM_TYPE].Value <<= sal_Int16(eZoomType);
+ pSettings[SC_TABLE_ZOOM_VALUE].Name = SC_ZOOMVALUE;
+ pSettings[SC_TABLE_ZOOM_VALUE].Value <<= nZoomValue;
+ pSettings[SC_TABLE_PAGE_VIEW_ZOOM_VALUE].Name = SC_PAGEVIEWZOOMVALUE;
+ pSettings[SC_TABLE_PAGE_VIEW_ZOOM_VALUE].Value <<= nPageZoomValue;
+
+ pSettings[SC_TABLE_SHOWGRID].Name = SC_UNO_SHOWGRID;
+ pSettings[SC_TABLE_SHOWGRID].Value <<= bShowGrid;
+
+ // Common SdrModel processing
+ rViewData.GetDocument().GetDrawLayer()->WriteUserDataSequence(rSettings);
+}
+
+void ScViewDataTable::ReadUserDataSequence(const uno::Sequence <beans::PropertyValue>& aSettings, ScViewData& rViewData, SCTAB nTab, bool& rHasZoom )
+{
+ rHasZoom = false;
+
+ sal_Int32 nTemp32(0);
+ sal_Int16 nTemp16(0);
+ sal_Int32 nTempPosV(0);
+ sal_Int32 nTempPosH(0);
+ sal_Int32 nTempPosVTw(0);
+ sal_Int32 nTempPosHTw(0);
+ bool bHasVSplitInTwips = false;
+ bool bHasHSplitInTwips = false;
+ for (const auto& rSetting : aSettings)
+ {
+ OUString sName(rSetting.Name);
+ if (sName == SC_CURSORPOSITIONX)
+ {
+ rSetting.Value >>= nTemp32;
+ nCurX = rViewData.GetDocument().SanitizeCol( static_cast<SCCOL>(nTemp32));
+ }
+ else if (sName == SC_CURSORPOSITIONY)
+ {
+ rSetting.Value >>= nTemp32;
+ nCurY = rViewData.GetDocument().SanitizeRow( static_cast<SCROW>(nTemp32));
+ }
+ else if (sName == SC_HORIZONTALSPLITMODE)
+ {
+ if ((rSetting.Value >>= nTemp16) && nTemp16 <= ScSplitMode::SC_SPLIT_MODE_MAX_ENUM)
+ eHSplitMode = static_cast<ScSplitMode>(nTemp16);
+ }
+ else if (sName == SC_VERTICALSPLITMODE)
+ {
+ if ((rSetting.Value >>= nTemp16) && nTemp16 <= ScSplitMode::SC_SPLIT_MODE_MAX_ENUM)
+ eVSplitMode = static_cast<ScSplitMode>(nTemp16);
+ }
+ else if (sName == SC_HORIZONTALSPLITPOSITION)
+ {
+ rSetting.Value >>= nTempPosH;
+ bHasHSplitInTwips = false;
+ }
+ else if (sName == SC_VERTICALSPLITPOSITION)
+ {
+ rSetting.Value >>= nTempPosV;
+ bHasVSplitInTwips = false;
+ }
+ else if (sName == SC_HORIZONTALSPLITPOSITION_TWIPS)
+ {
+ rSetting.Value >>= nTempPosHTw;
+ bHasHSplitInTwips = true;
+ }
+ else if (sName == SC_VERTICALSPLITPOSITION_TWIPS)
+ {
+ rSetting.Value >>= nTempPosVTw;
+ bHasVSplitInTwips = true;
+ }
+ else if (sName == SC_ACTIVESPLITRANGE)
+ {
+ if ((rSetting.Value >>= nTemp16) && nTemp16 <= ScSplitPos::SC_SPLIT_POS_MAX_ENUM)
+ eWhichActive = static_cast<ScSplitPos>(nTemp16);
+ }
+ else if (sName == SC_POSITIONLEFT)
+ {
+ rSetting.Value >>= nTemp32;
+ nPosX[SC_SPLIT_LEFT] = rViewData.GetDocument().SanitizeCol( static_cast<SCCOL>(nTemp32));
+ }
+ else if (sName == SC_POSITIONRIGHT)
+ {
+ rSetting.Value >>= nTemp32;
+ nPosX[SC_SPLIT_RIGHT] = rViewData.GetDocument().SanitizeCol( static_cast<SCCOL>(nTemp32));
+ }
+ else if (sName == SC_POSITIONTOP)
+ {
+ rSetting.Value >>= nTemp32;
+ nPosY[SC_SPLIT_TOP] = rViewData.GetDocument().SanitizeRow( static_cast<SCROW>(nTemp32));
+ }
+ else if (sName == SC_POSITIONBOTTOM)
+ {
+ rSetting.Value >>= nTemp32;
+ nPosY[SC_SPLIT_BOTTOM] = rViewData.GetDocument().SanitizeRow( static_cast<SCROW>(nTemp32));
+ }
+ else if (sName == SC_ZOOMTYPE)
+ {
+ rSetting.Value >>= nTemp16;
+ eZoomType = SvxZoomType(nTemp16);
+ rHasZoom = true; // set if there is any zoom information
+ }
+ else if (sName == SC_ZOOMVALUE)
+ {
+ rSetting.Value >>= nTemp32;
+ Fraction aZoom(nTemp32, 100);
+ aZoomX = aZoomY = aZoom;
+ rHasZoom = true;
+ }
+ else if (sName == SC_PAGEVIEWZOOMVALUE)
+ {
+ rSetting.Value >>= nTemp32;
+ Fraction aZoom(nTemp32, 100);
+ aPageZoomX = aPageZoomY = aZoom;
+ rHasZoom = true;
+ }
+ else if (sName == SC_UNO_SHOWGRID)
+ {
+ rSetting.Value >>= bShowGrid;
+ }
+ else if (sName == SC_TABLESELECTED)
+ {
+ bool bSelected = false;
+ rSetting.Value >>= bSelected;
+ rViewData.GetMarkData().SelectTable( nTab, bSelected );
+ }
+ else if (sName == SC_UNONAME_TABCOLOR)
+ {
+ // There are documents out there that have their tab color defined as a view setting.
+ Color aColor = COL_AUTO;
+ rSetting.Value >>= aColor;
+ if (aColor != COL_AUTO)
+ {
+ ScDocument& rDoc = rViewData.GetDocument();
+ rDoc.SetTabBgColor(nTab, aColor);
+ }
+ }
+ // Fallback to common SdrModel processing
+ else rViewData.GetDocument().GetDrawLayer()->ReadUserDataSequenceValue(&rSetting);
+ }
+
+ if (eHSplitMode == SC_SPLIT_FIX)
+ nFixPosX = rViewData.GetDocument().SanitizeCol( static_cast<SCCOL>( bHasHSplitInTwips ? nTempPosHTw : nTempPosH ));
+ else
+ nHSplitPos = bHasHSplitInTwips ? static_cast< tools::Long >( nTempPosHTw * rViewData.GetPPTX() ) : nTempPosH;
+
+ if (eVSplitMode == SC_SPLIT_FIX)
+ nFixPosY = rViewData.GetDocument().SanitizeRow( static_cast<SCROW>( bHasVSplitInTwips ? nTempPosVTw : nTempPosV ));
+ else
+ nVSplitPos = bHasVSplitInTwips ? static_cast< tools::Long >( nTempPosVTw * rViewData.GetPPTY() ) : nTempPosV;
+
+ eWhichActive = SanitizeWhichActive();
+}
+
+ScSplitPos ScViewDataTable::SanitizeWhichActive() const
+{
+ if ((WhichH(eWhichActive) == SC_SPLIT_RIGHT && eHSplitMode == SC_SPLIT_NONE) ||
+ (WhichV(eWhichActive) == SC_SPLIT_TOP && eVSplitMode == SC_SPLIT_NONE))
+ {
+ SAL_WARN("sc.ui","ScViewDataTable::SanitizeWhichActive - bad eWhichActive " << eWhichActive);
+ // The default always initialized grid window is SC_SPLIT_BOTTOMLEFT.
+ return SC_SPLIT_BOTTOMLEFT;
+ }
+ return eWhichActive;
+}
+
+ScViewData::ScViewData(ScDocShell& rDocSh, ScTabViewShell* pViewSh)
+ : ScViewData(nullptr, &rDocSh, pViewSh)
+{
+}
+
+ScViewData::ScViewData(ScDocument& rDoc)
+ : ScViewData(&rDoc, nullptr, nullptr)
+{
+}
+
+static ScViewOptions DefaultOptions()
+{
+ ScViewOptions aOptions;
+ aOptions.SetOption(VOPT_GRID, true);
+ aOptions.SetOption(VOPT_SYNTAX, false);
+ aOptions.SetOption(VOPT_HEADER, true);
+ aOptions.SetOption(VOPT_TABCONTROLS, true);
+ aOptions.SetOption(VOPT_VSCROLL, true);
+ aOptions.SetOption(VOPT_HSCROLL, true);
+ aOptions.SetOption(VOPT_OUTLINER, true);
+ return aOptions;
+}
+
+// Either pDoc or pDocSh must be valid
+ScViewData::ScViewData(ScDocument* pDoc, ScDocShell* pDocSh, ScTabViewShell* pViewSh) :
+ nPPTX(0.0),
+ nPPTY(0.0),
+ maMarkData (pDocSh ? pDocSh->GetDocument().GetSheetLimits() : pDoc->GetSheetLimits()),
+ maHighlightData (pDocSh ? pDocSh->GetDocument().GetSheetLimits() : pDoc->GetSheetLimits()),
+ pDocShell ( pDocSh ),
+ mrDoc (pDocSh ? pDocSh->GetDocument() : *pDoc),
+ pView ( pViewSh ),
+ maOptions (pDocSh ? pDocSh->GetDocument().GetViewOptions() : DefaultOptions()),
+ pSpellingView ( nullptr ),
+ aLogicMode ( MapUnit::Map100thMM ),
+ eDefZoomType( SvxZoomType::PERCENT ),
+ aDefZoomX ( 1,1 ),
+ aDefZoomY ( 1,1 ),
+ aDefPageZoomX( 3,5 ),
+ aDefPageZoomY( 3,5 ),
+ eRefType ( SC_REFTYPE_NONE ),
+ nTabNo ( 0 ),
+ nRefTabNo ( 0 ),
+ nRefStartX(0),
+ nRefStartY(0),
+ nRefStartZ(0),
+ nRefEndX(0),
+ nRefEndY(0),
+ nRefEndZ(0),
+ nFillStartX(0),
+ nFillStartY(0),
+ nFillEndX(0),
+ nFillEndY(0),
+ nPasteFlags ( ScPasteFlags::NONE ),
+ eEditActivePart( SC_SPLIT_BOTTOMLEFT ),
+ nFillMode ( ScFillMode::NONE ),
+ eEditAdjust ( SvxAdjust::Left ),
+ bActive ( true ), // how to initialize?
+ bIsRefMode ( false ),
+ bDelMarkValid( false ),
+ bPagebreak ( false ),
+ bSelCtrlMouseClick( false ),
+ bMoveArea ( false ),
+ bGrowing (false),
+ nFormulaBarLines(1),
+ m_nLOKPageUpDownOffset( 0 )
+{
+ assert(bool(pDoc) != bool(pDocSh)); // either one or the other, not both
+ maMarkData.SelectOneTable(0); // Sync with nTabNo
+
+ aScrSize = Size( o3tl::convert(STD_COL_WIDTH * OLE_STD_CELLS_X, o3tl::Length::twip, o3tl::Length::px),
+ o3tl::convert(mrDoc.GetSheetOptimalMinRowHeight(nTabNo) * OLE_STD_CELLS_Y,
+ o3tl::Length::twip, o3tl::Length::px));
+ maTabData.emplace_back( new ScViewDataTable(nullptr) );
+ pThisTab = maTabData[nTabNo].get();
+
+ nEditEndCol = nEditStartCol = nEditCol = 0;
+ nEditEndRow = nEditRow = 0;
+ nTabStartCol = SC_TABSTART_NONE;
+
+ // don't show hidden tables
+ if (!mrDoc.IsVisible(nTabNo))
+ {
+ while (!mrDoc.IsVisible(nTabNo) && mrDoc.HasTable(nTabNo + 1))
+ {
+ ++nTabNo;
+ maTabData.emplace_back(nullptr);
+ }
+ maTabData[nTabNo].reset( new ScViewDataTable(nullptr) );
+ pThisTab = maTabData[nTabNo].get();
+ }
+
+ SCTAB nTableCount = mrDoc.GetTableCount();
+ EnsureTabDataSize(nTableCount);
+
+ for (auto& xTabData : maTabData)
+ {
+ if (xTabData)
+ xTabData->InitData(mrDoc);
+ }
+
+ CalcPPT();
+}
+
+ScViewData::~ScViewData() COVERITY_NOEXCEPT_FALSE
+{
+ KillEditView();
+}
+
+ScDBFunc* ScViewData::GetView() const { return pView; }
+
+void ScViewData::UpdateCurrentTab()
+{
+ assert(0 <= nTabNo && o3tl::make_unsigned(nTabNo) < maTabData.size());
+ pThisTab = maTabData[nTabNo].get();
+ while (!pThisTab)
+ {
+ if (nTabNo > 0)
+ pThisTab = maTabData[--nTabNo].get();
+ else
+ {
+ maTabData[0].reset(new ScViewDataTable(&mrDoc));
+ pThisTab = maTabData[0].get();
+ }
+ }
+}
+
+void ScViewData::InsertTab( SCTAB nTab )
+{
+ if( nTab >= static_cast<SCTAB>(maTabData.size()))
+ maTabData.resize(nTab+1);
+ else
+ maTabData.insert( maTabData.begin() + nTab, nullptr );
+ CreateTabData( nTab );
+
+ UpdateCurrentTab();
+ maMarkData.InsertTab(nTab);
+
+ collectUIInformation({{}}, "InsertTab");
+}
+
+void ScViewData::InsertTabs( SCTAB nTab, SCTAB nNewSheets )
+{
+ if (nTab >= static_cast<SCTAB>(maTabData.size()))
+ maTabData.resize(nTab+nNewSheets);
+ else
+ {
+ // insert nNewSheets new tables at position nTab
+ auto prevSize = maTabData.size();
+ maTabData.resize(prevSize + nNewSheets);
+ std::move_backward(maTabData.begin() + nTab, maTabData.begin() + prevSize, maTabData.end());
+ }
+ for (SCTAB i = nTab; i < nTab + nNewSheets; ++i)
+ {
+ CreateTabData( i );
+ maMarkData.InsertTab(i);
+ }
+ UpdateCurrentTab();
+}
+
+void ScViewData::DeleteTab( SCTAB nTab )
+{
+ assert(nTab < static_cast<SCTAB>(maTabData.size()));
+ maTabData.erase(maTabData.begin() + nTab);
+
+ if (o3tl::make_unsigned(nTabNo) >= maTabData.size())
+ {
+ EnsureTabDataSize(1);
+ nTabNo = maTabData.size() - 1;
+ }
+ UpdateCurrentTab();
+ maMarkData.DeleteTab(nTab);
+}
+
+void ScViewData::DeleteTabs( SCTAB nTab, SCTAB nSheets )
+{
+ for (SCTAB i = 0; i < nSheets; ++i)
+ {
+ maMarkData.DeleteTab(nTab + i);
+ }
+ maTabData.erase(maTabData.begin() + nTab, maTabData.begin()+ nTab+nSheets);
+ if (o3tl::make_unsigned(nTabNo) >= maTabData.size())
+ {
+ EnsureTabDataSize(1);
+ nTabNo = maTabData.size() - 1;
+ }
+ UpdateCurrentTab();
+}
+
+void ScViewData::CopyTab( SCTAB nSrcTab, SCTAB nDestTab )
+{
+ if (nDestTab==SC_TAB_APPEND)
+ nDestTab = mrDoc.GetTableCount() - 1; // something had to have been copied
+
+ if (nDestTab > MAXTAB)
+ {
+ OSL_FAIL("too many sheets");
+ return;
+ }
+
+ if (nSrcTab >= static_cast<SCTAB>(maTabData.size()))
+ OSL_FAIL("pTabData out of bounds, FIX IT");
+
+ EnsureTabDataSize(nDestTab + 1);
+
+ if ( maTabData[nSrcTab] )
+ maTabData.emplace(maTabData.begin() + nDestTab, new ScViewDataTable( *maTabData[nSrcTab] ));
+ else
+ maTabData.insert(maTabData.begin() + nDestTab, nullptr);
+
+ UpdateCurrentTab();
+ maMarkData.InsertTab(nDestTab);
+}
+
+void ScViewData::MoveTab( SCTAB nSrcTab, SCTAB nDestTab )
+{
+ if (nDestTab==SC_TAB_APPEND)
+ nDestTab = mrDoc.GetTableCount() - 1;
+ std::unique_ptr<ScViewDataTable> pTab;
+ if (nSrcTab < static_cast<SCTAB>(maTabData.size()))
+ {
+ pTab = std::move(maTabData[nSrcTab]);
+ maTabData.erase( maTabData.begin() + nSrcTab );
+ }
+
+ if (nDestTab < static_cast<SCTAB>(maTabData.size()))
+ maTabData.insert( maTabData.begin() + nDestTab, std::move(pTab) );
+ else
+ {
+ EnsureTabDataSize(nDestTab + 1);
+ maTabData[nDestTab] = std::move(pTab);
+ }
+
+ UpdateCurrentTab();
+ maMarkData.DeleteTab(nSrcTab);
+ maMarkData.InsertTab(nDestTab); // adapted if needed
+}
+
+void ScViewData::CreateTabData( std::vector< SCTAB >& rvTabs )
+{
+ for ( const auto& rTab : rvTabs )
+ CreateTabData(rTab);
+}
+
+void ScViewData::SetZoomType( SvxZoomType eNew, std::vector< SCTAB >& tabs )
+{
+ bool bAll = tabs.empty();
+
+ if ( !bAll ) // create associated table data
+ CreateTabData( tabs );
+
+ if ( bAll )
+ {
+ for ( auto & i: maTabData )
+ {
+ if ( i )
+ i->eZoomType = eNew;
+ }
+ eDefZoomType = eNew;
+ }
+ else
+ {
+ for ( const SCTAB& i : tabs )
+ {
+ if ( i < static_cast<SCTAB>(maTabData.size()) && maTabData[i] )
+ maTabData[i]->eZoomType = eNew;
+ }
+ }
+}
+
+void ScViewData::SetZoomType( SvxZoomType eNew, bool bAll )
+{
+ std::vector< SCTAB > vTabs; // Empty for all tabs
+ if ( !bAll ) // get selected tabs
+ {
+ ScMarkData::const_iterator itr = maMarkData.begin(), itrEnd = maMarkData.end();
+ vTabs.insert(vTabs.begin(), itr, itrEnd);
+ }
+ SetZoomType( eNew, vTabs );
+}
+
+void ScViewData::SetZoom( const Fraction& rNewX, const Fraction& rNewY, std::vector< SCTAB >& tabs )
+{
+ bool bAll = tabs.empty();
+ if ( !bAll ) // create associated table data
+ CreateTabData( tabs );
+
+ // sanity check - we shouldn't need something this low / big
+ SAL_WARN_IF(rNewX < Fraction(1, 100) || rNewX > Fraction(100, 1), "sc.viewdata",
+ "fraction rNewX not sensible: " << static_cast<double>(rNewX));
+ SAL_WARN_IF(rNewY < Fraction(1, 100) || rNewY > Fraction(100, 1), "sc.viewdata",
+ "fraction rNewY not sensible: " << static_cast<double>(rNewY));
+
+ if ( bAll )
+ {
+ for ( auto & i: maTabData )
+ {
+ if ( i )
+ {
+ if ( bPagebreak )
+ {
+ i->aPageZoomX = rNewX;
+ i->aPageZoomY = rNewY;
+ }
+ else
+ {
+ i->aZoomX = rNewX;
+ i->aZoomY = rNewY;
+ }
+ }
+ }
+ if ( bPagebreak )
+ {
+ aDefPageZoomX = rNewX;
+ aDefPageZoomY = rNewY;
+ }
+ else
+ {
+ aDefZoomX = rNewX;
+ aDefZoomY = rNewY;
+ }
+ }
+ else
+ {
+ for ( const SCTAB& i : tabs )
+ {
+ if ( i < static_cast<SCTAB>(maTabData.size()) && maTabData[i] )
+ {
+ if ( bPagebreak )
+ {
+ maTabData[i]->aPageZoomX = rNewX;
+ maTabData[i]->aPageZoomY = rNewY;
+ }
+ else
+ {
+ maTabData[i]->aZoomX = rNewX;
+ maTabData[i]->aZoomY = rNewY;
+ }
+ }
+ }
+ }
+ RefreshZoom();
+}
+
+void ScViewData::SetZoom( const Fraction& rNewX, const Fraction& rNewY, bool bAll )
+{
+ std::vector< SCTAB > vTabs;
+ if ( !bAll ) // get selected tabs
+ {
+ ScMarkData::const_iterator itr = maMarkData.begin(), itrEnd = maMarkData.end();
+ vTabs.insert(vTabs.begin(), itr, itrEnd);
+ }
+ SetZoom( rNewX, rNewY, vTabs );
+}
+
+void ScViewData::SetShowGrid( bool bShow )
+{
+ CreateSelectedTabData();
+ maTabData[nTabNo]->bShowGrid = bShow;
+}
+
+void ScViewData::RefreshZoom()
+{
+ // recalculate zoom-dependent values (only for current sheet)
+
+ CalcPPT();
+ RecalcPixPos();
+ aScenButSize = Size(0,0);
+ aLogicMode.SetScaleX( GetZoomX() );
+ aLogicMode.SetScaleY( GetZoomY() );
+}
+
+void ScViewData::SetPagebreakMode( bool bSet )
+{
+ bPagebreak = bSet;
+
+ RefreshZoom();
+}
+
+ScMarkType ScViewData::GetSimpleArea( ScRange & rRange, ScMarkData & rNewMark ) const
+{
+ ScMarkType eMarkType = SC_MARK_NONE;
+
+ if ( rNewMark.IsMarked() || rNewMark.IsMultiMarked() )
+ {
+ if ( rNewMark.IsMultiMarked() )
+ rNewMark.MarkToSimple();
+
+ if ( rNewMark.IsMarked() && !rNewMark.IsMultiMarked() )
+ {
+ rRange = rNewMark.GetMarkArea();
+ if (ScViewUtil::HasFiltered(rRange, GetDocument()))
+ eMarkType = SC_MARK_SIMPLE_FILTERED;
+ else
+ eMarkType = SC_MARK_SIMPLE;
+ }
+ else
+ eMarkType = SC_MARK_MULTI;
+ }
+ if (eMarkType != SC_MARK_SIMPLE && eMarkType != SC_MARK_SIMPLE_FILTERED)
+ {
+ if (eMarkType == SC_MARK_NONE)
+ eMarkType = SC_MARK_SIMPLE;
+ const ScPatternAttr* pMarkPattern = mrDoc.GetPattern(GetCurX(), GetCurY(), GetTabNo());
+ if (pMarkPattern && pMarkPattern->GetItemSet().GetItemState(ATTR_MERGE, false) == SfxItemState::SET)
+ {
+ SCROW nRow = pMarkPattern->GetItem(ATTR_MERGE).GetRowMerge();
+ SCCOL nCol = pMarkPattern->GetItem(ATTR_MERGE).GetColMerge();
+ if ( nRow < 1 || nCol < 1 )
+ {
+ // This kind of cells do exist. Not sure if that is intended or a bug.
+ rRange = ScRange(GetCurX(), GetCurY(), GetTabNo());
+ }
+ else
+ {
+ rRange = ScRange(GetCurX(), GetCurY(), GetTabNo(),
+ GetCurX() + nCol - 1, GetCurY() + nRow - 1, GetTabNo());
+ if ( ScViewUtil::HasFiltered(rRange, GetDocument()) )
+ eMarkType = SC_MARK_SIMPLE_FILTERED;
+ }
+ }
+ else
+ rRange = ScRange(GetCurX(), GetCurY(), GetTabNo());
+ }
+ return eMarkType;
+}
+
+ScMarkType ScViewData::GetSimpleArea( SCCOL& rStartCol, SCROW& rStartRow, SCTAB& rStartTab,
+ SCCOL& rEndCol, SCROW& rEndRow, SCTAB& rEndTab ) const
+{
+ // parameter bMergeMark is no longer needed: The view's selection is never modified
+ // (a local copy is used), and a multi selection that adds to a single range can always
+ // be treated like a single selection (GetSimpleArea isn't used in selection
+ // handling itself)
+
+ ScRange aRange;
+ ScMarkData aNewMark(maMarkData); // use a local copy for MarkToSimple
+ ScMarkType eMarkType = GetSimpleArea( aRange, aNewMark);
+ aRange.GetVars( rStartCol, rStartRow, rStartTab, rEndCol, rEndRow, rEndTab);
+ return eMarkType;
+}
+
+ScMarkType ScViewData::GetSimpleArea( ScRange& rRange ) const
+{
+ // parameter bMergeMark is no longer needed, see above
+
+ ScMarkData aNewMark(maMarkData); // use a local copy for MarkToSimple
+ return GetSimpleArea( rRange, aNewMark);
+}
+
+void ScViewData::GetMultiArea( ScRangeListRef& rRange ) const
+{
+ // parameter bMergeMark is no longer needed, see GetSimpleArea
+
+ ScMarkData aNewMark(maMarkData); // use a local copy for MarkToSimple
+
+ bool bMulti = aNewMark.IsMultiMarked();
+ if (bMulti)
+ {
+ aNewMark.MarkToSimple();
+ bMulti = aNewMark.IsMultiMarked();
+ }
+ if (bMulti)
+ {
+ rRange = new ScRangeList;
+ aNewMark.FillRangeListWithMarks( rRange.get(), false );
+ }
+ else
+ {
+ ScRange aSimple;
+ GetSimpleArea(aSimple);
+ rRange = new ScRangeList(aSimple);
+ }
+}
+
+bool ScViewData::SimpleColMarked()
+{
+ SCCOL nStartCol;
+ SCROW nStartRow;
+ SCTAB nStartTab;
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ SCTAB nEndTab;
+ if (GetSimpleArea(nStartCol,nStartRow,nStartTab,nEndCol,nEndRow,nEndTab) == SC_MARK_SIMPLE)
+ if (nStartRow == 0 && nEndRow == mrDoc.MaxRow())
+ return true;
+
+ return false;
+}
+
+bool ScViewData::SimpleRowMarked()
+{
+ SCCOL nStartCol;
+ SCROW nStartRow;
+ SCTAB nStartTab;
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ SCTAB nEndTab;
+ if (GetSimpleArea(nStartCol,nStartRow,nStartTab,nEndCol,nEndRow,nEndTab) == SC_MARK_SIMPLE)
+ if (nStartCol == 0 && nEndCol == mrDoc.MaxCol())
+ return true;
+
+ return false;
+}
+
+bool ScViewData::IsMultiMarked() const
+{
+ // Test for "real" multi selection, calling MarkToSimple on a local copy,
+ // and taking filtered in simple area marks into account.
+
+ ScRange aDummy;
+ ScMarkType eType = GetSimpleArea(aDummy);
+ return (eType & SC_MARK_SIMPLE) != SC_MARK_SIMPLE;
+}
+
+bool ScViewData::SelectionForbidsPaste( ScDocument* pClipDoc )
+{
+ if (!pClipDoc)
+ {
+ // Same as checkDestRanges() in sc/source/ui/view/cellsh.cxx but
+ // different return details.
+
+ vcl::Window* pWin = GetActiveWin();
+ if (!pWin)
+ // No window doesn't mean paste would be forbidden.
+ return false;
+
+ const ScTransferObj* pOwnClip = ScTransferObj::GetOwnClipboard(ScTabViewShell::GetClipData(pWin));
+ if (!pOwnClip)
+ // Foreign content does not get repeatedly replicated.
+ return false;
+
+ pClipDoc = pOwnClip->GetDocument();
+ if (!pClipDoc)
+ // No clipdoc doesn't mean paste would be forbidden.
+ return false;
+ }
+
+ const ScRange aSrcRange = pClipDoc->GetClipParam().getWholeRange();
+ const SCROW nRowSize = aSrcRange.aEnd.Row() - aSrcRange.aStart.Row() + 1;
+ const SCCOL nColSize = aSrcRange.aEnd.Col() - aSrcRange.aStart.Col() + 1;
+
+ return SelectionForbidsPaste( nColSize, nRowSize);
+}
+
+bool ScViewData::SelectionForbidsPaste( SCCOL nSrcCols, SCROW nSrcRows )
+{
+ ScRange aSelRange( ScAddress::UNINITIALIZED );
+ ScMarkType eMarkType = GetSimpleArea( aSelRange);
+
+ if (eMarkType == SC_MARK_MULTI)
+ // Not because of DOOM.
+ return false;
+
+ if (aSelRange.aEnd.Row() - aSelRange.aStart.Row() + 1 == nSrcRows)
+ // This also covers entire col(s) copied to be pasted on entire cols.
+ return false;
+
+ if (aSelRange.aEnd.Col() - aSelRange.aStart.Col() + 1 == nSrcCols)
+ // This also covers entire row(s) copied to be pasted on entire rows.
+ return false;
+
+ return SelectionFillDOOM( aSelRange);
+}
+
+bool ScViewData::SelectionForbidsCellFill()
+{
+ ScRange aSelRange( ScAddress::UNINITIALIZED );
+ ScMarkType eMarkType = GetSimpleArea( aSelRange);
+ return eMarkType != SC_MARK_MULTI && SelectionFillDOOM( aSelRange);
+}
+
+// static
+bool ScViewData::SelectionFillDOOM( const ScRange& rRange )
+{
+ // Assume that more than 23 full columns (23M cells) will not be
+ // successful... Even with only 10 bytes per cell that would already be
+ // 230MB, formula cells would be 100 bytes and more per cell.
+ // rows * columns > 23m => rows > 23m / columns
+ // to not overflow in case number of available columns or rows would be
+ // arbitrarily increased.
+ // We could refine this and take some actual cell size into account,
+ // evaluate available memory and what not, but...
+ const sal_Int32 kMax = 23 * 1024 * 1024; // current MAXROWCOUNT1 is 1024*1024=1048576
+ return (rRange.aEnd.Row() - rRange.aStart.Row() + 1) > (kMax / (rRange.aEnd.Col() - rRange.aStart.Col() + 1));
+}
+
+void ScViewData::SetFillMode( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow )
+{
+ nFillMode = ScFillMode::FILL;
+ nFillStartX = nStartCol;
+ nFillStartY = nStartRow;
+ nFillEndX = nEndCol;
+ nFillEndY = nEndRow;
+}
+
+void ScViewData::SetDragMode( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
+ ScFillMode nMode )
+{
+ nFillMode = nMode;
+ nFillStartX = nStartCol;
+ nFillStartY = nStartRow;
+ nFillEndX = nEndCol;
+ nFillEndY = nEndRow;
+}
+
+void ScViewData::ResetFillMode()
+{
+ nFillMode = ScFillMode::NONE;
+}
+
+void ScViewData::GetFillData( SCCOL& rStartCol, SCROW& rStartRow,
+ SCCOL& rEndCol, SCROW& rEndRow )
+{
+ rStartCol = nFillStartX;
+ rStartRow = nFillStartY;
+ rEndCol = nFillEndX;
+ rEndRow = nFillEndY;
+}
+
+SCCOL ScViewData::GetOldCurX() const
+{
+ if (pThisTab->mbOldCursorValid)
+ return pThisTab->nOldCurX;
+ else
+ return pThisTab->nCurX;
+}
+
+SCROW ScViewData::GetOldCurY() const
+{
+ if (pThisTab->mbOldCursorValid)
+ return pThisTab->nOldCurY;
+ else
+ return pThisTab->nCurY;
+}
+
+void ScViewData::SetOldCursor( SCCOL nNewX, SCROW nNewY )
+{
+ pThisTab->nOldCurX = nNewX;
+ pThisTab->nOldCurY = nNewY;
+ pThisTab->mbOldCursorValid = true;
+}
+
+void ScViewData::ResetOldCursor()
+{
+ pThisTab->mbOldCursorValid = false;
+}
+
+SCCOL ScViewData::GetPosX( ScHSplitPos eWhich, SCTAB nForTab ) const
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ return 0;
+
+ if (nForTab == -1)
+ return pThisTab->nPosX[eWhich];
+
+ if (!ValidTab(nForTab) || (nForTab >= static_cast<SCTAB>(maTabData.size())))
+ return -1;
+
+ return maTabData[nForTab]->nPosX[eWhich];
+}
+
+SCROW ScViewData::GetPosY( ScVSplitPos eWhich, SCTAB nForTab ) const
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ return 0;
+
+ if (nForTab == -1)
+ return pThisTab->nPosY[eWhich];
+
+ if (!ValidTab(nForTab) || (nForTab >= static_cast<SCTAB>(maTabData.size())))
+ return -1;
+
+ return maTabData[nForTab]->nPosY[eWhich];
+}
+
+ScViewDataTable* ScViewData::FetchTableData(SCTAB nTabIndex) const
+{
+ if (!ValidTab(nTabIndex) || (nTabIndex >= static_cast<SCTAB>(maTabData.size())))
+ return nullptr;
+ ScViewDataTable* pRet = maTabData[nTabIndex].get();
+ SAL_WARN_IF(!pRet, "sc.viewdata", "ScViewData::FetchTableData: hidden sheet = " << nTabIndex);
+ return pRet;
+}
+
+SCCOL ScViewData::GetCurXForTab( SCTAB nTabIndex ) const
+{
+ ScViewDataTable* pTabData = FetchTableData(nTabIndex);
+ return pTabData ? pTabData->nCurX : -1;
+}
+
+SCROW ScViewData::GetCurYForTab( SCTAB nTabIndex ) const
+{
+ ScViewDataTable* pTabData = FetchTableData(nTabIndex);
+ return pTabData ? pTabData->nCurY : -1;
+}
+
+void ScViewData::SetCurXForTab( SCCOL nNewCurX, SCTAB nTabIndex )
+{
+ if (ScViewDataTable* pTabData = FetchTableData(nTabIndex))
+ pTabData->nCurX = nNewCurX;
+}
+
+void ScViewData::SetCurYForTab( SCCOL nNewCurY, SCTAB nTabIndex )
+{
+ if (ScViewDataTable* pTabData = FetchTableData(nTabIndex))
+ pTabData->nCurY = nNewCurY;
+}
+
+void ScViewData::SetMaxTiledCol( SCCOL nNewMaxCol )
+{
+ nNewMaxCol = std::clamp(nNewMaxCol, SCCOL(0), mrDoc.MaxCol());
+
+ const SCTAB nTab = GetTabNo();
+ auto GetColWidthPx = [this, nTab](SCCOL nCol) {
+ const sal_uInt16 nSize = this->mrDoc.GetColWidth(nCol, nTab);
+ const tools::Long nSizePx = ScViewData::ToPixel(nSize, nPPTX);
+ return nSizePx;
+ };
+
+ tools::Long nTotalPixels = GetLOKWidthHelper().computePosition(nNewMaxCol, GetColWidthPx);
+
+ SAL_INFO("sc.lok.docsize", "ScViewData::SetMaxTiledCol: nNewMaxCol: "
+ << nNewMaxCol << ", nTotalPixels: " << nTotalPixels);
+
+ GetLOKWidthHelper().removeByIndex(pThisTab->nMaxTiledCol);
+ GetLOKWidthHelper().insert(nNewMaxCol, nTotalPixels);
+
+ pThisTab->nMaxTiledCol = nNewMaxCol;
+}
+
+void ScViewData::SetMaxTiledRow( SCROW nNewMaxRow )
+{
+ if (nNewMaxRow < 0)
+ nNewMaxRow = 0;
+ if (nNewMaxRow > MAXTILEDROW)
+ nNewMaxRow = MAXTILEDROW;
+
+ const SCTAB nTab = GetTabNo();
+ auto GetRowHeightPx = [this, nTab](SCROW nRow) {
+ const sal_uInt16 nSize = this->mrDoc.GetRowHeight(nRow, nTab);
+ const tools::Long nSizePx = ScViewData::ToPixel(nSize, nPPTY);
+ return nSizePx;
+ };
+
+ tools::Long nTotalPixels = GetLOKHeightHelper().computePosition(nNewMaxRow, GetRowHeightPx);
+
+ SAL_INFO("sc.lok.docsize", "ScViewData::SetMaxTiledRow: nNewMaxRow: "
+ << nNewMaxRow << ", nTotalPixels: " << nTotalPixels);
+
+ GetLOKHeightHelper().removeByIndex(pThisTab->nMaxTiledRow);
+ GetLOKHeightHelper().insert(nNewMaxRow, nTotalPixels);
+
+ pThisTab->nMaxTiledRow = nNewMaxRow;
+}
+
+tools::Rectangle ScViewData::GetEditArea( ScSplitPos eWhich, SCCOL nPosX, SCROW nPosY,
+ vcl::Window* pWin, const ScPatternAttr* pPattern,
+ bool bForceToTop, bool bInPrintTwips )
+{
+ Point aCellTopLeft = bInPrintTwips ?
+ GetPrintTwipsPos(nPosX, nPosY) : GetScrPos(nPosX, nPosY, eWhich, true);
+ return ScEditUtil(&mrDoc, nPosX, nPosY, nTabNo, aCellTopLeft,
+ pWin->GetOutDev(), nPPTX, nPPTY, GetZoomX(), GetZoomY(), bInPrintTwips ).
+ GetEditArea( pPattern, bForceToTop );
+}
+
+void ScViewData::SetEditEngine( ScSplitPos eWhich,
+ ScEditEngineDefaulter* pNewEngine,
+ vcl::Window* pWin, SCCOL nNewX, SCROW nNewY )
+{
+ bool bLayoutRTL = mrDoc.IsLayoutRTL(nTabNo);
+ ScHSplitPos eHWhich = WhichH(eWhich);
+ ScVSplitPos eVWhich = WhichV(eWhich);
+ bool bLOKActive = comphelper::LibreOfficeKit::isActive();
+ bool bLOKPrintTwips = bLOKActive && comphelper::LibreOfficeKit::isCompatFlagSet(
+ comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs);
+ bool bLOKLayoutRTL = bLOKActive && bLayoutRTL;
+
+ bool bWasThere = false;
+ if (pEditView[eWhich])
+ {
+ // if the view is already there don't call anything that changes the cursor position
+ if (bEditActive[eWhich])
+ {
+ bWasThere = true;
+ }
+ else
+ {
+ lcl_LOKRemoveWindow(GetViewShell(), eWhich);
+ pEditView[eWhich]->SetEditEngine(pNewEngine);
+ }
+
+ if (pEditView[eWhich]->GetWindow() != pWin)
+ {
+ lcl_LOKRemoveWindow(GetViewShell(), eWhich);
+ pEditView[eWhich]->SetWindow(pWin);
+ OSL_FAIL("EditView Window has changed");
+ }
+ }
+ else
+ {
+ pEditView[eWhich].reset(new EditView( pNewEngine, pWin ));
+
+ if (bLOKActive)
+ {
+ // We can broadcast the view-cursor message in print-twips for all views.
+ pEditView[eWhich]->SetBroadcastLOKViewCursor(bLOKPrintTwips);
+ pEditView[eWhich]->RegisterViewShell(pView);
+ }
+ }
+
+ // add windows from other views
+ if (!bWasThere && bLOKActive)
+ {
+ ScTabViewShell* pThisViewShell = GetViewShell();
+ SCTAB nThisTabNo = GetTabNo();
+ auto lAddWindows =
+ [pThisViewShell, nThisTabNo, eWhich] (ScTabViewShell* pOtherViewShell)
+ {
+ ScViewData& rOtherViewData = pOtherViewShell->GetViewData();
+ SCTAB nOtherTabNo = rOtherViewData.GetTabNo();
+ if (nThisTabNo == nOtherTabNo)
+ pOtherViewShell->AddWindowToForeignEditView(pThisViewShell, eWhich);
+ };
+
+ SfxLokHelper::forEachOtherView(pThisViewShell, lAddWindows);
+ }
+
+ // if view is gone then during IdleFormat sometimes a cursor is drawn
+
+ EEControlBits nEC = pNewEngine->GetControlWord();
+ pNewEngine->SetControlWord(nEC & ~EEControlBits::DOIDLEFORMAT);
+
+ EVControlBits nVC = pEditView[eWhich]->GetControlWord();
+ pEditView[eWhich]->SetControlWord(nVC & ~EVControlBits::AUTOSCROLL);
+
+ bEditActive[eWhich] = true;
+
+ const ScPatternAttr* pPattern = mrDoc.GetPattern(nNewX, nNewY, nTabNo);
+ SvxCellHorJustify eJust = pPattern->GetItem( ATTR_HOR_JUSTIFY ).GetValue();
+
+ bool bBreak = ( eJust == SvxCellHorJustify::Block ) ||
+ pPattern->GetItem(ATTR_LINEBREAK).GetValue();
+
+ bool bAsianVertical = pNewEngine->IsEffectivelyVertical(); // set by InputHandler
+
+ tools::Rectangle aPixRect = ScEditUtil(&mrDoc, nNewX, nNewY, nTabNo, GetScrPos(nNewX, nNewY, eWhich),
+ pWin->GetOutDev(), nPPTX,nPPTY,GetZoomX(),GetZoomY() ).
+ GetEditArea( pPattern, true );
+
+ tools::Rectangle aPTwipsRect;
+ if (bLOKPrintTwips)
+ {
+ aPTwipsRect = ScEditUtil(&mrDoc, nNewX, nNewY, nTabNo, GetPrintTwipsPos(nNewX, nNewY),
+ pWin->GetOutDev(), nPPTX, nPPTY, GetZoomX(), GetZoomY(), true /* bInPrintTwips */).
+ GetEditArea(pPattern, true);
+ }
+
+ // when right-aligned, leave space for the cursor
+ // in vertical mode, editing is always right-aligned
+ if ( GetEditAdjust() == SvxAdjust::Right || bAsianVertical )
+ {
+ aPixRect.AdjustRight(1 );
+ if (bLOKPrintTwips)
+ aPTwipsRect.AdjustRight(o3tl::convert(1, o3tl::Length::px, o3tl::Length::twip));
+ }
+
+ if (bLOKPrintTwips)
+ {
+ if (!pEditView[eWhich]->HasLOKSpecialPositioning())
+ pEditView[eWhich]->InitLOKSpecialPositioning(MapUnit::MapTwip, aPTwipsRect, Point());
+ else
+ pEditView[eWhich]->SetLOKSpecialOutputArea(aPTwipsRect);
+ }
+
+ if (bLOKActive && pEditView[eWhich]->HasLOKSpecialPositioning())
+ pEditView[eWhich]->SetLOKSpecialFlags(bLOKLayoutRTL ? LOKSpecialFlags::LayoutRTL : LOKSpecialFlags::NONE);
+
+ tools::Rectangle aOutputArea = pWin->PixelToLogic( aPixRect, GetLogicMode() );
+ pEditView[eWhich]->SetOutputArea( aOutputArea );
+
+ if ( bActive && eWhich == GetActivePart() )
+ {
+ // keep the part that has the active edit view available after
+ // switching sheets or reference input on a different part
+ eEditActivePart = eWhich;
+
+ // modify members nEditCol etc. only if also extending for needed area
+ nEditCol = nNewX;
+ nEditRow = nNewY;
+ const ScMergeAttr* pMergeAttr = &pPattern->GetItem(ATTR_MERGE);
+ nEditEndCol = nEditCol;
+ if (pMergeAttr->GetColMerge() > 1)
+ nEditEndCol += pMergeAttr->GetColMerge() - 1;
+ nEditEndRow = nEditRow;
+ if (pMergeAttr->GetRowMerge() > 1)
+ nEditEndRow += pMergeAttr->GetRowMerge() - 1;
+ nEditStartCol = nEditCol;
+
+ // For growing use only the alignment value from the attribute, numbers
+ // (existing or started) with default alignment extend to the right.
+ bool bGrowCentered = ( eJust == SvxCellHorJustify::Center );
+ bool bGrowToLeft = ( eJust == SvxCellHorJustify::Right ); // visual left
+ bool bLOKRTLInvert = (bLOKActive && bLayoutRTL);
+ if ( bAsianVertical )
+ bGrowCentered = bGrowToLeft = false; // keep old behavior for asian mode
+
+ tools::Long nSizeXPix, nSizeXPTwips = 0;
+
+ const tools::Long nGridWidthPx = pView->GetGridWidth(eHWhich);
+ const tools::Long nGridHeightPx = pView->GetGridHeight(eVWhich);
+ tools::Long nGridWidthTwips = 0, nGridHeightTwips = 0;
+ if (bLOKPrintTwips)
+ {
+ Size aGridSize(nGridWidthPx, nGridHeightPx);
+ const MapMode& rWinMapMode = GetLogicMode();
+ aGridSize = OutputDevice::LogicToLogic(
+ pWin->PixelToLogic(aGridSize, rWinMapMode),
+ rWinMapMode, MapMode(MapUnit::MapTwip));
+ nGridWidthTwips = aGridSize.Width();
+ nGridHeightTwips = aGridSize.Height();
+ }
+
+ if (bBreak && !bAsianVertical)
+ {
+ nSizeXPix = aPixRect.GetWidth(); // papersize -> no horizontal scrolling
+ if (bLOKPrintTwips)
+ nSizeXPTwips = aPTwipsRect.GetWidth();
+ }
+ else
+ {
+ OSL_ENSURE(pView,"no View for EditView");
+
+ if ( bGrowCentered )
+ {
+ // growing into both directions until one edge is reached
+ //! should be limited to whole cells in both directions
+ tools::Long nLeft = aPixRect.Left();
+ tools::Long nRight = nGridWidthPx - aPixRect.Right();
+ nSizeXPix = aPixRect.GetWidth() + 2 * std::min( nLeft, nRight );
+ if (bLOKPrintTwips)
+ {
+ tools::Long nLeftPTwips = aPTwipsRect.Left();
+ tools::Long nRightPTwips = nGridWidthTwips - aPTwipsRect.Right();
+ nSizeXPTwips = aPTwipsRect.GetWidth() + 2 * std::min(nLeftPTwips, nRightPTwips);
+ }
+ }
+ else if ( (bGrowToLeft && !bLOKRTLInvert) || (!bGrowToLeft && bLOKRTLInvert) )
+ {
+ nSizeXPix = aPixRect.Right(); // space that's available in the window when growing to the left
+ if (bLOKPrintTwips)
+ nSizeXPTwips = aPTwipsRect.Right();
+ }
+ else
+ {
+ nSizeXPix = nGridWidthPx - aPixRect.Left();
+ if (bLOKPrintTwips)
+ nSizeXPTwips = nGridWidthTwips - aPTwipsRect.Left();
+ }
+
+ if ( nSizeXPix <= 0 )
+ {
+ nSizeXPix = aPixRect.GetWidth(); // editing outside to the right of the window -> keep cell width
+ if (bLOKPrintTwips)
+ nSizeXPTwips = aPTwipsRect.GetWidth();
+ }
+ }
+ OSL_ENSURE(pView,"no View for EditView");
+ tools::Long nSizeYPix = nGridHeightPx - aPixRect.Top();
+ tools::Long nSizeYPTwips = bLOKPrintTwips ? (nGridHeightTwips - aPTwipsRect.Top()) : 0;
+
+ if ( nSizeYPix <= 0 )
+ {
+ nSizeYPix = aPixRect.GetHeight(); // editing outside below the window -> keep cell height
+ if (bLOKPrintTwips)
+ nSizeYPTwips = aPTwipsRect.GetHeight();
+ }
+
+ Size aPaperSize = pView->GetActiveWin()->PixelToLogic( Size( nSizeXPix, nSizeYPix ), GetLogicMode() );
+ Size aPaperSizePTwips(nSizeXPTwips, nSizeYPTwips);
+ if ( bBreak && !bAsianVertical && SC_MOD()->GetInputOptions().GetTextWysiwyg() )
+ {
+ // if text is formatted for printer, use the exact same paper width
+ // (and same line breaks) as for output.
+
+ Fraction aFract(1,1);
+ constexpr auto HMM_PER_TWIPS = o3tl::convert(1.0, o3tl::Length::twip, o3tl::Length::mm100);
+ tools::Rectangle aUtilRect = ScEditUtil(&mrDoc, nNewX, nNewY, nTabNo, Point(0, 0), pWin->GetOutDev(),
+ HMM_PER_TWIPS, HMM_PER_TWIPS, aFract, aFract ).GetEditArea( pPattern, false );
+ aPaperSize.setWidth( aUtilRect.GetWidth() );
+ if (bLOKPrintTwips)
+ {
+ aPaperSizePTwips.setWidth(o3tl::convert(aUtilRect.GetWidth(), o3tl::Length::mm100, o3tl::Length::twip));
+ }
+ }
+
+ pNewEngine->SetPaperSize( aPaperSize );
+ if (bLOKPrintTwips)
+ pNewEngine->SetLOKSpecialPaperSize(aPaperSizePTwips);
+
+ // sichtbarer Ausschnitt
+ Size aPaper = pNewEngine->GetPaperSize();
+ tools::Rectangle aVis = pEditView[eWhich]->GetVisArea();
+ tools::Rectangle aVisPTwips;
+ if (bLOKPrintTwips)
+ aVisPTwips = pEditView[eWhich]->GetLOKSpecialVisArea();
+
+ tools::Long nDiff = aVis.Right() - aVis.Left();
+ tools::Long nDiffPTwips = bLOKPrintTwips ? (aVisPTwips.Right() - aVisPTwips.Left()) : 0;
+ if ( GetEditAdjust() == SvxAdjust::Right )
+ {
+ aVis.SetRight( aPaper.Width() - 1 );
+ if (bLOKPrintTwips)
+ aVisPTwips.SetRight( aPaperSizePTwips.Width() - 1 );
+ bMoveArea = !bLayoutRTL;
+ }
+ else if ( GetEditAdjust() == SvxAdjust::Center )
+ {
+ aVis.SetRight( ( aPaper.Width() - 1 + nDiff ) / 2 );
+ if (bLOKPrintTwips)
+ aVisPTwips.SetRight( ( aPaperSizePTwips.Width() - 1 + nDiffPTwips ) / 2 );
+ bMoveArea = true; // always
+ }
+ else
+ {
+ aVis.SetRight( nDiff );
+ if (bLOKPrintTwips)
+ aVisPTwips.SetRight(nDiffPTwips);
+ bMoveArea = bLayoutRTL;
+ }
+ aVis.SetLeft( aVis.Right() - nDiff );
+ if (bLOKPrintTwips)
+ aVisPTwips.SetLeft(aVisPTwips.Right() - nDiffPTwips);
+ // #i49561# Important note:
+ // The set offset of the visible area of the EditView for centered and
+ // right alignment in horizontal layout is consider by instances of
+ // class <ScEditObjectViewForwarder> in its methods <LogicToPixel(..)>
+ // and <PixelToLogic(..)>. This is needed for the correct visibility
+ // of paragraphs in edit mode at the accessibility API.
+ pEditView[eWhich]->SetVisArea(aVis);
+ if (bLOKPrintTwips)
+ pEditView[eWhich]->SetLOKSpecialVisArea(aVisPTwips);
+ // UpdateMode has been disabled in ScInputHandler::StartTable
+ // must be enabled before EditGrowY (GetTextHeight)
+ pNewEngine->SetUpdateLayout( true );
+
+ pNewEngine->SetStatusEventHdl( LINK( this, ScViewData, EditEngineHdl ) );
+
+ EditGrowY( true ); // adjust to existing text content
+ EditGrowX();
+
+ Point aDocPos = pEditView[eWhich]->GetWindowPosTopLeft(0);
+ if (aDocPos.Y() < aOutputArea.Top())
+ pEditView[eWhich]->Scroll( 0, aOutputArea.Top() - aDocPos.Y() );
+ }
+
+ // here bEditActive needs to be set already
+ // (due to Map-Mode during Paint)
+ if (!bWasThere)
+ pNewEngine->InsertView(pEditView[eWhich].get());
+
+ // background color of the cell
+ Color aBackCol = pPattern->GetItem(ATTR_BACKGROUND).GetColor();
+
+ ScModule* pScMod = SC_MOD();
+ if ( aBackCol.IsTransparent() )
+ {
+ aBackCol = pScMod->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor;
+ }
+ pEditView[eWhich]->SetBackgroundColor( aBackCol );
+
+ pEditView[eWhich]->Invalidate(); // needed?
+ // needed, if position changed
+}
+
+IMPL_LINK( ScViewData, EditEngineHdl, EditStatus&, rStatus, void )
+{
+ EditStatusFlags nStatus = rStatus.GetStatusWord();
+ if (nStatus & (EditStatusFlags::HSCROLL | EditStatusFlags::TextHeightChanged | EditStatusFlags::TEXTWIDTHCHANGED | EditStatusFlags::CURSOROUT))
+ {
+ EditGrowY();
+ EditGrowX();
+
+ if (nStatus & EditStatusFlags::CURSOROUT)
+ {
+ ScSplitPos eWhich = GetActivePart();
+ if (pEditView[eWhich])
+ pEditView[eWhich]->ShowCursor(false);
+ }
+ }
+}
+
+void ScViewData::EditGrowX()
+{
+ // It is insane to call EditGrowX while the output area is already growing.
+ // That could occur because of the call to SetDefaultItem later.
+ // We end up with wrong start/end edit columns and the changes
+ // to the output area performed by the inner call to this method are
+ // useless since they are discarded by the outer call.
+ if (bGrowing)
+ return;
+
+ comphelper::FlagRestorationGuard aFlagGuard(bGrowing, true);
+
+ bool bLOKActive = comphelper::LibreOfficeKit::isActive();
+ bool bLOKPrintTwips = bLOKActive && comphelper::LibreOfficeKit::isCompatFlagSet(
+ comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs);
+
+ ScDocument& rLocalDoc = GetDocument();
+
+ ScSplitPos eWhich = GetActivePart();
+ ScHSplitPos eHWhich = WhichH(eWhich);
+ EditView* pCurView = pEditView[eWhich].get();
+
+ if ( !pCurView || !bEditActive[eWhich])
+ return;
+
+ bool bLayoutRTL = rLocalDoc.IsLayoutRTL( nTabNo );
+
+ ScEditEngineDefaulter* pEngine =
+ static_cast<ScEditEngineDefaulter*>( pCurView->GetEditEngine() );
+ vcl::Window* pWin = pCurView->GetWindow();
+
+ // Get the left- and right-most column positions.
+ SCCOL nLeft = GetPosX(eHWhich);
+ SCCOL nRight = nLeft + VisibleCellsX(eHWhich);
+
+ Size aSize = pEngine->GetPaperSize();
+ Size aSizePTwips;
+ if (bLOKPrintTwips)
+ aSizePTwips = pEngine->GetLOKSpecialPaperSize();
+
+ tools::Rectangle aArea = pCurView->GetOutputArea();
+ tools::Rectangle aAreaPTwips;
+ if (bLOKPrintTwips)
+ aAreaPTwips = pCurView->GetLOKSpecialOutputArea();
+
+ tools::Long nOldRight = aArea.Right();
+
+ // Margin is already included in the original width.
+ tools::Long nTextWidth = pEngine->CalcTextWidth();
+
+ bool bChanged = false;
+ bool bAsianVertical = pEngine->IsEffectivelyVertical();
+
+ // get bGrow... variables the same way as in SetEditEngine
+ const ScPatternAttr* pPattern = rLocalDoc.GetPattern( nEditCol, nEditRow, nTabNo );
+ SvxCellHorJustify eJust = pPattern->GetItem( ATTR_HOR_JUSTIFY ).GetValue();
+ bool bGrowCentered = ( eJust == SvxCellHorJustify::Center );
+ bool bGrowToLeft = ( eJust == SvxCellHorJustify::Right ); // visual left
+ bool bGrowBackwards = bGrowToLeft; // logical left
+ if ( bLayoutRTL )
+ bGrowBackwards = !bGrowBackwards; // invert on RTL sheet
+ if ( bAsianVertical )
+ bGrowCentered = bGrowToLeft = bGrowBackwards = false; // keep old behavior for asian mode
+
+ bool bUnevenGrow = false;
+ if ( bGrowCentered )
+ {
+ while (aArea.GetWidth() + 0 < nTextWidth && ( nEditStartCol > nLeft || nEditEndCol < nRight ) )
+ {
+ tools::Long nLogicLeft = 0;
+ tools::Long nLogicLeftPTwips = 0;
+ if ( nEditStartCol > nLeft )
+ {
+ --nEditStartCol;
+ tools::Long nColWidth = rLocalDoc.GetColWidth( nEditStartCol, nTabNo );
+ tools::Long nLeftPix = ToPixel( nColWidth, nPPTX );
+ nLogicLeft = pWin->PixelToLogic(Size(nLeftPix,0)).Width();
+ if (bLOKPrintTwips)
+ nLogicLeftPTwips = nColWidth;
+ }
+ tools::Long nLogicRight = 0;
+ tools::Long nLogicRightPTwips = 0;
+ if ( nEditEndCol < nRight )
+ {
+ ++nEditEndCol;
+ tools::Long nColWidth = rLocalDoc.GetColWidth( nEditEndCol, nTabNo );
+ tools::Long nRightPix = ToPixel( nColWidth, nPPTX );
+ nLogicRight = pWin->PixelToLogic(Size(nRightPix,0)).Width();
+ if (bLOKPrintTwips)
+ nLogicRightPTwips = nColWidth;
+ }
+
+ aArea.AdjustLeft( -((bLayoutRTL && !bLOKActive) ? nLogicRight : nLogicLeft) );
+ aArea.AdjustRight((bLayoutRTL && !bLOKActive) ? nLogicLeft : nLogicRight );
+ if (bLOKPrintTwips)
+ {
+ aAreaPTwips.AdjustLeft(-nLogicLeftPTwips);
+ aAreaPTwips.AdjustRight(nLogicRightPTwips);
+ }
+
+ if ( aArea.Right() > aArea.Left() + aSize.Width() - 1 )
+ {
+ tools::Long nCenter = ( aArea.Left() + aArea.Right() ) / 2;
+ tools::Long nHalf = aSize.Width() / 2;
+ aArea.SetLeft( nCenter - nHalf + 1 );
+ aArea.SetRight( nCenter + aSize.Width() - nHalf - 1 );
+
+ if (bLOKPrintTwips)
+ {
+ tools::Long nCenterPTwips = ( aAreaPTwips.Left() + aAreaPTwips.Right() ) / 2;
+ tools::Long nHalfPTwips = aSizePTwips.Width() / 2;
+ aAreaPTwips.SetLeft( nCenterPTwips - nHalfPTwips + 1 );
+ aAreaPTwips.SetRight( nCenterPTwips + aSizePTwips.Width() - nHalfPTwips - 1 );
+ }
+ }
+
+ bChanged = true;
+ if ( nLogicLeft != nLogicRight )
+ bUnevenGrow = true;
+ }
+ }
+ else if ( bGrowBackwards )
+ {
+ while (aArea.GetWidth() + 0 < nTextWidth && nEditStartCol > nLeft)
+ {
+ --nEditStartCol;
+ tools::Long nColWidth = rLocalDoc.GetColWidth( nEditStartCol, nTabNo );
+ tools::Long nPix = ToPixel( nColWidth, nPPTX );
+ tools::Long nLogicWidth = pWin->PixelToLogic(Size(nPix,0)).Width();
+ tools::Long& nLogicWidthPTwips = nColWidth;
+
+ if ( !bLayoutRTL || bLOKActive )
+ {
+ aArea.AdjustLeft( -nLogicWidth );
+ if (bLOKPrintTwips)
+ aAreaPTwips.AdjustLeft( -nLogicWidthPTwips );
+ }
+ else
+ {
+ aArea.AdjustRight(nLogicWidth );
+ if (bLOKPrintTwips)
+ aAreaPTwips.AdjustRight(nLogicWidthPTwips);
+ }
+
+ if ( aArea.Right() > aArea.Left() + aSize.Width() - 1 )
+ {
+ if ( !bLayoutRTL || bLOKActive )
+ {
+ aArea.SetLeft( aArea.Right() - aSize.Width() + 1 );
+ if (bLOKPrintTwips)
+ aAreaPTwips.SetLeft( aAreaPTwips.Right() - aSizePTwips.Width() + 1 );
+ }
+ else
+ {
+ aArea.SetRight( aArea.Left() + aSize.Width() - 1 );
+ if (bLOKPrintTwips)
+ aAreaPTwips.SetRight( aAreaPTwips.Left() + aSizePTwips.Width() - 1 );
+ }
+ }
+
+ bChanged = true;
+ }
+ }
+ else
+ {
+ while (aArea.GetWidth() + 0 < nTextWidth && nEditEndCol < nRight)
+ {
+ ++nEditEndCol;
+ tools::Long nColWidth = rLocalDoc.GetColWidth( nEditEndCol, nTabNo );
+ tools::Long nPix = ToPixel( nColWidth, nPPTX );
+ tools::Long nLogicWidth = pWin->PixelToLogic(Size(nPix,0)).Width();
+ tools::Long& nLogicWidthPTwips = nColWidth;
+ if ( bLayoutRTL && !bLOKActive )
+ {
+ aArea.AdjustLeft( -nLogicWidth );
+ }
+ else
+ {
+ aArea.AdjustRight(nLogicWidth );
+ if (bLOKPrintTwips)
+ aAreaPTwips.AdjustRight(nLogicWidthPTwips);
+ }
+
+ if ( aArea.Right() > aArea.Left() + aSize.Width() - 1 )
+ {
+ if ( bLayoutRTL && !bLOKActive )
+ {
+ aArea.SetLeft( aArea.Right() - aSize.Width() + 1 );
+ }
+ else
+ {
+ aArea.SetRight( aArea.Left() + aSize.Width() - 1 );
+ if (bLOKPrintTwips)
+ aAreaPTwips.SetRight( aAreaPTwips.Left() + aSizePTwips.Width() - 1 );
+ }
+ }
+
+ bChanged = true;
+ }
+ }
+
+ if (!bChanged)
+ return;
+
+ if ( bMoveArea || bGrowCentered || bGrowBackwards || bLayoutRTL )
+ {
+ tools::Rectangle aVis = pCurView->GetVisArea();
+ tools::Rectangle aVisPTwips;
+ if (bLOKPrintTwips)
+ aVisPTwips = pCurView->GetLOKSpecialVisArea();
+
+ if ( bGrowCentered )
+ {
+ // switch to center-aligned (undo?) and reset VisArea to center
+
+ pEngine->SetDefaultItem( SvxAdjustItem( SvxAdjust::Center, EE_PARA_JUST ) );
+
+ tools::Long nCenter = aSize.Width() / 2;
+ tools::Long nVisSize = aArea.GetWidth();
+ aVis.SetLeft( nCenter - nVisSize / 2 );
+ aVis.SetRight( aVis.Left() + nVisSize - 1 );
+
+ if (bLOKPrintTwips)
+ {
+ tools::Long nCenterPTwips = aSizePTwips.Width() / 2;
+ tools::Long nVisSizePTwips = aAreaPTwips.GetWidth();
+ aVisPTwips.SetLeft( nCenterPTwips - nVisSizePTwips / 2 );
+ aVisPTwips.SetRight( aVisPTwips.Left() + nVisSizePTwips - 1 );
+ }
+ }
+ else if ( bGrowToLeft )
+ {
+ // switch to right-aligned (undo?) and reset VisArea to the right
+
+ pEngine->SetDefaultItem( SvxAdjustItem( SvxAdjust::Right, EE_PARA_JUST ) );
+
+ aVis.SetRight( aSize.Width() - 1 );
+ aVis.SetLeft( aSize.Width() - aArea.GetWidth() ); // with the new, increased area
+
+ if (bLOKPrintTwips)
+ {
+ aVisPTwips.SetRight( aSizePTwips.Width() - 1 );
+ aVisPTwips.SetLeft( aSizePTwips.Width() - aAreaPTwips.GetWidth() ); // with the new, increased area
+ }
+ }
+ else
+ {
+ // switch to left-aligned (undo?) and reset VisArea to the left
+
+ pEngine->SetDefaultItem( SvxAdjustItem( SvxAdjust::Left, EE_PARA_JUST ) );
+
+ tools::Long nMove = aVis.Left();
+ aVis.SetLeft( 0 );
+ aVis.AdjustRight( -nMove );
+
+ if (bLOKPrintTwips)
+ {
+ tools::Long nMovePTwips = aVisPTwips.Left();
+ aVisPTwips.SetLeft( 0 );
+ aVisPTwips.AdjustRight( -nMovePTwips );
+ }
+ }
+
+ pCurView->SetVisArea( aVis );
+ if (bLOKPrintTwips)
+ pCurView->SetLOKSpecialVisArea( aVisPTwips );
+
+ bMoveArea = false;
+ }
+
+ if (bLOKPrintTwips)
+ pCurView->SetLOKSpecialOutputArea(aAreaPTwips);
+
+ pCurView->SetOutputArea(aArea);
+
+ // In vertical mode, the whole text is moved to the next cell (right-aligned),
+ // so everything must be repainted. Otherwise, paint only the new area.
+ // If growing in centered alignment, if the cells left and right have different sizes,
+ // the whole text will move, and may not even obscure all of the original display.
+ if ( bUnevenGrow )
+ {
+ aArea.SetLeft( pWin->PixelToLogic( Point(0,0) ).X() );
+ aArea.SetRight( pWin->PixelToLogic( aScrSize ).Width() );
+ }
+ else if ( !bAsianVertical && !bGrowToLeft && !bGrowCentered )
+ aArea.SetLeft( nOldRight );
+ pWin->Invalidate(aArea);
+
+ // invalidate other views
+ pCurView->InvalidateOtherViewWindows(aArea);
+}
+
+void ScViewData::EditGrowY( bool bInitial )
+{
+ if (bGrowing)
+ return;
+
+ comphelper::FlagRestorationGuard aFlagGuard(bGrowing, true);
+
+ bool bLOKActive = comphelper::LibreOfficeKit::isActive();
+ bool bLOKPrintTwips = bLOKActive && comphelper::LibreOfficeKit::isCompatFlagSet(
+ comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs);
+
+ ScSplitPos eWhich = GetActivePart();
+ ScVSplitPos eVWhich = WhichV(eWhich);
+ EditView* pCurView = pEditView[eWhich].get();
+
+ if ( !pCurView || !bEditActive[eWhich])
+ return;
+
+ EVControlBits nControl = pEditView[eWhich]->GetControlWord();
+ if ( nControl & EVControlBits::AUTOSCROLL )
+ {
+ // if end of screen had already been reached and scrolling enabled,
+ // don't further try to grow the edit area
+
+ pCurView->SetOutputArea( pCurView->GetOutputArea() ); // re-align to pixels
+ return;
+ }
+
+ EditEngine* pEngine = pCurView->GetEditEngine();
+ vcl::Window* pWin = pCurView->GetWindow();
+
+ SCROW nBottom = GetPosY(eVWhich) + VisibleCellsY(eVWhich);
+
+ Size aSize = pEngine->GetPaperSize();
+ Size aSizePTwips;
+ tools::Rectangle aArea = pCurView->GetOutputArea();
+ tools::Rectangle aAreaPTwips;
+
+ if (bLOKPrintTwips)
+ {
+ aSizePTwips = pEngine->GetLOKSpecialPaperSize();
+ aAreaPTwips = pCurView->GetLOKSpecialOutputArea();
+ }
+
+ tools::Long nOldBottom = aArea.Bottom();
+ tools::Long nTextHeight = pEngine->GetTextHeight();
+
+ // When editing a formula in a cell with optimal height, allow a larger portion
+ // to be clipped before extending to following rows, to avoid obscuring cells for
+ // reference input (next row is likely to be useful in formulas).
+ tools::Long nAllowedExtra = SC_GROWY_SMALL_EXTRA;
+ if (nEditEndRow == nEditRow && !(mrDoc.GetRowFlags(nEditRow, nTabNo) & CRFlags::ManualSize) &&
+ pEngine->GetParagraphCount() <= 1 )
+ {
+ // If the (only) paragraph starts with a '=', it's a formula.
+ // If this is the initial call and the text is empty, allow the larger value, too,
+ // because this occurs in the normal progress of editing a formula.
+ // Subsequent calls with empty text might involve changed attributes (including
+ // font height), so they are treated like normal text.
+ OUString aText = pEngine->GetText( 0 );
+ if ( ( aText.isEmpty() && bInitial ) || aText.startsWith("=") )
+ nAllowedExtra = SC_GROWY_BIG_EXTRA;
+ }
+
+ bool bChanged = false;
+ bool bMaxReached = false;
+ while (aArea.GetHeight() + nAllowedExtra < nTextHeight && nEditEndRow < nBottom && !bMaxReached)
+ {
+ ++nEditEndRow;
+ ScDocument& rLocalDoc = GetDocument();
+ tools::Long nRowHeight = rLocalDoc.GetRowHeight( nEditEndRow, nTabNo );
+ tools::Long nPix = ToPixel( nRowHeight, nPPTY );
+ aArea.AdjustBottom(pWin->PixelToLogic(Size(0,nPix)).Height() );
+ if (bLOKPrintTwips)
+ aAreaPTwips.AdjustBottom(nRowHeight);
+
+ if ( aArea.Bottom() > aArea.Top() + aSize.Height() - 1 )
+ {
+ aArea.SetBottom( aArea.Top() + aSize.Height() - 1 );
+ if (bLOKPrintTwips)
+ aAreaPTwips.SetBottom( aAreaPTwips.Top() + aSizePTwips.Height() - 1 );
+ bMaxReached = true; // don't occupy more cells beyond paper size
+ }
+
+ bChanged = true;
+ nAllowedExtra = SC_GROWY_SMALL_EXTRA; // larger value is only for first row
+ }
+
+ if (!bChanged)
+ return;
+
+ if (bLOKPrintTwips)
+ pCurView->SetLOKSpecialOutputArea(aAreaPTwips);
+
+ pCurView->SetOutputArea(aArea);
+
+ if (nEditEndRow >= nBottom || bMaxReached)
+ {
+ if (!(nControl & EVControlBits::AUTOSCROLL))
+ pCurView->SetControlWord( nControl | EVControlBits::AUTOSCROLL );
+ }
+
+ aArea.SetTop( nOldBottom );
+ pWin->Invalidate(aArea);
+
+ // invalidate other views
+ pCurView->InvalidateOtherViewWindows(aArea);
+}
+
+void ScViewData::ResetEditView()
+{
+ EditEngine* pEngine = nullptr;
+ for (sal_uInt16 i=0; i<4; i++)
+ if (pEditView[i])
+ {
+ if (bEditActive[i])
+ {
+ lcl_LOKRemoveWindow(GetViewShell(), static_cast<ScSplitPos>(i));
+ pEngine = pEditView[i]->GetEditEngine();
+ pEngine->RemoveView(pEditView[i].get());
+ pEditView[i]->SetOutputArea( tools::Rectangle() );
+ }
+ bEditActive[i] = false;
+ }
+
+ if (pEngine)
+ pEngine->SetStatusEventHdl( Link<EditStatus&,void>() );
+}
+
+void ScViewData::KillEditView()
+{
+ EditEngine* pEngine = nullptr;
+ for (sal_uInt16 i=0; i<4; i++)
+ if (pEditView[i])
+ {
+ if (bEditActive[i])
+ {
+ pEngine = pEditView[i]->GetEditEngine();
+ if (pEngine)
+ pEngine->RemoveView(pEditView[i].get());
+ }
+ pEditView[i].reset();
+ }
+}
+
+void ScViewData::GetEditView( ScSplitPos eWhich, EditView*& rViewPtr, SCCOL& rCol, SCROW& rRow )
+{
+ rViewPtr = pEditView[eWhich].get();
+ rCol = nEditCol;
+ rRow = nEditRow;
+}
+
+void ScViewData::CreateTabData( SCTAB nNewTab )
+{
+ EnsureTabDataSize(nNewTab + 1);
+
+ if (!maTabData[nNewTab])
+ {
+ maTabData[nNewTab].reset(new ScViewDataTable(&mrDoc));
+
+ maTabData[nNewTab]->eZoomType = eDefZoomType;
+ maTabData[nNewTab]->aZoomX = aDefZoomX;
+ maTabData[nNewTab]->aZoomY = aDefZoomY;
+ maTabData[nNewTab]->aPageZoomX = aDefPageZoomX;
+ maTabData[nNewTab]->aPageZoomY = aDefPageZoomY;
+ }
+}
+
+void ScViewData::CreateSelectedTabData()
+{
+ for (const auto& rTab : maMarkData)
+ CreateTabData(rTab);
+}
+
+void ScViewData::EnsureTabDataSize(size_t nSize)
+{
+ if (nSize > maTabData.size())
+ maTabData.resize(nSize);
+}
+
+void ScViewData::SetTabNo( SCTAB nNewTab )
+{
+ if (!ValidTab(nNewTab))
+ {
+ OSL_FAIL("wrong sheet number");
+ return;
+ }
+
+ nTabNo = nNewTab;
+ CreateTabData(nTabNo);
+ pThisTab = maTabData[nTabNo].get();
+
+ CalcPPT(); // for common column width correction
+ RecalcPixPos(); //! not always needed!
+}
+
+ScPositionHelper* ScViewData::GetLOKWidthHelper(SCTAB nTabIndex)
+{
+ if (!ValidTab(nTabIndex) || (nTabIndex >= static_cast<SCTAB>(maTabData.size())) ||
+ !maTabData[nTabIndex])
+ {
+ return nullptr;
+ }
+ return &(maTabData[nTabIndex]->aWidthHelper);
+}
+
+ScPositionHelper* ScViewData::GetLOKHeightHelper(SCTAB nTabIndex)
+{
+ if (!ValidTab(nTabIndex) || (nTabIndex >= static_cast<SCTAB>(maTabData.size())) ||
+ !maTabData[nTabIndex])
+ {
+ return nullptr;
+ }
+ return &(maTabData[nTabIndex]->aHeightHelper);
+}
+
+void ScViewData::SetActivePart( ScSplitPos eNewActive )
+{
+ pThisTab->eWhichActive = eNewActive;
+
+ // Let's hope we find the culprit for tdf#117093
+ // Don't sanitize the real value (yet?) because this function might be
+ // called before setting the then corresponding split modes. For which in
+ // fact then the order should be changed.
+ assert(eNewActive == pThisTab->SanitizeWhichActive());
+}
+
+Point ScViewData::GetScrPos( SCCOL nWhereX, SCROW nWhereY, ScHSplitPos eWhich ) const
+{
+ OSL_ENSURE( eWhich==SC_SPLIT_LEFT || eWhich==SC_SPLIT_RIGHT, "wrong position" );
+ ScSplitPos ePos = ( eWhich == SC_SPLIT_LEFT ) ? SC_SPLIT_BOTTOMLEFT : SC_SPLIT_BOTTOMRIGHT;
+ return GetScrPos( nWhereX, nWhereY, ePos );
+}
+
+Point ScViewData::GetScrPos( SCCOL nWhereX, SCROW nWhereY, ScVSplitPos eWhich ) const
+{
+ OSL_ENSURE( eWhich==SC_SPLIT_TOP || eWhich==SC_SPLIT_BOTTOM, "wrong position" );
+ ScSplitPos ePos = ( eWhich == SC_SPLIT_TOP ) ? SC_SPLIT_TOPLEFT : SC_SPLIT_BOTTOMLEFT;
+ return GetScrPos( nWhereX, nWhereY, ePos );
+}
+
+Point ScViewData::GetScrPos( SCCOL nWhereX, SCROW nWhereY, ScSplitPos eWhich,
+ bool bAllowNeg, SCTAB nForTab ) const
+{
+ ScHSplitPos eWhichX = SC_SPLIT_LEFT;
+ ScVSplitPos eWhichY = SC_SPLIT_BOTTOM;
+ switch( eWhich )
+ {
+ case SC_SPLIT_TOPLEFT:
+ eWhichX = SC_SPLIT_LEFT;
+ eWhichY = SC_SPLIT_TOP;
+ break;
+ case SC_SPLIT_TOPRIGHT:
+ eWhichX = SC_SPLIT_RIGHT;
+ eWhichY = SC_SPLIT_TOP;
+ break;
+ case SC_SPLIT_BOTTOMLEFT:
+ eWhichX = SC_SPLIT_LEFT;
+ eWhichY = SC_SPLIT_BOTTOM;
+ break;
+ case SC_SPLIT_BOTTOMRIGHT:
+ eWhichX = SC_SPLIT_RIGHT;
+ eWhichY = SC_SPLIT_BOTTOM;
+ break;
+ }
+
+ if (nForTab == -1)
+ nForTab = nTabNo;
+ bool bForCurTab = (nForTab == nTabNo);
+ if (!bForCurTab && (!ValidTab(nForTab) || (nForTab >= static_cast<SCTAB>(maTabData.size()))))
+ {
+ SAL_WARN("sc.viewdata", "ScViewData::GetScrPos : invalid nForTab = " << nForTab);
+ nForTab = nTabNo;
+ bForCurTab = true;
+ }
+
+ ScViewDataTable* pViewTable = bForCurTab ? pThisTab : maTabData[nForTab].get();
+
+ if (pView)
+ {
+ const_cast<ScViewData*>(this)->aScrSize.setWidth( pView->GetGridWidth(eWhichX) );
+ const_cast<ScViewData*>(this)->aScrSize.setHeight( pView->GetGridHeight(eWhichY) );
+ }
+
+ sal_uInt16 nTSize;
+ bool bIsTiledRendering = comphelper::LibreOfficeKit::isActive();
+
+ SCCOL nPosX = GetPosX(eWhichX, nForTab);
+ tools::Long nScrPosX = 0;
+
+ if (bAllowNeg || nWhereX >= nPosX)
+ {
+ SCROW nStartPosX = nPosX;
+ if (bIsTiledRendering)
+ {
+ OSL_ENSURE(nPosX == 0, "Unsupported case.");
+ const auto& rNearest = pViewTable->aWidthHelper.getNearestByIndex(nWhereX - 1);
+ nStartPosX = rNearest.first + 1;
+ nScrPosX = rNearest.second;
+ }
+
+ if (nWhereX >= nStartPosX)
+ {
+ for (SCCOL nX = nStartPosX; nX < nWhereX && (bAllowNeg || bIsTiledRendering || nScrPosX <= aScrSize.Width()); nX++)
+ {
+ if (nX > mrDoc.MaxCol())
+ nScrPosX = 0x7FFFFFFF;
+ else
+ {
+ nTSize = mrDoc.GetColWidth(nX, nForTab);
+ if (nTSize)
+ {
+ tools::Long nSizeXPix = ToPixel( nTSize, nPPTX );
+ nScrPosX += nSizeXPix;
+ }
+ else
+ { // If the width is zero, the column is possibly hidden, skips groups of such columns.
+ SCCOL lastHidden = -1;
+ if(mrDoc.ColHidden(nX, nForTab, nullptr, &lastHidden) && lastHidden > nX)
+ nX = lastHidden - 1;
+ }
+ }
+ }
+ }
+ else
+ {
+ for (SCCOL nX = nStartPosX; nX > nWhereX;)
+ {
+ --nX;
+ nTSize = mrDoc.GetColWidth(nX, nForTab);
+ if (nTSize)
+ {
+ tools::Long nSizeXPix = ToPixel( nTSize, nPPTX );
+ nScrPosX -= nSizeXPix;
+ }
+ else
+ { // If the width is zero, the column is possibly hidden, skips groups of such columns.
+ SCCOL firstHidden = -1;
+ if(mrDoc.ColHidden(nX, nForTab, &firstHidden, nullptr) && firstHidden >= 0)
+ nX = firstHidden;
+ }
+ }
+ }
+
+ }
+
+
+ SCROW nPosY = GetPosY(eWhichY, nForTab);
+ tools::Long nScrPosY = 0;
+
+ if (bAllowNeg || nWhereY >= nPosY)
+ {
+ SCROW nStartPosY = nPosY;
+ if (bIsTiledRendering)
+ {
+ OSL_ENSURE(nPosY == 0, "Unsupported case.");
+ const auto& rNearest = pViewTable->aHeightHelper.getNearestByIndex(nWhereY - 1);
+ nStartPosY = rNearest.first + 1;
+ nScrPosY = rNearest.second;
+ }
+
+ if (nWhereY >= nStartPosY)
+ {
+ for (SCROW nY = nStartPosY; nY < nWhereY && (bAllowNeg || bIsTiledRendering || nScrPosY <= aScrSize.Height()); nY++)
+ {
+ if ( nY > mrDoc.MaxRow() )
+ nScrPosY = 0x7FFFFFFF;
+ else
+ {
+ nTSize = mrDoc.GetRowHeight( nY, nTabNo );
+ if (nTSize)
+ {
+ tools::Long nSizeYPix = ToPixel( nTSize, nPPTY );
+ nScrPosY += nSizeYPix;
+ }
+ else if ( nY < mrDoc.MaxRow() )
+ {
+ // skip multiple hidden rows (forward only for now)
+ SCROW nNext = mrDoc.FirstVisibleRow(nY + 1, mrDoc.MaxRow(), nTabNo);
+ if ( nNext > mrDoc.MaxRow() )
+ nY = mrDoc.MaxRow();
+ else
+ nY = nNext - 1; // +=nDir advances to next visible row
+ }
+ }
+ }
+ }
+ else
+ {
+ for (SCROW nY = nStartPosY; nY > nWhereY;)
+ {
+ --nY;
+ nTSize = mrDoc.GetRowHeight(nY, nForTab);
+ if (nTSize)
+ {
+ tools::Long nSizeYPix = ToPixel( nTSize, nPPTY );
+ nScrPosY -= nSizeYPix;
+ }
+ else
+ { // If the height is zero, the row is possibly hidden, skips groups of such rows.
+ SCROW firstHidden = -1;
+ if(mrDoc.RowHidden(nY, nForTab, &firstHidden, nullptr) && firstHidden >= 0)
+ nY = firstHidden;
+ }
+ }
+ }
+ }
+
+ if (mrDoc.IsLayoutRTL(nForTab) && !bIsTiledRendering)
+ {
+ // mirror horizontal position
+ nScrPosX = aScrSize.Width() - 1 - nScrPosX;
+ }
+
+ return Point( nScrPosX, nScrPosY );
+}
+
+Point ScViewData::GetPrintTwipsPos(SCCOL nCol, SCROW nRow) const
+{
+ // hidden ones are given 0 sizes by these by default.
+ // TODO: rewrite this to loop over spans (matters for jumbosheets).
+ tools::Long nPosX = nCol ? mrDoc.GetColWidth(0, nCol - 1, nTabNo) : 0;
+ // This is now fast as it loops over spans.
+ tools::Long nPosY = nRow ? mrDoc.GetRowHeight(0, nRow - 1, nTabNo) : 0;
+ // TODO: adjust for RTL layout case.
+
+ return Point(nPosX, nPosY);
+}
+
+Point ScViewData::GetPrintTwipsPosFromTileTwips(const Point& rTileTwipsPos) const
+{
+ const tools::Long nPixelX = static_cast<tools::Long>(rTileTwipsPos.X() * nPPTX);
+ const tools::Long nPixelY = static_cast<tools::Long>(rTileTwipsPos.Y() * nPPTY);
+ SCCOL nCol = 0;
+ SCROW nRow = 0;
+
+ // The following call (with bTestMerge = false) will not modify any members.
+ const_cast<ScViewData*>(this)->GetPosFromPixel(nPixelX, nPixelY, SC_SPLIT_TOPLEFT, nCol, nRow, false /* bTestMerge */);
+ const Point aPixCellPos = GetScrPos(nCol, nRow, SC_SPLIT_TOPLEFT, true /* bAllowNeg */);
+ const Point aTileTwipsCellPos(aPixCellPos.X() / nPPTX, aPixCellPos.Y() / nPPTY);
+ const Point aPrintTwipsCellPos = GetPrintTwipsPos(nCol, nRow);
+ return aPrintTwipsCellPos + (rTileTwipsPos - aTileTwipsCellPos);
+}
+
+OString ScViewData::describeCellCursorAt(SCCOL nX, SCROW nY, bool bPixelAligned) const
+{
+ const bool bPosSizeInPixels = bPixelAligned;
+ Point aCellPos = bPosSizeInPixels ? GetScrPos( nX, nY, SC_SPLIT_BOTTOMRIGHT, true ) :
+ GetPrintTwipsPos(nX, nY);
+
+ tools::Long nSizeX;
+ tools::Long nSizeY;
+ if (bPosSizeInPixels)
+ GetMergeSizePixel( nX, nY, nSizeX, nSizeY );
+ else
+ GetMergeSizePrintTwips(nX, nY, nSizeX, nSizeY);
+
+ std::stringstream ss;
+ if (bPosSizeInPixels)
+ {
+ double fPPTX = GetPPTX();
+ double fPPTY = GetPPTY();
+
+ // make it a slim cell cursor, but not empty
+ if (nSizeX == 0)
+ nSizeX = 1;
+
+ if (nSizeY == 0)
+ nSizeY = 1;
+
+ tools::Long nPosXTw = rtl::math::round(aCellPos.getX() / fPPTX);
+ tools::Long nPosYTw = rtl::math::round(aCellPos.getY() / fPPTY);
+ // look at Rectangle( const Point& rLT, const Size& rSize ) for the '- 1'
+ tools::Long nSizeXTw = rtl::math::round(nSizeX / fPPTX) - 1;
+ tools::Long nSizeYTw = rtl::math::round(nSizeY / fPPTY) - 1;
+
+ ss << nPosXTw << ", " << nPosYTw << ", " << nSizeXTw << ", " << nSizeYTw << ", "
+ << nX << ", " << nY;
+ }
+ else
+ {
+ // look at Rectangle( const Point& rLT, const Size& rSize ) for the decrement.
+ if (nSizeX)
+ --nSizeX;
+ if (nSizeY)
+ --nSizeY;
+ ss << aCellPos.getX() << ", " << aCellPos.getY()
+ << ", " << nSizeX << ", " << nSizeY << ", "
+ << nX << ", " << nY;
+ }
+
+ return OString(ss.str());
+}
+
+// Number of cells on a screen
+SCCOL ScViewData::CellsAtX( SCCOL nPosX, SCCOL nDir, ScHSplitPos eWhichX, sal_uInt16 nScrSizeX ) const
+{
+ OSL_ENSURE( nDir==1 || nDir==-1, "wrong CellsAt call" );
+
+ if (pView)
+ const_cast<ScViewData*>(this)->aScrSize.setWidth( pView->GetGridWidth(eWhichX) );
+
+ SCCOL nX;
+ sal_uInt16 nScrPosX = 0;
+ if (nScrSizeX == SC_SIZE_NONE) nScrSizeX = static_cast<sal_uInt16>(aScrSize.Width());
+
+ if (nDir==1)
+ nX = nPosX; // forwards
+ else
+ nX = nPosX-1; // backwards
+
+ bool bOut = false;
+ for ( ; nScrPosX<=nScrSizeX && !bOut; nX = sal::static_int_cast<SCCOL>(nX + nDir) )
+ {
+ SCCOL nColNo = nX;
+ if (nColNo < 0 || nColNo > mrDoc.MaxCol())
+ bOut = true;
+ else
+ {
+ sal_uInt16 nTSize = mrDoc.GetColWidth(nColNo, nTabNo);
+ if (nTSize)
+ {
+ tools::Long nSizeXPix = ToPixel( nTSize, nPPTX );
+ nScrPosX = sal::static_int_cast<sal_uInt16>( nScrPosX + static_cast<sal_uInt16>(nSizeXPix) );
+ }
+ }
+ }
+
+ if (nDir==1)
+ nX = sal::static_int_cast<SCCOL>( nX - nPosX );
+ else
+ nX = (nPosX-1)-nX;
+
+ if (nX>0) --nX;
+ return nX;
+}
+
+SCROW ScViewData::CellsAtY( SCROW nPosY, SCROW nDir, ScVSplitPos eWhichY, sal_uInt16 nScrSizeY ) const
+{
+ OSL_ENSURE( nDir==1 || nDir==-1, "wrong CellsAt call" );
+
+ if (pView)
+ const_cast<ScViewData*>(this)->aScrSize.setHeight( pView->GetGridHeight(eWhichY) );
+
+ if (nScrSizeY == SC_SIZE_NONE) nScrSizeY = static_cast<sal_uInt16>(aScrSize.Height());
+
+ SCROW nY;
+
+ if (nDir==1)
+ {
+ // forward
+ nY = nPosY;
+ tools::Long nScrPosY = 0;
+ AddPixelsWhile(nScrPosY, nScrSizeY, nY, mrDoc.MaxRow(), nPPTY, &mrDoc, nTabNo);
+ // Original loop ended on last evaluated +1 or if that was MaxRow even on MaxRow+2.
+ nY += (nY == mrDoc.MaxRow() ? 2 : 1);
+ nY -= nPosY;
+ }
+ else
+ {
+ // backward
+ nY = nPosY-1;
+ tools::Long nScrPosY = 0;
+ AddPixelsWhileBackward(nScrPosY, nScrSizeY, nY, 0, nPPTY, &mrDoc, nTabNo);
+ // Original loop ended on last evaluated -1 or if that was 0 even on -2.
+ nY -= (nY == 0 ? 2 : 1);
+ nY = (nPosY-1)-nY;
+ }
+
+ if (nY>0) --nY;
+ return nY;
+}
+
+SCCOL ScViewData::VisibleCellsX( ScHSplitPos eWhichX ) const
+{
+ return CellsAtX( GetPosX( eWhichX ), 1, eWhichX );
+}
+
+SCROW ScViewData::VisibleCellsY( ScVSplitPos eWhichY ) const
+{
+ return CellsAtY( GetPosY( eWhichY ), 1, eWhichY );
+}
+
+SCCOL ScViewData::PrevCellsX( ScHSplitPos eWhichX ) const
+{
+ return CellsAtX( GetPosX( eWhichX ), -1, eWhichX );
+}
+
+SCROW ScViewData::PrevCellsY( ScVSplitPos eWhichY ) const
+{
+ return CellsAtY( GetPosY( eWhichY ), -1, eWhichY );
+}
+
+bool ScViewData::GetMergeSizePixel( SCCOL nX, SCROW nY, tools::Long& rSizeXPix, tools::Long& rSizeYPix ) const
+{
+ const ScMergeAttr* pMerge = mrDoc.GetAttr(nX, nY, nTabNo, ATTR_MERGE);
+ if ( pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1 )
+ {
+ tools::Long nOutWidth = 0;
+ tools::Long nOutHeight = 0;
+ SCCOL nCountX = pMerge->GetColMerge();
+ for (SCCOL i=0; i<nCountX; i++)
+ nOutWidth += ToPixel(mrDoc.GetColWidth(nX + i, nTabNo), nPPTX);
+ SCROW nCountY = pMerge->GetRowMerge();
+
+ for (SCROW nRow = nY; nRow <= nY+nCountY-1; ++nRow)
+ {
+ SCROW nLastRow = nRow;
+ if (mrDoc.RowHidden(nRow, nTabNo, nullptr, &nLastRow))
+ {
+ nRow = nLastRow;
+ continue;
+ }
+
+ sal_uInt16 nHeight = mrDoc.GetRowHeight(nRow, nTabNo);
+ nOutHeight += ToPixel(nHeight, nPPTY);
+ }
+
+ rSizeXPix = nOutWidth;
+ rSizeYPix = nOutHeight;
+ return true;
+ }
+ else
+ {
+ rSizeXPix = ToPixel(mrDoc.GetColWidth(nX, nTabNo), nPPTX);
+ rSizeYPix = ToPixel(mrDoc.GetRowHeight(nY, nTabNo), nPPTY);
+ return false;
+ }
+}
+
+bool ScViewData::GetMergeSizePrintTwips(SCCOL nX, SCROW nY, tools::Long& rSizeXTwips, tools::Long& rSizeYTwips) const
+{
+ const ScMergeAttr* pMerge = mrDoc.GetAttr(nX, nY, nTabNo, ATTR_MERGE);
+ SCCOL nCountX = pMerge->GetColMerge();
+ if (!nCountX)
+ nCountX = 1;
+ rSizeXTwips = mrDoc.GetColWidth(nX, nX + nCountX - 1, nTabNo);
+
+ SCROW nCountY = pMerge->GetRowMerge();
+ if (!nCountY)
+ nCountY = 1;
+ rSizeYTwips = mrDoc.GetRowHeight(nY, nY + nCountY - 1, nTabNo);
+
+ return (nCountX > 1 || nCountY > 1);
+}
+
+void ScViewData::GetPosFromPixel( tools::Long nClickX, tools::Long nClickY, ScSplitPos eWhich,
+ SCCOL& rPosX, SCROW& rPosY,
+ bool bTestMerge, bool bRepair, SCTAB nForTab )
+{
+ // special handling of 0 is now in ScViewFunctionSet::SetCursorAtPoint
+
+ if (nForTab == -1)
+ nForTab = nTabNo;
+ bool bForCurTab = (nForTab == nTabNo);
+ if (!bForCurTab && (!ValidTab(nForTab) || (nForTab >= static_cast<SCTAB>(maTabData.size()))))
+ {
+ SAL_WARN("sc.viewdata", "ScViewData::GetPosFromPixel : invalid nForTab = " << nForTab);
+ nForTab = nTabNo;
+ bForCurTab = true;
+ }
+
+ ScHSplitPos eHWhich = WhichH(eWhich);
+ ScVSplitPos eVWhich = WhichV(eWhich);
+
+ if (mrDoc.IsLayoutRTL(nForTab))
+ {
+ if (!comphelper::LibreOfficeKit::isActive())
+ {
+ // mirror horizontal position
+ if (pView)
+ aScrSize.setWidth( pView->GetGridWidth(eHWhich) );
+ nClickX = aScrSize.Width() - 1 - nClickX;
+ }
+ }
+
+ SCCOL nStartPosX = GetPosX(eHWhich, nForTab);
+ SCROW nStartPosY = GetPosY(eVWhich, nForTab);
+ rPosX = nStartPosX;
+ rPosY = nStartPosY;
+ tools::Long nScrX = 0;
+ tools::Long nScrY = 0;
+
+ if (nClickX > 0)
+ {
+ while (rPosX <= mrDoc.MaxCol() && nClickX >= nScrX)
+ {
+ nScrX += ToPixel(mrDoc.GetColWidth(rPosX, nForTab), nPPTX);
+ ++rPosX;
+ }
+ --rPosX;
+ }
+ else
+ {
+ while ( rPosX>0 && nClickX < nScrX )
+ {
+ --rPosX;
+ nScrX -= ToPixel(mrDoc.GetColWidth(rPosX, nForTab), nPPTX);
+ }
+ }
+
+ if (nClickY > 0)
+ AddPixelsWhile(nScrY, nClickY, rPosY, mrDoc.MaxRow(), nPPTY, &mrDoc, nForTab);
+ else
+ {
+ /* TODO: could need some "SubPixelsWhileBackward" method */
+ while ( rPosY>0 && nClickY < nScrY )
+ {
+ --rPosY;
+ nScrY -= ToPixel(mrDoc.GetRowHeight(rPosY, nForTab), nPPTY);
+ }
+ }
+
+ // cells too big?
+ if ( rPosX == nStartPosX && nClickX > 0 )
+ {
+ if (pView)
+ aScrSize.setWidth( pView->GetGridWidth(eHWhich) );
+ if ( nClickX > aScrSize.Width() )
+ ++rPosX;
+ }
+ if ( rPosY == nStartPosY && nClickY > 0 )
+ {
+ if (pView)
+ aScrSize.setHeight( pView->GetGridHeight(eVWhich) );
+ if ( nClickY > aScrSize.Height() )
+ ++rPosY;
+ }
+
+ rPosX = std::clamp(rPosX, SCCOL(0), mrDoc.MaxCol());
+ rPosY = std::clamp(rPosY, SCROW(0), mrDoc.MaxRow());
+
+ if (!(bTestMerge && bForCurTab))
+ return;
+
+ // public method to adapt position
+ SCCOL nOrigX = rPosX;
+ SCROW nOrigY = rPosY;
+ mrDoc.SkipOverlapped(rPosX, rPosY, nTabNo);
+ bool bHOver = (nOrigX != rPosX);
+ bool bVOver = (nOrigY != rPosY);
+
+ if ( !(bRepair && ( bHOver || bVOver )) )
+ return;
+
+ const ScMergeAttr* pMerge = mrDoc.GetAttr(rPosX, rPosY, nTabNo, ATTR_MERGE);
+ if ( ( bHOver && pMerge->GetColMerge() <= 1 ) ||
+ ( bVOver && pMerge->GetRowMerge() <= 1 ) )
+ {
+ OSL_FAIL("merge error found");
+
+ mrDoc.RemoveFlagsTab(0, 0, mrDoc.MaxCol(), mrDoc.MaxRow(), nTabNo, ScMF::Hor | ScMF::Ver);
+ SCCOL nEndCol = mrDoc.MaxCol();
+ SCROW nEndRow = mrDoc.MaxRow();
+ mrDoc.ExtendMerge(0, 0, nEndCol, nEndRow, nTabNo, true);
+ if (pDocShell)
+ pDocShell->PostPaint(ScRange(0, 0, nTabNo, mrDoc.MaxCol(), mrDoc.MaxRow(), nTabNo),
+ PaintPartFlags::Grid);
+ }
+}
+
+void ScViewData::GetMouseQuadrant( const Point& rClickPos, ScSplitPos eWhich,
+ SCCOL nPosX, SCROW nPosY, bool& rLeft, bool& rTop )
+{
+ bool bLayoutRTL = mrDoc.IsLayoutRTL(nTabNo);
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ Point aCellStart = GetScrPos( nPosX, nPosY, eWhich, true );
+ tools::Long nSizeX;
+ tools::Long nSizeY;
+ GetMergeSizePixel( nPosX, nPosY, nSizeX, nSizeY );
+ rLeft = ( rClickPos.X() - aCellStart.X() ) * nLayoutSign <= nSizeX / 2;
+ rTop = rClickPos.Y() - aCellStart.Y() <= nSizeY / 2;
+}
+
+void ScViewData::SetPosX( ScHSplitPos eWhich, SCCOL nNewPosX )
+{
+ // in the tiled rendering case, nPosX [the leftmost visible column] must be 0
+ bool bIsTiledRendering = comphelper::LibreOfficeKit::isActive();
+ if (nNewPosX != 0 && !bIsTiledRendering)
+ {
+ SCCOL nOldPosX = pThisTab->nPosX[eWhich];
+ tools::Long nTPosX = pThisTab->nTPosX[eWhich];
+ tools::Long nPixPosX = pThisTab->nPixPosX[eWhich];
+ SCCOL i;
+ if ( nNewPosX > nOldPosX )
+ for ( i=nOldPosX; i<nNewPosX; i++ )
+ {
+ tools::Long nThis = mrDoc.GetColWidth(i, nTabNo);
+ nTPosX -= nThis;
+ nPixPosX -= ToPixel(sal::static_int_cast<sal_uInt16>(nThis), nPPTX);
+ }
+ else
+ for ( i=nNewPosX; i<nOldPosX; i++ )
+ {
+ tools::Long nThis = mrDoc.GetColWidth(i, nTabNo);
+ nTPosX += nThis;
+ nPixPosX += ToPixel(sal::static_int_cast<sal_uInt16>(nThis), nPPTX);
+ }
+
+ pThisTab->nPosX[eWhich] = nNewPosX;
+ pThisTab->nTPosX[eWhich] = nTPosX;
+ pThisTab->nMPosX[eWhich] = o3tl::convert(nTPosX, o3tl::Length::twip, o3tl::Length::mm100);
+ pThisTab->nPixPosX[eWhich] = nPixPosX;
+ }
+ else
+ {
+ pThisTab->nPixPosX[eWhich] =
+ pThisTab->nTPosX[eWhich] =
+ pThisTab->nMPosX[eWhich] =
+ pThisTab->nPosX[eWhich] = 0;
+ }
+}
+
+void ScViewData::SetPosY( ScVSplitPos eWhich, SCROW nNewPosY )
+{
+ // in the tiled rendering case, nPosY [the topmost visible row] must be 0
+ bool bIsTiledRendering = comphelper::LibreOfficeKit::isActive();
+ if (nNewPosY != 0 && !bIsTiledRendering)
+ {
+ SCROW nOldPosY = pThisTab->nPosY[eWhich];
+ tools::Long nTPosY = pThisTab->nTPosY[eWhich];
+ tools::Long nPixPosY = pThisTab->nPixPosY[eWhich];
+ SCROW i, nHeightEndRow;
+ if ( nNewPosY > nOldPosY )
+ for ( i=nOldPosY; i<nNewPosY; i++ )
+ {
+ tools::Long nThis = mrDoc.GetRowHeight(i, nTabNo, nullptr, &nHeightEndRow);
+ SCROW nRows = std::min( nNewPosY, nHeightEndRow + 1) - i;
+ i = nHeightEndRow;
+ nTPosY -= nThis * nRows;
+ nPixPosY -= ToPixel(sal::static_int_cast<sal_uInt16>(nThis), nPPTY) * nRows;
+ }
+ else
+ for ( i=nNewPosY; i<nOldPosY; i++ )
+ {
+ tools::Long nThis = mrDoc.GetRowHeight(i, nTabNo, nullptr, &nHeightEndRow);
+ SCROW nRows = std::min( nOldPosY, nHeightEndRow + 1) - i;
+ i = nHeightEndRow;
+ nTPosY += nThis * nRows;
+ nPixPosY += ToPixel(sal::static_int_cast<sal_uInt16>(nThis), nPPTY) * nRows;
+ }
+
+ pThisTab->nPosY[eWhich] = nNewPosY;
+ pThisTab->nTPosY[eWhich] = nTPosY;
+ pThisTab->nMPosY[eWhich] = o3tl::convert(nTPosY, o3tl::Length::twip, o3tl::Length::mm100);
+ pThisTab->nPixPosY[eWhich] = nPixPosY;
+ }
+ else
+ {
+ pThisTab->nPixPosY[eWhich] =
+ pThisTab->nTPosY[eWhich] =
+ pThisTab->nMPosY[eWhich] =
+ pThisTab->nPosY[eWhich] = 0;
+ }
+}
+
+void ScViewData::RecalcPixPos() // after zoom changes
+{
+ for (sal_uInt16 eWhich=0; eWhich<2; eWhich++)
+ {
+ tools::Long nPixPosX = 0;
+ SCCOL nPosX = pThisTab->nPosX[eWhich];
+ for (SCCOL i=0; i<nPosX; i++)
+ nPixPosX -= ToPixel(mrDoc.GetColWidth(i, nTabNo), nPPTX);
+ pThisTab->nPixPosX[eWhich] = nPixPosX;
+
+ tools::Long nPixPosY = 0;
+ SCROW nPosY = pThisTab->nPosY[eWhich];
+ tools::Long nRowHeight = -1;
+ SCROW nLastSameHeightRow = -1;
+ for (SCROW j=0; j<nPosY; j++)
+ {
+ if(nLastSameHeightRow < j)
+ nRowHeight = ToPixel(mrDoc.GetRowHeight(j, nTabNo, nullptr, &nLastSameHeightRow), nPPTY);
+ nPixPosY -= nRowHeight;
+ }
+ pThisTab->nPixPosY[eWhich] = nPixPosY;
+ }
+}
+
+const MapMode& ScViewData::GetLogicMode( ScSplitPos eWhich )
+{
+ aLogicMode.SetOrigin( Point( pThisTab->nMPosX[WhichH(eWhich)],
+ pThisTab->nMPosY[WhichV(eWhich)] ) );
+ return aLogicMode;
+}
+
+const MapMode& ScViewData::GetLogicMode()
+{
+ aLogicMode.SetOrigin( Point() );
+ return aLogicMode;
+}
+
+void ScViewData::SetScreen( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
+{
+ SCCOL nCol;
+ SCROW nRow;
+ sal_uInt16 nTSize;
+ tools::Long nSizePix;
+ tools::Long nScrPosX = 0;
+ tools::Long nScrPosY = 0;
+
+ SetActivePart( SC_SPLIT_BOTTOMLEFT );
+ SetPosX( SC_SPLIT_LEFT, nCol1 );
+ SetPosY( SC_SPLIT_BOTTOM, nRow1 );
+
+ for (nCol=nCol1; nCol<=nCol2; nCol++)
+ {
+ nTSize = mrDoc.GetColWidth(nCol, nTabNo);
+ if (nTSize)
+ {
+ nSizePix = ToPixel( nTSize, nPPTX );
+ nScrPosX += static_cast<sal_uInt16>(nSizePix);
+ }
+ }
+
+ for (nRow=nRow1; nRow<=nRow2; nRow++)
+ {
+ nTSize = mrDoc.GetRowHeight(nRow, nTabNo);
+ if (nTSize)
+ {
+ nSizePix = ToPixel( nTSize, nPPTY );
+ nScrPosY += static_cast<sal_uInt16>(nSizePix);
+ }
+ }
+
+ aScrSize = Size( nScrPosX, nScrPosY );
+}
+
+void ScViewData::SetScreenPos( const Point& rVisAreaStart )
+{
+ tools::Long nSize;
+ tools::Long nTwips;
+ tools::Long nAdd;
+ bool bEnd;
+
+ nSize = 0;
+ nTwips = o3tl::convert(rVisAreaStart.X(), o3tl::Length::mm100, o3tl::Length::twip);
+ if (mrDoc.IsLayoutRTL(nTabNo))
+ nTwips = -nTwips;
+ SCCOL nX1 = 0;
+ bEnd = false;
+ while (!bEnd)
+ {
+ nAdd = static_cast<tools::Long>(mrDoc.GetColWidth(nX1, nTabNo));
+ if (nSize + nAdd <= nTwips + 1 && nX1 < mrDoc.MaxCol())
+ {
+ nSize += nAdd;
+ ++nX1;
+ }
+ else
+ bEnd = true;
+ }
+
+ nSize = 0;
+ nTwips = o3tl::convert(rVisAreaStart.Y(), o3tl::Length::mm100, o3tl::Length::twip);
+ SCROW nY1 = 0;
+ bEnd = false;
+ while (!bEnd)
+ {
+ nAdd = static_cast<tools::Long>(mrDoc.GetRowHeight(nY1, nTabNo));
+ if (nSize + nAdd <= nTwips + 1 && nY1 < mrDoc.MaxRow())
+ {
+ nSize += nAdd;
+ ++nY1;
+ }
+ else
+ bEnd = true;
+ }
+
+ SetActivePart( SC_SPLIT_BOTTOMLEFT );
+ SetPosX( SC_SPLIT_LEFT, nX1 );
+ SetPosY( SC_SPLIT_BOTTOM, nY1 );
+
+ SetCurX( nX1 );
+ SetCurY( nY1 );
+}
+
+void ScViewData::SetScreen( const tools::Rectangle& rVisArea )
+{
+ SetScreenPos( rVisArea.TopLeft() );
+
+ // here without GetOutputFactor(), since it's for the output into a Metafile
+
+ aScrSize = rVisArea.GetSize();
+ aScrSize.setWidth(std::round(o3tl::convert( aScrSize.Width(), o3tl::Length::mm100, o3tl::Length::twip) * ScGlobal::nScreenPPTX));
+ aScrSize.setHeight(std::round(o3tl::convert( aScrSize.Height(), o3tl::Length::mm100, o3tl::Length::twip) * ScGlobal::nScreenPPTY));
+}
+
+ScDocFunc& ScViewData::GetDocFunc() const
+{
+ return pDocShell->GetDocFunc();
+}
+
+SfxBindings& ScViewData::GetBindings()
+{
+ assert(pView && "GetBindings() without ViewShell");
+ return pView->GetViewFrame().GetBindings();
+}
+
+SfxDispatcher& ScViewData::GetDispatcher()
+{
+ assert(pView && "GetDispatcher() without ViewShell");
+ return *pView->GetViewFrame().GetDispatcher();
+}
+
+ScMarkData& ScViewData::GetMarkData()
+{
+ return maMarkData;
+}
+
+ScMarkData& ScViewData::GetHighlightData()
+{
+ return maHighlightData;
+}
+
+const ScMarkData& ScViewData::GetMarkData() const
+{
+ return maMarkData;
+}
+
+weld::Window* ScViewData::GetDialogParent()
+{
+ assert(pView && "GetDialogParent() without ViewShell");
+ return pView->GetDialogParent();
+}
+
+ScGridWindow* ScViewData::GetActiveWin()
+{
+ assert(pView && "GetActiveWin() without View");
+ return pView->GetActiveWin();
+}
+
+const ScGridWindow* ScViewData::GetActiveWin() const
+{
+ assert(pView && "GetActiveWin() without View");
+ return pView->GetActiveWin();
+}
+
+ScDrawView* ScViewData::GetScDrawView()
+{
+ assert(pView && "GetScDrawView() without View");
+ return pView->GetScDrawView();
+}
+
+bool ScViewData::IsMinimized() const
+{
+ assert(pView && "IsMinimized() without View");
+ return pView->IsMinimized();
+}
+
+void ScViewData::UpdateScreenZoom( const Fraction& rNewX, const Fraction& rNewY )
+{
+ Fraction aOldX = GetZoomX();
+ Fraction aOldY = GetZoomY();
+
+ SetZoom( rNewX, rNewY, false );
+
+ Fraction aWidth = GetZoomX();
+ aWidth *= Fraction( aScrSize.Width(),1 );
+ aWidth /= aOldX;
+
+ Fraction aHeight = GetZoomY();
+ aHeight *= Fraction( aScrSize.Height(),1 );
+ aHeight /= aOldY;
+
+ aScrSize.setWidth( static_cast<tools::Long>(aWidth) );
+ aScrSize.setHeight( static_cast<tools::Long>(aHeight) );
+}
+
+void ScViewData::CalcPPT()
+{
+ double nOldPPTX = nPPTX;
+ double nOldPPTY = nPPTY;
+ nPPTX = ScGlobal::nScreenPPTX * static_cast<double>(GetZoomX());
+ if (pDocShell)
+ nPPTX = nPPTX / pDocShell->GetOutputFactor(); // Factor is printer to screen
+ nPPTY = ScGlobal::nScreenPPTY * static_cast<double>(GetZoomY());
+
+ // if detective objects are present,
+ // try to adjust horizontal scale so the most common column width has minimal rounding errors,
+ // to avoid differences between cell and drawing layer output
+
+ if (mrDoc.HasDetectiveObjects(nTabNo))
+ {
+ SCCOL nEndCol = 0;
+ SCROW nDummy = 0;
+ mrDoc.GetTableArea(nTabNo, nEndCol, nDummy);
+ if (nEndCol<20)
+ nEndCol = 20; // same end position as when determining draw scale
+
+ sal_uInt16 nTwips = mrDoc.GetCommonWidth(nEndCol, nTabNo);
+ if ( nTwips )
+ {
+ double fOriginal = nTwips * nPPTX;
+ if ( fOriginal < static_cast<double>(nEndCol) )
+ {
+ // if one column is smaller than the column count,
+ // rounding errors are likely to add up to a whole column.
+
+ double fRounded = ::rtl::math::approxFloor( fOriginal + 0.5 );
+ if ( fRounded > 0.0 )
+ {
+ double fScale = fRounded / fOriginal + 1E-6;
+ if ( fScale >= 0.9 && fScale <= 1.1 )
+ nPPTX *= fScale;
+ }
+ }
+ }
+ }
+
+ if (!comphelper::LibreOfficeKit::isActive())
+ return;
+
+ SCTAB nTabCount = maTabData.size();
+ bool bResetWidths = (nPPTX != nOldPPTX);
+ bool bResetHeights = (nPPTY != nOldPPTY);
+ for (SCTAB nTabIdx = 0; nTabIdx < nTabCount; ++nTabIdx)
+ {
+ if (!maTabData[nTabIdx])
+ continue;
+
+ if (bResetWidths)
+ if (auto* pWHelper = GetLOKWidthHelper(nTabIdx))
+ pWHelper->invalidateByPosition(0L);
+
+ if (bResetHeights)
+ if (auto* pHHelper = GetLOKHeightHelper(nTabIdx))
+ pHHelper->invalidateByPosition(0L);
+ }
+}
+
+#define SC_OLD_TABSEP '/'
+#define SC_NEW_TABSEP '+'
+
+void ScViewData::WriteUserData(OUString& rData)
+{
+ // nZoom (until 364v) or nZoom/nPageZoom/bPageMode (from 364w)
+ // nTab
+ // Tab control width
+ // per sheet:
+ // CursorX/CursorY/HSplitMode/VSplitMode/HSplitPos/VSplitPos/SplitActive/
+ // PosX[left]/PosX[right]/PosY[top]/PosY[bottom]
+ // when rows bigger than 8192, "+" instead of "/"
+
+ sal_uInt16 nZoom = static_cast<sal_uInt16>(tools::Long(pThisTab->aZoomY * 100));
+ rData = OUString::number( nZoom ) + "/";
+ nZoom = static_cast<sal_uInt16>(tools::Long(pThisTab->aPageZoomY * 100));
+ rData += OUString::number( nZoom ) + "/";
+ if (bPagebreak)
+ rData += "1";
+ else
+ rData += "0";
+
+ rData += ";" + OUString::number( nTabNo ) + ";" + TAG_TABBARWIDTH +
+ OUString::number( pView->GetTabBarWidth() );
+
+ SCTAB nTabCount = mrDoc.GetTableCount();
+ for (SCTAB i=0; i<nTabCount; i++)
+ {
+ rData += ";"; // Numbering must not get mixed up under any circumstances
+ if (i < static_cast<SCTAB>(maTabData.size()) && maTabData[i])
+ {
+ OUString cTabSep(SC_OLD_TABSEP); // like 3.1
+ if ( maTabData[i]->nCurY > MAXROW_30 ||
+ maTabData[i]->nPosY[0] > MAXROW_30 || maTabData[i]->nPosY[1] > MAXROW_30 ||
+ ( maTabData[i]->eVSplitMode == SC_SPLIT_FIX &&
+ maTabData[i]->nFixPosY > MAXROW_30 ) )
+ {
+ cTabSep = OUStringChar(SC_NEW_TABSEP); // in order to not kill a 3.1-version
+ }
+
+ rData += OUString::number( maTabData[i]->nCurX ) + cTabSep +
+ OUString::number( maTabData[i]->nCurY ) + cTabSep +
+ OUString::number( maTabData[i]->eHSplitMode ) + cTabSep +
+ OUString::number( maTabData[i]->eVSplitMode ) + cTabSep;
+ if ( maTabData[i]->eHSplitMode == SC_SPLIT_FIX )
+ rData += OUString::number( maTabData[i]->nFixPosX );
+ else
+ rData += OUString::number( maTabData[i]->nHSplitPos );
+ rData += cTabSep;
+ if ( maTabData[i]->eVSplitMode == SC_SPLIT_FIX )
+ rData += OUString::number( maTabData[i]->nFixPosY );
+ else
+ rData += OUString::number( maTabData[i]->nVSplitPos );
+ rData += cTabSep +
+ OUString::number( maTabData[i]->eWhichActive ) + cTabSep +
+ OUString::number( maTabData[i]->nPosX[0] ) + cTabSep +
+ OUString::number( maTabData[i]->nPosX[1] ) + cTabSep +
+ OUString::number( maTabData[i]->nPosY[0] ) + cTabSep +
+ OUString::number( maTabData[i]->nPosY[1] );
+ }
+ }
+}
+
+void ScViewData::ReadUserData(std::u16string_view rData)
+{
+ if (rData.empty()) // empty string on "reload"
+ return; // then exit without assertion
+
+ if ( comphelper::string::getTokenCount(rData, ';') <= 2 )
+ {
+ // when reload, in page preview, the preview UserData may have been left intact.
+ // we don't want the zoom from the page preview here.
+ OSL_FAIL("ReadUserData: This is not my data");
+ return;
+ }
+
+ sal_Int32 nMainIdx {0};
+ sal_Int32 nIdx {0};
+
+ std::u16string_view aZoomStr = o3tl::getToken(rData, 0, ';', nMainIdx); // Zoom/PageZoom/Mode
+ sal_Unicode cMode = o3tl::getToken(aZoomStr, 2, '/', nIdx)[0]; // 0 or "0"/"1"
+ SetPagebreakMode( cMode == '1' );
+ // SetPagebreakMode must always be called due to CalcPPT / RecalcPixPos()
+
+ // sheet may have become invalid (for instance last version):
+ SCTAB nNewTab = static_cast<SCTAB>(o3tl::toUInt32(o3tl::getToken(rData, 0, ';', nMainIdx)));
+ if (mrDoc.HasTable(nNewTab))
+ SetTabNo(nNewTab);
+
+ // if available, get tab bar width:
+ const sal_Int32 nMainIdxRef {nMainIdx};
+ std::u16string_view aTabOpt = o3tl::getToken(rData, 0, ';', nMainIdx);
+
+ std::u16string_view aRest;
+ if (o3tl::starts_with(aTabOpt, TAG_TABBARWIDTH, &aRest))
+ {
+ pView->SetTabBarWidth(o3tl::toInt32(aRest));
+ }
+ else
+ {
+ // Tab bar width not specified, token to be processed again
+ nMainIdx = nMainIdxRef;
+ }
+
+ // per sheet
+ SCTAB nPos = 0;
+ while ( nMainIdx>0 )
+ {
+ aTabOpt = o3tl::getToken(rData, 0, ';', nMainIdx);
+ EnsureTabDataSize(nPos + 1);
+ if (!maTabData[nPos])
+ maTabData[nPos].reset(new ScViewDataTable(&mrDoc));
+
+ sal_Unicode cTabSep = 0;
+ if (comphelper::string::getTokenCount(aTabOpt, SC_OLD_TABSEP) >= 11)
+ cTabSep = SC_OLD_TABSEP;
+ else if (comphelper::string::getTokenCount(aTabOpt, SC_NEW_TABSEP) >= 11)
+ cTabSep = SC_NEW_TABSEP;
+ // '+' is only allowed, if we can deal with rows > 8192
+
+ if (cTabSep)
+ {
+ nIdx = 0;
+ maTabData[nPos]->nCurX = mrDoc.SanitizeCol(static_cast<SCCOL>(o3tl::toInt32(o3tl::getToken(aTabOpt, 0, cTabSep, nIdx))));
+ maTabData[nPos]->nCurY = mrDoc.SanitizeRow(o3tl::toInt32(o3tl::getToken(aTabOpt, 0, cTabSep, nIdx)));
+ maTabData[nPos]->eHSplitMode = static_cast<ScSplitMode>(o3tl::toInt32(o3tl::getToken(aTabOpt, 0, cTabSep, nIdx)));
+ maTabData[nPos]->eVSplitMode = static_cast<ScSplitMode>(o3tl::toInt32(o3tl::getToken(aTabOpt, 0, cTabSep, nIdx)));
+
+ sal_Int32 nTmp = o3tl::toInt32(o3tl::getToken(aTabOpt, 0, cTabSep, nIdx));
+ if ( maTabData[nPos]->eHSplitMode == SC_SPLIT_FIX )
+ {
+ maTabData[nPos]->nFixPosX = mrDoc.SanitizeCol(static_cast<SCCOL>(nTmp));
+ UpdateFixX(nPos);
+ }
+ else
+ maTabData[nPos]->nHSplitPos = nTmp;
+
+ nTmp = o3tl::toInt32(o3tl::getToken(aTabOpt, 0, cTabSep, nIdx));
+ if ( maTabData[nPos]->eVSplitMode == SC_SPLIT_FIX )
+ {
+ maTabData[nPos]->nFixPosY = mrDoc.SanitizeRow(nTmp);
+ UpdateFixY(nPos);
+ }
+ else
+ maTabData[nPos]->nVSplitPos = nTmp;
+
+ maTabData[nPos]->eWhichActive = static_cast<ScSplitPos>(o3tl::toInt32(o3tl::getToken(aTabOpt, 0, cTabSep, nIdx)));
+ maTabData[nPos]->nPosX[0] = mrDoc.SanitizeCol(static_cast<SCCOL>(o3tl::toInt32(o3tl::getToken(aTabOpt, 0, cTabSep, nIdx))));
+ maTabData[nPos]->nPosX[1] = mrDoc.SanitizeCol(static_cast<SCCOL>(o3tl::toInt32(o3tl::getToken(aTabOpt, 0, cTabSep, nIdx))));
+ maTabData[nPos]->nPosY[0] = mrDoc.SanitizeRow(o3tl::toInt32(o3tl::getToken(aTabOpt, 0, cTabSep, nIdx)));
+ maTabData[nPos]->nPosY[1] = mrDoc.SanitizeRow(o3tl::toInt32(o3tl::getToken(aTabOpt, 0, cTabSep, nIdx)));
+
+ maTabData[nPos]->eWhichActive = maTabData[nPos]->SanitizeWhichActive();
+ }
+ ++nPos;
+ }
+
+ RecalcPixPos();
+}
+
+void ScViewData::WriteExtOptions( ScExtDocOptions& rDocOpt ) const
+{
+ // *** Fill extended document data for export filters ***
+
+ // document settings
+ ScExtDocSettings& rDocSett = rDocOpt.GetDocSettings();
+
+ // displayed sheet
+ rDocSett.mnDisplTab = GetTabNo();
+
+ // width of the tabbar, relative to frame window width
+ rDocSett.mfTabBarWidth = pView->GetPendingRelTabBarWidth();
+ if( rDocSett.mfTabBarWidth < 0.0 )
+ rDocSett.mfTabBarWidth = ScTabView::GetRelTabBarWidth();
+
+ bool bLOKActive = comphelper::LibreOfficeKit::isActive();
+
+ // sheet settings
+ for( SCTAB nTab = 0; nTab < static_cast<SCTAB>(maTabData.size()); ++nTab )
+ {
+ if( const ScViewDataTable* pViewTab = maTabData[ nTab ].get() )
+ {
+ ScExtTabSettings& rTabSett = rDocOpt.GetOrCreateTabSettings( nTab );
+
+ // split mode
+ ScSplitMode eExHSplit = pViewTab->eHSplitMode;
+ ScSplitMode eExVSplit = pViewTab->eVSplitMode;
+ SCCOL nExFixPosX = pViewTab->nFixPosX;
+ SCROW nExFixPosY = pViewTab->nFixPosY;
+ tools::Long nExHSplitPos = pViewTab->nHSplitPos;
+ tools::Long nExVSplitPos = pViewTab->nVSplitPos;
+
+ if (bLOKActive)
+ {
+ OverrideWithLOKFreeze(eExHSplit, eExVSplit,
+ nExFixPosX, nExFixPosY,
+ nExHSplitPos, nExVSplitPos, nTab);
+ }
+
+ bool bHSplit = eExHSplit != SC_SPLIT_NONE;
+ bool bVSplit = eExVSplit != SC_SPLIT_NONE;
+ bool bRealSplit = (eExHSplit == SC_SPLIT_NORMAL) || (eExVSplit == SC_SPLIT_NORMAL);
+ bool bFrozen = (eExHSplit == SC_SPLIT_FIX) || (eExVSplit == SC_SPLIT_FIX);
+ OSL_ENSURE( !bRealSplit || !bFrozen, "ScViewData::WriteExtOptions - split and freeze in same sheet" );
+ rTabSett.mbFrozenPanes = !bRealSplit && bFrozen;
+
+ // split and freeze position
+ rTabSett.maSplitPos = Point( 0, 0 );
+ rTabSett.maFreezePos.Set( 0, 0, nTab );
+ if( bRealSplit )
+ {
+ Point& rSplitPos = rTabSett.maSplitPos;
+ rSplitPos = Point( bHSplit ? nExHSplitPos : 0, bVSplit ? nExVSplitPos : 0 );
+ rSplitPos = Application::GetDefaultDevice()->PixelToLogic( rSplitPos, MapMode( MapUnit::MapTwip ) );
+ if( pDocShell )
+ rSplitPos.setX( static_cast<tools::Long>(static_cast<double>(rSplitPos.X()) / pDocShell->GetOutputFactor()) );
+ }
+ else if( bFrozen )
+ {
+ if( bHSplit ) rTabSett.maFreezePos.SetCol( nExFixPosX );
+ if( bVSplit ) rTabSett.maFreezePos.SetRow( nExFixPosY );
+ }
+
+ // first visible cell in top-left and additional panes
+ rTabSett.maFirstVis.Set( pViewTab->nPosX[ SC_SPLIT_LEFT ], pViewTab->nPosY[ bVSplit ? SC_SPLIT_TOP : SC_SPLIT_BOTTOM ], nTab );
+ rTabSett.maSecondVis.Set( pViewTab->nPosX[ SC_SPLIT_RIGHT ], pViewTab->nPosY[ SC_SPLIT_BOTTOM ], nTab );
+
+ // active pane
+ switch( pViewTab->eWhichActive )
+ {
+ // no horizontal split -> always use left panes
+ // no vertical split -> always use top panes
+ case SC_SPLIT_TOPLEFT:
+ rTabSett.meActivePane = SCEXT_PANE_TOPLEFT;
+ break;
+ case SC_SPLIT_TOPRIGHT:
+ rTabSett.meActivePane = bHSplit ? SCEXT_PANE_TOPRIGHT : SCEXT_PANE_TOPLEFT;
+ break;
+ case SC_SPLIT_BOTTOMLEFT:
+ rTabSett.meActivePane = bVSplit ? SCEXT_PANE_BOTTOMLEFT : SCEXT_PANE_TOPLEFT;
+ break;
+ case SC_SPLIT_BOTTOMRIGHT:
+ rTabSett.meActivePane = bHSplit ?
+ (bVSplit ? SCEXT_PANE_BOTTOMRIGHT : SCEXT_PANE_TOPRIGHT) :
+ (bVSplit ? SCEXT_PANE_BOTTOMLEFT : SCEXT_PANE_TOPLEFT);
+ break;
+ }
+
+ // cursor position
+ rTabSett.maCursor.Set( pViewTab->nCurX, pViewTab->nCurY, nTab );
+
+ // sheet selection and selected ranges
+ const ScMarkData& rMarkData = GetMarkData();
+ rTabSett.mbSelected = rMarkData.GetTableSelect( nTab );
+ rMarkData.FillRangeListWithMarks( &rTabSett.maSelection, true );
+ rTabSett.mbShowGrid = pViewTab->bShowGrid;
+
+ // view mode and zoom
+ rTabSett.mbPageMode = bPagebreak;
+ rTabSett.mnNormalZoom = static_cast< tools::Long >( pViewTab->aZoomY * Fraction( 100.0 ) );
+ rTabSett.mnPageZoom = static_cast< tools::Long >( pViewTab->aPageZoomY * Fraction( 100.0 ) );
+ }
+ }
+}
+
+void ScViewData::ReadExtOptions( const ScExtDocOptions& rDocOpt )
+{
+ // *** Get extended document data from import filters ***
+
+ if( !rDocOpt.IsChanged() ) return;
+
+ // document settings
+ const ScExtDocSettings& rDocSett = rDocOpt.GetDocSettings();
+
+ // displayed sheet
+ SetTabNo( rDocSett.mnDisplTab );
+
+ /* Width of the tabbar, relative to frame window width. We do not have the
+ correct width of the frame window here -> store in ScTabView, which sets
+ the size in the next resize. */
+ pView->SetPendingRelTabBarWidth( rDocSett.mfTabBarWidth );
+
+ // sheet settings
+ SCTAB nLastTab = rDocOpt.GetLastTab();
+ if (static_cast<SCTAB>(maTabData.size()) <= nLastTab)
+ maTabData.resize(nLastTab+1);
+
+ for( SCTAB nTab = 0; nTab < static_cast<SCTAB>(maTabData.size()); ++nTab )
+ {
+ if( const ScExtTabSettings* pTabSett = rDocOpt.GetTabSettings( nTab ) )
+ {
+ if( !maTabData[ nTab ] )
+ maTabData[nTab].reset(new ScViewDataTable(&mrDoc));
+
+ const ScExtTabSettings& rTabSett = *pTabSett;
+ ScViewDataTable& rViewTab = *maTabData[ nTab ];
+
+ // split mode initialization
+ bool bFrozen = rTabSett.mbFrozenPanes;
+ bool bHSplit = bFrozen ? (rTabSett.maFreezePos.Col() > 0) : (rTabSett.maSplitPos.X() > 0);
+ bool bVSplit = bFrozen ? (rTabSett.maFreezePos.Row() > 0) : (rTabSett.maSplitPos.Y() > 0);
+
+ // first visible cell of top-left pane and additional panes
+ if (rTabSett.maFirstVis.IsValid())
+ {
+ rViewTab.nPosX[ SC_SPLIT_LEFT ] = rTabSett.maFirstVis.Col();
+ rViewTab.nPosY[ bVSplit ? SC_SPLIT_TOP : SC_SPLIT_BOTTOM ] = rTabSett.maFirstVis.Row();
+ }
+
+ if (rTabSett.maSecondVis.IsValid())
+ {
+ if (bHSplit)
+ rViewTab.nPosX[ SC_SPLIT_RIGHT ] = rTabSett.maSecondVis.Col();
+ if (bVSplit)
+ rViewTab.nPosY[ SC_SPLIT_BOTTOM ] = rTabSett.maSecondVis.Row();
+ }
+
+ // split mode, split and freeze position
+ rViewTab.eHSplitMode = rViewTab.eVSplitMode = SC_SPLIT_NONE;
+ rViewTab.nHSplitPos = rViewTab.nVSplitPos = 0;
+ rViewTab.nFixPosX = 0;
+ rViewTab.nFixPosY = 0;
+ if( bFrozen )
+ {
+ if( bHSplit )
+ {
+ rViewTab.eHSplitMode = SC_SPLIT_FIX;
+ rViewTab.nFixPosX = rTabSett.maFreezePos.Col();
+ UpdateFixX( nTab );
+ }
+ if( bVSplit )
+ {
+ rViewTab.eVSplitMode = SC_SPLIT_FIX;
+ rViewTab.nFixPosY = rTabSett.maFreezePos.Row();
+ UpdateFixY( nTab );
+ }
+ }
+ else
+ {
+ Point aPixel = Application::GetDefaultDevice()->LogicToPixel(
+ rTabSett.maSplitPos, MapMode( MapUnit::MapTwip ) ); //! Zoom?
+ // the test for use of printer metrics for text formatting here
+ // effectively results in the nFactor = 1.0 regardless of the Option setting.
+ if( pDocShell && SC_MOD()->GetInputOptions().GetTextWysiwyg())
+ {
+ double nFactor = pDocShell->GetOutputFactor();
+ aPixel.setX( static_cast<tools::Long>( aPixel.X() * nFactor + 0.5 ) );
+ }
+
+ bHSplit = bHSplit && aPixel.X() > 0;
+ bVSplit = bVSplit && aPixel.Y() > 0;
+ if( bHSplit )
+ {
+ rViewTab.eHSplitMode = SC_SPLIT_NORMAL;
+ rViewTab.nHSplitPos = aPixel.X();
+ }
+ if( bVSplit )
+ {
+ rViewTab.eVSplitMode = SC_SPLIT_NORMAL;
+ rViewTab.nVSplitPos = aPixel.Y();
+ }
+ }
+
+ // active pane
+ ScSplitPos ePos = SC_SPLIT_BOTTOMLEFT;
+ switch( rTabSett.meActivePane )
+ {
+ // no horizontal split -> always use left panes
+ // no vertical split -> always use *bottom* panes
+ case SCEXT_PANE_TOPLEFT:
+ ePos = bVSplit ? SC_SPLIT_TOPLEFT : SC_SPLIT_BOTTOMLEFT;
+ break;
+ case SCEXT_PANE_TOPRIGHT:
+ ePos = bHSplit ?
+ (bVSplit ? SC_SPLIT_TOPRIGHT : SC_SPLIT_BOTTOMRIGHT) :
+ (bVSplit ? SC_SPLIT_TOPLEFT : SC_SPLIT_BOTTOMLEFT);
+ break;
+ case SCEXT_PANE_BOTTOMLEFT:
+ ePos = SC_SPLIT_BOTTOMLEFT;
+ break;
+ case SCEXT_PANE_BOTTOMRIGHT:
+ ePos = bHSplit ? SC_SPLIT_BOTTOMRIGHT : SC_SPLIT_BOTTOMLEFT;
+ break;
+ }
+ rViewTab.eWhichActive = ePos;
+
+ // cursor position
+ const ScAddress& rCursor = rTabSett.maCursor;
+ if( rCursor.IsValid() )
+ {
+ rViewTab.nCurX = rCursor.Col();
+ rViewTab.nCurY = rCursor.Row();
+ }
+
+ // sheet selection and selected ranges
+ ScMarkData& rMarkData = GetMarkData();
+ rMarkData.SelectTable( nTab, rTabSett.mbSelected );
+
+ // zoom for each sheet
+ if( rTabSett.mnNormalZoom )
+ rViewTab.aZoomX = rViewTab.aZoomY = Fraction( rTabSett.mnNormalZoom, 100 );
+ if( rTabSett.mnPageZoom )
+ rViewTab.aPageZoomX = rViewTab.aPageZoomY = Fraction( rTabSett.mnPageZoom, 100 );
+
+ rViewTab.bShowGrid = rTabSett.mbShowGrid;
+
+ // get some settings from displayed Excel sheet, set at Calc document
+ if( nTab == GetTabNo() )
+ {
+ // view mode and default zoom (for new sheets) from current sheet
+ if( rTabSett.mnNormalZoom )
+ aDefZoomX = aDefZoomY = Fraction( rTabSett.mnNormalZoom, 100L );
+ if( rTabSett.mnPageZoom )
+ aDefPageZoomX = aDefPageZoomY = Fraction( rTabSett.mnPageZoom, 100L );
+ /* #i46820# set pagebreak mode via SetPagebreakMode(), this will
+ update map modes that are needed to draw text correctly. */
+ SetPagebreakMode( rTabSett.mbPageMode );
+ }
+ }
+ }
+
+ if (comphelper::LibreOfficeKit::isActive())
+ DeriveLOKFreezeAllSheets();
+
+ // RecalcPixPos or so - also nMPos - also for ReadUserData ??!?!
+}
+
+void ScViewData::WriteUserDataSequence(uno::Sequence <beans::PropertyValue>& rSettings) const
+{
+ rSettings.realloc(SC_VIEWSETTINGS_COUNT);
+ // + 1, because we have to put the view id in the sequence
+ beans::PropertyValue* pSettings = rSettings.getArray();
+
+ sal_uInt16 nViewID(pView->GetViewFrame().GetCurViewId());
+ pSettings[SC_VIEW_ID].Name = SC_VIEWID;
+ pSettings[SC_VIEW_ID].Value <<= SC_VIEW + OUString::number(nViewID);
+
+ uno::Reference<container::XNameContainer> xNameContainer =
+ document::NamedPropertyValues::create( comphelper::getProcessComponentContext() );
+ for (SCTAB nTab=0; nTab<static_cast<SCTAB>(maTabData.size()); nTab++)
+ {
+ if (maTabData[nTab])
+ {
+ uno::Sequence <beans::PropertyValue> aTableViewSettings;
+ maTabData[nTab]->WriteUserDataSequence(aTableViewSettings, *this, nTab);
+ OUString sTabName;
+ GetDocument().GetName( nTab, sTabName );
+ try
+ {
+ xNameContainer->insertByName(sTabName, uno::Any(aTableViewSettings));
+ }
+ //#101739#; two tables with the same name are possible
+ catch ( container::ElementExistException& )
+ {
+ OSL_FAIL("seems there are two tables with the same name");
+ }
+ catch ( uno::RuntimeException& )
+ {
+ OSL_FAIL("something went wrong");
+ }
+ }
+ }
+ pSettings[SC_TABLE_VIEWSETTINGS].Name = SC_TABLES;
+ pSettings[SC_TABLE_VIEWSETTINGS].Value <<= xNameContainer;
+
+ OUString sName;
+ GetDocument().GetName( nTabNo, sName );
+ pSettings[SC_ACTIVE_TABLE].Name = SC_ACTIVETABLE;
+ pSettings[SC_ACTIVE_TABLE].Value <<= sName;
+ pSettings[SC_HORIZONTAL_SCROLL_BAR_WIDTH].Name = SC_HORIZONTALSCROLLBARWIDTH;
+ pSettings[SC_HORIZONTAL_SCROLL_BAR_WIDTH].Value <<= sal_Int32(pView->GetTabBarWidth());
+ sal_Int32 nZoomValue = tools::Long(pThisTab->aZoomY * 100);
+ sal_Int32 nPageZoomValue = tools::Long(pThisTab->aPageZoomY * 100);
+ pSettings[SC_ZOOM_TYPE].Name = SC_ZOOMTYPE;
+ pSettings[SC_ZOOM_TYPE].Value <<= sal_Int16(pThisTab->eZoomType);
+ pSettings[SC_ZOOM_VALUE].Name = SC_ZOOMVALUE;
+ pSettings[SC_ZOOM_VALUE].Value <<= nZoomValue;
+ pSettings[SC_PAGE_VIEW_ZOOM_VALUE].Name = SC_PAGEVIEWZOOMVALUE;
+ pSettings[SC_PAGE_VIEW_ZOOM_VALUE].Value <<= nPageZoomValue;
+ pSettings[SC_PAGE_BREAK_PREVIEW].Name = SC_SHOWPAGEBREAKPREVIEW;
+ pSettings[SC_PAGE_BREAK_PREVIEW].Value <<= bPagebreak;
+
+ pSettings[SC_SHOWZERO].Name = SC_UNO_SHOWZERO;
+ pSettings[SC_SHOWZERO].Value <<= maOptions.GetOption(VOPT_NULLVALS);
+ pSettings[SC_SHOWNOTES].Name = SC_UNO_SHOWNOTES;
+ pSettings[SC_SHOWNOTES].Value <<= maOptions.GetOption(VOPT_NOTES);
+ pSettings[SC_SHOWFORMULASMARKS].Name = SC_UNO_SHOWFORMULASMARKS;
+ pSettings[SC_SHOWFORMULASMARKS].Value <<= maOptions.GetOption(VOPT_FORMULAS_MARKS);
+ pSettings[SC_SHOWGRID].Name = SC_UNO_SHOWGRID;
+ pSettings[SC_SHOWGRID].Value <<= maOptions.GetOption(VOPT_GRID);
+ pSettings[SC_GRIDCOLOR].Name = SC_UNO_GRIDCOLOR;
+ OUString aColorName;
+ Color aColor = maOptions.GetGridColor(&aColorName);
+ pSettings[SC_GRIDCOLOR].Value <<= aColor;
+ pSettings[SC_SHOWPAGEBR].Name = SC_UNO_SHOWPAGEBR;
+ pSettings[SC_SHOWPAGEBR].Value <<= maOptions.GetOption(VOPT_PAGEBREAKS);
+ pSettings[SC_COLROWHDR].Name = SC_UNO_COLROWHDR;
+ pSettings[SC_COLROWHDR].Value <<= maOptions.GetOption(VOPT_HEADER);
+ pSettings[SC_SHEETTABS].Name = SC_UNO_SHEETTABS;
+ pSettings[SC_SHEETTABS].Value <<= maOptions.GetOption(VOPT_TABCONTROLS);
+ pSettings[SC_OUTLSYMB].Name = SC_UNO_OUTLSYMB;
+ pSettings[SC_OUTLSYMB].Value <<= maOptions.GetOption(VOPT_OUTLINER);
+ pSettings[SC_VALUE_HIGHLIGHTING].Name = SC_UNO_VALUEHIGH;
+ pSettings[SC_VALUE_HIGHLIGHTING].Value <<= maOptions.GetOption(VOPT_SYNTAX);
+ pSettings[SC_FORMULA_BAR_HEIGHT_VALUE].Name = SC_FORMULABARHEIGHT;
+ pSettings[SC_FORMULA_BAR_HEIGHT_VALUE].Value <<= GetFormulaBarLines();;
+
+ const ScGridOptions& aGridOpt = maOptions.GetGridOptions();
+ pSettings[SC_SNAPTORASTER].Name = SC_UNO_SNAPTORASTER;
+ pSettings[SC_SNAPTORASTER].Value <<= aGridOpt.GetUseGridSnap();
+ pSettings[SC_RASTERVIS].Name = SC_UNO_RASTERVIS;
+ pSettings[SC_RASTERVIS].Value <<= aGridOpt.GetGridVisible();
+ pSettings[SC_RASTERRESX].Name = SC_UNO_RASTERRESX;
+ pSettings[SC_RASTERRESX].Value <<= static_cast<sal_Int32>(aGridOpt.GetFieldDrawX());
+ pSettings[SC_RASTERRESY].Name = SC_UNO_RASTERRESY;
+ pSettings[SC_RASTERRESY].Value <<= static_cast<sal_Int32>(aGridOpt.GetFieldDrawY());
+ pSettings[SC_RASTERSUBX].Name = SC_UNO_RASTERSUBX;
+ pSettings[SC_RASTERSUBX].Value <<= static_cast<sal_Int32>(aGridOpt.GetFieldDivisionX());
+ pSettings[SC_RASTERSUBY].Name = SC_UNO_RASTERSUBY;
+ pSettings[SC_RASTERSUBY].Value <<= static_cast<sal_Int32>(aGridOpt.GetFieldDivisionY());
+ pSettings[SC_RASTERSYNC].Name = SC_UNO_RASTERSYNC;
+ pSettings[SC_RASTERSYNC].Value <<= aGridOpt.GetSynchronize();
+
+ // Common SdrModel processing
+ GetDocument().GetDrawLayer()->WriteUserDataSequence(rSettings);
+}
+
+void ScViewData::ReadUserDataSequence(const uno::Sequence <beans::PropertyValue>& rSettings)
+{
+ std::vector<bool> aHasZoomVect( GetDocument().GetTableCount(), false );
+
+ sal_Int32 nTemp32(0);
+ sal_Int16 nTemp16(0);
+ sal_Int16 nFormulaBarLineCount(0);
+ bool bPageMode(false);
+
+ EnsureTabDataSize(GetDocument().GetTableCount());
+
+ for (const auto& rSetting : rSettings)
+ {
+ // SC_VIEWID has to parse and use by mba
+ OUString sName(rSetting.Name);
+ if (sName == SC_TABLES)
+ {
+ uno::Reference<container::XNameContainer> xNameContainer;
+ if ((rSetting.Value >>= xNameContainer) && xNameContainer->hasElements())
+ {
+ const uno::Sequence< OUString > aNames(xNameContainer->getElementNames());
+ for (const OUString& sTabName : aNames)
+ {
+ SCTAB nTab(0);
+ if (GetDocument().GetTable(sTabName, nTab))
+ {
+ uno::Any aAny = xNameContainer->getByName(sTabName);
+ uno::Sequence<beans::PropertyValue> aTabSettings;
+ if (aAny >>= aTabSettings)
+ {
+ EnsureTabDataSize(nTab + 1);
+ if (!maTabData[nTab])
+ maTabData[nTab].reset(new ScViewDataTable(&mrDoc));
+
+ bool bHasZoom = false;
+ maTabData[nTab]->ReadUserDataSequence(aTabSettings, *this, nTab, bHasZoom);
+ aHasZoomVect[nTab] = bHasZoom;
+ }
+ }
+ }
+ }
+ }
+ else if (sName == SC_ACTIVETABLE)
+ {
+ OUString sTabName;
+ if(rSetting.Value >>= sTabName)
+ {
+ SCTAB nTab(0);
+ if (GetDocument().GetTable(sTabName, nTab))
+ nTabNo = nTab;
+ }
+ }
+ else if (sName == SC_HORIZONTALSCROLLBARWIDTH)
+ {
+ if (rSetting.Value >>= nTemp32)
+ pView->SetTabBarWidth(nTemp32);
+ }
+ else if (sName == SC_RELHORIZONTALTABBARWIDTH)
+ {
+ double fWidth = 0.0;
+ if (rSetting.Value >>= fWidth)
+ pView->SetPendingRelTabBarWidth( fWidth );
+ }
+ else if (sName == SC_ZOOMTYPE)
+ {
+ if (rSetting.Value >>= nTemp16)
+ eDefZoomType = SvxZoomType(nTemp16);
+ }
+ else if (sName == SC_ZOOMVALUE)
+ {
+ if (rSetting.Value >>= nTemp32)
+ {
+ Fraction aZoom(nTemp32, 100);
+ aDefZoomX = aDefZoomY = aZoom;
+ }
+ }
+ else if (sName == SC_PAGEVIEWZOOMVALUE)
+ {
+ if (rSetting.Value >>= nTemp32)
+ {
+ Fraction aZoom(nTemp32, 100);
+ aDefPageZoomX = aDefPageZoomY = aZoom;
+ }
+ }
+ else if (sName == SC_FORMULABARHEIGHT)
+ {
+ if (rSetting.Value >>= nFormulaBarLineCount)
+ {
+ SetFormulaBarLines(nFormulaBarLineCount);
+ // Notify formula bar about changed lines
+ ScInputHandler* pInputHdl = SC_MOD()->GetInputHdl();
+ if (pInputHdl)
+ {
+ ScInputWindow* pInputWin = pInputHdl->GetInputWindow();
+ if (pInputWin)
+ pInputWin->NumLinesChanged();
+ }
+ }
+ }
+ else if (sName == SC_SHOWPAGEBREAKPREVIEW)
+ bPageMode = ScUnoHelpFunctions::GetBoolFromAny( rSetting.Value );
+ else if ( sName == SC_UNO_SHOWZERO )
+ maOptions.SetOption(VOPT_NULLVALS, ScUnoHelpFunctions::GetBoolFromAny(rSetting.Value));
+ else if ( sName == SC_UNO_SHOWNOTES )
+ maOptions.SetOption(VOPT_NOTES, ScUnoHelpFunctions::GetBoolFromAny(rSetting.Value));
+ else if ( sName == SC_UNO_SHOWFORMULASMARKS )
+ maOptions.SetOption(VOPT_FORMULAS_MARKS, ScUnoHelpFunctions::GetBoolFromAny(rSetting.Value));
+ else if ( sName == SC_UNO_SHOWGRID )
+ maOptions.SetOption(VOPT_GRID, ScUnoHelpFunctions::GetBoolFromAny(rSetting.Value));
+ else if ( sName == SC_UNO_GRIDCOLOR )
+ {
+ Color aColor;
+ if (rSetting.Value >>= aColor)
+ maOptions.SetGridColor(aColor, OUString());
+ }
+ else if ( sName == SC_UNO_SHOWPAGEBR )
+ maOptions.SetOption(VOPT_PAGEBREAKS, ScUnoHelpFunctions::GetBoolFromAny(rSetting.Value));
+ else if ( sName == SC_UNO_COLROWHDR )
+ maOptions.SetOption(VOPT_HEADER, ScUnoHelpFunctions::GetBoolFromAny(rSetting.Value));
+ else if ( sName == SC_UNO_SHEETTABS )
+ maOptions.SetOption(VOPT_TABCONTROLS, ScUnoHelpFunctions::GetBoolFromAny(rSetting.Value));
+ else if ( sName == SC_UNO_OUTLSYMB )
+ maOptions.SetOption(VOPT_OUTLINER, ScUnoHelpFunctions::GetBoolFromAny(rSetting.Value));
+ else if ( sName == SC_UNO_SHOWOBJ )
+ {
+ // #i80528# placeholders not supported anymore
+ if ( rSetting.Value >>= nTemp16 )
+ maOptions.SetObjMode(VOBJ_TYPE_OLE, (nTemp16 == 1) ? VOBJ_MODE_HIDE : VOBJ_MODE_SHOW);
+ }
+ else if ( sName == SC_UNO_SHOWCHARTS )
+ {
+ // #i80528# placeholders not supported anymore
+ if ( rSetting.Value >>= nTemp16 )
+ maOptions.SetObjMode(VOBJ_TYPE_CHART, (nTemp16 == 1) ? VOBJ_MODE_HIDE : VOBJ_MODE_SHOW);
+ }
+ else if ( sName == SC_UNO_SHOWDRAW )
+ {
+ // #i80528# placeholders not supported anymore
+ if ( rSetting.Value >>= nTemp16 )
+ maOptions.SetObjMode(VOBJ_TYPE_DRAW, (nTemp16 == 1) ? VOBJ_MODE_HIDE : VOBJ_MODE_SHOW);
+ }
+ else if ( sName == SC_UNO_VALUEHIGH && !comphelper::LibreOfficeKit::isActive() )
+ maOptions.SetOption(VOPT_SYNTAX, ScUnoHelpFunctions::GetBoolFromAny(rSetting.Value));
+ else
+ {
+ ScGridOptions aGridOpt(maOptions.GetGridOptions());
+ if ( sName == SC_UNO_SNAPTORASTER )
+ aGridOpt.SetUseGridSnap( ScUnoHelpFunctions::GetBoolFromAny( rSetting.Value ) );
+ else if ( sName == SC_UNO_RASTERVIS )
+ aGridOpt.SetGridVisible( ScUnoHelpFunctions::GetBoolFromAny( rSetting.Value ) );
+ else if ( sName == SC_UNO_RASTERRESX )
+ aGridOpt.SetFieldDrawX( static_cast <sal_uInt32> ( ScUnoHelpFunctions::GetInt32FromAny( rSetting.Value ) ) );
+ else if ( sName == SC_UNO_RASTERRESY )
+ aGridOpt.SetFieldDrawY( static_cast <sal_uInt32> ( ScUnoHelpFunctions::GetInt32FromAny( rSetting.Value ) ) );
+ else if ( sName == SC_UNO_RASTERSUBX )
+ aGridOpt.SetFieldDivisionX( static_cast <sal_uInt32> ( ScUnoHelpFunctions::GetInt32FromAny( rSetting.Value ) ) );
+ else if ( sName == SC_UNO_RASTERSUBY )
+ aGridOpt.SetFieldDivisionY( static_cast <sal_uInt32> ( ScUnoHelpFunctions::GetInt32FromAny( rSetting.Value ) ) );
+ else if ( sName == SC_UNO_RASTERSYNC )
+ aGridOpt.SetSynchronize( ScUnoHelpFunctions::GetBoolFromAny( rSetting.Value ) );
+ // Fallback to common SdrModel processing
+ else GetDocument().GetDrawLayer()->ReadUserDataSequenceValue(&rSetting);
+
+ maOptions.SetGridOptions(aGridOpt);
+ }
+ }
+
+ // copy default zoom to sheets where a different one wasn't specified
+ for (SCTAB nZoomTab=0; nZoomTab< static_cast<SCTAB>(maTabData.size()); ++nZoomTab)
+ if (maTabData[nZoomTab] && ( nZoomTab >= static_cast<SCTAB>(aHasZoomVect.size()) || !aHasZoomVect[nZoomTab] ))
+ {
+ maTabData[nZoomTab]->eZoomType = eDefZoomType;
+ maTabData[nZoomTab]->aZoomX = aDefZoomX;
+ maTabData[nZoomTab]->aZoomY = aDefZoomY;
+ maTabData[nZoomTab]->aPageZoomX = aDefPageZoomX;
+ maTabData[nZoomTab]->aPageZoomY = aDefPageZoomY;
+ }
+
+ if (rSettings.hasElements())
+ SetPagebreakMode( bPageMode );
+
+ // #i47426# write view options to document, needed e.g. for Excel export
+ mrDoc.SetViewOptions(maOptions);
+
+ if (comphelper::LibreOfficeKit::isActive())
+ DeriveLOKFreezeAllSheets();
+}
+
+void ScViewData::SetOptions( const ScViewOptions& rOpt )
+{
+ // if visibility of horizontal ScrollBar is changed, TabBar may have to be resized...
+ bool bHScrollChanged = (rOpt.GetOption(VOPT_HSCROLL) != maOptions.GetOption(VOPT_HSCROLL));
+
+ // if graphics are turned on or off, animation has to be started or stopped
+ // graphics are controlled by VOBJ_TYPE_OLE
+ bool bGraphicsChanged = (maOptions.GetObjMode(VOBJ_TYPE_OLE) !=
+ rOpt.GetObjMode(VOBJ_TYPE_OLE) );
+
+ maOptions = rOpt;
+ OSL_ENSURE( pView, "No View" );
+
+ if( pView )
+ {
+ pView->ViewOptionsHasChanged( bHScrollChanged, bGraphicsChanged );
+ }
+}
+
+Point ScViewData::GetMousePosPixel()
+{
+ OSL_ENSURE( pView, "GetMousePosPixel() without View" );
+ return pView->GetMousePosPixel();
+}
+
+void ScViewData::UpdateInputHandler( bool bForce )
+{
+ if (pView)
+ pView->UpdateInputHandler(bForce);
+}
+
+bool ScViewData::IsOle() const
+{
+ return pDocShell && pDocShell->IsOle();
+}
+
+bool ScViewData::UpdateFixX( SCTAB nTab ) // true = value changed
+{
+ if (!ValidTab(nTab)) // Default
+ nTab=nTabNo; // current table
+
+ if (!pView || maTabData[nTab]->eHSplitMode != SC_SPLIT_FIX)
+ return false;
+
+ ScDocument& rLocalDoc = GetDocument();
+ if (!rLocalDoc.HasTable(nTab)) // if called from reload, the sheet may not exist
+ return false;
+
+ SCCOL nFix = maTabData[nTab]->nFixPosX;
+ tools::Long nNewPos = 0;
+ for (SCCOL nX=maTabData[nTab]->nPosX[SC_SPLIT_LEFT]; nX<nFix; nX++)
+ {
+ sal_uInt16 nTSize = rLocalDoc.GetColWidth( nX, nTab );
+ if (nTSize)
+ {
+ tools::Long nPix = ToPixel( nTSize, nPPTX );
+ nNewPos += nPix;
+ }
+ }
+ nNewPos += pView->GetGridOffset().X();
+ if (nNewPos != maTabData[nTab]->nHSplitPos)
+ {
+ maTabData[nTab]->nHSplitPos = nNewPos;
+ if (nTab == nTabNo)
+ RecalcPixPos(); // should not be needed
+ return true;
+ }
+
+ return false;
+}
+
+bool ScViewData::UpdateFixY( SCTAB nTab ) // true = value changed
+{
+ if (!ValidTab(nTab)) // Default
+ nTab=nTabNo; // current table
+
+ if (!pView || maTabData[nTab]->eVSplitMode != SC_SPLIT_FIX)
+ return false;
+
+ ScDocument& rLocalDoc = GetDocument();
+ if (!rLocalDoc.HasTable(nTab)) // if called from reload, the sheet may not exist
+ return false;
+
+ SCROW nFix = maTabData[nTab]->nFixPosY;
+ tools::Long nNewPos = 0;
+ for (SCROW nY=maTabData[nTab]->nPosY[SC_SPLIT_TOP]; nY<nFix; nY++)
+ {
+ sal_uInt16 nTSize = rLocalDoc.GetRowHeight( nY, nTab );
+ if (nTSize)
+ {
+ tools::Long nPix = ToPixel( nTSize, nPPTY );
+ nNewPos += nPix;
+ }
+ }
+ nNewPos += pView->GetGridOffset().Y();
+ if (nNewPos != maTabData[nTab]->nVSplitPos)
+ {
+ maTabData[nTab]->nVSplitPos = nNewPos;
+ if (nTab == nTabNo)
+ RecalcPixPos(); // should not be needed
+ return true;
+ }
+
+ return false;
+}
+
+void ScViewData::UpdateOutlinerFlags( Outliner& rOutl ) const
+{
+ ScDocument& rLocalDoc = GetDocument();
+ bool bOnlineSpell = rLocalDoc.GetDocOptions().IsAutoSpell();
+
+ EEControlBits nCntrl = rOutl.GetControlWord();
+ nCntrl |= EEControlBits::MARKNONURLFIELDS;
+ nCntrl &= ~EEControlBits::MARKURLFIELDS; // URLs not shaded for output
+ nCntrl |= EEControlBits::AUTOCORRECT;
+ if( bOnlineSpell )
+ nCntrl |= EEControlBits::ONLINESPELLING;
+ else
+ nCntrl &= ~EEControlBits::ONLINESPELLING;
+ rOutl.SetControlWord(nCntrl);
+
+ rOutl.SetCalcFieldValueHdl( LINK( SC_MOD(), ScModule, CalcFieldValueHdl ) );
+
+ // don't call GetSpellChecker if online spelling isn't enabled.
+ // The language for AutoCorrect etc. is taken from the pool defaults
+ // (set in ScDocument::UpdateDrawLanguages)
+
+ if ( bOnlineSpell )
+ {
+ css::uno::Reference<css::linguistic2::XSpellChecker1> xXSpellChecker1( LinguMgr::GetSpellChecker() );
+ rOutl.SetSpeller( xXSpellChecker1 );
+ }
+
+ rOutl.SetDefaultHorizontalTextDirection(
+ rLocalDoc.GetEditTextDirection( nTabNo ) );
+}
+
+ScAddress ScViewData::GetCurPos() const
+{
+ return ScAddress( GetCurX(), GetCurY(), GetTabNo() );
+}
+
+void ScViewData::SetRefStart( SCCOL nNewX, SCROW nNewY, SCTAB nNewZ )
+{
+ nRefStartX = nNewX; nRefStartY = nNewY; nRefStartZ = nNewZ;
+}
+
+void ScViewData::SetRefEnd( SCCOL nNewX, SCROW nNewY, SCTAB nNewZ )
+{
+ nRefEndX = nNewX; nRefEndY = nNewY; nRefEndZ = nNewZ;
+}
+
+void ScViewData::AddPixelsWhile( tools::Long & rScrY, tools::Long nEndPixels, SCROW & rPosY,
+ SCROW nEndRow, double nPPTY, const ScDocument * pDoc, SCTAB nTabNo )
+{
+ SCROW nRow = rPosY;
+ while (rScrY <= nEndPixels && nRow <= nEndRow)
+ {
+ SCROW nHeightEndRow;
+ sal_uInt16 nHeight = pDoc->GetRowHeight( nRow, nTabNo, nullptr, &nHeightEndRow);
+ if (nHeightEndRow > nEndRow)
+ nHeightEndRow = nEndRow;
+ if (!nHeight)
+ {
+ if (ValidTab(nTabNo) && nTabNo <= pDoc->GetMaxTableNumber())
+ nRow = nHeightEndRow + 1;
+ else
+ break;
+ }
+ else
+ {
+ SCROW nRows = nHeightEndRow - nRow + 1;
+ sal_Int64 nPixel = ToPixel( nHeight, nPPTY);
+ sal_Int64 nAdd = nPixel * nRows;
+ if (nAdd + rScrY > nEndPixels)
+ {
+ sal_Int64 nDiff = rScrY + nAdd - nEndPixels;
+ nRows -= static_cast<SCROW>(nDiff / nPixel);
+ nAdd = nPixel * nRows;
+ // We're looking for a value that satisfies loop condition.
+ if (nAdd + rScrY <= nEndPixels)
+ {
+ ++nRows;
+ nAdd += nPixel;
+ }
+ }
+ rScrY += static_cast<tools::Long>(nAdd);
+ nRow += nRows;
+ }
+ }
+ if (nRow > rPosY)
+ --nRow;
+ rPosY = nRow;
+}
+
+void ScViewData::AddPixelsWhileBackward( tools::Long & rScrY, tools::Long nEndPixels,
+ SCROW & rPosY, SCROW nStartRow, double nPPTY, const ScDocument * pDoc,
+ SCTAB nTabNo )
+{
+ SCROW nRow = rPosY;
+ while (rScrY <= nEndPixels && nRow >= nStartRow)
+ {
+ SCROW nHeightStartRow;
+ sal_uInt16 nHeight = pDoc->GetRowHeight( nRow, nTabNo, &nHeightStartRow, nullptr);
+ if (nHeightStartRow < nStartRow)
+ nHeightStartRow = nStartRow;
+ if (!nHeight)
+ nRow = nHeightStartRow - 1;
+ else
+ {
+ SCROW nRows = nRow - nHeightStartRow + 1;
+ sal_Int64 nPixel = ToPixel( nHeight, nPPTY);
+ sal_Int64 nAdd = nPixel * nRows;
+ if (nAdd + rScrY > nEndPixels)
+ {
+ sal_Int64 nDiff = nAdd + rScrY - nEndPixels;
+ nRows -= static_cast<SCROW>(nDiff / nPixel);
+ nAdd = nPixel * nRows;
+ // We're looking for a value that satisfies loop condition.
+ if (nAdd + rScrY <= nEndPixels)
+ {
+ ++nRows;
+ nAdd += nPixel;
+ }
+ }
+ rScrY += static_cast<tools::Long>(nAdd);
+ nRow -= nRows;
+ }
+ }
+ if (nRow < rPosY)
+ ++nRow;
+ rPosY = nRow;
+}
+
+SCCOLROW ScViewData::GetLOKSheetFreezeIndex(bool bIsCol) const
+{
+ SCCOLROW nFreezeIndex = bIsCol ? mrDoc.GetLOKFreezeCol(nTabNo) : mrDoc.GetLOKFreezeRow(nTabNo);
+ return nFreezeIndex >= 0 ? nFreezeIndex : 0;
+}
+
+bool ScViewData::SetLOKSheetFreezeIndex(const SCCOLROW nFreezeIndex, bool bIsCol, SCTAB nForTab)
+{
+ if (nForTab == -1)
+ {
+ nForTab = nTabNo;
+ }
+ else if (!ValidTab(nForTab) || (nForTab >= static_cast<SCTAB>(maTabData.size())))
+ {
+ SAL_WARN("sc.viewdata", "ScViewData::SetLOKSheetFreezeIndex : invalid nForTab = " << nForTab);
+ return false;
+ }
+
+ return bIsCol ? mrDoc.SetLOKFreezeCol(static_cast<SCCOL>(nFreezeIndex), nForTab)
+ : mrDoc.SetLOKFreezeRow(static_cast<SCROW>(nFreezeIndex), nForTab);
+}
+
+bool ScViewData::RemoveLOKFreeze()
+{
+ bool colUnfreezed = SetLOKSheetFreezeIndex(0, true);
+ bool rowUnfreezed = SetLOKSheetFreezeIndex(0, false);
+ return colUnfreezed || rowUnfreezed;
+}
+
+void ScViewData::DeriveLOKFreezeAllSheets()
+{
+ SCTAB nMaxTab = static_cast<SCTAB>(maTabData.size()) - 1;
+ for (SCTAB nTab = 0; nTab <= nMaxTab; ++nTab)
+ DeriveLOKFreezeIfNeeded(nTab);
+}
+
+void ScViewData::DeriveLOKFreezeIfNeeded(SCTAB nForTab)
+{
+ if (!ValidTab(nForTab) || (nForTab >= static_cast<SCTAB>(maTabData.size())))
+ {
+ SAL_WARN("sc.viewdata", "ScViewData::DeriveLOKFreezeIfNeeded : invalid nForTab = " << nForTab);
+ return;
+ }
+
+ ScViewDataTable* pViewTable = maTabData[nForTab].get();
+ if (!pViewTable)
+ return;
+
+ bool bConvertToFreezeX = false;
+ bool bConvertToFreezeY = false;
+ SCCOL nFreezeCol = mrDoc.GetLOKFreezeCol(nForTab);
+ SCROW nFreezeRow = mrDoc.GetLOKFreezeRow(nForTab);
+
+ if (nFreezeCol == -1)
+ {
+ ScSplitMode eSplitMode = pViewTable->eHSplitMode;
+ if (eSplitMode == SC_SPLIT_FIX)
+ nFreezeCol = pViewTable->nFixPosX;
+ else if (eSplitMode == SC_SPLIT_NORMAL)
+ bConvertToFreezeX = true;
+ else
+ nFreezeCol = 0;
+ }
+
+ if (nFreezeRow == -1)
+ {
+ ScSplitMode eSplitMode = pViewTable->eVSplitMode;
+ if (eSplitMode == SC_SPLIT_FIX)
+ nFreezeRow = pViewTable->nFixPosY;
+ else if (eSplitMode == SC_SPLIT_NORMAL)
+ bConvertToFreezeY = true;
+ else
+ nFreezeRow = 0;
+ }
+
+ if (bConvertToFreezeX || bConvertToFreezeY)
+ {
+ SCCOL nCol;
+ SCROW nRow;
+ GetPosFromPixel(bConvertToFreezeX ? pViewTable->nHSplitPos : 0,
+ bConvertToFreezeY ? pViewTable->nVSplitPos : 0,
+ SC_SPLIT_BOTTOMLEFT, nCol, nRow,
+ false /* bTestMerge */, false /* bRepair */,
+ nForTab);
+ if (bConvertToFreezeX)
+ nFreezeCol = nCol;
+ if (bConvertToFreezeY)
+ nFreezeRow = nRow;
+ }
+
+ mrDoc.SetLOKFreezeCol(nFreezeCol, nForTab);
+ mrDoc.SetLOKFreezeRow(nFreezeRow, nForTab);
+}
+
+void ScViewData::OverrideWithLOKFreeze(ScSplitMode& eExHSplitMode, ScSplitMode& eExVSplitMode,
+ SCCOL& nExFixPosX, SCROW& nExFixPosY,
+ tools::Long& nExHSplitPos, tools::Long& nExVSplitPos, SCTAB nForTab) const
+{
+ SCCOL nFreezeCol = mrDoc.GetLOKFreezeCol(nForTab);
+ SCROW nFreezeRow = mrDoc.GetLOKFreezeRow(nForTab);
+
+ bool bConvertToScrPosX = false;
+ bool bConvertToScrPosY = false;
+
+ if (nFreezeCol >= 0)
+ {
+ if (eExHSplitMode == SC_SPLIT_NONE)
+ eExHSplitMode = SC_SPLIT_FIX;
+
+ if (eExHSplitMode == SC_SPLIT_FIX)
+ {
+ nExFixPosX = nFreezeCol;
+ pThisTab->nPosX[SC_SPLIT_RIGHT] = nFreezeCol;
+ }
+ else
+ bConvertToScrPosX = true;
+ }
+
+ if (nFreezeRow >= 0)
+ {
+ if (eExVSplitMode == SC_SPLIT_NONE)
+ eExVSplitMode = SC_SPLIT_FIX;
+
+ if (eExVSplitMode == SC_SPLIT_FIX)
+ {
+ nExFixPosY = nFreezeRow;
+ pThisTab->nPosY[SC_SPLIT_BOTTOM] = nFreezeRow;
+ }
+ else
+ bConvertToScrPosY = true;
+ }
+
+ if (bConvertToScrPosX || bConvertToScrPosY)
+ {
+ Point aExSplitPos = GetScrPos(nFreezeCol, nFreezeRow, SC_SPLIT_BOTTOMLEFT, true, nForTab);
+ if (bConvertToScrPosX)
+ nExHSplitPos = aExSplitPos.X();
+ if (bConvertToScrPosY)
+ nExVSplitPos = aExSplitPos.Y();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/viewfun2.cxx b/sc/source/ui/view/viewfun2.cxx
new file mode 100644
index 0000000000..224bb722e0
--- /dev/null
+++ b/sc/source/ui/view/viewfun2.cxx
@@ -0,0 +1,3487 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source eCode 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 .
+ */
+
+#include <scitems.hxx>
+
+#include <sfx2/app.hxx>
+#include <sfx2/request.hxx>
+#include <editeng/borderline.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/lineitem.hxx>
+#include <editeng/scripttypeitem.hxx>
+#include <svl/srchitem.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/objitem.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <svl/numformat.hxx>
+#include <svl/stritem.hxx>
+#include <svl/zforlist.hxx>
+#include <svx/srchdlg.hxx>
+#include <svx/svdview.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <osl/diagnose.h>
+
+#include <viewfunc.hxx>
+#include <vcl/uitest/logger.hxx>
+#include <vcl/uitest/eventdescription.hxx>
+
+#include <sc.hrc>
+#include <globstr.hrc>
+#include <scresid.hxx>
+
+#include <attrib.hxx>
+#include <autoform.hxx>
+#include <formulacell.hxx>
+#include <cellmergeoption.hxx>
+#include <compiler.hxx>
+#include <docfunc.hxx>
+#include <docpool.hxx>
+#include <docsh.hxx>
+#include <docoptio.hxx>
+#include <global.hxx>
+#include <patattr.hxx>
+#include <printfun.hxx>
+#include <refundo.hxx>
+#include <table.hxx>
+#include <tablink.hxx>
+#include <tabvwsh.hxx>
+#include <uiitems.hxx>
+#include <undoblk.hxx>
+#include <undotab.hxx>
+#include <sizedev.hxx>
+#include <editable.hxx>
+#include <docuno.hxx>
+#include <charthelper.hxx>
+#include <tabbgcolor.hxx>
+#include <clipparam.hxx>
+#include <prnsave.hxx>
+#include <searchresults.hxx>
+#include <tokenarray.hxx>
+#include <rowheightcontext.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <comphelper/lok.hxx>
+#include <mergecellsdialog.hxx>
+#include <sheetevents.hxx>
+#include <columnspanset.hxx>
+
+#include <vector>
+#include <memory>
+#include <boost/property_tree/json_parser.hpp>
+#include <tools/json_writer.hxx>
+
+#include <officecfg/Office/Calc.hxx>
+
+using namespace com::sun::star;
+using ::editeng::SvxBorderLine;
+
+namespace {
+
+void collectUIInformation(std::map<OUString, OUString>&& aParameters, const OUString& rAction)
+{
+ EventDescription aDescription;
+ aDescription.aID = "grid_window";
+ aDescription.aAction = rAction;
+ aDescription.aParameters = std::move(aParameters);
+ aDescription.aParent = "MainWindow";
+ aDescription.aKeyWord = "ScGridWinUIObject";
+
+ UITestLogger::getInstance().logEvent(aDescription);
+}
+}
+
+using ::std::vector;
+using ::std::unique_ptr;
+
+bool ScViewFunc::AdjustBlockHeight( bool bPaint, ScMarkData* pMarkData )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ if (!pMarkData)
+ pMarkData = &GetViewData().GetMarkData();
+
+ ScDocument& rDoc = pDocSh->GetDocument();
+ std::vector<sc::ColRowSpan> aMarkedRows = pMarkData->GetMarkedRowSpans();
+
+ if (aMarkedRows.empty())
+ {
+ SCROW nCurRow = GetViewData().GetCurY();
+ aMarkedRows.emplace_back(nCurRow, nCurRow);
+ }
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ SCCOLROW nStart = aMarkedRows[0].mnStart;
+ OnLOKSetWidthOrHeight(nStart, /*width: */ false);
+ }
+
+ double nPPTX = GetViewData().GetPPTX();
+ double nPPTY = GetViewData().GetPPTY();
+ Fraction aZoomX = GetViewData().GetZoomX();
+ Fraction aZoomY = GetViewData().GetZoomY();
+
+ ScSizeDeviceProvider aProv(pDocSh);
+ if (aProv.IsPrinter())
+ {
+ nPPTX = aProv.GetPPTX();
+ nPPTY = aProv.GetPPTY();
+ aZoomX = aZoomY = Fraction( 1, 1 );
+ }
+
+ sc::RowHeightContext aCxt(rDoc.MaxRow(), nPPTX, nPPTY, aZoomX, aZoomY, aProv.GetDevice());
+ bool bAnyChanged = false;
+ for (const SCTAB& nTab : *pMarkData)
+ {
+ bool bChanged = false;
+ SCROW nPaintY = 0;
+ for (const auto& rRow : aMarkedRows)
+ {
+ SCROW nStartNo = rRow.mnStart;
+ SCROW nEndNo = rRow.mnEnd;
+ ScAddress aTopLeft(0, nStartNo, nTab);
+ rDoc.UpdateScriptTypes(aTopLeft, rDoc.GetSheetLimits().GetMaxColCount(), nEndNo-nStartNo+1);
+ if (rDoc.SetOptimalHeight(aCxt, nStartNo, nEndNo, nTab, true))
+ {
+ if (!bChanged)
+ nPaintY = nStartNo;
+ bAnyChanged = bChanged = true;
+ }
+ }
+ // tdf#76183: recalculate objects' positions
+ if (bChanged)
+ rDoc.SetDrawPageSize(nTab);
+ if ( bPaint && bChanged )
+ pDocSh->PostPaint( 0, nPaintY, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab,
+ PaintPartFlags::Grid | PaintPartFlags::Left );
+ }
+
+ if ( bPaint && bAnyChanged )
+ pDocSh->UpdateOle(GetViewData());
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
+ GetViewData().GetViewShell(),
+ false /* bColumns */, true /* bRows */,
+ true /* bSizes*/, false /* bHidden */, false /* bFiltered */,
+ false /* bGroups */, nTab);
+ ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), ROW_HEADER, nTab);
+ }
+
+ return bAnyChanged;
+}
+
+bool ScViewFunc::AdjustRowHeight( SCROW nStartRow, SCROW nEndRow, bool bApi )
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ OnLOKSetWidthOrHeight(nStartRow, /*width: */ false);
+ }
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTab = GetViewData().GetTabNo();
+ double nPPTX = GetViewData().GetPPTX();
+ double nPPTY = GetViewData().GetPPTY();
+ Fraction aZoomX = GetViewData().GetZoomX();
+ Fraction aZoomY = GetViewData().GetZoomY();
+ sal_uInt16 nOldPixel = 0;
+ if (nStartRow == nEndRow)
+ nOldPixel = static_cast<sal_uInt16>(rDoc.GetRowHeight(nStartRow,nTab) * nPPTY);
+
+ ScSizeDeviceProvider aProv(pDocSh);
+ if (aProv.IsPrinter())
+ {
+ nPPTX = aProv.GetPPTX();
+ nPPTY = aProv.GetPPTY();
+ aZoomX = aZoomY = Fraction( 1, 1 );
+ }
+ sc::RowHeightContext aCxt(rDoc.MaxRow(), nPPTX, nPPTY, aZoomX, aZoomY, aProv.GetDevice());
+ bool bChanged = rDoc.SetOptimalHeight(aCxt, nStartRow, nEndRow, nTab, bApi);
+
+ // tdf#76183: recalculate objects' positions
+ if (bChanged)
+ rDoc.SetDrawPageSize(nTab);
+
+ if (bChanged && ( nStartRow == nEndRow ))
+ {
+ sal_uInt16 nNewPixel = static_cast<sal_uInt16>(rDoc.GetRowHeight(nStartRow,nTab) * nPPTY);
+ if ( nNewPixel == nOldPixel )
+ bChanged = false;
+ }
+
+ if ( bChanged )
+ pDocSh->PostPaint( 0, nStartRow, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab,
+ PaintPartFlags::Grid | PaintPartFlags::Left );
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
+ GetViewData().GetViewShell(),
+ false /* bColumns */, true /* bRows */,
+ true /* bSizes*/, false /* bHidden */, false /* bFiltered */,
+ false /* bGroups */, nTab);
+ ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), ROW_HEADER, GetViewData().GetTabNo());
+ }
+
+ return bChanged;
+}
+
+namespace {
+
+enum ScAutoSum
+{
+ ScAutoSumNone = 0,
+ ScAutoSumData,
+ ScAutoSumSum,
+ ScAutoSumAverage,
+ ScAutoSumMax,
+ ScAutoSumMin,
+ ScAutoSumCount,
+ ScAutoSumCountA,
+ ScAutoSumProduct,
+ ScAutoSumStDev,
+ ScAutoSumStDevP,
+ ScAutoSumVar,
+ ScAutoSumVarP,
+ ScAutoSumEnd
+};
+
+}
+
+static ScAutoSum lcl_IsAutoSumData( ScDocument& rDoc, SCCOL nCol, SCROW nRow,
+ SCTAB nTab, ScDirection eDir, SCCOLROW& nExtend )
+{
+ ScRefCellValue aCell(rDoc, ScAddress(nCol, nRow, nTab));
+ if (aCell.hasNumeric())
+ {
+ if (aCell.getType() == CELLTYPE_FORMULA)
+ {
+ ScAutoSum val = ScAutoSumNone;
+ ScTokenArray* pCode = aCell.getFormula()->GetCode();
+ if ( pCode )
+ {
+ switch( pCode->GetOuterFuncOpCode() )
+ {
+ case ocSum : val = ScAutoSumSum;
+ break;
+ case ocAverage : val = ScAutoSumAverage;
+ break;
+ case ocMax : val = ScAutoSumMax;
+ break;
+ case ocMin : val = ScAutoSumMin;
+ break;
+ case ocCount : val = ScAutoSumCount;
+ break;
+ case ocCount2 : val = ScAutoSumCountA;
+ break;
+ case ocProduct : val = ScAutoSumProduct;
+ break;
+ case ocStDev : val = ScAutoSumStDev;
+ break;
+ case ocStDevP : val = ScAutoSumStDevP;
+ break;
+ case ocVar : val = ScAutoSumVar;
+ break;
+ case ocVarP : val = ScAutoSumVarP;
+ break;
+ default :
+ break;
+ }
+ if ( pCode->GetAdjacentExtendOfOuterFuncRefs( nExtend,
+ ScAddress( nCol, nRow, nTab ), eDir ) )
+ return val;
+ }
+ }
+ return ScAutoSumData;
+ }
+ return ScAutoSumNone;
+}
+
+#define SC_AUTOSUM_MAXCOUNT 20
+
+static ScAutoSum lcl_SeekAutoSumData( ScDocument& rDoc, SCCOL& nCol, SCROW& nRow,
+ SCTAB nTab, ScDirection eDir, SCCOLROW& nExtend )
+{
+ sal_uInt16 nCount = 0;
+ while (nCount < SC_AUTOSUM_MAXCOUNT)
+ {
+ if ( eDir == DIR_TOP )
+ {
+ if (nRow > 0)
+ --nRow;
+ else
+ return ScAutoSumNone;
+ }
+ else
+ {
+ if (nCol > 0)
+ --nCol;
+ else
+ return ScAutoSumNone;
+ }
+ ScAutoSum eSum;
+ if ( (eSum = lcl_IsAutoSumData(
+ rDoc, nCol, nRow, nTab, eDir, nExtend )) != ScAutoSumNone )
+ return eSum;
+ ++nCount;
+ }
+ return ScAutoSumNone;
+}
+
+#undef SC_AUTOSUM_MAXCOUNT
+
+static bool lcl_FindNextSumEntryInColumn( ScDocument& rDoc, SCCOL nCol, SCROW& nRow,
+ SCTAB nTab, SCCOLROW& nExtend, SCROW nMinRow )
+{
+ const SCROW nTmp = nRow;
+ ScAutoSum eSkip = ScAutoSumNone;
+ for (;;)
+ {
+ eSkip = lcl_IsAutoSumData( rDoc, nCol, nRow, nTab, DIR_TOP, nExtend );
+ if (eSkip != ScAutoSumData || nRow <= nMinRow )
+ break;
+ --nRow;
+ }
+ return eSkip >= ScAutoSumSum && nRow < nTmp;
+}
+
+static bool lcl_FindNextSumEntryInRow( ScDocument& rDoc, SCCOL& nCol, SCROW nRow,
+ SCTAB nTab, SCCOLROW& nExtend, SCCOL nMinCol )
+{
+ const SCCOL nTmp = nCol;
+ ScAutoSum eSkip = ScAutoSumNone;
+ for (;;)
+ {
+ eSkip = lcl_IsAutoSumData( rDoc, nCol, nRow, nTab, DIR_LEFT, nExtend );
+ if (eSkip != ScAutoSumData || nCol <= nMinCol )
+ break;
+ --nCol;
+ }
+ return eSkip >= ScAutoSumSum && nCol < nTmp;
+}
+
+static ScAutoSum lcl_GetAutoSumForColumnRange( ScDocument& rDoc, ScRangeList& rRangeList, const ScRange& rRange )
+{
+ const ScAddress aStart = rRange.aStart;
+ const ScAddress aEnd = rRange.aEnd;
+ if ( aStart.Col() != aEnd.Col() )
+ {
+ return ScAutoSumNone;
+ }
+
+ const SCTAB nTab = aEnd.Tab();
+ const SCCOL nCol = aEnd.Col();
+ SCROW nEndRow = aEnd.Row();
+ SCROW nStartRow = nEndRow;
+ SCCOLROW nExtend = 0;
+ ScAutoSum eSum = lcl_IsAutoSumData( rDoc, nCol, nEndRow, nTab, DIR_TOP, nExtend /*out*/ );
+
+ if ( eSum >= ScAutoSumSum )
+ {
+ bool bContinue = false;
+ do
+ {
+ rRangeList.push_back( ScRange( nCol, nStartRow, nTab, nCol, nEndRow, nTab ) );
+ nEndRow = static_cast< SCROW >( nExtend );
+ bContinue = lcl_FindNextSumEntryInColumn( rDoc, nCol, nEndRow /*inout*/, nTab, nExtend /*out*/, aStart.Row() );
+ if ( bContinue )
+ {
+ nStartRow = nEndRow;
+ }
+ } while ( bContinue );
+ }
+ else
+ {
+ while ( nStartRow > aStart.Row() )
+ {
+ eSum = lcl_IsAutoSumData( rDoc, nCol, nStartRow-1, nTab, DIR_TOP, nExtend /*out*/ );
+ if (eSum >= ScAutoSumSum )
+ break;
+ --nStartRow;
+ }
+ rRangeList.push_back( ScRange( nCol, nStartRow, nTab, nCol, nEndRow, nTab ) );
+ if (eSum == ScAutoSumNone)
+ eSum = ScAutoSumData;
+ }
+
+ return eSum;
+}
+
+static ScAutoSum lcl_GetAutoSumForRowRange( ScDocument& rDoc, ScRangeList& rRangeList, const ScRange& rRange )
+{
+ const ScAddress aStart = rRange.aStart;
+ const ScAddress aEnd = rRange.aEnd;
+ if ( aStart.Row() != aEnd.Row() )
+ {
+ return ScAutoSumNone;
+ }
+
+ const SCTAB nTab = aEnd.Tab();
+ const SCROW nRow = aEnd.Row();
+ SCCOL nEndCol = aEnd.Col();
+ SCCOL nStartCol = nEndCol;
+ SCCOLROW nExtend = 0;
+ ScAutoSum eSum = lcl_IsAutoSumData( rDoc, nEndCol, nRow, nTab, DIR_LEFT, nExtend /*out*/ );
+
+ if ( eSum >= ScAutoSumSum )
+ {
+ bool bContinue = false;
+ do
+ {
+ rRangeList.push_back( ScRange( nStartCol, nRow, nTab, nEndCol, nRow, nTab ) );
+ nEndCol = static_cast< SCCOL >( nExtend );
+ bContinue = lcl_FindNextSumEntryInRow( rDoc, nEndCol /*inout*/, nRow, nTab, nExtend /*out*/, aStart.Col() );
+ if ( bContinue )
+ {
+ nStartCol = nEndCol;
+ }
+ } while ( bContinue );
+ }
+ else
+ {
+ while ( nStartCol > aStart.Col() )
+ {
+ eSum = lcl_IsAutoSumData( rDoc, nStartCol-1, nRow, nTab, DIR_LEFT, nExtend /*out*/ );
+ if (eSum >= ScAutoSumSum )
+ break;
+ --nStartCol;
+ }
+ rRangeList.push_back( ScRange( nStartCol, nRow, nTab, nEndCol, nRow, nTab ) );
+ if (eSum == ScAutoSumNone)
+ eSum = ScAutoSumData;
+ }
+
+ return eSum;
+}
+
+static sal_Int8 GetSubTotal( const OpCode eCode )
+{
+ sal_Int8 val;
+ switch ( eCode )
+ {
+ case ocSum : val = 9;
+ break;
+ case ocAverage : val = 1;
+ break;
+ case ocMax : val = 4;
+ break;
+ case ocMin : val = 5;
+ break;
+ case ocCount : val = 2;
+ break;
+ case ocCount2 : val = 3;
+ break;
+ case ocProduct : val = 6;
+ break;
+ case ocStDev : val = 7;
+ break;
+ case ocStDevP : val = 8;
+ break;
+ case ocVar : val = 10;
+ break;
+ case ocVarP : val = 11;
+ break;
+ default : val = 9;
+ }
+
+ return val;
+}
+
+bool ScViewFunc::GetAutoSumArea( ScRangeList& rRangeList )
+{
+ ScDocument& rDoc = GetViewData().GetDocument();
+ SCTAB nTab = GetViewData().GetTabNo();
+
+ SCCOL nCol = GetViewData().GetCurX();
+ SCROW nRow = GetViewData().GetCurY();
+
+ SCCOL nStartCol = nCol;
+ SCROW nStartRow = nRow;
+ SCCOL nEndCol = nCol;
+ SCROW nEndRow = nRow;
+ SCCOL nSeekCol = nCol;
+ SCROW nSeekRow = nRow;
+ SCCOLROW nExtend; // will become valid via reference for ScAutoSumSum
+
+ bool bCol = false;
+ bool bRow = false;
+
+ ScAutoSum eSum;
+ if ( nRow != 0
+ && ((eSum = lcl_IsAutoSumData( rDoc, nCol, nRow-1, nTab,
+ DIR_TOP, nExtend /*out*/ )) == ScAutoSumData )
+ && ((eSum = lcl_IsAutoSumData( rDoc, nCol, nRow-1, nTab,
+ DIR_LEFT, nExtend /*out*/ )) == ScAutoSumData )
+ )
+ {
+ bRow = true;
+ nSeekRow = nRow - 1;
+ }
+ else if ( nCol != 0 && (eSum = lcl_IsAutoSumData( rDoc, nCol-1, nRow, nTab,
+ DIR_LEFT, nExtend /*out*/ )) == ScAutoSumData )
+ {
+ bCol = true;
+ nSeekCol = nCol - 1;
+ }
+ else if ( (eSum = lcl_SeekAutoSumData( rDoc, nCol, nSeekRow, nTab, DIR_TOP, nExtend /*out*/ )) != ScAutoSumNone )
+ bRow = true;
+ else if (( eSum = lcl_SeekAutoSumData( rDoc, nSeekCol, nRow, nTab, DIR_LEFT, nExtend /*out*/ )) != ScAutoSumNone )
+ bCol = true;
+
+ if ( bCol || bRow )
+ {
+ if ( bRow )
+ {
+ nStartRow = nSeekRow; // nSeekRow might be adjusted via reference
+ if ( eSum >= ScAutoSumSum && eSum < ScAutoSumEnd )
+ nEndRow = nStartRow; // only sum sums
+ else
+ nEndRow = nRow - 1; // maybe extend data area at bottom
+ }
+ else
+ {
+ nStartCol = nSeekCol; // nSeekCol might be adjusted via reference
+ if ( eSum >= ScAutoSumSum )
+ nEndCol = nStartCol; // only sum sums
+ else
+ nEndCol = nCol - 1; // maybe extend data area to the right
+ }
+ bool bContinue = false;
+ do
+ {
+ if ( eSum == ScAutoSumData )
+ {
+ if ( bRow )
+ {
+ while ( nStartRow != 0 && lcl_IsAutoSumData( rDoc, nCol,
+ nStartRow-1, nTab, DIR_TOP, nExtend /*out*/ ) == eSum )
+ --nStartRow;
+ }
+ else
+ {
+ while ( nStartCol != 0 && lcl_IsAutoSumData( rDoc, nStartCol-1,
+ nRow, nTab, DIR_LEFT, nExtend /*out*/ ) == eSum )
+ --nStartCol;
+ }
+ }
+ rRangeList.push_back(
+ ScRange( nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab ) );
+ if ( eSum >= ScAutoSumSum )
+ {
+ if ( bRow )
+ {
+ nEndRow = static_cast< SCROW >( nExtend );
+ bContinue = lcl_FindNextSumEntryInColumn( rDoc, nCol, nEndRow /*inout*/, nTab, nExtend /*out*/, 0 );
+ if ( bContinue )
+ {
+ nStartRow = nEndRow;
+ }
+ }
+ else
+ {
+ nEndCol = static_cast< SCCOL >( nExtend );
+ bContinue = lcl_FindNextSumEntryInRow( rDoc, nEndCol /*inout*/, nRow, nTab, nExtend /*out*/, 0 );
+ if ( bContinue )
+ {
+ nStartCol = nEndCol;
+ }
+ }
+ }
+ } while ( bContinue );
+ return true;
+ }
+ return false;
+}
+
+void ScViewFunc::EnterAutoSum(const ScRangeList& rRangeList, bool bSubTotal, const ScAddress& rAddr, const OpCode eCode)
+{
+ OUString aFormula = GetAutoSumFormula( rRangeList, bSubTotal, rAddr , eCode);
+ EnterBlock( aFormula, nullptr );
+}
+
+bool ScViewFunc::AutoSum( const ScRange& rRange, bool bSubTotal, bool bSetCursor, bool bContinue , const OpCode eCode)
+{
+ ScDocument& rDoc = GetViewData().GetDocument();
+ const SCTAB nTab = rRange.aStart.Tab();
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ const SCCOL nEndCol = rRange.aEnd.Col();
+ const SCROW nEndRow = rRange.aEnd.Row();
+ SCCOLROW nExtend = 0; // out parameter for lcl_IsAutoSumData
+
+ // ignore rows at the top of the given range which don't contain autosum data
+ bool bRowData = false;
+ for ( SCROW nRow = nStartRow; nRow <= nEndRow; ++nRow )
+ {
+ for ( SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol )
+ {
+ if ( lcl_IsAutoSumData( rDoc, nCol, nRow, nTab, DIR_TOP, nExtend ) != ScAutoSumNone )
+ {
+ bRowData = true;
+ break;
+ }
+ }
+ if ( bRowData )
+ {
+ nStartRow = nRow;
+ break;
+ }
+ }
+ if ( !bRowData )
+ {
+ return false;
+ }
+
+ // ignore columns at the left of the given range which don't contain autosum data
+ bool bColData = false;
+ for ( SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol )
+ {
+ for ( SCROW nRow = nStartRow; nRow <= nEndRow; ++nRow )
+ {
+ if ( lcl_IsAutoSumData( rDoc, nCol, nRow, nTab, DIR_LEFT, nExtend ) != ScAutoSumNone )
+ {
+ bColData = true;
+ break;
+ }
+ }
+ if ( bColData )
+ {
+ nStartCol = nCol;
+ break;
+ }
+ }
+ if ( !bColData )
+ {
+ return false;
+ }
+
+ const bool bEndRowEmpty = rDoc.IsBlockEmpty( nStartCol, nEndRow, nEndCol, nEndRow, nTab );
+ const bool bEndColEmpty = rDoc.IsBlockEmpty( nEndCol, nStartRow, nEndCol, nEndRow, nTab );
+ bool bRow = ( nStartRow != nEndRow ) && ( bEndRowEmpty || !bEndColEmpty );
+ bool bCol = ( nStartCol != nEndCol ) && ( bEndColEmpty || nStartRow == nEndRow );
+
+ // find an empty row for entering the result
+ SCROW nInsRow = nEndRow;
+ if ( bRow && !bEndRowEmpty )
+ {
+ if ( nInsRow < rDoc.MaxRow() )
+ {
+ ++nInsRow;
+ while ( !rDoc.IsBlockEmpty( nStartCol, nInsRow, nEndCol, nInsRow, nTab ) )
+ {
+ if ( nInsRow < rDoc.MaxRow() )
+ {
+ ++nInsRow;
+ }
+ else
+ {
+ bRow = false;
+ break;
+ }
+ }
+ }
+ else
+ {
+ bRow = false;
+ }
+ }
+
+ // find an empty column for entering the result
+ SCCOL nInsCol = nEndCol;
+ if ( bCol && !bEndColEmpty )
+ {
+ if ( nInsCol < rDoc.MaxCol() )
+ {
+ ++nInsCol;
+ while ( !rDoc.IsBlockEmpty( nInsCol, nStartRow, nInsCol, nEndRow, nTab ) )
+ {
+ if ( nInsCol < rDoc.MaxCol() )
+ {
+ ++nInsCol;
+ }
+ else
+ {
+ bCol = false;
+ break;
+ }
+ }
+ }
+ else
+ {
+ bCol = false;
+ }
+ }
+
+ if ( !bRow && !bCol )
+ {
+ return false;
+ }
+
+ SCCOL nMarkEndCol = nEndCol;
+ SCROW nMarkEndRow = nEndRow;
+ ScAutoSum eSum = ScAutoSumNone;
+ SCROW nColSums = 0;
+ SCCOL nRowSums = 0;
+ SCROW nColSumsStartRow = 0;
+ SCCOL nRowSumsStartCol = 0;
+
+ if ( bRow )
+ {
+ // calculate the row sums for all columns of the given range
+
+ SCROW nSumEndRow = nEndRow;
+
+ if ( bEndRowEmpty )
+ {
+ // the last row of the given range is empty;
+ // don't take into account for calculating the autosum
+ --nSumEndRow;
+ }
+ else
+ {
+ // increase mark range
+ ++nMarkEndRow;
+ }
+
+ for ( SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol )
+ {
+ if ( !rDoc.IsBlockEmpty( nCol, nStartRow, nCol, nSumEndRow, nTab ) )
+ {
+ ScRangeList aRangeList;
+ // Include the originally selected start row.
+ const ScRange aRange( nCol, rRange.aStart.Row(), nTab, nCol, nSumEndRow, nTab );
+ if ( (eSum = lcl_GetAutoSumForColumnRange( rDoc, aRangeList, aRange )) != ScAutoSumNone )
+ {
+ if (++nRowSums == 1)
+ nRowSumsStartCol = aRangeList[0].aStart.Col();
+ const OUString aFormula = GetAutoSumFormula(
+ aRangeList, bSubTotal, ScAddress(nCol, nInsRow, nTab), eCode);
+ EnterData( nCol, nInsRow, nTab, aFormula );
+ }
+ }
+ }
+ }
+
+ if ( bCol )
+ {
+ // calculate the column sums for all rows of the given range
+
+ SCCOL nSumEndCol = nEndCol;
+
+ if ( bEndColEmpty )
+ {
+ // the last column of the given range is empty;
+ // don't take into account for calculating the autosum
+ --nSumEndCol;
+ }
+ else
+ {
+ // increase mark range
+ ++nMarkEndCol;
+ }
+
+ for ( SCROW nRow = nStartRow; nRow <= nEndRow; ++nRow )
+ {
+ if ( !rDoc.IsBlockEmpty( nStartCol, nRow, nSumEndCol, nRow, nTab ) )
+ {
+ ScRangeList aRangeList;
+ // Include the originally selected start column.
+ const ScRange aRange( rRange.aStart.Col(), nRow, nTab, nSumEndCol, nRow, nTab );
+ if ( (eSum = lcl_GetAutoSumForRowRange( rDoc, aRangeList, aRange )) != ScAutoSumNone )
+ {
+ if (++nColSums == 1)
+ nColSumsStartRow = aRangeList[0].aStart.Row();
+ const OUString aFormula = GetAutoSumFormula( aRangeList, bSubTotal, ScAddress(nInsCol, nRow, nTab), eCode );
+ EnterData( nInsCol, nRow, nTab, aFormula );
+ }
+ }
+ }
+ }
+
+ // Set new mark range and cursor position.
+ // For sum of sums (and data until sum) mark the actual resulting range if
+ // there is only one, or the data range if more than one. Otherwise use the
+ // original selection. All extended by end column/row where the sum is put.
+ const ScRange aMarkRange(
+ (eSum >= ScAutoSumSum ?
+ (nRowSums == 1 ? nRowSumsStartCol : nStartCol) :
+ rRange.aStart.Col()),
+ (eSum >= ScAutoSumSum ?
+ (nColSums == 1 ? nColSumsStartRow : nStartRow) :
+ rRange.aStart.Row()),
+ nTab, nMarkEndCol, nMarkEndRow, nTab );
+ MarkRange( aMarkRange, false, bContinue );
+ if ( bSetCursor )
+ {
+ SetCursor( nMarkEndCol, nMarkEndRow );
+ }
+
+ return true;
+}
+
+OUString ScViewFunc::GetAutoSumFormula( const ScRangeList& rRangeList, bool bSubTotal, const ScAddress& rAddr , const OpCode eCode)
+{
+ ScViewData& rViewData = GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ ScTokenArray aArray(rDoc);
+
+ aArray.AddOpCode(bSubTotal ? ocSubTotal : eCode);
+ aArray.AddOpCode(ocOpen);
+
+ if (bSubTotal)
+ {
+ aArray.AddDouble( GetSubTotal( eCode ) );
+ aArray.AddOpCode(ocSep);
+ }
+
+ if(!rRangeList.empty())
+ {
+ ScRangeList aRangeList = rRangeList;
+ size_t ListSize = aRangeList.size();
+ for ( size_t i = 0; i < ListSize; ++i )
+ {
+ const ScRange & r = aRangeList[i];
+ if (i != 0)
+ aArray.AddOpCode(ocSep);
+ ScComplexRefData aRef;
+ aRef.InitRangeRel(rDoc, r, rAddr);
+ aArray.AddDoubleReference(aRef);
+ }
+ }
+
+ aArray.AddOpCode(ocClose);
+
+ ScCompiler aComp(rDoc, rAddr, aArray, rDoc.GetGrammar());
+ OUStringBuffer aBuf;
+ aComp.CreateStringFromTokenArray(aBuf);
+ aBuf.insert(0, "=");
+ return aBuf.makeStringAndClear();
+}
+
+void ScViewFunc::EnterBlock( const OUString& rString, const EditTextObject* pData )
+{
+ // test for multi selection
+
+ SCCOL nCol = GetViewData().GetCurX();
+ SCROW nRow = GetViewData().GetCurY();
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ if ( rMark.IsMultiMarked() )
+ {
+ rMark.MarkToSimple();
+ if ( rMark.IsMultiMarked() )
+ { // "Insert into multi selection not possible"
+ ErrorMessage(STR_MSSG_PASTEFROMCLIP_0);
+
+ // insert into single cell
+ if ( pData )
+ EnterData(nCol, nRow, nTab, *pData);
+ else
+ EnterData( nCol, nRow, nTab, rString );
+ return;
+ }
+ }
+
+ if (GetViewData().SelectionForbidsCellFill())
+ {
+ PaintArea(nCol, nRow, nCol, nRow); // possibly the edit-engine is still painted there
+ return;
+ }
+
+ ScDocument& rDoc = GetViewData().GetDocument();
+ OUString aNewStr = rString;
+ if ( pData )
+ {
+ const ScPatternAttr* pOldPattern = rDoc.GetPattern( nCol, nRow, nTab );
+ ScTabEditEngine aEngine( *pOldPattern, rDoc.GetEnginePool(), &rDoc );
+ aEngine.SetTextCurrentDefaults(*pData);
+
+ ScEditAttrTester aTester( &aEngine );
+ if (!aTester.NeedsObject())
+ {
+ aNewStr = aEngine.GetText();
+ pData = nullptr;
+ }
+ }
+
+ // Insert via PasteFromClip
+ weld::WaitObject aWait(GetViewData().GetDialogParent());
+
+ ScAddress aPos( nCol, nRow, nTab );
+
+ ScDocumentUniquePtr pInsDoc(new ScDocument( SCDOCMODE_CLIP ));
+ pInsDoc->ResetClip( &rDoc, nTab );
+
+ if (aNewStr[0] == '=') // Formula ?
+ {
+ // SetString not possible, because in Clipboard-Documents nothing will be compiled!
+ pInsDoc->SetFormulaCell(aPos, new ScFormulaCell(rDoc, aPos, aNewStr));
+ }
+ else if ( pData )
+ {
+ // A copy of pData will be stored.
+ pInsDoc->SetEditText(aPos, *pData, rDoc.GetEditPool());
+ }
+ else
+ pInsDoc->SetString( nCol, nRow, nTab, aNewStr );
+
+ pInsDoc->SetClipArea( ScRange(aPos) );
+ // insert Block, with Undo etc.
+ if ( !PasteFromClip( InsertDeleteFlags::CONTENTS, pInsDoc.get(), ScPasteFunc::NONE, false, false,
+ false, INS_NONE, InsertDeleteFlags::ATTRIB ) )
+ return;
+
+ const SfxUInt32Item* pItem = pInsDoc->GetAttr(
+ nCol, nRow, nTab, ATTR_VALUE_FORMAT );
+ if ( pItem )
+ { // set number format if incompatible
+ // MarkData was already MarkToSimple'ed in PasteFromClip
+ const ScRange& aRange = rMark.GetMarkArea();
+ ScPatternAttr aPattern( rDoc.GetPool() );
+ aPattern.GetItemSet().Put( *pItem );
+ SvNumFormatType nNewType = rDoc.GetFormatTable()->GetType( pItem->GetValue() );
+ rDoc.ApplyPatternIfNumberformatIncompatible( aRange, rMark,
+ aPattern, nNewType );
+ }
+}
+
+// manual page break
+
+void ScViewFunc::InsertPageBreak( bool bColumn, bool bRecord, const ScAddress* pPos,
+ bool bSetModified )
+{
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScAddress aCursor;
+ if (pPos)
+ aCursor = *pPos;
+ else
+ aCursor = ScAddress( GetViewData().GetCurX(), GetViewData().GetCurY(), nTab );
+
+ bool bSuccess = GetViewData().GetDocShell()->GetDocFunc().
+ InsertPageBreak( bColumn, aCursor, bRecord, bSetModified );
+
+ if ( bSuccess && bSetModified )
+ UpdatePageBreakData( true ); // for PageBreak-Mode
+}
+
+void ScViewFunc::DeletePageBreak( bool bColumn, bool bRecord, const ScAddress* pPos,
+ bool bSetModified )
+{
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScAddress aCursor;
+ if (pPos)
+ aCursor = *pPos;
+ else
+ aCursor = ScAddress( GetViewData().GetCurX(), GetViewData().GetCurY(), nTab );
+
+ bool bSuccess = GetViewData().GetDocShell()->GetDocFunc().
+ RemovePageBreak( bColumn, aCursor, bRecord, bSetModified );
+
+ if ( bSuccess && bSetModified )
+ UpdatePageBreakData( true ); // for PageBreak-Mode
+}
+
+void ScViewFunc::RemoveManualBreaks()
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTab = GetViewData().GetTabNo();
+ bool bUndo(rDoc.IsUndoEnabled());
+
+ if (bUndo)
+ {
+ ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nTab, nTab, true, true );
+ rDoc.CopyToDocument( 0,0,nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE, false, *pUndoDoc );
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoRemoveBreaks>( pDocSh, nTab, std::move(pUndoDoc) ) );
+ }
+
+ rDoc.RemoveManualBreaks(nTab);
+ rDoc.UpdatePageBreaks(nTab);
+
+ UpdatePageBreakData( true );
+ pDocSh->SetDocumentModified();
+ pDocSh->PostPaint( 0,0,nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, PaintPartFlags::Grid );
+}
+
+void ScViewFunc::SetPrintZoom(sal_uInt16 nScale)
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ SCTAB nTab = GetViewData().GetTabNo();
+ pDocSh->SetPrintZoom( nTab, nScale, 0/*nPages*/ );
+}
+
+void ScViewFunc::AdjustPrintZoom()
+{
+ ScRange aRange;
+ if ( GetViewData().GetSimpleArea( aRange ) != SC_MARK_SIMPLE )
+ aRange = GetViewData().GetMarkData().GetMultiMarkArea();
+ GetViewData().GetDocShell()->AdjustPrintZoom( aRange );
+}
+
+void ScViewFunc::SetPrintRanges( bool bEntireSheet, const OUString* pPrint,
+ const OUString* pRepCol, const OUString* pRepRow,
+ bool bAddPrint )
+{
+ // on all selected tables
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ bool bUndo (rDoc.IsUndoEnabled());
+
+ std::unique_ptr<ScPrintRangeSaver> pOldRanges = rDoc.CreatePrintRangeSaver();
+
+ ScAddress::Details aDetails(rDoc.GetAddressConvention(), 0, 0);
+
+ for (const SCTAB& nTab : rMark)
+ {
+ ScRange aRange( 0,0,nTab );
+
+ // print ranges
+
+ if( !bAddPrint )
+ {
+ rDoc.ClearPrintRanges( nTab );
+ rDoc.ClearPrintNamedRanges(nTab);
+ }
+
+ if( bEntireSheet )
+ {
+ rDoc.SetPrintEntireSheet( nTab );
+ }
+ else if ( pPrint )
+ {
+ if ( !pPrint->isEmpty() )
+ {
+ const sal_Unicode sep = ScCompiler::GetNativeSymbolChar(ocSep);
+ sal_Int32 nPos = 0;
+ do
+ {
+ const OUString aToken = pPrint->getToken(0, sep, nPos);
+ if ( aRange.ParseAny( aToken, rDoc, aDetails ) & ScRefFlags::VALID )
+ rDoc.AddPrintRange( nTab, aRange );
+ }
+ while (nPos >= 0);
+ }
+ }
+ else // NULL = use selection (print range is always set), use empty string to delete all ranges
+ {
+ if ( GetViewData().GetSimpleArea( aRange ) == SC_MARK_SIMPLE )
+ {
+ rDoc.AddPrintRange( nTab, aRange );
+ }
+ else if ( rMark.IsMultiMarked() )
+ {
+ rMark.MarkToMulti();
+ ScRangeListRef pList( new ScRangeList );
+ rMark.FillRangeListWithMarks( pList.get(), false );
+ for (size_t i = 0, n = pList->size(); i < n; ++i)
+ {
+ const ScRange & rR = (*pList)[i];
+ rDoc.AddPrintRange(nTab, rR);
+ }
+ }
+ }
+
+ // repeat columns
+
+ if ( pRepCol )
+ {
+ if ( pRepCol->isEmpty() )
+ rDoc.SetRepeatColRange( nTab, std::nullopt );
+ else
+ if ( aRange.ParseAny( *pRepCol, rDoc, aDetails ) & ScRefFlags::VALID )
+ rDoc.SetRepeatColRange( nTab, std::move(aRange) );
+ }
+
+ // repeat rows
+
+ if ( pRepRow )
+ {
+ if ( pRepRow->isEmpty() )
+ rDoc.SetRepeatRowRange( nTab, std::nullopt );
+ else
+ if ( aRange.ParseAny( *pRepRow, rDoc, aDetails ) & ScRefFlags::VALID )
+ rDoc.SetRepeatRowRange( nTab, std::move(aRange) );
+ }
+ }
+
+ // undo (for all tables)
+ if (bUndo)
+ {
+ SCTAB nCurTab = GetViewData().GetTabNo();
+ std::unique_ptr<ScPrintRangeSaver> pNewRanges = rDoc.CreatePrintRangeSaver();
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ tools::JsonWriter aJsonWriter;
+ pNewRanges->GetPrintRangesInfo(aJsonWriter);
+
+ SfxViewShell* pViewShell = GetViewData().GetViewShell();
+ const OString message = aJsonWriter.finishAndGetAsOString();
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_PRINT_RANGES, message);
+ }
+
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoPrintRange>( pDocSh, nCurTab, std::move(pOldRanges), std::move(pNewRanges) ) );
+ }
+ else
+ pOldRanges.reset();
+
+ // update page breaks
+
+ for (const auto& rTab : rMark)
+ ScPrintFunc( pDocSh, pDocSh->GetPrinter(), rTab ).UpdatePages();
+
+ SfxBindings& rBindings = GetViewData().GetBindings();
+ rBindings.Invalidate( SID_DELETE_PRINTAREA );
+
+ pDocSh->SetDocumentModified();
+}
+
+// Merge cells
+
+bool ScViewFunc::TestMergeCells() // pre-test (for menu)
+{
+ // simple test: true if there's a selection but no multi selection and not filtered
+
+ const ScMarkData& rMark = GetViewData().GetMarkData();
+ if ( rMark.IsMarked() || rMark.IsMultiMarked() )
+ {
+ ScRange aRange;
+ bool bMergeable = ( GetViewData().GetSimpleArea( aRange ) == SC_MARK_SIMPLE );
+ bMergeable = bMergeable && ( aRange.aStart.Col() != aRange.aEnd.Col() ||
+ aRange.aStart.Row() != aRange.aEnd.Row() );
+ return bMergeable;
+ }
+ else
+ return false;
+}
+
+void ScViewFunc::MergeCells( bool bApi, bool bDoContents, bool bCenter,
+ const sal_uInt16 nSlot )
+{
+ // Editable- and Being-Nested- test must be at the beginning (in DocFunc too),
+ // so that the Contents-QueryBox won't appear
+ ScEditableTester aTester( this );
+ if (!aTester.IsEditable())
+ {
+ ErrorMessage(aTester.GetMessageId());
+ return;
+ }
+
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ rMark.MarkToSimple();
+ if (!rMark.IsMarked())
+ {
+ ErrorMessage(STR_NOMULTISELECT);
+ return;
+ }
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+
+ const ScRange& aMarkRange = rMark.GetMarkArea();
+ SCCOL nStartCol = aMarkRange.aStart.Col();
+ SCROW nStartRow = aMarkRange.aStart.Row();
+ SCTAB nStartTab = aMarkRange.aStart.Tab();
+ SCCOL nEndCol = aMarkRange.aEnd.Col();
+ SCROW nEndRow = aMarkRange.aEnd.Row();
+ SCTAB nEndTab = aMarkRange.aEnd.Tab();
+ if ( nStartCol == nEndCol && nStartRow == nEndRow )
+ {
+ // nothing to do
+ return;
+ }
+
+ if ( rDoc.HasAttrib( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab,
+ HasAttrFlags::Merged | HasAttrFlags::Overlapped ) )
+ { // "Don't nest merging !"
+ ErrorMessage(STR_MSSG_MERGECELLS_0);
+ return;
+ }
+
+ // Check for the contents of all selected tables.
+ bool bAskDialog = false;
+ ScCellMergeOption aMergeOption(nStartCol, nStartRow, nEndCol, nEndRow, bCenter);
+ for (const SCTAB& i : rMark)
+ {
+ aMergeOption.maTabs.insert(i);
+
+ sc::MultiDataCellState aState = rDoc.HasMultipleDataCells(aMergeOption.getSingleRange(i));
+ switch (aState.meState)
+ {
+ case sc::MultiDataCellState::HasMultipleCells:
+ {
+ // this range contains multiple data cells.
+ bAskDialog = true;
+ break;
+ }
+ case sc::MultiDataCellState::HasOneCell:
+ {
+ // this range contains only one data cell.
+ if (nStartCol != aState.mnCol1 || nStartRow != aState.mnRow1)
+ bDoContents = true; // move the value to the top-left.
+ break;
+ }
+ default:
+ ;
+ }
+ }
+
+ bool bEmptyMergedCells = officecfg::Office::Calc::Compatibility::MergeCells::EmptyMergedCells::get();
+
+ auto doMerge = [this, pDocSh, aMergeOption, bApi, nStartCol, nStartRow, aMarkRange]
+ (bool bNowDoContents, bool bNowEmptyMergedCells)
+ {
+ if (pDocSh->GetDocFunc().MergeCells(aMergeOption, bNowDoContents, true/*bRecord*/,
+ bApi, bNowEmptyMergedCells))
+ {
+ SetCursor( nStartCol, nStartRow );
+ // DoneBlockMode( sal_False);
+ Unmark();
+
+ pDocSh->UpdateOle(GetViewData());
+ UpdateInputLine();
+
+ OUString aStartAddress = aMarkRange.aStart.GetColRowString();
+ OUString aEndAddress = aMarkRange.aEnd.GetColRowString();
+
+ collectUIInformation({{"RANGE", aStartAddress + ":" + aEndAddress}}, "MERGE_CELLS");
+ }
+ };
+
+ if (bAskDialog)
+ {
+ bool bShowDialog = officecfg::Office::Calc::Compatibility::MergeCells::ShowDialog::get();
+ if (!bApi && bShowDialog)
+ {
+ auto pBox = std::make_shared<ScMergeCellsDialog>(GetViewData().GetDialogParent());
+
+ SfxViewShell* pViewShell = GetViewData().GetViewShell();
+
+ weld::DialogController::runAsync(pBox, [=](sal_Int32 nRetVal) {
+ if (nRetVal == RET_OK)
+ {
+ bool bRealDoContents = bDoContents;
+ bool bRealEmptyMergedCells = bEmptyMergedCells;
+ switch (pBox->GetMergeCellsOption())
+ {
+ case MoveContentHiddenCells:
+ bRealDoContents = true;
+ break;
+ case KeepContentHiddenCells:
+ bRealEmptyMergedCells = false;
+ break;
+ case EmptyContentHiddenCells:
+ bRealEmptyMergedCells = true;
+ break;
+ default:
+ assert(!"Unknown option for merge cells.");
+ break;
+ }
+
+ doMerge(bRealDoContents, bRealEmptyMergedCells);
+
+ if (nSlot != 0)
+ {
+ SfxRequest aReq(pViewShell->GetViewFrame(), nSlot);
+ if (!bApi && bRealDoContents)
+ aReq.AppendItem(SfxBoolItem(nSlot, bDoContents));
+ SfxBindings& rBindings = pViewShell->GetViewFrame().GetBindings();
+ rBindings.Invalidate(nSlot);
+ aReq.Done();
+ }
+ }
+ // else cancelled
+ });
+ }
+ } else
+ doMerge(bDoContents, bEmptyMergedCells);
+}
+
+bool ScViewFunc::TestRemoveMerge()
+{
+ bool bMerged = false;
+ ScRange aRange;
+ if (GetViewData().GetSimpleArea( aRange ) == SC_MARK_SIMPLE)
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+ if ( rDoc.HasAttrib( aRange, HasAttrFlags::Merged ) )
+ bMerged = true;
+ }
+ return bMerged;
+}
+
+static bool lcl_extendMergeRange(ScCellMergeOption& rOption, const ScRange& rRange)
+{
+ bool bExtended = false;
+ if (rOption.mnStartCol > rRange.aStart.Col())
+ {
+ rOption.mnStartCol = rRange.aStart.Col();
+ bExtended = true;
+ }
+ if (rOption.mnStartRow > rRange.aStart.Row())
+ {
+ rOption.mnStartRow = rRange.aStart.Row();
+ bExtended = true;
+ }
+ if (rOption.mnEndCol < rRange.aEnd.Col())
+ {
+ rOption.mnEndCol = rRange.aEnd.Col();
+ bExtended = true;
+ }
+ if (rOption.mnEndRow < rRange.aEnd.Row())
+ {
+ rOption.mnEndRow = rRange.aEnd.Row();
+ bExtended = true;
+ }
+ return bExtended;
+}
+
+bool ScViewFunc::RemoveMerge()
+{
+ ScRange aRange;
+ ScEditableTester aTester( this );
+ if (!aTester.IsEditable())
+ {
+ ErrorMessage(aTester.GetMessageId());
+ return false;
+ }
+ else if (GetViewData().GetSimpleArea( aRange ) == SC_MARK_SIMPLE)
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScRange aExtended( aRange );
+ rDoc.ExtendMerge( aExtended );
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ const ScMarkData& rMark = GetViewData().GetMarkData();
+ ScCellMergeOption aOption(aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row());
+ bool bExtended = false;
+ do
+ {
+ bExtended = false;
+ for (const SCTAB& i : rMark)
+ {
+ aOption.maTabs.insert(i);
+ aExtended.aStart.SetTab(i);
+ aExtended.aEnd.SetTab(i);
+ rDoc.ExtendMerge(aExtended);
+ rDoc.ExtendOverlapped(aExtended);
+
+ // Expand the current range to be inclusive of all merged
+ // areas on all sheets.
+ bExtended = lcl_extendMergeRange(aOption, aExtended);
+ }
+ }
+ while (bExtended);
+
+ bool bOk = pDocSh->GetDocFunc().UnmergeCells(aOption, true/*bRecord*/, nullptr);
+ aExtended = aOption.getFirstSingleRange();
+ MarkRange( aExtended );
+
+ if (bOk)
+ pDocSh->UpdateOle(GetViewData());
+ }
+
+ OUString aCellLocation = aRange.aStart.GetColRowString();
+ collectUIInformation({{"CELL", aCellLocation}}, "UNMERGE_CELL");
+
+ return true; //! bOk ??
+}
+
+void ScViewFunc::FillSimple( FillDir eDir )
+{
+ ScRange aRange;
+ if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
+ {
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ const ScMarkData& rMark = GetViewData().GetMarkData();
+ bool bSuccess = pDocSh->GetDocFunc().FillSimple( aRange, &rMark, eDir, false );
+ if (bSuccess)
+ {
+ pDocSh->UpdateOle(GetViewData());
+ UpdateScrollBars();
+
+ auto& rDoc = pDocSh->GetDocument();
+ bool bDoAutoSpell = rDoc.GetDocOptions().IsAutoSpell();
+ if ( bDoAutoSpell )
+ {
+ // Copy AutoSpellData from above(left/right/below) if no selection.
+ switch (eDir)
+ {
+ case FILL_TO_BOTTOM:
+ if (aRange.aStart.Row() > 0 && aRange.aStart.Row() == aRange.aEnd.Row())
+ aRange.aStart.IncRow(-1);
+ break;
+ case FILL_TO_TOP:
+ if (aRange.aEnd.Row() < rDoc.MaxRow() && aRange.aStart.Row() == aRange.aEnd.Row())
+ aRange.aEnd.IncRow(1);
+ break;
+ case FILL_TO_RIGHT:
+ if (aRange.aStart.Col() > 0 && aRange.aStart.Col() == aRange.aEnd.Col())
+ aRange.aStart.IncCol(-1);
+ break;
+ case FILL_TO_LEFT:
+ if (aRange.aEnd.Col() < rDoc.MaxCol() && aRange.aStart.Col() == aRange.aEnd.Col())
+ aRange.aEnd.IncCol(1);
+ break;
+ }
+ CopyAutoSpellData(eDir, aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row(),
+ ::std::numeric_limits<sal_uLong>::max());
+ }
+
+ // Invalidate cell slots and update input line with new content.
+ CellContentChanged();
+ }
+ }
+ else
+ ErrorMessage(STR_NOMULTISELECT);
+}
+
+void ScViewFunc::FillSeries( FillDir eDir, FillCmd eCmd, FillDateCmd eDateCmd,
+ double fStart, double fStep, double fMax )
+{
+ ScRange aRange;
+ if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
+ {
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ const ScMarkData& rMark = GetViewData().GetMarkData();
+ bool bSuccess = pDocSh->GetDocFunc().
+ FillSeries( aRange, &rMark, eDir, eCmd, eDateCmd,
+ fStart, fStep, fMax, false );
+ if (bSuccess)
+ {
+ pDocSh->UpdateOle(GetViewData());
+ UpdateScrollBars();
+
+ HelperNotifyChanges::NotifyIfChangesListeners(*pDocSh, aRange);
+ }
+ }
+ else
+ ErrorMessage(STR_NOMULTISELECT);
+}
+
+void ScViewFunc::FillAuto( FillDir eDir, SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow, sal_uLong nCount )
+{
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScRange aRange( nStartCol,nStartRow,nTab, nEndCol,nEndRow,nTab );
+ ScRange aSourceRange( aRange );
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ const ScMarkData& rMark = GetViewData().GetMarkData();
+ bool bSuccess = pDocSh->GetDocFunc().
+ FillAuto( aRange, &rMark, eDir, nCount, false );
+ if (!bSuccess)
+ return;
+
+ MarkRange( aRange, false ); // aRange was modified in FillAuto
+ pDocSh->UpdateOle(GetViewData());
+ UpdateScrollBars();
+
+ bool bDoAutoSpell = pDocSh->GetDocument().GetDocOptions().IsAutoSpell();
+ if ( bDoAutoSpell )
+ CopyAutoSpellData(eDir, nStartCol, nStartRow, nEndCol, nEndRow, nCount);
+
+ ScModelObj* pModelObj = pDocSh->GetModel();
+
+ ScRangeList aChangeRanges;
+ ScRange aChangeRange( aRange );
+ switch (eDir)
+ {
+ case FILL_TO_BOTTOM:
+ aChangeRange.aStart.SetRow( aSourceRange.aEnd.Row() + 1 );
+ break;
+ case FILL_TO_TOP:
+ aChangeRange.aEnd.SetRow( aSourceRange.aStart.Row() - 1 );
+ break;
+ case FILL_TO_RIGHT:
+ aChangeRange.aStart.SetCol( aSourceRange.aEnd.Col() + 1 );
+ break;
+ case FILL_TO_LEFT:
+ aChangeRange.aEnd.SetCol( aSourceRange.aStart.Col() - 1 );
+ break;
+ default:
+ break;
+ }
+ aChangeRanges.push_back( aChangeRange );
+
+ if (HelperNotifyChanges::getMustPropagateChangesModel(pModelObj))
+ HelperNotifyChanges::Notify(*pModelObj, aChangeRanges);
+ else if (pModelObj)
+ HelperNotifyChanges::Notify(*pModelObj, aChangeRanges, "data-area-invalidate");
+}
+
+void ScViewFunc::CopyAutoSpellData( FillDir eDir, SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow, sal_uLong nCount )
+{
+ const ScDocument* pDoc = &GetViewData().GetDocument();
+ SCTAB nTab = GetViewData().GetTabNo();
+ CellType eCellType;
+
+ ScGridWindow* pWin = GetActiveWin();
+ if ( pWin->InsideVisibleRange(nStartCol, nStartRow) && pWin->InsideVisibleRange(nEndCol, nEndRow) )
+ {
+ if ( nCount == ::std::numeric_limits<sal_uLong>::max() )
+ {
+ switch( eDir )
+ {
+ case FILL_TO_BOTTOM:
+ for ( SCCOL nColItr = nStartCol; nColItr <= nEndCol; ++nColItr )
+ {
+ eCellType = pDoc->GetCellType(nColItr, nStartRow, nTab); // We need this optimization only for EditTextObject source cells
+ if (eCellType != CELLTYPE_EDIT)
+ continue;
+
+ const std::vector<editeng::MisspellRanges>* pRanges = pWin->GetAutoSpellData(nColItr, nStartRow);
+ if ( !pRanges )
+ continue;
+ for ( SCROW nRowItr = nStartRow + 1; nRowItr <= nEndRow; ++nRowItr )
+ pWin->SetAutoSpellData(nColItr, nRowItr, pRanges);
+ }
+ break;
+ case FILL_TO_TOP:
+ for ( SCCOL nColItr = nStartCol; nColItr <= nEndCol; ++nColItr )
+ {
+ eCellType = pDoc->GetCellType(nColItr, nEndRow, nTab); // We need this optimization only for EditTextObject source cells
+ if (eCellType != CELLTYPE_EDIT)
+ continue;
+
+ const std::vector<editeng::MisspellRanges>* pRanges = pWin->GetAutoSpellData(nColItr, nEndRow);
+ if ( !pRanges )
+ continue;
+ for ( SCROW nRowItr = nEndRow - 1; nRowItr >= nStartRow; --nRowItr )
+ pWin->SetAutoSpellData(nColItr, nRowItr, pRanges);
+ }
+ break;
+ case FILL_TO_RIGHT:
+ for ( SCROW nRowItr = nStartRow; nRowItr <= nEndRow; ++nRowItr )
+ {
+ eCellType = pDoc->GetCellType(nStartCol, nRowItr, nTab); // We need this optimization only for EditTextObject source cells
+ if (eCellType != CELLTYPE_EDIT)
+ continue;
+
+ const std::vector<editeng::MisspellRanges>* pRanges = pWin->GetAutoSpellData(nStartCol, nRowItr);
+ if ( !pRanges )
+ continue;
+ for ( SCCOL nColItr = nStartCol + 1; nColItr <= nEndCol; ++nColItr )
+ pWin->SetAutoSpellData(nColItr, nRowItr, pRanges);
+ }
+ break;
+ case FILL_TO_LEFT:
+ for ( SCROW nRowItr = nStartRow; nRowItr <= nEndRow; ++nRowItr )
+ {
+ eCellType = pDoc->GetCellType(nEndCol, nRowItr, nTab); // We need this optimization only for EditTextObject source cells
+ if (eCellType != CELLTYPE_EDIT)
+ continue;
+
+ const std::vector<editeng::MisspellRanges>* pRanges = pWin->GetAutoSpellData(nEndCol, nRowItr);
+ if ( !pRanges )
+ continue;
+ for ( SCCOL nColItr = nEndCol - 1; nColItr >= nStartCol; --nColItr )
+ pWin->SetAutoSpellData(nColItr, nRowItr, pRanges);
+ }
+ break;
+ }
+ return;
+ }
+
+ typedef const std::vector<editeng::MisspellRanges>* MisspellRangesType;
+ SCROW nRowRepeatSize = nEndRow - nStartRow + 1;
+ SCCOL nColRepeatSize = nEndCol - nStartCol + 1;
+ SCROW nTillRow = 0;
+ SCCOL nTillCol = 0;
+ std::vector<std::vector<MisspellRangesType>> aSourceSpellRanges(nRowRepeatSize, std::vector<MisspellRangesType>(nColRepeatSize, nullptr));
+
+ for ( SCROW nRowIdx = 0; nRowIdx < nRowRepeatSize; ++nRowIdx )
+ {
+ for ( SCCOL nColIdx = 0; nColIdx < nColRepeatSize; ++nColIdx )
+ {
+ eCellType = pDoc->GetCellType(nStartCol + nColIdx, nStartRow + nRowIdx, nTab); // We need this optimization only for EditTextObject source cells
+ if (eCellType != CELLTYPE_EDIT)
+ continue;
+
+ aSourceSpellRanges[nRowIdx][nColIdx] = pWin->GetAutoSpellData( nStartCol + nColIdx, nStartRow + nRowIdx );
+ }
+ }
+
+ switch( eDir )
+ {
+ case FILL_TO_BOTTOM:
+ nTillRow = nEndRow + nCount;
+ for ( SCCOL nColItr = nStartCol; nColItr <= nEndCol; ++nColItr )
+ {
+ for ( SCROW nRowItr = nEndRow + 1; nRowItr <= nTillRow; ++nRowItr )
+ {
+ size_t nSourceRowIdx = ( nRowItr - nEndRow - 1 ) % nRowRepeatSize;
+ MisspellRangesType pRanges = aSourceSpellRanges[nSourceRowIdx][nColItr - nStartCol];
+ if ( !pRanges )
+ continue;
+ pWin->SetAutoSpellData(nColItr, nRowItr, pRanges);
+ }
+ }
+ break;
+
+ case FILL_TO_TOP:
+ nTillRow = nStartRow - nCount;
+ for ( SCCOL nColItr = nStartCol; nColItr <= nEndCol; ++nColItr )
+ {
+ for ( SCROW nRowItr = nStartRow - 1; nRowItr >= nTillRow; --nRowItr )
+ {
+ size_t nSourceRowIdx = nRowRepeatSize - 1 - ( ( nStartRow - 1 - nRowItr ) % nRowRepeatSize );
+ MisspellRangesType pRanges = aSourceSpellRanges[nSourceRowIdx][nColItr - nStartCol];
+ if ( !pRanges )
+ continue;
+ pWin->SetAutoSpellData(nColItr, nRowItr, pRanges);
+ }
+ }
+ break;
+
+ case FILL_TO_RIGHT:
+ nTillCol = nEndCol + nCount;
+ for ( SCCOL nColItr = nEndCol + 1; nColItr <= nTillCol; ++nColItr )
+ {
+ size_t nSourceColIdx = ( nColItr - nEndCol - 1 ) % nColRepeatSize;
+ for ( SCROW nRowItr = nStartRow; nRowItr <= nEndRow; ++nRowItr )
+ {
+ MisspellRangesType pRanges = aSourceSpellRanges[nRowItr - nStartRow][nSourceColIdx];
+ if ( !pRanges )
+ continue;
+ pWin->SetAutoSpellData(nColItr, nRowItr, pRanges);
+ }
+ }
+ break;
+
+ case FILL_TO_LEFT:
+ nTillCol = nStartCol - nCount;
+ for ( SCCOL nColItr = nStartCol - 1; nColItr >= nTillCol; --nColItr )
+ {
+ size_t nSourceColIdx = nColRepeatSize - 1 - ( ( nStartCol - 1 - nColItr ) % nColRepeatSize );
+ for ( SCROW nRowItr = nStartRow; nRowItr <= nEndRow; ++nRowItr )
+ {
+ MisspellRangesType pRanges = aSourceSpellRanges[nRowItr - nStartRow][nSourceColIdx];
+ if ( !pRanges )
+ continue;
+ pWin->SetAutoSpellData(nColItr, nRowItr, pRanges);
+ }
+ }
+ break;
+ }
+ }
+ else
+ pWin->ResetAutoSpellForContentChange();
+
+}
+
+void ScViewFunc::FillTab( InsertDeleteFlags nFlags, ScPasteFunc nFunction, bool bSkipEmpty, bool bAsLink )
+{
+ //! allow source sheet to be protected
+ ScEditableTester aTester( this );
+ if (!aTester.IsEditable())
+ {
+ ErrorMessage(aTester.GetMessageId());
+ return;
+ }
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ SCTAB nTab = GetViewData().GetTabNo();
+ bool bUndo(rDoc.IsUndoEnabled());
+
+ ScRange aMarkRange;
+ rMark.MarkToSimple();
+ bool bMulti = rMark.IsMultiMarked();
+ if (bMulti)
+ aMarkRange = rMark.GetMultiMarkArea();
+ else if (rMark.IsMarked())
+ aMarkRange = rMark.GetMarkArea();
+ else
+ aMarkRange = ScRange( GetViewData().GetCurX(), GetViewData().GetCurY(), nTab );
+
+ ScDocumentUniquePtr pUndoDoc;
+
+ if (bUndo)
+ {
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nTab, nTab );
+
+ for (const SCTAB& i : rMark)
+ if (i != nTab )
+ {
+ pUndoDoc->AddUndoTab( i, i );
+ aMarkRange.aStart.SetTab( i );
+ aMarkRange.aEnd.SetTab( i );
+ rDoc.CopyToDocument( aMarkRange, InsertDeleteFlags::ALL, bMulti, *pUndoDoc );
+ }
+ }
+
+ if (bMulti)
+ rDoc.FillTabMarked( nTab, rMark, nFlags, nFunction, bSkipEmpty, bAsLink );
+ else
+ {
+ aMarkRange.aStart.SetTab( nTab );
+ aMarkRange.aEnd.SetTab( nTab );
+ rDoc.FillTab( aMarkRange, rMark, nFlags, nFunction, bSkipEmpty, bAsLink );
+ }
+
+ if (bUndo)
+ { //! for ChangeTrack not until the end
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoFillTable>( pDocSh, rMark,
+ aMarkRange.aStart.Col(), aMarkRange.aStart.Row(), nTab,
+ aMarkRange.aEnd.Col(), aMarkRange.aEnd.Row(), nTab,
+ std::move(pUndoDoc), bMulti, nTab, nFlags, nFunction, bSkipEmpty, bAsLink ) );
+ }
+
+ pDocSh->PostPaintGridAll();
+ pDocSh->PostDataChanged();
+}
+
+/** Downward fill of selected cell(s) by double-clicking cross-hair cursor
+
+ Either, extends a current selection if non-empty cells exist immediately
+ below the selection, overwriting cells below the selection up to the
+ minimum row of already filled cells.
+
+ Or, extends a current selection down to the last non-empty cell of an
+ adjacent column when the lower-right corner of the selection is
+ double-clicked. It uses a left-adjoining non-empty column as a guide if
+ such is available, otherwise a right-adjoining non-empty column is used.
+
+ @return No return value
+
+ @see #i12313#
+*/
+void ScViewFunc::FillCrossDblClick()
+{
+ ScRange aRange;
+ GetViewData().GetSimpleArea( aRange );
+ aRange.PutInOrder();
+
+ SCTAB nTab = GetViewData().GetCurPos().Tab();
+ SCCOL nStartX = aRange.aStart.Col();
+ SCROW nStartY = aRange.aStart.Row();
+ SCCOL nEndX = aRange.aEnd.Col();
+ SCROW nEndY = aRange.aEnd.Row();
+
+ ScDocument& rDoc = GetViewData().GetDocument();
+
+ if (nEndY >= rDoc.MaxRow())
+ // Nothing to fill.
+ return;
+
+ // Make sure the selection is not empty
+ if ( rDoc.IsBlockEmpty( nStartX, nStartY, nEndX, nEndY, nTab ) )
+ return;
+
+ // If there is data in all columns immediately below the selection then
+ // switch to overwriting fill.
+ SCROW nOverWriteEndRow = rDoc.MaxRow();
+ for (SCCOL nCol = nStartX; nCol <= nEndX; ++nCol)
+ {
+ if (rDoc.HasData( nCol, nEndY + 1, nTab))
+ {
+ // Determine the shortest data column to end the fill.
+ SCROW nY = nEndY + 1;
+ // FindAreaPos() returns the start row of the next data block if
+ // the current row is the last row of a data block and an empty
+ // cell follows. Somewhat unexpected behaviour...
+ // So check beforehand if there is one non-empty cell following.
+ if (rDoc.HasData( nCol, nY + 1, nTab))
+ {
+ rDoc.FindAreaPos( nCol, nY, nTab, SC_MOVE_DOWN);
+ if (nOverWriteEndRow > nY)
+ nOverWriteEndRow = nY;
+ }
+ else
+ {
+ nOverWriteEndRow = nY;
+ }
+ }
+ else
+ {
+ nOverWriteEndRow = 0;
+ break; // for
+ }
+ }
+
+ if (nOverWriteEndRow > nEndY)
+ {
+ FillAuto( FILL_TO_BOTTOM, nStartX, nStartY, nEndX, nEndY, nOverWriteEndRow - nEndY);
+ return;
+ }
+
+ // Non-overwriting fill follows.
+
+ const bool bDataLeft = (nStartX > 0);
+ if (!bDataLeft && nEndX >= rDoc.MaxCol())
+ // Absolutely no data left or right of selection.
+ return;
+
+ // Check that there is
+ // 1) data immediately left (preferred) or right of start (row) of selection
+ // 2) data there below
+ // 3) no data immediately below selection
+
+ SCCOL nMovX = (bDataLeft ? nStartX - 1 : nEndX + 1);
+ SCROW nMovY = nStartY;
+ bool bDataFound = (rDoc.HasData( nMovX, nStartY, nTab) && rDoc.HasData( nMovX, nStartY + 1, nTab));
+ if (!bDataFound && bDataLeft && nEndX < rDoc.MaxCol())
+ {
+ nMovX = nEndX + 1; // check right
+ bDataFound = (rDoc.HasData( nMovX, nStartY, nTab) && rDoc.HasData( nMovX, nStartY + 1, nTab));
+ }
+
+ if (!(bDataFound && rDoc.IsEmptyData( nStartX, nEndY + 1, nEndX, nEndY + 1, nTab )))
+ return;
+
+ // Get end of data left or right.
+ rDoc.FindAreaPos( nMovX, nMovY, nTab, SC_MOVE_DOWN);
+ // Find minimum end row of below empty area and data right.
+ for (SCCOL nX = nStartX; nX <= nEndX; ++nX)
+ {
+ SCROW nY = nEndY + 1;
+ // Get next row with data in this column.
+ rDoc.FindAreaPos( nX, nY, nTab, SC_MOVE_DOWN);
+ if (nMovY == rDoc.MaxRow() && nY == rDoc.MaxRow())
+ {
+ // FindAreaPos() returns MAXROW also if there is no data at all
+ // from the start, so check if that contains data if the nearby
+ // (left or right) data ends there and increment if no data
+ // here, pretending the next data would be thereafter so nMovY
+ // will not be decremented.
+ if (!rDoc.HasData( nX, nY, nTab))
+ ++nY;
+ }
+ if (nMovY > nY - 1)
+ nMovY = nY - 1;
+ }
+
+ if (nMovY > nEndY)
+ {
+ FillAuto( FILL_TO_BOTTOM, nStartX, nStartY, nEndX, nEndY, nMovY - nEndY);
+ }
+}
+
+void ScViewFunc::ConvertFormulaToValue()
+{
+ ScRange aRange;
+ GetViewData().GetSimpleArea(aRange);
+ aRange.PutInOrder();
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ pDocSh->GetDocFunc().ConvertFormulaToValue(aRange, true);
+ // tdf#131326 - invalidate cell slots and update input line with new content
+ CellContentChanged();
+ pDocSh->PostPaint(aRange, PaintPartFlags::Grid);
+}
+
+void ScViewFunc::TransliterateText( TransliterationFlags nType )
+{
+ ScMarkData aFuncMark = GetViewData().GetMarkData();
+ if ( !aFuncMark.IsMarked() && !aFuncMark.IsMultiMarked() )
+ {
+ // no selection -> use cursor position
+
+ ScAddress aCursor( GetViewData().GetCurX(), GetViewData().GetCurY(), GetViewData().GetTabNo() );
+ aFuncMark.SetMarkArea( ScRange( aCursor ) );
+ }
+
+ bool bSuccess = GetViewData().GetDocShell()->GetDocFunc().
+ TransliterateText( aFuncMark, nType, false );
+ if (bSuccess)
+ {
+ GetViewData().GetViewShell()->UpdateInputHandler();
+ }
+}
+
+// AutoFormat
+
+ScAutoFormatData* ScViewFunc::CreateAutoFormatData()
+{
+ ScAutoFormatData* pData = nullptr;
+ SCCOL nStartCol;
+ SCROW nStartRow;
+ SCTAB nStartTab;
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ SCTAB nEndTab;
+ if (GetViewData().GetSimpleArea(nStartCol,nStartRow,nStartTab,nEndCol,nEndRow,nEndTab) == SC_MARK_SIMPLE)
+ {
+ if ( nEndCol-nStartCol >= 3 && nEndRow-nStartRow >= 3 )
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+ pData = new ScAutoFormatData;
+ rDoc.GetAutoFormatData( nStartTab, nStartCol,nStartRow,nEndCol,nEndRow, *pData );
+ }
+ }
+ return pData;
+}
+
+void ScViewFunc::AutoFormat( sal_uInt16 nFormatNo )
+{
+ ScRange aRange;
+ if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
+ {
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+
+ bool bSuccess = pDocSh->GetDocFunc().AutoFormat( aRange, &rMark, nFormatNo, false );
+ if (bSuccess)
+ pDocSh->UpdateOle(GetViewData());
+ }
+ else
+ ErrorMessage(STR_NOMULTISELECT);
+}
+
+// Search & Replace
+
+bool ScViewFunc::SearchAndReplace( const SvxSearchItem* pSearchItem,
+ bool bAddUndo, bool bIsApi )
+{
+ SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::Empty);
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ if (bAddUndo && !rDoc.IsUndoEnabled())
+ bAddUndo = false;
+
+ if ( !rMark.IsMarked() && !rMark.IsMultiMarked() && (pSearchItem->HasStartPoint()) )
+ {
+ // No selection -> but we have a start point (top left corner of the
+ // current view), start searching from there, not from the current
+ // cursor position.
+ SCCOL nPosX;
+ SCROW nPosY;
+
+ int nPixelX = pSearchItem->GetStartPointX() * GetViewData().GetPPTX();
+ int nPixelY = pSearchItem->GetStartPointY() * GetViewData().GetPPTY();
+
+ GetViewData().GetPosFromPixel(nPixelX, nPixelY, GetViewData().GetActivePart(), nPosX, nPosY);
+
+ AlignToCursor( nPosX, nPosY, SC_FOLLOW_JUMP );
+ SetCursor( nPosX, nPosY, true );
+ }
+
+ SCCOL nCol, nOldCol;
+ SCROW nRow, nOldRow;
+ SCTAB nTab, nOldTab;
+ nCol = nOldCol = GetViewData().GetCurX();
+ nRow = nOldRow = GetViewData().GetCurY();
+ nTab = nOldTab = GetViewData().GetTabNo();
+
+ SvxSearchCmd nCommand = pSearchItem->GetCommand();
+ bool bAllTables = pSearchItem->IsAllTables();
+ std::set<SCTAB> aOldSelectedTables;
+ SCTAB nLastTab = rDoc.GetTableCount() - 1;
+ SCTAB nStartTab, nEndTab;
+ if ( bAllTables )
+ {
+ nStartTab = 0;
+ nEndTab = nLastTab;
+ std::set<SCTAB> aTmp(rMark.begin(), rMark.end());
+ aOldSelectedTables.swap(aTmp);
+ }
+ else
+ { //! at least one is always selected
+ nStartTab = rMark.GetFirstSelected();
+ nEndTab = rMark.GetLastSelected();
+ }
+
+ if ( nCommand == SvxSearchCmd::FIND
+ || nCommand == SvxSearchCmd::FIND_ALL)
+ bAddUndo = false;
+
+ //! account for bAttrib during Undo !!!
+
+ ScDocumentUniquePtr pUndoDoc;
+ std::unique_ptr<ScMarkData> pUndoMark;
+ OUString aUndoStr;
+ if (bAddUndo)
+ {
+ pUndoMark.reset(new ScMarkData(rMark)); // Mark is being modified
+ if ( nCommand == SvxSearchCmd::REPLACE_ALL )
+ {
+ pUndoDoc.reset(new ScDocument(SCDOCMODE_UNDO));
+ pUndoDoc->InitUndo( rDoc, nStartTab, nEndTab );
+ }
+ }
+
+ if ( bAllTables )
+ { //! select all, after pUndoMark has been created
+ for ( SCTAB j = nStartTab; j <= nEndTab; j++ )
+ {
+ rMark.SelectTable( j, true );
+ }
+ }
+
+ DoneBlockMode(true); // don't delete mark
+ InitOwnBlockMode( ScRange( nCol, nRow, nStartTab, nCol, nRow, nEndTab));
+
+ // If search starts at the beginning don't ask again whether it shall start at the beginning
+ bool bFirst = true;
+ if ( nCol == 0 && nRow == 0 && nTab == nStartTab && !pSearchItem->GetBackward() )
+ bFirst = false;
+
+ bool bFound = false;
+ while (true)
+ {
+ GetFrameWin()->EnterWait();
+ ScRangeList aMatchedRanges;
+ bool bMatchedRangesWereClamped = false;
+ if (rDoc.SearchAndReplace(*pSearchItem, nCol, nRow, nTab, rMark, aMatchedRanges, aUndoStr, pUndoDoc.get(), bMatchedRangesWereClamped))
+ {
+ bFound = true;
+ if (bAddUndo)
+ {
+ GetViewData().GetDocShell()->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoReplace>( GetViewData().GetDocShell(), *pUndoMark,
+ nCol, nRow, nTab,
+ aUndoStr, std::move(pUndoDoc), pSearchItem ) );
+ }
+
+ if (nCommand == SvxSearchCmd::FIND_ALL || nCommand == SvxSearchCmd::REPLACE_ALL)
+ {
+ SfxViewFrame* pViewFrm = SfxViewFrame::Current();
+ bool bShow = GetViewData().GetViewShell()->GetViewData().GetOptions().GetOption( VOPT_SUMMARY );
+
+ if (bShow && pViewFrm && !comphelper::LibreOfficeKit::isActive())
+ {
+ pViewFrm->ShowChildWindow(sc::SearchResultsDlgWrapper::GetChildWindowId());
+ SfxChildWindow* pWnd = pViewFrm->GetChildWindow(sc::SearchResultsDlgWrapper::GetChildWindowId());
+ if (pWnd)
+ {
+ sc::SearchResultsDlg* pDlg = static_cast<sc::SearchResultsDlg*>(pWnd->GetController().get());
+ if (pDlg)
+ {
+ const bool bCellNotes = (pSearchItem->GetCellType() == SvxSearchCellType::NOTE);
+ // ScCellIterator iterates over cells with content,
+ // for empty cells iterate over match positions.
+ const bool bEmptyCells = (!bCellNotes
+ && ((nCommand == SvxSearchCmd::FIND_ALL
+ && ScDocument::IsEmptyCellSearch(*pSearchItem))
+ || (nCommand == SvxSearchCmd::REPLACE_ALL
+ && pSearchItem->GetReplaceString().isEmpty())));
+ pDlg->FillResults(rDoc, aMatchedRanges, bCellNotes, bEmptyCells, bMatchedRangesWereClamped);
+ }
+ }
+ }
+
+ rMark.ResetMark();
+ for (size_t i = 0, n = aMatchedRanges.size(); i < n; ++i)
+ {
+ const ScRange& r = aMatchedRanges[i];
+ if (r.aStart.Tab() == nTab)
+ rMark.SetMultiMarkArea(r);
+ }
+ }
+
+ break; // break 'while (TRUE)'
+ }
+ else if ( bFirst && (nCommand == SvxSearchCmd::FIND ||
+ nCommand == SvxSearchCmd::REPLACE) )
+ {
+ bFirst = false;
+ GetFrameWin()->LeaveWait();
+ if (!bIsApi)
+ {
+ if ( nStartTab == nEndTab )
+ SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::EndSheet);
+ else
+ SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::End);
+
+ rDoc.GetSearchAndReplaceStart( *pSearchItem, nCol, nRow );
+ if (pSearchItem->GetBackward())
+ nTab = nEndTab;
+ else
+ nTab = nStartTab;
+ }
+ else
+ {
+ break; // break 'while (TRUE)'
+ }
+ }
+ else // nothing found
+ {
+ if ( nCommand == SvxSearchCmd::FIND_ALL || nCommand == SvxSearchCmd::REPLACE_ALL )
+ {
+ pDocSh->PostPaintGridAll(); // Mark
+ }
+
+ GetFrameWin()->LeaveWait();
+ if (!bIsApi)
+ {
+ GetViewData().GetViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_SEARCH_NOT_FOUND, pSearchItem->GetSearchString().toUtf8());
+ SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::NotFound);
+ }
+
+ break; // break 'while (TRUE)'
+ }
+ } // of while true
+
+ if (!aOldSelectedTables.empty())
+ {
+ // restore originally selected table
+ for (SCTAB i = 0; i <= nEndTab; ++i)
+ rMark.SelectTable(i, false);
+
+ for (const auto& rTab : aOldSelectedTables)
+ rMark.SelectTable(rTab, true);
+
+ if ( bFound )
+ { // if a table is selected as a "match" it remains selected.
+ rMark.SelectTable( nTab, true );
+ // It's a swap if only one table was selected before
+ //! otherwise now one table more might be selected
+ if ( aOldSelectedTables.size() == 1 && nTab != nOldTab )
+ rMark.SelectTable( nOldTab, false );
+ }
+ }
+
+ // Avoid LOK selection notifications before we have all the results.
+ GetViewData().GetViewShell()->setTiledSearching(true);
+ MarkDataChanged();
+ GetViewData().GetViewShell()->setTiledSearching(false);
+
+ if ( bFound )
+ {
+ if ( nTab != GetViewData().GetTabNo() )
+ SetTabNo( nTab );
+
+ // if nothing is marked, DoneBlockMode, then marking can start
+ // directly from this place via Shift-Cursor
+ if (!rMark.IsMarked() && !rMark.IsMultiMarked())
+ DoneBlockMode(true);
+
+ AlignToCursor( nCol, nRow, SC_FOLLOW_JUMP );
+ SetCursor( nCol, nRow, true );
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ Point aCurPos = GetViewData().GetScrPos(nCol, nRow, GetViewData().GetActivePart());
+
+ // just update the cell selection
+ ScGridWindow* pGridWindow = GetViewData().GetActiveWin();
+ // Don't move cell selection handles for find-all: selection of all but the first result would be lost.
+ if (pGridWindow && nCommand == SvxSearchCmd::FIND)
+ {
+ // move the cell selection handles
+ pGridWindow->SetCellSelectionPixel(LOK_SETTEXTSELECTION_RESET, aCurPos.X(), aCurPos.Y());
+ pGridWindow->SetCellSelectionPixel(LOK_SETTEXTSELECTION_START, aCurPos.X(), aCurPos.Y());
+ pGridWindow->SetCellSelectionPixel(LOK_SETTEXTSELECTION_END, aCurPos.X(), aCurPos.Y());
+ }
+
+ if (pGridWindow)
+ {
+ std::vector<tools::Rectangle> aLogicRects;
+ pGridWindow->GetCellSelection(aLogicRects);
+
+ boost::property_tree::ptree aTree;
+ aTree.put("searchString", pSearchItem->GetSearchString().toUtf8().getStr());
+ aTree.put("highlightAll", nCommand == SvxSearchCmd::FIND_ALL);
+
+ boost::property_tree::ptree aSelections;
+ for (const tools::Rectangle& rLogicRect : aLogicRects)
+ {
+ boost::property_tree::ptree aSelection;
+ aSelection.put("part", OString::number(nTab).getStr());
+ aSelection.put("rectangles", rLogicRect.toString().getStr());
+ aSelections.push_back(std::make_pair("", aSelection));
+ }
+ aTree.add_child("searchResultSelection", aSelections);
+
+ std::stringstream aStream;
+ boost::property_tree::write_json(aStream, aTree);
+ OString aPayload( aStream.str() );
+ SfxViewShell* pViewShell = GetViewData().GetViewShell();
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_SEARCH_RESULT_SELECTION, aPayload);
+
+ // Trigger LOK_CALLBACK_TEXT_SELECTION now.
+ MarkDataChanged();
+ }
+ }
+
+ if ( nCommand == SvxSearchCmd::REPLACE
+ || nCommand == SvxSearchCmd::REPLACE_ALL )
+ {
+ if ( nCommand == SvxSearchCmd::REPLACE )
+ {
+ pDocSh->PostPaint( nCol,nRow,nTab, nCol,nRow,nTab, PaintPartFlags::Grid );
+
+ // jump to next cell if we replaced everything in the cell
+ // where the cursor was positioned (but avoid switching tabs)
+ if ( nCol == nOldCol && nRow == nOldRow && nTab == nOldTab )
+ {
+ SvxSearchItem aSearchItem = ScGlobal::GetSearchItem();
+ aSearchItem.SetCommand(SvxSearchCmd::FIND);
+ aSearchItem.SetWhich(SID_SEARCH_ITEM);
+
+ ScRangeList aMatchedRanges;
+ bool bMatchedRangesWereClamped;
+ ScTable::UpdateSearchItemAddressForReplace( aSearchItem, nCol, nRow );
+ if ( rDoc.SearchAndReplace( aSearchItem, nCol, nRow, nTab, rMark, aMatchedRanges, aUndoStr, nullptr, bMatchedRangesWereClamped ) &&
+ ( nTab == nOldTab ) &&
+ ( nCol != nOldCol || nRow != nOldRow ) )
+ {
+ AlignToCursor(nCol, nRow, SC_FOLLOW_JUMP);
+ SetCursor( nCol, nRow, true );
+ }
+ }
+ }
+ else
+ pDocSh->PostPaintGridAll();
+ pDocSh->SetDocumentModified();
+ }
+ else if ( nCommand == SvxSearchCmd::FIND_ALL )
+ pDocSh->PostPaintGridAll(); // mark
+ GetFrameWin()->LeaveWait();
+ }
+ return bFound;
+}
+
+// Goal Seek
+
+void ScViewFunc::Solve( const ScSolveParam& rParam )
+{
+ ScDocument& rDoc = GetViewData().GetDocument();
+
+ SCCOL nDestCol = rParam.aRefVariableCell.Col();
+ SCROW nDestRow = rParam.aRefVariableCell.Row();
+ SCTAB nDestTab = rParam.aRefVariableCell.Tab();
+
+ ScEditableTester aTester( rDoc, nDestTab, nDestCol,nDestRow, nDestCol,nDestRow );
+ if (!aTester.IsEditable())
+ {
+ ErrorMessage(aTester.GetMessageId());
+ return;
+ }
+
+ OUString aTargetValStr;
+ if ( rParam.pStrTargetVal )
+ aTargetValStr = *rParam.pStrTargetVal;
+
+ OUString aMsgStr;
+ OUString aResStr;
+ double nSolveResult;
+
+ GetFrameWin()->EnterWait();
+
+ bool bExact =
+ rDoc.Solver(
+ rParam.aRefFormulaCell.Col(),
+ rParam.aRefFormulaCell.Row(),
+ rParam.aRefFormulaCell.Tab(),
+ nDestCol, nDestRow, nDestTab,
+ aTargetValStr,
+ nSolveResult );
+
+ GetFrameWin()->LeaveWait();
+
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ sal_uLong nFormat = 0;
+ const ScPatternAttr* pPattern = rDoc.GetPattern( nDestCol, nDestRow, nDestTab );
+ if ( pPattern )
+ nFormat = pPattern->GetNumberFormat( pFormatter );
+ const Color* p;
+ pFormatter->GetOutputString( nSolveResult, nFormat, aResStr, &p );
+
+ if ( bExact )
+ {
+ aMsgStr += ScResId( STR_MSSG_SOLVE_0 ) +
+ aResStr +
+ ScResId( STR_MSSG_SOLVE_1 );
+ }
+ else
+ {
+ aMsgStr = ScResId( STR_MSSG_SOLVE_2 ) +
+ ScResId( STR_MSSG_SOLVE_3 ) +
+ aResStr +
+ ScResId( STR_MSSG_SOLVE_4 );
+ }
+
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetViewData().GetDialogParent(),
+ VclMessageType::Question, VclButtonsType::YesNo, aMsgStr));
+ xBox->set_title(ScResId(STR_MSSG_DOSUBTOTALS_0));
+ xBox->set_default_response(RET_NO);
+ if (xBox->run() == RET_YES)
+ EnterValue( nDestCol, nDestRow, nDestTab, nSolveResult );
+
+ GetViewData().GetViewShell()->UpdateInputHandler( true );
+}
+
+// multi operation
+
+void ScViewFunc::TabOp( const ScTabOpParam& rParam, bool bRecord )
+{
+ ScRange aRange;
+ if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
+ {
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ pDocSh->GetDocFunc().TabOp( aRange, &rMark, rParam, bRecord, false );
+ }
+ else
+ ErrorMessage(STR_NOMULTISELECT);
+}
+
+void ScViewFunc::MakeScenario( const OUString& rName, const OUString& rComment,
+ const Color& rColor, ScScenarioFlags nFlags )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ SCTAB nTab = GetViewData().GetTabNo();
+
+ SCTAB nNewTab = pDocSh->MakeScenario( nTab, rName, rComment, rColor, nFlags, rMark );
+ if (nFlags & ScScenarioFlags::CopyAll)
+ SetTabNo( nNewTab, true ); // ScScenarioFlags::CopyAll -> visible
+ else
+ {
+ SfxBindings& rBindings = GetViewData().GetBindings();
+ rBindings.Invalidate( SID_STATUS_DOCPOS ); // Statusbar
+ rBindings.Invalidate( SID_ROWCOL_SELCOUNT ); // Statusbar
+ rBindings.Invalidate( SID_TABLES_COUNT );
+ rBindings.Invalidate( SID_SELECT_SCENARIO );
+ rBindings.Invalidate( FID_TABLE_SHOW );
+ }
+}
+
+void ScViewFunc::ExtendScenario()
+{
+ ScEditableTester aTester( this );
+ if (!aTester.IsEditable())
+ {
+ ErrorMessage(aTester.GetMessageId());
+ return;
+ }
+
+ // Undo: apply attributes
+
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScPatternAttr aPattern( rDoc.GetPool() );
+ aPattern.GetItemSet().Put( ScMergeFlagAttr( ScMF::Scenario ) );
+ aPattern.GetItemSet().Put( ScProtectionAttr( true ) );
+ ApplySelectionPattern(aPattern);
+}
+
+void ScViewFunc::UseScenario( const OUString& rName )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ SCTAB nTab = GetViewData().GetTabNo();
+
+ DoneBlockMode();
+ InitOwnBlockMode( ScRange( GetViewData().GetCurX(), GetViewData().GetCurY(), nTab));
+ pDocSh->UseScenario( nTab, rName );
+}
+
+// Insert table
+
+bool ScViewFunc::InsertTable( const OUString& rName, SCTAB nTab, bool bRecord )
+{
+ // Order Table/Name is inverted for DocFunc
+ bool bSuccess = GetViewData().GetDocShell()->GetDocFunc().
+ InsertTable( nTab, rName, bRecord, false );
+ if (bSuccess)
+ SetTabNo( nTab, true );
+
+ return bSuccess;
+}
+
+// Insert tables
+
+void ScViewFunc::InsertTables(std::vector<OUString>& aNames, SCTAB nTab,
+ SCTAB nCount, bool bRecord )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ weld::WaitObject aWait(GetViewData().GetDialogParent());
+
+ if (bRecord)
+ {
+ rDoc.BeginDrawUndo(); // InsertTab creates a SdrUndoNewPage
+ }
+
+ bool bFlag=false;
+
+ if(aNames.empty())
+ {
+ rDoc.CreateValidTabNames(aNames, nCount);
+ }
+ if (rDoc.InsertTabs(nTab, aNames))
+ {
+ pDocSh->Broadcast( ScTablesHint( SC_TABS_INSERTED, nTab, nCount ) );
+ bFlag = true;
+ }
+
+ if (!bFlag)
+ return;
+
+ if (bRecord)
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoInsertTables>( pDocSh, nTab, std::move(aNames)));
+
+ // Update views
+
+ SetTabNo( nTab, true );
+ pDocSh->PostPaintExtras();
+ pDocSh->SetDocumentModified();
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
+}
+
+bool ScViewFunc::AppendTable( const OUString& rName, bool bRecord )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ weld::WaitObject aWait(GetViewData().GetDialogParent());
+
+ if (bRecord)
+ rDoc.BeginDrawUndo(); // InsertTab creates a SdrUndoNewPage
+
+ if (rDoc.InsertTab( SC_TAB_APPEND, rName ))
+ {
+ SCTAB nTab = rDoc.GetTableCount()-1;
+ if (bRecord)
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoInsertTab>( pDocSh, nTab, true, rName));
+ GetViewData().InsertTab( nTab );
+ SetTabNo( nTab, true );
+ pDocSh->PostPaintExtras();
+ pDocSh->SetDocumentModified();
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+void ScViewFunc::DeleteTable( SCTAB nTab, bool bRecord )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+
+ bool bSuccess = pDocSh->GetDocFunc().DeleteTable( nTab, bRecord );
+ if (bSuccess)
+ {
+ SCTAB nNewTab = nTab;
+ if ( nNewTab >= rDoc.GetTableCount() )
+ --nNewTab;
+ SetTabNo( nNewTab, true );
+ }
+}
+
+//only use this method for undo for now, all sheets must be connected
+//this method doesn't support undo for now, merge it when it with the other method later
+void ScViewFunc::DeleteTables( const SCTAB nTab, SCTAB nSheets )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ bool bVbaEnabled = rDoc.IsInVBAMode();
+ SCTAB nNewTab = nTab;
+ weld::WaitObject aWait(GetViewData().GetDialogParent());
+
+ while ( nNewTab > 0 && !rDoc.IsVisible( nNewTab ) )
+ --nNewTab;
+
+ if (!rDoc.DeleteTabs(nTab, nSheets))
+ return;
+
+ if( bVbaEnabled )
+ {
+ for (SCTAB aTab = 0; aTab < nSheets; ++aTab)
+ {
+ OUString sCodeName;
+ bool bHasCodeName = rDoc.GetCodeName( nTab + aTab, sCodeName );
+ if ( bHasCodeName )
+ VBA_DeleteModule( *pDocSh, sCodeName );
+ }
+ }
+
+ pDocSh->Broadcast( ScTablesHint( SC_TABS_DELETED, nTab, nSheets ) );
+ if ( nNewTab >= rDoc.GetTableCount() )
+ nNewTab = rDoc.GetTableCount() - 1;
+ SetTabNo( nNewTab, true );
+
+ pDocSh->PostPaintExtras();
+ pDocSh->SetDocumentModified();
+
+ SfxApplication* pSfxApp = SfxGetpApp(); // Navigator
+ pSfxApp->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
+ pSfxApp->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged ) );
+ pSfxApp->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
+}
+
+bool ScViewFunc::DeleteTables(const vector<SCTAB> &TheTabs, bool bRecord )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ bool bVbaEnabled = rDoc.IsInVBAMode();
+ SCTAB nNewTab = TheTabs.front();
+ weld::WaitObject aWait(GetViewData().GetDialogParent());
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+ if ( bVbaEnabled )
+ bRecord = false;
+
+ while ( nNewTab > 0 && !rDoc.IsVisible( nNewTab ) )
+ --nNewTab;
+
+ bool bWasLinked = false;
+ ScDocumentUniquePtr pUndoDoc;
+ std::unique_ptr<ScRefUndoData> pUndoData;
+ if (bRecord)
+ {
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ SCTAB nCount = rDoc.GetTableCount();
+
+ OUString aOldName;
+ for(size_t i=0; i<TheTabs.size(); ++i)
+ {
+ SCTAB nTab = TheTabs[i];
+ if (i==0)
+ pUndoDoc->InitUndo( rDoc, nTab,nTab, true,true ); // incl. column/fow flags
+ else
+ pUndoDoc->AddUndoTab( nTab,nTab, true,true ); // incl. column/fow flags
+
+ rDoc.CopyToDocument(0,0,nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, InsertDeleteFlags::ALL,false, *pUndoDoc );
+ rDoc.GetName( nTab, aOldName );
+ pUndoDoc->RenameTab( nTab, aOldName );
+ if (rDoc.IsLinked(nTab))
+ {
+ bWasLinked = true;
+ pUndoDoc->SetLink( nTab, rDoc.GetLinkMode(nTab), rDoc.GetLinkDoc(nTab),
+ rDoc.GetLinkFlt(nTab), rDoc.GetLinkOpt(nTab),
+ rDoc.GetLinkTab(nTab),
+ rDoc.GetLinkRefreshDelay(nTab) );
+ }
+ if ( rDoc.IsScenario(nTab) )
+ {
+ pUndoDoc->SetScenario( nTab, true );
+ OUString aComment;
+ Color aColor;
+ ScScenarioFlags nScenFlags;
+ rDoc.GetScenarioData( nTab, aComment, aColor, nScenFlags );
+ pUndoDoc->SetScenarioData( nTab, aComment, aColor, nScenFlags );
+ bool bActive = rDoc.IsActiveScenario( nTab );
+ pUndoDoc->SetActiveScenario( nTab, bActive );
+ }
+ pUndoDoc->SetVisible( nTab, rDoc.IsVisible( nTab ) );
+ pUndoDoc->SetTabBgColor( nTab, rDoc.GetTabBgColor(nTab) );
+ auto pSheetEvents = rDoc.GetSheetEvents( nTab );
+ pUndoDoc->SetSheetEvents( nTab, std::unique_ptr<ScSheetEvents>(pSheetEvents ? new ScSheetEvents(*pSheetEvents) : nullptr) );
+ pUndoDoc->SetLayoutRTL( nTab, rDoc.IsLayoutRTL( nTab ) );
+
+ if ( rDoc.IsTabProtected( nTab ) )
+ pUndoDoc->SetTabProtection(nTab, rDoc.GetTabProtection(nTab));
+
+ // Drawing-Layer is responsible for its Undo !!!
+ // pUndoDoc->TransferDrawPage(rDoc, nTab,nTab);
+ }
+
+ pUndoDoc->AddUndoTab( 0, nCount-1 ); // all Tabs for references
+
+ rDoc.BeginDrawUndo(); // DeleteTab creates a SdrUndoDelPage
+
+ pUndoData.reset(new ScRefUndoData( &rDoc ));
+ }
+
+ bool bDelDone = false;
+
+ for(int i=TheTabs.size()-1; i>=0; --i)
+ {
+ OUString sCodeName;
+ bool bHasCodeName = rDoc.GetCodeName( TheTabs[i], sCodeName );
+ if (rDoc.DeleteTab(TheTabs[i]))
+ {
+ bDelDone = true;
+ if( bVbaEnabled && bHasCodeName )
+ {
+ VBA_DeleteModule( *pDocSh, sCodeName );
+ }
+ pDocSh->Broadcast( ScTablesHint( SC_TAB_DELETED, TheTabs[i] ) );
+ }
+ }
+ if (bRecord)
+ {
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoDeleteTab>( GetViewData().GetDocShell(), TheTabs,
+ std::move(pUndoDoc), std::move(pUndoData) ));
+ }
+
+ if (bDelDone)
+ {
+ if ( nNewTab >= rDoc.GetTableCount() )
+ nNewTab = rDoc.GetTableCount() - 1;
+
+ SetTabNo( nNewTab, true );
+
+ if (bWasLinked)
+ {
+ pDocSh->UpdateLinks(); // update Link-Manager
+ GetViewData().GetBindings().Invalidate(SID_LINKS);
+ }
+
+ pDocSh->PostPaintExtras();
+ pDocSh->SetDocumentModified();
+
+ SfxApplication* pSfxApp = SfxGetpApp(); // Navigator
+ pSfxApp->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
+ pSfxApp->Broadcast( SfxHint( SfxHintId::ScAreasChanged ) );
+ pSfxApp->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged ) );
+ pSfxApp->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
+ }
+ return bDelDone;
+}
+
+bool ScViewFunc::RenameTable( const OUString& rName, SCTAB nTab )
+{
+ // order Table/Name is inverted for DocFunc
+ bool bSuccess = GetViewData().GetDocShell()->GetDocFunc().
+ RenameTable( nTab, rName, true, false );
+ if (bSuccess)
+ {
+ // the table name might be part of a formula
+ GetViewData().GetViewShell()->UpdateInputHandler();
+ }
+ return bSuccess;
+}
+
+bool ScViewFunc::SetTabBgColor( const Color& rColor, SCTAB nTab )
+{
+ bool bSuccess = GetViewData().GetDocShell()->GetDocFunc().SetTabBgColor( nTab, rColor, true, false );
+ if (bSuccess)
+ {
+ GetViewData().GetViewShell()->UpdateInputHandler();
+ }
+ return bSuccess;
+}
+
+bool ScViewFunc::SetTabBgColor( ScUndoTabColorInfo::List& rUndoSetTabBgColorInfoList )
+{
+ bool bSuccess = GetViewData().GetDocShell()->GetDocFunc().SetTabBgColor( rUndoSetTabBgColorInfoList, false );
+ if (bSuccess)
+ {
+ GetViewData().GetViewShell()->UpdateInputHandler();
+ }
+ return bSuccess;
+}
+
+void ScViewFunc::InsertAreaLink( const OUString& rFile,
+ const OUString& rFilter, const OUString& rOptions,
+ const OUString& rSource )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ SCCOL nPosX = GetViewData().GetCurX();
+ SCROW nPosY = GetViewData().GetCurY();
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScAddress aPos( nPosX, nPosY, nTab );
+
+ pDocSh->GetDocFunc().InsertAreaLink( rFile, rFilter, rOptions, rSource, aPos, 0/*nRefresh*/, false, false );
+}
+
+void ScViewFunc::InsertTableLink( const OUString& rFile,
+ const OUString& rFilter, const OUString& rOptions,
+ std::u16string_view rTabName )
+{
+ OUString aFilterName = rFilter;
+ OUString aOpt = rOptions;
+ ScDocumentLoader aLoader( rFile, aFilterName, aOpt );
+ if (aLoader.IsError())
+ return;
+
+ ScDocShell* pSrcSh = aLoader.GetDocShell();
+ ScDocument& rSrcDoc = pSrcSh->GetDocument();
+ SCTAB nTab = MAXTAB+1;
+ if (rTabName.empty()) // no name given -> first table
+ nTab = 0;
+ else
+ {
+ OUString aTemp;
+ SCTAB nCount = rSrcDoc.GetTableCount();
+ for (SCTAB i=0; i<nCount; i++)
+ {
+ rSrcDoc.GetName( i, aTemp );
+ if ( aTemp == rTabName )
+ nTab = i;
+ }
+ }
+
+ if ( nTab <= MAXTAB )
+ ImportTables( pSrcSh, 1, &nTab, true,
+ GetViewData().GetTabNo() );
+}
+
+// Copy/link tables from another document
+
+void ScViewFunc::ImportTables( ScDocShell* pSrcShell,
+ SCTAB nCount, const SCTAB* pSrcTabs, bool bLink,SCTAB nTab )
+{
+ ScDocument& rSrcDoc = pSrcShell->GetDocument();
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ bool bUndo(rDoc.IsUndoEnabled());
+
+ bool bError = false;
+ bool bRefs = false;
+ bool bName = false;
+
+ if (rSrcDoc.GetDrawLayer())
+ pDocSh->MakeDrawLayer();
+
+ if (bUndo)
+ rDoc.BeginDrawUndo(); // drawing layer must do its own undo actions
+
+ SCTAB nInsCount = 0;
+ SCTAB i;
+ for( i=0; i<nCount; i++ )
+ { // insert sheets first and update all references
+ OUString aName;
+ rSrcDoc.GetName( pSrcTabs[i], aName );
+ rDoc.CreateValidTabName( aName );
+ if ( !rDoc.InsertTab( nTab+i, aName ) )
+ {
+ bError = true; // total error
+ break; // for
+ }
+ ++nInsCount;
+ }
+ for (i=0; i<nCount && !bError; i++)
+ {
+ SCTAB nSrcTab = pSrcTabs[i];
+ SCTAB nDestTab1=nTab+i;
+ sal_uLong nErrVal = pDocSh->TransferTab( *pSrcShell, nSrcTab, nDestTab1,
+ false, false ); // no insert
+
+ switch (nErrVal)
+ {
+ case 0: // internal error or full of errors
+ bError = true;
+ break;
+ case 2:
+ bRefs = true;
+ break;
+ case 3:
+ bName = true;
+ break;
+ case 4:
+ bRefs = bName = true;
+ break;
+ }
+
+ }
+
+ if (bLink)
+ {
+ sfx2::LinkManager* pLinkManager = rDoc.GetLinkManager();
+
+ SfxMedium* pMed = pSrcShell->GetMedium();
+ OUString aFileName = pMed->GetName();
+ OUString aFilterName;
+ if (pMed->GetFilter())
+ aFilterName = pMed->GetFilter()->GetFilterName();
+ OUString aOptions = ScDocumentLoader::GetOptions(*pMed);
+
+ bool bWasThere = rDoc.HasLink( aFileName, aFilterName, aOptions );
+
+ sal_uLong nRefresh = 0;
+ OUString aTabStr;
+ for (i=0; i<nInsCount; i++)
+ {
+ rSrcDoc.GetName( pSrcTabs[i], aTabStr );
+ rDoc.SetLink( nTab+i, ScLinkMode::NORMAL,
+ aFileName, aFilterName, aOptions, aTabStr, nRefresh );
+ }
+
+ if (!bWasThere) // Insert link only once per source document
+ {
+ ScTableLink* pLink = new ScTableLink( pDocSh, aFileName, aFilterName, aOptions, nRefresh );
+ pLink->SetInCreate( true );
+ pLinkManager->InsertFileLink( *pLink, sfx2::SvBaseLinkObjectType::ClientFile, aFileName, &aFilterName );
+ pLink->Update();
+ pLink->SetInCreate( false );
+
+ SfxBindings& rBindings = GetViewData().GetBindings();
+ rBindings.Invalidate( SID_LINKS );
+ }
+ }
+
+ if (bUndo)
+ {
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoImportTab>( pDocSh, nTab, nCount ) );
+ }
+
+ for (i=0; i<nInsCount; i++)
+ GetViewData().InsertTab(nTab);
+ SetTabNo(nTab,true);
+ pDocSh->PostPaint( 0,0,0, rDoc.MaxCol(), rDoc.MaxRow(), MAXTAB,
+ PaintPartFlags::Grid | PaintPartFlags::Top | PaintPartFlags::Left | PaintPartFlags::Extras );
+
+ SfxApplication* pSfxApp = SfxGetpApp();
+ pSfxApp->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
+ pSfxApp->Broadcast( SfxHint( SfxHintId::ScAreasChanged ) );
+
+ pDocSh->PostPaintExtras();
+ pDocSh->PostPaintGridAll();
+ pDocSh->SetDocumentModified();
+
+ if (bRefs)
+ ErrorMessage(STR_ABSREFLOST);
+ if (bName)
+ ErrorMessage(STR_NAMECONFLICT);
+}
+
+// Move/Copy table to another document
+
+void ScViewFunc::MoveTable(
+ sal_uInt16 nDestDocNo, SCTAB nDestTab, bool bCopy, const OUString* pNewTabName )
+{
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScDocShell* pDocShell = GetViewData().GetDocShell();
+ ScDocShell* pDestShell = nullptr;
+ ScTabViewShell* pDestViewSh = nullptr;
+ bool bUndo (rDoc.IsUndoEnabled());
+ bool bRename = pNewTabName && !pNewTabName->isEmpty();
+
+ bool bNewDoc = (nDestDocNo == SC_DOC_NEW);
+ if ( bNewDoc )
+ {
+ nDestTab = 0; // firstly insert
+
+ // execute without SfxCallMode::RECORD, because already contained in move command
+
+ SfxStringItem aItem( SID_FILE_NAME, "private:factory/" + STRING_SCAPP );
+ SfxStringItem aTarget( SID_TARGETNAME, "_blank" );
+
+ const SfxPoolItemHolder aResult(GetViewData().GetDispatcher().ExecuteList(
+ SID_OPENDOC, SfxCallMode::API|SfxCallMode::SYNCHRON,
+ { &aItem, &aTarget }));
+ if (nullptr != aResult.getItem())
+ {
+ if ( auto pObjectItem = dynamic_cast<const SfxObjectItem*>(aResult.getItem()) )
+ pDestShell = dynamic_cast<ScDocShell*>( pObjectItem->GetShell() );
+ else if ( auto pViewFrameItem = dynamic_cast<const SfxViewFrameItem*>(aResult.getItem()))
+ {
+ SfxViewFrame* pFrm = pViewFrameItem->GetFrame();
+ if (pFrm)
+ pDestShell = dynamic_cast<ScDocShell*>( pFrm->GetObjectShell() );
+ }
+ if (pDestShell)
+ pDestViewSh = pDestShell->GetBestViewShell();
+ }
+ }
+ else
+ pDestShell = ScDocShell::GetShellByNum( nDestDocNo );
+
+ if (!pDestShell)
+ {
+ OSL_FAIL("Destination document not found !!!");
+ return;
+ }
+
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ if (bRename && rMark.GetSelectCount() != 1)
+ {
+ // Custom sheet name is provided, but more than one sheet is selected.
+ // We don't support this scenario at the moment.
+ return;
+ }
+
+ ScDocument& rDestDoc = pDestShell->GetDocument();
+
+ if (&rDestDoc != &rDoc)
+ {
+ if (bNewDoc)
+ {
+ while (rDestDoc.GetTableCount() > 1)
+ rDestDoc.DeleteTab(0);
+ rDestDoc.RenameTab( 0, "______42_____" );
+ }
+
+ SCTAB nTabCount = rDoc.GetTableCount();
+ SCTAB nTabSelCount = rMark.GetSelectCount();
+
+ vector<SCTAB> TheTabs;
+
+ for(SCTAB i=0; i<nTabCount; ++i)
+ {
+ if(rMark.GetTableSelect(i))
+ {
+ OUString aTabName;
+ rDoc.GetName( i, aTabName);
+ TheTabs.push_back(i);
+ for(SCTAB j=i+1;j<nTabCount;j++)
+ {
+ if((!rDoc.IsVisible(j)) && rDoc.IsScenario(j))
+ {
+ rDoc.GetName( j, aTabName);
+ TheTabs.push_back(j);
+ i=j;
+ }
+ else break;
+ }
+ }
+ }
+
+ GetFrameWin()->EnterWait();
+
+ if (rDoc.GetDrawLayer())
+ pDestShell->MakeDrawLayer();
+
+ if (!bNewDoc && bUndo)
+ rDestDoc.BeginDrawUndo(); // drawing layer must do its own undo actions
+
+ sal_uLong nErrVal =1;
+ if(nDestTab==SC_TAB_APPEND)
+ nDestTab=rDestDoc.GetTableCount();
+ SCTAB nDestTab1=nDestTab;
+ ScClipParam aParam;
+ for( size_t j=0; j<TheTabs.size(); ++j, ++nDestTab1 )
+ { // insert sheets first and update all references
+ OUString aName;
+ if (bRename)
+ aName = *pNewTabName;
+ else
+ rDoc.GetName( TheTabs[j], aName );
+
+ rDestDoc.CreateValidTabName( aName );
+ if ( !rDestDoc.InsertTab( nDestTab1, aName ) )
+ {
+ nErrVal = 0; // total error
+ break; // for
+ }
+ ScRange aRange( 0, 0, TheTabs[j], rDoc.MaxCol(), rDoc.MaxRow(), TheTabs[j] );
+ aParam.maRanges.push_back(aRange);
+ }
+ rDoc.SetClipParam(aParam);
+ if ( nErrVal > 0 )
+ {
+ nDestTab1 = nDestTab;
+ for(SCTAB nTab : TheTabs)
+ {
+ nErrVal = pDestShell->TransferTab( *pDocShell, nTab, nDestTab1, false, false );
+ nDestTab1++;
+ }
+ }
+ if (!bNewDoc && bUndo)
+ {
+ OUString sName;
+ rDestDoc.GetName(nDestTab, sName);
+ pDestShell->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoImportTab>( pDestShell, nDestTab,
+ static_cast<SCTAB>(TheTabs.size())));
+
+ }
+ else
+ {
+ pDestShell->GetUndoManager()->Clear();
+ }
+
+ GetFrameWin()->LeaveWait();
+ switch (nErrVal)
+ {
+ case 0: // internal error or full of errors
+ {
+ ErrorMessage(STR_TABINSERT_ERROR);
+ return;
+ }
+ case 2:
+ ErrorMessage(STR_ABSREFLOST);
+ break;
+ case 3:
+ ErrorMessage(STR_NAMECONFLICT);
+ break;
+ case 4:
+ {
+ ErrorMessage(STR_ABSREFLOST);
+ ErrorMessage(STR_NAMECONFLICT);
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (!bCopy)
+ {
+ if(nTabCount!=nTabSelCount)
+ DeleteTables(TheTabs); // incl. Paint & Undo
+ else
+ ErrorMessage(STR_TABREMOVE_ERROR);
+ }
+
+ if (bNewDoc)
+ {
+ // ChartListenerCollection must be updated before DeleteTab
+ if ( rDestDoc.IsChartListenerCollectionNeedsUpdate() )
+ rDestDoc.UpdateChartListenerCollection();
+
+ SCTAB nNumTabsInserted = static_cast<SCTAB>(TheTabs.size());
+ pDestShell->Broadcast( ScTablesHint( SC_TABS_INSERTED, 0, nNumTabsInserted ) );
+
+ rDestDoc.DeleteTab( nNumTabsInserted ); // old first table
+ pDestShell->Broadcast( ScTablesHint( SC_TAB_DELETED, nNumTabsInserted ) );
+
+ if (pDestViewSh)
+ {
+ // Make sure to clear the cached page view after sheet
+ // deletion, which still points to the sdr page belonging to
+ // the deleted sheet.
+ SdrView* pSdrView = pDestViewSh->GetScDrawView();
+ if (pSdrView)
+ pSdrView->ClearPageView();
+
+ pDestViewSh->TabChanged(); // pages on the drawing layer
+ }
+ pDestShell->PostPaint( 0,0,0, rDoc.MaxCol(), rDoc.MaxRow(), MAXTAB,
+ PaintPartFlags::Grid | PaintPartFlags::Top | PaintPartFlags::Left |
+ PaintPartFlags::Extras | PaintPartFlags::Size );
+ // PaintPartFlags::Size for outline
+ }
+ else
+ {
+ pDestShell->Broadcast( ScTablesHint( SC_TAB_INSERTED, nDestTab ) );
+ pDestShell->PostPaintExtras();
+ pDestShell->PostPaintGridAll();
+ }
+
+ TheTabs.clear();
+
+ pDestShell->SetDocumentModified();
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
+ }
+ else
+ {
+ // Move or copy within the same document.
+ SCTAB nTabCount = rDoc.GetTableCount();
+
+ unique_ptr< vector<SCTAB> > pSrcTabs(new vector<SCTAB>);
+ unique_ptr< vector<SCTAB> > pDestTabs(new vector<SCTAB>);
+ unique_ptr< vector<OUString> > pTabNames(new vector<OUString>);
+ unique_ptr< vector<OUString> > pDestNames;
+ pSrcTabs->reserve(nTabCount);
+ pDestTabs->reserve(nTabCount);
+ pTabNames->reserve(nTabCount);
+ OUString aDestName;
+
+ for(SCTAB i=0;i<nTabCount;i++)
+ {
+ if(rMark.GetTableSelect(i))
+ {
+ OUString aTabName;
+ rDoc.GetName( i, aTabName);
+ pTabNames->push_back(aTabName);
+
+ for(SCTAB j=i+1;j<nTabCount;j++)
+ {
+ if((!rDoc.IsVisible(j)) && rDoc.IsScenario(j))
+ {
+ rDoc.GetName( j, aTabName);
+ pTabNames->push_back(aTabName);
+ i=j;
+ }
+ else break;
+ }
+ }
+ }
+
+ if (bCopy && bUndo)
+ rDoc.BeginDrawUndo(); // drawing layer must do its own undo actions
+
+ rDoc.GetName( nDestTab, aDestName);
+ SCTAB nDestTab1=nDestTab;
+ SCTAB nMovTab=0;
+ for (size_t j = 0, n = pTabNames->size(); j < n; ++j)
+ {
+ nTabCount = rDoc.GetTableCount();
+ const OUString& rStr = (*pTabNames)[j];
+ if(!rDoc.GetTable(rStr,nMovTab))
+ {
+ nMovTab=nTabCount;
+ }
+ if(!rDoc.GetTable(aDestName,nDestTab1))
+ {
+ nDestTab1=nTabCount;
+ }
+ pDocShell->MoveTable( nMovTab, nDestTab1, bCopy, false ); // Undo is here
+
+ // tdf#43175 - Adjust chart references on every copied sheet
+ if (bCopy)
+ {
+ // New position of source table after moving
+ SCTAB nSrcTab = (nDestTab1 <= nMovTab) ? nMovTab + 1 : nMovTab;
+ //#i29848# adjust references to data on the copied sheet
+ ScChartHelper::AdjustRangesOfChartsOnDestinationPage(rDoc, rDestDoc, nSrcTab,
+ nDestTab1);
+ }
+
+ if(bCopy && rDoc.IsScenario(nMovTab))
+ {
+ OUString aComment;
+ Color aColor;
+ ScScenarioFlags nFlags;
+
+ rDoc.GetScenarioData(nMovTab, aComment,aColor, nFlags);
+ rDoc.SetScenario(nDestTab1,true);
+ rDoc.SetScenarioData(nDestTab1,aComment,aColor,nFlags);
+ bool bActive = rDoc.IsActiveScenario(nMovTab );
+ rDoc.SetActiveScenario( nDestTab1, bActive );
+ bool bVisible=rDoc.IsVisible(nMovTab);
+ rDoc.SetVisible(nDestTab1,bVisible );
+ }
+
+ pSrcTabs->push_back(nMovTab);
+
+ if(!bCopy)
+ {
+ if(!rDoc.GetTable(rStr,nDestTab1))
+ {
+ nDestTab1=nTabCount;
+ }
+ }
+
+ pDestTabs->push_back(nDestTab1);
+ }
+
+ // Rename must be done after all sheets have been moved.
+ if (bRename)
+ {
+ pDestNames.reset(new vector<OUString>);
+ size_t n = pDestTabs->size();
+ pDestNames->reserve(n);
+ for (size_t j = 0; j < n; ++j)
+ {
+ SCTAB nRenameTab = (*pDestTabs)[j];
+ OUString aTabName = *pNewTabName;
+ rDoc.CreateValidTabName( aTabName );
+ pDestNames->push_back(aTabName);
+ rDoc.RenameTab(nRenameTab, aTabName);
+ }
+ }
+ else
+ // No need to keep this around when we are not renaming.
+ pTabNames.reset();
+
+ SCTAB nTab = GetViewData().GetTabNo();
+
+ if (bUndo)
+ {
+ if (bCopy)
+ {
+ pDocShell->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoCopyTab>(
+ pDocShell, std::move(pSrcTabs), std::move(pDestTabs), std::move(pDestNames)));
+ }
+ else
+ {
+ pDocShell->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoMoveTab>(
+ pDocShell, std::move(pSrcTabs), std::move(pDestTabs), std::move(pTabNames), std::move(pDestNames)));
+ }
+ }
+
+ SCTAB nNewTab = nDestTab;
+ if (nNewTab == SC_TAB_APPEND)
+ nNewTab = rDoc.GetTableCount()-1;
+ else if (!bCopy && nTab<nDestTab)
+ nNewTab--;
+
+ SetTabNo( nNewTab, true );
+ }
+}
+
+void ScViewFunc::ShowTable( const std::vector<OUString>& rNames )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ bool bUndo(rDoc.IsUndoEnabled());
+
+ std::vector<SCTAB> undoTabs;
+ SCTAB nPos = 0;
+
+ bool bFound(false);
+
+ for (const OUString& aName : rNames)
+ {
+ if (rDoc.GetTable(aName, nPos))
+ {
+ rDoc.SetVisible( nPos, true );
+ SetTabNo( nPos, true );
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
+ if (!bFound)
+ bFound = true;
+ if (bUndo)
+ undoTabs.push_back(nPos);
+ }
+ }
+ if (bFound)
+ {
+ if (bUndo)
+ {
+ pDocSh->GetUndoManager()->AddUndoAction( std::make_unique<ScUndoShowHideTab>( pDocSh, std::move(undoTabs), true ) );
+ }
+ pDocSh->PostPaint(0,0,0,rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB, PaintPartFlags::Extras);
+ pDocSh->SetDocumentModified();
+ }
+}
+
+void ScViewFunc::HideTable( const ScMarkData& rMark, SCTAB nTabToSelect )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ bool bUndo(rDoc.IsUndoEnabled());
+ SCTAB nVisible = 0;
+ SCTAB nTabCount = rDoc.GetTableCount();
+
+ SCTAB nTabSelCount = rMark.GetSelectCount();
+
+ // check to make sure we won't hide all sheets. we need at least one visible at all times.
+ for ( SCTAB i=0; i < nTabCount && nVisible <= nTabSelCount ; i++ )
+ if (rDoc.IsVisible(i))
+ ++nVisible;
+
+ if (nVisible <= nTabSelCount)
+ return;
+
+ std::vector<SCTAB> undoTabs;
+
+ // need to take a copy of selectedtabs since it is modified in the loop
+ const ScMarkData::MarkedTabsType selectedTabs = rMark.GetSelectedTabs();
+ for (const SCTAB& nTab : selectedTabs)
+ {
+ if (rDoc.IsVisible( nTab ))
+ {
+ rDoc.SetVisible( nTab, false );
+ // Update views
+ pDocSh->Broadcast( ScTablesHint( SC_TAB_HIDDEN, nTab ) );
+ SetTabNo( nTab, true );
+ // Store for undo
+ if (bUndo)
+ undoTabs.push_back(nTab);
+ }
+ }
+
+ if (nTabToSelect != -1)
+ SetTabNo(nTabToSelect);
+
+ if (bUndo)
+ {
+ pDocSh->GetUndoManager()->AddUndoAction( std::make_unique<ScUndoShowHideTab>( pDocSh, std::move(undoTabs), false ) );
+ }
+
+ // Update views
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
+ pDocSh->PostPaint(0,0,0,rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB, PaintPartFlags::Extras);
+ pDocSh->SetDocumentModified();
+}
+
+void ScViewFunc::InsertSpecialChar( const OUString& rStr, const vcl::Font& rFont )
+{
+ ScEditableTester aTester( this );
+ if (!aTester.IsEditable())
+ {
+ ErrorMessage(aTester.GetMessageId());
+ return;
+ }
+
+ const sal_Unicode* pChar = rStr.getStr();
+ ScTabViewShell* pViewShell = GetViewData().GetViewShell();
+ SvxFontItem aFontItem( rFont.GetFamilyType(),
+ rFont.GetFamilyName(),
+ rFont.GetStyleName(),
+ rFont.GetPitch(),
+ rFont.GetCharSet(),
+ ATTR_FONT );
+
+ // if string contains WEAK characters, set all fonts
+ SvtScriptType nScript;
+ ScDocument& rDoc = GetViewData().GetDocument();
+ if ( rDoc.HasStringWeakCharacters( rStr ) )
+ nScript = SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX;
+ else
+ nScript = rDoc.GetStringScriptType( rStr );
+
+ SvxScriptSetItem aSetItem( SID_ATTR_CHAR_FONT, pViewShell->GetPool() );
+ aSetItem.PutItemForScriptType( nScript, aFontItem );
+ ApplyUserItemSet( aSetItem.GetItemSet() );
+
+ while ( *pChar )
+ pViewShell->TabKeyInput( KeyEvent( *(pChar++), vcl::KeyCode() ) );
+}
+
+void ScViewFunc::UpdateLineAttrs( SvxBorderLine& rLine,
+ const SvxBorderLine* pDestLine,
+ const SvxBorderLine* pSrcLine,
+ bool bColor )
+{
+ if ( !(pSrcLine && pDestLine) )
+ return;
+
+ if ( bColor )
+ {
+ rLine.SetColor ( pSrcLine->GetColor() );
+ rLine.SetBorderLineStyle(pDestLine->GetBorderLineStyle());
+ rLine.SetWidth ( pDestLine->GetWidth() );
+ }
+ else
+ {
+ rLine.SetColor ( pDestLine->GetColor() );
+ rLine.SetBorderLineStyle(pSrcLine->GetBorderLineStyle());
+ rLine.SetWidth ( pSrcLine->GetWidth() );
+ }
+}
+
+#define SET_LINE_ATTRIBUTES(LINE,BOXLINE) \
+ pBoxLine = aBoxItem.Get##LINE(); \
+ if ( pBoxLine ) \
+ { \
+ if ( pLine ) \
+ { \
+ UpdateLineAttrs( aLine, pBoxLine, pLine, bColorOnly ); \
+ aBoxItem.SetLine( &aLine, BOXLINE ); \
+ } \
+ else \
+ aBoxItem.SetLine( nullptr, BOXLINE ); \
+ }
+
+void ScViewFunc::SetSelectionFrameLines( const SvxBorderLine* pLine,
+ bool bColorOnly )
+{
+ // Not editable only due to a matrix? Attribute is ok anyhow.
+ bool bOnlyNotBecauseOfMatrix;
+ if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
+ {
+ ErrorMessage(STR_PROTECTIONERR);
+ return;
+ }
+
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScMarkData aFuncMark( GetViewData().GetMarkData() ); // local copy for UnmarkFiltered
+ ScViewUtil::UnmarkFiltered( aFuncMark, rDoc );
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ const ScPatternAttr* pSelAttrs = GetSelectionPattern();
+ const SfxItemSet& rSelItemSet = pSelAttrs->GetItemSet();
+
+ const SfxPoolItem* pBorderAttr = nullptr;
+ SfxItemState eItemState = rSelItemSet.GetItemState( ATTR_BORDER, true, &pBorderAttr );
+
+ const SfxPoolItem* pTLBRItem = nullptr;
+ SfxItemState eTLBRState = rSelItemSet.GetItemState( ATTR_BORDER_TLBR, true, &pTLBRItem );
+
+ const SfxPoolItem* pBLTRItem = nullptr;
+ SfxItemState eBLTRState = rSelItemSet.GetItemState( ATTR_BORDER_BLTR, true, &pBLTRItem );
+
+ // any of the lines visible?
+ if( !((eItemState != SfxItemState::DEFAULT) || (eTLBRState != SfxItemState::DEFAULT) || (eBLTRState != SfxItemState::DEFAULT)) )
+ return;
+
+ // none of the lines don't care?
+ if( (eItemState != SfxItemState::DONTCARE) && (eTLBRState != SfxItemState::DONTCARE) && (eBLTRState != SfxItemState::DONTCARE) )
+ {
+ SfxItemSetFixed<ATTR_PATTERN_START, ATTR_PATTERN_END> aOldSet( *rDoc.GetPool() );
+ SfxItemSetFixed<ATTR_PATTERN_START, ATTR_PATTERN_END> aNewSet( *rDoc.GetPool() );
+
+ SvxBorderLine aLine;
+
+ if( pBorderAttr )
+ {
+ const SvxBorderLine* pBoxLine = nullptr;
+ SvxBoxItem aBoxItem( *static_cast<const SvxBoxItem*>(pBorderAttr) );
+ SvxBoxInfoItem aBoxInfoItem( ATTR_BORDER_INNER );
+
+ // here pBoxLine is used
+ SET_LINE_ATTRIBUTES(Top,SvxBoxItemLine::TOP)
+ SET_LINE_ATTRIBUTES(Bottom,SvxBoxItemLine::BOTTOM)
+ SET_LINE_ATTRIBUTES(Left,SvxBoxItemLine::LEFT)
+ SET_LINE_ATTRIBUTES(Right,SvxBoxItemLine::RIGHT)
+
+ aBoxInfoItem.SetLine( aBoxItem.GetTop(), SvxBoxInfoItemLine::HORI );
+ aBoxInfoItem.SetLine( aBoxItem.GetLeft(), SvxBoxInfoItemLine::VERT );
+ aBoxInfoItem.ResetFlags(); // set Lines to Valid
+
+ aOldSet.Put( *pBorderAttr );
+ aNewSet.Put( aBoxItem );
+ aNewSet.Put( aBoxInfoItem );
+ }
+
+ if( pTLBRItem && static_cast<const SvxLineItem*>(pTLBRItem)->GetLine() )
+ {
+ SvxLineItem aTLBRItem( *static_cast<const SvxLineItem*>(pTLBRItem) );
+ UpdateLineAttrs( aLine, aTLBRItem.GetLine(), pLine, bColorOnly );
+ aTLBRItem.SetLine( &aLine );
+ aOldSet.Put( *pTLBRItem );
+ aNewSet.Put( aTLBRItem );
+ }
+
+ if( pBLTRItem && static_cast<const SvxLineItem*>(pBLTRItem)->GetLine() )
+ {
+ SvxLineItem aBLTRItem( *static_cast<const SvxLineItem*>(pBLTRItem) );
+ UpdateLineAttrs( aLine, aBLTRItem.GetLine(), pLine, bColorOnly );
+ aBLTRItem.SetLine( &aLine );
+ aOldSet.Put( *pBLTRItem );
+ aNewSet.Put( aBLTRItem );
+ }
+
+ ApplyAttributes( aNewSet, aOldSet );
+ }
+ else // if ( eItemState == SfxItemState::DONTCARE )
+ {
+ aFuncMark.MarkToMulti();
+ rDoc.ApplySelectionLineStyle( aFuncMark, pLine, bColorOnly );
+ }
+
+ const ScRange& aMarkRange = aFuncMark.GetMultiMarkArea();
+ SCCOL nStartCol = aMarkRange.aStart.Col();
+ SCROW nStartRow = aMarkRange.aStart.Row();
+ SCTAB nStartTab = aMarkRange.aStart.Tab();
+ SCCOL nEndCol = aMarkRange.aEnd.Col();
+ SCROW nEndRow = aMarkRange.aEnd.Row();
+ SCTAB nEndTab = aMarkRange.aEnd.Tab();
+ pDocSh->PostPaint( nStartCol, nStartRow, nStartTab,
+ nEndCol, nEndRow, nEndTab,
+ PaintPartFlags::Grid, SC_PF_LINES | SC_PF_TESTMERGE );
+
+ pDocSh->UpdateOle(GetViewData());
+ pDocSh->SetDocumentModified();
+}
+
+#undef SET_LINE_ATTRIBUTES
+
+void ScViewFunc::SetValidation( const ScValidationData& rNew )
+{
+ ScDocument& rDoc = GetViewData().GetDocument();
+ sal_uInt32 nIndex = rDoc.AddValidationEntry(rNew); // for it there is no Undo
+ SfxUInt32Item aItem( ATTR_VALIDDATA, nIndex );
+
+ ApplyAttr( aItem ); // with Paint and Undo...
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/viewfun3.cxx b/sc/source/ui/view/viewfun3.cxx
new file mode 100644
index 0000000000..7a6403237b
--- /dev/null
+++ b/sc/source/ui/view/viewfun3.cxx
@@ -0,0 +1,2028 @@
+/* -*- 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 .
+ */
+
+#include <scitems.hxx>
+#include <svx/svdpage.hxx>
+#include <sfx2/docfile.hxx>
+#include <comphelper/classids.hxx>
+#include <comphelper/lok.hxx>
+#include <sot/formats.hxx>
+#include <sot/storage.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <tools/urlobj.hxx>
+#include <sot/exchange.hxx>
+#include <memory>
+#include <vcl/uitest/logger.hxx>
+#include <vcl/uitest/eventdescription.hxx>
+#include <vcl/TypeSerializer.hxx>
+#include <osl/diagnose.h>
+
+#include <attrib.hxx>
+#include <patattr.hxx>
+#include <dociter.hxx>
+#include <viewfunc.hxx>
+#include <tabvwsh.hxx>
+#include <docsh.hxx>
+#include <docfunc.hxx>
+#include <undoblk.hxx>
+#include <refundo.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <global.hxx>
+#include <transobj.hxx>
+#include <drwtrans.hxx>
+#include <chgtrack.hxx>
+#include <waitoff.hxx>
+#include <scmod.hxx>
+#include <inputopt.hxx>
+#include <warnbox.hxx>
+#include <drwlayer.hxx>
+#include <editable.hxx>
+#include <docuno.hxx>
+#include <clipparam.hxx>
+#include <undodat.hxx>
+#include <drawview.hxx>
+#include <cliputil.hxx>
+#include <clipoptions.hxx>
+#include <gridwin.hxx>
+#include <com/sun/star/util/XCloneable.hpp>
+
+using namespace com::sun::star;
+
+namespace {
+
+void collectUIInformation(std::map<OUString, OUString>&& aParameters, const OUString& action)
+{
+ EventDescription aDescription;
+ aDescription.aID = "grid_window";
+ aDescription.aAction = action;
+ aDescription.aParameters = std::move(aParameters);
+ aDescription.aParent = "MainWindow";
+ aDescription.aKeyWord = "ScGridWinUIObject";
+
+ UITestLogger::getInstance().logEvent(aDescription);
+}
+
+}
+
+// GlobalName of writer-DocShell from comphelper/classids.hxx
+
+// C U T
+
+void ScViewFunc::CutToClip()
+{
+ UpdateInputLine();
+
+ ScEditableTester aTester( this );
+ if (!aTester.IsEditable()) // selection editable?
+ {
+ ErrorMessage( aTester.GetMessageId() );
+ return;
+ }
+
+ ScRange aRange; // delete this range
+ if ( GetViewData().GetSimpleArea( aRange ) == SC_MARK_SIMPLE )
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ const bool bRecord(rDoc.IsUndoEnabled()); // Undo/Redo
+
+ ScDocShellModificator aModificator( *pDocSh );
+
+ if ( !rMark.IsMarked() && !rMark.IsMultiMarked() ) // mark the range if not marked yet
+ {
+ DoneBlockMode();
+ InitOwnBlockMode( aRange );
+ rMark.SetMarkArea( aRange );
+ MarkDataChanged();
+ }
+
+ CopyToClip( nullptr, true, false, true/*bIncludeObjects*/ ); // copy to clipboard
+
+ ScAddress aOldEnd( aRange.aEnd ); // combined cells in this range?
+ rDoc.ExtendMerge( aRange, true );
+
+ ScDocumentUniquePtr pUndoDoc;
+ if ( bRecord )
+ {
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndoSelected( rDoc, rMark );
+ // all sheets - CopyToDocument skips those that don't exist in pUndoDoc
+ ScRange aCopyRange = aRange;
+ aCopyRange.aStart.SetTab(0);
+ aCopyRange.aEnd.SetTab(rDoc.GetTableCount()-1);
+ rDoc.CopyToDocument( aCopyRange, (InsertDeleteFlags::ALL & ~InsertDeleteFlags::OBJECTS) | InsertDeleteFlags::NOCAPTIONS, false, *pUndoDoc );
+ rDoc.BeginDrawUndo();
+ }
+
+ sal_uInt16 nExtFlags = 0;
+ pDocSh->UpdatePaintExt( nExtFlags, aRange );
+
+ rMark.MarkToMulti();
+ rDoc.DeleteSelection( InsertDeleteFlags::ALL, rMark );
+ rDoc.DeleteObjectsInSelection( rMark );
+ rMark.MarkToSimple();
+
+ if ( !AdjustRowHeight( aRange.aStart.Row(), aRange.aEnd.Row(), true ) )
+ pDocSh->PostPaint( aRange, PaintPartFlags::Grid, nExtFlags );
+
+ if ( bRecord ) // Draw-Undo now available
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoCut>( pDocSh, aRange, aOldEnd, rMark, std::move(pUndoDoc) ) );
+
+ aModificator.SetDocumentModified();
+ pDocSh->UpdateOle(GetViewData());
+
+ CellContentChanged();
+
+ OUString aStartAddress = aRange.aStart.GetColRowString();
+ OUString aEndAddress = aRange.aEnd.GetColRowString();
+
+ collectUIInformation({{"RANGE", aStartAddress + ":" + aEndAddress}}, "CUT");
+ }
+ else
+ ErrorMessage( STR_NOMULTISELECT );
+}
+
+// C O P Y
+
+bool ScViewFunc::CopyToClip( ScDocument* pClipDoc, bool bCut, bool bApi, bool bIncludeObjects, bool bStopEdit )
+{
+ ScRange aRange;
+ ScMarkType eMarkType = GetViewData().GetSimpleArea( aRange );
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ bool bDone = false;
+
+ if ( eMarkType == SC_MARK_SIMPLE || eMarkType == SC_MARK_SIMPLE_FILTERED )
+ {
+ ScRangeList aRangeList( aRange );
+ bDone = CopyToClip( pClipDoc, aRangeList, bCut, bApi, bIncludeObjects, bStopEdit );
+ }
+ else if (eMarkType == SC_MARK_MULTI)
+ {
+ ScRangeList aRangeList;
+ rMark.MarkToSimple();
+ rMark.FillRangeListWithMarks(&aRangeList, false);
+ bDone = CopyToClip( pClipDoc, aRangeList, bCut, bApi, bIncludeObjects, bStopEdit );
+ }
+ else
+ {
+ if (!bApi)
+ ErrorMessage(STR_NOMULTISELECT);
+ }
+ if( !bCut ){
+ OUString aStartAddress = aRange.aStart.GetColRowString();
+ OUString aEndAddress = aRange.aEnd.GetColRowString();
+ collectUIInformation({{"RANGE", aStartAddress + ":" + aEndAddress}}, "COPY");
+ }
+ return bDone;
+}
+
+// Copy the content of the Range into clipboard.
+bool ScViewFunc::CopyToClip( ScDocument* pClipDoc, const ScRangeList& rRanges, bool bCut, bool bApi, bool bIncludeObjects, bool bStopEdit )
+{
+ if ( rRanges.empty() )
+ return false;
+ if ( bStopEdit )
+ UpdateInputLine();
+
+ bool bDone;
+ if (rRanges.size() > 1) // isMultiRange
+ bDone = CopyToClipMultiRange(pClipDoc, rRanges, bCut, bApi, bIncludeObjects);
+ else
+ bDone = CopyToClipSingleRange(pClipDoc, rRanges, bCut, bIncludeObjects);
+
+ return bDone;
+}
+
+bool ScViewFunc::CopyToClipSingleRange( ScDocument* pClipDoc, const ScRangeList& rRanges, bool bCut, bool bIncludeObjects )
+{
+ ScRange aRange = rRanges[0];
+ ScClipParam aClipParam( aRange, bCut );
+ aClipParam.maRanges = rRanges;
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+
+ if (rDoc.HasSelectedBlockMatrixFragment( aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row(), rMark ) )
+ return false;
+
+ std::shared_ptr<ScDocument> pSysClipDoc;
+ if ( !pClipDoc ) // no clip doc specified
+ {
+ // Create one (deleted by ScTransferObj), and copy into system.
+ pSysClipDoc = std::make_shared<ScDocument>( SCDOCMODE_CLIP );
+ pClipDoc = pSysClipDoc.get();
+ }
+ if ( !bCut )
+ {
+ ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack();
+ if ( pChangeTrack )
+ pChangeTrack->ResetLastCut();
+ }
+
+ if ( pSysClipDoc && bIncludeObjects )
+ {
+ bool bAnyOle = rDoc.HasOLEObjectsInArea( aRange );
+ // Update ScGlobal::xDrawClipDocShellRef.
+ ScDrawLayer::SetGlobalDrawPersist( ScTransferObj::SetDrawClipDoc( bAnyOle, pSysClipDoc ) );
+ }
+
+ // is this necessary?, will setting the doc id upset the
+ // following paste operation with range? would be nicer to just set this always
+ // and lose the 'if' above
+ aClipParam.setSourceDocID( rDoc.GetDocumentID() );
+
+ if (ScDocShell* pObjectShell = rDoc.GetDocumentShell())
+ {
+ // Copy document properties from pObjectShell to pClipDoc (to its clip options, as it has no object shell).
+ uno::Reference<util::XCloneable> xCloneable(pObjectShell->getDocProperties(), uno::UNO_QUERY_THROW);
+ std::unique_ptr<ScClipOptions> pOptions(new ScClipOptions);
+ pOptions->m_xDocumentProperties.set(xCloneable->createClone(), uno::UNO_QUERY);
+ pClipDoc->SetClipOptions(std::move(pOptions));
+ }
+
+ rDoc.CopyToClip( aClipParam, pClipDoc, &rMark, false, bIncludeObjects );
+ if (ScDrawLayer* pDrawLayer = pClipDoc->GetDrawLayer())
+ {
+ ScClipParam& rClipDocClipParam = pClipDoc->GetClipParam();
+ ScRangeListVector& rRangesVector = rClipDocClipParam.maProtectedChartRangesVector;
+ SCTAB nTabCount = pClipDoc->GetTableCount();
+ for ( SCTAB nTab = 0; nTab < nTabCount; ++nTab )
+ {
+ SdrPage* pPage = pDrawLayer->GetPage( static_cast< sal_uInt16 >( nTab ) );
+ if ( pPage )
+ {
+ ScChartHelper::FillProtectedChartRangesVector( rRangesVector, rDoc, pPage );
+ }
+ }
+ }
+
+ if ( pSysClipDoc )
+ {
+ ScDrawLayer::SetGlobalDrawPersist(nullptr);
+ ScGlobal::SetClipDocName( rDoc.GetDocumentShell()->GetTitle( SFX_TITLE_FULLNAME ) );
+ }
+ pClipDoc->ExtendMerge( aRange, true );
+
+ if ( pSysClipDoc )
+ {
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ TransferableObjectDescriptor aObjDesc;
+ pDocSh->FillTransferableObjectDescriptor( aObjDesc );
+ aObjDesc.maDisplayName = pDocSh->GetMedium()->GetURLObject().GetURLNoPass();
+ // maSize is set in ScTransferObj ctor
+
+ rtl::Reference<ScTransferObj> pTransferObj(new ScTransferObj( pSysClipDoc, std::move(aObjDesc) ));
+ if ( ScGlobal::xDrawClipDocShellRef.is() )
+ {
+ SfxObjectShellRef aPersistRef( ScGlobal::xDrawClipDocShellRef.get() );
+ pTransferObj->SetDrawPersist( aPersistRef );// keep persist for ole objects alive
+ }
+ pTransferObj->CopyToClipboard( GetActiveWin() );
+ }
+
+ return true;
+}
+
+bool ScViewFunc::CopyToClipMultiRange( const ScDocument* pInputClipDoc, const ScRangeList& rRanges, bool bCut, bool bApi, bool bIncludeObjects )
+{
+ if (bCut)
+ {
+ // We don't support cutting of multi-selections.
+ if (!bApi)
+ ErrorMessage(STR_NOMULTISELECT);
+ return false;
+ }
+ if (pInputClipDoc)
+ {
+ // TODO: What's this for?
+ if (!bApi)
+ ErrorMessage(STR_NOMULTISELECT);
+ return false;
+ }
+
+ ScClipParam aClipParam( rRanges[0], bCut );
+ aClipParam.maRanges = rRanges;
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ bool bDone = false;
+ bool bSuccess = false;
+ aClipParam.mbCutMode = false;
+
+ do
+ {
+ ScDocumentUniquePtr pDocClip(new ScDocument(SCDOCMODE_CLIP));
+
+ // Check for geometrical feasibility of the ranges.
+ bool bValidRanges = true;
+ ScRange const * p = &aClipParam.maRanges.front();
+ SCCOL nPrevColDelta = 0;
+ SCROW nPrevRowDelta = 0;
+ SCCOL nPrevCol = p->aStart.Col();
+ SCROW nPrevRow = p->aStart.Row();
+ SCCOL nPrevColSize = p->aEnd.Col() - p->aStart.Col() + 1;
+ SCROW nPrevRowSize = p->aEnd.Row() - p->aStart.Row() + 1;
+ for ( size_t i = 1; i < aClipParam.maRanges.size(); ++i )
+ {
+ p = &aClipParam.maRanges[i];
+ if ( rDoc.HasSelectedBlockMatrixFragment(
+ p->aStart.Col(), p->aStart.Row(), p->aEnd.Col(), p->aEnd.Row(), rMark) )
+ {
+ if (!bApi)
+ ErrorMessage(STR_MATRIXFRAGMENTERR);
+ return false;
+ }
+
+ SCCOL nColDelta = p->aStart.Col() - nPrevCol;
+ SCROW nRowDelta = p->aStart.Row() - nPrevRow;
+
+ if ((nColDelta && nRowDelta) || (nPrevColDelta && nRowDelta) || (nPrevRowDelta && nColDelta))
+ {
+ bValidRanges = false;
+ break;
+ }
+
+ if (aClipParam.meDirection == ScClipParam::Unspecified)
+ {
+ if (nColDelta)
+ aClipParam.meDirection = ScClipParam::Column;
+ if (nRowDelta)
+ aClipParam.meDirection = ScClipParam::Row;
+ }
+
+ SCCOL nColSize = p->aEnd.Col() - p->aStart.Col() + 1;
+ SCROW nRowSize = p->aEnd.Row() - p->aStart.Row() + 1;
+
+ if (aClipParam.meDirection == ScClipParam::Column && nRowSize != nPrevRowSize)
+ {
+ // column-oriented ranges must have identical row size.
+ bValidRanges = false;
+ break;
+ }
+ if (aClipParam.meDirection == ScClipParam::Row && nColSize != nPrevColSize)
+ {
+ // likewise, row-oriented ranges must have identical
+ // column size.
+ bValidRanges = false;
+ break;
+ }
+
+ nPrevCol = p->aStart.Col();
+ nPrevRow = p->aStart.Row();
+ nPrevColDelta = nColDelta;
+ nPrevRowDelta = nRowDelta;
+ nPrevColSize = nColSize;
+ nPrevRowSize = nRowSize;
+ }
+ if (!bValidRanges)
+ break;
+ rDoc.CopyToClip(aClipParam, pDocClip.get(), &rMark, false, bIncludeObjects );
+
+ ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack();
+ if ( pChangeTrack )
+ pChangeTrack->ResetLastCut(); // no more cut-mode
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ TransferableObjectDescriptor aObjDesc;
+ pDocSh->FillTransferableObjectDescriptor( aObjDesc );
+ aObjDesc.maDisplayName = pDocSh->GetMedium()->GetURLObject().GetURLNoPass();
+ // maSize is set in ScTransferObj ctor
+
+ rtl::Reference<ScTransferObj> pTransferObj(new ScTransferObj( std::move(pDocClip), std::move(aObjDesc) ));
+ if ( ScGlobal::xDrawClipDocShellRef.is() )
+ {
+ SfxObjectShellRef aPersistRef( ScGlobal::xDrawClipDocShellRef.get() );
+ pTransferObj->SetDrawPersist( aPersistRef ); // keep persist for ole objects alive
+ }
+ pTransferObj->CopyToClipboard( GetActiveWin() ); // system clipboard
+
+ bSuccess = true;
+ }
+ while (false);
+
+ if (!bSuccess && !bApi)
+ ErrorMessage(STR_NOMULTISELECT);
+
+ bDone = bSuccess;
+
+ return bDone;
+}
+
+rtl::Reference<ScTransferObj> ScViewFunc::CopyToTransferable()
+{
+ ScRange aRange;
+ auto eMarkType = GetViewData().GetSimpleArea( aRange );
+ if ( eMarkType == SC_MARK_SIMPLE || eMarkType == SC_MARK_SIMPLE_FILTERED )
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ if ( !rDoc.HasSelectedBlockMatrixFragment(
+ aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row(),
+ rMark ) )
+ {
+ ScDocumentUniquePtr pClipDoc(new ScDocument( SCDOCMODE_CLIP )); // create one (deleted by ScTransferObj)
+
+ bool bAnyOle = rDoc.HasOLEObjectsInArea( aRange, &rMark );
+ ScDrawLayer::SetGlobalDrawPersist( ScTransferObj::SetDrawClipDoc( bAnyOle ) );
+
+ ScClipParam aClipParam(aRange, false);
+ rDoc.CopyToClip(aClipParam, pClipDoc.get(), &rMark, false, true);
+
+ ScDrawLayer::SetGlobalDrawPersist(nullptr);
+ pClipDoc->ExtendMerge( aRange, true );
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ TransferableObjectDescriptor aObjDesc;
+ pDocSh->FillTransferableObjectDescriptor( aObjDesc );
+ aObjDesc.maDisplayName = pDocSh->GetMedium()->GetURLObject().GetURLNoPass();
+ return new ScTransferObj( std::move(pClipDoc), std::move(aObjDesc) );
+ }
+ }
+
+ return nullptr;
+}
+
+// P A S T E
+
+void ScViewFunc::PasteDraw()
+{
+ ScViewData& rViewData = GetViewData();
+ SCCOL nPosX = rViewData.GetCurX();
+ SCROW nPosY = rViewData.GetCurY();
+ vcl::Window* pWin = GetActiveWin();
+ Point aPos = pWin->PixelToLogic( rViewData.GetScrPos( nPosX, nPosY,
+ rViewData.GetActivePart() ) );
+ const ScDrawTransferObj* pDrawClip = ScDrawTransferObj::GetOwnClipboard(ScTabViewShell::GetClipData(rViewData.GetActiveWin()));
+ if (pDrawClip)
+ {
+ const OUString& aSrcShellID = pDrawClip->GetShellID();
+ OUString aDestShellID = SfxObjectShell::CreateShellID(rViewData.GetDocShell());
+ PasteDraw(aPos, pDrawClip->GetModel(), false, aSrcShellID, aDestShellID);
+ }
+}
+
+void ScViewFunc::PasteFromSystem()
+{
+ UpdateInputLine();
+
+ vcl::Window* pWin = GetActiveWin();
+ css::uno::Reference<css::datatransfer::XTransferable2> xTransferable2(ScTabViewShell::GetClipData(pWin));
+ const ScTransferObj* pOwnClip = ScTransferObj::GetOwnClipboard(xTransferable2);
+ // keep a reference in case the clipboard is changed during PasteFromClip
+ const ScDrawTransferObj* pDrawClip = ScDrawTransferObj::GetOwnClipboard(xTransferable2);
+ if (pOwnClip)
+ {
+ PasteFromClip( InsertDeleteFlags::ALL, pOwnClip->GetDocument(),
+ ScPasteFunc::NONE, false, false, false, INS_NONE, InsertDeleteFlags::NONE,
+ true ); // allow warning dialog
+ }
+ else if (pDrawClip)
+ PasteDraw();
+ else
+ {
+ TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( pWin ) );
+
+ {
+ SotClipboardFormatId nBiff8 = SotExchange::RegisterFormatName("Biff8");
+ SotClipboardFormatId nBiff5 = SotExchange::RegisterFormatName("Biff5");
+
+ SotClipboardFormatId nFormat; // output param for GetExchangeAction
+ sal_uInt8 nEventAction; // output param for GetExchangeAction
+
+ uno::Reference<css::datatransfer::XTransferable> xTransferable( aDataHelper.GetXTransferable() );
+ sal_uInt8 nAction = SotExchange::GetExchangeAction(
+ aDataHelper.GetDataFlavorExVector(),
+ SotExchangeDest::SCDOC_FREE_AREA,
+ EXCHG_IN_ACTION_COPY,
+ EXCHG_IN_ACTION_DEFAULT,
+ nFormat, nEventAction, SotClipboardFormatId::NONE,
+ &xTransferable );
+
+ if ( nAction != EXCHG_INOUT_ACTION_NONE )
+ {
+ switch( nAction )
+ {
+ case EXCHG_OUT_ACTION_INSERT_SVXB:
+ case EXCHG_OUT_ACTION_INSERT_GDIMETAFILE:
+ case EXCHG_OUT_ACTION_INSERT_BITMAP:
+ case EXCHG_OUT_ACTION_INSERT_GRAPH:
+ // SotClipboardFormatId::BITMAP
+ // SotClipboardFormatId::PNG
+ // SotClipboardFormatId::GDIMETAFILE
+ // SotClipboardFormatId::SVXB
+ PasteFromSystem(nFormat);
+ break;
+ default:
+ nAction = EXCHG_INOUT_ACTION_NONE;
+ }
+ }
+
+ if ( nAction == EXCHG_INOUT_ACTION_NONE )
+ {
+ // first SvDraw-model, then drawing
+ // (only one drawing is allowed)
+
+ if (aDataHelper.HasFormat( SotClipboardFormatId::DRAWING ))
+ {
+ // special case for tables from drawing
+ if( aDataHelper.HasFormat( SotClipboardFormatId::RTF ) )
+ {
+ PasteFromSystem( SotClipboardFormatId::RTF );
+ }
+ else if( aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT ) )
+ {
+ PasteFromSystem( SotClipboardFormatId::RICHTEXT );
+ }
+ else
+ {
+ PasteFromSystem( SotClipboardFormatId::DRAWING );
+ }
+ }
+ else if (aDataHelper.HasFormat( SotClipboardFormatId::EMBED_SOURCE ))
+ {
+ // If it's a Writer object, insert RTF instead of OLE
+
+ // Else, if the class id is all-zero, and SYLK is available,
+ // it probably is spreadsheet cells that have been put
+ // on the clipboard by OOo, so use the SYLK. (fdo#31077)
+
+ bool bDoRtf = false;
+ TransferableObjectDescriptor aObjDesc;
+ if( aDataHelper.GetTransferableObjectDescriptor( SotClipboardFormatId::OBJECTDESCRIPTOR, aObjDesc ) )
+ {
+ bDoRtf = ( ( aObjDesc.maClassName == SvGlobalName( SO3_SW_CLASSID ) ||
+ aObjDesc.maClassName == SvGlobalName( SO3_SWWEB_CLASSID ) )
+ && ( aDataHelper.HasFormat( SotClipboardFormatId::RTF ) || aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT ) ) );
+ }
+ if ( bDoRtf )
+ PasteFromSystem( aDataHelper.HasFormat( SotClipboardFormatId::RTF ) ? SotClipboardFormatId::RTF : SotClipboardFormatId::RICHTEXT );
+ else if ( aObjDesc.maClassName == SvGlobalName( 0,0,0,0,0,0,0,0,0,0,0 )
+ && aDataHelper.HasFormat( SotClipboardFormatId::SYLK ))
+ PasteFromSystem( SotClipboardFormatId::SYLK );
+ else
+ PasteFromSystem( SotClipboardFormatId::EMBED_SOURCE );
+ }
+ else if (aDataHelper.HasFormat( SotClipboardFormatId::LINK_SOURCE ))
+ PasteFromSystem( SotClipboardFormatId::LINK_SOURCE );
+ // the following format can not affect scenario from #89579#
+ else if (aDataHelper.HasFormat( SotClipboardFormatId::EMBEDDED_OBJ_OLE ))
+ PasteFromSystem( SotClipboardFormatId::EMBEDDED_OBJ_OLE );
+ // SotClipboardFormatId::PRIVATE no longer here (can't work if pOwnClip is NULL)
+ else if (aDataHelper.HasFormat(nBiff8)) // before xxx_OLE formats
+ PasteFromSystem(nBiff8);
+ else if (aDataHelper.HasFormat(nBiff5))
+ PasteFromSystem(nBiff5);
+ else if (aDataHelper.HasFormat(SotClipboardFormatId::RTF))
+ PasteFromSystem(SotClipboardFormatId::RTF);
+ else if (aDataHelper.HasFormat(SotClipboardFormatId::RICHTEXT))
+ PasteFromSystem(SotClipboardFormatId::RICHTEXT);
+ else if (aDataHelper.HasFormat(SotClipboardFormatId::HTML))
+ PasteFromSystem(SotClipboardFormatId::HTML);
+ else if (aDataHelper.HasFormat(SotClipboardFormatId::BITMAP))
+ PasteFromSystem(SotClipboardFormatId::BITMAP);
+ else if (aDataHelper.HasFormat(SotClipboardFormatId::HTML_SIMPLE))
+ PasteFromSystem(SotClipboardFormatId::HTML_SIMPLE);
+ else if (aDataHelper.HasFormat(SotClipboardFormatId::SYLK))
+ PasteFromSystem(SotClipboardFormatId::SYLK);
+ else if (aDataHelper.HasFormat(SotClipboardFormatId::STRING_TSVC))
+ PasteFromSystem(SotClipboardFormatId::STRING_TSVC);
+ else if (aDataHelper.HasFormat(SotClipboardFormatId::STRING))
+ PasteFromSystem(SotClipboardFormatId::STRING);
+ // xxx_OLE formats come last, like in SotExchange tables
+ else if (aDataHelper.HasFormat( SotClipboardFormatId::EMBED_SOURCE_OLE ))
+ PasteFromSystem( SotClipboardFormatId::EMBED_SOURCE_OLE );
+ else if (aDataHelper.HasFormat( SotClipboardFormatId::LINK_SOURCE_OLE ))
+ PasteFromSystem( SotClipboardFormatId::LINK_SOURCE_OLE );
+ }
+ }
+ }
+ // no exception-> SID_PASTE has FastCall-flag from idl
+ // will be called in case of empty clipboard (#42531#)
+}
+
+void ScViewFunc::PasteFromTransferable( const uno::Reference<datatransfer::XTransferable>& rxTransferable )
+{
+ if (auto pOwnClip = dynamic_cast<ScTransferObj*>(rxTransferable.get()))
+ {
+ PasteFromClip( InsertDeleteFlags::ALL, pOwnClip->GetDocument(),
+ ScPasteFunc::NONE, false, false, false, INS_NONE, InsertDeleteFlags::NONE,
+ true ); // allow warning dialog
+ }
+ else if (auto pDrawClip = dynamic_cast<ScDrawTransferObj*>(rxTransferable.get()))
+ {
+ ScViewData& rViewData = GetViewData();
+ SCCOL nPosX = rViewData.GetCurX();
+ SCROW nPosY = rViewData.GetCurY();
+ vcl::Window* pWin = GetActiveWin();
+ Point aPos = pWin->PixelToLogic( rViewData.GetScrPos( nPosX, nPosY, rViewData.GetActivePart() ) );
+ PasteDraw(
+ aPos, pDrawClip->GetModel(), false,
+ pDrawClip->GetShellID(), SfxObjectShell::CreateShellID(rViewData.GetDocShell()));
+ }
+ else
+ {
+ TransferableDataHelper aDataHelper( rxTransferable );
+ SotClipboardFormatId nBiff8 = SotExchange::RegisterFormatName("Biff8");
+ SotClipboardFormatId nBiff5 = SotExchange::RegisterFormatName("Biff5");
+ SotClipboardFormatId nFormatId = SotClipboardFormatId::NONE;
+ // first SvDraw-model, then drawing
+ // (only one drawing is allowed)
+
+ if (aDataHelper.HasFormat( SotClipboardFormatId::DRAWING ))
+ nFormatId = SotClipboardFormatId::DRAWING;
+ else if (aDataHelper.HasFormat( SotClipboardFormatId::SVXB ))
+ nFormatId = SotClipboardFormatId::SVXB;
+ else if (aDataHelper.HasFormat( SotClipboardFormatId::EMBED_SOURCE ))
+ {
+ // If it's a Writer object, insert RTF instead of OLE
+ bool bDoRtf = false;
+ TransferableObjectDescriptor aObjDesc;
+ if( aDataHelper.GetTransferableObjectDescriptor( SotClipboardFormatId::OBJECTDESCRIPTOR, aObjDesc ) )
+ {
+ bDoRtf = ( ( aObjDesc.maClassName == SvGlobalName( SO3_SW_CLASSID ) ||
+ aObjDesc.maClassName == SvGlobalName( SO3_SWWEB_CLASSID ) )
+ && ( aDataHelper.HasFormat( SotClipboardFormatId::RTF ) || aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT ) ));
+ }
+ if ( bDoRtf )
+ nFormatId = aDataHelper.HasFormat( SotClipboardFormatId::RTF ) ? SotClipboardFormatId::RTF : SotClipboardFormatId::RICHTEXT;
+ else
+ nFormatId = SotClipboardFormatId::EMBED_SOURCE;
+ }
+ else if (aDataHelper.HasFormat( SotClipboardFormatId::LINK_SOURCE ))
+ nFormatId = SotClipboardFormatId::LINK_SOURCE;
+ // the following format can not affect scenario from #89579#
+ else if (aDataHelper.HasFormat( SotClipboardFormatId::EMBEDDED_OBJ_OLE ))
+ nFormatId = SotClipboardFormatId::EMBEDDED_OBJ_OLE;
+ // SotClipboardFormatId::PRIVATE no longer here (can't work if pOwnClip is NULL)
+ else if (aDataHelper.HasFormat(nBiff8)) // before xxx_OLE formats
+ nFormatId = nBiff8;
+ else if (aDataHelper.HasFormat(nBiff5))
+ nFormatId = nBiff5;
+ else if (aDataHelper.HasFormat(SotClipboardFormatId::RTF))
+ nFormatId = SotClipboardFormatId::RTF;
+ else if (aDataHelper.HasFormat(SotClipboardFormatId::RICHTEXT))
+ nFormatId = SotClipboardFormatId::RICHTEXT;
+ else if (aDataHelper.HasFormat(SotClipboardFormatId::HTML))
+ nFormatId = SotClipboardFormatId::HTML;
+ else if (aDataHelper.HasFormat(SotClipboardFormatId::HTML_SIMPLE))
+ nFormatId = SotClipboardFormatId::HTML_SIMPLE;
+ else if (aDataHelper.HasFormat(SotClipboardFormatId::SYLK))
+ nFormatId = SotClipboardFormatId::SYLK;
+ else if (aDataHelper.HasFormat(SotClipboardFormatId::STRING_TSVC))
+ nFormatId = SotClipboardFormatId::STRING_TSVC;
+ else if (aDataHelper.HasFormat(SotClipboardFormatId::STRING))
+ nFormatId = SotClipboardFormatId::STRING;
+ else if (aDataHelper.HasFormat(SotClipboardFormatId::GDIMETAFILE))
+ nFormatId = SotClipboardFormatId::GDIMETAFILE;
+ else if (aDataHelper.HasFormat(SotClipboardFormatId::BITMAP))
+ nFormatId = SotClipboardFormatId::BITMAP;
+ // xxx_OLE formats come last, like in SotExchange tables
+ else if (aDataHelper.HasFormat( SotClipboardFormatId::EMBED_SOURCE_OLE ))
+ nFormatId = SotClipboardFormatId::EMBED_SOURCE_OLE;
+ else if (aDataHelper.HasFormat( SotClipboardFormatId::LINK_SOURCE_OLE ))
+ nFormatId = SotClipboardFormatId::LINK_SOURCE_OLE;
+ else
+ return;
+
+ PasteDataFormat( nFormatId, aDataHelper.GetTransferable(),
+ GetViewData().GetCurX(), GetViewData().GetCurY(), nullptr );
+ }
+}
+
+bool ScViewFunc::PasteFromSystem( SotClipboardFormatId nFormatId, bool bApi )
+{
+ UpdateInputLine();
+
+ bool bRet = true;
+ vcl::Window* pWin = GetActiveWin();
+ // keep a reference in case the clipboard is changed during PasteFromClip
+ const ScTransferObj* pOwnClip = ScTransferObj::GetOwnClipboard(ScTabViewShell::GetClipData(pWin));
+ if ( nFormatId == SotClipboardFormatId::NONE && pOwnClip )
+ {
+ PasteFromClip( InsertDeleteFlags::ALL, pOwnClip->GetDocument(),
+ ScPasteFunc::NONE, false, false, false, INS_NONE, InsertDeleteFlags::NONE,
+ !bApi ); // allow warning dialog
+ }
+ else
+ {
+ TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( pWin ) );
+ if ( !aDataHelper.GetTransferable().is() )
+ return false;
+
+ SCCOL nPosX = 0;
+ SCROW nPosY = 0;
+
+ ScViewData& rViewData = GetViewData();
+ ScRange aRange;
+ if ( rViewData.GetSimpleArea( aRange ) == SC_MARK_SIMPLE )
+ {
+ nPosX = aRange.aStart.Col();
+ nPosY = aRange.aStart.Row();
+ }
+ else
+ {
+ nPosX = rViewData.GetCurX();
+ nPosY = rViewData.GetCurY();
+ }
+
+ bRet = PasteDataFormat( nFormatId, aDataHelper.GetTransferable(),
+ nPosX, nPosY,
+ nullptr, false, !bApi ); // allow warning dialog
+
+ if ( !bRet && !bApi )
+ {
+ ErrorMessage(STR_PASTE_ERROR);
+ }
+ else if (comphelper::LibreOfficeKit::isActive())
+ {
+ SfxViewShell* pViewShell = rViewData.GetViewShell();
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(pViewShell, true /* bColumns */, true /* bRows */,
+ true /* bSizes */, false /* bHidden */, false /* bFiltered */, false /* bGroups */, rViewData.GetTabNo());
+ }
+ }
+ return bRet;
+}
+
+// P A S T E
+
+bool ScViewFunc::PasteOnDrawObjectLinked(
+ const uno::Reference<datatransfer::XTransferable>& rxTransferable,
+ SdrObject& rHitObj)
+{
+ TransferableDataHelper aDataHelper( rxTransferable );
+
+ if ( aDataHelper.HasFormat( SotClipboardFormatId::SVXB ) )
+ {
+ tools::SvRef<SotTempStream> xStm;
+ ScDrawView* pScDrawView = GetScDrawView();
+
+ if( pScDrawView && aDataHelper.GetSotStorageStream( SotClipboardFormatId::SVXB, xStm ) )
+ {
+ Graphic aGraphic;
+ TypeSerializer aSerializer(*xStm);
+ aSerializer.readGraphic(aGraphic);
+
+ const OUString aBeginUndo(ScResId(STR_UNDO_DRAGDROP));
+
+ if(pScDrawView->ApplyGraphicToObject( rHitObj, aGraphic, aBeginUndo, "" ))
+ {
+ return true;
+ }
+ }
+ }
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::GDIMETAFILE ) )
+ {
+ GDIMetaFile aMtf;
+ ScDrawView* pScDrawView = GetScDrawView();
+
+ if( pScDrawView && aDataHelper.GetGDIMetaFile( SotClipboardFormatId::GDIMETAFILE, aMtf ) )
+ {
+ const OUString aBeginUndo(ScResId(STR_UNDO_DRAGDROP));
+
+ if(pScDrawView->ApplyGraphicToObject( rHitObj, Graphic(aMtf), aBeginUndo, "" ))
+ {
+ return true;
+ }
+ }
+ }
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::BITMAP ) || aDataHelper.HasFormat( SotClipboardFormatId::PNG ) )
+ {
+ BitmapEx aBmpEx;
+ ScDrawView* pScDrawView = GetScDrawView();
+
+ if( pScDrawView && aDataHelper.GetBitmapEx( SotClipboardFormatId::BITMAP, aBmpEx ) )
+ {
+ const OUString aBeginUndo(ScResId(STR_UNDO_DRAGDROP));
+
+ if(pScDrawView->ApplyGraphicToObject( rHitObj, Graphic(aBmpEx), aBeginUndo, "" ))
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+static bool lcl_SelHasAttrib( const ScDocument& rDoc, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
+ const ScMarkData& rTabSelection, HasAttrFlags nMask )
+{
+ return std::any_of(rTabSelection.begin(), rTabSelection.end(),
+ [&](const SCTAB& rTab) { return rDoc.HasAttrib( nCol1, nRow1, rTab, nCol2, nRow2, rTab, nMask ); });
+}
+
+// paste into sheet:
+
+// internal paste
+
+namespace {
+
+bool checkDestRangeForOverwrite(const ScRangeList& rDestRanges, const ScDocument& rDoc, const ScMarkData& rMark, weld::Window* pParentWnd)
+{
+ bool bIsEmpty = true;
+ size_t nRangeSize = rDestRanges.size();
+ for (const auto& rTab : rMark)
+ {
+ for (size_t i = 0; i < nRangeSize && bIsEmpty; ++i)
+ {
+ const ScRange& rRange = rDestRanges[i];
+ bIsEmpty = rDoc.IsBlockEmpty(
+ rRange.aStart.Col(), rRange.aStart.Row(),
+ rRange.aEnd.Col(), rRange.aEnd.Row(), rTab );
+ }
+ if (!bIsEmpty)
+ break;
+ }
+
+ if (!bIsEmpty)
+ {
+ ScReplaceWarnBox aBox(pParentWnd);
+ if (aBox.run() != RET_YES)
+ {
+ // changing the configuration is within the ScReplaceWarnBox
+ return false;
+ }
+ }
+ return true;
+}
+
+}
+
+bool ScViewFunc::PasteFromClip( InsertDeleteFlags nFlags, ScDocument* pClipDoc,
+ ScPasteFunc nFunction, bool bSkipEmptyCells,
+ bool bTranspose, bool bAsLink,
+ InsCellCmd eMoveMode, InsertDeleteFlags nUndoExtraFlags,
+ bool bAllowDialogs )
+{
+ if (!pClipDoc)
+ {
+ OSL_FAIL("PasteFromClip: pClipDoc=0 not allowed");
+ return false;
+ }
+
+ if (GetViewData().SelectionForbidsPaste(pClipDoc))
+ return false;
+
+ // undo: save all or no content
+ InsertDeleteFlags nContFlags = InsertDeleteFlags::NONE;
+ if (nFlags & InsertDeleteFlags::CONTENTS)
+ nContFlags |= InsertDeleteFlags::CONTENTS;
+ if (nFlags & InsertDeleteFlags::ATTRIB)
+ nContFlags |= InsertDeleteFlags::ATTRIB;
+ // move attributes to undo without copying them from clip to doc
+ InsertDeleteFlags nUndoFlags = nContFlags;
+ if (nUndoExtraFlags & InsertDeleteFlags::ATTRIB)
+ nUndoFlags |= InsertDeleteFlags::ATTRIB;
+ // do not copy note captions into undo document
+ nUndoFlags |= InsertDeleteFlags::NOCAPTIONS;
+
+ ScClipParam& rClipParam = pClipDoc->GetClipParam();
+ if (rClipParam.isMultiRange())
+ {
+ // Source data is multi-range.
+ return PasteMultiRangesFromClip(nFlags, pClipDoc, nFunction, bSkipEmptyCells, bTranspose,
+ bAsLink, bAllowDialogs, eMoveMode, nUndoFlags);
+ }
+
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ if (rMark.IsMultiMarked())
+ {
+ // Source data is single-range but destination is multi-range.
+ return PasteFromClipToMultiRanges(
+ nFlags, pClipDoc, nFunction, bSkipEmptyCells, bTranspose, bAsLink, bAllowDialogs,
+ eMoveMode, nUndoFlags);
+ }
+
+ bool bCutMode = pClipDoc->IsCutMode(); // if transposing, take from original clipdoc
+ bool bIncludeFiltered = bCutMode;
+
+ // paste drawing: also if InsertDeleteFlags::NOTE is set (to create drawing layer for note captions)
+ bool bPasteDraw = ( pClipDoc->GetDrawLayer() && ( nFlags & (InsertDeleteFlags::OBJECTS|InsertDeleteFlags::NOTE) ) );
+
+ ScDocShellRef aTransShellRef; // for objects in xTransClip - must remain valid as long as xTransClip
+ ScDocument* pOrigClipDoc = nullptr;
+ ScDocumentUniquePtr xTransClip;
+ if ( bTranspose )
+ {
+ SCCOL nX;
+ SCROW nY;
+ // include filtered rows until TransposeClip can skip them
+ pClipDoc->GetClipArea( nX, nY, true );
+ if ( nY > static_cast<sal_Int32>(pClipDoc->MaxCol()) ) // too many lines for transpose
+ {
+ ErrorMessage(STR_PASTE_FULL);
+ return false;
+ }
+ pOrigClipDoc = pClipDoc; // refs
+
+ if ( bPasteDraw )
+ {
+ aTransShellRef = new ScDocShell; // DocShell needs a Ref immediately
+ aTransShellRef->DoInitNew();
+ }
+ ScDrawLayer::SetGlobalDrawPersist( aTransShellRef.get() );
+
+ xTransClip.reset( new ScDocument( SCDOCMODE_CLIP ));
+ pClipDoc->TransposeClip(xTransClip.get(), nFlags, bAsLink, bIncludeFiltered);
+ pClipDoc = xTransClip.get();
+
+ ScDrawLayer::SetGlobalDrawPersist(nullptr);
+ }
+
+ // TODO: position this call better for performance.
+ ResetAutoSpellForContentChange();
+
+ SCCOL nStartCol;
+ SCROW nStartRow;
+ SCTAB nStartTab;
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ SCTAB nEndTab;
+ SCCOL nClipSizeX;
+ SCROW nClipSizeY;
+ pClipDoc->GetClipArea( nClipSizeX, nClipSizeY, true ); // size in clipboard doc
+
+ // size in target doc: include filtered rows only if CutMode is set
+ SCCOL nDestSizeX;
+ SCROW nDestSizeY;
+ pClipDoc->GetClipArea( nDestSizeX, nDestSizeY, bIncludeFiltered );
+
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ SfxUndoManager* pUndoMgr = pDocSh->GetUndoManager();
+ const bool bRecord(rDoc.IsUndoEnabled());
+
+ ScDocShellModificator aModificator( *pDocSh );
+
+ ScRange aMarkRange;
+ ScMarkData aFilteredMark( rMark); // local copy for all modifications
+ ScMarkType eMarkType = GetViewData().GetSimpleArea( aMarkRange, aFilteredMark);
+ bool bMarkIsFiltered = (eMarkType == SC_MARK_SIMPLE_FILTERED);
+ bool bNoPaste = ((eMarkType != SC_MARK_SIMPLE && !bMarkIsFiltered) ||
+ (bMarkIsFiltered && (eMoveMode != INS_NONE || bAsLink)));
+
+ if (!bNoPaste)
+ {
+ if (!rMark.IsMarked())
+ {
+ // Create a selection with clipboard row count and check that for
+ // filtered.
+ nStartCol = GetViewData().GetCurX();
+ nStartRow = GetViewData().GetCurY();
+ nStartTab = GetViewData().GetTabNo();
+ nEndCol = nStartCol + nDestSizeX;
+ nEndRow = nStartRow + nDestSizeY;
+ nEndTab = nStartTab;
+ aMarkRange = ScRange( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab);
+ if (ScViewUtil::HasFiltered(aMarkRange, rDoc))
+ {
+ bMarkIsFiltered = true;
+ // Fit to clipboard's row count unfiltered rows. If there is no
+ // fit assume that pasting is not possible. Note that nDestSizeY is
+ // size-1 (difference).
+ if (!ScViewUtil::FitToUnfilteredRows(aMarkRange, rDoc, nDestSizeY+1))
+ bNoPaste = true;
+ }
+ aFilteredMark.SetMarkArea( aMarkRange);
+ }
+ else
+ {
+ // Expand the marked area when the destination area is larger than the
+ // current selection, to get the undo do the right thing. (i#106711)
+ ScRange aRange = aFilteredMark.GetMarkArea();
+ if( (aRange.aEnd.Col() - aRange.aStart.Col()) < nDestSizeX )
+ {
+ aRange.aEnd.SetCol(aRange.aStart.Col() + nDestSizeX);
+ aFilteredMark.SetMarkArea(aRange);
+ }
+ }
+ }
+
+ if (bNoPaste)
+ {
+ ErrorMessage(STR_MSSG_PASTEFROMCLIP_0);
+ return false;
+ }
+
+ SCROW nUnfilteredRows = aMarkRange.aEnd.Row() - aMarkRange.aStart.Row() + 1;
+ ScRangeList aRangeList;
+ if (bMarkIsFiltered)
+ {
+ ScViewUtil::UnmarkFiltered(aFilteredMark, rDoc);
+ aFilteredMark.FillRangeListWithMarks( &aRangeList, false);
+ nUnfilteredRows = 0;
+ size_t ListSize = aRangeList.size();
+ for ( size_t i = 0; i < ListSize; ++i )
+ {
+ ScRange & r = aRangeList[i];
+ nUnfilteredRows += r.aEnd.Row() - r.aStart.Row() + 1;
+ }
+#if 0
+ /* This isn't needed but could be a desired restriction. */
+ // For filtered, destination rows have to be an exact multiple of
+ // source rows. Note that nDestSizeY is size-1 (difference), so
+ // nDestSizeY==0 fits always.
+ if ((nUnfilteredRows % (nDestSizeY+1)) != 0)
+ {
+ /* FIXME: this should be a more descriptive error message then. */
+ ErrorMessage(STR_MSSG_PASTEFROMCLIP_0);
+ return false;
+ }
+#endif
+ }
+
+ // Also for a filtered selection the area is used, for undo et al.
+ if ( aFilteredMark.IsMarked() || bMarkIsFiltered )
+ {
+ aMarkRange.GetVars( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab);
+ SCCOL nBlockAddX = nEndCol-nStartCol;
+ SCROW nBlockAddY = nEndRow-nStartRow;
+
+ // request, if the selection is greater than one row/column, but smaller
+ // as the Clipboard (then inserting is done beyond the selection)
+
+ // ClipSize is not size, but difference
+ if ( ( nBlockAddX != 0 && nBlockAddX < nDestSizeX ) ||
+ ( nBlockAddY != 0 && nBlockAddY < nDestSizeY ) ||
+ ( bMarkIsFiltered && nUnfilteredRows < nDestSizeY+1 ) )
+ {
+ ScWaitCursorOff aWaitOff( GetFrameWin() );
+ OUString aMessage = ScResId( STR_PASTE_BIGGER );
+
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(GetViewData().GetDialogParent(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ aMessage));
+ xQueryBox->set_default_response(RET_NO);
+ if (xQueryBox->run() != RET_YES)
+ {
+ return false;
+ }
+ }
+
+ if (nBlockAddX <= nDestSizeX)
+ nEndCol = nStartCol + nDestSizeX;
+
+ if (nBlockAddY <= nDestSizeY)
+ {
+ nEndRow = nStartRow + nDestSizeY;
+ if (bMarkIsFiltered || nEndRow > aMarkRange.aEnd.Row())
+ {
+ // Same as above if nothing was marked: re-fit selection to
+ // unfiltered rows. Extending the selection actually may
+ // introduce filtered rows where there weren't any before, so
+ // we also need to test for that.
+ aMarkRange = ScRange( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab);
+ if (bMarkIsFiltered || ScViewUtil::HasFiltered(aMarkRange, rDoc))
+ {
+ bMarkIsFiltered = true;
+ // Worst case: all rows up to the end of the sheet are filtered.
+ if (!ScViewUtil::FitToUnfilteredRows(aMarkRange, rDoc, nDestSizeY+1))
+ {
+ ErrorMessage(STR_PASTE_FULL);
+ return false;
+ }
+ }
+ aMarkRange.GetVars( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab);
+ aFilteredMark.SetMarkArea( aMarkRange);
+ if (bMarkIsFiltered)
+ {
+ ScViewUtil::UnmarkFiltered(aFilteredMark, rDoc);
+ aFilteredMark.FillRangeListWithMarks( &aRangeList, true);
+ }
+ }
+ }
+ }
+ else
+ {
+ nStartCol = GetViewData().GetCurX();
+ nStartRow = GetViewData().GetCurY();
+ nStartTab = GetViewData().GetTabNo();
+ nEndCol = nStartCol + nDestSizeX;
+ nEndRow = nStartRow + nDestSizeY;
+ nEndTab = nStartTab;
+ }
+
+ bool bOffLimits = !rDoc.ValidCol(nEndCol) || !rDoc.ValidRow(nEndRow);
+
+ // target-range, as displayed:
+ ScRange aUserRange( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab );
+
+ // should lines be inserted?
+ // ( too large nEndCol/nEndRow are detected below)
+ bool bInsertCells = ( eMoveMode != INS_NONE && !bOffLimits );
+ if ( bInsertCells )
+ {
+ // Instead of EnterListAction, the paste undo action is merged into the
+ // insert action, so Repeat can insert the right cells
+
+ MarkRange( aUserRange ); // set through CopyFromClip
+
+ // CutMode is reset on insertion of cols/rows but needed again on cell move
+ bool bCut = pClipDoc->IsCutMode();
+ if (!InsertCells( eMoveMode, bRecord, true )) // is inserting possible?
+ {
+ return false;
+ // #i21036# EnterListAction isn't used, and InsertCells doesn't insert
+ // its undo action on failure, so no undo handling is needed here
+ }
+ if ( bCut )
+ pClipDoc->SetCutMode( bCut );
+ }
+ else if (!bOffLimits)
+ {
+ bool bAskIfNotEmpty = bAllowDialogs &&
+ ( nFlags & InsertDeleteFlags::CONTENTS ) &&
+ nFunction == ScPasteFunc::NONE &&
+ SC_MOD()->GetInputOptions().GetReplaceCellsWarn();
+ if ( bAskIfNotEmpty )
+ {
+ ScRangeList aTestRanges(aUserRange);
+ if (!checkDestRangeForOverwrite(aTestRanges, rDoc, aFilteredMark, GetViewData().GetDialogParent()))
+ return false;
+ }
+ }
+
+ SCCOL nClipStartX; // enlarge clipboard-range
+ SCROW nClipStartY;
+ pClipDoc->GetClipStart( nClipStartX, nClipStartY );
+ SCCOL nUndoEndCol = nClipStartX + nClipSizeX;
+ SCROW nUndoEndRow = nClipStartY + nClipSizeY; // end of source area in clipboard document
+ bool bClipOver = false;
+ // #i68690# ExtendMerge for the clip doc must be called with the clipboard's sheet numbers.
+ // The same end column/row can be used for all calls because the clip doc doesn't contain
+ // content outside the clip area.
+ for (SCTAB nClipTab=0; nClipTab < pClipDoc->GetTableCount(); nClipTab++)
+ if ( pClipDoc->HasTable(nClipTab) )
+ if ( pClipDoc->ExtendMerge( nClipStartX,nClipStartY, nUndoEndCol,nUndoEndRow, nClipTab ) )
+ bClipOver = true;
+ nUndoEndCol -= nClipStartX + nClipSizeX;
+ nUndoEndRow -= nClipStartY + nClipSizeY; // now contains only the difference added by ExtendMerge
+ nUndoEndCol = sal::static_int_cast<SCCOL>( nUndoEndCol + nEndCol );
+ nUndoEndRow = sal::static_int_cast<SCROW>( nUndoEndRow + nEndRow ); // destination area, expanded for merged cells
+
+ if (nUndoEndCol>pClipDoc->MaxCol() || nUndoEndRow>pClipDoc->MaxRow())
+ {
+ ErrorMessage(STR_PASTE_FULL);
+ return false;
+ }
+
+ rDoc.ExtendMergeSel( nStartCol,nStartRow, nUndoEndCol,nUndoEndRow, aFilteredMark );
+
+ // check cell-protection
+
+ ScEditableTester aTester( rDoc, nStartTab, nStartCol,nStartRow, nUndoEndCol,nUndoEndRow );
+ if (!aTester.IsEditable())
+ {
+ ErrorMessage(aTester.GetMessageId());
+ return false;
+ }
+
+ //! check overlapping
+ //! just check truly intersection !!!!!!!
+
+ ScDocFunc& rDocFunc = pDocSh->GetDocFunc();
+ if ( bRecord )
+ {
+ OUString aUndo = ScResId( pClipDoc->IsCutMode() ? STR_UNDO_MOVE : STR_UNDO_COPY );
+ pUndoMgr->EnterListAction( aUndo, aUndo, 0, GetViewData().GetViewShell()->GetViewShellId() );
+ }
+
+ if (bClipOver)
+ if (lcl_SelHasAttrib( rDoc, nStartCol,nStartRow, nUndoEndCol,nUndoEndRow, aFilteredMark, HasAttrFlags::Overlapped ))
+ { // "Cell merge not possible if cells already merged"
+ ScDocAttrIterator aIter( rDoc, nStartTab, nStartCol, nStartRow, nUndoEndCol, nUndoEndRow );
+ const ScPatternAttr* pPattern = nullptr;
+ SCCOL nCol = -1;
+ SCROW nRow1 = -1;
+ SCROW nRow2 = -1;
+ while ( ( pPattern = aIter.GetNext( nCol, nRow1, nRow2 ) ) != nullptr )
+ {
+ const ScMergeAttr& rMergeFlag = pPattern->GetItem(ATTR_MERGE);
+ const ScMergeFlagAttr& rMergeFlagAttr = pPattern->GetItem(ATTR_MERGE_FLAG);
+ if (rMergeFlag.IsMerged() || rMergeFlagAttr.IsOverlapped())
+ {
+ ScRange aRange(nCol, nRow1, nStartTab);
+ rDoc.ExtendOverlapped(aRange);
+ rDoc.ExtendMerge(aRange, true);
+ rDocFunc.UnmergeCells(aRange, bRecord, nullptr /*TODO: should pass combined UndoDoc if bRecord*/);
+ }
+ }
+ }
+
+ if ( !bCutMode )
+ {
+ ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack();
+ if ( pChangeTrack )
+ pChangeTrack->ResetLastCut(); // no more cut-mode
+ }
+
+ bool bColInfo = ( nStartRow==0 && nEndRow==rDoc.MaxRow() );
+ bool bRowInfo = ( nStartCol==0 && nEndCol==rDoc.MaxCol() );
+
+ ScDocumentUniquePtr pUndoDoc;
+ std::unique_ptr<ScDocument> pRefUndoDoc;
+ std::unique_ptr<ScRefUndoData> pUndoData;
+
+ if ( bRecord )
+ {
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndoSelected( rDoc, aFilteredMark, bColInfo, bRowInfo );
+
+ // all sheets - CopyToDocument skips those that don't exist in pUndoDoc
+ SCTAB nTabCount = rDoc.GetTableCount();
+ rDoc.CopyToDocument( nStartCol, nStartRow, 0, nUndoEndCol, nUndoEndRow, nTabCount-1,
+ nUndoFlags, false, *pUndoDoc );
+
+ if ( bCutMode )
+ {
+ pRefUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pRefUndoDoc->InitUndo( rDoc, 0, nTabCount-1 );
+
+ pUndoData.reset(new ScRefUndoData( &rDoc ));
+ }
+ }
+
+ sal_uInt16 nExtFlags = 0;
+ pDocSh->UpdatePaintExt( nExtFlags, nStartCol, nStartRow, nStartTab,
+ nEndCol, nEndRow, nEndTab ); // content before the change
+
+ if (GetViewData().IsActive())
+ {
+ DoneBlockMode();
+ InitOwnBlockMode( aUserRange );
+ }
+ rMark.SetMarkArea( aUserRange );
+ MarkDataChanged();
+
+ // copy from clipboard
+ // save original data in case of calculation
+
+ ScDocumentUniquePtr pMixDoc;
+ if (nFunction != ScPasteFunc::NONE)
+ {
+ bSkipEmptyCells = false;
+ if ( nFlags & InsertDeleteFlags::CONTENTS )
+ {
+ pMixDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pMixDoc->InitUndo( rDoc, nStartTab, nEndTab );
+ rDoc.CopyToDocument(nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab,
+ InsertDeleteFlags::CONTENTS, false, *pMixDoc);
+ }
+ }
+
+ /* Make draw layer and start drawing undo.
+ - Needed before AdjustBlockHeight to track moved drawing objects.
+ - Needed before rDoc.CopyFromClip to track inserted note caption objects.
+ */
+ if ( bPasteDraw )
+ pDocSh->MakeDrawLayer();
+ if ( bRecord )
+ rDoc.BeginDrawUndo();
+
+ InsertDeleteFlags nNoObjFlags = nFlags & ~InsertDeleteFlags::OBJECTS;
+ if (!bAsLink)
+ {
+ // copy normally (original range)
+ rDoc.CopyFromClip( aUserRange, aFilteredMark, nNoObjFlags,
+ pRefUndoDoc.get(), pClipDoc, true, false, bIncludeFiltered,
+ bSkipEmptyCells, (bMarkIsFiltered ? &aRangeList : nullptr) );
+
+ // adapt refs manually in case of transpose
+ if ( bTranspose && bCutMode && (nFlags & InsertDeleteFlags::CONTENTS) )
+ rDoc.UpdateTranspose( aUserRange.aStart, pOrigClipDoc, aFilteredMark, pRefUndoDoc.get() );
+ }
+ else if (!bTranspose)
+ {
+ // copy with bAsLink=TRUE
+ rDoc.CopyFromClip( aUserRange, aFilteredMark, nNoObjFlags, pRefUndoDoc.get(), pClipDoc,
+ true, true, bIncludeFiltered, bSkipEmptyCells );
+ }
+ else
+ {
+ // copy all content (TransClipDoc contains only formula)
+ rDoc.CopyFromClip( aUserRange, aFilteredMark, nContFlags, pRefUndoDoc.get(), pClipDoc );
+ }
+
+ // skipped rows and merged cells don't mix
+ if ( !bIncludeFiltered && pClipDoc->HasClipFilteredRows() )
+ rDocFunc.UnmergeCells( aUserRange, false, nullptr );
+
+ rDoc.ExtendMergeSel( nStartCol, nStartRow, nEndCol, nEndRow, aFilteredMark, true ); // refresh
+ // new range
+
+ if ( pMixDoc ) // calculate with original data?
+ {
+ rDoc.MixDocument( aUserRange, nFunction, bSkipEmptyCells, *pMixDoc );
+ }
+ pMixDoc.reset();
+
+ AdjustBlockHeight(); // update row heights before pasting objects
+
+ ::std::vector< OUString > aExcludedChartNames;
+ SdrPage* pPage = nullptr;
+
+ if ( nFlags & InsertDeleteFlags::OBJECTS )
+ {
+ ScDrawView* pScDrawView = GetScDrawView();
+ SdrModel* pModel = ( pScDrawView ? &pScDrawView->GetModel() : nullptr );
+ pPage = ( pModel ? pModel->GetPage( static_cast< sal_uInt16 >( nStartTab ) ) : nullptr );
+ if ( pPage )
+ {
+ ScChartHelper::GetChartNames( aExcludedChartNames, pPage );
+ }
+
+ // Paste the drawing objects after the row heights have been updated.
+
+ rDoc.CopyFromClip( aUserRange, aFilteredMark, InsertDeleteFlags::OBJECTS, pRefUndoDoc.get(), pClipDoc,
+ true, false, bIncludeFiltered );
+ }
+
+ pDocSh->UpdatePaintExt( nExtFlags, nStartCol, nStartRow, nStartTab,
+ nEndCol, nEndRow, nEndTab ); // content after the change
+
+ // if necessary, delete autofilter-heads
+ if (bCutMode)
+ if (rDoc.RefreshAutoFilter( nClipStartX,nClipStartY, nClipStartX+nClipSizeX,
+ nClipStartY+nClipSizeY, nStartTab ))
+ {
+ pDocSh->PostPaint(
+ ScRange(nClipStartX, nClipStartY, nStartTab, nClipStartX+nClipSizeX, nClipStartY, nStartTab),
+ PaintPartFlags::Grid );
+ }
+
+ //! remove block-range on RefUndoDoc !!!
+
+ if ( bRecord )
+ {
+ ScDocumentUniquePtr pRedoDoc;
+ // copy redo data after appearance of the first undo
+ // don't create Redo-Doc without RefUndoDoc
+
+ if (pRefUndoDoc)
+ {
+ pRedoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pRedoDoc->InitUndo( rDoc, nStartTab, nEndTab, bColInfo, bRowInfo );
+
+ // move adapted refs to Redo-Doc
+
+ SCTAB nTabCount = rDoc.GetTableCount();
+ pRedoDoc->AddUndoTab( 0, nTabCount-1 );
+ rDoc.CopyUpdated( pRefUndoDoc.get(), pRedoDoc.get() );
+
+ // move old refs to Undo-Doc
+
+ // not charts?
+ pUndoDoc->AddUndoTab( 0, nTabCount-1 );
+ pRefUndoDoc->DeleteArea( nStartCol, nStartRow, nEndCol, nEndRow, aFilteredMark, InsertDeleteFlags::ALL );
+ pRefUndoDoc->CopyToDocument( 0,0,0, pUndoDoc->MaxCol(), pUndoDoc->MaxRow(), nTabCount-1,
+ InsertDeleteFlags::FORMULA, false, *pUndoDoc );
+ pRefUndoDoc.reset();
+ }
+
+ // DeleteUnchanged for pUndoData is in ScUndoPaste ctor,
+ // UndoData for redo is made during first undo
+
+ ScUndoPasteOptions aOptions; // store options for repeat
+ aOptions.nFunction = nFunction;
+ aOptions.bSkipEmptyCells = bSkipEmptyCells;
+ aOptions.bTranspose = bTranspose;
+ aOptions.bAsLink = bAsLink;
+ aOptions.eMoveMode = eMoveMode;
+
+ std::unique_ptr<SfxUndoAction> pUndo(new ScUndoPaste(
+ pDocSh, ScRange(nStartCol, nStartRow, nStartTab, nUndoEndCol, nUndoEndRow, nEndTab),
+ aFilteredMark, std::move(pUndoDoc), std::move(pRedoDoc), nFlags | nUndoFlags, std::move(pUndoData),
+ false, &aOptions )); // false = Redo data not yet copied
+
+ if ( bInsertCells )
+ {
+ // Merge the paste undo action into the insert action.
+ // Use ScUndoWrapper so the ScUndoPaste pointer can be stored in the insert action.
+
+ pUndoMgr->AddUndoAction( std::make_unique<ScUndoWrapper>( std::move(pUndo) ), true );
+ }
+ else
+ pUndoMgr->AddUndoAction( std::move(pUndo) );
+ pUndoMgr->LeaveListAction();
+ }
+
+ PaintPartFlags nPaint = PaintPartFlags::Grid;
+ if (bColInfo)
+ {
+ nPaint |= PaintPartFlags::Top;
+ nUndoEndCol = rDoc.MaxCol(); // just for drawing !
+ }
+ if (bRowInfo)
+ {
+ nPaint |= PaintPartFlags::Left;
+ nUndoEndRow = rDoc.MaxRow(); // just for drawing !
+ }
+ pDocSh->PostPaint(
+ ScRange(nStartCol, nStartRow, nStartTab, nUndoEndCol, nUndoEndRow, nEndTab),
+ nPaint, nExtFlags);
+ // AdjustBlockHeight has already been called above
+
+ aModificator.SetDocumentModified();
+ PostPasteFromClip(aUserRange, rMark);
+
+ if ( nFlags & InsertDeleteFlags::OBJECTS )
+ {
+ ScModelObj* pModelObj = pDocSh->GetModel();
+ if ( pPage && pModelObj )
+ {
+ bool bSameDoc = ( rClipParam.getSourceDocID() == rDoc.GetDocumentID() );
+ const ScRangeListVector& rProtectedChartRangesVector( rClipParam.maProtectedChartRangesVector );
+ ScChartHelper::CreateProtectedChartListenersAndNotify( rDoc, pPage, pModelObj, nStartTab,
+ rProtectedChartRangesVector, aExcludedChartNames, bSameDoc );
+ }
+ }
+ OUString aStartAddress = aMarkRange.aStart.GetColRowString();
+ OUString aEndAddress = aMarkRange.aEnd.GetColRowString();
+ collectUIInformation({{"RANGE", aStartAddress + ":" + aEndAddress}}, "PASTE");
+ return true;
+}
+
+bool ScViewFunc::PasteMultiRangesFromClip(InsertDeleteFlags nFlags, ScDocument* pClipDoc,
+ ScPasteFunc nFunction, bool bSkipEmptyCells,
+ bool bTranspose, bool bAsLink,
+ bool bAllowDialogs, InsCellCmd eMoveMode,
+ InsertDeleteFlags nUndoFlags)
+{
+ ScViewData& rViewData = GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+ ScMarkData aMark(rViewData.GetMarkData());
+ const ScAddress& rCurPos = rViewData.GetCurPos();
+ ScClipParam& rClipParam = pClipDoc->GetClipParam();
+ SCCOL nColSize = rClipParam.getPasteColSize();
+ SCROW nRowSize = rClipParam.getPasteRowSize(*pClipDoc, /*bIncludeFiltered*/false);
+
+ if (bTranspose)
+ {
+ if (static_cast<SCROW>(rCurPos.Col()) + nRowSize-1 > static_cast<SCROW>(pClipDoc->MaxCol()))
+ {
+ ErrorMessage(STR_PASTE_FULL);
+ return false;
+ }
+
+ ScDocumentUniquePtr pTransClip(new ScDocument(SCDOCMODE_CLIP));
+ pClipDoc->TransposeClip(pTransClip.get(), nFlags, bAsLink, /*bIncludeFiltered*/false);
+ pClipDoc = pTransClip.release();
+ SCCOL nTempColSize = nColSize;
+ nColSize = static_cast<SCCOL>(nRowSize);
+ nRowSize = static_cast<SCROW>(nTempColSize);
+ }
+
+ if (!rDoc.ValidCol(rCurPos.Col()+nColSize-1) || !rDoc.ValidRow(rCurPos.Row()+nRowSize-1))
+ {
+ ErrorMessage(STR_PASTE_FULL);
+ return false;
+ }
+
+ // Determine the first and last selected sheet numbers.
+ SCTAB nTab1 = aMark.GetFirstSelected();
+ SCTAB nTab2 = aMark.GetLastSelected();
+
+ ScDocShellModificator aModificator(*pDocSh);
+
+ // For multi-selection paste, we don't support cell duplication for larger
+ // destination range. In case the destination is marked, we reset it to
+ // the clip size.
+ ScRange aMarkedRange(rCurPos.Col(), rCurPos.Row(), nTab1,
+ rCurPos.Col()+nColSize-1, rCurPos.Row()+nRowSize-1, nTab2);
+
+ // Extend the marked range to account for filtered rows in the destination
+ // area.
+ if (ScViewUtil::HasFiltered(aMarkedRange, rDoc))
+ {
+ if (!ScViewUtil::FitToUnfilteredRows(aMarkedRange, rDoc, nRowSize))
+ return false;
+ }
+
+ bool bAskIfNotEmpty =
+ bAllowDialogs && (nFlags & InsertDeleteFlags::CONTENTS) &&
+ nFunction == ScPasteFunc::NONE && SC_MOD()->GetInputOptions().GetReplaceCellsWarn();
+
+ if (bAskIfNotEmpty)
+ {
+ ScRangeList aTestRanges(aMarkedRange);
+ if (!checkDestRangeForOverwrite(aTestRanges, rDoc, aMark, GetViewData().GetDialogParent()))
+ return false;
+ }
+
+ aMark.SetMarkArea(aMarkedRange);
+ MarkRange(aMarkedRange);
+
+ bool bInsertCells = (eMoveMode != INS_NONE);
+ if (bInsertCells)
+ {
+ if (!InsertCells(eMoveMode, rDoc.IsUndoEnabled(), true))
+ return false;
+ }
+
+ // TODO: position this call better for performance.
+ ResetAutoSpellForContentChange();
+
+ bool bRowInfo = ( aMarkedRange.aStart.Col()==0 && aMarkedRange.aEnd.Col()==pClipDoc->MaxCol() );
+ ScDocumentUniquePtr pUndoDoc;
+ if (rDoc.IsUndoEnabled())
+ {
+ pUndoDoc.reset(new ScDocument(SCDOCMODE_UNDO));
+ pUndoDoc->InitUndoSelected(rDoc, aMark, false, bRowInfo);
+ rDoc.CopyToDocument(aMarkedRange, nUndoFlags, false, *pUndoDoc, &aMark);
+ }
+
+ ScDocumentUniquePtr pMixDoc;
+ if ( bSkipEmptyCells || nFunction != ScPasteFunc::NONE)
+ {
+ if ( nFlags & InsertDeleteFlags::CONTENTS )
+ {
+ pMixDoc.reset(new ScDocument(SCDOCMODE_UNDO));
+ pMixDoc->InitUndoSelected(rDoc, aMark);
+ rDoc.CopyToDocument(aMarkedRange, InsertDeleteFlags::CONTENTS, false, *pMixDoc, &aMark);
+ }
+ }
+
+ /* Make draw layer and start drawing undo.
+ - Needed before AdjustBlockHeight to track moved drawing objects.
+ - Needed before rDoc.CopyFromClip to track inserted note caption objects.
+ */
+ if (nFlags & InsertDeleteFlags::OBJECTS)
+ pDocSh->MakeDrawLayer();
+ if (rDoc.IsUndoEnabled())
+ rDoc.BeginDrawUndo();
+
+ InsertDeleteFlags nCopyFlags = nFlags & ~InsertDeleteFlags::OBJECTS;
+ // in case of transpose, links were added in TransposeClip()
+ if (bAsLink && bTranspose)
+ nCopyFlags |= InsertDeleteFlags::FORMULA;
+ rDoc.CopyMultiRangeFromClip(rCurPos, aMark, nCopyFlags, pClipDoc, true, bAsLink && !bTranspose,
+ /*bIncludeFiltered*/false, bSkipEmptyCells);
+
+ if (pMixDoc)
+ rDoc.MixDocument(aMarkedRange, nFunction, bSkipEmptyCells, *pMixDoc);
+
+ AdjustBlockHeight(); // update row heights before pasting objects
+
+ if (nFlags & InsertDeleteFlags::OBJECTS)
+ {
+ // Paste the drawing objects after the row heights have been updated.
+ rDoc.CopyMultiRangeFromClip(rCurPos, aMark, InsertDeleteFlags::OBJECTS, pClipDoc, true,
+ false, /*bIncludeFiltered*/false, true);
+ }
+
+ if (bRowInfo)
+ pDocSh->PostPaint(aMarkedRange.aStart.Col(), aMarkedRange.aStart.Row(), nTab1, pClipDoc->MaxCol(), pClipDoc->MaxRow(), nTab1, PaintPartFlags::Grid|PaintPartFlags::Left);
+ else
+ {
+ ScRange aTmp = aMarkedRange;
+ aTmp.aStart.SetTab(nTab1);
+ aTmp.aEnd.SetTab(nTab1);
+ pDocSh->PostPaint(aTmp, PaintPartFlags::Grid);
+ }
+
+ if (rDoc.IsUndoEnabled())
+ {
+ SfxUndoManager* pUndoMgr = pDocSh->GetUndoManager();
+ OUString aUndo = ScResId(
+ pClipDoc->IsCutMode() ? STR_UNDO_CUT : STR_UNDO_COPY);
+ pUndoMgr->EnterListAction(aUndo, aUndo, 0, GetViewData().GetViewShell()->GetViewShellId());
+
+ ScUndoPasteOptions aOptions; // store options for repeat
+ aOptions.nFunction = nFunction;
+ aOptions.bSkipEmptyCells = bSkipEmptyCells;
+ aOptions.bTranspose = bTranspose;
+ aOptions.bAsLink = bAsLink;
+ aOptions.eMoveMode = eMoveMode;
+
+ std::unique_ptr<ScUndoPaste> pUndo(new ScUndoPaste(pDocSh,
+ aMarkedRange, aMark, std::move(pUndoDoc), nullptr, nFlags|nUndoFlags, nullptr, false, &aOptions));
+
+ if (bInsertCells)
+ pUndoMgr->AddUndoAction(std::make_unique<ScUndoWrapper>(std::move(pUndo)), true);
+ else
+ pUndoMgr->AddUndoAction(std::move(pUndo));
+
+ pUndoMgr->LeaveListAction();
+ }
+
+ aModificator.SetDocumentModified();
+ PostPasteFromClip(aMarkedRange, aMark);
+ return true;
+}
+
+bool ScViewFunc::PasteFromClipToMultiRanges(
+ InsertDeleteFlags nFlags, ScDocument* pClipDoc, ScPasteFunc nFunction,
+ bool bSkipEmptyCells, bool bTranspose, bool bAsLink, bool bAllowDialogs,
+ InsCellCmd eMoveMode, InsertDeleteFlags nUndoFlags )
+{
+ if (bTranspose)
+ {
+ // We don't allow transpose for this yet.
+ ErrorMessage(STR_MSSG_PASTEFROMCLIP_0);
+ return false;
+ }
+
+ if (eMoveMode != INS_NONE)
+ {
+ // We don't allow insertion mode either. Too complicated.
+ ErrorMessage(STR_MSSG_PASTEFROMCLIP_0);
+ return false;
+ }
+
+ ScViewData& rViewData = GetViewData();
+ ScClipParam& rClipParam = pClipDoc->GetClipParam();
+ if (rClipParam.mbCutMode)
+ {
+ // No cut and paste with this, please.
+ ErrorMessage(STR_MSSG_PASTEFROMCLIP_0);
+ return false;
+ }
+
+ const ScAddress& rCurPos = rViewData.GetCurPos();
+ ScDocument& rDoc = rViewData.GetDocument();
+
+ ScRange aSrcRange = rClipParam.getWholeRange();
+ SCROW nRowSize = aSrcRange.aEnd.Row() - aSrcRange.aStart.Row() + 1;
+ SCCOL nColSize = aSrcRange.aEnd.Col() - aSrcRange.aStart.Col() + 1;
+
+ if (!rDoc.ValidCol(rCurPos.Col()+nColSize-1) || !rDoc.ValidRow(rCurPos.Row()+nRowSize-1))
+ {
+ ErrorMessage(STR_PASTE_FULL);
+ return false;
+ }
+
+ ScMarkData aMark(rViewData.GetMarkData());
+
+ ScRangeList aRanges;
+ aMark.MarkToSimple();
+ aMark.FillRangeListWithMarks(&aRanges, false);
+ if (!ScClipUtil::CheckDestRanges(rDoc, nColSize, nRowSize, aMark, aRanges))
+ {
+ ErrorMessage(STR_MSSG_PASTEFROMCLIP_0);
+ return false;
+ }
+
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+
+ ScDocShellModificator aModificator(*pDocSh);
+
+ bool bAskIfNotEmpty =
+ bAllowDialogs && (nFlags & InsertDeleteFlags::CONTENTS) &&
+ nFunction == ScPasteFunc::NONE && SC_MOD()->GetInputOptions().GetReplaceCellsWarn();
+
+ if (bAskIfNotEmpty)
+ {
+ if (!checkDestRangeForOverwrite(aRanges, rDoc, aMark, GetViewData().GetDialogParent()))
+ return false;
+ }
+
+ // TODO: position this call better for performance.
+ ResetAutoSpellForContentChange();
+
+ ScDocumentUniquePtr pUndoDoc;
+ if (rDoc.IsUndoEnabled())
+ {
+ pUndoDoc.reset(new ScDocument(SCDOCMODE_UNDO));
+ pUndoDoc->InitUndoSelected(rDoc, aMark);
+ for (size_t i = 0, n = aRanges.size(); i < n; ++i)
+ {
+ rDoc.CopyToDocument(
+ aRanges[i], nUndoFlags, false, *pUndoDoc, &aMark);
+ }
+ }
+
+ ScDocumentUniquePtr pMixDoc;
+ if (bSkipEmptyCells || nFunction != ScPasteFunc::NONE)
+ {
+ if (nFlags & InsertDeleteFlags::CONTENTS)
+ {
+ pMixDoc.reset(new ScDocument(SCDOCMODE_UNDO));
+ pMixDoc->InitUndoSelected(rDoc, aMark);
+ for (size_t i = 0, n = aRanges.size(); i < n; ++i)
+ {
+ rDoc.CopyToDocument(
+ aRanges[i], InsertDeleteFlags::CONTENTS, false, *pMixDoc, &aMark);
+ }
+ }
+ }
+
+ if (nFlags & InsertDeleteFlags::OBJECTS)
+ pDocSh->MakeDrawLayer();
+ if (rDoc.IsUndoEnabled())
+ rDoc.BeginDrawUndo();
+
+ // First, paste everything but the drawing objects.
+ for (size_t i = 0, n = aRanges.size(); i < n; ++i)
+ {
+ rDoc.CopyFromClip(
+ aRanges[i], aMark, (nFlags & ~InsertDeleteFlags::OBJECTS), nullptr, pClipDoc,
+ false, false, true, bSkipEmptyCells);
+ }
+
+ if (pMixDoc)
+ {
+ for (size_t i = 0, n = aRanges.size(); i < n; ++i)
+ rDoc.MixDocument(aRanges[i], nFunction, bSkipEmptyCells, *pMixDoc);
+ }
+
+ AdjustBlockHeight(); // update row heights before pasting objects
+
+ // Then paste the objects.
+ if (nFlags & InsertDeleteFlags::OBJECTS)
+ {
+ for (size_t i = 0, n = aRanges.size(); i < n; ++i)
+ {
+ rDoc.CopyFromClip(
+ aRanges[i], aMark, InsertDeleteFlags::OBJECTS, nullptr, pClipDoc,
+ false, false, true, bSkipEmptyCells);
+ }
+ }
+
+ // Refresh the range that includes all pasted ranges. We only need to
+ // refresh the current sheet.
+ PaintPartFlags nPaint = PaintPartFlags::Grid;
+ bool bRowInfo = (aSrcRange.aStart.Col()==0 && aSrcRange.aEnd.Col()==pClipDoc->MaxCol());
+ if (bRowInfo)
+ nPaint |= PaintPartFlags::Left;
+ pDocSh->PostPaint(aRanges, nPaint);
+
+ if (rDoc.IsUndoEnabled())
+ {
+ SfxUndoManager* pUndoMgr = pDocSh->GetUndoManager();
+ OUString aUndo = ScResId(
+ pClipDoc->IsCutMode() ? STR_UNDO_CUT : STR_UNDO_COPY);
+ pUndoMgr->EnterListAction(aUndo, aUndo, 0, GetViewData().GetViewShell()->GetViewShellId());
+
+ ScUndoPasteOptions aOptions; // store options for repeat
+ aOptions.nFunction = nFunction;
+ aOptions.bSkipEmptyCells = bSkipEmptyCells;
+ aOptions.bTranspose = bTranspose;
+ aOptions.bAsLink = bAsLink;
+ aOptions.eMoveMode = eMoveMode;
+
+
+ pUndoMgr->AddUndoAction(
+ std::make_unique<ScUndoPaste>(
+ pDocSh, aRanges, aMark, std::move(pUndoDoc), nullptr, nFlags|nUndoFlags, nullptr, false, &aOptions));
+ pUndoMgr->LeaveListAction();
+ }
+
+ aModificator.SetDocumentModified();
+ PostPasteFromClip(aRanges, aMark);
+
+ return false;
+}
+
+void ScViewFunc::PostPasteFromClip(const ScRangeList& rPasteRanges, const ScMarkData& rMark)
+{
+ ScViewData& rViewData = GetViewData();
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+ pDocSh->UpdateOle(rViewData);
+
+ SelectionChanged(true);
+
+ ScModelObj* pModelObj = pDocSh->GetModel();
+
+ ScRangeList aChangeRanges;
+ for (size_t i = 0, n = rPasteRanges.size(); i < n; ++i)
+ {
+ const ScRange& r = rPasteRanges[i];
+ for (const auto& rTab : rMark)
+ {
+ ScRange aChangeRange(r);
+ aChangeRange.aStart.SetTab(rTab);
+ aChangeRange.aEnd.SetTab(rTab);
+ aChangeRanges.push_back(aChangeRange);
+ }
+ }
+
+ if (HelperNotifyChanges::getMustPropagateChangesModel(pModelObj))
+ HelperNotifyChanges::Notify(*pModelObj, aChangeRanges, "paste");
+ else if (pModelObj)
+ HelperNotifyChanges::Notify(*pModelObj, aChangeRanges, "data-area-invalidate");
+}
+
+// D R A G A N D D R O P
+
+// inside the doc
+
+bool ScViewFunc::MoveBlockTo( const ScRange& rSource, const ScAddress& rDestPos,
+ bool bCut )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ HideAllCursors();
+
+ ResetAutoSpellForContentChange();
+
+ bool bSuccess = true;
+ SCTAB nDestTab = rDestPos.Tab();
+ const ScMarkData& rMark = GetViewData().GetMarkData();
+ if ( rSource.aStart.Tab() == nDestTab && rSource.aEnd.Tab() == nDestTab && rMark.GetSelectCount() > 1 )
+ {
+ // moving within one table and several tables selected -> apply to all selected tables
+
+ OUString aUndo = ScResId( bCut ? STR_UNDO_MOVE : STR_UNDO_COPY );
+ pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, GetViewData().GetViewShell()->GetViewShellId() );
+
+ // collect ranges of consecutive selected tables
+
+ ScRange aLocalSource = rSource;
+ ScAddress aLocalDest = rDestPos;
+ SCTAB nTabCount = pDocSh->GetDocument().GetTableCount();
+ SCTAB nStartTab = 0;
+ while ( nStartTab < nTabCount && bSuccess )
+ {
+ while ( nStartTab < nTabCount && !rMark.GetTableSelect(nStartTab) )
+ ++nStartTab;
+ if ( nStartTab < nTabCount )
+ {
+ SCTAB nEndTab = nStartTab;
+ while ( nEndTab+1 < nTabCount && rMark.GetTableSelect(nEndTab+1) )
+ ++nEndTab;
+
+ aLocalSource.aStart.SetTab( nStartTab );
+ aLocalSource.aEnd.SetTab( nEndTab );
+ aLocalDest.SetTab( nStartTab );
+
+ bSuccess = pDocSh->GetDocFunc().MoveBlock(
+ aLocalSource, aLocalDest, bCut, true/*bRecord*/, true/*bPaint*/, true/*bApi*/ );
+
+ nStartTab = nEndTab + 1;
+ }
+ }
+
+ pDocSh->GetUndoManager()->LeaveListAction();
+ }
+ else
+ {
+ // move the block as specified
+ bSuccess = pDocSh->GetDocFunc().MoveBlock(
+ rSource, rDestPos, bCut, true/*bRecord*/, true/*bPaint*/, true/*bApi*/ );
+ }
+
+ ShowAllCursors();
+ if (bSuccess)
+ {
+ // mark destination range
+ ScAddress aDestEnd(
+ rDestPos.Col() + rSource.aEnd.Col() - rSource.aStart.Col(),
+ rDestPos.Row() + rSource.aEnd.Row() - rSource.aStart.Row(),
+ nDestTab );
+
+ bool bIncludeFiltered = bCut;
+ if ( !bIncludeFiltered )
+ {
+ // find number of non-filtered rows
+ SCROW nPastedCount = pDocSh->GetDocument().CountNonFilteredRows(
+ rSource.aStart.Row(), rSource.aEnd.Row(), rSource.aStart.Tab());
+
+ if ( nPastedCount == 0 )
+ nPastedCount = 1;
+ aDestEnd.SetRow( rDestPos.Row() + nPastedCount - 1 );
+ }
+
+ MarkRange( ScRange( rDestPos, aDestEnd ), false ); //! sal_False ???
+
+ pDocSh->UpdateOle(GetViewData());
+ SelectionChanged();
+ }
+ return bSuccess;
+}
+
+// link inside the doc
+
+bool ScViewFunc::LinkBlock( const ScRange& rSource, const ScAddress& rDestPos )
+{
+ // check overlapping
+
+ if ( rSource.aStart.Tab() == rDestPos.Tab() )
+ {
+ SCCOL nDestEndCol = rDestPos.Col() + ( rSource.aEnd.Col() - rSource.aStart.Col() );
+ SCROW nDestEndRow = rDestPos.Row() + ( rSource.aEnd.Row() - rSource.aStart.Row() );
+
+ if ( rSource.aStart.Col() <= nDestEndCol && rDestPos.Col() <= rSource.aEnd.Col() &&
+ rSource.aStart.Row() <= nDestEndRow && rDestPos.Row() <= rSource.aEnd.Row() )
+ {
+ return false;
+ }
+ }
+
+ // run with paste
+
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScDocumentUniquePtr pClipDoc(new ScDocument( SCDOCMODE_CLIP ));
+ rDoc.CopyTabToClip( rSource.aStart.Col(), rSource.aStart.Row(),
+ rSource.aEnd.Col(), rSource.aEnd.Row(),
+ rSource.aStart.Tab(), pClipDoc.get() );
+
+ // mark destination area (set cursor, no marks)
+
+ if ( GetViewData().GetTabNo() != rDestPos.Tab() )
+ SetTabNo( rDestPos.Tab() );
+
+ MoveCursorAbs( rDestPos.Col(), rDestPos.Row(), SC_FOLLOW_NONE, false, false );
+
+ // Paste
+
+ PasteFromClip( InsertDeleteFlags::ALL, pClipDoc.get(), ScPasteFunc::NONE, false, false, true ); // as a link
+
+ return true;
+}
+
+void ScViewFunc::DataFormPutData( SCROW nCurrentRow ,
+ SCROW nStartRow , SCCOL nStartCol ,
+ SCROW nEndRow , SCCOL nEndCol ,
+ std::vector<std::unique_ptr<ScDataFormFragment>>& rEdits,
+ sal_uInt16 aColLength )
+{
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ ScDocShellModificator aModificator( *pDocSh );
+ SfxUndoManager* pUndoMgr = pDocSh->GetUndoManager();
+
+ const bool bRecord( rDoc.IsUndoEnabled());
+ ScDocumentUniquePtr pUndoDoc;
+ ScDocumentUniquePtr pRedoDoc;
+ std::unique_ptr<ScRefUndoData> pUndoData;
+ SCTAB nTab = GetViewData().GetTabNo();
+ SCTAB nStartTab = nTab;
+ SCTAB nEndTab = nTab;
+
+ {
+ ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack();
+ if ( pChangeTrack )
+ pChangeTrack->ResetLastCut(); // no more cut-mode
+ }
+ ScRange aUserRange( nStartCol, nCurrentRow, nStartTab, nEndCol, nCurrentRow, nEndTab );
+ bool bColInfo = ( nStartRow==0 && nEndRow==rDoc.MaxRow() );
+ bool bRowInfo = ( nStartCol==0 && nEndCol==rDoc.MaxCol() );
+ SCCOL nUndoEndCol = nStartCol+aColLength-1;
+ SCROW nUndoEndRow = nCurrentRow;
+
+ if ( bRecord )
+ {
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndoSelected( rDoc , rMark , bColInfo , bRowInfo );
+ rDoc.CopyToDocument( aUserRange , InsertDeleteFlags::VALUE , false, *pUndoDoc );
+ }
+ sal_uInt16 nExtFlags = 0;
+ pDocSh->UpdatePaintExt( nExtFlags, nStartCol, nStartRow, nStartTab , nEndCol, nEndRow, nEndTab ); // content before the change
+ rDoc.BeginDrawUndo();
+
+ for(sal_uInt16 i = 0; i < aColLength; i++)
+ {
+ if (rEdits[i] != nullptr)
+ {
+ OUString aFieldName = rEdits[i]->m_xEdit->get_text();
+ rDoc.SetString( nStartCol + i, nCurrentRow, nTab, aFieldName );
+ }
+ }
+ pDocSh->UpdatePaintExt( nExtFlags, nStartCol, nCurrentRow, nStartTab, nEndCol, nCurrentRow, nEndTab ); // content after the change
+ std::unique_ptr<SfxUndoAction> pUndo( new ScUndoDataForm( pDocSh,
+ nStartCol, nCurrentRow, nStartTab,
+ nUndoEndCol, nUndoEndRow, nEndTab, rMark,
+ std::move(pUndoDoc), std::move(pRedoDoc),
+ std::move(pUndoData) ) );
+ pUndoMgr->AddUndoAction( std::make_unique<ScUndoWrapper>( std::move(pUndo) ), true );
+
+ PaintPartFlags nPaint = PaintPartFlags::Grid;
+ if (bColInfo)
+ {
+ nPaint |= PaintPartFlags::Top;
+ nUndoEndCol = rDoc.MaxCol(); // just for drawing !
+ }
+ if (bRowInfo)
+ {
+ nPaint |= PaintPartFlags::Left;
+ nUndoEndRow = rDoc.MaxRow(); // just for drawing !
+ }
+
+ pDocSh->PostPaint(
+ ScRange(nStartCol, nCurrentRow, nStartTab, nUndoEndCol, nUndoEndRow, nEndTab),
+ nPaint, nExtFlags);
+ pDocSh->UpdateOle(GetViewData());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/viewfun4.cxx b/sc/source/ui/view/viewfun4.cxx
new file mode 100644
index 0000000000..d75541b302
--- /dev/null
+++ b/sc/source/ui/view/viewfun4.cxx
@@ -0,0 +1,791 @@
+/* -*- 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 .
+ */
+
+#include <config_features.h>
+
+#include <memory>
+#include <editeng/eeitem.hxx>
+
+#include <editeng/editobj.hxx>
+#include <editeng/editstat.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/flditem.hxx>
+#include <sot/storage.hxx>
+#include <svx/hlnkitem.hxx>
+#include <editeng/unolingu.hxx>
+
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/fcontnr.hxx>
+#include <svtools/langtab.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <svl/stritem.hxx>
+#include <vcl/transfer.hxx>
+#include <svl/urlbmk.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <avmedia/mediawindow.hxx>
+#include <osl/diagnose.h>
+
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/storagehelper.hxx>
+
+#include <viewfunc.hxx>
+#include <docsh.hxx>
+#include <document.hxx>
+#include <globstr.hrc>
+#include <global.hxx>
+#include <scresid.hxx>
+#include <undoblk.hxx>
+#include <undocell.hxx>
+#include <formulacell.hxx>
+#include <scmod.hxx>
+#include <spelleng.hxx>
+#include <patattr.hxx>
+#include <tabvwsh.hxx>
+#include <impex.hxx>
+#include <editutil.hxx>
+#include <editable.hxx>
+#include <dociter.hxx>
+#include <reffind.hxx>
+#include <compiler.hxx>
+#include <tokenarray.hxx>
+#include <refupdatecontext.hxx>
+#include <gridwin.hxx>
+#include <refundo.hxx>
+
+using namespace com::sun::star;
+
+void ScViewFunc::PasteRTF( SCCOL nStartCol, SCROW nStartRow,
+ const css::uno::Reference< css::datatransfer::XTransferable >& rxTransferable )
+{
+ TransferableDataHelper aDataHelper( rxTransferable );
+ if ( aDataHelper.HasFormat( SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT ) )
+ {
+ HideAllCursors();
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTab = GetViewData().GetTabNo();
+ const bool bRecord (rDoc.IsUndoEnabled());
+
+ const ScPatternAttr* pPattern = rDoc.GetPattern( nStartCol, nStartRow, nTab );
+ std::optional<ScTabEditEngine> pEngine(std::in_place, *pPattern, rDoc.GetEnginePool(), &rDoc );
+ pEngine->EnableUndo( false );
+
+ vcl::Window* pActWin = GetActiveWin();
+ if (pActWin)
+ {
+ pEngine->SetPaperSize(Size(100000,100000));
+ ScopedVclPtrInstance< vcl::Window > aWin( pActWin );
+ EditView aEditView( &*pEngine, aWin.get() );
+ aEditView.SetOutputArea(tools::Rectangle(0,0,100000,100000));
+
+ // same method now for clipboard or drag&drop
+ // mba: clipboard always must contain absolute URLs (could be from alien source)
+ aEditView.InsertText( rxTransferable, OUString(), true );
+ }
+
+ sal_Int32 nParCnt = pEngine->GetParagraphCount();
+ if (nParCnt)
+ {
+ SCROW nEndRow = nStartRow + static_cast<SCROW>(nParCnt) - 1;
+ if (nEndRow > rDoc.MaxRow())
+ nEndRow = rDoc.MaxRow();
+
+ ScDocumentUniquePtr pUndoDoc;
+ if (bRecord)
+ {
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nTab, nTab );
+ rDoc.CopyToDocument( nStartCol,nStartRow,nTab, nStartCol,nEndRow,nTab, InsertDeleteFlags::ALL, false, *pUndoDoc );
+ }
+
+ SCROW nRow = nStartRow;
+
+ // Temporarily turn off undo generation for this lot
+ bool bUndoEnabled = rDoc.IsUndoEnabled();
+ rDoc.EnableUndo( false );
+ for( sal_Int32 n = 0; n < nParCnt; n++ )
+ {
+ std::unique_ptr<EditTextObject> pObject(pEngine->CreateTextObject(n));
+ EnterData(nStartCol, nRow, nTab, *pObject, true);
+ if( ++nRow > rDoc.MaxRow() )
+ break;
+ }
+ rDoc.EnableUndo(bUndoEnabled);
+
+ if (bRecord)
+ {
+ ScDocumentUniquePtr pRedoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ pRedoDoc->InitUndo( rDoc, nTab, nTab );
+ rDoc.CopyToDocument( nStartCol,nStartRow,nTab, nStartCol,nEndRow,nTab, InsertDeleteFlags::ALL|InsertDeleteFlags::NOCAPTIONS, false, *pRedoDoc );
+
+ ScRange aMarkRange(nStartCol, nStartRow, nTab, nStartCol, nEndRow, nTab);
+ ScMarkData aDestMark(rDoc.GetSheetLimits());
+ aDestMark.SetMarkArea( aMarkRange );
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoPaste>( pDocSh, aMarkRange, aDestMark,
+ std::move(pUndoDoc), std::move(pRedoDoc), InsertDeleteFlags::ALL, nullptr));
+ }
+ }
+
+ pEngine.reset();
+
+ ShowAllCursors();
+ }
+ else
+ {
+ HideAllCursors();
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScImportExport aImpEx( pDocSh->GetDocument(),
+ ScAddress( nStartCol, nStartRow, GetViewData().GetTabNo() ) );
+
+ OUString aStr;
+ tools::SvRef<SotTempStream> xStream;
+ if ( aDataHelper.GetSotStorageStream( SotClipboardFormatId::RTF, xStream ) && xStream.is() )
+ // mba: clipboard always must contain absolute URLs (could be from alien source)
+ aImpEx.ImportStream( *xStream, OUString(), SotClipboardFormatId::RTF );
+ else if ( aDataHelper.GetString( SotClipboardFormatId::RTF, aStr ) )
+ aImpEx.ImportString( aStr, SotClipboardFormatId::RTF );
+ else if ( aDataHelper.GetSotStorageStream( SotClipboardFormatId::RICHTEXT, xStream ) && xStream.is() )
+ aImpEx.ImportStream( *xStream, OUString(), SotClipboardFormatId::RICHTEXT );
+ else if ( aDataHelper.GetString( SotClipboardFormatId::RICHTEXT, aStr ) )
+ aImpEx.ImportString( aStr, SotClipboardFormatId::RICHTEXT );
+
+ AdjustRowHeight( nStartRow, aImpEx.GetRange().aEnd.Row(), true );
+ pDocSh->UpdateOle(GetViewData());
+ ShowAllCursors();
+ }
+}
+void ScViewFunc::DoRefConversion()
+{
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ SCTAB nTabCount = rDoc.GetTableCount();
+ bool bRecord = true;
+ if (!rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ ScRange aMarkRange;
+ rMark.MarkToSimple();
+ bool bMulti = rMark.IsMultiMarked();
+ if (bMulti)
+ aMarkRange = rMark.GetMultiMarkArea();
+ else if (rMark.IsMarked())
+ aMarkRange = rMark.GetMarkArea();
+ else
+ {
+ aMarkRange = ScRange( GetViewData().GetCurX(),
+ GetViewData().GetCurY(), GetViewData().GetTabNo() );
+ }
+ ScEditableTester aTester( rDoc, aMarkRange.aStart.Col(), aMarkRange.aStart.Row(),
+ aMarkRange.aEnd.Col(), aMarkRange.aEnd.Row(),rMark );
+ if (!aTester.IsEditable())
+ {
+ ErrorMessage(aTester.GetMessageId());
+ return;
+ }
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ bool bOk = false;
+
+ ScDocumentUniquePtr pUndoDoc;
+ if (bRecord)
+ {
+ pUndoDoc.reset( new ScDocument( SCDOCMODE_UNDO ) );
+ SCTAB nTab = aMarkRange.aStart.Tab();
+ pUndoDoc->InitUndo( rDoc, nTab, nTab );
+
+ if ( rMark.GetSelectCount() > 1 )
+ {
+ for (const auto& rTab : rMark)
+ if ( rTab != nTab )
+ pUndoDoc->AddUndoTab( rTab, rTab );
+ }
+ ScRange aCopyRange = aMarkRange;
+ aCopyRange.aStart.SetTab(0);
+ aCopyRange.aEnd.SetTab(nTabCount-1);
+ rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ALL, bMulti, *pUndoDoc, &rMark );
+ }
+
+ ScRangeListRef xRanges;
+ GetViewData().GetMultiArea( xRanges );
+ size_t nCount = xRanges->size();
+
+ for (const SCTAB& i : rMark)
+ {
+ for (size_t j = 0; j < nCount; ++j)
+ {
+ ScRange aRange = (*xRanges)[j];
+ aRange.aStart.SetTab(i);
+ aRange.aEnd.SetTab(i);
+ ScCellIterator aIter( rDoc, aRange );
+ for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
+ {
+ if (aIter.getType() != CELLTYPE_FORMULA)
+ continue;
+
+ ScFormulaCell* pCell = aIter.getFormulaCell();
+ ScMatrixMode eMatrixMode = pCell->GetMatrixFlag();
+ if (eMatrixMode == ScMatrixMode::Reference)
+ continue;
+
+ OUString aOld = pCell->GetFormula();
+ sal_Int32 nLen = aOld.getLength();
+ if (eMatrixMode == ScMatrixMode::Formula)
+ {
+ assert(nLen >= 2 && aOld[0] == '{' && aOld[nLen-1] == '}');
+ nLen -= 2;
+ aOld = aOld.copy( 1, nLen);
+ }
+ ScRefFinder aFinder( aOld, aIter.GetPos(), rDoc, rDoc.GetAddressConvention() );
+ aFinder.ToggleRel( 0, nLen );
+ if (aFinder.GetFound())
+ {
+ ScAddress aPos = pCell->aPos;
+ const OUString& aNew = aFinder.GetText();
+ ScCompiler aComp( rDoc, aPos, rDoc.GetGrammar());
+ std::unique_ptr<ScTokenArray> pArr(aComp.CompileString(aNew));
+ ScFormulaCell* pNewCell =
+ new ScFormulaCell(
+ rDoc, aPos, *pArr, formula::FormulaGrammar::GRAM_DEFAULT, eMatrixMode);
+
+ rDoc.SetFormulaCell(aPos, pNewCell);
+ bOk = true;
+ }
+ }
+ }
+ }
+ if (bRecord)
+ {
+ ScDocumentUniquePtr pRedoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ SCTAB nTab = aMarkRange.aStart.Tab();
+ pRedoDoc->InitUndo( rDoc, nTab, nTab );
+
+ if ( rMark.GetSelectCount() > 1 )
+ {
+ for (const auto& rTab : rMark)
+ if ( rTab != nTab )
+ pRedoDoc->AddUndoTab( rTab, rTab );
+ }
+ ScRange aCopyRange = aMarkRange;
+ aCopyRange.aStart.SetTab(0);
+ aCopyRange.aEnd.SetTab(nTabCount-1);
+ rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ALL, bMulti, *pRedoDoc, &rMark );
+
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoRefConversion>( pDocSh,
+ aMarkRange, rMark, std::move(pUndoDoc), std::move(pRedoDoc), bMulti) );
+ }
+
+ pDocSh->PostPaint( aMarkRange, PaintPartFlags::Grid );
+ pDocSh->UpdateOle(GetViewData());
+ pDocSh->SetDocumentModified();
+ CellContentChanged();
+
+ if (!bOk)
+ ErrorMessage(STR_ERR_NOREF);
+}
+
+// Thesaurus - Undo ok
+void ScViewFunc::DoThesaurus()
+{
+ SCCOL nCol;
+ SCROW nRow;
+ SCTAB nTab;
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ ScSplitPos eWhich = GetViewData().GetActivePart();
+ EESpellState eState;
+ EditView* pEditView = nullptr;
+ std::unique_ptr<ESelection> pEditSel;
+ std::unique_ptr<ScEditEngineDefaulter> pThesaurusEngine;
+ bool bIsEditMode = GetViewData().HasEditView(eWhich);
+ bool bRecord = true;
+ if (!rDoc.IsUndoEnabled())
+ bRecord = false;
+ if (bIsEditMode) // edit mode active
+ {
+ GetViewData().GetEditView(eWhich, pEditView, nCol, nRow);
+ pEditSel.reset(new ESelection(pEditView->GetSelection()));
+ SC_MOD()->InputEnterHandler();
+ GetViewData().GetBindings().Update(); // otherwise the Sfx becomes mixed-up...
+ }
+ else
+ {
+ nCol = GetViewData().GetCurX();
+ nRow = GetViewData().GetCurY();
+ }
+ nTab = GetViewData().GetTabNo();
+
+ ScAddress aPos(nCol, nRow, nTab);
+ ScEditableTester aTester( rDoc, nCol, nRow, nCol, nRow, rMark );
+ if (!aTester.IsEditable())
+ {
+ ErrorMessage(aTester.GetMessageId());
+ return;
+ }
+
+ ScCellValue aOldText;
+ aOldText.assign(rDoc, aPos);
+ if (aOldText.getType() != CELLTYPE_STRING && aOldText.getType() != CELLTYPE_EDIT)
+ {
+ ErrorMessage(STR_THESAURUS_NO_STRING);
+ return;
+ }
+
+ uno::Reference<linguistic2::XSpellChecker1> xSpeller = LinguMgr::GetSpellChecker();
+
+ pThesaurusEngine.reset(new ScEditEngineDefaulter(rDoc.GetEnginePool()));
+ pThesaurusEngine->SetEditTextObjectPool( rDoc.GetEditPool() );
+ pThesaurusEngine->SetRefDevice(GetViewData().GetActiveWin()->GetOutDev());
+ pThesaurusEngine->SetSpeller(xSpeller);
+ MakeEditView(pThesaurusEngine.get(), nCol, nRow );
+ SfxItemSet aEditDefaults(pThesaurusEngine->GetEmptyItemSet());
+ const ScPatternAttr* pPattern = rDoc.GetPattern(nCol, nRow, nTab);
+ if (pPattern)
+ {
+ pPattern->FillEditItemSet( &aEditDefaults );
+ pThesaurusEngine->SetDefaults( aEditDefaults );
+ }
+
+ if (aOldText.getType() == CELLTYPE_EDIT)
+ pThesaurusEngine->SetTextCurrentDefaults(*aOldText.getEditText());
+ else
+ pThesaurusEngine->SetTextCurrentDefaults(aOldText.getString(rDoc));
+
+ pEditView = GetViewData().GetEditView(GetViewData().GetActivePart());
+ if (pEditSel)
+ pEditView->SetSelection(*pEditSel);
+ else
+ pEditView->SetSelection(ESelection(0,0,0,0));
+
+ pThesaurusEngine->ClearModifyFlag();
+
+ // language is now in EditEngine attributes -> no longer passed to StartThesaurus
+
+ eState = pEditView->StartThesaurus(GetViewData().GetDialogParent());
+ OSL_ENSURE(eState != EESpellState::NoSpeller, "No SpellChecker");
+
+ if (eState == EESpellState::ErrorFound) // should happen later through Wrapper!
+ {
+ LanguageType eLnge = ScViewUtil::GetEffLanguage( rDoc, ScAddress( nCol, nRow, nTab ) );
+ OUString aErr = SvtLanguageTable::GetLanguageString(eLnge) + ScResId( STR_SPELLING_NO_LANG );
+
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetViewData().GetDialogParent(),
+ VclMessageType::Info, VclButtonsType::Ok,
+ aErr));
+ xInfoBox->run();
+ }
+ if (pThesaurusEngine->IsModified())
+ {
+ ScCellValue aNewText;
+
+ if (aOldText.getType() == CELLTYPE_EDIT)
+ {
+ // The cell will own the text object instance.
+ std::unique_ptr<EditTextObject> pText = pThesaurusEngine->CreateTextObject();
+ auto tmp = pText.get();
+ if (rDoc.SetEditText(ScAddress(nCol,nRow,nTab), std::move(pText)))
+ aNewText.set(*tmp);
+ }
+ else
+ {
+ OUString aStr = pThesaurusEngine->GetText();
+ aNewText.set(rDoc.GetSharedStringPool().intern(aStr));
+ rDoc.SetString(nCol, nRow, nTab, aStr);
+ }
+
+ pDocSh->SetDocumentModified();
+ if (bRecord)
+ {
+ GetViewData().GetDocShell()->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoThesaurus>(
+ GetViewData().GetDocShell(), nCol, nRow, nTab, aOldText, aNewText));
+ }
+ }
+
+ KillEditView(true);
+ pDocSh->PostPaintGridAll();
+}
+
+void ScViewFunc::DoHangulHanjaConversion()
+{
+ ScConversionParam aConvParam( SC_CONVERSION_HANGULHANJA, LANGUAGE_KOREAN, 0, true );
+ DoSheetConversion( aConvParam );
+}
+
+void ScViewFunc::DoSheetConversion( const ScConversionParam& rConvParam )
+{
+ SCCOL nCol;
+ SCROW nRow;
+ SCTAB nTab;
+ ScViewData& rViewData = GetViewData();
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScMarkData& rMark = rViewData.GetMarkData();
+ ScSplitPos eWhich = rViewData.GetActivePart();
+ EditView* pEditView = nullptr;
+ bool bIsEditMode = rViewData.HasEditView(eWhich);
+ bool bRecord = true;
+ if (!rDoc.IsUndoEnabled())
+ bRecord = false;
+ if (bIsEditMode) // edit mode active
+ {
+ rViewData.GetEditView(eWhich, pEditView, nCol, nRow);
+ SC_MOD()->InputEnterHandler();
+ }
+ else
+ {
+ nCol = rViewData.GetCurX();
+ nRow = rViewData.GetCurY();
+
+ AlignToCursor( nCol, nRow, SC_FOLLOW_JUMP);
+ }
+ nTab = rViewData.GetTabNo();
+
+ rMark.MarkToMulti();
+ bool bMarked = rMark.IsMultiMarked();
+ if (bMarked)
+ {
+ ScEditableTester aTester( rDoc, rMark );
+ if (!aTester.IsEditable())
+ {
+ ErrorMessage(aTester.GetMessageId());
+ return;
+ }
+ }
+
+ ScDocumentUniquePtr pUndoDoc;
+ ScDocumentUniquePtr pRedoDoc;
+ if (bRecord)
+ {
+ pUndoDoc.reset( new ScDocument( SCDOCMODE_UNDO ) );
+ pUndoDoc->InitUndo( rDoc, nTab, nTab );
+ pRedoDoc.reset( new ScDocument( SCDOCMODE_UNDO ) );
+ pRedoDoc->InitUndo( rDoc, nTab, nTab );
+
+ if ( rMark.GetSelectCount() > 1 )
+ {
+ for (const auto& rTab : rMark)
+ if ( rTab != nTab )
+ {
+ pUndoDoc->AddUndoTab( rTab, rTab );
+ pRedoDoc->AddUndoTab( rTab, rTab );
+ }
+ }
+ }
+
+ // from here no return
+
+ bool bOldEnabled = rDoc.IsIdleEnabled();
+ rDoc.EnableIdle(false); // stop online spelling
+
+ // *** create and init the edit engine *** --------------------------------
+
+ std::unique_ptr<ScConversionEngineBase> pEngine;
+ switch( rConvParam.GetType() )
+ {
+ case SC_CONVERSION_SPELLCHECK:
+ pEngine.reset(new ScSpellingEngine(
+ rDoc.GetEnginePool(), rViewData, pUndoDoc.get(), pRedoDoc.get(), LinguMgr::GetSpellChecker() ));
+ break;
+ case SC_CONVERSION_HANGULHANJA:
+ case SC_CONVERSION_CHINESE_TRANSL:
+ pEngine.reset(new ScTextConversionEngine(
+ rDoc.GetEnginePool(), rViewData, rConvParam, pUndoDoc.get(), pRedoDoc.get() ));
+ break;
+ default:
+ OSL_FAIL( "ScViewFunc::DoSheetConversion - unknown conversion type" );
+ }
+
+ MakeEditView( pEngine.get(), nCol, nRow );
+ pEngine->SetRefDevice( rViewData.GetActiveWin()->GetOutDev() );
+ // simulate dummy cell:
+ pEditView = rViewData.GetEditView( rViewData.GetActivePart() );
+ rViewData.SetSpellingView( pEditView );
+ tools::Rectangle aRect( Point( 0, 0 ), Point( 0, 0 ) );
+ pEditView->SetOutputArea( aRect );
+ pEngine->SetControlWord( EEControlBits::USECHARATTRIBS );
+ pEngine->EnableUndo( false );
+ pEngine->SetPaperSize( aRect.GetSize() );
+ pEngine->SetTextCurrentDefaults( OUString() );
+
+ // *** do the conversion *** ----------------------------------------------
+
+ pEngine->ClearModifyFlag();
+ pEngine->ConvertAll(GetViewData().GetDialogParent(), *pEditView);
+
+ // *** undo/redo *** ------------------------------------------------------
+
+ if( pEngine->IsAnyModified() )
+ {
+ if (bRecord)
+ {
+ SCCOL nNewCol = rViewData.GetCurX();
+ SCROW nNewRow = rViewData.GetCurY();
+ rViewData.GetDocShell()->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoConversion>(
+ pDocSh, rMark,
+ nCol, nRow, nTab, std::move(pUndoDoc),
+ nNewCol, nNewRow, nTab, std::move(pRedoDoc), rConvParam ) );
+ }
+
+ sc::SetFormulaDirtyContext aCxt;
+ rDoc.SetAllFormulasDirty(aCxt);
+
+ pDocSh->SetDocumentModified();
+ }
+ else
+ {
+ pUndoDoc.reset();
+ pRedoDoc.reset();
+ }
+
+ // *** final cleanup *** --------------------------------------------------
+
+ rViewData.SetSpellingView( nullptr );
+ KillEditView(true);
+ pEngine.reset();
+ pDocSh->PostPaintGridAll();
+ rViewData.GetViewShell()->UpdateInputHandler();
+ rDoc.EnableIdle(bOldEnabled);
+}
+
+// past from SotClipboardFormatId::FILE items
+// is not called directly from Drop, but asynchronously -> dialogs are allowed
+
+bool ScViewFunc::PasteFile( const Point& rPos, const OUString& rFile, bool bLink )
+{
+ INetURLObject aURL;
+ aURL.SetSmartURL( rFile );
+ OUString aStrURL = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+
+ // is it a media URL?
+#if HAVE_FEATURE_AVMEDIA
+ if( ::avmedia::MediaWindow::isMediaURL( aStrURL, ""/*TODO?*/ ) )
+ {
+ const SfxStringItem aMediaURLItem( SID_INSERT_AVMEDIA, aStrURL );
+ const SfxPoolItemHolder aResult(GetViewData().GetDispatcher().ExecuteList(
+ SID_INSERT_AVMEDIA, SfxCallMode::SYNCHRON,
+ { &aMediaURLItem }));
+ return (nullptr != aResult.getItem());
+ }
+#endif
+
+ if (!bLink) // for bLink only graphics or URL
+ {
+ // 1. can I open the file?
+ std::shared_ptr<const SfxFilter> pFlt;
+
+ // search only for its own filters, without selection box (as in ScDocumentLoader)
+ SfxFilterMatcher aMatcher( ScDocShell::Factory().GetFilterContainer()->GetName() );
+ SfxMedium aSfxMedium( aStrURL, (StreamMode::READ | StreamMode::SHARE_DENYNONE) );
+ // #i73992# GuessFilter no longer calls UseInteractionHandler.
+ // This is UI, so it can be called here.
+ aSfxMedium.UseInteractionHandler(true);
+ ErrCode nErr = aMatcher.GuessFilter( aSfxMedium, pFlt );
+
+ if ( pFlt && !nErr )
+ {
+ // code stolen from the SFX!
+ SfxDispatcher &rDispatcher = GetViewData().GetDispatcher();
+ SfxStringItem aFileNameItem( SID_FILE_NAME, aStrURL );
+ SfxStringItem aFilterItem( SID_FILTER_NAME, pFlt->GetName() );
+ // #i69524# add target, as in SfxApplication when the Open dialog is used
+ SfxStringItem aTargetItem( SID_TARGETNAME, "_default" );
+
+ // Open Asynchronously, because it can also happen from D&D
+ // and that is not so good for the MAC...
+ const SfxPoolItemHolder aResult(rDispatcher.ExecuteList(SID_OPENDOC,
+ SfxCallMode::ASYNCHRON,
+ { &aFileNameItem, &aFilterItem, &aTargetItem}));
+ return (nullptr != aResult.getItem());
+ }
+ }
+
+ // 2. can the file be inserted using the graphics filter?
+ // (as a link, since the Gallery provides it in this way)
+
+ Graphic aGraphic;
+ GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter();
+
+ if (!rGraphicFilter.ImportGraphic(aGraphic, aURL,
+ GRFILTER_FORMAT_DONTKNOW ))
+ {
+ if ( bLink )
+ {
+ return PasteGraphic( rPos, aGraphic, aStrURL );
+ }
+ else
+ {
+ // #i76709# if bLink isn't set, pass empty URL/filter, so a non-linked image is inserted
+ return PasteGraphic( rPos, aGraphic, OUString() );
+ }
+ }
+
+ if (bLink) // for bLink everything, which is not graphics, as URL
+ {
+ tools::Rectangle aRect( rPos, Size(0,0) );
+ ScRange aRange = GetViewData().GetDocument().
+ GetRange( GetViewData().GetTabNo(), aRect );
+ SCCOL nPosX = aRange.aStart.Col();
+ SCROW nPosY = aRange.aStart.Row();
+
+ InsertBookmark( aStrURL, aStrURL, nPosX, nPosY );
+ return true;
+ }
+ else
+ {
+ // 3. can the file be inserted as OLE?
+ // also non-storages, for instance sounds (#38282#)
+ uno::Reference < embed::XStorage > xStorage = comphelper::OStorageHelper::GetTemporaryStorage();
+
+ //TODO/LATER: what about "bLink"?
+
+ uno::Sequence < beans::PropertyValue > aMedium{ comphelper::makePropertyValue("URL",
+ aStrURL) };
+
+ comphelper::EmbeddedObjectContainer aCnt( xStorage );
+ OUString aName;
+ uno::Reference < embed::XEmbeddedObject > xObj = aCnt.InsertEmbeddedObject( aMedium, aName );
+ if( xObj.is() )
+ return PasteObject( rPos, xObj, nullptr );
+
+ // If an OLE object can't be created, insert a URL button
+
+ GetViewData().GetViewShell()->InsertURLButton( aStrURL, aStrURL, OUString(), &rPos );
+ return true;
+ }
+}
+
+bool ScViewFunc::PasteBookmark( SotClipboardFormatId nFormatId,
+ const css::uno::Reference< css::datatransfer::XTransferable >& rxTransferable,
+ SCCOL nPosX, SCROW nPosY )
+{
+ INetBookmark aBookmark;
+ TransferableDataHelper aDataHelper( rxTransferable );
+ if ( !aDataHelper.GetINetBookmark( nFormatId, aBookmark ) )
+ return false;
+
+ InsertBookmark( aBookmark.GetDescription(), aBookmark.GetURL(), nPosX, nPosY );
+ return true;
+}
+
+void ScViewFunc::InsertBookmark( const OUString& rDescription, const OUString& rURL,
+ SCCOL nPosX, SCROW nPosY, const OUString* pTarget,
+ bool bTryReplace )
+{
+ ScViewData& rViewData = GetViewData();
+ if ( rViewData.HasEditView( rViewData.GetActivePart() ) &&
+ nPosX >= rViewData.GetEditStartCol() && nPosX <= rViewData.GetEditEndCol() &&
+ nPosY >= rViewData.GetEditStartRow() && nPosY <= rViewData.GetEditEndRow() )
+ {
+ // insert into the cell which just got edited
+
+ OUString aTargetFrame;
+ if (pTarget)
+ aTargetFrame = *pTarget;
+ rViewData.GetViewShell()->InsertURLField( rDescription, rURL, aTargetFrame );
+ return;
+ }
+
+ // insert into not edited cell
+
+ ScDocument& rDoc = GetViewData().GetDocument();
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScAddress aCellPos( nPosX, nPosY, nTab );
+ EditEngine aEngine( rDoc.GetEnginePool() );
+
+ const EditTextObject* pOld = rDoc.GetEditText(aCellPos);
+ if (pOld)
+ aEngine.SetText(*pOld);
+ else
+ {
+ OUString aOld = rDoc.GetInputString(nPosX, nPosY, nTab);
+ if (!aOld.isEmpty())
+ aEngine.SetText(aOld);
+ }
+
+ sal_Int32 nPara = aEngine.GetParagraphCount();
+ if (nPara)
+ --nPara;
+ sal_Int32 nTxtLen = aEngine.GetTextLen(nPara);
+ ESelection aInsSel( nPara, nTxtLen, nPara, nTxtLen );
+
+ if ( bTryReplace && HasBookmarkAtCursor( nullptr ) )
+ {
+ // if called from hyperlink slot and cell contains only a URL,
+ // replace old URL with new one
+
+ aInsSel = ESelection( 0, 0, 0, 1 ); // replace first character (field)
+ }
+
+ SvxURLField aField( rURL, rDescription, SvxURLFormat::AppDefault );
+ if (pTarget)
+ aField.SetTargetFrame(*pTarget);
+ aEngine.QuickInsertField( SvxFieldItem( aField, EE_FEATURE_FIELD ), aInsSel );
+
+ std::unique_ptr<EditTextObject> pData(aEngine.CreateTextObject());
+ EnterData(nPosX, nPosY, nTab, *pData);
+}
+
+bool ScViewFunc::HasBookmarkAtCursor( SvxHyperlinkItem* pContent )
+{
+ ScAddress aPos( GetViewData().GetCurX(), GetViewData().GetCurY(), GetViewData().GetTabNo() );
+ ScDocument& rDoc = GetViewData().GetDocShell()->GetDocument();
+
+ const EditTextObject* pData = rDoc.GetEditText(aPos);
+ if (!pData)
+ return false;
+
+ if (!pData->IsFieldObject())
+ // not a field object.
+ return false;
+
+ const SvxFieldItem* pFieldItem = pData->GetField();
+ if (!pFieldItem)
+ // doesn't have a field item.
+ return false;
+
+ const SvxFieldData* pField = pFieldItem->GetField();
+ if (!pField)
+ // doesn't have a field item data.
+ return false;
+
+ if (pField->GetClassId() != css::text::textfield::Type::URL)
+ // not a URL field.
+ return false;
+
+ if (pContent)
+ {
+ const SvxURLField* pURLField = static_cast<const SvxURLField*>(pField);
+ pContent->SetName( pURLField->GetRepresentation() );
+ pContent->SetURL( pURLField->GetURL() );
+ pContent->SetTargetFrame( pURLField->GetTargetFrame() );
+ }
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/viewfun5.cxx b/sc/source/ui/view/viewfun5.cxx
new file mode 100644
index 0000000000..1098319506
--- /dev/null
+++ b/sc/source/ui/view/viewfun5.cxx
@@ -0,0 +1,818 @@
+/* -*- 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 .
+ */
+
+#include <com/sun/star/embed/XEmbedObjectClipboardCreator.hpp>
+#include <com/sun/star/embed/Aspects.hpp>
+#include <com/sun/star/embed/MSOLEObjectSystemCreator.hpp>
+
+#include <svx/unomodel.hxx>
+#include <unotools/streamwrap.hxx>
+
+#include <svx/fmmodel.hxx>
+#include <svx/svditer.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svdouno.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdpage.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/docfile.hxx>
+#include <comphelper/classids.hxx>
+#include <sot/formats.hxx>
+#include <sot/filelist.hxx>
+#include <sot/storage.hxx>
+#include <svl/stritem.hxx>
+#include <vcl/transfer.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/TypeSerializer.hxx>
+#include <osl/thread.h>
+#include <o3tl/unit_conversion.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <comphelper/automationinvokedzone.hxx>
+#include <comphelper/lok.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <comphelper/string.hxx>
+
+#include <viewfunc.hxx>
+#include <docsh.hxx>
+#include <drawview.hxx>
+#include <impex.hxx>
+#include <dbdata.hxx>
+#include <sc.hrc>
+#include <filter.hxx>
+#include <globstr.hrc>
+#include <global.hxx>
+#include <scextopt.hxx>
+#include <tabvwsh.hxx>
+#include <compiler.hxx>
+#include <scmod.hxx>
+
+#include <asciiopt.hxx>
+#include <scabstdlg.hxx>
+#include <clipparam.hxx>
+#include <markdata.hxx>
+#include <sfx2/frame.hxx>
+#include <svx/dbaexchange.hxx>
+#include <memory>
+
+using namespace com::sun::star;
+
+bool ScViewFunc::PasteDataFormat( SotClipboardFormatId nFormatId,
+ const uno::Reference<datatransfer::XTransferable>& rxTransferable,
+ SCCOL nPosX, SCROW nPosY, const Point* pLogicPos, bool bLink, bool bAllowDialogs )
+{
+ ScDocument& rDoc = GetViewData().GetDocument();
+ rDoc.SetPastingDrawFromOtherDoc( true );
+
+ Point aPos; // inserting position (1/100 mm)
+ if (pLogicPos)
+ aPos = *pLogicPos;
+ else
+ {
+ // inserting position isn't needed for text formats
+ bool bIsTextFormat = ( ScImportExport::IsFormatSupported( nFormatId ) ||
+ nFormatId == SotClipboardFormatId::RTF );
+ if ( !bIsTextFormat )
+ {
+ // Window MapMode isn't drawing MapMode if DrawingLayer hasn't been created yet
+
+ SCTAB nTab = GetViewData().GetTabNo();
+ tools::Long nXT = 0;
+ for (SCCOL i=0; i<nPosX; i++)
+ nXT += rDoc.GetColWidth(i,nTab);
+ if (rDoc.IsNegativePage(nTab))
+ nXT = -nXT;
+ tools::Long nYT = rDoc.GetRowHeight( 0, nPosY-1, nTab);
+ aPos = Point(o3tl::convert(nXT, o3tl::Length::twip, o3tl::Length::mm100),
+ o3tl::convert(nYT, o3tl::Length::twip, o3tl::Length::mm100));
+ }
+ }
+
+ TransferableDataHelper aDataHelper( rxTransferable );
+ bool bRet = false;
+
+ // handle individual formats
+
+ if ( nFormatId == SotClipboardFormatId::EMBED_SOURCE ||
+ nFormatId == SotClipboardFormatId::LINK_SOURCE ||
+ nFormatId == SotClipboardFormatId::EMBED_SOURCE_OLE ||
+ nFormatId == SotClipboardFormatId::LINK_SOURCE_OLE ||
+ nFormatId == SotClipboardFormatId::EMBEDDED_OBJ_OLE )
+ {
+ uno::Reference < io::XInputStream > xStm;
+ TransferableObjectDescriptor aObjDesc;
+
+ if (aDataHelper.GetTransferableObjectDescriptor(SotClipboardFormatId::OBJECTDESCRIPTOR, aObjDesc))
+ xStm = aDataHelper.GetInputStream(nFormatId, OUString());
+
+ if (xStm.is())
+ {
+ if ( aObjDesc.maClassName == SvGlobalName( SO3_SC_CLASSID_60 ) )
+ {
+ uno::Reference < embed::XStorage > xStore = ::comphelper::OStorageHelper::GetStorageFromInputStream( xStm );
+
+ // mba: BaseURL doesn't make sense for clipboard
+ // #i43716# Medium must be allocated with "new".
+ // DoLoad stores the pointer and deletes it with the SfxObjectShell.
+ SfxMedium* pMedium = new SfxMedium( xStore, OUString() );
+
+ // TODO/LATER: is it a problem that we don't support binary formats here?
+ ScDocShellRef xDocShRef = new ScDocShell(SfxModelFlags::EMBEDDED_OBJECT);
+ if (xDocShRef->DoLoad(pMedium))
+ {
+ ScDocument& rSrcDoc = xDocShRef->GetDocument();
+ SCTAB nSrcTab = rSrcDoc.GetVisibleTab();
+ if (!rSrcDoc.HasTable(nSrcTab))
+ nSrcTab = 0;
+
+ ScMarkData aSrcMark(rSrcDoc.GetSheetLimits());
+ aSrcMark.SelectOneTable( nSrcTab ); // for CopyToClip
+ ScDocumentUniquePtr pClipDoc(new ScDocument( SCDOCMODE_CLIP ));
+
+ SCCOL nFirstCol, nLastCol;
+ SCROW nFirstRow, nLastRow;
+ if ( rSrcDoc.GetDataStart( nSrcTab, nFirstCol, nFirstRow ) )
+ {
+ rSrcDoc.GetCellArea( nSrcTab, nLastCol, nLastRow );
+ if (nLastCol < nFirstCol)
+ nLastCol = nFirstCol;
+ if (nLastRow < nFirstRow)
+ nLastRow = nFirstRow;
+ }
+ else
+ {
+ nFirstCol = nLastCol = 0;
+ nFirstRow = nLastRow = 0;
+ }
+
+ bool bIncludeObjects = false; // include drawing layer objects in CopyToClip ?
+
+ if (nFormatId == SotClipboardFormatId::EMBED_SOURCE)
+ {
+ const ScDrawLayer* pDraw = rSrcDoc.GetDrawLayer();
+ SCCOL nPrintEndCol = nFirstCol;
+ SCROW nPrintEndRow = nFirstRow;
+ bool bHasObjects = pDraw && pDraw->HasObjects();
+ // Extend the range to include the drawing layer objects.
+ if (bHasObjects && rSrcDoc.GetPrintArea(nSrcTab, nPrintEndCol, nPrintEndRow, true))
+ {
+ nLastCol = std::max<SCCOL>(nLastCol, nPrintEndCol);
+ nLastRow = std::max<SCROW>(nLastRow, nPrintEndRow);
+ }
+
+ bIncludeObjects = bHasObjects;
+ }
+
+ ScClipParam aClipParam(ScRange(nFirstCol, nFirstRow, nSrcTab, nLastCol, nLastRow, nSrcTab), false);
+ rSrcDoc.CopyToClip(aClipParam, pClipDoc.get(), &aSrcMark, false, bIncludeObjects);
+ ScGlobal::SetClipDocName( xDocShRef->GetTitle( SFX_TITLE_FULLNAME ) );
+
+ SetCursor( nPosX, nPosY );
+ Unmark();
+ PasteFromClip( InsertDeleteFlags::ALL, pClipDoc.get(),
+ ScPasteFunc::NONE, false, false, false, INS_NONE, InsertDeleteFlags::NONE,
+ bAllowDialogs );
+ bRet = true;
+ }
+
+ xDocShRef->DoClose();
+ xDocShRef.clear();
+ }
+ else
+ {
+ OUString aName;
+ uno::Reference < embed::XEmbeddedObject > xObj = GetViewData().GetViewShell()->GetObjectShell()->
+ GetEmbeddedObjectContainer().InsertEmbeddedObject( xStm, aName );
+ if ( xObj.is() )
+ {
+ // try to get the replacement image from the clipboard
+ Graphic aGraphic;
+ SotClipboardFormatId nGrFormat = SotClipboardFormatId::NONE;
+
+ // limit the size of the preview metafile to 100000 actions
+ GDIMetaFile aMetafile;
+ if (aDataHelper.GetGDIMetaFile(SotClipboardFormatId::GDIMETAFILE, aMetafile, 100000))
+ {
+ nGrFormat = SotClipboardFormatId::GDIMETAFILE;
+ aGraphic = aMetafile;
+ }
+
+ // insert replacement image ( if there is one ) into the object helper
+ if ( nGrFormat != SotClipboardFormatId::NONE )
+ {
+ datatransfer::DataFlavor aDataFlavor;
+ SotExchange::GetFormatDataFlavor( nGrFormat, aDataFlavor );
+ PasteObject( aPos, xObj, &aObjDesc.maSize, &aGraphic, aDataFlavor.MimeType, aObjDesc.mnViewAspect );
+ }
+ else
+ PasteObject( aPos, xObj, &aObjDesc.maSize );
+
+ bRet = true;
+ }
+ else
+ {
+ OSL_FAIL("Error in CreateAndLoad");
+ }
+ }
+ }
+ else
+ {
+ if ( aDataHelper.GetTransferableObjectDescriptor( SotClipboardFormatId::OBJECTDESCRIPTOR_OLE, aObjDesc ) )
+ {
+ OUString aName;
+ uno::Reference < embed::XEmbeddedObject > xObj;
+ xStm = aDataHelper.GetInputStream(SotClipboardFormatId::EMBED_SOURCE_OLE, OUString());
+ if (!xStm.is())
+ aDataHelper.GetInputStream(SotClipboardFormatId::EMBEDDED_OBJ_OLE, OUString());
+
+ if (xStm.is())
+ {
+ xObj = GetViewData().GetDocShell()->GetEmbeddedObjectContainer().InsertEmbeddedObject( xStm, aName );
+ }
+ else
+ {
+ try
+ {
+ uno::Reference< embed::XStorage > xTmpStor = ::comphelper::OStorageHelper::GetTemporaryStorage();
+ uno::Reference < embed::XEmbedObjectClipboardCreator > xClipboardCreator =
+ embed::MSOLEObjectSystemCreator::create( ::comphelper::getProcessComponentContext() );
+
+ embed::InsertedObjectInfo aInfo = xClipboardCreator->createInstanceInitFromClipboard(
+ xTmpStor,
+ "DummyName",
+ uno::Sequence< beans::PropertyValue >() );
+
+ // TODO/LATER: in future InsertedObjectInfo will be used to get container related information
+ // for example whether the object should be an iconified one
+ xObj = aInfo.Object;
+ if ( xObj.is() )
+ GetViewData().GetDocShell()->GetEmbeddedObjectContainer().InsertEmbeddedObject( xObj, aName );
+ }
+ catch( uno::Exception& )
+ {}
+ }
+
+ if ( xObj.is() )
+ {
+ // try to get the replacement image from the clipboard
+ Graphic aGraphic;
+ SotClipboardFormatId nGrFormat = SotClipboardFormatId::NONE;
+
+// (for Selection Manager in Trusted Solaris)
+#ifndef __sun
+ if( aDataHelper.GetGraphic( SotClipboardFormatId::SVXB, aGraphic ) )
+ nGrFormat = SotClipboardFormatId::SVXB;
+ else if( aDataHelper.GetGraphic( SotClipboardFormatId::GDIMETAFILE, aGraphic ) )
+ nGrFormat = SotClipboardFormatId::GDIMETAFILE;
+ else if( aDataHelper.GetGraphic( SotClipboardFormatId::BITMAP, aGraphic ) )
+ nGrFormat = SotClipboardFormatId::BITMAP;
+#endif
+
+ // insert replacement image ( if there is one ) into the object helper
+ if ( nGrFormat != SotClipboardFormatId::NONE )
+ {
+ datatransfer::DataFlavor aDataFlavor;
+ SotExchange::GetFormatDataFlavor( nGrFormat, aDataFlavor );
+ PasteObject( aPos, xObj, &aObjDesc.maSize, &aGraphic, aDataFlavor.MimeType, aObjDesc.mnViewAspect );
+ }
+ else
+ PasteObject( aPos, xObj, &aObjDesc.maSize );
+
+ // let object stay in loaded state after insertion
+ SdrOle2Obj::Unload( xObj, embed::Aspects::MSOLE_CONTENT );
+ bRet = true;
+ }
+ else
+ {
+ OSL_FAIL("Error creating external OLE object");
+ }
+ }
+ //TODO/LATER: if format is not available, create picture
+ }
+ }
+ else if ( nFormatId == SotClipboardFormatId::LINK ) // LINK is also in ScImportExport
+ {
+ bRet = PasteLink( rxTransferable );
+ }
+ else if ( ScImportExport::IsFormatSupported( nFormatId ) || nFormatId == SotClipboardFormatId::RTF ||
+ nFormatId == SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT )
+ {
+ if ( nFormatId == SotClipboardFormatId::RTF && ( aDataHelper.HasFormat( SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT ) ) )
+ {
+ // use EditView's PasteSpecial / Drop
+ PasteRTF( nPosX, nPosY, rxTransferable );
+ bRet = true;
+ }
+ else
+ {
+ ScAddress aCellPos( nPosX, nPosY, GetViewData().GetTabNo() );
+ auto pObj = std::make_shared<ScImportExport>(GetViewData().GetDocument(), aCellPos);
+ pObj->SetOverwriting( true );
+
+
+ auto pStrBuffer = std::make_shared<OUString>();
+ tools::SvRef<SotTempStream> xStream;
+ if ( aDataHelper.GetSotStorageStream( nFormatId, xStream ) && xStream.is() )
+ {
+ // Static variables for per-session storage. This could be
+ // changed to longer-term storage in future.
+ static bool bHaveSavedPreferences = false;
+ static LanguageType eSavedLanguage;
+ static bool bSavedDateConversion;
+ static bool bSavedScientificConversion;
+
+ if (nFormatId == SotClipboardFormatId::HTML &&
+ !comphelper::LibreOfficeKit::isActive())
+ {
+ if (bHaveSavedPreferences)
+ {
+ ScAsciiOptions aOptions;
+ aOptions.SetLanguage(eSavedLanguage);
+ aOptions.SetDetectSpecialNumber(bSavedDateConversion);
+ aOptions.SetDetectScientificNumber(bSavedScientificConversion);
+ pObj->SetExtOptions(aOptions);
+ }
+ else
+ {
+ // Launch the text import options dialog. For now, we do
+ // this for html pasting only, but in the future it may
+ // make sense to do it for other data types too.
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+ vcl::Window* pParent = GetActiveWin();
+ ScopedVclPtr<AbstractScTextImportOptionsDlg> pDlg(
+ pFact->CreateScTextImportOptionsDlg(pParent ? pParent->GetFrameWeld() : nullptr));
+
+ if (pDlg->Execute() == RET_OK)
+ {
+ ScAsciiOptions aOptions;
+ aOptions.SetLanguage(pDlg->GetLanguageType());
+ aOptions.SetDetectSpecialNumber(pDlg->IsDateConversionSet());
+ aOptions.SetDetectScientificNumber(pDlg->IsScientificConversionSet());
+ if (!pDlg->IsKeepAskingSet())
+ {
+ bHaveSavedPreferences = true;
+ eSavedLanguage = pDlg->GetLanguageType();
+ bSavedDateConversion = pDlg->IsDateConversionSet();
+ bSavedScientificConversion = pDlg->IsScientificConversionSet();
+ }
+ pObj->SetExtOptions(aOptions);
+ }
+ else
+ {
+ // prevent error dialog for user cancel action
+ bRet = true;
+ }
+ }
+ }
+ if(!bRet)
+ bRet = pObj->ImportStream( *xStream, OUString(), nFormatId );
+ // mba: clipboard always must contain absolute URLs (could be from alien source)
+ }
+ else if ((nFormatId == SotClipboardFormatId::STRING || nFormatId == SotClipboardFormatId::STRING_TSVC)
+ && aDataHelper.GetString( nFormatId, *pStrBuffer ))
+ {
+ // Do CSV dialog if more than one line. But not if invoked from Automation.
+ const SfxViewShell* pViewShell = SfxViewShell::Current();
+ sal_Int32 nDelim = pStrBuffer->indexOf('\n');
+ if (!(pViewShell && pViewShell->isLOKMobilePhone()) && !comphelper::Automation::AutomationInvokedZone::isActive()
+ && nDelim >= 0 && nDelim != pStrBuffer->getLength () - 1)
+ {
+ vcl::Window* pParent = GetActiveWin();
+
+ auto pStrm = std::make_shared<ScImportStringStream>(*pStrBuffer);
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+ VclPtr<AbstractScImportAsciiDlg> pDlg(
+ pFact->CreateScImportAsciiDlg(pParent ? pParent->GetFrameWeld() : nullptr, OUString(), pStrm.get(), SC_PASTETEXT));
+
+ bAllowDialogs = bAllowDialogs && !SC_MOD()->IsInExecuteDrop();
+
+ pDlg->StartExecuteAsync([this, pDlg, &rDoc, pStrm, nFormatId, pStrBuffer, pObj, bAllowDialogs](sal_Int32 nResult){
+ bool bShowErrorDialog = bAllowDialogs;
+ if (RET_OK == nResult)
+ {
+ ScAsciiOptions aOptions;
+ pDlg->GetOptions( aOptions );
+ pDlg->SaveParameters();
+ pObj->SetExtOptions( aOptions );
+ pObj->ImportString( *pStrBuffer, nFormatId );
+
+ // TODO: what if (aObj.IsOverflow())
+ // Content was partially pasted, which can be undone by
+ // the user though.
+ bShowErrorDialog = bShowErrorDialog && pObj->IsOverflow();
+ }
+ else
+ {
+ bShowErrorDialog = false;
+ // Yes, no failure, don't raise a "couldn't paste"
+ // dialog if user cancelled.
+ }
+
+ InvalidateAttribs();
+ GetViewData().UpdateInputHandler();
+
+ rDoc.SetPastingDrawFromOtherDoc( false );
+
+ if (bShowErrorDialog)
+ ErrorMessage(STR_PASTE_ERROR);
+ pDlg->disposeOnce();
+ });
+ return true;
+ }
+ else
+ bRet = pObj->ImportString( *pStrBuffer, nFormatId );
+ }
+ else if ((nFormatId != SotClipboardFormatId::STRING && nFormatId != SotClipboardFormatId::STRING_TSVC)
+ && aDataHelper.GetString( nFormatId, *pStrBuffer ))
+ bRet = pObj->ImportString( *pStrBuffer, nFormatId );
+
+ InvalidateAttribs();
+ GetViewData().UpdateInputHandler();
+ }
+ }
+ else if (nFormatId == SotClipboardFormatId::SBA_DATAEXCHANGE)
+ {
+ // import of database data into table
+
+ const DataFlavorExVector& rVector = aDataHelper.GetDataFlavorExVector();
+ if ( svx::ODataAccessObjectTransferable::canExtractObjectDescriptor(rVector) )
+ {
+ // transport the whole ODataAccessDescriptor as slot parameter
+ svx::ODataAccessDescriptor aDesc = svx::ODataAccessObjectTransferable::extractObjectDescriptor(aDataHelper);
+ uno::Any aDescAny;
+ uno::Sequence<beans::PropertyValue> aProperties = aDesc.createPropertyValueSequence();
+ aDescAny <<= aProperties;
+ SfxUnoAnyItem aDataDesc(SID_SBA_IMPORT, aDescAny);
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ SCTAB nTab = GetViewData().GetTabNo();
+
+ ClickCursor(nPosX, nPosY, false); // set cursor position
+
+ // Creation of database area "Import1" isn't here, but in the DocShell
+ // slot execute, so it can be added to the undo action
+
+ ScDBData* pDBData = pDocSh->GetDBData( ScRange(nPosX,nPosY,nTab), SC_DB_OLD, ScGetDBSelection::Keep );
+ OUString sTarget;
+ if (pDBData)
+ sTarget = pDBData->GetName();
+ else
+ {
+ ScAddress aCellPos( nPosX,nPosY,nTab );
+ sTarget = aCellPos.Format(ScRefFlags::ADDR_ABS_3D, &rDoc, rDoc.GetAddressConvention());
+ }
+ SfxStringItem aTarget(FN_PARAM_1, sTarget);
+
+ bool bAreaIsNew = !pDBData;
+ SfxBoolItem aAreaNew(FN_PARAM_2, bAreaIsNew);
+
+ // asynchronous, to avoid doing the whole import in drop handler
+ SfxDispatcher& rDisp = GetViewData().GetDispatcher();
+ rDisp.ExecuteList(SID_SBA_IMPORT, SfxCallMode::ASYNCHRON,
+ { &aDataDesc, &aTarget, &aAreaNew });
+
+ bRet = true;
+ }
+ }
+ else if (nFormatId == SotClipboardFormatId::SBA_FIELDDATAEXCHANGE)
+ {
+ // insert database field control
+
+ if ( svx::OColumnTransferable::canExtractColumnDescriptor( aDataHelper.GetDataFlavorExVector(), ColumnTransferFormatFlags::COLUMN_DESCRIPTOR | ColumnTransferFormatFlags::CONTROL_EXCHANGE ) )
+ {
+ MakeDrawLayer();
+ ScDrawView* pScDrawView = GetScDrawView();
+ rtl::Reference<SdrObject> pObj = pScDrawView->CreateFieldControl( svx::OColumnTransferable::extractColumnDescriptor( aDataHelper ) );
+ if (pObj)
+ {
+ Point aInsPos = aPos;
+ tools::Rectangle aRect(pObj->GetLogicRect());
+ aInsPos.AdjustX( -(aRect.GetSize().Width() / 2) );
+ aInsPos.AdjustY( -(aRect.GetSize().Height() / 2) );
+ if ( aInsPos.X() < 0 ) aInsPos.setX( 0 );
+ if ( aInsPos.Y() < 0 ) aInsPos.setY( 0 );
+ aRect.SetPos(aInsPos);
+ pObj->SetLogicRect(aRect);
+
+ if ( dynamic_cast<const SdrUnoObj*>( pObj.get() ) != nullptr )
+ pObj->NbcSetLayer(SC_LAYER_CONTROLS);
+ else
+ pObj->NbcSetLayer(SC_LAYER_FRONT);
+ if (dynamic_cast<const SdrObjGroup*>( pObj.get() ) != nullptr)
+ {
+ SdrObjListIter aIter( *pObj, SdrIterMode::DeepWithGroups );
+ SdrObject* pSubObj = aIter.Next();
+ while (pSubObj)
+ {
+ if ( dynamic_cast<const SdrUnoObj*>( pSubObj) != nullptr )
+ pSubObj->NbcSetLayer(SC_LAYER_CONTROLS);
+ else
+ pSubObj->NbcSetLayer(SC_LAYER_FRONT);
+ pSubObj = aIter.Next();
+ }
+ }
+
+ pScDrawView->InsertObjectSafe(pObj.get(), *pScDrawView->GetSdrPageView());
+
+ GetViewData().GetViewShell()->SetDrawShell( true );
+ bRet = true;
+ }
+ }
+ }
+ else if (nFormatId == SotClipboardFormatId::BITMAP || nFormatId == SotClipboardFormatId::PNG || nFormatId == SotClipboardFormatId::JPEG)
+ {
+ BitmapEx aBmpEx;
+ if( aDataHelper.GetBitmapEx( SotClipboardFormatId::BITMAP, aBmpEx ) )
+ bRet = PasteBitmapEx( aPos, aBmpEx );
+ }
+ else if (nFormatId == SotClipboardFormatId::GDIMETAFILE)
+ {
+ GDIMetaFile aMtf;
+ if( aDataHelper.GetGDIMetaFile( SotClipboardFormatId::GDIMETAFILE, aMtf ) )
+ bRet = PasteMetaFile( aPos, aMtf );
+ }
+ else if (nFormatId == SotClipboardFormatId::SVXB)
+ {
+ tools::SvRef<SotTempStream> xStm;
+ if( aDataHelper.GetSotStorageStream( SotClipboardFormatId::SVXB, xStm ) )
+ {
+ Graphic aGraphic;
+ TypeSerializer aSerializer(*xStm);
+ aSerializer.readGraphic(aGraphic);
+ bRet = PasteGraphic( aPos, aGraphic, OUString() );
+ }
+ }
+ else if ( nFormatId == SotClipboardFormatId::DRAWING )
+ {
+ tools::SvRef<SotTempStream> xStm;
+ if( aDataHelper.GetSotStorageStream( SotClipboardFormatId::DRAWING, xStm ) )
+ {
+ MakeDrawLayer(); // before loading model, so 3D factory has been created
+
+ ScDocShellRef aDragShellRef( new ScDocShell );
+ aDragShellRef->MakeDrawLayer();
+ aDragShellRef->DoInitNew();
+
+ ScDrawLayer* pModel = aDragShellRef->GetDocument().GetDrawLayer();
+
+ xStm->Seek(0);
+
+ css::uno::Reference< css::io::XInputStream > xInputStream( new utl::OInputStreamWrapper( *xStm ) );
+ SvxDrawingLayerImport( pModel, xInputStream );
+
+ // set everything to right layer:
+ size_t nObjCount = 0;
+ sal_uInt16 nPages = pModel->GetPageCount();
+ for (sal_uInt16 i=0; i<nPages; i++)
+ {
+ SdrPage* pPage = pModel->GetPage(i);
+ SdrObjListIter aIter( pPage, SdrIterMode::DeepWithGroups );
+ SdrObject* pObject = aIter.Next();
+ while (pObject)
+ {
+ if ( dynamic_cast<const SdrUnoObj*>( pObject) != nullptr )
+ pObject->NbcSetLayer(SC_LAYER_CONTROLS);
+ else
+ pObject->NbcSetLayer(SC_LAYER_FRONT);
+ pObject = aIter.Next();
+ }
+
+ nObjCount += pPage->GetObjCount(); // count group object only once
+ }
+
+ PasteDraw(aPos, pModel, (nObjCount > 1), u"A", u"B"); // grouped if more than 1 object
+ aDragShellRef->DoClose();
+ bRet = true;
+ }
+ }
+ else if ( (nFormatId == SotClipboardFormatId::BIFF_5) || (nFormatId == SotClipboardFormatId::BIFF_8) )
+ {
+ // do excel import into a clipboard document
+ //TODO/MBA: testing
+ uno::Reference <io::XInputStream> xStm = aDataHelper.GetInputStream(nFormatId, OUString());
+ if (xStm.is())
+ {
+ ScDocument aInsDoc( SCDOCMODE_CLIP );
+ SCTAB nSrcTab = 0; // Biff5 in clipboard: always sheet 0
+ aInsDoc.ResetClip( &rDoc, nSrcTab );
+
+ SfxMedium aMed;
+ aMed.GetItemSet().Put( SfxUnoAnyItem( SID_INPUTSTREAM, uno::Any( xStm ) ) );
+ ErrCode eErr = ScFormatFilter::Get().ScImportExcel( aMed, &aInsDoc, EIF_AUTO );
+ if ( eErr == ERRCODE_NONE )
+ {
+ ScRange aSource;
+ const ScExtDocOptions* pExtOpt = aInsDoc.GetExtDocOptions();
+ const ScExtTabSettings* pTabSett = pExtOpt ? pExtOpt->GetTabSettings( nSrcTab ) : nullptr;
+ if( pTabSett && pTabSett->maUsedArea.IsValid() )
+ {
+ aSource = pTabSett->maUsedArea;
+ // ensure correct sheet indexes
+ aSource.aStart.SetTab( nSrcTab );
+ aSource.aEnd.SetTab( nSrcTab );
+// don't use selection area: if cursor is moved in Excel after Copy, selection
+// represents the new cursor position and not the copied area
+ }
+ else
+ {
+ OSL_FAIL("no dimension"); //! possible?
+ SCCOL nFirstCol, nLastCol;
+ SCROW nFirstRow, nLastRow;
+ if ( aInsDoc.GetDataStart( nSrcTab, nFirstCol, nFirstRow ) )
+ aInsDoc.GetCellArea( nSrcTab, nLastCol, nLastRow );
+ else
+ {
+ nFirstCol = nLastCol = 0;
+ nFirstRow = nLastRow = 0;
+ }
+ aSource = ScRange( nFirstCol, nFirstRow, nSrcTab,
+ nLastCol, nLastRow, nSrcTab );
+ }
+
+ if ( pLogicPos )
+ {
+ // position specified (Drag&Drop) - change selection
+ MoveCursorAbs( nPosX, nPosY, SC_FOLLOW_NONE, false, false );
+ Unmark();
+ }
+
+ aInsDoc.SetClipArea( aSource );
+ PasteFromClip( InsertDeleteFlags::ALL, &aInsDoc,
+ ScPasteFunc::NONE, false, false, false, INS_NONE, InsertDeleteFlags::NONE,
+ bAllowDialogs );
+ bRet = true;
+ }
+ }
+ }
+ else if ( nFormatId == SotClipboardFormatId::SIMPLE_FILE )
+ {
+ OUString aFile;
+ if ( aDataHelper.GetString( nFormatId, aFile ) )
+ bRet = PasteFile( aPos, aFile, bLink );
+ }
+ else if ( nFormatId == SotClipboardFormatId::FILE_LIST )
+ {
+ FileList aFileList;
+ if ( aDataHelper.GetFileList( nFormatId, aFileList ) )
+ {
+ sal_uLong nCount = aFileList.Count();
+ for( sal_uLong i = 0; i < nCount ; i++ )
+ {
+ OUString aFile = aFileList.GetFile( i );
+
+ PasteFile( aPos, aFile, bLink );
+
+ aPos.AdjustX(400 );
+ aPos.AdjustY(400 );
+ }
+ bRet = true;
+ }
+ }
+ else if ( nFormatId == SotClipboardFormatId::SOLK ||
+ nFormatId == SotClipboardFormatId::UNIFORMRESOURCELOCATOR ||
+ nFormatId == SotClipboardFormatId::NETSCAPE_BOOKMARK ||
+ nFormatId == SotClipboardFormatId::FILEGRPDESCRIPTOR )
+ {
+ bRet = PasteBookmark( nFormatId, rxTransferable, nPosX, nPosY );
+ }
+
+ rDoc.SetPastingDrawFromOtherDoc( false );
+
+ return bRet;
+}
+
+bool ScViewFunc::PasteLink( const uno::Reference<datatransfer::XTransferable>& rxTransferable )
+{
+ TransferableDataHelper aDataHelper( rxTransferable );
+
+ // get link data from transferable before string data,
+ // so the source knows it will be used for a link
+
+ uno::Sequence<sal_Int8> aSequence = aDataHelper.GetSequence(SotClipboardFormatId::LINK, OUString());
+ if (!aSequence.hasElements())
+ {
+ OSL_FAIL("DDE Data not found.");
+ return false;
+ }
+
+ // check size (only if string is available in transferable)
+
+ sal_uInt16 nCols = 1;
+ sal_uInt16 nRows = 1;
+ if ( aDataHelper.HasFormat( SotClipboardFormatId::STRING ) )
+ {
+ OUString aDataStr;
+ if ( aDataHelper.GetString( SotClipboardFormatId::STRING, aDataStr ) )
+ {
+ // get size from string the same way as in ScDdeLink::DataChanged
+
+ aDataStr = convertLineEnd(aDataStr, LINEEND_LF);
+ sal_Int32 nLen = aDataStr.getLength();
+ if (nLen && aDataStr[nLen-1] == '\n')
+ aDataStr = aDataStr.copy(0, nLen-1);
+
+ if (!aDataStr.isEmpty())
+ {
+ nRows = comphelper::string::getTokenCount(aDataStr, '\n');
+ std::u16string_view aLine = o3tl::getToken(aDataStr, 0, '\n' );
+ if (!aLine.empty())
+ nCols = comphelper::string::getTokenCount(aLine, '\t');
+ }
+ }
+ }
+
+ // create formula
+
+ sal_Int32 nSeqLen = aSequence.getLength();
+ const char* p = reinterpret_cast<const char*>(aSequence.getConstArray());
+
+ rtl_TextEncoding eSysEnc = osl_getThreadTextEncoding();
+
+ // char array delimited by \0.
+ // app \0 topic \0 item \0 (extra \0) where the extra is optional.
+ ::std::vector<OUString> aStrs;
+ const char* pStart = p;
+ sal_Int32 nStart = 0;
+ for (sal_Int32 i = 0; i < nSeqLen; ++i, ++p)
+ {
+ if (*p == '\0')
+ {
+ sal_Int32 nLen = i - nStart;
+ aStrs.emplace_back(pStart, nLen, eSysEnc);
+ nStart = ++i;
+ pStart = ++p;
+ }
+ }
+
+ if (aStrs.size() < 3)
+ return false;
+
+ const OUString& pApp = aStrs[0];
+ const OUString& pTopic = aStrs[1];
+ const OUString& pItem = aStrs[2];
+ const OUString* pExtra = nullptr;
+ if (aStrs.size() > 3)
+ pExtra = &aStrs[3];
+
+ if ( pExtra && *pExtra == "calc:extref" )
+ {
+ // Paste this as an external reference. Note that paste link always
+ // uses Calc A1 syntax even when another formula syntax is specified
+ // in the UI.
+ EnterMatrix("='"
+ + ScGlobal::GetAbsDocName(pTopic, GetViewData().GetDocument().GetDocumentShell())
+ + "'#" + pItem
+ , ::formula::FormulaGrammar::GRAM_NATIVE);
+ return true;
+ }
+ else
+ {
+ // DDE in all other cases.
+
+ // TODO: we could define ocQuote for "
+ EnterMatrix("=" + ScCompiler::GetNativeSymbol(ocDde)
+ + ScCompiler::GetNativeSymbol(ocOpen)
+ + "\"" + pApp + "\""
+ + ScCompiler::GetNativeSymbol(ocSep)
+ + "\"" + pTopic + "\""
+ + ScCompiler::GetNativeSymbol(ocSep)
+ + "\"" + pItem + "\""
+ + ScCompiler::GetNativeSymbol(ocClose)
+ , ::formula::FormulaGrammar::GRAM_NATIVE);
+ }
+
+ // mark range
+
+ SCTAB nTab = GetViewData().GetTabNo();
+ SCCOL nCurX = GetViewData().GetCurX();
+ SCROW nCurY = GetViewData().GetCurY();
+ HideAllCursors();
+ DoneBlockMode();
+ InitBlockMode( nCurX, nCurY, nTab );
+ MarkCursor( nCurX+static_cast<SCCOL>(nCols)-1, nCurY+static_cast<SCROW>(nRows)-1, nTab );
+ ShowAllCursors();
+ CursorPosChanged();
+
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/viewfun6.cxx b/sc/source/ui/view/viewfun6.cxx
new file mode 100644
index 0000000000..a840670dfc
--- /dev/null
+++ b/sc/source/ui/view/viewfun6.cxx
@@ -0,0 +1,559 @@
+/* -*- 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 .
+ */
+
+#include <formula/token.hxx>
+#include <svx/svdocapt.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <svl/stritem.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <svl/zformat.hxx>
+#include <vcl/uitest/logger.hxx>
+#include <vcl/uitest/eventdescription.hxx>
+#include <editeng/editview.hxx>
+#include <rtl/math.hxx>
+#include <sal/log.hxx>
+
+#include <viewfunc.hxx>
+#include <viewdata.hxx>
+#include <drwlayer.hxx>
+#include <docsh.hxx>
+#include <futext.hxx>
+#include <docfunc.hxx>
+#include <sc.hrc>
+#include <fusel.hxx>
+#include <reftokenhelper.hxx>
+#include <externalrefmgr.hxx>
+#include <markdata.hxx>
+#include <drawview.hxx>
+#include <inputhdl.hxx>
+#include <tabvwsh.hxx>
+#include <scmod.hxx>
+#include <postit.hxx>
+#include <comphelper/scopeguard.hxx>
+
+#include <vector>
+
+namespace
+{
+
+void collectUIInformation( const OUString& aevent )
+{
+ EventDescription aDescription;
+ aDescription.aID = "grid_window";
+ aDescription.aParameters = {{ aevent , ""}};
+ aDescription.aAction = "COMMENT";
+ aDescription.aParent = "MainWindow";
+ aDescription.aKeyWord = "ScGridWinUIObject";
+ UITestLogger::getInstance().logEvent(aDescription);
+}
+
+}
+
+using ::std::vector;
+
+void ScViewFunc::DetectiveAddPred()
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ pDocSh->GetDocFunc().DetectiveAddPred( GetViewData().GetCurPos() );
+ RecalcPPT(); //! use broadcast in DocFunc instead?
+}
+
+void ScViewFunc::DetectiveDelPred()
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ pDocSh->GetDocFunc().DetectiveDelPred( GetViewData().GetCurPos() );
+ RecalcPPT();
+}
+
+void ScViewFunc::DetectiveAddSucc()
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ pDocSh->GetDocFunc().DetectiveAddSucc( GetViewData().GetCurPos() );
+ RecalcPPT();
+}
+
+void ScViewFunc::DetectiveDelSucc()
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ pDocSh->GetDocFunc().DetectiveDelSucc( GetViewData().GetCurPos() );
+ RecalcPPT();
+}
+
+void ScViewFunc::DetectiveAddError()
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ pDocSh->GetDocFunc().DetectiveAddError( GetViewData().GetCurPos() );
+ RecalcPPT();
+}
+
+void ScViewFunc::DetectiveDelAll()
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ pDocSh->GetDocFunc().DetectiveDelAll( GetViewData().GetTabNo() );
+ RecalcPPT();
+}
+
+void ScViewFunc::DetectiveMarkInvalid()
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ pDocSh->GetDocFunc().DetectiveMarkInvalid( GetViewData().GetTabNo() );
+ RecalcPPT();
+}
+
+void ScViewFunc::DetectiveRefresh()
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ pDocSh->GetDocFunc().DetectiveRefresh();
+ RecalcPPT();
+}
+
+static void lcl_jumpToRange(const ScRange& rRange, ScViewData* pView, const ScDocument& rDoc)
+{
+ OUString aAddrText(rRange.Format(rDoc, ScRefFlags::RANGE_ABS_3D));
+ SfxStringItem aPosItem(SID_CURRENTCELL, aAddrText);
+ SfxBoolItem aUnmarkItem(FN_PARAM_1, true); // remove existing selection
+ pView->GetDispatcher().ExecuteList(
+ SID_CURRENTCELL, SfxCallMode::SYNCHRON | SfxCallMode::RECORD,
+ { &aPosItem, &aUnmarkItem });
+}
+
+void ScViewFunc::MarkAndJumpToRanges(const ScRangeList& rRanges)
+{
+ ScViewData& rView = GetViewData();
+ ScDocShell* pDocSh = rView.GetDocShell();
+
+ ScRangeList aRanges(rRanges);
+ ScRangeList aRangesToMark;
+ ScAddress aCurPos = rView.GetCurPos();
+ size_t ListSize = aRanges.size();
+ for ( size_t i = 0; i < ListSize; ++i )
+ {
+ const ScRange & r = aRanges[i];
+ // Collect only those ranges that are on the same sheet as the current
+ // cursor.
+ if (r.aStart.Tab() == aCurPos.Tab())
+ aRangesToMark.push_back(r);
+ }
+
+ if (aRangesToMark.empty())
+ return;
+
+ // Jump to the first range of all precedent ranges.
+ const ScRange & r = aRangesToMark.front();
+ lcl_jumpToRange(r, &rView, pDocSh->GetDocument());
+
+ ListSize = aRangesToMark.size();
+ for ( size_t i = 0; i < ListSize; ++i )
+ {
+ MarkRange(aRangesToMark[i], false, true);
+ }
+}
+
+void ScViewFunc::DetectiveMarkPred()
+{
+ ScViewData& rView = GetViewData();
+ ScDocShell* pDocSh = rView.GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScMarkData& rMarkData = rView.GetMarkData();
+ ScAddress aCurPos = rView.GetCurPos();
+ ScRangeList aRanges;
+ if (rMarkData.IsMarked() || rMarkData.IsMultiMarked())
+ rMarkData.FillRangeListWithMarks(&aRanges, false);
+ else
+ aRanges.push_back(aCurPos);
+
+ vector<ScTokenRef> aRefTokens;
+ pDocSh->GetDocFunc().DetectiveCollectAllPreds(aRanges, aRefTokens);
+
+ if (aRefTokens.empty())
+ // No precedents found. Nothing to do.
+ return;
+
+ ScTokenRef p = aRefTokens.front();
+ if (ScRefTokenHelper::isExternalRef(p))
+ {
+ // This is external. Open the external document if available, and
+ // jump to the destination.
+
+ sal_uInt16 nFileId = p->GetIndex();
+ ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
+ const OUString* pPath = pRefMgr->getExternalFileName(nFileId);
+
+ ScRange aRange;
+ if (pPath && ScRefTokenHelper::getRangeFromToken(&rDoc, aRange, p, aCurPos, true))
+ {
+ OUString aTabName = p->GetString().getString();
+ OUString aRangeStr(aRange.Format(rDoc, ScRefFlags::VALID));
+ OUString sUrl =
+ *pPath +
+ "#" +
+ aTabName +
+ "." +
+ aRangeStr;
+
+ ScGlobal::OpenURL(sUrl, OUString());
+ }
+ return;
+ }
+ else
+ {
+ ScRange aRange;
+ ScRefTokenHelper::getRangeFromToken(&rDoc, aRange, p, aCurPos);
+ if (aRange.aStart.Tab() != aCurPos.Tab())
+ {
+ // The first precedent range is on a different sheet. Jump to it
+ // immediately and forget the rest.
+ lcl_jumpToRange(aRange, &rView, rDoc);
+ return;
+ }
+ }
+
+ ScRangeList aDestRanges;
+ ScRefTokenHelper::getRangeListFromTokens(&rDoc, aDestRanges, aRefTokens, aCurPos);
+ MarkAndJumpToRanges(aDestRanges);
+}
+
+void ScViewFunc::DetectiveMarkSucc()
+{
+ ScViewData& rView = GetViewData();
+ ScDocShell* pDocSh = rView.GetDocShell();
+ ScMarkData& rMarkData = rView.GetMarkData();
+ ScAddress aCurPos = rView.GetCurPos();
+ ScRangeList aRanges;
+ if (rMarkData.IsMarked() || rMarkData.IsMultiMarked())
+ rMarkData.FillRangeListWithMarks(&aRanges, false);
+ else
+ aRanges.push_back(aCurPos);
+
+ vector<ScTokenRef> aRefTokens;
+ pDocSh->GetDocFunc().DetectiveCollectAllSuccs(aRanges, aRefTokens);
+
+ if (aRefTokens.empty())
+ // No dependents found. Nothing to do.
+ return;
+
+ ScRangeList aDestRanges;
+ ScRefTokenHelper::getRangeListFromTokens(&rView.GetDocument(), aDestRanges, aRefTokens, aCurPos);
+ MarkAndJumpToRanges(aDestRanges);
+}
+
+/** Insert date or time into current cell.
+
+ If cell is in input or edit mode, insert date/time at cursor position, else
+ create a date or time or date+time cell as follows:
+
+ - key date on time cell => current date + time of cell => date+time formatted cell
+ - unless time cell was empty or 00:00 time => current date => date formatted cell
+ - key date on date+time cell => current date + 00:00 time => date+time formatted cell
+ - unless date was current date => current date => date formatted cell
+ - key date on other cell => current date => date formatted cell
+ - key time on date cell => date of cell + current time => date+time formatted cell
+ - unless date cell was empty => current time => time formatted cell
+ - key time on date+time cell => current time => time formatted cell
+ - unless cell was empty => current date+time => date+time formatted cell
+ - key time on other cell => current time => time formatted cell
+ */
+void ScViewFunc::InsertCurrentTime(SvNumFormatType nReqFmt, const OUString& rUndoStr)
+{
+ ScViewData& rViewData = GetViewData();
+
+ ScInputHandler* pInputHdl = SC_MOD()->GetInputHdl( rViewData.GetViewShell());
+ bool bInputMode = (pInputHdl && pInputHdl->IsInputMode());
+
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScAddress aCurPos = rViewData.GetCurPos();
+ const sal_uInt32 nCurNumFormat = rDoc.GetNumberFormat(aCurPos);
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ const SvNumberformat* pCurNumFormatEntry = pFormatter->GetEntry(nCurNumFormat);
+ const SvNumFormatType nCurNumFormatType = (pCurNumFormatEntry ?
+ pCurNumFormatEntry->GetMaskedType() : SvNumFormatType::UNDEFINED);
+
+ const int nView(comphelper::LibreOfficeKit::isActive() ? SfxLokHelper::getView() : -1);
+ if (nView >= 0)
+ {
+ const auto [isTimezoneSet, aTimezone] = SfxLokHelper::getViewTimezone(nView);
+ comphelper::LibreOfficeKit::setTimezone(isTimezoneSet, aTimezone);
+ }
+
+ comphelper::ScopeGuard aAutoUserTimezone(
+ [nView]()
+ {
+ if (nView >= 0)
+ {
+ const auto [isTimezoneSet, aTimezone] = SfxLokHelper::getDefaultTimezone();
+ comphelper::LibreOfficeKit::setTimezone(isTimezoneSet, aTimezone);
+ }
+ });
+
+ if (bInputMode)
+ {
+ double fVal = 0.0;
+ sal_uInt32 nFormat = 0;
+ switch (nReqFmt)
+ {
+ case SvNumFormatType::DATE:
+ {
+ Date aActDate( Date::SYSTEM );
+ fVal = aActDate - pFormatter->GetNullDate();
+ if (nCurNumFormatType == SvNumFormatType::DATE)
+ nFormat = nCurNumFormat;
+ }
+ break;
+ case SvNumFormatType::TIME:
+ {
+ tools::Time aActTime( tools::Time::SYSTEM );
+ fVal = aActTime.GetTimeInDays();
+ if (nCurNumFormatType == SvNumFormatType::TIME)
+ nFormat = nCurNumFormat;
+ }
+ break;
+ default:
+ SAL_WARN("sc.ui","unhandled current date/time request");
+ nReqFmt = SvNumFormatType::DATETIME;
+ [[fallthrough]];
+ case SvNumFormatType::DATETIME:
+ {
+ DateTime aActDateTime( DateTime::SYSTEM );
+ fVal = DateTime::Sub( aActDateTime, DateTime( pFormatter->GetNullDate()));
+ if (nCurNumFormatType == SvNumFormatType::DATETIME)
+ nFormat = nCurNumFormat;
+ }
+ break;
+ }
+
+ if (!nFormat)
+ {
+ const LanguageType nLang = (pCurNumFormatEntry ? pCurNumFormatEntry->GetLanguage() : ScGlobal::eLnge);
+ nFormat = pFormatter->GetStandardFormat( nReqFmt, nLang);
+ // This would return a more precise format with seconds and 100th
+ // seconds for a time request.
+ //nFormat = pFormatter->GetStandardFormat( fVal, nFormat, nReqFmt, nLang);
+ }
+ OUString aString;
+ const Color* pColor;
+ pFormatter->GetOutputString( fVal, nFormat, aString, &pColor);
+
+ pInputHdl->DataChanging();
+ EditView* pTopView = pInputHdl->GetTopView();
+ if (pTopView)
+ pTopView->InsertText( aString);
+ EditView* pTableView = pInputHdl->GetTableView();
+ if (pTableView)
+ pTableView->InsertText( aString);
+ pInputHdl->DataChanged();
+ }
+ else
+ {
+ // Clear "Enter pastes" mode.
+ rViewData.SetPasteMode( ScPasteFlags::NONE );
+ // Clear CopySourceOverlay in each window of a split/frozen tabview.
+ rViewData.GetViewShell()->UpdateCopySourceOverlay();
+
+ bool bForceReqFmt = false;
+ const double fCell = rDoc.GetValue( aCurPos);
+ // Combine requested date/time stamp with existing cell time/date, if any.
+ switch (nReqFmt)
+ {
+ case SvNumFormatType::DATE:
+ switch (nCurNumFormatType)
+ {
+ case SvNumFormatType::TIME:
+ // An empty cell formatted as time (or 00:00 time) shall
+ // not result in the current date with 00:00 time, but only
+ // in current date.
+ if (fCell != 0.0)
+ nReqFmt = SvNumFormatType::DATETIME;
+ break;
+ case SvNumFormatType::DATETIME:
+ {
+ // Force to only date if the existing date+time is the
+ // current date. This way inserting current date twice
+ // on an existing date+time cell can be used to force
+ // date, which otherwise would only be possible by
+ // applying a date format.
+ double fDate = rtl::math::approxFloor( fCell);
+ if (fDate == (Date( Date::SYSTEM) - pFormatter->GetNullDate()))
+ bForceReqFmt = true;
+ }
+ break;
+ default: break;
+ }
+ break;
+ case SvNumFormatType::TIME:
+ switch (nCurNumFormatType)
+ {
+ case SvNumFormatType::DATE:
+ // An empty cell formatted as date shall not result in the
+ // null date and current time, but only in current time.
+ if (fCell != 0.0)
+ nReqFmt = SvNumFormatType::DATETIME;
+ break;
+ case SvNumFormatType::DATETIME:
+ // Requesting current time on an empty date+time cell
+ // inserts both current date+time.
+ if (fCell == 0.0)
+ nReqFmt = SvNumFormatType::DATETIME;
+ else
+ {
+ // Add current time to an existing date+time where time is
+ // zero and date is current date, else force time only.
+ double fDate = rtl::math::approxFloor( fCell);
+ double fTime = fCell - fDate;
+ if (fTime == 0.0 && fDate == (Date( Date::SYSTEM) - pFormatter->GetNullDate()))
+ nReqFmt = SvNumFormatType::DATETIME;
+ else
+ bForceReqFmt = true;
+ }
+ break;
+ default: break;
+ }
+ break;
+ default:
+ SAL_WARN("sc.ui","unhandled current date/time request");
+ nReqFmt = SvNumFormatType::DATETIME;
+ [[fallthrough]];
+ case SvNumFormatType::DATETIME:
+ break;
+ }
+ double fVal = 0.0;
+ switch (nReqFmt)
+ {
+ case SvNumFormatType::DATE:
+ {
+ Date aActDate( Date::SYSTEM );
+ fVal = aActDate - pFormatter->GetNullDate();
+ }
+ break;
+ case SvNumFormatType::TIME:
+ {
+ tools::Time aActTime( tools::Time::SYSTEM );
+ fVal = aActTime.GetTimeInDays();
+ }
+ break;
+ case SvNumFormatType::DATETIME:
+ switch (nCurNumFormatType)
+ {
+ case SvNumFormatType::DATE:
+ {
+ double fDate = rtl::math::approxFloor( fCell);
+ tools::Time aActTime( tools::Time::SYSTEM );
+ fVal = fDate + aActTime.GetTimeInDays();
+ }
+ break;
+ case SvNumFormatType::TIME:
+ {
+ double fTime = fCell - rtl::math::approxFloor( fCell);
+ Date aActDate( Date::SYSTEM );
+ fVal = (aActDate - pFormatter->GetNullDate()) + fTime;
+ }
+ break;
+ default:
+ {
+ DateTime aActDateTime( DateTime::SYSTEM );
+ fVal = DateTime::Sub( aActDateTime, DateTime( pFormatter->GetNullDate()));
+ }
+ }
+ break;
+ default: break;
+
+ }
+
+ SfxUndoManager* pUndoMgr = pDocSh->GetUndoManager();
+ pUndoMgr->EnterListAction(rUndoStr, rUndoStr, 0, rViewData.GetViewShell()->GetViewShellId());
+
+ pDocSh->GetDocFunc().SetValueCell(aCurPos, fVal, true);
+
+ // Set the new cell format only when it differs from the current cell
+ // format type. Preserve a date+time format unless we force a format
+ // through.
+ if (bForceReqFmt || (nReqFmt != nCurNumFormatType && nCurNumFormatType != SvNumFormatType::DATETIME))
+ SetNumberFormat(nReqFmt);
+ else
+ rViewData.UpdateInputHandler(); // update input bar with new value
+
+ pUndoMgr->LeaveListAction();
+ }
+}
+
+void ScViewFunc::ShowNote( bool bShow )
+{
+ if( bShow )
+ HideNoteMarker();
+ const ScViewData& rViewData = GetViewData();
+ ScAddress aPos( rViewData.GetCurX(), rViewData.GetCurY(), rViewData.GetTabNo() );
+ // show note moved to ScDocFunc, to be able to use it in notesuno.cxx
+ rViewData.GetDocShell()->GetDocFunc().ShowNote( aPos, bShow );
+}
+
+void ScViewFunc::EditNote()
+{
+ // for editing display and activate
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCCOL nCol = GetViewData().GetCurX();
+ SCROW nRow = GetViewData().GetCurY();
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScAddress aPos( nCol, nRow, nTab );
+
+ // start drawing undo to catch undo action for insertion of the caption object
+ pDocSh->MakeDrawLayer();
+ ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer();
+ pDrawLayer->BeginCalcUndo(true);
+ // generated undo action is processed in FuText::StopEditMode
+
+ // get existing note or create a new note (including caption drawing object)
+ ScPostIt* pNote = rDoc.GetOrCreateNote( aPos );
+ if(!pNote)
+ return;
+
+ // hide temporary note caption
+ HideNoteMarker();
+ // show caption object without changing internal visibility state
+ pNote->ShowCaptionTemp( aPos );
+
+ /* Drawing object has been created in ScDocument::GetOrCreateNote() or
+ in ScPostIt::ShowCaptionTemp(), so ScPostIt::GetCaption() should
+ return a caption object. */
+ SdrCaptionObj* pCaption = pNote->GetCaption();
+ if( !pCaption )
+ return;
+
+ if ( ScDrawView* pScDrawView = GetScDrawView() )
+ pScDrawView->SyncForGrid( pCaption );
+
+ // activate object (as in FuSelection::TestComment)
+ GetViewData().GetDispatcher().Execute( SID_DRAW_NOTEEDIT, SfxCallMode::SYNCHRON | SfxCallMode::RECORD );
+ // now get the created FuText and set into EditMode
+ FuText* pFuText = dynamic_cast<FuText*>(GetDrawFuncPtr());
+ if (pFuText)
+ {
+ ScrollToObject( pCaption ); // make object fully visible
+ pFuText->SetInEditMode( pCaption );
+
+ ScTabView::OnLOKNoteStateChanged( pNote );
+ }
+ collectUIInformation("OPEN");
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/viewfun7.cxx b/sc/source/ui/view/viewfun7.cxx
new file mode 100644
index 0000000000..f704256756
--- /dev/null
+++ b/sc/source/ui/view/viewfun7.cxx
@@ -0,0 +1,462 @@
+/* -*- 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 .
+ */
+
+#include <com/sun/star/embed/NoVisualAreaSizeException.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+
+#include <svx/svditer.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdouno.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdundo.hxx>
+#include <svtools/embedhlp.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/ipclient.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <com/sun/star/embed/Aspects.hpp>
+#include <osl/diagnose.h>
+
+#include <document.hxx>
+#include <viewfunc.hxx>
+#include <tabvwsh.hxx>
+#include <drawview.hxx>
+#include <scmod.hxx>
+#include <drwlayer.hxx>
+#include <drwtrans.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <docuno.hxx>
+#include <docsh.hxx>
+#include <dragdata.hxx>
+#include <gridwin.hxx>
+#include <stlpool.hxx>
+
+bool bPasteIsMove = false;
+
+using namespace com::sun::star;
+
+static void lcl_AdjustInsertPos( ScViewData& rData, Point& rPos, const Size& rSize )
+{
+ SdrPage* pPage = rData.GetScDrawView()->GetModel().GetPage( static_cast<sal_uInt16>(rData.GetTabNo()) );
+ OSL_ENSURE(pPage,"pPage ???");
+ Size aPgSize( pPage->GetSize() );
+ if (aPgSize.Width() < 0)
+ aPgSize.setWidth( -aPgSize.Width() );
+ tools::Long x = aPgSize.Width() - rPos.X() - rSize.Width();
+ tools::Long y = aPgSize.Height() - rPos.Y() - rSize.Height();
+ // if necessary: adjustments (80/200) for pixel approx. errors
+ if( x < 0 )
+ rPos.AdjustX(x + 80 );
+ if( y < 0 )
+ rPos.AdjustY(y + 200 );
+ rPos.AdjustX(rSize.Width() / 2 ); // position at paste is center
+ rPos.AdjustY(rSize.Height() / 2 );
+}
+
+void ScViewFunc::PasteDraw( const Point& rLogicPos, SdrModel* pModel,
+ bool bGroup, std::u16string_view rSrcShellID, std::u16string_view rDestShellID )
+{
+ bool bSameDocClipboard = rSrcShellID == rDestShellID;
+
+ MakeDrawLayer();
+ Point aPos( rLogicPos );
+
+ // MapMode at Outliner-RefDevice has to be right (as in FuText::MakeOutliner)
+ //! merge with FuText::MakeOutliner?
+ MapMode aOldMapMode;
+ OutputDevice* pRef = GetViewData().GetDocument().GetDrawLayer()->GetRefDevice();
+ if (pRef)
+ {
+ aOldMapMode = pRef->GetMapMode();
+ pRef->SetMapMode( MapMode(MapUnit::Map100thMM) );
+ }
+
+ bool bNegativePage = GetViewData().GetDocument().IsNegativePage( GetViewData().GetTabNo() );
+
+ SdrView* pDragEditView = nullptr;
+ ScModule* pScMod = SC_MOD();
+ const ScDragData& rData = pScMod->GetDragData();
+ ScDrawTransferObj* pDrawTrans = rData.pDrawTransfer;
+ if (pDrawTrans)
+ {
+ pDragEditView = pDrawTrans->GetDragSourceView();
+
+ aPos -= aDragStartDiff;
+ if ( bNegativePage )
+ {
+ if (aPos.X() > 0) aPos.setX( 0 );
+ }
+ else
+ {
+ if (aPos.X() < 0) aPos.setX( 0 );
+ }
+ if (aPos.Y() < 0) aPos.setY( 0 );
+ }
+
+ ScDrawView* pScDrawView = GetScDrawView();
+ if (bGroup)
+ pScDrawView->BegUndo( ScResId( STR_UNDO_PASTE ) );
+
+ bool bSameDoc = ( pDragEditView && &pDragEditView->GetModel() == &pScDrawView->GetModel() );
+ if (bSameDoc)
+ {
+ // copy locally - incl. charts
+
+ Point aSourceStart = pDragEditView->GetAllMarkedRect().TopLeft();
+ tools::Long nDiffX = aPos.X() - aSourceStart.X();
+ tools::Long nDiffY = aPos.Y() - aSourceStart.Y();
+
+ // move within a page?
+
+ if ( bPasteIsMove &&
+ pScDrawView->GetSdrPageView()->GetPage() ==
+ pDragEditView->GetSdrPageView()->GetPage() )
+ {
+ if ( nDiffX != 0 || nDiffY != 0 )
+ pDragEditView->MoveAllMarked(Size(nDiffX,nDiffY));
+ }
+ else
+ {
+ SdrModel& rDrawModel = pDragEditView->GetModel();
+ SCTAB nTab = GetViewData().GetTabNo();
+ SdrPage* pDestPage = rDrawModel.GetPage( static_cast< sal_uInt16 >( nTab ) );
+ OSL_ENSURE(pDestPage,"who is this, Page?");
+
+ ::std::vector< OUString > aExcludedChartNames;
+ if ( pDestPage )
+ {
+ ScChartHelper::GetChartNames( aExcludedChartNames, pDestPage );
+ }
+
+ SdrMarkList aMark = pDragEditView->GetMarkedObjectList();
+ aMark.ForceSort();
+ const size_t nMarkCnt=aMark.GetMarkCount();
+ for (size_t nm=0; nm<nMarkCnt; ++nm) {
+ const SdrMark* pM=aMark.GetMark(nm);
+ const SdrObject* pObj=pM->GetMarkedSdrObj();
+
+ // Directly Clone to target SdrModel
+ rtl::Reference<SdrObject> pNewObj(pObj->CloneSdrObject(rDrawModel));
+
+ if (pNewObj!=nullptr)
+ {
+ // copy graphics within the same model - always needs new name
+ if ( dynamic_cast<const SdrGrafObj*>( pNewObj.get()) != nullptr && !bPasteIsMove )
+ pNewObj->SetName(static_cast<ScDrawLayer*>(&rDrawModel)->GetNewGraphicName());
+
+ if (nDiffX!=0 || nDiffY!=0)
+ pNewObj->NbcMove(Size(nDiffX,nDiffY));
+ if (pDestPage)
+ pDestPage->InsertObject( pNewObj.get() );
+ pScDrawView->AddUndo(std::make_unique<SdrUndoInsertObj>( *pNewObj ));
+
+ if (ScDrawLayer::IsCellAnchored(*pNewObj))
+ ScDrawLayer::SetCellAnchoredFromPosition(*pNewObj, GetViewData().GetDocument(), nTab,
+ ScDrawLayer::IsResizeWithCell(*pNewObj));
+ }
+ }
+
+ if (bPasteIsMove)
+ pDragEditView->DeleteMarked();
+
+ ScDocument& rDocument = GetViewData().GetDocument();
+ ScDocShell* pDocShell = GetViewData().GetDocShell();
+ ScModelObj* pModelObj = ( pDocShell ? pDocShell->GetModel() : nullptr );
+ if ( pDestPage && pModelObj && pDrawTrans )
+ {
+ const ScRangeListVector& rProtectedChartRangesVector( pDrawTrans->GetProtectedChartRangesVector() );
+ ScChartHelper::CreateProtectedChartListenersAndNotify( rDocument, pDestPage, pModelObj, nTab,
+ rProtectedChartRangesVector, aExcludedChartNames, bSameDoc );
+ }
+ }
+ }
+ else
+ {
+ bPasteIsMove = false; // no internal move happened
+ SdrView aView(*pModel); // #i71529# never create a base class of SdrView directly!
+ SdrPageView* pPv = aView.ShowSdrPage(aView.GetModel().GetPage(0));
+ aView.MarkAllObj(pPv);
+ Size aSize = aView.GetAllMarkedRect().GetSize();
+ lcl_AdjustInsertPos( GetViewData(), aPos, aSize );
+
+ // don't change marking if OLE object is active
+ // (at Drop from OLE object it would be deactivated in the middle of ExecuteDrag!)
+
+ SdrInsertFlags nOptions = SdrInsertFlags::NONE;
+ SfxInPlaceClient* pClient = GetViewData().GetViewShell()->GetIPClient();
+ if ( pClient && pClient->IsObjectInPlaceActive() )
+ nOptions |= SdrInsertFlags::DONTMARK;
+
+ ::std::vector< OUString > aExcludedChartNames;
+ SCTAB nTab = GetViewData().GetTabNo();
+ SdrPage* pPage = pScDrawView->GetModel().GetPage( static_cast< sal_uInt16 >( nTab ) );
+ OSL_ENSURE( pPage, "Page?" );
+ if ( pPage )
+ {
+ ScChartHelper::GetChartNames( aExcludedChartNames, pPage );
+ }
+
+ if ( !bSameDocClipboard )
+ {
+ auto pPool = static_cast<ScStyleSheetPool*>(pScDrawView->GetModel().GetStyleSheetPool());
+ pPool->CopyUsedGraphicStylesFrom(pModel->GetStyleSheetPool());
+
+ // #89247# Set flag for ScDocument::UpdateChartListeners() which is
+ // called during paste.
+ GetViewData().GetDocument().SetPastingDrawFromOtherDoc( true );
+ }
+
+ pScDrawView->Paste(*pModel, aPos, nullptr, nOptions);
+
+ if ( !bSameDocClipboard )
+ GetViewData().GetDocument().SetPastingDrawFromOtherDoc( false );
+
+ // Paste puts all objects on the active (front) layer
+ // controls must be on SC_LAYER_CONTROLS
+ if (pPage)
+ {
+ SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups );
+ SdrObject* pObject = aIter.Next();
+ while (pObject)
+ {
+ if ( dynamic_cast<const SdrUnoObj*>( pObject) != nullptr && pObject->GetLayer() != SC_LAYER_CONTROLS )
+ pObject->NbcSetLayer(SC_LAYER_CONTROLS);
+
+ if (ScDrawLayer::IsCellAnchored(*pObject))
+ ScDrawLayer::SetCellAnchoredFromPosition(*pObject, GetViewData().GetDocument(), nTab,
+ ScDrawLayer::IsResizeWithCell(*pObject));
+
+ pObject = aIter.Next();
+ }
+ }
+
+ // all graphics objects must have names
+ GetViewData().GetDocument().EnsureGraphicNames();
+
+ ScDocument& rDocument = GetViewData().GetDocument();
+ ScDocShell* pDocShell = GetViewData().GetDocShell();
+ ScModelObj* pModelObj = ( pDocShell ? pDocShell->GetModel() : nullptr );
+ const ScDrawTransferObj* pTransferObj = ScDrawTransferObj::GetOwnClipboard(ScTabViewShell::GetClipData(GetViewData().GetActiveWin()));
+ if ( pPage && pModelObj && ( pTransferObj || pDrawTrans ) )
+ {
+ const ScRangeListVector& rProtectedChartRangesVector(
+ pTransferObj ? pTransferObj->GetProtectedChartRangesVector() : pDrawTrans->GetProtectedChartRangesVector() );
+ ScChartHelper::CreateProtectedChartListenersAndNotify( rDocument, pPage, pModelObj, nTab,
+ rProtectedChartRangesVector, aExcludedChartNames, bSameDocClipboard );
+ }
+ }
+
+ if (bGroup)
+ {
+ pScDrawView->GroupMarked();
+ pScDrawView->EndUndo();
+ }
+
+ if (pRef)
+ pRef->SetMapMode( aOldMapMode );
+
+ // GetViewData().GetViewShell()->SetDrawShell( true );
+ // It is not sufficient to just set the DrawShell if we pasted, for
+ // example, a chart. SetDrawShellOrSub() would only work for D&D in the
+ // same document but not if inserting from the clipboard, therefore
+ // MarkListHasChanged() is what we need.
+ pScDrawView->MarkListHasChanged();
+
+}
+
+bool ScViewFunc::PasteObject( const Point& rPos, const uno::Reference < embed::XEmbeddedObject >& xObj,
+ const Size* pDescSize, const Graphic* pReplGraph, const OUString& aMediaType, sal_Int64 nAspect )
+{
+ MakeDrawLayer();
+ if ( xObj.is() )
+ {
+ OUString aName;
+ //TODO/MBA: is that OK?
+ comphelper::EmbeddedObjectContainer& aCnt = GetViewData().GetViewShell()->GetObjectShell()->GetEmbeddedObjectContainer();
+ if ( !aCnt.HasEmbeddedObject( xObj ) )
+ aCnt.InsertEmbeddedObject( xObj, aName );
+ else
+ aName = aCnt.GetEmbeddedObjectName( xObj );
+
+ svt::EmbeddedObjectRef aObjRef( xObj, nAspect );
+ if ( pReplGraph )
+ aObjRef.SetGraphic( *pReplGraph, aMediaType );
+
+ Size aSize;
+ if ( nAspect == embed::Aspects::MSOLE_ICON )
+ {
+ MapMode aMapMode( MapUnit::Map100thMM );
+ aSize = aObjRef.GetSize( &aMapMode );
+ }
+ else
+ {
+ // working with visual area can switch object to running state
+ MapUnit aMapObj = VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( nAspect ) );
+ MapUnit aMap100 = MapUnit::Map100thMM;
+
+ if ( pDescSize && pDescSize->Width() && pDescSize->Height() )
+ {
+ // use size from object descriptor if given
+ aSize = OutputDevice::LogicToLogic(*pDescSize, MapMode(aMap100), MapMode(aMapObj));
+ awt::Size aSz;
+ aSz.Width = aSize.Width();
+ aSz.Height = aSize.Height();
+ xObj->setVisualAreaSize( nAspect, aSz );
+ }
+
+ awt::Size aSz;
+ try
+ {
+ aSz = xObj->getVisualAreaSize( nAspect );
+ }
+ catch ( embed::NoVisualAreaSizeException& )
+ {
+ // the default size will be set later
+ }
+
+ aSize = Size( aSz.Width, aSz.Height );
+ aSize = OutputDevice::LogicToLogic(aSize, MapMode(aMapObj), MapMode(aMap100)); // for SdrOle2Obj
+
+ if( aSize.IsEmpty() )
+ {
+ OSL_FAIL("SvObjectDescriptor::GetSize == 0");
+ aSize.setWidth( 5000 );
+ aSize.setHeight( 5000 );
+ aSize = OutputDevice::LogicToLogic(aSize, MapMode(aMap100), MapMode(aMapObj));
+ aSz.Width = aSize.Width();
+ aSz.Height = aSize.Height();
+ xObj->setVisualAreaSize( nAspect, aSz );
+ }
+ }
+
+ // don't call AdjustInsertPos
+ Point aInsPos = rPos;
+ if ( GetViewData().GetDocument().IsNegativePage( GetViewData().GetTabNo() ) )
+ aInsPos.AdjustX( -(aSize.Width()) );
+ tools::Rectangle aRect( aInsPos, aSize );
+
+ ScDrawView* pDrView = GetScDrawView();
+ rtl::Reference<SdrOle2Obj> pSdrObj = new SdrOle2Obj(
+ pDrView->getSdrModelFromSdrView(),
+ aObjRef,
+ aName,
+ aRect);
+
+ SdrPageView* pPV = pDrView->GetSdrPageView();
+ pDrView->InsertObjectSafe( pSdrObj.get(), *pPV ); // don't mark if OLE
+ GetViewData().GetViewShell()->SetDrawShell( true );
+ return true;
+ }
+ else
+ return false;
+}
+
+bool ScViewFunc::PasteBitmapEx( const Point& rPos, const BitmapEx& rBmpEx )
+{
+ Graphic aGraphic(rBmpEx);
+ return PasteGraphic( rPos, aGraphic, "" );
+}
+
+bool ScViewFunc::PasteMetaFile( const Point& rPos, const GDIMetaFile& rMtf )
+{
+ Graphic aGraphic(rMtf);
+ return PasteGraphic( rPos, aGraphic, "" );
+}
+
+bool ScViewFunc::PasteGraphic( const Point& rPos, const Graphic& rGraphic,
+ const OUString& rFile )
+{
+ MakeDrawLayer();
+ ScDrawView* pScDrawView = GetScDrawView();
+
+ if (!pScDrawView)
+ return false;
+
+ // #i123922# check if the drop was over an existing object; if yes, evtl. replace
+ // the graphic for a SdrGraphObj (including link state updates) or adapt the fill
+ // style for other objects
+ SdrPageView* pPageView = pScDrawView->GetSdrPageView();
+ if (pPageView)
+ {
+ SdrObject* pPickObj = pScDrawView->PickObj(rPos, pScDrawView->getHitTolLog(), pPageView);
+ if (pPickObj)
+ {
+ const OUString aBeginUndo(ScResId(STR_UNDO_DRAGDROP));
+ SdrObject* pResult = pScDrawView->ApplyGraphicToObject(
+ *pPickObj,
+ rGraphic,
+ aBeginUndo,
+ rFile);
+
+ if (pResult)
+ {
+ // we are done; mark the modified/new object
+ pScDrawView->MarkObj(pResult, pScDrawView->GetSdrPageView());
+ return true;
+ }
+ }
+ }
+
+ Point aPos( rPos );
+ vcl::Window* pWin = GetActiveWin();
+ MapMode aSourceMap = rGraphic.GetPrefMapMode();
+ MapMode aDestMap( MapUnit::Map100thMM );
+
+ if (aSourceMap.GetMapUnit() == MapUnit::MapPixel)
+ {
+ // consider pixel correction, so bitmap fits to screen
+ Fraction aScaleX, aScaleY;
+ pScDrawView->CalcNormScale( aScaleX, aScaleY );
+ aDestMap.SetScaleX(aScaleX);
+ aDestMap.SetScaleY(aScaleY);
+ }
+
+ Size aSize = pWin->LogicToLogic( rGraphic.GetPrefSize(), &aSourceMap, &aDestMap );
+
+ if ( GetViewData().GetDocument().IsNegativePage( GetViewData().GetTabNo() ) )
+ aPos.AdjustX( -(aSize.Width()) );
+
+ GetViewData().GetViewShell()->SetDrawShell( true );
+ tools::Rectangle aRect(aPos, aSize);
+ rtl::Reference<SdrGrafObj> pGrafObj = new SdrGrafObj(
+ pScDrawView->getSdrModelFromSdrView(),
+ rGraphic,
+ aRect);
+
+ // path was the name of the graphic in history
+
+ ScDrawLayer* pLayer = static_cast<ScDrawLayer*>(&pScDrawView->GetModel());
+ OUString aName = pLayer->GetNewGraphicName(); // "Graphics"
+ pGrafObj->SetName(aName);
+
+ // don't mark if OLE
+ bool bSuccess = pScDrawView->InsertObjectSafe(pGrafObj.get(), *pScDrawView->GetSdrPageView());
+
+ // SetGraphicLink has to be used after inserting the object,
+ // otherwise an empty graphic is swapped in and the contact stuff crashes.
+ // See #i37444#.
+ if (bSuccess && !rFile.isEmpty())
+ pGrafObj->SetGraphicLink( rFile );
+
+ return bSuccess;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/viewfunc.cxx b/sc/source/ui/view/viewfunc.cxx
new file mode 100644
index 0000000000..25633cae38
--- /dev/null
+++ b/sc/source/ui/view/viewfunc.cxx
@@ -0,0 +1,3183 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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 .
+ */
+
+#include <config_features.h>
+
+#include <scitems.hxx>
+
+#include <sfx2/app.hxx>
+#include <svx/algitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <sfx2/bindings.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <svl/zformat.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/virdev.hxx>
+#include <stdlib.h>
+#include <unotools/charclass.hxx>
+#include <vcl/uitest/logger.hxx>
+#include <vcl/uitest/eventdescription.hxx>
+#include <osl/diagnose.h>
+#include <formula/paramclass.hxx>
+
+#include <viewfunc.hxx>
+#include <tabvwsh.hxx>
+#include <docsh.hxx>
+#include <attrib.hxx>
+#include <patattr.hxx>
+#include <docpool.hxx>
+#include <sc.hrc>
+#include <undocell.hxx>
+#include <undoblk.hxx>
+#include <refundo.hxx>
+#include <olinetab.hxx>
+#include <rangenam.hxx>
+#include <globstr.hrc>
+#include <global.hxx>
+#include <stlsheet.hxx>
+#include <editutil.hxx>
+#include <formulacell.hxx>
+#include <scresid.hxx>
+#include <inputhdl.hxx>
+#include <scmod.hxx>
+#include <inputopt.hxx>
+#include <compiler.hxx>
+#include <docfunc.hxx>
+#include <appoptio.hxx>
+#include <sizedev.hxx>
+#include <editable.hxx>
+#include <scui_def.hxx>
+#include <funcdesc.hxx>
+#include <docuno.hxx>
+#include <cellsuno.hxx>
+#include <tokenarray.hxx>
+#include <rowheightcontext.hxx>
+#include <comphelper/lok.hxx>
+#include <conditio.hxx>
+#include <columnspanset.hxx>
+#include <stringutil.hxx>
+#include <SparklineList.hxx>
+
+#include <memory>
+
+static void ShowFilteredRows(ScDocument& rDoc, SCTAB nTab, SCCOLROW nStartNo, SCCOLROW nEndNo,
+ bool bShow)
+{
+ SCROW nFirstRow = nStartNo;
+ SCROW nLastRow = nStartNo;
+ do
+ {
+ if (!rDoc.RowFiltered(nFirstRow, nTab, nullptr, &nLastRow))
+ rDoc.ShowRows(nFirstRow, nLastRow < nEndNo ? nLastRow : nEndNo, nTab, bShow);
+ nFirstRow = nLastRow + 1;
+ } while (nFirstRow <= nEndNo);
+}
+
+static void lcl_PostRepaintCondFormat( const ScConditionalFormat *pCondFmt, ScDocShell *pDocSh )
+{
+ if( pCondFmt )
+ {
+ const ScRangeList& rRanges = pCondFmt->GetRange();
+
+ pDocSh->PostPaint( rRanges, PaintPartFlags::All );
+ }
+}
+
+static void lcl_PostRepaintSparkLine(sc::SparklineList* pSparklineList, const ScRange& rRange,
+ ScDocShell* pDocSh)
+{
+ if (pSparklineList)
+ {
+ for (auto& rSparkLineGroup : pSparklineList->getSparklineGroups())
+ {
+ for (auto& rSparkline : pSparklineList->getSparklinesFor(rSparkLineGroup))
+ {
+ if (rSparkline->getInputRange().Contains(rRange))
+ {
+ pDocSh->PostPaint(
+ ScRange(rSparkline->getColumn(), rSparkline->getRow(), rRange.aStart.Tab()),
+ PaintPartFlags::All, SC_PF_TESTMERGE);
+ }
+ }
+ }
+ }
+}
+
+ScViewFunc::ScViewFunc( vcl::Window* pParent, ScDocShell& rDocSh, ScTabViewShell* pViewShell ) :
+ ScTabView( pParent, rDocSh, pViewShell ),
+ bFormatValid( false )
+{
+}
+
+ScViewFunc::~ScViewFunc()
+{
+}
+
+namespace {
+
+void collectUIInformation(std::map<OUString, OUString>&& aParameters, const OUString& rAction)
+{
+ EventDescription aDescription;
+ aDescription.aID = "grid_window";
+ aDescription.aAction = rAction;
+ aDescription.aParameters = std::move(aParameters);
+ aDescription.aParent = "MainWindow";
+ aDescription.aKeyWord = "ScGridWinUIObject";
+
+ UITestLogger::getInstance().logEvent(aDescription);
+}
+
+}
+
+void ScViewFunc::StartFormatArea()
+{
+ // anything to do?
+ if ( !SC_MOD()->GetInputOptions().GetExtendFormat() )
+ return;
+
+ // start only with single cell (marked or cursor position)
+ ScRange aMarkRange;
+ bool bOk = (GetViewData().GetSimpleArea( aMarkRange ) == SC_MARK_SIMPLE);
+ if ( bOk && aMarkRange.aStart != aMarkRange.aEnd )
+ bOk = false;
+
+ if (bOk)
+ {
+ bFormatValid = true;
+ aFormatSource = aMarkRange.aStart;
+ aFormatArea = ScRange( aFormatSource );
+ }
+ else
+ bFormatValid = false; // discard old range
+}
+
+bool ScViewFunc::TestFormatArea( SCCOL nCol, SCROW nRow, SCTAB nTab, bool bAttrChanged )
+{
+ // anything to do?
+ if ( !SC_MOD()->GetInputOptions().GetExtendFormat() )
+ return false;
+
+ // Test: treat input with numberformat (bAttrChanged) always as new Attribute
+ // (discard old Area ). If not wanted, discard if-statement
+ if ( bAttrChanged )
+ {
+ StartFormatArea();
+ return false;
+ }
+
+ //! Test if cell empty ???
+
+ bool bFound = false;
+ ScRange aNewRange = aFormatArea;
+ if ( bFormatValid && nTab == aFormatSource.Tab() )
+ {
+ if ( nRow >= aFormatArea.aStart.Row() && nRow <= aFormatArea.aEnd.Row() )
+ {
+ // within range?
+ if ( nCol >= aFormatArea.aStart.Col() && nCol <= aFormatArea.aEnd.Col() )
+ {
+ bFound = true; // do not change range
+ }
+ // left ?
+ if ( nCol+1 == aFormatArea.aStart.Col() )
+ {
+ bFound = true;
+ aNewRange.aStart.SetCol( nCol );
+ }
+ // right ?
+ if ( nCol == aFormatArea.aEnd.Col()+1 )
+ {
+ bFound = true;
+ aNewRange.aEnd.SetCol( nCol );
+ }
+ }
+ if ( nCol >= aFormatArea.aStart.Col() && nCol <= aFormatArea.aEnd.Col() )
+ {
+ // top ?
+ if ( nRow+1 == aFormatArea.aStart.Row() )
+ {
+ bFound = true;
+ aNewRange.aStart.SetRow( nRow );
+ }
+ // bottom ?
+ if ( nRow == aFormatArea.aEnd.Row()+1 )
+ {
+ bFound = true;
+ aNewRange.aEnd.SetRow( nRow );
+ }
+ }
+ }
+
+ if (bFound)
+ aFormatArea = aNewRange; // extend
+ else
+ bFormatValid = false; // outside of range -> break
+
+ return bFound;
+}
+
+void ScViewFunc::DoAutoAttributes( SCCOL nCol, SCROW nRow, SCTAB nTab,
+ bool bAttrChanged )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+
+ const ScPatternAttr* pSource = rDoc.GetPattern(
+ aFormatSource.Col(), aFormatSource.Row(), nTab );
+ if ( !pSource->GetItem(ATTR_MERGE).IsMerged() )
+ {
+ ScRange aRange( nCol, nRow, nTab, nCol, nRow, nTab );
+ ScMarkData aMark(rDoc.GetSheetLimits());
+ aMark.SetMarkArea( aRange );
+
+ ScDocFunc &rFunc = GetViewData().GetDocFunc();
+
+ // pOldPattern is only valid until call to ApplyAttributes!
+ const ScPatternAttr* pOldPattern = rDoc.GetPattern( nCol, nRow, nTab );
+ const ScStyleSheet* pSrcStyle = pSource->GetStyleSheet();
+ if ( pSrcStyle && pSrcStyle != pOldPattern->GetStyleSheet() )
+ rFunc.ApplyStyle( aMark, pSrcStyle->GetName(), false );
+
+ rFunc.ApplyAttributes( aMark, *pSource, false );
+ }
+
+ if ( bAttrChanged ) // value entered with number format?
+ aFormatSource.Set( nCol, nRow, nTab ); // then set a new source
+}
+
+// additional routines
+
+sal_uInt16 ScViewFunc::GetOptimalColWidth( SCCOL nCol, SCTAB nTab, bool bFormula )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+
+ double nPPTX = GetViewData().GetPPTX();
+ double nPPTY = GetViewData().GetPPTY();
+ Fraction aZoomX = GetViewData().GetZoomX();
+ Fraction aZoomY = GetViewData().GetZoomY();
+
+ ScSizeDeviceProvider aProv(pDocSh);
+ if (aProv.IsPrinter())
+ {
+ nPPTX = aProv.GetPPTX();
+ nPPTY = aProv.GetPPTY();
+ aZoomX = aZoomY = Fraction( 1, 1 );
+ }
+
+ sal_uInt16 nTwips = rDoc.GetOptimalColWidth( nCol, nTab, aProv.GetDevice(),
+ nPPTX, nPPTY, aZoomX, aZoomY, bFormula, &rMark );
+ return nTwips;
+}
+
+bool ScViewFunc::SelectionEditable( bool* pOnlyNotBecauseOfMatrix /* = NULL */ )
+{
+ bool bRet;
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ if (rMark.IsMarked() || rMark.IsMultiMarked())
+ bRet = rDoc.IsSelectionEditable( rMark, pOnlyNotBecauseOfMatrix );
+ else
+ {
+ SCCOL nCol = GetViewData().GetCurX();
+ SCROW nRow = GetViewData().GetCurY();
+ SCTAB nTab = GetViewData().GetTabNo();
+ bRet = rDoc.IsBlockEditable( nTab, nCol, nRow, nCol, nRow,
+ pOnlyNotBecauseOfMatrix );
+ }
+ return bRet;
+}
+
+static bool lcl_FunctionKnown( sal_uInt16 nOpCode )
+{
+ const ScFunctionList* pFuncList = ScGlobal::GetStarCalcFunctionList();
+ if ( pFuncList )
+ {
+ sal_uLong nCount = pFuncList->GetCount();
+ for (sal_uLong i=0; i<nCount; i++)
+ if ( pFuncList->GetFunction(i)->nFIndex == nOpCode )
+ return true;
+ }
+ return false;
+}
+
+static bool lcl_AddFunction( ScAppOptions& rAppOpt, sal_uInt16 nOpCode )
+{
+ sal_uInt16 nOldCount = rAppOpt.GetLRUFuncListCount();
+ sal_uInt16* pOldList = rAppOpt.GetLRUFuncList();
+ sal_uInt16 nPos;
+ for (nPos=0; nPos<nOldCount; nPos++)
+ if (pOldList[nPos] == nOpCode) // is the function already in the list?
+ {
+ if ( nPos == 0 )
+ return false; // already at the top -> no change
+
+ // count doesn't change, so the original array is modified
+
+ for (sal_uInt16 nCopy=nPos; nCopy>0; nCopy--)
+ pOldList[nCopy] = pOldList[nCopy-1];
+ pOldList[0] = nOpCode;
+
+ return true; // list has changed
+ }
+
+ if ( !lcl_FunctionKnown( nOpCode ) )
+ return false; // not in function list -> no change
+
+ sal_uInt16 nNewCount = std::min( static_cast<sal_uInt16>(nOldCount + 1), sal_uInt16(LRU_MAX) );
+ sal_uInt16 nNewList[LRU_MAX];
+ nNewList[0] = nOpCode;
+ for (nPos=1; nPos<nNewCount; nPos++)
+ nNewList[nPos] = pOldList[nPos-1];
+ rAppOpt.SetLRUFuncList( nNewList, nNewCount );
+
+ return true; // list has changed
+}
+
+namespace HelperNotifyChanges
+{
+ static void NotifyIfChangesListeners(const ScDocShell &rDocShell, ScMarkData& rMark,
+ SCCOL nCol, SCROW nRow, const OUString& rType = "cell-change")
+ {
+ ScModelObj* pModelObj = rDocShell.GetModel();
+
+ ScRangeList aChangeRanges;
+ for (const auto& rTab : rMark)
+ aChangeRanges.push_back( ScRange( nCol, nRow, rTab ) );
+
+ if (getMustPropagateChangesModel(pModelObj))
+ Notify(*pModelObj, aChangeRanges, rType);
+ else
+ {
+ Notify(*pModelObj, aChangeRanges, isDataAreaInvalidateType(rType)
+ ? OUString("data-area-invalidate") : OUString("data-area-extend"));
+ }
+ }
+}
+
+namespace
+{
+ class AutoCorrectQuery : public weld::MessageDialogController
+ {
+ private:
+ std::unique_ptr<weld::TextView> m_xError;
+ public:
+ AutoCorrectQuery(weld::Window* pParent, const OUString& rFormula)
+ : weld::MessageDialogController(pParent, "modules/scalc/ui/warnautocorrect.ui", "WarnAutoCorrect", "grid")
+ , m_xError(m_xBuilder->weld_text_view("error"))
+ {
+ m_xDialog->set_default_response(RET_YES);
+
+ const int nMaxWidth = m_xError->get_approximate_digit_width() * 65;
+ const int nMaxHeight = m_xError->get_height_rows(6);
+ m_xError->set_size_request(nMaxWidth, nMaxHeight);
+
+ m_xError->set_text(rFormula);
+ }
+ };
+}
+
+// actual functions
+
+// input - undo OK
+void ScViewFunc::EnterData( SCCOL nCol, SCROW nRow, SCTAB nTab,
+ const OUString& rString,
+ const EditTextObject* pData,
+ bool bMatrixExpand )
+{
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScMarkData rMark(GetViewData().GetMarkData());
+ bool bRecord = rDoc.IsUndoEnabled();
+ SCTAB i;
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocFunc &rFunc = GetViewData().GetDocFunc();
+ ScDocShellModificator aModificator( *pDocSh );
+
+ ScEditableTester aTester( rDoc, nCol,nRow, nCol,nRow, rMark );
+ if (!aTester.IsEditable())
+ {
+ ErrorMessage(aTester.GetMessageId());
+ PaintArea(nCol, nRow, nCol, nRow); // possibly the edit-engine is still painted there
+ return;
+ }
+
+ if ( bRecord )
+ rFunc.EnterListAction( STR_UNDO_ENTERDATA );
+
+ bool bFormula = false;
+
+ // do not check formula if it is a text cell
+ sal_uInt32 format = rDoc.GetNumberFormat( nCol, nRow, nTab );
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ // a single '=' character is handled as string (needed for special filters)
+ if ( pFormatter->GetType(format) != SvNumFormatType::TEXT && rString.getLength() > 1 )
+ {
+ if ( rString[0] == '=' )
+ {
+ // handle as formula
+ bFormula = true;
+ }
+ else if ( rString[0] == '+' || rString[0] == '-' )
+ {
+ // if there is more than one leading '+' or '-' character, remove the additional ones
+ sal_Int32 nIndex = 1;
+ sal_Int32 nLen = rString.getLength();
+ while ( nIndex < nLen && ( rString[ nIndex ] == '+' || rString[ nIndex ] == '-' ) )
+ {
+ ++nIndex;
+ }
+ OUString aString = rString.replaceAt( 1, nIndex - 1, u"" );
+
+ // if the remaining part without the leading '+' or '-' character
+ // is non-empty and not a number, handle as formula
+ if ( aString.getLength() > 1 )
+ {
+ double fNumber = 0;
+ if ( !pFormatter->IsNumberFormat( aString, format, fNumber ) )
+ {
+ bFormula = true;
+ }
+ }
+ }
+ }
+
+ bool bNumFmtChanged = false;
+ if ( bFormula )
+ { // formula, compile with autoCorrection
+ i = rMark.GetFirstSelected();
+ ScAddress aPos( nCol, nRow, i );
+ ScCompiler aComp( rDoc, aPos, rDoc.GetGrammar(), true, false );
+//2do: enable/disable autoCorrection via calcoptions
+ aComp.SetAutoCorrection( true );
+ if ( rString[0] == '+' || rString[0] == '-' )
+ {
+ aComp.SetExtendedErrorDetection( ScCompiler::EXTENDED_ERROR_DETECTION_NAME_BREAK );
+ }
+ OUString aFormula( rString );
+ std::unique_ptr< ScTokenArray > pArr;
+ bool bAgain;
+ do
+ {
+ bAgain = false;
+ bool bAddEqual = false;
+ pArr = aComp.CompileString( aFormula );
+ bool bCorrected = aComp.IsCorrected();
+ std::unique_ptr< ScTokenArray > pArrFirst;
+ if ( bCorrected )
+ { // try to parse with first parser-correction
+ pArrFirst = std::move( pArr );
+ pArr = aComp.CompileString( aComp.GetCorrectedFormula() );
+ }
+ if ( pArr->GetCodeError() == FormulaError::NONE )
+ {
+ bAddEqual = true;
+ aComp.CompileTokenArray();
+ bCorrected |= aComp.IsCorrected();
+ }
+ if ( bCorrected )
+ {
+ OUString aCorrectedFormula;
+ if ( bAddEqual )
+ {
+ aCorrectedFormula = "=" + aComp.GetCorrectedFormula();
+ }
+ else
+ aCorrectedFormula = aComp.GetCorrectedFormula();
+ short nResult;
+ if ( aCorrectedFormula.getLength() == 1 )
+ nResult = RET_NO; // empty formula, just '='
+ else
+ {
+ AutoCorrectQuery aQueryBox(GetViewData().GetDialogParent(), aCorrectedFormula);
+ nResult = aQueryBox.run();
+ }
+ if ( nResult == RET_YES )
+ {
+ aFormula = aCorrectedFormula;
+ bAgain = true;
+ }
+ else
+ {
+ if ( pArrFirst )
+ pArr = std::move( pArrFirst );
+ }
+ }
+ } while ( bAgain );
+ // to be used in multiple tabs, the formula must be compiled anew
+ // via ScFormulaCell copy-ctor because of RangeNames,
+ // the same code-array for all cells is not possible.
+ // If the array has an error, (it) must be RPN-erased in the newly generated
+ // cells and the error be set explicitly, so that
+ // via FormulaCell copy-ctor and Interpreter it will be, when possible,
+ // ironed out again, too intelligent... e.g.: =1))
+ FormulaError nError = pArr->GetCodeError();
+ if ( nError == FormulaError::NONE )
+ {
+ // update list of recent functions with all functions that
+ // are not within parentheses
+
+ ScModule* pScMod = SC_MOD();
+ ScAppOptions aAppOpt = pScMod->GetAppOptions();
+ bool bOptChanged = false;
+
+ formula::FormulaToken** ppToken = pArr->GetArray();
+ sal_uInt16 nTokens = pArr->GetLen();
+ sal_uInt16 nLevel = 0;
+ for (sal_uInt16 nTP=0; nTP<nTokens; nTP++)
+ {
+ formula::FormulaToken* pTok = ppToken[nTP];
+ OpCode eOp = pTok->GetOpCode();
+ if ( eOp == ocOpen )
+ ++nLevel;
+ else if ( eOp == ocClose && nLevel )
+ --nLevel;
+ if ( nLevel == 0 && pTok->IsFunction() &&
+ lcl_AddFunction( aAppOpt, sal::static_int_cast<sal_uInt16>( eOp ) ) )
+ bOptChanged = true;
+ }
+
+ if ( bOptChanged )
+ {
+ pScMod->SetAppOptions(aAppOpt);
+ }
+
+ if (bMatrixExpand)
+ {
+ // If the outer function/operator returns an array/matrix then
+ // enter a matrix formula. ScViewFunc::EnterMatrix() takes care
+ // of selection/mark of the result dimensions or preselected
+ // mark. If the user wanted less or a single cell then should
+ // mark such prior to entering the formula.
+ const formula::FormulaToken* pToken = pArr->LastRPNToken();
+ if (pToken && (formula::FormulaCompiler::IsMatrixFunction( pToken->GetOpCode())
+ || pToken->IsInForceArray()))
+ {
+ // Discard this (still empty here) Undo action,
+ // EnterMatrix() will create its own.
+ if (bRecord)
+ rFunc.EndListAction();
+
+ // Use corrected formula string.
+ EnterMatrix( aFormula, rDoc.GetGrammar());
+
+ return;
+ }
+ }
+ }
+
+ ScFormulaCell aCell(rDoc, aPos, std::move( pArr ), formula::FormulaGrammar::GRAM_DEFAULT, ScMatrixMode::NONE);
+
+ for (const auto& rTab : rMark)
+ {
+ i = rTab;
+ aPos.SetTab( i );
+ const sal_uInt32 nIndex = rDoc.GetAttr(
+ nCol, nRow, i, ATTR_VALUE_FORMAT )->GetValue();
+ const SvNumFormatType nType = pFormatter->GetType( nIndex);
+ if (nType == SvNumFormatType::TEXT ||
+ ((rString[0] == '+' || rString[0] == '-') && nError != FormulaError::NONE && rString == aFormula))
+ {
+ if ( pData )
+ {
+ // A clone of pData will be stored in the cell.
+ rFunc.SetEditCell(aPos, *pData, true);
+ }
+ else
+ rFunc.SetStringCell(aPos, aFormula, true);
+ }
+ else
+ {
+ ScFormulaCell* pCell = new ScFormulaCell( aCell, rDoc, aPos );
+ if ( nError != FormulaError::NONE )
+ {
+ pCell->GetCode()->DelRPN();
+ pCell->SetErrCode( nError );
+ if(pCell->GetCode()->IsHyperLink())
+ pCell->GetCode()->SetHyperLink(false);
+ }
+ if (nType == SvNumFormatType::LOGICAL)
+ {
+ // Reset to General so the actual format can be determined
+ // after the cell has been interpreted. A sticky boolean
+ // number format is highly likely unwanted... see tdf#75650.
+ // General of same locale as current number format.
+ const SvNumberformat* pEntry = pFormatter->GetEntry( nIndex);
+ const LanguageType nLang = (pEntry ? pEntry->GetLanguage() : ScGlobal::eLnge);
+ const sal_uInt32 nFormat = pFormatter->GetStandardFormat( SvNumFormatType::NUMBER, nLang);
+ ScPatternAttr aPattern( rDoc.GetPool());
+ aPattern.GetItemSet().Put( SfxUInt32Item( ATTR_VALUE_FORMAT, nFormat));
+ ScMarkData aMark(rDoc.GetSheetLimits());
+ aMark.SelectTable( i, true);
+ aMark.SetMarkArea( ScRange( aPos));
+ rFunc.ApplyAttributes( aMark, aPattern, false);
+ bNumFmtChanged = true;
+ }
+ rFunc.SetFormulaCell(aPos, pCell, true);
+ }
+ }
+ }
+ else
+ {
+ ScFieldEditEngine& rEngine = rDoc.GetEditEngine();
+ for (const auto& rTab : rMark)
+ {
+ bool bNumFmtSet = false;
+ const ScAddress aScAddress(nCol, nRow, rTab);
+
+ // tdf#104902 - handle embedded newline
+ if (ScStringUtil::isMultiline(rString))
+ {
+ rEngine.SetTextCurrentDefaults(rString);
+ rDoc.SetEditText(aScAddress, rEngine.CreateTextObject());
+ pDocSh->AdjustRowHeight(nRow, nRow, rTab);
+ }
+ else
+ {
+ rFunc.SetNormalString(bNumFmtSet, aScAddress, rString, false);
+ }
+
+ if (bNumFmtSet)
+ {
+ /* FIXME: if set on any sheet results in changed only on
+ * sheet nTab for TestFormatArea() and DoAutoAttributes() */
+ bNumFmtChanged = true;
+ }
+ }
+ }
+
+ bool bAutoFormat = TestFormatArea(nCol, nRow, nTab, bNumFmtChanged);
+
+ if (bAutoFormat)
+ DoAutoAttributes(nCol, nRow, nTab, bNumFmtChanged);
+
+ pDocSh->UpdateOle(GetViewData());
+
+ const OUString aType(rString.isEmpty() ? u"delete-content" : u"cell-change");
+ HelperNotifyChanges::NotifyIfChangesListeners(*pDocSh, rMark, nCol, nRow, aType);
+
+ if ( bRecord )
+ rFunc.EndListAction();
+
+ aModificator.SetDocumentModified();
+ lcl_PostRepaintCondFormat( rDoc.GetCondFormat( nCol, nRow, nTab ), pDocSh );
+ lcl_PostRepaintSparkLine(rDoc.GetSparklineList(nTab), ScRange(nCol, nRow, nTab), pDocSh);
+}
+
+// enter value in single cell (on nTab only)
+
+void ScViewFunc::EnterValue( SCCOL nCol, SCROW nRow, SCTAB nTab, const double& rValue )
+{
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+
+ if (!pDocSh)
+ return;
+
+ bool bUndo(rDoc.IsUndoEnabled());
+ ScDocShellModificator aModificator( *pDocSh );
+
+ ScEditableTester aTester( rDoc, nTab, nCol,nRow, nCol,nRow );
+ if (aTester.IsEditable())
+ {
+ ScAddress aPos( nCol, nRow, nTab );
+ ScCellValue aUndoCell;
+ if (bUndo)
+ aUndoCell.assign(rDoc, aPos);
+
+ rDoc.SetValue( nCol, nRow, nTab, rValue );
+
+ // because of ChangeTrack after change in document
+ if (bUndo)
+ {
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoEnterValue>(pDocSh, aPos, aUndoCell, rValue));
+ }
+
+ pDocSh->PostPaintCell( aPos );
+ pDocSh->UpdateOle(GetViewData());
+ aModificator.SetDocumentModified();
+ }
+ else
+ ErrorMessage(aTester.GetMessageId());
+}
+
+void ScViewFunc::EnterData( SCCOL nCol, SCROW nRow, SCTAB nTab,
+ const EditTextObject& rData, bool bTestSimple )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ bool bRecord = rDoc.IsUndoEnabled();
+
+ ScDocShellModificator aModificator( *pDocSh );
+
+ ScEditableTester aTester( rDoc, nTab, nCol,nRow, nCol,nRow );
+ if (aTester.IsEditable())
+ {
+
+ // test for attribute
+
+ bool bSimple = false;
+ bool bCommon = false;
+ std::unique_ptr<ScPatternAttr> pCellAttrs;
+ OUString aString;
+
+ const ScPatternAttr* pOldPattern = rDoc.GetPattern( nCol, nRow, nTab );
+ ScTabEditEngine aEngine( *pOldPattern, rDoc.GetEnginePool(), &rDoc );
+ aEngine.SetTextCurrentDefaults(rData);
+
+ if (bTestSimple) // test, if simple string without attribute
+ {
+ ScEditAttrTester aAttrTester( &aEngine );
+ bSimple = !aAttrTester.NeedsObject();
+ bCommon = aAttrTester.NeedsCellAttr();
+
+ // formulas have to be recognized even if they're formatted
+ // (but common attributes are still collected)
+
+ if (!bSimple)
+ {
+ OUString aParStr(aEngine.GetText( 0 ));
+ if ( aParStr[0] == '=' )
+ bSimple = true;
+ }
+
+ if (bCommon) // attribute for tab
+ {
+ pCellAttrs.reset(new ScPatternAttr( *pOldPattern ));
+ pCellAttrs->GetFromEditItemSet( &aAttrTester.GetAttribs() );
+ //! remove common attributes from EditEngine?
+ }
+ }
+
+ // #i97726# always get text for "repeat" of undo action
+ aString = ScEditUtil::GetMultilineString(aEngine);
+
+ // undo
+
+ std::unique_ptr<EditTextObject> pUndoData;
+ ScUndoEnterData::ValuesType aOldValues;
+
+ if (bRecord && !bSimple)
+ {
+ for (const auto& rTab : rMark)
+ {
+ ScUndoEnterData::Value aOldValue;
+ aOldValue.mnTab = rTab;
+ aOldValue.maCell.assign(rDoc, ScAddress(nCol, nRow, rTab));
+ aOldValues.push_back(aOldValue);
+ }
+
+ pUndoData = rData.Clone();
+ }
+
+ // enter data
+
+ if (bCommon)
+ rDoc.ApplyPattern(nCol,nRow,nTab,*pCellAttrs); //! undo
+
+ if (bSimple)
+ {
+ if (bCommon)
+ AdjustRowHeight(nRow,nRow,true);
+
+ EnterData( nCol, nRow, nTab, aString, nullptr, true /*bMatrixExpand*/);
+ }
+ else
+ {
+ for (const auto& rTab : rMark)
+ {
+ ScAddress aPos(nCol, nRow, rTab);
+ rDoc.SetEditText(aPos, rData, rDoc.GetEditPool());
+ }
+
+ if ( bRecord )
+ { // because of ChangeTrack current first
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoEnterData>(pDocSh, ScAddress(nCol,nRow,nTab), aOldValues, aString, std::move(pUndoData)));
+ }
+
+ HideAllCursors();
+
+ AdjustRowHeight(nRow,nRow,true);
+
+ for (const auto& rTab : rMark)
+ pDocSh->PostPaintCell( nCol, nRow, rTab );
+
+ ShowAllCursors();
+
+ pDocSh->UpdateOle(GetViewData());
+
+ bool bIsEmpty = rData.GetParagraphCount() == 0
+ || (rData.GetParagraphCount() == 1 && rData.GetText(0).isEmpty());
+ const OUString aType(bIsEmpty ? u"delete-content" : u"cell-change");
+ HelperNotifyChanges::NotifyIfChangesListeners(*pDocSh, rMark, nCol, nRow, aType);
+
+ aModificator.SetDocumentModified();
+ }
+ lcl_PostRepaintCondFormat( rDoc.GetCondFormat( nCol, nRow, nTab ), pDocSh );
+ }
+ else
+ {
+ ErrorMessage(aTester.GetMessageId());
+ PaintArea( nCol, nRow, nCol, nRow ); // possibly the edit-engine is still painted there
+ }
+}
+
+void ScViewFunc::EnterDataAtCursor( const OUString& rString )
+{
+ SCCOL nPosX = GetViewData().GetCurX();
+ SCROW nPosY = GetViewData().GetCurY();
+ SCTAB nTab = GetViewData().GetTabNo();
+
+ EnterData( nPosX, nPosY, nTab, rString );
+ // tdf#154174: update repeated data in the cell
+ GetViewData().GetViewShell()->UpdateInputHandler();
+}
+
+void ScViewFunc::EnterMatrix( const OUString& rString, ::formula::FormulaGrammar::Grammar eGram )
+{
+ ScViewData& rData = GetViewData();
+ const SCCOL nCol = rData.GetCurX();
+ const SCROW nRow = rData.GetCurY();
+ const ScMarkData& rMark = rData.GetMarkData();
+ if ( !rMark.IsMarked() && !rMark.IsMultiMarked() )
+ {
+ // nothing marked -> temporarily calculate block
+ // with size of result formula to get the size
+
+ ScDocument& rDoc = rData.GetDocument();
+ SCTAB nTab = rData.GetTabNo();
+ ScFormulaCell aFormCell( rDoc, ScAddress(nCol,nRow,nTab), rString, eGram, ScMatrixMode::Formula );
+
+ SCSIZE nSizeX;
+ SCSIZE nSizeY;
+ aFormCell.GetResultDimensions( nSizeX, nSizeY );
+ if ( nSizeX != 0 && nSizeY != 0 &&
+ nCol+nSizeX-1 <= sal::static_int_cast<SCSIZE>(rDoc.MaxCol()) &&
+ nRow+nSizeY-1 <= sal::static_int_cast<SCSIZE>(rDoc.MaxRow()) )
+ {
+ ScRange aResult( nCol, nRow, nTab,
+ sal::static_int_cast<SCCOL>(nCol+nSizeX-1),
+ sal::static_int_cast<SCROW>(nRow+nSizeY-1), nTab );
+ MarkRange( aResult, false );
+ }
+ }
+
+ ScRange aRange;
+ if (rData.GetSimpleArea(aRange) == SC_MARK_SIMPLE)
+ {
+ ScDocShell* pDocSh = rData.GetDocShell();
+ bool bSuccess = pDocSh->GetDocFunc().EnterMatrix(
+ aRange, &rMark, nullptr, rString, false, false, OUString(), eGram );
+ if (bSuccess)
+ pDocSh->UpdateOle(GetViewData());
+ else
+ PaintArea(nCol, nRow, nCol, nRow); // possibly the edit-engine is still painted there
+ }
+ else
+ ErrorMessage(STR_NOMULTISELECT);
+}
+
+SvtScriptType ScViewFunc::GetSelectionScriptType()
+{
+ SvtScriptType nScript = SvtScriptType::NONE;
+
+ ScDocument& rDoc = GetViewData().GetDocument();
+ const ScMarkData& rMark = GetViewData().GetMarkData();
+ if ( !rMark.IsMarked() && !rMark.IsMultiMarked() )
+ {
+ // no selection -> cursor
+
+ nScript = rDoc.GetScriptType( GetViewData().GetCurX(),
+ GetViewData().GetCurY(), GetViewData().GetTabNo());
+ }
+ else
+ {
+ ScRangeList aRanges;
+ rMark.FillRangeListWithMarks( &aRanges, false );
+ nScript = rDoc.GetRangeScriptType(aRanges);
+ }
+
+ if (nScript == SvtScriptType::NONE)
+ nScript = ScGlobal::GetDefaultScriptType();
+
+ return nScript;
+}
+
+static void ShrinkToDataArea(ScMarkData& rFuncMark, ScDocument& rDoc);
+
+const ScPatternAttr* ScViewFunc::GetSelectionPattern()
+{
+ // Don't use UnmarkFiltered in slot state functions, for performance reasons.
+ // The displayed state is always that of the whole selection including filtered rows.
+
+ ScMarkData aMark = GetViewData().GetMarkData();
+ ScDocument& rDoc = GetViewData().GetDocument();
+
+ // tdf#155368 if the selection is the whole sheet, we need to shrink the mark area, otherwise
+ // we will not return a consistent result
+ // (consistent compared to what happens in ScViewFunc::ApplySelectionPattern)
+ ShrinkToDataArea( aMark, rDoc );
+
+ if ( aMark.IsMarked() || aMark.IsMultiMarked() )
+ {
+ // MarkToMulti is no longer necessary for rDoc.GetSelectionPattern
+ const ScPatternAttr* pAttr = rDoc.GetSelectionPattern( aMark );
+ return pAttr;
+ }
+ else
+ {
+ SCCOL nCol = GetViewData().GetCurX();
+ SCROW nRow = GetViewData().GetCurY();
+ SCTAB nTab = GetViewData().GetTabNo();
+
+ // copy sheet selection
+ aMark.SetMarkArea( ScRange( nCol, nRow, nTab ) );
+ const ScPatternAttr* pAttr = rDoc.GetSelectionPattern( aMark );
+ return pAttr;
+ }
+}
+
+void ScViewFunc::GetSelectionFrame(
+ std::shared_ptr<SvxBoxItem>& rLineOuter,
+ std::shared_ptr<SvxBoxInfoItem>& rLineInner )
+{
+ ScDocument& rDoc = GetViewData().GetDocument();
+ const ScMarkData& rMark = GetViewData().GetMarkData();
+
+ if ( rMark.IsMarked() || rMark.IsMultiMarked() )
+ {
+ rDoc.GetSelectionFrame( rMark, *rLineOuter, *rLineInner );
+ }
+ else
+ {
+ const ScPatternAttr* pAttrs =
+ rDoc.GetPattern( GetViewData().GetCurX(),
+ GetViewData().GetCurY(),
+ GetViewData().GetTabNo() );
+
+ rLineOuter.reset(pAttrs->GetItem(ATTR_BORDER).Clone());
+ rLineInner.reset(pAttrs->GetItem(ATTR_BORDER_INNER).Clone());
+
+ rLineInner->SetTable(false);
+ rLineInner->SetDist(true);
+ rLineInner->SetMinDist(false);
+ }
+}
+
+// apply attribute - undo OK
+//
+// complete set ( ATTR_STARTINDEX, ATTR_ENDINDEX )
+
+void ScViewFunc::ApplyAttributes( const SfxItemSet& rDialogSet,
+ const SfxItemSet& rOldSet,
+ bool bAdjustBlockHeight)
+{
+ // not editable because of matrix only? attribute OK nonetheless
+ bool bOnlyNotBecauseOfMatrix;
+ if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
+ {
+ ErrorMessage(STR_PROTECTIONERR);
+ return;
+ }
+
+ ScPatternAttr aOldAttrs(( SfxItemSet(rOldSet) ));
+ ScPatternAttr aNewAttrs(( SfxItemSet(rDialogSet) ));
+ aNewAttrs.DeleteUnchanged( &aOldAttrs );
+
+ if ( rDialogSet.GetItemState( ATTR_VALUE_FORMAT ) == SfxItemState::SET )
+ { // don't reset to default SYSTEM GENERAL if not intended
+ sal_uInt32 nOldFormat =
+ rOldSet.Get( ATTR_VALUE_FORMAT ).GetValue();
+ sal_uInt32 nNewFormat =
+ rDialogSet.Get( ATTR_VALUE_FORMAT ).GetValue();
+ if ( nNewFormat != nOldFormat )
+ {
+ SvNumberFormatter* pFormatter =
+ GetViewData().GetDocument().GetFormatTable();
+ const SvNumberformat* pOldEntry = pFormatter->GetEntry( nOldFormat );
+ LanguageType eOldLang =
+ pOldEntry ? pOldEntry->GetLanguage() : LANGUAGE_DONTKNOW;
+ const SvNumberformat* pNewEntry = pFormatter->GetEntry( nNewFormat );
+ LanguageType eNewLang =
+ pNewEntry ? pNewEntry->GetLanguage() : LANGUAGE_DONTKNOW;
+ if ( eNewLang != eOldLang )
+ {
+ aNewAttrs.GetItemSet().Put(
+ SvxLanguageItem( eNewLang, ATTR_LANGUAGE_FORMAT ) );
+
+ // only the language has changed -> do not touch numberformat-attribute
+ sal_uInt32 nNewMod = nNewFormat % SV_COUNTRY_LANGUAGE_OFFSET;
+ if ( nNewMod == ( nOldFormat % SV_COUNTRY_LANGUAGE_OFFSET ) &&
+ nNewMod <= SV_MAX_COUNT_STANDARD_FORMATS )
+ aNewAttrs.GetItemSet().ClearItem( ATTR_VALUE_FORMAT );
+ }
+ }
+ }
+
+ if (rDialogSet.HasItem(ATTR_FONT_LANGUAGE))
+ // font language has changed. Redo the online spelling.
+ ResetAutoSpell();
+
+ const SvxBoxItem& rOldOuter = rOldSet.Get(ATTR_BORDER);
+ const SvxBoxItem& rNewOuter = rDialogSet.Get(ATTR_BORDER);
+ const SvxBoxInfoItem& rOldInner = rOldSet.Get(ATTR_BORDER_INNER);
+ const SvxBoxInfoItem& rNewInner = rDialogSet.Get(ATTR_BORDER_INNER);
+ SfxItemSet& rNewSet = aNewAttrs.GetItemSet();
+ SfxItemPool* pNewPool = rNewSet.GetPool();
+
+ pNewPool->DirectPutItemInPool(rNewOuter); // don't delete yet
+ pNewPool->DirectPutItemInPool(rNewInner);
+ rNewSet.ClearItem( ATTR_BORDER );
+ rNewSet.ClearItem( ATTR_BORDER_INNER );
+
+ /*
+ * establish whether border attribute is to be set:
+ * 1. new != old
+ * 2. is one of the borders not-DontCare (since 238.f: IsxxValid())
+ *
+ */
+
+ bool bFrame = (rDialogSet.GetItemState( ATTR_BORDER ) != SfxItemState::DEFAULT)
+ || (rDialogSet.GetItemState( ATTR_BORDER_INNER ) != SfxItemState::DEFAULT);
+
+ if (SfxPoolItem::areSame(rNewOuter, rOldOuter) && SfxPoolItem::areSame(rNewInner, rOldInner))
+ bFrame = false;
+
+ // this should be intercepted by the pool: ?!??!??
+
+ if (bFrame && rNewOuter == rOldOuter && rNewInner == rOldInner)
+ bFrame = false;
+
+ bFrame = bFrame
+ && ( rNewInner.IsValid(SvxBoxInfoItemValidFlags::LEFT)
+ || rNewInner.IsValid(SvxBoxInfoItemValidFlags::RIGHT)
+ || rNewInner.IsValid(SvxBoxInfoItemValidFlags::TOP)
+ || rNewInner.IsValid(SvxBoxInfoItemValidFlags::BOTTOM)
+ || rNewInner.IsValid(SvxBoxInfoItemValidFlags::HORI)
+ || rNewInner.IsValid(SvxBoxInfoItemValidFlags::VERT) );
+
+ if (!bFrame)
+ ApplySelectionPattern( aNewAttrs ); // standard only
+ else
+ {
+ // if new items are default-items, overwrite the old items:
+
+ bool bDefNewOuter = IsStaticDefaultItem(&rNewOuter);
+ bool bDefNewInner = IsStaticDefaultItem(&rNewInner);
+
+ ApplyPatternLines( aNewAttrs,
+ bDefNewOuter ? rOldOuter : rNewOuter,
+ bDefNewInner ? &rOldInner : &rNewInner );
+ }
+
+ pNewPool->DirectRemoveItemFromPool(rNewOuter); // release
+ pNewPool->DirectRemoveItemFromPool(rNewInner);
+
+ // adjust height only if needed
+ if (bAdjustBlockHeight)
+ AdjustBlockHeight();
+
+ // CellContentChanged is called in ApplySelectionPattern / ApplyPatternLines
+}
+
+void ScViewFunc::ApplyAttr( const SfxPoolItem& rAttrItem, bool bAdjustBlockHeight )
+{
+ // not editable because of matrix only? attribute OK nonetheless
+ bool bOnlyNotBecauseOfMatrix;
+ if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
+ {
+ ErrorMessage(STR_PROTECTIONERR);
+ return;
+ }
+
+ ScPatternAttr aNewAttrs(
+ SfxItemSetFixed<ATTR_PATTERN_START, ATTR_PATTERN_END>( *GetViewData().GetDocument().GetPool() ) );
+
+ aNewAttrs.GetItemSet().Put( rAttrItem );
+ // if justify is set (with Buttons), always indentation 0
+ if ( rAttrItem.Which() == ATTR_HOR_JUSTIFY )
+ aNewAttrs.GetItemSet().Put( ScIndentItem( 0 ) );
+ ApplySelectionPattern( aNewAttrs );
+
+ // Prevent useless compute
+ if (bAdjustBlockHeight)
+ AdjustBlockHeight();
+
+ // CellContentChanged is called in ApplySelectionPattern
+}
+
+// patterns and borders
+
+void ScViewFunc::ApplyPatternLines( const ScPatternAttr& rAttr, const SvxBoxItem& rNewOuter,
+ const SvxBoxInfoItem* pNewInner )
+{
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScMarkData aFuncMark( GetViewData().GetMarkData() ); // local copy for UnmarkFiltered
+ ScViewUtil::UnmarkFiltered( aFuncMark, rDoc );
+ bool bRecord = true;
+ if (!rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ bool bRemoveAdjCellBorder = rNewOuter.IsRemoveAdjacentCellBorder();
+ ScRange aMarkRange, aMarkRangeWithEnvelope;
+ aFuncMark.MarkToSimple();
+ bool bMulti = aFuncMark.IsMultiMarked();
+ if (bMulti)
+ aMarkRange = aFuncMark.GetMultiMarkArea();
+ else if (aFuncMark.IsMarked())
+ aMarkRange = aFuncMark.GetMarkArea();
+ else
+ {
+ aMarkRange = ScRange( GetViewData().GetCurX(),
+ GetViewData().GetCurY(), GetViewData().GetTabNo() );
+ DoneBlockMode();
+ InitOwnBlockMode( aMarkRange );
+ aFuncMark.SetMarkArea(aMarkRange);
+ MarkDataChanged();
+ }
+ if( bRemoveAdjCellBorder )
+ aFuncMark.GetSelectionCover( aMarkRangeWithEnvelope );
+ else
+ aMarkRangeWithEnvelope = aMarkRange;
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+
+ ScDocShellModificator aModificator( *pDocSh );
+
+ if (bRecord)
+ {
+ ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ SCTAB nStartTab = aMarkRange.aStart.Tab();
+ SCTAB nTabCount = rDoc.GetTableCount();
+ bool bCopyOnlyMarked = false;
+ if( !bRemoveAdjCellBorder )
+ bCopyOnlyMarked = bMulti;
+ pUndoDoc->InitUndo( rDoc, nStartTab, nStartTab );
+ for (const auto& rTab : aFuncMark)
+ if (rTab != nStartTab)
+ pUndoDoc->AddUndoTab( rTab, rTab );
+
+ ScRange aCopyRange = aMarkRangeWithEnvelope;
+ aCopyRange.aStart.SetTab(0);
+ aCopyRange.aEnd.SetTab(nTabCount-1);
+ rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, bCopyOnlyMarked, *pUndoDoc, &aFuncMark );
+
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoSelectionAttr>(
+ pDocSh, aFuncMark,
+ aMarkRange.aStart.Col(), aMarkRange.aStart.Row(), aMarkRange.aStart.Tab(),
+ aMarkRange.aEnd.Col(), aMarkRange.aEnd.Row(), aMarkRange.aEnd.Tab(),
+ std::move(pUndoDoc), bCopyOnlyMarked, &rAttr, &rNewOuter, pNewInner, &aMarkRangeWithEnvelope ) );
+ }
+
+ sal_uInt16 nExt = SC_PF_TESTMERGE;
+ pDocSh->UpdatePaintExt( nExt, aMarkRangeWithEnvelope ); // content before the change
+
+ rDoc.ApplySelectionFrame(aFuncMark, rNewOuter, pNewInner);
+
+ pDocSh->UpdatePaintExt( nExt, aMarkRangeWithEnvelope ); // content after the change
+
+ aFuncMark.MarkToMulti();
+ rDoc.ApplySelectionPattern( rAttr, aFuncMark );
+
+ pDocSh->PostPaint( aMarkRange, PaintPartFlags::Grid, nExt );
+ pDocSh->UpdateOle(GetViewData());
+ aModificator.SetDocumentModified();
+ CellContentChanged();
+
+ StartFormatArea();
+}
+
+// tdf#147842 if the marked area is the entire sheet, then shrink it to the data area.
+// Otherwise ctrl-A, perform-action, will take a very long time as it tries to modify
+// cells that we are not using.
+static void ShrinkToDataArea(ScMarkData& rFuncMark, ScDocument& rDoc)
+{
+ // do not make it marked if it is not already marked
+ if (!rFuncMark.IsMarked())
+ return;
+ if (rFuncMark.IsMultiMarked())
+ return;
+ ScRange aMarkArea = rFuncMark.GetMarkArea();
+ const ScSheetLimits& rLimits = rDoc.GetSheetLimits();
+ if (aMarkArea.aStart.Row() != 0 || aMarkArea.aStart.Col() != 0)
+ return;
+ if (aMarkArea.aEnd.Row() != rLimits.MaxRow() || aMarkArea.aEnd.Col() != rLimits.MaxCol())
+ return;
+ if (aMarkArea.aStart.Tab() != aMarkArea.aEnd.Tab())
+ return;
+ SCCOL nStartCol = aMarkArea.aStart.Col();
+ SCROW nStartRow = aMarkArea.aStart.Row();
+ SCCOL nEndCol = aMarkArea.aEnd.Col();
+ SCROW nEndRow = aMarkArea.aEnd.Row();
+ rDoc.ShrinkToDataArea(aMarkArea.aStart.Tab(), nStartCol, nStartRow, nEndCol, nEndRow);
+ aMarkArea.aStart.SetCol(nStartCol);
+ aMarkArea.aStart.SetRow(nStartRow);
+ aMarkArea.aEnd.SetCol(nEndCol);
+ aMarkArea.aEnd.SetRow(nEndRow);
+ rFuncMark.ResetMark();
+ rFuncMark.SetMarkArea(aMarkArea);
+}
+
+// pattern only
+
+void ScViewFunc::ApplySelectionPattern( const ScPatternAttr& rAttr, bool bCursorOnly )
+{
+ ScViewData& rViewData = GetViewData();
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScMarkData aFuncMark( rViewData.GetMarkData() ); // local copy for UnmarkFiltered
+ ScViewUtil::UnmarkFiltered( aFuncMark, rDoc );
+
+ bool bRecord = true;
+ if (!rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ // State from old ItemSet doesn't matter for paint flags, as any change will be
+ // from SfxItemState::SET in the new ItemSet (default is ignored in ApplyPattern).
+ // New alignment is checked (check in PostPaint isn't enough) in case a right
+ // alignment is changed to left.
+ const SfxItemSet& rNewSet = rAttr.GetItemSet();
+ bool bSetLines = rNewSet.GetItemState( ATTR_BORDER ) == SfxItemState::SET ||
+ rNewSet.GetItemState( ATTR_SHADOW ) == SfxItemState::SET;
+ bool bSetAlign = rNewSet.GetItemState( ATTR_HOR_JUSTIFY ) == SfxItemState::SET;
+
+ sal_uInt16 nExtFlags = 0;
+ if ( bSetLines )
+ nExtFlags |= SC_PF_LINES;
+ if ( bSetAlign )
+ nExtFlags |= SC_PF_WHOLEROWS;
+
+ ScDocShellModificator aModificator( *pDocSh );
+
+ bool bMulti = aFuncMark.IsMultiMarked();
+ aFuncMark.MarkToMulti();
+ bool bOnlyTab = (!aFuncMark.IsMultiMarked() && !bCursorOnly && aFuncMark.GetSelectCount() > 1);
+ if (bOnlyTab)
+ {
+ SCCOL nCol = rViewData.GetCurX();
+ SCROW nRow = rViewData.GetCurY();
+ SCTAB nTab = rViewData.GetTabNo();
+ aFuncMark.SetMarkArea(ScRange(nCol,nRow,nTab));
+ aFuncMark.MarkToMulti();
+ }
+
+ ScRangeList aChangeRanges;
+
+ if (aFuncMark.IsMultiMarked() && !bCursorOnly)
+ {
+ const ScRange& aMarkRange = aFuncMark.GetMultiMarkArea();
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (const auto& rTab : aFuncMark)
+ {
+ ScRange aChangeRange( aMarkRange );
+ aChangeRange.aStart.SetTab( rTab );
+ aChangeRange.aEnd.SetTab( rTab );
+ aChangeRanges.push_back( aChangeRange );
+ }
+
+ SCCOL nStartCol = aMarkRange.aStart.Col();
+ SCROW nStartRow = aMarkRange.aStart.Row();
+ SCTAB nStartTab = aMarkRange.aStart.Tab();
+ SCCOL nEndCol = aMarkRange.aEnd.Col();
+ SCROW nEndRow = aMarkRange.aEnd.Row();
+ SCTAB nEndTab = aMarkRange.aEnd.Tab();
+
+ ScEditDataArray* pEditDataArray = nullptr;
+ if (bRecord)
+ {
+ ScRange aCopyRange = aMarkRange;
+ aCopyRange.aStart.SetTab(0);
+ aCopyRange.aEnd.SetTab(nTabCount-1);
+
+ ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nStartTab, nStartTab );
+ for (const auto& rTab : aFuncMark)
+ if (rTab != nStartTab)
+ pUndoDoc->AddUndoTab( rTab, rTab );
+ rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, bMulti, *pUndoDoc, &aFuncMark );
+
+ aFuncMark.MarkToMulti();
+
+ ScUndoSelectionAttr* pUndoAttr = new ScUndoSelectionAttr(
+ pDocSh, aFuncMark, nStartCol, nStartRow, nStartTab,
+ nEndCol, nEndRow, nEndTab, std::move(pUndoDoc), bMulti, &rAttr );
+ pDocSh->GetUndoManager()->AddUndoAction(std::unique_ptr<ScUndoSelectionAttr>(pUndoAttr));
+ pEditDataArray = pUndoAttr->GetDataArray();
+ }
+
+ rDoc.ApplySelectionPattern( rAttr, aFuncMark, pEditDataArray );
+
+ pDocSh->PostPaint( nStartCol, nStartRow, nStartTab,
+ nEndCol, nEndRow, nEndTab,
+ PaintPartFlags::Grid, nExtFlags | SC_PF_TESTMERGE );
+ pDocSh->UpdateOle(GetViewData());
+ aModificator.SetDocumentModified();
+ CellContentChanged();
+ }
+ else // single cell - simpler undo
+ {
+ SCCOL nCol = rViewData.GetCurX();
+ SCROW nRow = rViewData.GetCurY();
+ SCTAB nTab = rViewData.GetTabNo();
+
+ std::unique_ptr<EditTextObject> pOldEditData;
+ std::unique_ptr<EditTextObject> pNewEditData;
+ ScAddress aPos(nCol, nRow, nTab);
+ ScRefCellValue aCell(rDoc, aPos);
+ if (aCell.getType() == CELLTYPE_EDIT)
+ {
+ const EditTextObject* pEditObj = aCell.getEditText();
+ pOldEditData = pEditObj->Clone();
+ rDoc.RemoveEditTextCharAttribs(aPos, rAttr);
+ pEditObj = rDoc.GetEditText(aPos);
+ pNewEditData = pEditObj->Clone();
+ }
+
+ aChangeRanges.push_back(aPos);
+ std::optional<ScPatternAttr> pOldPat(*rDoc.GetPattern( nCol, nRow, nTab ));
+
+ rDoc.ApplyPattern( nCol, nRow, nTab, rAttr );
+
+ const ScPatternAttr* pNewPat = rDoc.GetPattern( nCol, nRow, nTab );
+
+ if (bRecord)
+ {
+ std::unique_ptr<ScUndoCursorAttr> pUndo(new ScUndoCursorAttr(
+ pDocSh, nCol, nRow, nTab, &*pOldPat, pNewPat, &rAttr ));
+ pUndo->SetEditData(std::move(pOldEditData), std::move(pNewEditData));
+ pDocSh->GetUndoManager()->AddUndoAction(std::move(pUndo));
+ }
+ pOldPat.reset(); // is copied in undo (Pool)
+
+ pDocSh->PostPaint( nCol,nRow,nTab, nCol,nRow,nTab, PaintPartFlags::Grid, nExtFlags | SC_PF_TESTMERGE );
+ pDocSh->UpdateOle(GetViewData());
+ aModificator.SetDocumentModified();
+ CellContentChanged();
+ }
+
+ ScModelObj* pModelObj = pDocSh->GetModel();
+
+ if (HelperNotifyChanges::getMustPropagateChangesModel(pModelObj))
+ {
+ css::uno::Sequence< css::beans::PropertyValue > aProperties;
+ sal_Int32 nCount = 0;
+ const SfxItemPropertyMap& rMap = ScCellObj::GetCellPropertyMap();
+ for ( sal_uInt16 nWhich = ATTR_PATTERN_START; nWhich <= ATTR_PATTERN_END; ++nWhich )
+ {
+ const SfxPoolItem* pItem = nullptr;
+ if ( rNewSet.GetItemState( nWhich, true, &pItem ) == SfxItemState::SET && pItem )
+ {
+ for ( const auto pEntry : rMap.getPropertyEntries())
+ {
+ if ( pEntry->nWID == nWhich )
+ {
+ css::uno::Any aVal;
+ pItem->QueryValue( aVal, pEntry->nMemberId );
+ aProperties.realloc( nCount + 1 );
+ auto pProperties = aProperties.getArray();
+ pProperties[ nCount ].Name = pEntry->aName;
+ pProperties[ nCount ].Value = aVal;
+ ++nCount;
+ }
+ }
+ }
+ }
+ HelperNotifyChanges::Notify(*pModelObj, aChangeRanges, "attribute", aProperties);
+ }
+
+ StartFormatArea();
+}
+
+void ScViewFunc::ApplyUserItemSet( const SfxItemSet& rItemSet )
+{
+ // ItemSet from UI, may have different pool
+
+ bool bOnlyNotBecauseOfMatrix;
+ if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
+ {
+ ErrorMessage(STR_PROTECTIONERR);
+ return;
+ }
+
+ ScPatternAttr aNewAttrs( GetViewData().GetDocument().GetPool() );
+ SfxItemSet& rNewSet = aNewAttrs.GetItemSet();
+ rNewSet.Put( rItemSet, false );
+ ApplySelectionPattern( aNewAttrs );
+
+ AdjustBlockHeight();
+}
+
+const SfxStyleSheet* ScViewFunc::GetStyleSheetFromMarked()
+{
+ // Don't use UnmarkFiltered in slot state functions, for performance reasons.
+ // The displayed state is always that of the whole selection including filtered rows.
+
+ const ScStyleSheet* pSheet = nullptr;
+ ScViewData& rViewData = GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ ScMarkData& rMark = rViewData.GetMarkData();
+
+ if ( rMark.IsMarked() || rMark.IsMultiMarked() )
+ pSheet = rDoc.GetSelectionStyle( rMark ); // MarkToMulti isn't necessary
+ else
+ pSheet = rDoc.GetStyle( rViewData.GetCurX(),
+ rViewData.GetCurY(),
+ rViewData.GetTabNo() );
+
+ return pSheet;
+}
+
+void ScViewFunc::SetStyleSheetToMarked( const SfxStyleSheet* pStyleSheet )
+{
+ // not editable because of matrix only? attribute OK nonetheless
+ bool bOnlyNotBecauseOfMatrix;
+ if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
+ {
+ ErrorMessage(STR_PROTECTIONERR);
+ return;
+ }
+
+ if ( !pStyleSheet) return;
+
+ ScViewData& rViewData = GetViewData();
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScMarkData aFuncMark( rViewData.GetMarkData() ); // local copy for UnmarkFiltered
+ ScViewUtil::UnmarkFiltered( aFuncMark, rDoc );
+ SCTAB nTabCount = rDoc.GetTableCount();
+ bool bRecord = true;
+ if (!rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ ScDocShellModificator aModificator( *pDocSh );
+
+ if ( aFuncMark.IsMarked() || aFuncMark.IsMultiMarked() )
+ {
+ aFuncMark.MarkToMulti();
+ const ScRange& aMarkRange = aFuncMark.GetMultiMarkArea();
+
+ if ( bRecord )
+ {
+ SCTAB nTab = rViewData.GetTabNo();
+ ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nTab, nTab );
+ for (const auto& rTab : aFuncMark)
+ if (rTab != nTab)
+ pUndoDoc->AddUndoTab( rTab, rTab );
+
+ ScRange aCopyRange = aMarkRange;
+ aCopyRange.aStart.SetTab(0);
+ aCopyRange.aEnd.SetTab(nTabCount-1);
+ rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, true, *pUndoDoc, &aFuncMark );
+ aFuncMark.MarkToMulti();
+
+ OUString aName = pStyleSheet->GetName();
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoSelectionStyle>( pDocSh, aFuncMark, aMarkRange, aName, std::move(pUndoDoc) ) );
+ }
+
+ rDoc.ApplySelectionStyle( static_cast<const ScStyleSheet&>(*pStyleSheet), aFuncMark );
+
+ if (!AdjustBlockHeight())
+ rViewData.GetDocShell()->PostPaint( aMarkRange, PaintPartFlags::Grid );
+
+ aFuncMark.MarkToSimple();
+ }
+ else
+ {
+ SCCOL nCol = rViewData.GetCurX();
+ SCROW nRow = rViewData.GetCurY();
+ SCTAB nTab = rViewData.GetTabNo();
+
+ if ( bRecord )
+ {
+ ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nTab, nTab );
+ for (const auto& rTab : aFuncMark)
+ if (rTab != nTab)
+ pUndoDoc->AddUndoTab( rTab, rTab );
+
+ ScRange aCopyRange( nCol, nRow, 0, nCol, nRow, nTabCount-1 );
+ rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, false, *pUndoDoc );
+
+ ScRange aMarkRange ( nCol, nRow, nTab );
+ ScMarkData aUndoMark = aFuncMark;
+ aUndoMark.SetMultiMarkArea( aMarkRange );
+
+ OUString aName = pStyleSheet->GetName();
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoSelectionStyle>( pDocSh, aUndoMark, aMarkRange, aName, std::move(pUndoDoc) ) );
+ }
+
+ for (const auto& rTab : aFuncMark)
+ rDoc.ApplyStyle( nCol, nRow, rTab, static_cast<const ScStyleSheet&>(*pStyleSheet) );
+
+ if (!AdjustBlockHeight())
+ rViewData.GetDocShell()->PostPaintCell( nCol, nRow, nTab );
+
+ }
+
+ aModificator.SetDocumentModified();
+
+ StartFormatArea();
+}
+
+void ScViewFunc::RemoveStyleSheetInUse( const SfxStyleSheetBase* pStyleSheet )
+{
+ if ( !pStyleSheet) return;
+
+ ScViewData& rViewData = GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+
+ ScDocShellModificator aModificator( *pDocSh );
+
+ ScopedVclPtrInstance< VirtualDevice > pVirtDev;
+ pVirtDev->SetMapMode(MapMode(MapUnit::MapPixel));
+ rDoc.StyleSheetChanged( pStyleSheet, true, pVirtDev,
+ rViewData.GetPPTX(),
+ rViewData.GetPPTY(),
+ rViewData.GetZoomX(),
+ rViewData.GetZoomY() );
+
+ pDocSh->PostPaint( 0,0,0, rDoc.MaxCol(), rDoc.MaxRow(), MAXTAB, PaintPartFlags::Grid|PaintPartFlags::Left );
+ aModificator.SetDocumentModified();
+
+ ScInputHandler* pHdl = SC_MOD()->GetInputHdl();
+ if (pHdl)
+ pHdl->ForgetLastPattern();
+}
+
+void ScViewFunc::UpdateStyleSheetInUse( const SfxStyleSheetBase* pStyleSheet )
+{
+ if ( !pStyleSheet) return;
+
+ ScViewData& rViewData = GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+
+ ScDocShellModificator aModificator( *pDocSh );
+
+ ScopedVclPtrInstance< VirtualDevice > pVirtDev;
+ pVirtDev->SetMapMode(MapMode(MapUnit::MapPixel));
+ rDoc.StyleSheetChanged( pStyleSheet, false, pVirtDev,
+ rViewData.GetPPTX(),
+ rViewData.GetPPTY(),
+ rViewData.GetZoomX(),
+ rViewData.GetZoomY() );
+
+ pDocSh->PostPaint( 0,0,0, rDoc.MaxCol(), rDoc.MaxRow(), MAXTAB, PaintPartFlags::Grid|PaintPartFlags::Left );
+ aModificator.SetDocumentModified();
+
+ ScInputHandler* pHdl = SC_MOD()->GetInputHdl();
+ if (pHdl)
+ pHdl->ForgetLastPattern();
+}
+
+
+void ScViewFunc::OnLOKInsertDeleteColumn(SCCOL nStartCol, tools::Long nOffset)
+{
+ if (!comphelper::LibreOfficeKit::isActive() || nOffset == 0)
+ return;
+
+ SCTAB nCurrentTabIndex = GetViewData().GetTabNo();
+ SfxViewShell* pCurrentViewShell = GetViewData().GetViewShell();
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
+ if (pTabViewShell && pTabViewShell->GetDocId() == pCurrentViewShell->GetDocId())
+ {
+ if (ScPositionHelper* pPosHelper = pTabViewShell->GetViewData().GetLOKWidthHelper(nCurrentTabIndex))
+ pPosHelper->invalidateByIndex(nStartCol);
+
+ // if we remove a column the cursor position and the current selection
+ // in other views could need to be moved on the left by one column.
+ if (pTabViewShell != this)
+ {
+ if (pTabViewShell->getPart() == nCurrentTabIndex)
+ {
+ SCCOL nX = pTabViewShell->GetViewData().GetCurX();
+ if (nX > nStartCol || (nX == nStartCol && nOffset > 0))
+ {
+ ScInputHandler* pInputHdl = pTabViewShell->GetInputHandler();
+ SCROW nY = pTabViewShell->GetViewData().GetCurY();
+ pTabViewShell->SetCursor(nX + nOffset, nY);
+ if (pInputHdl && pInputHdl->IsInputMode())
+ {
+ pInputHdl->SetModified();
+ }
+ }
+
+ ScMarkData aMultiMark( pTabViewShell->GetViewData().GetMarkData() );
+ aMultiMark.SetMarking( false );
+ aMultiMark.MarkToMulti();
+ if (aMultiMark.IsMultiMarked())
+ {
+ aMultiMark.ShiftCols(pTabViewShell->GetViewData().GetDocument(), nStartCol, nOffset);
+ pTabViewShell->SetMarkData(aMultiMark);
+ }
+ }
+ else
+ {
+ SCROW nX = pTabViewShell->GetViewData().GetCurXForTab(nCurrentTabIndex);
+ if (nX > nStartCol || (nX == nStartCol && nOffset > 0))
+ {
+ pTabViewShell->GetViewData().SetCurXForTab(nX + nOffset, nCurrentTabIndex);
+ }
+ }
+ }
+ }
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+}
+
+void ScViewFunc::OnLOKInsertDeleteRow(SCROW nStartRow, tools::Long nOffset)
+{
+ if (!comphelper::LibreOfficeKit::isActive() || nOffset == 0)
+ return;
+
+ SCTAB nCurrentTabIndex = GetViewData().GetTabNo();
+ SfxViewShell* pCurrentViewShell = GetViewData().GetViewShell();
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
+ if (pTabViewShell && pTabViewShell->GetDocId() == pCurrentViewShell->GetDocId())
+ {
+ if (ScPositionHelper* pPosHelper = pTabViewShell->GetViewData().GetLOKHeightHelper(nCurrentTabIndex))
+ pPosHelper->invalidateByIndex(nStartRow);
+
+ // if we remove a row the cursor position and the current selection
+ // in other views could need to be moved up by one row.
+ if (pTabViewShell != this)
+ {
+ if (pTabViewShell->getPart() == nCurrentTabIndex)
+ {
+ SCROW nY = pTabViewShell->GetViewData().GetCurY();
+ if (nY > nStartRow || (nY == nStartRow && nOffset > 0))
+ {
+ ScInputHandler* pInputHdl = pTabViewShell->GetInputHandler();
+ SCCOL nX = pTabViewShell->GetViewData().GetCurX();
+ pTabViewShell->SetCursor(nX, nY + nOffset);
+ if (pInputHdl && pInputHdl->IsInputMode())
+ {
+ pInputHdl->SetModified();
+ }
+ }
+
+ ScMarkData aMultiMark( pTabViewShell->GetViewData().GetMarkData() );
+ aMultiMark.SetMarking( false );
+ aMultiMark.MarkToMulti();
+ if (aMultiMark.IsMultiMarked())
+ {
+ aMultiMark.ShiftRows(pTabViewShell->GetViewData().GetDocument(), nStartRow, nOffset);
+ pTabViewShell->SetMarkData(aMultiMark);
+ }
+ }
+ else
+ {
+ SCROW nY = pTabViewShell->GetViewData().GetCurYForTab(nCurrentTabIndex);
+ if (nY > nStartRow || (nY == nStartRow && nOffset > 0))
+ {
+ pTabViewShell->GetViewData().SetCurYForTab(nY + nOffset, nCurrentTabIndex);
+ }
+ }
+ }
+ }
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+}
+
+void ScViewFunc::OnLOKSetWidthOrHeight(SCCOLROW nStart, bool bWidth)
+{
+ if (!comphelper::LibreOfficeKit::isActive())
+ return;
+
+ SCTAB nCurTab = GetViewData().GetTabNo();
+ SfxViewShell* pCurrentViewShell = GetViewData().GetViewShell();
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
+ if (pTabViewShell && pTabViewShell->GetDocId() == pCurrentViewShell->GetDocId())
+ {
+ if (bWidth)
+ {
+ if (ScPositionHelper* pPosHelper = pTabViewShell->GetViewData().GetLOKWidthHelper(nCurTab))
+ pPosHelper->invalidateByIndex(nStart);
+ }
+ else
+ {
+ if (ScPositionHelper* pPosHelper = pTabViewShell->GetViewData().GetLOKHeightHelper(nCurTab))
+ pPosHelper->invalidateByIndex(nStart);
+ }
+ }
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+}
+
+// insert cells - undo OK
+
+bool ScViewFunc::InsertCells( InsCellCmd eCmd, bool bRecord, bool bPartOfPaste )
+{
+ ScRange aRange;
+ ScMarkType eMarkType = GetViewData().GetSimpleArea(aRange);
+ if (eMarkType == SC_MARK_SIMPLE || eMarkType == SC_MARK_SIMPLE_FILTERED)
+ {
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ const ScMarkData& rMark = GetViewData().GetMarkData();
+ bool bSuccess = pDocSh->GetDocFunc().InsertCells( aRange, &rMark, eCmd, bRecord, false, bPartOfPaste );
+ if (bSuccess)
+ {
+ ResetAutoSpellForContentChange();
+ bool bInsertCols = ( eCmd == INS_INSCOLS_BEFORE || eCmd == INS_INSCOLS_AFTER);
+ bool bInsertRows = ( eCmd == INS_INSROWS_BEFORE || eCmd == INS_INSROWS_AFTER );
+
+ pDocSh->UpdateOle(GetViewData());
+ CellContentChanged();
+
+ if ( bInsertCols || bInsertRows )
+ {
+ OUString aOperation = bInsertRows ?
+ OUString("insert-rows"):
+ OUString("insert-columns");
+ HelperNotifyChanges::NotifyIfChangesListeners(*pDocSh, aRange, aOperation);
+ }
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ if (bInsertCols)
+ ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), COLUMN_HEADER, GetViewData().GetTabNo());
+
+ if (bInsertRows)
+ ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), ROW_HEADER, GetViewData().GetTabNo());
+
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
+ bInsertCols, bInsertRows, true /* bSizes*/,
+ true /* bHidden */, true /* bFiltered */,
+ true /* bGroups */, GetViewData().GetTabNo());
+ }
+ }
+ else
+ {
+ ErrorMessage(STR_ERR_INSERT_CELLS);
+ }
+
+ OUString aStartAddress = aRange.aStart.GetColRowString();
+ OUString aEndAddress = aRange.aEnd.GetColRowString();
+ collectUIInformation({{"RANGE", aStartAddress + ":" + aEndAddress}}, "INSERT_CELLS");
+ return bSuccess;
+ }
+ else
+ {
+ ErrorMessage(STR_NOMULTISELECT);
+ return false;
+ }
+}
+
+// delete cells - undo OK
+
+void ScViewFunc::DeleteCells( DelCellCmd eCmd )
+{
+ ScRange aRange;
+ if ( GetViewData().GetSimpleArea( aRange ) == SC_MARK_SIMPLE )
+ {
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ const ScMarkData& rMark = GetViewData().GetMarkData();
+
+#if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
+ // #i94841# [Collaboration] if deleting rows is rejected, the content is sometimes wrong
+ if ( pDocSh->IsDocShared() && ( eCmd == DelCellCmd::Rows || eCmd == DelCellCmd::Cols ) )
+ {
+ ScRange aDelRange( aRange.aStart );
+ SCCOLROW nCount = 0;
+ if ( eCmd == DelCellCmd::Rows )
+ {
+ nCount = sal::static_int_cast< SCCOLROW >( aRange.aEnd.Row() - aRange.aStart.Row() + 1 );
+ }
+ else
+ {
+ nCount = sal::static_int_cast< SCCOLROW >( aRange.aEnd.Col() - aRange.aStart.Col() + 1 );
+ }
+ while ( nCount > 0 )
+ {
+ pDocSh->GetDocFunc().DeleteCells( aDelRange, &rMark, eCmd, false );
+ --nCount;
+ }
+ }
+ else
+#endif
+ {
+ pDocSh->GetDocFunc().DeleteCells( aRange, &rMark, eCmd, false );
+ }
+
+ ResetAutoSpellForContentChange();
+ pDocSh->UpdateOle(GetViewData());
+ CellContentChanged();
+
+ if ( eCmd == DelCellCmd::Rows || eCmd == DelCellCmd::Cols )
+ {
+ OUString aOperation = ( eCmd == DelCellCmd::Rows) ?
+ OUString("delete-rows"):
+ OUString("delete-columns");
+ HelperNotifyChanges::NotifyIfChangesListeners(*pDocSh, aRange, aOperation);
+ }
+
+ // put cursor directly behind deleted range
+ SCCOL nCurX = GetViewData().GetCurX();
+ SCROW nCurY = GetViewData().GetCurY();
+ if ( eCmd==DelCellCmd::CellsLeft || eCmd==DelCellCmd::Cols )
+ nCurX = aRange.aStart.Col();
+ else
+ nCurY = aRange.aStart.Row();
+ SetCursor( nCurX, nCurY );
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ bool bColsDeleted = (eCmd == DelCellCmd::Cols);
+ bool bRowsDeleted = (eCmd == DelCellCmd::Rows);
+ if (bColsDeleted)
+ ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), COLUMN_HEADER, GetViewData().GetTabNo());
+
+ if (bRowsDeleted)
+ ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), ROW_HEADER, GetViewData().GetTabNo());
+
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
+ bColsDeleted, bRowsDeleted, true /* bSizes*/,
+ true /* bHidden */, true /* bFiltered */,
+ true /* bGroups */, GetViewData().GetTabNo());
+ }
+ }
+ else
+ {
+ if (eCmd == DelCellCmd::Cols)
+ DeleteMulti( false );
+ else if (eCmd == DelCellCmd::Rows)
+ DeleteMulti( true );
+ else
+ ErrorMessage(STR_NOMULTISELECT);
+ }
+
+ OUString aStartAddress = aRange.aStart.GetColRowString();
+ OUString aEndAddress = aRange.aEnd.GetColRowString();
+ collectUIInformation({{"RANGE", aStartAddress + ":" + aEndAddress}}, "DELETE_CELLS");
+
+ Unmark();
+}
+
+void ScViewFunc::DeleteMulti( bool bRows )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocShellModificator aModificator( *pDocSh );
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScMarkData aFuncMark( GetViewData().GetMarkData() ); // local copy for UnmarkFiltered
+ ScViewUtil::UnmarkFiltered( aFuncMark, rDoc );
+
+ bool bRecord = true;
+ if (!rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ std::vector<sc::ColRowSpan> aSpans;
+ if (bRows)
+ aSpans = aFuncMark.GetMarkedRowSpans();
+ else
+ aSpans = aFuncMark.GetMarkedColSpans();
+
+ if (aSpans.empty())
+ {
+ SCCOLROW nCurPos = bRows ? GetViewData().GetCurY() : GetViewData().GetCurX();
+ aSpans.emplace_back(nCurPos, nCurPos);
+ }
+
+ // test if allowed
+
+ TranslateId pErrorId;
+ bool bNeedRefresh = false;
+ for (size_t i = 0, n = aSpans.size(); i < n && !pErrorId; ++i)
+ {
+ SCCOLROW nStart = aSpans[i].mnStart;
+ SCCOLROW nEnd = aSpans[i].mnEnd;
+
+ SCCOL nStartCol, nEndCol;
+ SCROW nStartRow, nEndRow;
+ if ( bRows )
+ {
+ nStartCol = 0;
+ nEndCol = rDoc.MaxCol();
+ nStartRow = static_cast<SCROW>(nStart);
+ nEndRow = static_cast<SCROW>(nEnd);
+ }
+ else
+ {
+ nStartCol = static_cast<SCCOL>(nStart);
+ nEndCol = static_cast<SCCOL>(nEnd);
+ nStartRow = 0;
+ nEndRow = rDoc.MaxRow();
+ }
+
+ // cell protection (only needed for first range, as all following cells are moved)
+ if (i == 0)
+ {
+ // test to the end of the sheet
+ ScEditableTester aTester( rDoc, nTab, nStartCol, nStartRow, rDoc.MaxCol(), rDoc.MaxRow() );
+ if (!aTester.IsEditable())
+ pErrorId = aTester.GetMessageId();
+ }
+
+ // merged cells
+ SCCOL nMergeStartX = nStartCol;
+ SCROW nMergeStartY = nStartRow;
+ SCCOL nMergeEndX = nEndCol;
+ SCROW nMergeEndY = nEndRow;
+ rDoc.ExtendMerge( nMergeStartX, nMergeStartY, nMergeEndX, nMergeEndY, nTab );
+ rDoc.ExtendOverlapped( nMergeStartX, nMergeStartY, nMergeEndX, nMergeEndY, nTab );
+
+ if ( nMergeStartX != nStartCol || nMergeStartY != nStartRow )
+ {
+ // Disallow deleting parts of a merged cell.
+ // Deleting the start is allowed (merge is removed), so the end doesn't have to be checked.
+
+ pErrorId = STR_MSSG_DELETECELLS_0;
+ }
+ if ( nMergeEndX != nEndCol || nMergeEndY != nEndRow )
+ {
+ // detect if the start of a merged cell is deleted, so the merge flags can be refreshed
+
+ bNeedRefresh = true;
+ }
+ }
+
+ if (pErrorId)
+ {
+ ErrorMessage(pErrorId);
+ return;
+ }
+
+ // proceed
+
+ weld::WaitObject aWait(GetViewData().GetDialogParent()); // important for TrackFormulas in UpdateReference
+
+ ResetAutoSpellForContentChange();
+
+ ScDocumentUniquePtr pUndoDoc;
+ std::unique_ptr<ScRefUndoData> pUndoData;
+ if (bRecord)
+ {
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nTab, nTab, !bRows, bRows ); // row height
+
+ for (const sc::ColRowSpan & rSpan : aSpans)
+ {
+ SCCOLROW nStart = rSpan.mnStart;
+ SCCOLROW nEnd = rSpan.mnEnd;
+ if (bRows)
+ rDoc.CopyToDocument( 0,nStart,nTab, rDoc.MaxCol(), nEnd,nTab, InsertDeleteFlags::ALL,false,*pUndoDoc );
+ else
+ rDoc.CopyToDocument( static_cast<SCCOL>(nStart),0,nTab,
+ static_cast<SCCOL>(nEnd), rDoc.MaxRow(), nTab,
+ InsertDeleteFlags::ALL,false,*pUndoDoc );
+ }
+
+ // all Formulas because of references
+ SCTAB nTabCount = rDoc.GetTableCount();
+ pUndoDoc->AddUndoTab( 0, nTabCount-1 );
+ rDoc.CopyToDocument( 0,0,0, rDoc.MaxCol(), rDoc.MaxRow(), MAXTAB, InsertDeleteFlags::FORMULA,false,*pUndoDoc );
+
+ pUndoData.reset(new ScRefUndoData( &rDoc ));
+
+ rDoc.BeginDrawUndo();
+ }
+
+ std::vector<sc::ColRowSpan>::const_reverse_iterator ri = aSpans.rbegin(), riEnd = aSpans.rend();
+ aFuncMark.SelectOneTable(nTab);
+ for (; ri != riEnd; ++ri)
+ {
+ SCCOLROW nEnd = ri->mnEnd;
+ SCCOLROW nStart = ri->mnStart;
+
+ if (bRows)
+ {
+ rDoc.DeleteObjectsInArea(0, nStart, rDoc.MaxCol(), nEnd, aFuncMark, true);
+ rDoc.DeleteRow(0, nTab, rDoc.MaxCol(), nTab, nStart, static_cast<SCSIZE>(nEnd - nStart + 1));
+ }
+ else
+ {
+ rDoc.DeleteObjectsInArea(nStart, 0, nEnd, rDoc.MaxRow(), aFuncMark, true);
+ rDoc.DeleteCol(0, nTab, rDoc.MaxRow(), nTab, static_cast<SCCOL>(nStart), static_cast<SCSIZE>(nEnd - nStart + 1));
+ }
+ }
+
+ if (bNeedRefresh)
+ {
+ SCCOLROW nFirstStart = aSpans[0].mnStart;
+ SCCOL nStartCol = bRows ? 0 : static_cast<SCCOL>(nFirstStart);
+ SCROW nStartRow = bRows ? static_cast<SCROW>(nFirstStart) : 0;
+ SCCOL nEndCol = rDoc.MaxCol();
+ SCROW nEndRow = rDoc.MaxRow();
+
+ rDoc.RemoveFlagsTab( nStartCol, nStartRow, nEndCol, nEndRow, nTab, ScMF::Hor | ScMF::Ver );
+ rDoc.ExtendMerge( nStartCol, nStartRow, nEndCol, nEndRow, nTab, true );
+ }
+
+ if (bRecord)
+ {
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoDeleteMulti>(
+ pDocSh, bRows, bNeedRefresh, nTab, std::vector(aSpans), std::move(pUndoDoc), std::move(pUndoData)));
+ }
+
+ if (!AdjustRowHeight(0, rDoc.MaxRow(), true))
+ {
+ if (bRows)
+ {
+ pDocSh->PostPaint(
+ 0, aSpans[0].mnStart, nTab,
+ rDoc.MaxCol(), rDoc.MaxRow(), nTab, (PaintPartFlags::Grid | PaintPartFlags::Left));
+ }
+ else
+ {
+ pDocSh->PostPaint(
+ static_cast<SCCOL>(aSpans[0].mnStart), 0, nTab,
+ rDoc.MaxCol(), rDoc.MaxRow(), nTab, (PaintPartFlags::Grid | PaintPartFlags::Top));
+ }
+ }
+
+ aModificator.SetDocumentModified();
+
+ CellContentChanged();
+
+ // put cursor directly behind the first deleted range
+ SCCOL nCurX = GetViewData().GetCurX();
+ SCROW nCurY = GetViewData().GetCurY();
+ if ( bRows )
+ nCurY = aSpans[0].mnStart;
+ else
+ nCurX = static_cast<SCCOL>(aSpans[0].mnStart);
+ SetCursor( nCurX, nCurY );
+
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
+}
+
+// delete contents
+
+void ScViewFunc::DeleteContents( InsertDeleteFlags nFlags )
+{
+ ScViewData& rViewData = GetViewData();
+ rViewData.SetPasteMode( ScPasteFlags::NONE );
+ rViewData.GetViewShell()->UpdateCopySourceOverlay();
+
+ // not editable because of matrix only? attribute OK nonetheless
+ bool bOnlyNotBecauseOfMatrix;
+ bool bEditable = SelectionEditable( &bOnlyNotBecauseOfMatrix );
+ if ( !bEditable )
+ {
+ if ( !(bOnlyNotBecauseOfMatrix &&
+ ((nFlags & (InsertDeleteFlags::ATTRIB | InsertDeleteFlags::EDITATTR)) == nFlags)) )
+ {
+ ErrorMessage(bOnlyNotBecauseOfMatrix ? STR_MATRIXFRAGMENTERR : STR_PROTECTIONERR);
+ return;
+ }
+ }
+
+ ScRange aMarkRange;
+ bool bSimple = false;
+
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScMarkData aFuncMark( GetViewData().GetMarkData() ); // local copy for UnmarkFiltered
+ ScViewUtil::UnmarkFiltered( aFuncMark, rDoc );
+
+ bool bRecord =true;
+ if (!rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ if ( !aFuncMark.IsMarked() && !aFuncMark.IsMultiMarked() )
+ {
+ aMarkRange.aStart.SetCol(GetViewData().GetCurX());
+ aMarkRange.aStart.SetRow(GetViewData().GetCurY());
+ aMarkRange.aStart.SetTab(GetViewData().GetTabNo());
+ aMarkRange.aEnd = aMarkRange.aStart;
+ if ( rDoc.HasAttrib( aMarkRange, HasAttrFlags::Merged ) )
+ {
+ aFuncMark.SetMarkArea( aMarkRange );
+ }
+ else
+ bSimple = true;
+ }
+
+ HideAllCursors(); // for if summary is cancelled
+
+ ScDocFunc& rDocFunc = pDocSh->GetDocFunc();
+
+ // Can we really be sure that we can pass the bApi parameter as false to DeleteCell() and
+ // DeleteContents() here? (Meaning that this is interactive use.) Is this never invoked from
+ // scripting and whatnot?
+ if (bSimple)
+ rDocFunc.DeleteCell(aMarkRange.aStart, aFuncMark, nFlags, bRecord, /*bApi=*/ false);
+ else
+ rDocFunc.DeleteContents(aFuncMark, nFlags, bRecord, /*bApi=*/ false);
+
+ pDocSh->UpdateOle(GetViewData());
+
+ if (ScModelObj* pModelObj = pDocSh->GetModel())
+ {
+ ScRangeList aChangeRanges;
+ if ( bSimple )
+ {
+ aChangeRanges.push_back( aMarkRange );
+ }
+ else
+ {
+ aFuncMark.FillRangeListWithMarks( &aChangeRanges, false );
+ }
+
+ if (HelperNotifyChanges::getMustPropagateChangesModel(pModelObj))
+ HelperNotifyChanges::Notify(*pModelObj, aChangeRanges, "delete-content");
+ else if (pModelObj)
+ HelperNotifyChanges::Notify(*pModelObj, aChangeRanges, "data-area-invalidate");
+ }
+
+ CellContentChanged();
+ ShowAllCursors();
+
+ if ( nFlags & InsertDeleteFlags::ATTRIB )
+ {
+ if ( nFlags & InsertDeleteFlags::CONTENTS )
+ bFormatValid = false;
+ else
+ StartFormatArea(); // delete attribute is also attribute-change
+ }
+ OUString aStartAddress = aMarkRange.aStart.GetColRowString();
+ OUString aEndAddress = aMarkRange.aEnd.GetColRowString();
+ collectUIInformation({{"RANGE", aStartAddress + ":" + aEndAddress}}, "DELETE");
+}
+
+// column width/row height (via header) - undo OK
+
+void ScViewFunc::SetWidthOrHeight(
+ bool bWidth, const std::vector<sc::ColRowSpan>& rRanges, ScSizeMode eMode,
+ sal_uInt16 nSizeTwips, bool bRecord, const ScMarkData* pMarkData )
+{
+ if (rRanges.empty())
+ return;
+
+ // Use view's mark if none specified, but do not modify the original data,
+ // i.e. no MarkToMulti() on that.
+ ScMarkData aMarkData( pMarkData ? *pMarkData : GetViewData().GetMarkData());
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCCOL nCurX = GetViewData().GetCurX();
+ SCROW nCurY = GetViewData().GetCurY();
+ SCTAB nFirstTab = aMarkData.GetFirstSelected();
+ SCTAB nCurTab = GetViewData().GetTabNo();
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ ScDocShellModificator aModificator( *pDocSh );
+
+ bool bAllowed = true;
+ for (const SCTAB& nTab : aMarkData)
+ {
+ bAllowed = std::all_of(rRanges.begin(), rRanges.end(),
+ [&bWidth, &rDoc, &nTab](const sc::ColRowSpan& rRange) {
+ bool bOnlyMatrix;
+ bool bIsBlockEditable;
+ if (bWidth)
+ bIsBlockEditable = rDoc.IsBlockEditable(nTab, rRange.mnStart, 0, rRange.mnEnd, rDoc.MaxRow(), &bOnlyMatrix);
+ else
+ bIsBlockEditable = rDoc.IsBlockEditable(nTab, 0, rRange.mnStart, rDoc.MaxCol(), rRange.mnEnd, &bOnlyMatrix);
+ return bIsBlockEditable || bOnlyMatrix;
+ });
+ if (!bAllowed)
+ break;
+ }
+
+ // Allow users to resize cols/rows in readonly docs despite the r/o state.
+ // It is frustrating to be unable to see content in mis-sized cells.
+ if( !bAllowed && !pDocSh->IsReadOnly() )
+ {
+ ErrorMessage(STR_PROTECTIONERR);
+ return;
+ }
+
+ SCCOLROW nStart = rRanges.front().mnStart;
+ SCCOLROW nEnd = rRanges.back().mnEnd;
+
+ OnLOKSetWidthOrHeight(nStart, bWidth);
+
+ bool bFormula = false;
+ if ( eMode == SC_SIZE_OPTIMAL )
+ {
+ const ScViewOptions& rOpts = GetViewData().GetOptions();
+ bFormula = rOpts.GetOption( VOPT_FORMULAS );
+ }
+
+ ScDocumentUniquePtr pUndoDoc;
+ std::unique_ptr<ScOutlineTable> pUndoTab;
+ std::vector<sc::ColRowSpan> aUndoRanges;
+
+ if ( bRecord )
+ {
+ rDoc.BeginDrawUndo(); // Drawing Updates
+
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ for (const SCTAB& nTab : aMarkData)
+ {
+ if (bWidth)
+ {
+ if ( nTab == nFirstTab )
+ pUndoDoc->InitUndo( rDoc, nTab, nTab, true );
+ else
+ pUndoDoc->AddUndoTab( nTab, nTab, true );
+ rDoc.CopyToDocument( static_cast<SCCOL>(nStart), 0, nTab,
+ static_cast<SCCOL>(nEnd), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE,
+ false, *pUndoDoc );
+ }
+ else
+ {
+ if ( nTab == nFirstTab )
+ pUndoDoc->InitUndo( rDoc, nTab, nTab, false, true );
+ else
+ pUndoDoc->AddUndoTab( nTab, nTab, false, true );
+ rDoc.CopyToDocument( 0, nStart, nTab, rDoc.MaxCol(), nEnd, nTab, InsertDeleteFlags::NONE, false, *pUndoDoc );
+ }
+ }
+
+ aUndoRanges = rRanges;
+
+ //! outlines from all tab?
+ ScOutlineTable* pTable = rDoc.GetOutlineTable( nCurTab );
+ if (pTable)
+ pUndoTab.reset(new ScOutlineTable( *pTable ));
+ }
+
+ if ( eMode==SC_SIZE_OPTIMAL || eMode==SC_SIZE_VISOPT )
+ aMarkData.MarkToMulti();
+
+ bool bShow = nSizeTwips > 0 || eMode != SC_SIZE_DIRECT;
+ bool bOutline = false;
+
+ for (const SCTAB& nTab : aMarkData)
+ {
+ for (const sc::ColRowSpan & rRange : rRanges)
+ {
+ SCCOLROW nStartNo = rRange.mnStart;
+ SCCOLROW nEndNo = rRange.mnEnd;
+
+ if ( !bWidth ) // height always blockwise
+ {
+ if ( eMode==SC_SIZE_OPTIMAL || eMode==SC_SIZE_VISOPT )
+ {
+ bool bAll = ( eMode==SC_SIZE_OPTIMAL );
+ bool bFiltered = false;
+ if (!bAll)
+ {
+ // delete CRFlags::ManualSize for all in range,
+ // then SetOptimalHeight with bShrink = FALSE
+ for (SCROW nRow = nStartNo; nRow <= nEndNo; ++nRow)
+ {
+ SCROW nLastRow = nRow;
+ if (rDoc.RowHidden(nRow, nTab, nullptr, &nLastRow))
+ {
+ nRow = nLastRow;
+ continue;
+ }
+
+ CRFlags nOld = rDoc.GetRowFlags(nRow, nTab);
+ if (nOld & CRFlags::ManualSize)
+ rDoc.SetRowFlags(nRow, nTab, nOld & ~CRFlags::ManualSize);
+ }
+ }
+ else
+ {
+ SCROW nLastRow = nStartNo;
+ if (rDoc.RowFiltered(nStartNo, nTab, nullptr, &nLastRow)
+ || nLastRow < nEndNo)
+ bFiltered = true;
+ }
+
+
+ double nPPTX = GetViewData().GetPPTX();
+ double nPPTY = GetViewData().GetPPTY();
+ Fraction aZoomX = GetViewData().GetZoomX();
+ Fraction aZoomY = GetViewData().GetZoomY();
+
+ ScSizeDeviceProvider aProv(pDocSh);
+ if (aProv.IsPrinter())
+ {
+ nPPTX = aProv.GetPPTX();
+ nPPTY = aProv.GetPPTY();
+ aZoomX = aZoomY = Fraction( 1, 1 );
+ }
+
+ sc::RowHeightContext aCxt(rDoc.MaxRow(), nPPTX, nPPTY, aZoomX, aZoomY, aProv.GetDevice());
+ aCxt.setForceAutoSize(bAll);
+ aCxt.setExtraHeight(nSizeTwips);
+ rDoc.SetOptimalHeight(aCxt, nStartNo, nEndNo, nTab, true);
+
+ if (bFiltered)
+ ShowFilteredRows(rDoc, nTab, nStartNo, nEndNo, bShow);
+
+ // Manual-Flag already (re)set in SetOptimalHeight in case of bAll=sal_True
+ // (set for Extra-Height, else reset).
+ }
+ else if ( eMode==SC_SIZE_DIRECT )
+ {
+ if (nSizeTwips)
+ {
+ rDoc.SetRowHeightRange( nStartNo, nEndNo, nTab, nSizeTwips );
+ rDoc.SetManualHeight( nStartNo, nEndNo, nTab, true ); // height was set manually
+ }
+
+ // tdf#36383 - Skip consecutive rows hidden by AutoFilter
+ ShowFilteredRows(rDoc, nTab, nStartNo, nEndNo, nSizeTwips != 0);
+
+ if (!bShow && nStartNo <= nCurY && nCurY <= nEndNo && nTab == nCurTab)
+ {
+ nCurY = -1;
+ }
+ }
+ else if ( eMode==SC_SIZE_SHOW )
+ {
+ rDoc.ShowRows( nStartNo, nEndNo, nTab, true );
+ }
+ }
+ else // column width
+ {
+ for (SCCOL nCol=static_cast<SCCOL>(nStartNo); nCol<=static_cast<SCCOL>(nEndNo); nCol++)
+ {
+ const bool bIsColHidden = rDoc.ColHidden(nCol, nTab);
+ if ( eMode != SC_SIZE_VISOPT || !bIsColHidden )
+ {
+ sal_uInt16 nThisSize = nSizeTwips;
+
+ if ( eMode==SC_SIZE_OPTIMAL || eMode==SC_SIZE_VISOPT )
+ nThisSize = nSizeTwips + GetOptimalColWidth( nCol, nTab, bFormula );
+ if ( nThisSize )
+ rDoc.SetColWidth( nCol, nTab, nThisSize );
+
+ // tdf#131073 - Don't show hidden cols after setting optimal col width
+ if (eMode == SC_SIZE_OPTIMAL)
+ rDoc.ShowCol(nCol, nTab, !bIsColHidden);
+ else
+ rDoc.ShowCol( nCol, nTab, bShow );
+
+ if (!bShow && nCol == nCurX && nTab == nCurTab)
+ {
+ nCurX = -1;
+ }
+ }
+ }
+ }
+
+ // adjust outline
+ if (bWidth)
+ {
+ if ( rDoc.UpdateOutlineCol( static_cast<SCCOL>(nStartNo),
+ static_cast<SCCOL>(nEndNo), nTab, bShow ) )
+ bOutline = true;
+ }
+ else
+ {
+ if ( rDoc.UpdateOutlineRow( nStartNo, nEndNo, nTab, bShow ) )
+ bOutline = true;
+ }
+ }
+ rDoc.SetDrawPageSize(nTab);
+ }
+
+ if (!bOutline)
+ pUndoTab.reset();
+
+ if (bRecord)
+ {
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoWidthOrHeight>(
+ pDocSh, aMarkData, nStart, nCurTab, nEnd, nCurTab,
+ std::move(pUndoDoc), std::move(aUndoRanges), std::move(pUndoTab), eMode, nSizeTwips, bWidth));
+ }
+
+ if (nCurX < 0)
+ {
+ MoveCursorRel( 1, 0, SC_FOLLOW_LINE, false );
+ }
+
+ if (nCurY < 0)
+ {
+ MoveCursorRel( 0, 1, SC_FOLLOW_LINE, false );
+ }
+
+ // fdo#36247 Ensure that the drawing layer's map mode scaling factors match
+ // the new heights and widths.
+ GetViewData().GetView()->RefreshZoom();
+
+ for (const SCTAB& nTab : aMarkData)
+ rDoc.UpdatePageBreaks( nTab );
+
+ bool bAffectsVisibility = (eMode != SC_SIZE_ORIGINAL && eMode != SC_SIZE_VISOPT);
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
+ bWidth /* bColumns */, !bWidth /* bRows */,
+ true /* bSizes*/, bAffectsVisibility /* bHidden */, bAffectsVisibility /* bFiltered */,
+ false /* bGroups */, nCurTab);
+ GetViewData().GetView()->UpdateScrollBars(bWidth ? COLUMN_HEADER : ROW_HEADER);
+
+ {
+ for (const SCTAB& nTab : aMarkData)
+ {
+ if (bWidth)
+ {
+ if (rDoc.HasAttrib( static_cast<SCCOL>(nStart),0,nTab,
+ static_cast<SCCOL>(nEnd), rDoc.MaxRow(), nTab,
+ HasAttrFlags::Merged | HasAttrFlags::Overlapped ))
+ nStart = 0;
+ if (nStart > 0) // go upwards because of Lines and cursor
+ --nStart;
+ pDocSh->PostPaint( static_cast<SCCOL>(nStart), 0, nTab,
+ rDoc.MaxCol(), rDoc.MaxRow(), nTab, PaintPartFlags::Grid | PaintPartFlags::Top );
+ }
+ else
+ {
+ if (rDoc.HasAttrib( 0,nStart,nTab, rDoc.MaxCol(), nEnd,nTab, HasAttrFlags::Merged | HasAttrFlags::Overlapped ))
+ nStart = 0;
+ if (nStart != 0)
+ --nStart;
+ pDocSh->PostPaint( 0, nStart, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, PaintPartFlags::Grid | PaintPartFlags::Left );
+ }
+ }
+
+ pDocSh->UpdateOle(GetViewData());
+ if( !pDocSh->IsReadOnly() )
+ aModificator.SetDocumentModified();
+ }
+
+ if ( !bWidth )
+ return;
+
+ ScModelObj* pModelObj = pDocSh->GetModel();
+
+ if (!HelperNotifyChanges::getMustPropagateChangesModel(pModelObj))
+ return;
+
+ ScRangeList aChangeRanges;
+ for (const SCTAB& nTab : aMarkData)
+ {
+ for (const sc::ColRowSpan & rRange : rRanges)
+ {
+ SCCOL nStartCol = rRange.mnStart;
+ SCCOL nEndCol = rRange.mnEnd;
+ for ( SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol )
+ {
+ aChangeRanges.push_back( ScRange( nCol, 0, nTab ) );
+ }
+ }
+ }
+ HelperNotifyChanges::Notify(*pModelObj, aChangeRanges, "column-resize");
+}
+
+// column width/row height (via marked range)
+
+void ScViewFunc::SetMarkedWidthOrHeight( bool bWidth, ScSizeMode eMode, sal_uInt16 nSizeTwips )
+{
+ ScMarkData& rMark = GetViewData().GetMarkData();
+
+ rMark.MarkToMulti();
+ if (!rMark.IsMultiMarked())
+ {
+ SCCOL nCol = GetViewData().GetCurX();
+ SCROW nRow = GetViewData().GetCurY();
+ SCTAB nTab = GetViewData().GetTabNo();
+ const ScRange aMarkRange( nCol, nRow, nTab);
+ DoneBlockMode();
+ InitOwnBlockMode( aMarkRange );
+ rMark.SetMultiMarkArea( aMarkRange );
+ MarkDataChanged();
+ }
+
+ std::vector<sc::ColRowSpan> aRanges =
+ bWidth ? rMark.GetMarkedColSpans() : rMark.GetMarkedRowSpans();
+
+ SetWidthOrHeight(bWidth, aRanges, eMode, nSizeTwips);
+
+ rMark.MarkToSimple();
+}
+
+void ScViewFunc::ModifyCellSize( ScDirection eDir, bool bOptimal )
+{
+ ScModule* pScMod = SC_MOD();
+ bool bAnyEdit = pScMod->IsInputMode();
+ SCCOL nCol = GetViewData().GetCurX();
+ SCROW nRow = GetViewData().GetCurY();
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+
+ bool bAllowed, bOnlyMatrix;
+ if ( eDir == DIR_LEFT || eDir == DIR_RIGHT )
+ bAllowed = rDoc.IsBlockEditable( nTab, nCol,0, nCol,rDoc.MaxRow(), &bOnlyMatrix );
+ else
+ bAllowed = rDoc.IsBlockEditable( nTab, 0,nRow, rDoc.MaxCol(), nRow, &bOnlyMatrix );
+ if ( !bAllowed && !bOnlyMatrix )
+ {
+ ErrorMessage(STR_PROTECTIONERR);
+ return;
+ }
+
+ HideAllCursors();
+
+ //! step size adjustable
+ // step size is also minimum
+ constexpr sal_uInt16 nStepX = STD_COL_WIDTH / 5;
+ const sal_uInt16 nStepY = rDoc.GetSheetOptimalMinRowHeight(nTab);
+
+ sal_uInt16 nWidth = rDoc.GetColWidth( nCol, nTab );
+ sal_uInt16 nHeight = rDoc.GetRowHeight( nRow, nTab );
+ std::vector<sc::ColRowSpan> aRange(1, sc::ColRowSpan(0,0));
+ if ( eDir == DIR_LEFT || eDir == DIR_RIGHT )
+ {
+ if (bOptimal) // width of this single cell
+ {
+ if ( bAnyEdit )
+ {
+ // when editing the actual entered width
+ ScInputHandler* pHdl = pScMod->GetInputHdl( GetViewData().GetViewShell() );
+ if (pHdl)
+ {
+ tools::Long nEdit = pHdl->GetTextSize().Width(); // in 0.01 mm
+
+ const ScPatternAttr* pPattern = rDoc.GetPattern( nCol, nRow, nTab );
+ const SvxMarginItem& rMItem = pPattern->GetItem(ATTR_MARGIN);
+ sal_uInt16 nMargin = rMItem.GetLeftMargin() + rMItem.GetRightMargin();
+ if ( pPattern->GetItem( ATTR_HOR_JUSTIFY ).GetValue() == SvxCellHorJustify::Left )
+ nMargin = sal::static_int_cast<sal_uInt16>(
+ nMargin + pPattern->GetItem(ATTR_INDENT).GetValue() );
+
+ nWidth = std::round(o3tl::convert(nEdit * pDocSh->GetOutputFactor(),
+ o3tl::Length::mm100, o3tl::Length::twip))
+ + nMargin + STD_EXTRA_WIDTH;
+ }
+ }
+ else
+ {
+ double nPPTX = GetViewData().GetPPTX();
+ double nPPTY = GetViewData().GetPPTY();
+ Fraction aZoomX = GetViewData().GetZoomX();
+ Fraction aZoomY = GetViewData().GetZoomY();
+
+ ScSizeDeviceProvider aProv(pDocSh);
+ if (aProv.IsPrinter())
+ {
+ nPPTX = aProv.GetPPTX();
+ nPPTY = aProv.GetPPTY();
+ aZoomX = aZoomY = Fraction( 1, 1 );
+ }
+
+ tools::Long nPixel = rDoc.GetNeededSize( nCol, nRow, nTab, aProv.GetDevice(),
+ nPPTX, nPPTY, aZoomX, aZoomY, true );
+ sal_uInt16 nTwips = static_cast<sal_uInt16>( nPixel / nPPTX );
+ if (nTwips != 0)
+ nWidth = nTwips + STD_EXTRA_WIDTH;
+ else
+ nWidth = STD_COL_WIDTH;
+ }
+ }
+ else // increment / decrement
+ {
+ if ( eDir == DIR_RIGHT )
+ nWidth = sal::static_int_cast<sal_uInt16>( nWidth + nStepX );
+ else if ( nWidth > nStepX )
+ nWidth = sal::static_int_cast<sal_uInt16>( nWidth - nStepX );
+ if ( nWidth < nStepX ) nWidth = nStepX;
+ if ( nWidth > MAX_COL_WIDTH ) nWidth = MAX_COL_WIDTH;
+ }
+ aRange[0].mnStart = nCol;
+ aRange[0].mnEnd = nCol;
+ SetWidthOrHeight(true, aRange, SC_SIZE_DIRECT, nWidth);
+
+ // adjust height of this row if width demands/allows this
+
+ if (!bAnyEdit)
+ {
+ const ScPatternAttr* pPattern = rDoc.GetPattern( nCol, nRow, nTab );
+ bool bNeedHeight =
+ pPattern->GetItem( ATTR_LINEBREAK ).GetValue() ||
+ pPattern->GetItem( ATTR_HOR_JUSTIFY ).GetValue() == SvxCellHorJustify::Block;
+ if (bNeedHeight)
+ AdjustRowHeight( nRow, nRow, true );
+ }
+ }
+ else
+ {
+ ScSizeMode eMode;
+ if (bOptimal)
+ {
+ eMode = SC_SIZE_OPTIMAL;
+ nHeight = 0;
+ }
+ else
+ {
+ eMode = SC_SIZE_DIRECT;
+ if ( eDir == DIR_BOTTOM )
+ nHeight = sal::static_int_cast<sal_uInt16>( nHeight + nStepY );
+ else if ( nHeight > nStepY )
+ nHeight = sal::static_int_cast<sal_uInt16>( nHeight - nStepY );
+ if ( nHeight < nStepY ) nHeight = nStepY;
+ if ( nHeight > MAX_ROW_HEIGHT ) nHeight = MAX_ROW_HEIGHT;
+ }
+ aRange[0].mnStart = nRow;
+ aRange[0].mnEnd = nRow;
+ SetWidthOrHeight(false, aRange, eMode, nHeight);
+ }
+
+ if ( bAnyEdit )
+ {
+ UpdateEditView();
+ if ( rDoc.HasAttrib( nCol, nRow, nTab, nCol, nRow, nTab, HasAttrFlags::NeedHeight ) )
+ {
+ ScInputHandler* pHdl = pScMod->GetInputHdl( GetViewData().GetViewShell() );
+ if (pHdl)
+ pHdl->SetModified(); // so that the height is adjusted with Enter
+ }
+ }
+
+ ShowAllCursors();
+}
+
+void ScViewFunc::ProtectSheet( SCTAB nTab, const ScTableProtection& rProtect )
+{
+ if (nTab == TABLEID_DOC)
+ return;
+
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScDocFunc &rFunc = pDocSh->GetDocFunc();
+ bool bUndo(rDoc.IsUndoEnabled());
+
+ // modifying several tabs is handled here
+
+ if (bUndo)
+ {
+ OUString aUndo = ScResId( STR_UNDO_PROTECT_TAB );
+ pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, GetViewData().GetViewShell()->GetViewShellId() );
+ }
+
+ for (const auto& rTab : rMark)
+ {
+ rFunc.ProtectSheet(rTab, rProtect);
+ }
+
+ if (bUndo)
+ pDocSh->GetUndoManager()->LeaveListAction();
+
+ UpdateLayerLocks(); //! broadcast to all views
+}
+
+void ScViewFunc::ProtectDoc( const OUString& rPassword )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocFunc &rFunc = pDocSh->GetDocFunc();
+
+ rFunc.Protect( TABLEID_DOC, rPassword );
+
+ UpdateLayerLocks(); //! broadcast to all views
+}
+
+bool ScViewFunc::Unprotect( SCTAB nTab, const OUString& rPassword )
+{
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScDocFunc &rFunc = pDocSh->GetDocFunc();
+ bool bChanged = false;
+ bool bUndo (rDoc.IsUndoEnabled());
+
+ if ( nTab == TABLEID_DOC || rMark.GetSelectCount() <= 1 )
+ {
+ bChanged = rFunc.Unprotect( nTab, rPassword, false );
+ if (bChanged && nTab != TABLEID_DOC)
+ SetTabProtectionSymbol(nTab, false);
+ }
+ else
+ {
+ // modifying several tabs is handled here
+
+ if (bUndo)
+ {
+ OUString aUndo = ScResId( STR_UNDO_UNPROTECT_TAB );
+ pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, GetViewData().GetViewShell()->GetViewShellId() );
+ }
+
+ for (const auto& rTab : rMark)
+ {
+ if ( rFunc.Unprotect( rTab, rPassword, false ) )
+ {
+ bChanged = true;
+ SetTabProtectionSymbol( rTab, false);
+ }
+ }
+
+ if (bUndo)
+ pDocSh->GetUndoManager()->LeaveListAction();
+ }
+
+ if (bChanged)
+ UpdateLayerLocks(); //! broadcast to all views
+
+ return bChanged;
+}
+
+void ScViewFunc::SetNoteText( const ScAddress& rPos, const OUString& rNoteText )
+{
+ GetViewData().GetDocShell()->GetDocFunc().SetNoteText( rPos, rNoteText, false );
+}
+
+void ScViewFunc::ReplaceNote( const ScAddress& rPos, const OUString& rNoteText, const OUString* pAuthor, const OUString* pDate )
+{
+ GetViewData().GetDocShell()->GetDocFunc().ReplaceNote( rPos, rNoteText, pAuthor, pDate, false );
+}
+
+void ScViewFunc::SetNumberFormat( SvNumFormatType nFormatType, sal_uLong nAdd )
+{
+ // not editable because of matrix only? attribute OK nonetheless
+ bool bOnlyNotBecauseOfMatrix;
+ if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
+ {
+ ErrorMessage(STR_PROTECTIONERR);
+ return;
+ }
+
+ sal_uInt32 nNumberFormat = 0;
+ ScViewData& rViewData = GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ SvNumberFormatter* pNumberFormatter = rDoc.GetFormatTable();
+ LanguageType eLanguage = ScGlobal::eLnge;
+ ScPatternAttr aNewAttrs( rDoc.GetPool() );
+
+ // always take language from cursor position, even if there is a selection
+
+ sal_uInt32 nCurrentNumberFormat = rDoc.GetNumberFormat( rViewData.GetCurX(),
+ rViewData.GetCurY(),
+ rViewData.GetTabNo());
+ const SvNumberformat* pEntry = pNumberFormatter->GetEntry( nCurrentNumberFormat );
+ if (pEntry)
+ eLanguage = pEntry->GetLanguage(); // else keep ScGlobal::eLnge
+
+ nNumberFormat = pNumberFormatter->GetStandardFormat( nFormatType, eLanguage ) + nAdd;
+
+ SfxItemSet& rSet = aNewAttrs.GetItemSet();
+ rSet.Put( SfxUInt32Item( ATTR_VALUE_FORMAT, nNumberFormat ) );
+ // ATTR_LANGUAGE_FORMAT not
+ ApplySelectionPattern( aNewAttrs );
+}
+
+void ScViewFunc::SetNumFmtByStr( const OUString& rCode )
+{
+ // not editable because of matrix only? attribute OK nonetheless
+ bool bOnlyNotBecauseOfMatrix;
+ if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
+ {
+ ErrorMessage(STR_PROTECTIONERR);
+ return;
+ }
+
+ ScViewData& rViewData = GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+
+ // language always from cursor position
+
+ sal_uInt32 nCurrentNumberFormat = rDoc.GetNumberFormat( rViewData.GetCurX(), rViewData.GetCurY(),
+ rViewData.GetTabNo());
+ const SvNumberformat* pEntry = pFormatter->GetEntry( nCurrentNumberFormat );
+ LanguageType eLanguage = pEntry ? pEntry->GetLanguage() : ScGlobal::eLnge;
+
+ // determine index for String
+
+ bool bOk = true;
+ sal_uInt32 nNumberFormat = pFormatter->GetEntryKey( rCode, eLanguage );
+ if ( nNumberFormat == NUMBERFORMAT_ENTRY_NOT_FOUND )
+ {
+ // enter new
+
+ OUString aFormat = rCode; // will be changed
+ sal_Int32 nErrPos = 0;
+ SvNumFormatType nType = SvNumFormatType::ALL; //! ???
+ bOk = pFormatter->PutEntry( aFormat, nErrPos, nType, nNumberFormat, eLanguage );
+ }
+
+ if ( bOk ) // valid format?
+ {
+ ScPatternAttr aNewAttrs( rDoc.GetPool() );
+ SfxItemSet& rSet = aNewAttrs.GetItemSet();
+ rSet.Put( SfxUInt32Item( ATTR_VALUE_FORMAT, nNumberFormat ) );
+ rSet.Put( SvxLanguageItem( eLanguage, ATTR_LANGUAGE_FORMAT ) );
+ ApplySelectionPattern( aNewAttrs );
+ }
+
+ //! else return error / issue warning ???
+}
+
+void ScViewFunc::ChangeNumFmtDecimals( bool bIncrement )
+{
+ // not editable because of matrix only? attribute OK nonetheless
+ bool bOnlyNotBecauseOfMatrix;
+ if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
+ {
+ ErrorMessage(STR_PROTECTIONERR);
+ return;
+ }
+
+ ScDocument& rDoc = GetViewData().GetDocument();
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+
+ SCCOL nCol = GetViewData().GetCurX();
+ SCROW nRow = GetViewData().GetCurY();
+ SCTAB nTab = GetViewData().GetTabNo();
+
+ sal_uInt32 nOldFormat = rDoc.GetNumberFormat( nCol, nRow, nTab );
+ const SvNumberformat* pOldEntry = pFormatter->GetEntry( nOldFormat );
+ if (!pOldEntry)
+ {
+ OSL_FAIL("numberformat not found !!!");
+ return;
+ }
+
+ // what have we got here?
+
+ sal_uInt32 nNewFormat = nOldFormat;
+ bool bError = false;
+
+ LanguageType eLanguage = pOldEntry->GetLanguage();
+ bool bThousand, bNegRed;
+ sal_uInt16 nPrecision, nLeading;
+ pOldEntry->GetFormatSpecialInfo( bThousand, bNegRed, nPrecision, nLeading );
+
+ SvNumFormatType nOldType = pOldEntry->GetType();
+ if ( SvNumFormatType::ALL == ( nOldType & (
+ SvNumFormatType::NUMBER | SvNumFormatType::CURRENCY | SvNumFormatType::PERCENT | SvNumFormatType::SCIENTIFIC | SvNumFormatType::TIME ) ) )
+ {
+ // date, fraction, logical, text can not be changed
+ bError = true;
+ }
+
+ //! SvNumberformat has a Member bStandard, but doesn't disclose it
+ bool bWasStandard = ( nOldFormat == pFormatter->GetStandardIndex( eLanguage ) );
+ OUString sExponentialStandardFormat = "";
+ if (bWasStandard)
+ {
+ // with "Standard" the decimal places depend on cell content
+ // 0 if empty or text -> no decimal places
+ double nVal = rDoc.GetValue( ScAddress( nCol, nRow, nTab ) );
+
+ // the ways of the Numberformatters are unfathomable, so try:
+ OUString aOut;
+ const Color* pCol;
+ const_cast<SvNumberformat*>(pOldEntry)->GetOutputString( nVal, aOut, &pCol );
+
+ nPrecision = 0;
+ // 'E' for exponential is fixed in Numberformatter
+ sal_Int32 nIndexE = aOut.indexOf('E');
+ if ( nIndexE >= 0 )
+ {
+ sExponentialStandardFormat = aOut.copy( nIndexE ).replace( '-', '+' );
+ for ( sal_Int32 i=1 ; i<sExponentialStandardFormat.getLength() ; i++ )
+ {
+ if ( sExponentialStandardFormat[i] >= '1' && sExponentialStandardFormat[i] <= '9' )
+ sExponentialStandardFormat = sExponentialStandardFormat.replaceAt( i, 1, u"0" );
+ }
+ aOut = aOut.copy( 0, nIndexE ); // remove exponential part
+ }
+ OUString aDecSep( pFormatter->GetFormatDecimalSep( nOldFormat ) );
+ sal_Int32 nPos = aOut.indexOf( aDecSep );
+ if ( nPos >= 0 )
+ nPrecision = aOut.getLength() - nPos - aDecSep.getLength();
+ // else keep 0
+ }
+ else
+ {
+ if ( (nOldType & SvNumFormatType::SCIENTIFIC) && !bThousand &&
+ (pOldEntry->GetFormatIntegerDigits()%3 == 0) && pOldEntry->GetFormatIntegerDigits() > 0 )
+ bThousand = true;
+ }
+
+ if (!bError)
+ {
+ if (bIncrement)
+ {
+ if (nPrecision<20)
+ ++nPrecision; // increment
+ else
+ bError = true; // 20 is maximum
+ }
+ else
+ {
+ if (nPrecision)
+ --nPrecision; // decrement
+ else
+ bError = true; // 0 is minimum
+ }
+ }
+
+ if (!bError)
+ {
+ OUString aNewPicture = pFormatter->GenerateFormat(nOldFormat, eLanguage,
+ bThousand, bNegRed,
+ nPrecision, nLeading)
+ + sExponentialStandardFormat;
+
+ nNewFormat = pFormatter->GetEntryKey( aNewPicture, eLanguage );
+ if ( nNewFormat == NUMBERFORMAT_ENTRY_NOT_FOUND )
+ {
+ sal_Int32 nErrPos = 0;
+ SvNumFormatType nNewType = SvNumFormatType::ALL;
+ bool bOk = pFormatter->PutEntry( aNewPicture, nErrPos,
+ nNewType, nNewFormat, eLanguage );
+ OSL_ENSURE( bOk, "incorrect numberformat generated" );
+ if (!bOk)
+ bError = true;
+ }
+ }
+
+ if (!bError)
+ {
+ ScPatternAttr aNewAttrs( rDoc.GetPool() );
+ SfxItemSet& rSet = aNewAttrs.GetItemSet();
+ rSet.Put( SfxUInt32Item( ATTR_VALUE_FORMAT, nNewFormat ) );
+ // ATTR_LANGUAGE_FORMAT not
+ ApplySelectionPattern( aNewAttrs );
+ }
+}
+
+void ScViewFunc::ChangeIndent( bool bIncrement )
+{
+ ScViewData& rViewData = GetViewData();
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+ ScMarkData& rMark = rViewData.GetMarkData();
+
+ ScMarkData aWorkMark = rMark;
+ ScViewUtil::UnmarkFiltered( aWorkMark, pDocSh->GetDocument() );
+ aWorkMark.MarkToMulti();
+ if (!aWorkMark.IsMultiMarked())
+ {
+ SCCOL nCol = rViewData.GetCurX();
+ SCROW nRow = rViewData.GetCurY();
+ SCTAB nTab = rViewData.GetTabNo();
+ aWorkMark.SetMultiMarkArea( ScRange(nCol,nRow,nTab) );
+ }
+
+ bool bSuccess = pDocSh->GetDocFunc().ChangeIndent( aWorkMark, bIncrement, false );
+ if (bSuccess)
+ {
+ pDocSh->UpdateOle(rViewData);
+ StartFormatArea();
+
+ // stuff for sidebar panels
+ SfxBindings& rBindings = GetViewData().GetBindings();
+ rBindings.Invalidate( SID_H_ALIGNCELL );
+ rBindings.Invalidate( SID_ATTR_ALIGN_INDENT );
+ }
+}
+
+bool ScViewFunc::InsertName( const OUString& rName, const OUString& rSymbol,
+ const OUString& rType )
+{
+ // Type = P,R,C,F (and combinations)
+ //! undo...
+
+ bool bOk = false;
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScRangeName* pList = rDoc.GetRangeName();
+
+ ScRangeData::Type nType = ScRangeData::Type::Name;
+ auto pNewEntry = std::make_unique<ScRangeData>(
+ rDoc, rName, rSymbol, ScAddress( GetViewData().GetCurX(),
+ GetViewData().GetCurY(), nTab), nType );
+ OUString aUpType = rType.toAsciiUpperCase();
+ if ( aUpType.indexOf( 'P' ) != -1 )
+ nType |= ScRangeData::Type::PrintArea;
+ if ( aUpType.indexOf( 'R' ) != -1 )
+ nType |= ScRangeData::Type::RowHeader;
+ if ( aUpType.indexOf( 'C' ) != -1 )
+ nType |= ScRangeData::Type::ColHeader;
+ if ( aUpType.indexOf( 'F' ) != -1 )
+ nType |= ScRangeData::Type::Criteria;
+ pNewEntry->AddType(nType);
+
+ if ( pNewEntry->GetErrCode() == FormulaError::NONE ) // text valid?
+ {
+ ScDocShellModificator aModificator( *pDocSh );
+
+ rDoc.PreprocessRangeNameUpdate();
+
+ // input available yet? Then remove beforehand (=change)
+ ScRangeData* pData = pList->findByUpperName(ScGlobal::getCharClass().uppercase(rName));
+ if (pData)
+ { // take old Index
+ pNewEntry->SetIndex(pData->GetIndex());
+ pList->erase(*pData);
+ }
+
+ // don't delete, insert took ownership, even on failure!
+ if ( pList->insert( pNewEntry.release() ) )
+ bOk = true;
+
+ rDoc.CompileHybridFormula();
+
+ aModificator.SetDocumentModified();
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreasChanged ) );
+ }
+
+ return bOk;
+}
+
+void ScViewFunc::CreateNames( CreateNameFlags nFlags )
+{
+ bool bDone = false;
+ ScRange aRange;
+ if ( GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE )
+ bDone = GetViewData().GetDocShell()->GetDocFunc().CreateNames( aRange, nFlags, false );
+
+ if (!bDone)
+ ErrorMessage(STR_CREATENAME_MARKERR);
+}
+
+CreateNameFlags ScViewFunc::GetCreateNameFlags()
+{
+ CreateNameFlags nFlags = CreateNameFlags::NONE;
+
+ SCCOL nStartCol, nEndCol;
+ SCROW nStartRow, nEndRow;
+ SCTAB nDummy;
+ if (GetViewData().GetSimpleArea(nStartCol,nStartRow,nDummy,nEndCol,nEndRow,nDummy) == SC_MARK_SIMPLE)
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+ SCTAB nTab = GetViewData().GetTabNo();
+ bool bOk;
+ SCCOL i;
+ SCROW j;
+
+ bOk = true;
+ SCCOL nFirstCol = nStartCol;
+ SCCOL nLastCol = nEndCol;
+ if (nStartCol+1 < nEndCol) { ++nFirstCol; --nLastCol; }
+ for (i=nFirstCol; i<=nLastCol && bOk; i++)
+ if (!rDoc.HasStringData( i,nStartRow,nTab ))
+ bOk = false;
+ if (bOk)
+ nFlags |= CreateNameFlags::Top;
+ else // Bottom only if not Top
+ {
+ bOk = true;
+ for (i=nFirstCol; i<=nLastCol && bOk; i++)
+ if (!rDoc.HasStringData( i,nEndRow,nTab ))
+ bOk = false;
+ if (bOk)
+ nFlags |= CreateNameFlags::Bottom;
+ }
+
+ bOk = true;
+ SCROW nFirstRow = nStartRow;
+ SCROW nLastRow = nEndRow;
+ if (nStartRow+1 < nEndRow) { ++nFirstRow; --nLastRow; }
+ for (j=nFirstRow; j<=nLastRow && bOk; j++)
+ if (!rDoc.HasStringData( nStartCol,j,nTab ))
+ bOk = false;
+ if (bOk)
+ nFlags |= CreateNameFlags::Left;
+ else // Right only if not Left
+ {
+ bOk = true;
+ for (j=nFirstRow; j<=nLastRow && bOk; j++)
+ if (!rDoc.HasStringData( nEndCol,j,nTab ))
+ bOk = false;
+ if (bOk)
+ nFlags |= CreateNameFlags::Right;
+ }
+ }
+
+ if (nStartCol == nEndCol)
+ nFlags &= ~CreateNameFlags( CreateNameFlags::Left | CreateNameFlags::Right );
+ if (nStartRow == nEndRow)
+ nFlags &= ~CreateNameFlags( CreateNameFlags::Top | CreateNameFlags::Bottom );
+
+ return nFlags;
+}
+
+void ScViewFunc::InsertNameList()
+{
+ ScAddress aPos( GetViewData().GetCurX(), GetViewData().GetCurY(), GetViewData().GetTabNo() );
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ if ( pDocSh->GetDocFunc().InsertNameList( aPos, false ) )
+ pDocSh->UpdateOle(GetViewData());
+}
+
+void ScViewFunc::UpdateSelectionArea( const ScMarkData& rSel, ScPatternAttr* pAttr )
+{
+ ScDocShell* pDocShell = GetViewData().GetDocShell();
+ ScRange aMarkRange;
+ if (rSel.IsMultiMarked() )
+ aMarkRange = rSel.GetMultiMarkArea();
+ else
+ aMarkRange = rSel.GetMarkArea();
+
+ bool bSetLines = false;
+ bool bSetAlign = false;
+ if ( pAttr )
+ {
+ const SfxItemSet& rNewSet = pAttr->GetItemSet();
+ bSetLines = rNewSet.GetItemState( ATTR_BORDER ) == SfxItemState::SET ||
+ rNewSet.GetItemState( ATTR_SHADOW ) == SfxItemState::SET;
+ bSetAlign = rNewSet.GetItemState( ATTR_HOR_JUSTIFY ) == SfxItemState::SET;
+ }
+
+ sal_uInt16 nExtFlags = 0;
+ if ( bSetLines )
+ nExtFlags |= SC_PF_LINES;
+ if ( bSetAlign )
+ nExtFlags |= SC_PF_WHOLEROWS;
+
+ SCCOL nStartCol = aMarkRange.aStart.Col();
+ SCROW nStartRow = aMarkRange.aStart.Row();
+ SCTAB nStartTab = aMarkRange.aStart.Tab();
+ SCCOL nEndCol = aMarkRange.aEnd.Col();
+ SCROW nEndRow = aMarkRange.aEnd.Row();
+ SCTAB nEndTab = aMarkRange.aEnd.Tab();
+ pDocShell->PostPaint( nStartCol, nStartRow, nStartTab,
+ nEndCol, nEndRow, nEndTab,
+ PaintPartFlags::Grid, nExtFlags | SC_PF_TESTMERGE );
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ pTabViewShell->AdjustBlockHeight(false, const_cast<ScMarkData*>(&rSel));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/viewutil.cxx b/sc/source/ui/view/viewutil.cxx
new file mode 100644
index 0000000000..68575ac79f
--- /dev/null
+++ b/sc/source/ui/view/viewutil.cxx
@@ -0,0 +1,426 @@
+/* -*- 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 .
+ */
+
+#include <scitems.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/dispatch.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/scripttypeitem.hxx>
+#include <i18nutil/transliteration.hxx>
+#include <svl/itempool.hxx>
+#include <svl/itemset.hxx>
+#include <svl/cjkoptions.hxx>
+#include <svl/ctloptions.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <svl/eitem.hxx>
+#include <osl/diagnose.h>
+
+#include <viewutil.hxx>
+#include <chgtrack.hxx>
+#include <chgviset.hxx>
+#include <markdata.hxx>
+#include <document.hxx>
+#include <tabvwsh.hxx>
+
+#include <svx/svxdlg.hxx>
+#include <svx/svxids.hrc>
+#include <memory>
+
+void ScViewUtil::PutItemScript( SfxItemSet& rShellSet, const SfxItemSet& rCoreSet,
+ sal_uInt16 nWhichId, SvtScriptType nScript )
+{
+ // take the effective item from rCoreSet according to nScript
+ // and put in rShellSet under the (base) nWhichId
+
+ SfxItemPool& rPool = *rShellSet.GetPool();
+ SvxScriptSetItem aSetItem( rPool.GetSlotId(nWhichId), rPool );
+ // use PutExtended with eDefaultAs = SfxItemState::SET, so defaults from rCoreSet
+ // (document pool) are read and put into rShellSet (MessagePool)
+ aSetItem.GetItemSet().PutExtended( rCoreSet, SfxItemState::DONTCARE, SfxItemState::SET );
+ const SfxPoolItem* pI = aSetItem.GetItemOfScript( nScript );
+ if (pI)
+ {
+ rShellSet.Put( pI->CloneSetWhich(nWhichId) );
+ }
+ else
+ rShellSet.InvalidateItem( nWhichId );
+}
+
+LanguageType ScViewUtil::GetEffLanguage( ScDocument& rDoc, const ScAddress& rPos )
+{
+ // used for thesaurus
+
+ SvtScriptType nScript = rDoc.GetScriptType(rPos.Col(), rPos.Row(), rPos.Tab());
+ sal_uInt16 nWhich = ( nScript == SvtScriptType::ASIAN ) ? ATTR_CJK_FONT_LANGUAGE :
+ ( ( nScript == SvtScriptType::COMPLEX ) ? ATTR_CTL_FONT_LANGUAGE : ATTR_FONT_LANGUAGE );
+ const SfxPoolItem* pItem = rDoc.GetAttr( rPos.Col(), rPos.Row(), rPos.Tab(), nWhich);
+ const SvxLanguageItem* pLangIt = dynamic_cast<const SvxLanguageItem*>( pItem );
+ LanguageType eLnge;
+ if (pLangIt)
+ {
+ eLnge = pLangIt->GetValue();
+ if (eLnge == LANGUAGE_DONTKNOW) //! can this happen?
+ {
+ LanguageType eLatin, eCjk, eCtl;
+ rDoc.GetLanguage( eLatin, eCjk, eCtl );
+ eLnge = ( nScript == SvtScriptType::ASIAN ) ? eCjk :
+ ( ( nScript == SvtScriptType::COMPLEX ) ? eCtl : eLatin );
+ }
+ }
+ else
+ eLnge = LANGUAGE_ENGLISH_US;
+ if ( eLnge == LANGUAGE_SYSTEM )
+ eLnge = Application::GetSettings().GetLanguageTag().getLanguageType(); // never use SYSTEM for spelling
+
+ return eLnge;
+}
+
+TransliterationFlags ScViewUtil::GetTransliterationType( sal_uInt16 nSlotID )
+{
+ TransliterationFlags nType = TransliterationFlags::NONE;
+ switch ( nSlotID )
+ {
+ case SID_TRANSLITERATE_SENTENCE_CASE:
+ nType = TransliterationFlags::SENTENCE_CASE;
+ break;
+ case SID_TRANSLITERATE_TITLE_CASE:
+ nType = TransliterationFlags::TITLE_CASE;
+ break;
+ case SID_TRANSLITERATE_TOGGLE_CASE:
+ nType = TransliterationFlags::TOGGLE_CASE;
+ break;
+ case SID_TRANSLITERATE_UPPER:
+ nType = TransliterationFlags::LOWERCASE_UPPERCASE;
+ break;
+ case SID_TRANSLITERATE_LOWER:
+ nType = TransliterationFlags::UPPERCASE_LOWERCASE;
+ break;
+ case SID_TRANSLITERATE_HALFWIDTH:
+ nType = TransliterationFlags::FULLWIDTH_HALFWIDTH;
+ break;
+ case SID_TRANSLITERATE_FULLWIDTH:
+ nType = TransliterationFlags::HALFWIDTH_FULLWIDTH;
+ break;
+ case SID_TRANSLITERATE_HIRAGANA:
+ nType = TransliterationFlags::KATAKANA_HIRAGANA;
+ break;
+ case SID_TRANSLITERATE_KATAKANA:
+ nType = TransliterationFlags::HIRAGANA_KATAKANA;
+ break;
+ }
+ return nType;
+}
+
+bool ScViewUtil::IsActionShown( const ScChangeAction& rAction,
+ const ScChangeViewSettings& rSettings,
+ ScDocument& rDocument )
+{
+ // discarded are displayed as inverted accepted action, because of this
+ // order of ShowRejected/ShowAccepted is important
+
+ if ( !rSettings.IsShowRejected() && rAction.IsRejecting() )
+ return false;
+
+ if ( !rSettings.IsShowAccepted() && rAction.IsAccepted() && !rAction.IsRejecting() )
+ return false;
+
+ if ( rSettings.HasAuthor() && rAction.GetUser() != rSettings.GetTheAuthorToShow() )
+ return false;
+
+ if ( rSettings.HasComment() )
+ {
+ OUString aTmp = rAction.GetDescription(rDocument);
+ OUString aComStr = rAction.GetComment() + " (" + aTmp + ")";
+
+ if(!rSettings.IsValidComment(&aComStr))
+ return false;
+ }
+
+ if ( rSettings.HasRange() )
+ if ( !rSettings.GetTheRangeList().Intersects( rAction.GetBigRange().MakeRange( rDocument ) ) )
+ return false;
+
+ if (rSettings.HasDate() && rSettings.GetTheDateMode() != SvxRedlinDateMode::NONE)
+ {
+ DateTime aDateTime = rAction.GetDateTime();
+ const DateTime& rFirst = rSettings.GetTheFirstDateTime();
+ const DateTime& rLast = rSettings.GetTheLastDateTime();
+ switch ( rSettings.GetTheDateMode() )
+ { // corresponds with ScHighlightChgDlg::OKBtnHdl
+ case SvxRedlinDateMode::BEFORE:
+ if ( aDateTime > rFirst )
+ return false;
+ break;
+
+ case SvxRedlinDateMode::SINCE:
+ if ( aDateTime < rFirst )
+ return false;
+ break;
+
+ case SvxRedlinDateMode::EQUAL:
+ case SvxRedlinDateMode::BETWEEN:
+ if ( aDateTime < rFirst || aDateTime > rLast )
+ return false;
+ break;
+
+ case SvxRedlinDateMode::NOTEQUAL:
+ if ( aDateTime >= rFirst && aDateTime <= rLast )
+ return false;
+ break;
+
+ case SvxRedlinDateMode::SAVE:
+ {
+ ScChangeTrack* pTrack = rDocument.GetChangeTrack();
+ if ( !pTrack || pTrack->GetLastSavedActionNumber() >=
+ rAction.GetActionNumber() )
+ return false;
+ }
+ break;
+
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+
+ if ( rSettings.HasActionRange() )
+ {
+ sal_uLong nAction = rAction.GetActionNumber();
+ sal_uLong nFirstAction;
+ sal_uLong nLastAction;
+ rSettings.GetTheActionRange( nFirstAction, nLastAction );
+ if ( nAction < nFirstAction || nAction > nLastAction )
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void ScViewUtil::UnmarkFiltered( ScMarkData& rMark, const ScDocument& rDoc )
+{
+ rMark.MarkToMulti();
+
+ const ScRange& aMultiArea = rMark.GetMultiMarkArea();
+ SCCOL nStartCol = aMultiArea.aStart.Col();
+ SCROW nStartRow = aMultiArea.aStart.Row();
+ SCCOL nEndCol = aMultiArea.aEnd.Col();
+ SCROW nEndRow = aMultiArea.aEnd.Row();
+
+ bool bChanged = false;
+ for (const SCTAB& nTab : rMark)
+ {
+ for (SCROW nRow = nStartRow; nRow <= nEndRow; ++nRow)
+ {
+ SCROW nLastRow = nRow;
+ if (rDoc.RowFiltered(nRow, nTab, nullptr, &nLastRow))
+ {
+ // use nStartCol/nEndCol, so the multi mark area isn't extended to all columns
+ // (visible in repaint for indentation)
+ rMark.SetMultiMarkArea(
+ ScRange(nStartCol, nRow, nTab, nEndCol, nLastRow, nTab), false);
+ bChanged = true;
+ nRow = nLastRow;
+ }
+ }
+ }
+
+ if ( bChanged && !rMark.HasAnyMultiMarks() )
+ rMark.ResetMark();
+
+ rMark.MarkToSimple();
+}
+
+bool ScViewUtil::FitToUnfilteredRows( ScRange & rRange, const ScDocument& rDoc, size_t nRows )
+{
+ SCTAB nTab = rRange.aStart.Tab();
+ bool bOneTabOnly = (nTab == rRange.aEnd.Tab());
+ // Always fit the range on its first sheet.
+ OSL_ENSURE( bOneTabOnly, "ScViewUtil::ExtendToUnfilteredRows: works only on one sheet");
+ SCROW nStartRow = rRange.aStart.Row();
+ SCROW nLastRow = rDoc.LastNonFilteredRow(nStartRow, rDoc.MaxRow(), nTab);
+ if (rDoc.ValidRow(nLastRow))
+ rRange.aEnd.SetRow(nLastRow);
+ SCROW nCount = rDoc.CountNonFilteredRows(nStartRow, rDoc.MaxRow(), nTab);
+ return static_cast<size_t>(nCount) == nRows && bOneTabOnly;
+}
+
+bool ScViewUtil::HasFiltered( const ScRange& rRange, const ScDocument& rDoc )
+{
+ SCROW nStartRow = rRange.aStart.Row();
+ SCROW nEndRow = rRange.aEnd.Row();
+ for (SCTAB nTab=rRange.aStart.Tab(); nTab<=rRange.aEnd.Tab(); nTab++)
+ {
+ if (rDoc.HasFilteredRows(nStartRow, nEndRow, nTab))
+ return true;
+ }
+
+ return false;
+}
+
+void ScViewUtil::HideDisabledSlot( SfxItemSet& rSet, SfxBindings& rBindings, sal_uInt16 nSlotId )
+{
+ SvtCTLOptions aCTLOptions;
+ bool bEnabled = true;
+
+ switch( nSlotId )
+ {
+ case SID_CHINESE_CONVERSION:
+ case SID_HANGUL_HANJA_CONVERSION:
+ bEnabled = SvtCJKOptions::IsAnyEnabled();
+ break;
+
+ case SID_TRANSLITERATE_HALFWIDTH:
+ case SID_TRANSLITERATE_FULLWIDTH:
+ case SID_TRANSLITERATE_HIRAGANA:
+ case SID_TRANSLITERATE_KATAKANA:
+ bEnabled = SvtCJKOptions::IsChangeCaseMapEnabled();
+ break;
+
+ case SID_INSERT_RLM:
+ case SID_INSERT_LRM:
+ bEnabled = SvtCTLOptions::IsCTLFontEnabled();
+ break;
+
+ default:
+ OSL_FAIL( "ScViewUtil::HideDisabledSlot - unknown slot ID" );
+ return;
+ }
+
+ rBindings.SetVisibleState( nSlotId, bEnabled );
+ if( !bEnabled )
+ rSet.DisableItem( nSlotId );
+}
+
+void ScViewUtil::ExecuteCharMap(const SvxFontItem& rOldFont,
+ const ScTabViewShell& rShell)
+{
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ SfxViewFrame& rFrame = rShell.GetViewFrame();
+ SfxAllItemSet aSet( rFrame.GetObjectShell()->GetPool() );
+ aSet.Put( SfxBoolItem( FN_PARAM_1, false ) );
+ aSet.Put( SvxFontItem( rOldFont.GetFamily(), rOldFont.GetFamilyName(), rOldFont.GetStyleName(), rOldFont.GetPitch(), rOldFont.GetCharSet(), aSet.GetPool()->GetWhich( SID_ATTR_CHAR_FONT ) ) );
+ auto xFrame = rFrame.GetFrame().GetFrameInterface();
+ ScopedVclPtr<SfxAbstractDialog> pDlg(pFact->CreateCharMapDialog(rShell.GetFrameWeld(), aSet, xFrame));
+ pDlg->Execute();
+}
+
+bool ScViewUtil::IsFullScreen( const SfxViewShell& rViewShell )
+{
+ SfxBindings& rBindings = rViewShell.GetViewFrame().GetBindings();
+ std::unique_ptr<SfxBoolItem> pItem;
+ bool bIsFullScreen = false;
+
+ if (rBindings.QueryState( SID_WIN_FULLSCREEN, pItem ) >= SfxItemState::DEFAULT)
+ bIsFullScreen = pItem->GetValue();
+
+ return bIsFullScreen;
+}
+
+void ScViewUtil::SetFullScreen( const SfxViewShell& rViewShell, bool bSet )
+{
+ if( IsFullScreen( rViewShell ) != bSet )
+ {
+ SfxBoolItem aItem( SID_WIN_FULLSCREEN, bSet );
+ rViewShell.GetDispatcher()->ExecuteList(SID_WIN_FULLSCREEN,
+ SfxCallMode::RECORD, { &aItem });
+ }
+}
+
+ScUpdateRect::ScUpdateRect( SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2 )
+ : nNewStartX(0)
+ , nNewStartY(0)
+ , nNewEndX(0)
+ , nNewEndY(0)
+{
+ PutInOrder( nX1, nX2 );
+ PutInOrder( nY1, nY2 );
+
+ nOldStartX = nX1;
+ nOldStartY = nY1;
+ nOldEndX = nX2;
+ nOldEndY = nY2;
+}
+
+void ScUpdateRect::SetNew( SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2 )
+{
+ PutInOrder( nX1, nX2 );
+ PutInOrder( nY1, nY2 );
+
+ nNewStartX = nX1;
+ nNewStartY = nY1;
+ nNewEndX = nX2;
+ nNewEndY = nY2;
+}
+
+bool ScUpdateRect::GetDiff( SCCOL& rX1, SCROW& rY1, SCCOL& rX2, SCROW& rY2 )
+{
+ if ( nNewStartX == nOldStartX && nNewEndX == nOldEndX &&
+ nNewStartY == nOldStartY && nNewEndY == nOldEndY )
+ {
+ rX1 = nNewStartX;
+ rY1 = nNewStartY;
+ rX2 = nNewStartX;
+ rY2 = nNewStartY;
+ return false;
+ }
+
+ rX1 = std::min(nNewStartX,nOldStartX);
+ rY1 = std::min(nNewStartY,nOldStartY);
+ rX2 = std::max(nNewEndX,nOldEndX);
+ rY2 = std::max(nNewEndY,nOldEndY);
+
+ if ( nNewStartX == nOldStartX && nNewEndX == nOldEndX )
+ {
+ if ( nNewStartY == nOldStartY )
+ {
+ rY1 = std::min( nNewEndY, nOldEndY );
+ rY2 = std::max( nNewEndY, nOldEndY );
+ }
+ else if ( nNewEndY == nOldEndY )
+ {
+ rY1 = std::min( nNewStartY, nOldStartY );
+ rY2 = std::max( nNewStartY, nOldStartY );
+ }
+ }
+ else if ( nNewStartY == nOldStartY && nNewEndY == nOldEndY )
+ {
+ if ( nNewStartX == nOldStartX )
+ {
+ rX1 = std::min( nNewEndX, nOldEndX );
+ rX2 = std::max( nNewEndX, nOldEndX );
+ }
+ else if ( nNewEndX == nOldEndX )
+ {
+ rX1 = std::min( nNewStartX, nOldStartX );
+ rX2 = std::max( nNewStartX, nOldStartX );
+ }
+ }
+
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/waitoff.cxx b/sc/source/ui/view/waitoff.cxx
new file mode 100644
index 0000000000..94b2728bc0
--- /dev/null
+++ b/sc/source/ui/view/waitoff.cxx
@@ -0,0 +1,51 @@
+/* -*- 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 .
+ */
+
+#include <vcl/window.hxx>
+
+#include <waitoff.hxx>
+
+ScWaitCursorOff::ScWaitCursorOff( vcl::Window* pWinP )
+ :
+ pWin( pWinP ),
+ nWaiters(0)
+{
+ if ( pWin )
+ {
+ while ( pWin->IsWait() )
+ {
+ nWaiters++;
+ pWin->LeaveWait();
+ }
+ }
+}
+
+ScWaitCursorOff::~ScWaitCursorOff()
+{
+ if ( pWin )
+ {
+ while ( nWaiters )
+ {
+ nWaiters--;
+ pWin->EnterWait();
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */