summaryrefslogtreecommitdiffstats
path: root/sc/source/ui/dbgui
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /sc/source/ui/dbgui
parentInitial commit. (diff)
downloadlibreoffice-upstream.tar.xz
libreoffice-upstream.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sc/source/ui/dbgui')
-rw-r--r--sc/source/ui/dbgui/PivotLayoutDialog.cxx719
-rw-r--r--sc/source/ui/dbgui/PivotLayoutTreeList.cxx132
-rw-r--r--sc/source/ui/dbgui/PivotLayoutTreeListBase.cxx122
-rw-r--r--sc/source/ui/dbgui/PivotLayoutTreeListData.cxx287
-rw-r--r--sc/source/ui/dbgui/PivotLayoutTreeListLabel.cxx85
-rw-r--r--sc/source/ui/dbgui/asciiopt.cxx296
-rw-r--r--sc/source/ui/dbgui/consdlg.cxx535
-rw-r--r--sc/source/ui/dbgui/csvcontrol.cxx284
-rw-r--r--sc/source/ui/dbgui/csvgrid.cxx1418
-rw-r--r--sc/source/ui/dbgui/csvruler.cxx666
-rw-r--r--sc/source/ui/dbgui/csvsplits.cxx102
-rw-r--r--sc/source/ui/dbgui/csvtablebox.cxx369
-rw-r--r--sc/source/ui/dbgui/dapidata.cxx169
-rw-r--r--sc/source/ui/dbgui/dapitype.cxx153
-rw-r--r--sc/source/ui/dbgui/dbnamdlg.cxx645
-rw-r--r--sc/source/ui/dbgui/dpgroupdlg.cxx353
-rw-r--r--sc/source/ui/dbgui/filtdlg.cxx1571
-rw-r--r--sc/source/ui/dbgui/foptmgr.cxx260
-rw-r--r--sc/source/ui/dbgui/imoptdlg.cxx135
-rw-r--r--sc/source/ui/dbgui/pfiltdlg.cxx507
-rw-r--r--sc/source/ui/dbgui/pvfundlg.cxx973
-rw-r--r--sc/source/ui/dbgui/scendlg.cxx163
-rw-r--r--sc/source/ui/dbgui/scuiasciiopt.cxx943
-rw-r--r--sc/source/ui/dbgui/scuiimoptdlg.cxx340
-rw-r--r--sc/source/ui/dbgui/sfiltdlg.cxx435
-rw-r--r--sc/source/ui/dbgui/sortdlg.cxx65
-rw-r--r--sc/source/ui/dbgui/sortkeydlg.cxx77
-rw-r--r--sc/source/ui/dbgui/subtdlg.cxx46
-rw-r--r--sc/source/ui/dbgui/textimportoptions.cxx96
-rw-r--r--sc/source/ui/dbgui/tpsort.cxx855
-rw-r--r--sc/source/ui/dbgui/tpsubt.cxx621
-rw-r--r--sc/source/ui/dbgui/validate.cxx927
32 files changed, 14349 insertions, 0 deletions
diff --git a/sc/source/ui/dbgui/PivotLayoutDialog.cxx b/sc/source/ui/dbgui/PivotLayoutDialog.cxx
new file mode 100644
index 000000000..9244a4d7e
--- /dev/null
+++ b/sc/source/ui/dbgui/PivotLayoutDialog.cxx
@@ -0,0 +1,719 @@
+/* -*- 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:
+ */
+
+#include <PivotLayoutTreeList.hxx>
+#include <PivotLayoutDialog.hxx>
+#include <reffact.hxx>
+
+#include <rangeutl.hxx>
+#include <uiitems.hxx>
+#include <dputil.hxx>
+#include <dbdocfun.hxx>
+#include <dpsave.hxx>
+#include <dpshttab.hxx>
+#include <scmod.hxx>
+
+#include <memory>
+#include <vector>
+
+#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
+#include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
+
+using namespace css::uno;
+using namespace css::sheet;
+
+ScItemValue::ScItemValue(OUString const & aName, SCCOL nColumn, PivotFunc nFunctionMask) :
+ maName(aName),
+ maFunctionData(nColumn, nFunctionMask),
+ mpOriginalItemValue(this)
+{}
+
+ScItemValue::ScItemValue(const ScItemValue* pInputItemValue) :
+ maName(pInputItemValue->maName),
+ maFunctionData(pInputItemValue->maFunctionData),
+ mpOriginalItemValue(this)
+{}
+
+ScItemValue::~ScItemValue()
+{}
+
+namespace
+{
+
+ScRange lclGetRangeForNamedRange(OUString const & aName, const ScDocument& rDocument)
+{
+ ScRange aInvalidRange(ScAddress::INITIALIZE_INVALID);
+ ScRangeName* pRangeName = rDocument.GetRangeName();
+ if (pRangeName == nullptr)
+ return aInvalidRange;
+
+ const ScRangeData* pData = pRangeName->findByUpperName(aName.toAsciiUpperCase());
+ if (pData == nullptr)
+ return aInvalidRange;
+
+ ScRange aRange;
+ if (pData->IsReference(aRange))
+ return aRange;
+
+ return aInvalidRange;
+}
+
+}
+
+ScPivotLayoutDialog::ScPivotLayoutDialog(
+ SfxBindings* pSfxBindings, SfxChildWindow* pChildWindow, weld::Window* pParent,
+ ScViewData* pViewData, const ScDPObject* pPivotTableObject, bool bNewPivotTable)
+ : ScAnyRefDlgController(pSfxBindings, pChildWindow, pParent, "modules/scalc/ui/pivottablelayoutdialog.ui", "PivotTableLayout")
+ , maPivotTableObject(*pPivotTableObject)
+ , mpPreviouslyFocusedListBox(nullptr)
+ , mpViewData(pViewData)
+ , mrDocument(pViewData->GetDocument())
+ , mbNewPivotTable(bNewPivotTable)
+ , maAddressDetails(mrDocument.GetAddressConvention(), 0, 0)
+ , mbDialogLostFocus(false)
+ , mpActiveEdit(nullptr)
+ , mxListBoxField(new ScPivotLayoutTreeListLabel(m_xBuilder->weld_tree_view("listbox-fields")))
+ , mxListBoxPage(new ScPivotLayoutTreeList(m_xBuilder->weld_tree_view("listbox-page")))
+ , mxListBoxColumn(new ScPivotLayoutTreeList(m_xBuilder->weld_tree_view("listbox-column")))
+ , mxListBoxRow(new ScPivotLayoutTreeList(m_xBuilder->weld_tree_view("listbox-row")))
+ , mxListBoxData(new ScPivotLayoutTreeListData(m_xBuilder->weld_tree_view("listbox-data")))
+ , mxCheckIgnoreEmptyRows(m_xBuilder->weld_check_button("check-ignore-empty-rows"))
+ , mxCheckTotalColumns(m_xBuilder->weld_check_button("check-total-columns"))
+ , mxCheckAddFilter(m_xBuilder->weld_check_button("check-add-filter"))
+ , mxCheckIdentifyCategories(m_xBuilder->weld_check_button("check-identify-categories"))
+ , mxCheckTotalRows(m_xBuilder->weld_check_button("check-total-rows"))
+ , mxCheckDrillToDetail(m_xBuilder->weld_check_button("check-drill-to-details"))
+ , mxSourceRadioNamedRange(m_xBuilder->weld_radio_button("source-radio-named-range"))
+ , mxSourceRadioSelection(m_xBuilder->weld_radio_button("source-radio-selection"))
+ , mxSourceListBox(m_xBuilder->weld_combo_box("source-list"))
+ , mxSourceEdit(new formula::RefEdit(m_xBuilder->weld_entry("source-edit")))
+ , mxSourceButton(new formula::RefButton(m_xBuilder->weld_button("source-button")))
+ , mxDestinationRadioNewSheet(m_xBuilder->weld_radio_button("destination-radio-new-sheet"))
+ , mxDestinationRadioNamedRange(m_xBuilder->weld_radio_button("destination-radio-named-range"))
+ , mxDestinationRadioSelection(m_xBuilder->weld_radio_button("destination-radio-selection"))
+ , mxDestinationListBox(m_xBuilder->weld_combo_box("destination-list"))
+ , mxDestinationEdit(new formula::RefEdit(m_xBuilder->weld_entry("destination-edit")))
+ , mxDestinationButton(new formula::RefButton(m_xBuilder->weld_button("destination-button")))
+ , mxBtnOK(m_xBuilder->weld_button("ok"))
+ , mxBtnCancel(m_xBuilder->weld_button("cancel"))
+ , mxSourceFrame(m_xBuilder->weld_frame("frame2"))
+ , mxSourceLabel(mxSourceFrame->weld_label_widget())
+ , mxDestFrame(m_xBuilder->weld_frame("frame1"))
+ , mxDestLabel(mxDestFrame->weld_label_widget())
+ , mxOptions(m_xBuilder->weld_expander("options"))
+ , mxMore(m_xBuilder->weld_expander("more"))
+{
+ // Source UI
+ Link<weld::Toggleable&,void> aLink2 = LINK(this, ScPivotLayoutDialog, ToggleSource);
+ mxSourceRadioNamedRange->connect_toggled(aLink2);
+ mxSourceRadioSelection->connect_toggled(aLink2);
+
+ mxSourceEdit->SetReferences(this, mxSourceLabel.get());
+ mxSourceButton->SetReferences(this, mxSourceEdit.get());
+
+ Link<formula::RefEdit&,void> aEditLink = LINK(this, ScPivotLayoutDialog, GetEditFocusHandler);
+ mxDestinationEdit->SetGetFocusHdl(aEditLink);
+ mxSourceEdit->SetGetFocusHdl(aEditLink);
+
+ aEditLink = LINK(this, ScPivotLayoutDialog, LoseEditFocusHandler);
+ mxDestinationEdit->SetLoseFocusHdl(aEditLink);
+ mxSourceEdit->SetLoseFocusHdl(aEditLink);
+
+ mxSourceEdit->SetModifyHdl(LINK(this, ScPivotLayoutDialog, SourceEditModified));
+ mxSourceListBox->connect_changed(LINK(this, ScPivotLayoutDialog, SourceListSelected));
+
+ // Destination UI
+ aLink2 = LINK(this, ScPivotLayoutDialog, ToggleDestination);
+ mxDestinationRadioNewSheet->connect_toggled(aLink2);
+ mxDestinationRadioNamedRange->connect_toggled(aLink2);
+ mxDestinationRadioSelection->connect_toggled(aLink2);
+
+ mxDestinationEdit->SetReferences(this, mxDestLabel.get());
+ mxDestinationButton->SetReferences(this, mxDestinationEdit.get());
+
+ Link<formula::RefButton&,void> aButtonLink = LINK(this, ScPivotLayoutDialog, GetButtonFocusHandler);
+ mxSourceButton->SetGetFocusHdl(aButtonLink);
+ mxDestinationButton->SetGetFocusHdl(aButtonLink);
+
+ aButtonLink = LINK(this, ScPivotLayoutDialog, LoseButtonFocusHandler);
+ mxSourceButton->SetLoseFocusHdl(aButtonLink);
+ mxDestinationButton->SetLoseFocusHdl(aButtonLink);
+
+ // Buttons
+ mxBtnCancel->connect_clicked(LINK(this, ScPivotLayoutDialog, CancelClicked));
+ mxBtnOK->connect_clicked(LINK(this, ScPivotLayoutDialog, OKClicked));
+
+ // Initialize Data
+ maPivotTableObject.FillOldParam(maPivotParameters);
+ maPivotTableObject.FillLabelData(maPivotParameters);
+
+ mxListBoxField->Setup (this);
+ mxListBoxPage->Setup (this, ScPivotLayoutTreeList::PAGE_LIST);
+ mxListBoxColumn->Setup(this, ScPivotLayoutTreeList::COLUMN_LIST);
+ mxListBoxRow->Setup (this, ScPivotLayoutTreeList::ROW_LIST);
+ mxListBoxData->Setup (this);
+
+ FillValuesToListBoxes();
+
+ // Initialize Options
+ const ScDPSaveData* pSaveData = maPivotTableObject.GetSaveData();
+ if (pSaveData == nullptr)
+ {
+ mxCheckAddFilter->set_active(false);
+ mxCheckDrillToDetail->set_active(false);
+ }
+ else
+ {
+ mxCheckAddFilter->set_active(pSaveData->GetFilterButton());
+ mxCheckDrillToDetail->set_active(pSaveData->GetDrillDown());
+ }
+
+ mxCheckIgnoreEmptyRows->set_active(maPivotParameters.bIgnoreEmptyRows);
+ mxCheckIdentifyCategories->set_active(maPivotParameters.bDetectCategories);
+ mxCheckTotalColumns->set_active(maPivotParameters.bMakeTotalCol);
+ mxCheckTotalRows->set_active(maPivotParameters.bMakeTotalRow);
+
+ SetupSource();
+ SetupDestination();
+}
+
+ScPivotLayoutDialog::~ScPivotLayoutDialog()
+{
+}
+
+void ScPivotLayoutDialog::SetupSource()
+{
+ mxSourceListBox->clear();
+
+ ScRange aSourceRange;
+ OUString sSourceNamedRangeName;
+
+ if (maPivotTableObject.GetSheetDesc())
+ {
+ const ScSheetSourceDesc* pSheetSourceDesc = maPivotTableObject.GetSheetDesc();
+ aSourceRange = pSheetSourceDesc->GetSourceRange();
+
+ if(!aSourceRange.IsValid())
+ {
+ // Source is probably a DB Range
+ mxSourceRadioNamedRange->set_sensitive(false);
+ mxSourceRadioSelection->set_sensitive(false);
+ ToggleSource();
+ return;
+ }
+ else
+ {
+ OUString aSourceRangeName = aSourceRange.Format(mrDocument, ScRefFlags::RANGE_ABS_3D, maAddressDetails);
+ mxSourceEdit->SetText(aSourceRangeName);
+ }
+ }
+ else
+ {
+ mxSourceRadioNamedRange->set_sensitive(false);
+ mxSourceRadioSelection->set_sensitive(false);
+ ToggleSource();
+ return;
+ }
+
+ // Setup Named Ranges
+ bool bIsNamedRange = false;
+
+ ScAreaNameIterator aIterator(mrDocument);
+ OUString aEachName;
+ ScRange aEachRange;
+
+ while (aIterator.Next(aEachName, aEachRange))
+ {
+ if (!aIterator.WasDBName())
+ {
+ mxSourceListBox->append_text(aEachName);
+ if (aEachRange == aSourceRange)
+ {
+ sSourceNamedRangeName = aEachName;
+ bIsNamedRange = true;
+ }
+ }
+ }
+
+ bool bSourceBoxHasEntries = mxSourceListBox->get_count() > 0;
+
+ if (bIsNamedRange)
+ {
+ mxSourceListBox->set_active_text(sSourceNamedRangeName);
+ mxSourceRadioNamedRange->set_active(true);
+ }
+ else
+ {
+ // If entries - select first entry
+ mxSourceListBox->set_active(bSourceBoxHasEntries ? 0 : -1);
+ mxSourceRadioSelection->set_active(true);
+ }
+
+ // If no entries disable the radio button.
+ if (!bSourceBoxHasEntries)
+ mxSourceRadioNamedRange->set_sensitive(false);
+
+ ToggleSource();
+}
+
+void ScPivotLayoutDialog::SetupDestination()
+{
+ mxDestinationListBox->clear();
+
+ // Fill up named ranges
+ ScAreaNameIterator aIterator(mrDocument);
+ OUString aName;
+ ScRange aRange;
+
+ while (aIterator.Next(aName, aRange))
+ {
+ if (!aIterator.WasDBName())
+ {
+ mxDestinationListBox->append_text(aName);
+ }
+ }
+
+ // If entries - select first entry, otherwise disable the radio button.
+ if (mxDestinationListBox->get_count() > 0)
+ mxDestinationListBox->set_active(0);
+ else
+ mxDestinationRadioNamedRange->set_sensitive(false);
+
+ //
+ if (mbNewPivotTable)
+ {
+ mxDestinationRadioNewSheet->set_active(true);
+ }
+ else
+ {
+ if (maPivotParameters.nTab != MAXTAB + 1)
+ {
+ ScAddress aAddress(maPivotParameters.nCol, maPivotParameters.nRow, maPivotParameters.nTab);
+ OUString aAddressString = aAddress.Format(ScRefFlags::ADDR_ABS_3D, &mrDocument, maAddressDetails);
+ mxDestinationEdit->SetText(aAddressString);
+ mxDestinationRadioSelection->set_active(true);
+ }
+ }
+
+ ToggleDestination();
+}
+
+void ScPivotLayoutDialog::FillValuesToListBoxes()
+{
+ mxListBoxField->FillLabelFields(maPivotParameters.maLabelArray);
+ mxListBoxData->FillDataField(maPivotParameters.maDataFields);
+ mxListBoxColumn->FillFields(maPivotParameters.maColFields);
+ mxListBoxRow->FillFields(maPivotParameters.maRowFields);
+ mxListBoxPage->FillFields(maPivotParameters.maPageFields);
+}
+
+void ScPivotLayoutDialog::SetActive()
+{
+ if (mbDialogLostFocus)
+ {
+ mbDialogLostFocus = false;
+ if(mpActiveEdit != nullptr)
+ {
+ mpActiveEdit->GrabFocus();
+ if (mpActiveEdit == mxSourceEdit.get())
+ UpdateSourceRange();
+ }
+ }
+ else
+ {
+ m_xDialog->grab_focus();
+ }
+
+ RefInputDone();
+}
+
+void ScPivotLayoutDialog::SetReference(const ScRange& rReferenceRange, ScDocument& rDocument)
+{
+ if (!mbDialogLostFocus)
+ return;
+
+ if (mpActiveEdit == nullptr)
+ return;
+
+ if (rReferenceRange.aStart != rReferenceRange.aEnd)
+ RefInputStart(mpActiveEdit);
+
+ OUString aReferenceString = rReferenceRange.Format(rDocument, ScRefFlags::RANGE_ABS_3D, maAddressDetails);
+
+ if (mpActiveEdit == mxSourceEdit.get())
+ {
+ mxSourceEdit->SetRefString(aReferenceString);
+ }
+ else if (mpActiveEdit == mxDestinationEdit.get())
+ {
+ mxDestinationEdit->SetRefString(aReferenceString);
+ }
+}
+
+bool ScPivotLayoutDialog::IsRefInputMode() const
+{
+ return mbDialogLostFocus;
+}
+
+void ScPivotLayoutDialog::ItemInserted(const ScItemValue* pItemValue, ScPivotLayoutTreeList::SvPivotTreeListType eType)
+{
+ if (pItemValue == nullptr)
+ return;
+
+ switch (eType)
+ {
+ case ScPivotLayoutTreeList::ROW_LIST:
+ case ScPivotLayoutTreeList::COLUMN_LIST:
+ case ScPivotLayoutTreeList::PAGE_LIST:
+ {
+ mxListBoxRow->RemoveEntryForItem(pItemValue);
+ mxListBoxColumn->RemoveEntryForItem(pItemValue);
+ mxListBoxPage->RemoveEntryForItem(pItemValue);
+ }
+ break;
+ case ScPivotLayoutTreeList::LABEL_LIST:
+ {
+ mxListBoxRow->RemoveEntryForItem(pItemValue);
+ mxListBoxColumn->RemoveEntryForItem(pItemValue);
+ mxListBoxPage->RemoveEntryForItem(pItemValue);
+ mxListBoxData->RemoveEntryForItem(pItemValue);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void ScPivotLayoutDialog::UpdateSourceRange()
+{
+ if (!maPivotTableObject.GetSheetDesc())
+ return;
+
+ ScSheetSourceDesc aSourceSheet = *maPivotTableObject.GetSheetDesc();
+
+ if (mxSourceRadioNamedRange->get_active())
+ {
+ OUString aEntryString = mxSourceListBox->get_active_text();
+ ScRange aSourceRange = lclGetRangeForNamedRange(aEntryString, mrDocument);
+ if (!aSourceRange.IsValid() || aSourceSheet.GetSourceRange() == aSourceRange)
+ return;
+ aSourceSheet.SetRangeName(aEntryString);
+ }
+ else if (mxSourceRadioSelection->get_active())
+ {
+ OUString aSourceString = mxSourceEdit->GetText();
+ ScRange aSourceRange;
+ ScRefFlags nResult = aSourceRange.Parse(aSourceString, mrDocument, maAddressDetails);
+
+ bool bIsValid = (nResult & ScRefFlags::VALID) == ScRefFlags::VALID; // aSourceString is valid
+
+ mxSourceEdit->SetRefValid(true);
+
+ if (bIsValid)
+ {
+ ScRefAddress aStart;
+ ScRefAddress aEnd;
+
+ ConvertDoubleRef(mrDocument, aSourceString, 1, aStart, aEnd, maAddressDetails);
+ aSourceRange.aStart = aStart.GetAddress();
+ aSourceRange.aEnd = aEnd.GetAddress();
+ }
+ else
+ {
+ aSourceRange = lclGetRangeForNamedRange(aSourceString, mrDocument);
+ }
+
+ if (!aSourceRange.IsValid())
+ {
+ mxSourceEdit->SetRefValid(false);
+ return;
+ }
+
+ if (aSourceSheet.GetSourceRange() == aSourceRange)
+ return;
+
+ aSourceSheet.SetSourceRange(aSourceRange);
+ if (aSourceSheet.CheckSourceRange())
+ {
+ mxSourceEdit->SetRefValid(false);
+ return;
+ }
+ }
+ else
+ {
+ return;
+ }
+
+ maPivotTableObject.SetSheetDesc(aSourceSheet);
+ maPivotTableObject.FillOldParam(maPivotParameters);
+ maPivotTableObject.FillLabelData(maPivotParameters);
+
+ FillValuesToListBoxes();
+}
+
+void ScPivotLayoutDialog::ApplyChanges()
+{
+ ScDPSaveData aSaveData;
+ ApplySaveData(aSaveData);
+ ApplyLabelData(aSaveData);
+
+ ScDPObject *pOldDPObj = mrDocument.GetDPAtCursor( maPivotParameters.nCol, maPivotParameters.nRow, maPivotParameters.nTab);
+ ScRange aDestinationRange;
+ bool bToNewSheet = false;
+
+ if (!GetDestination(aDestinationRange, bToNewSheet))
+ return;
+
+ SetDispatcherLock(false);
+ SwitchToDocument();
+
+ sal_uInt16 nWhichPivot = SC_MOD()->GetPool().GetWhich(SID_PIVOT_TABLE);
+ ScPivotItem aPivotItem(nWhichPivot, &aSaveData, &aDestinationRange, bToNewSheet);
+ mpViewData->GetViewShell()->SetDialogDPObject(std::make_unique<ScDPObject>(maPivotTableObject));
+
+
+ SfxDispatcher* pDispatcher = GetBindings().GetDispatcher();
+ SfxCallMode const nCallMode = SfxCallMode::SLOT | SfxCallMode::RECORD;
+ const SfxPoolItem* pResult = pDispatcher->ExecuteList(SID_PIVOT_TABLE,
+ nCallMode, { &aPivotItem });
+
+ if (pResult != nullptr)
+ {
+ // existing pivot table might have moved to a new range or a new sheet
+ if ( pOldDPObj != nullptr )
+ {
+ const ScRange& rOldRange = pOldDPObj->GetOutRange();
+
+ ScDPObject *pDPObj = nullptr;
+ // FIXME: if the new range overlaps with the old one, the table actually doesn't move
+ // and shouldn't therefore be deleted
+ if ( ( ( rOldRange != aDestinationRange ) && !rOldRange.Contains( aDestinationRange ) )
+ || bToNewSheet )
+ {
+ pDPObj = mrDocument.GetDPAtCursor( maPivotParameters.nCol, maPivotParameters.nRow, maPivotParameters.nTab);
+ }
+ if (pDPObj)
+ {
+ ScDBDocFunc aFunc( *(mpViewData->GetDocShell() ));
+ aFunc.RemovePivotTable( *pDPObj, true, false);
+ mpViewData->GetView()->CursorPosChanged();
+ }
+ }
+ return;
+ }
+
+ SetDispatcherLock(true);
+}
+
+void ScPivotLayoutDialog::ApplySaveData(ScDPSaveData& rSaveData)
+{
+ rSaveData.SetIgnoreEmptyRows(mxCheckIgnoreEmptyRows->get_active());
+ rSaveData.SetRepeatIfEmpty(mxCheckIdentifyCategories->get_active());
+ rSaveData.SetColumnGrand(mxCheckTotalColumns->get_active());
+ rSaveData.SetRowGrand(mxCheckTotalRows->get_active());
+ rSaveData.SetFilterButton(mxCheckAddFilter->get_active());
+ rSaveData.SetDrillDown(mxCheckDrillToDetail->get_active());
+
+ Reference<XDimensionsSupplier> xSource = maPivotTableObject.GetSource();
+
+ ScPivotFieldVector aPageFieldVector;
+ mxListBoxPage->PushEntriesToPivotFieldVector(aPageFieldVector);
+ ScDPObject::ConvertOrientation(rSaveData, aPageFieldVector, DataPilotFieldOrientation_PAGE,
+ xSource, maPivotParameters.maLabelArray);
+
+ ScPivotFieldVector aColFieldVector;
+ mxListBoxColumn->PushEntriesToPivotFieldVector(aColFieldVector);
+ ScDPObject::ConvertOrientation(rSaveData, aColFieldVector, DataPilotFieldOrientation_COLUMN,
+ xSource, maPivotParameters.maLabelArray);
+
+ ScPivotFieldVector aRowFieldVector;
+ mxListBoxRow->PushEntriesToPivotFieldVector(aRowFieldVector);
+ ScDPObject::ConvertOrientation(rSaveData, aRowFieldVector, DataPilotFieldOrientation_ROW,
+ xSource, maPivotParameters.maLabelArray);
+
+ ScPivotFieldVector aDataFieldVector;
+ mxListBoxData->PushEntriesToPivotFieldVector(aDataFieldVector);
+ ScDPObject::ConvertOrientation(rSaveData, aDataFieldVector, DataPilotFieldOrientation_DATA,
+ xSource, maPivotParameters.maLabelArray,
+ &aColFieldVector, &aRowFieldVector, &aPageFieldVector);
+}
+
+void ScPivotLayoutDialog::ApplyLabelData(const ScDPSaveData& rSaveData)
+{
+ ScDPLabelDataVector& rLabelDataVector = GetLabelDataVector();
+
+ for (std::unique_ptr<ScDPLabelData> const & pLabelData : rLabelDataVector)
+ {
+ OUString aUnoName = ScDPUtil::createDuplicateDimensionName(pLabelData->maName, pLabelData->mnDupCount);
+ ScDPSaveDimension* pSaveDimensions = rSaveData.GetExistingDimensionByName(aUnoName);
+
+ if (pSaveDimensions == nullptr)
+ continue;
+
+ pSaveDimensions->SetUsedHierarchy(pLabelData->mnUsedHier);
+ pSaveDimensions->SetShowEmpty(pLabelData->mbShowAll);
+ pSaveDimensions->SetRepeatItemLabels(pLabelData->mbRepeatItemLabels);
+ pSaveDimensions->SetSortInfo(&pLabelData->maSortInfo);
+ pSaveDimensions->SetLayoutInfo(&pLabelData->maLayoutInfo);
+ pSaveDimensions->SetAutoShowInfo(&pLabelData->maShowInfo);
+
+ bool bManualSort = (pLabelData->maSortInfo.Mode == DataPilotFieldSortMode::MANUAL);
+
+ for (ScDPLabelData::Member const & rLabelMember : pLabelData->maMembers)
+ {
+ ScDPSaveMember* pMember = pSaveDimensions->GetMemberByName(rLabelMember.maName);
+
+ if (bManualSort || !rLabelMember.mbVisible || !rLabelMember.mbShowDetails)
+ {
+ pMember->SetIsVisible(rLabelMember.mbVisible);
+ pMember->SetShowDetails(rLabelMember.mbShowDetails);
+ }
+ }
+ }
+}
+
+bool ScPivotLayoutDialog::GetDestination(ScRange& aDestinationRange, bool& bToNewSheet)
+{
+ bToNewSheet = false;
+
+ if (mxDestinationRadioNamedRange->get_active())
+ {
+ OUString aName = mxDestinationListBox->get_active_text();
+ aDestinationRange = lclGetRangeForNamedRange(aName, mrDocument);
+ if (!aDestinationRange.IsValid())
+ return false;
+ }
+ else if (mxDestinationRadioSelection->get_active())
+ {
+ ScAddress aAddress;
+ aAddress.Parse(mxDestinationEdit->GetText(), mrDocument, maAddressDetails);
+ aDestinationRange = ScRange(aAddress);
+ }
+ else
+ {
+ bToNewSheet = true;
+ aDestinationRange = ScRange(maPivotParameters.nCol, maPivotParameters.nRow, maPivotParameters.nTab);
+ }
+ return true;
+}
+
+ScItemValue* ScPivotLayoutDialog::GetItem(SCCOL nColumn)
+{
+ return mxListBoxField->GetItem(nColumn);
+}
+
+bool ScPivotLayoutDialog::IsDataElement(SCCOL nColumn)
+{
+ return mxListBoxField->IsDataElement(nColumn);
+}
+
+ScDPLabelData& ScPivotLayoutDialog::GetLabelData(SCCOL nColumn)
+{
+ return *maPivotParameters.maLabelArray[nColumn];
+}
+
+void ScPivotLayoutDialog::PushDataFieldNames(std::vector<ScDPName>& rDataFieldNames)
+{
+ mxListBoxData->PushDataFieldNames(rDataFieldNames);
+}
+
+void ScPivotLayoutDialog::Close()
+{
+ DoClose(ScPivotLayoutWrapper::GetChildWindowId());
+ SfxDialogController::Close();
+}
+
+IMPL_LINK_NOARG( ScPivotLayoutDialog, OKClicked, weld::Button&, void )
+{
+ /* tdf#137726 hide so it's not a candidate to be parent of any error
+ messages that may appear because this dialog is going to disappear on
+ response(RET_OK) and the error dialog is not run in its own event loop
+ but instead async */
+ m_xDialog->hide();
+
+ ApplyChanges();
+ m_xDialog->response(RET_OK);
+}
+
+IMPL_LINK_NOARG( ScPivotLayoutDialog, CancelClicked, weld::Button&, void )
+{
+ m_xDialog->response(RET_CANCEL);
+}
+
+IMPL_LINK(ScPivotLayoutDialog, GetEditFocusHandler, formula::RefEdit&, rCtrl, void)
+{
+ mpActiveEdit = &rCtrl;
+ mpActiveEdit->SelectAll();
+}
+
+IMPL_LINK(ScPivotLayoutDialog, GetButtonFocusHandler, formula::RefButton&, rCtrl, void)
+{
+ mpActiveEdit = nullptr;
+
+ if (&rCtrl == mxSourceButton.get())
+ mpActiveEdit = mxSourceEdit.get();
+ else if (&rCtrl == mxDestinationButton.get())
+ mpActiveEdit = mxDestinationEdit.get();
+
+ if (mpActiveEdit)
+ mpActiveEdit->SelectAll();
+}
+
+IMPL_LINK_NOARG(ScPivotLayoutDialog, LoseEditFocusHandler, formula::RefEdit&, void)
+{
+ mbDialogLostFocus = !m_xDialog->has_toplevel_focus();
+}
+
+IMPL_LINK_NOARG(ScPivotLayoutDialog, LoseButtonFocusHandler, formula::RefButton&, void)
+{
+ mbDialogLostFocus = !m_xDialog->has_toplevel_focus();
+}
+
+IMPL_LINK_NOARG(ScPivotLayoutDialog, SourceListSelected, weld::ComboBox&, void)
+{
+ UpdateSourceRange();
+}
+
+IMPL_LINK_NOARG(ScPivotLayoutDialog, SourceEditModified, formula::RefEdit&, void)
+{
+ UpdateSourceRange();
+}
+
+IMPL_LINK_NOARG(ScPivotLayoutDialog, ToggleSource, weld::Toggleable&, void)
+{
+ ToggleSource();
+}
+
+void ScPivotLayoutDialog::ToggleSource()
+{
+ bool bNamedRange = mxSourceRadioNamedRange->get_active();
+ bool bSelection = mxSourceRadioSelection->get_active();
+ mxSourceListBox->set_sensitive(bNamedRange);
+ mxSourceButton->GetWidget()->set_sensitive(bSelection);
+ mxSourceEdit->GetWidget()->set_sensitive(bSelection);
+ UpdateSourceRange();
+}
+
+IMPL_LINK_NOARG(ScPivotLayoutDialog, ToggleDestination, weld::Toggleable&, void)
+{
+ ToggleDestination();
+}
+
+void ScPivotLayoutDialog::ToggleDestination()
+{
+ bool bNamedRange = mxDestinationRadioNamedRange->get_active();
+ bool bSelection = mxDestinationRadioSelection->get_active();
+ mxDestinationListBox->set_sensitive(bNamedRange);
+ mxDestinationButton->GetWidget()->set_sensitive(bSelection);
+ mxDestinationEdit->GetWidget()->set_sensitive(bSelection);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/PivotLayoutTreeList.cxx b/sc/source/ui/dbgui/PivotLayoutTreeList.cxx
new file mode 100644
index 000000000..c173d7ca7
--- /dev/null
+++ b/sc/source/ui/dbgui/PivotLayoutTreeList.cxx
@@ -0,0 +1,132 @@
+/* -*- 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:
+ */
+
+#include <memory>
+#include <PivotLayoutTreeList.hxx>
+#include <PivotLayoutDialog.hxx>
+
+#include <vcl/event.hxx>
+#include <pivot.hxx>
+
+ScPivotLayoutTreeList::ScPivotLayoutTreeList(std::unique_ptr<weld::TreeView> xControl)
+ : ScPivotLayoutTreeListBase(std::move(xControl))
+{
+ mxControl->connect_key_press(LINK(this, ScPivotLayoutTreeList, KeyInputHdl));
+ mxControl->connect_row_activated(LINK(this, ScPivotLayoutTreeList, DoubleClickHdl));
+}
+
+ScPivotLayoutTreeList::~ScPivotLayoutTreeList()
+{
+ if (mpSubtotalDlg)
+ {
+ mpSubtotalDlg->Response(RET_CANCEL);
+ mpSubtotalDlg.clear();
+ }
+}
+
+void ScPivotLayoutTreeList::Setup(ScPivotLayoutDialog* pParent, SvPivotTreeListType eType)
+{
+ mpParent = pParent;
+ meType = eType;
+}
+
+IMPL_LINK_NOARG(ScPivotLayoutTreeList, DoubleClickHdl, weld::TreeView&, bool)
+{
+ int nEntry = mxControl->get_cursor_index();
+ if (nEntry == -1)
+ return true;
+
+ ScItemValue* pCurrentItemValue = weld::fromId<ScItemValue*>(mxControl->get_id(nEntry));
+ ScPivotFuncData& rCurrentFunctionData = pCurrentItemValue->maFunctionData;
+ SCCOL nCurrentColumn = rCurrentFunctionData.mnCol;
+
+ if (mpParent->IsDataElement(nCurrentColumn))
+ return true;
+
+ ScDPLabelData& rCurrentLabelData = mpParent->GetLabelData(nCurrentColumn);
+
+ ScAbstractDialogFactory* pFactory = ScAbstractDialogFactory::Create();
+
+ maDataFieldNames.clear();
+ mpParent->PushDataFieldNames(maDataFieldNames);
+
+ mpSubtotalDlg = pFactory->CreateScDPSubtotalDlg(mxControl.get(), mpParent->maPivotTableObject,
+ rCurrentLabelData, rCurrentFunctionData,
+ maDataFieldNames);
+
+ mpSubtotalDlg->StartExecuteAsync([this, pCurrentItemValue, nCurrentColumn](int nResult) {
+ if (nResult == RET_OK)
+ {
+ mpSubtotalDlg->FillLabelData(mpParent->GetLabelData(nCurrentColumn));
+ pCurrentItemValue->maFunctionData.mnFuncMask = mpSubtotalDlg->GetFuncMask();
+ }
+
+ mpSubtotalDlg.disposeAndClear();
+ });
+
+ return true;
+}
+
+void ScPivotLayoutTreeList::FillFields(ScPivotFieldVector& rFieldVector)
+{
+ mxControl->clear();
+ maItemValues.clear();
+
+ for (const ScPivotField& rField : rFieldVector)
+ {
+ OUString aLabel = mpParent->GetItem(rField.nCol)->maName;
+ ScItemValue* pItemValue = new ScItemValue(aLabel, rField.nCol, rField.nFuncMask);
+ maItemValues.push_back(std::unique_ptr<ScItemValue>(pItemValue));
+ OUString sId(weld::toId(pItemValue));
+ mxControl->append(sId, pItemValue->maName);
+ }
+}
+
+void ScPivotLayoutTreeList::InsertEntryForSourceTarget(weld::TreeView& rSource, int nTarget)
+{
+ ScItemValue* pItemValue = weld::fromId<ScItemValue*>(rSource.get_selected_id());
+ ScItemValue* pOriginalItemValue = pItemValue->mpOriginalItemValue;
+
+ // Don't allow to add "Data" element to page fields
+ if (meType == PAGE_LIST && mpParent->IsDataElement(pItemValue->maFunctionData.mnCol))
+ return;
+
+ mpParent->ItemInserted(pOriginalItemValue, meType);
+
+ InsertEntryForItem(pOriginalItemValue, nTarget);
+}
+
+void ScPivotLayoutTreeList::InsertEntryForItem(const ScItemValue* pItemValue, int nPosition)
+{
+ ScItemValue* pListItemValue = new ScItemValue(pItemValue);
+ maItemValues.push_back(std::unique_ptr<ScItemValue>(pListItemValue));
+ OUString sName = pListItemValue->maName;
+ OUString sId(weld::toId(pListItemValue));
+ mxControl->insert(nullptr, nPosition, &sName, &sId, nullptr, nullptr, false, nullptr);
+}
+
+IMPL_LINK(ScPivotLayoutTreeList, KeyInputHdl, const KeyEvent&, rKeyEvent, bool)
+{
+ vcl::KeyCode aCode = rKeyEvent.GetKeyCode();
+ sal_uInt16 nCode = aCode.GetCode();
+
+ if (nCode == KEY_DELETE)
+ {
+ const int nEntry = mxControl->get_cursor_index();
+ if (nEntry != -1)
+ mxControl->remove(nEntry);
+ return true;
+ }
+
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/PivotLayoutTreeListBase.cxx b/sc/source/ui/dbgui/PivotLayoutTreeListBase.cxx
new file mode 100644
index 000000000..45af29a4f
--- /dev/null
+++ b/sc/source/ui/dbgui/PivotLayoutTreeListBase.cxx
@@ -0,0 +1,122 @@
+/* -*- 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:
+ */
+
+#include <PivotLayoutTreeListBase.hxx>
+#include <PivotLayoutDialog.hxx>
+
+ScPivotLayoutTreeListBase::ScPivotLayoutTreeListBase(std::unique_ptr<weld::TreeView> xControl, SvPivotTreeListType eType)
+ : mxControl(std::move(xControl))
+ , maDropTargetHelper(*this)
+ , meType(eType)
+ , mpParent(nullptr)
+{
+ mxControl->connect_focus_in(LINK(this, ScPivotLayoutTreeListBase, GetFocusHdl));
+ mxControl->connect_mnemonic_activate(LINK(this, ScPivotLayoutTreeListBase, MnemonicActivateHdl));
+ mxControl->connect_focus_out(LINK(this, ScPivotLayoutTreeListBase, LoseFocusHdl));
+}
+
+ScPivotLayoutTreeListBase::~ScPivotLayoutTreeListBase()
+{
+}
+
+void ScPivotLayoutTreeListBase::Setup(ScPivotLayoutDialog* pParent)
+{
+ mpParent = pParent;
+}
+
+ScPivotLayoutTreeDropTarget::ScPivotLayoutTreeDropTarget(ScPivotLayoutTreeListBase& rTreeView)
+ : DropTargetHelper(rTreeView.get_widget().get_drop_target())
+ , m_rTreeView(rTreeView)
+{
+}
+
+sal_Int8 ScPivotLayoutTreeDropTarget::AcceptDrop(const AcceptDropEvent& rEvt)
+{
+ // to enable the autoscroll when we're close to the edges
+ weld::TreeView& rWidget = m_rTreeView.get_widget();
+ rWidget.get_dest_row_at_pos(rEvt.maPosPixel, nullptr, true);
+ return DND_ACTION_MOVE;
+}
+
+sal_Int8 ScPivotLayoutTreeDropTarget::ExecuteDrop( const ExecuteDropEvent& rEvt )
+{
+ weld::TreeView& rWidget = m_rTreeView.get_widget();
+ weld::TreeView* pSource = rWidget.get_drag_source();
+ if (!pSource)
+ return DND_ACTION_NONE;
+
+ std::unique_ptr<weld::TreeIter> xTarget(rWidget.make_iterator());
+ int nTargetPos = -1;
+ if (rWidget.get_dest_row_at_pos(rEvt.maPosPixel, xTarget.get(), true))
+ nTargetPos = rWidget.get_iter_index_in_parent(*xTarget);
+ m_rTreeView.InsertEntryForSourceTarget(*pSource, nTargetPos);
+ rWidget.unset_drag_dest_row();
+ return DND_ACTION_NONE;
+}
+
+void ScPivotLayoutTreeListBase::PushEntriesToPivotFieldVector(ScPivotFieldVector& rVector)
+{
+ std::unique_ptr<weld::TreeIter> xEachEntry(mxControl->make_iterator());
+ if (!mxControl->get_iter_first(*xEachEntry))
+ return;
+ do
+ {
+ ScItemValue* pItemValue = weld::fromId<ScItemValue*>(mxControl->get_id(*xEachEntry));
+ ScPivotFuncData& rFunctionData = pItemValue->maFunctionData;
+
+ ScPivotField aField;
+ aField.nCol = rFunctionData.mnCol;
+ aField.mnOriginalDim = rFunctionData.mnOriginalDim;
+ aField.nFuncMask = rFunctionData.mnFuncMask;
+ aField.mnDupCount = rFunctionData.mnDupCount;
+ aField.maFieldRef = rFunctionData.maFieldRef;
+ rVector.push_back(aField);
+ } while (mxControl->iter_next(*xEachEntry));
+}
+
+void ScPivotLayoutTreeListBase::InsertEntryForSourceTarget(weld::TreeView& /*pSource*/, int /*nTarget*/)
+{
+}
+
+void ScPivotLayoutTreeListBase::RemoveEntryForItem(const ScItemValue* pItemValue)
+{
+ OUString sId(weld::toId(pItemValue));
+ int nPos = mxControl->find_id(sId);
+ if (nPos == -1)
+ return;
+ mxControl->remove(nPos);
+}
+
+IMPL_LINK_NOARG(ScPivotLayoutTreeListBase, GetFocusHdl, weld::Widget&, void)
+{
+ if (!mpParent)
+ return;
+ mpParent->mpPreviouslyFocusedListBox = this;
+}
+
+IMPL_LINK_NOARG(ScPivotLayoutTreeListBase, MnemonicActivateHdl, weld::Widget&, bool)
+{
+ if (!mpParent || !mpParent->mpPreviouslyFocusedListBox)
+ return false;
+ weld::TreeView& rSource = mpParent->mpPreviouslyFocusedListBox->get_widget();
+ int nEntry = rSource.get_cursor_index();
+ if (nEntry != -1)
+ InsertEntryForSourceTarget(rSource, -1);
+ return true;
+}
+
+IMPL_LINK_NOARG(ScPivotLayoutTreeListBase, LoseFocusHdl, weld::Widget&, void)
+{
+ if (!mpParent)
+ return;
+ mpParent->mpPreviouslyFocusedListBox = nullptr;
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/PivotLayoutTreeListData.cxx b/sc/source/ui/dbgui/PivotLayoutTreeListData.cxx
new file mode 100644
index 000000000..19992fc31
--- /dev/null
+++ b/sc/source/ui/dbgui/PivotLayoutTreeListData.cxx
@@ -0,0 +1,287 @@
+/* -*- 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:
+ */
+
+#include <memory>
+#include <string_view>
+
+#include <PivotLayoutTreeListData.hxx>
+#include <PivotLayoutDialog.hxx>
+
+#include <vcl/event.hxx>
+#include <pivot.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+
+namespace
+{
+
+OUString lclGetFunctionMaskName(const PivotFunc nFunctionMask)
+{
+ TranslateId pStrId;
+ switch (nFunctionMask)
+ {
+ case PivotFunc::Sum: pStrId = STR_FUN_TEXT_SUM; break;
+ case PivotFunc::Count: pStrId = STR_FUN_TEXT_COUNT; break;
+ case PivotFunc::Average: pStrId = STR_FUN_TEXT_AVG; break;
+ case PivotFunc::Median: pStrId = STR_FUN_TEXT_MEDIAN; break;
+ case PivotFunc::Max: pStrId = STR_FUN_TEXT_MAX; break;
+ case PivotFunc::Min: pStrId = STR_FUN_TEXT_MIN; break;
+ case PivotFunc::Product: pStrId = STR_FUN_TEXT_PRODUCT; break;
+ case PivotFunc::CountNum: pStrId = STR_FUN_TEXT_COUNT; break;
+ case PivotFunc::StdDev: pStrId = STR_FUN_TEXT_STDDEV; break;
+ case PivotFunc::StdDevP: pStrId = STR_FUN_TEXT_STDDEV; break;
+ case PivotFunc::StdVar: pStrId = STR_FUN_TEXT_VAR; break;
+ case PivotFunc::StdVarP: pStrId = STR_FUN_TEXT_VAR; break;
+ default:
+ assert(false);
+ break;
+ }
+ if (pStrId)
+ return ScResId(pStrId);
+ else
+ return OUString();
+}
+
+OUString lclCreateDataItemName(const PivotFunc nFunctionMask, std::u16string_view rName, const sal_uInt8 nDuplicationCount)
+{
+ OUString aBuffer = lclGetFunctionMaskName(nFunctionMask) + " - " + rName;
+ if(nDuplicationCount > 0)
+ {
+ aBuffer += " " + OUString::number(nDuplicationCount);
+ }
+ return aBuffer;
+}
+
+} // anonymous namespace
+
+ScPivotLayoutTreeListData::ScPivotLayoutTreeListData(std::unique_ptr<weld::TreeView> xControl)
+ : ScPivotLayoutTreeListBase(std::move(xControl))
+{
+ mxControl->connect_key_press(LINK(this, ScPivotLayoutTreeListData, KeyInputHdl));
+ mxControl->connect_row_activated(LINK(this, ScPivotLayoutTreeListData, DoubleClickHdl));
+}
+
+ScPivotLayoutTreeListData::~ScPivotLayoutTreeListData()
+{
+ if (mpFunctionDlg)
+ {
+ mpFunctionDlg->Response(RET_CANCEL);
+ mpFunctionDlg.clear();
+ }
+}
+
+IMPL_LINK_NOARG(ScPivotLayoutTreeListData, DoubleClickHdl, weld::TreeView&, bool)
+{
+ int nEntry = mxControl->get_cursor_index();
+ if (nEntry == -1)
+ return true;
+
+ ScItemValue* pCurrentItemValue = weld::fromId<ScItemValue*>(mxControl->get_id(nEntry));
+ ScPivotFuncData& rCurrentFunctionData = pCurrentItemValue->maFunctionData;
+
+ SCCOL nCurrentColumn = rCurrentFunctionData.mnCol;
+ ScDPLabelData& rCurrentLabelData = mpParent->GetLabelData(nCurrentColumn);
+
+ ScAbstractDialogFactory* pFactory = ScAbstractDialogFactory::Create();
+
+ mpFunctionDlg = pFactory->CreateScDPFunctionDlg(mxControl.get(), mpParent->GetLabelDataVector(), rCurrentLabelData, rCurrentFunctionData);
+
+ mpFunctionDlg->StartExecuteAsync([this, pCurrentItemValue,
+ rCurrentLabelData, nEntry](int nResult) mutable {
+ if (nResult == RET_OK)
+ {
+ ScPivotFuncData& rFunctionData = pCurrentItemValue->maFunctionData;
+ rFunctionData.mnFuncMask = mpFunctionDlg->GetFuncMask();
+ rCurrentLabelData.mnFuncMask = mpFunctionDlg->GetFuncMask();
+
+ rFunctionData.maFieldRef = mpFunctionDlg->GetFieldRef();
+
+ ScDPLabelData& rDFData = mpParent->GetLabelData(rFunctionData.mnCol);
+
+ AdjustDuplicateCount(pCurrentItemValue);
+
+ OUString sDataItemName = lclCreateDataItemName(
+ rFunctionData.mnFuncMask,
+ rDFData.maName,
+ rFunctionData.mnDupCount);
+
+ mxControl->set_text(nEntry, sDataItemName);
+ }
+
+ mpFunctionDlg->disposeOnce();
+ });
+
+ return true;
+}
+
+void ScPivotLayoutTreeListData::FillDataField(ScPivotFieldVector& rDataFields)
+{
+ mxControl->clear();
+ maDataItemValues.clear();
+
+ for (const ScPivotField& rField : rDataFields)
+ {
+ if (rField.nCol == PIVOT_DATA_FIELD)
+ continue;
+
+ SCCOL nColumn;
+ if (rField.mnOriginalDim >= 0)
+ nColumn = rField.mnOriginalDim;
+ else
+ nColumn = rField.nCol;
+
+ ScItemValue* pOriginalItemValue = mpParent->GetItem(nColumn);
+ ScItemValue* pItemValue = new ScItemValue(pOriginalItemValue->maName, nColumn, rField.nFuncMask);
+
+ pItemValue->mpOriginalItemValue = pOriginalItemValue;
+ pItemValue->maFunctionData.mnOriginalDim = rField.mnOriginalDim;
+ pItemValue->maFunctionData.maFieldRef = rField.maFieldRef;
+
+ AdjustDuplicateCount(pItemValue);
+ OUString sDataItemName = lclCreateDataItemName(pItemValue->maFunctionData.mnFuncMask,
+ pItemValue->maName,
+ pItemValue->maFunctionData.mnDupCount);
+
+ maDataItemValues.push_back(std::unique_ptr<ScItemValue>(pItemValue));
+ OUString sId(weld::toId(pItemValue));
+ mxControl->append(sId, sDataItemName);
+ }
+}
+
+void ScPivotLayoutTreeListData::PushDataFieldNames(std::vector<ScDPName>& rDataFieldNames)
+{
+ std::unique_ptr<weld::TreeIter> xLoopEntry(mxControl->make_iterator());
+ if (!mxControl->get_iter_first(*xLoopEntry))
+ return;
+
+ do
+ {
+ ScItemValue* pEachItemValue = weld::fromId<ScItemValue*>(mxControl->get_id(*xLoopEntry));
+ SCCOL nColumn = pEachItemValue->maFunctionData.mnCol;
+
+ ScDPLabelData& rLabelData = mpParent->GetLabelData(nColumn);
+
+ if (rLabelData.maName.isEmpty())
+ continue;
+
+ OUString sLayoutName = rLabelData.maLayoutName;
+ if (sLayoutName.isEmpty())
+ {
+ sLayoutName = lclCreateDataItemName(
+ pEachItemValue->maFunctionData.mnFuncMask,
+ pEachItemValue->maName,
+ pEachItemValue->maFunctionData.mnDupCount);
+ }
+
+ rDataFieldNames.emplace_back(rLabelData.maName, sLayoutName, rLabelData.mnDupCount);
+ } while (mxControl->iter_next(*xLoopEntry));
+}
+
+void ScPivotLayoutTreeListData::InsertEntryForSourceTarget(weld::TreeView& rSource, int nTarget)
+{
+ if (rSource.count_selected_rows() <=0)
+ return;
+
+ ScItemValue* pItemValue = weld::fromId<ScItemValue*>(rSource.get_selected_id());
+
+ if (mpParent->IsDataElement(pItemValue->maFunctionData.mnCol))
+ return;
+
+ if (&rSource == mxControl.get())
+ {
+ OUString sText = mxControl->get_selected_text();
+ OUString sId(weld::toId(pItemValue));
+ mxControl->remove_id(sId);
+ mxControl->insert(nullptr, nTarget, &sText, &sId, nullptr, nullptr, false, nullptr);
+ }
+ else
+ {
+ InsertEntryForItem(pItemValue->mpOriginalItemValue, nTarget);
+ }
+}
+
+void ScPivotLayoutTreeListData::InsertEntryForItem(ScItemValue* pItemValue, int nPosition)
+{
+ ScItemValue* pDataItemValue = new ScItemValue(pItemValue);
+ pDataItemValue->mpOriginalItemValue = pItemValue;
+ maDataItemValues.push_back(std::unique_ptr<ScItemValue>(pDataItemValue));
+
+ ScPivotFuncData& rFunctionData = pDataItemValue->maFunctionData;
+
+ if (rFunctionData.mnFuncMask == PivotFunc::NONE ||
+ rFunctionData.mnFuncMask == PivotFunc::Auto)
+ {
+ rFunctionData.mnFuncMask = PivotFunc::Sum;
+ }
+
+ AdjustDuplicateCount(pDataItemValue);
+
+ OUString sDataName = lclCreateDataItemName(
+ rFunctionData.mnFuncMask,
+ pDataItemValue->maName,
+ rFunctionData.mnDupCount);
+
+ OUString sId(weld::toId(pDataItemValue));
+ mxControl->insert(nullptr, nPosition, &sDataName, &sId, nullptr, nullptr, false, nullptr);
+}
+
+void ScPivotLayoutTreeListData::AdjustDuplicateCount(ScItemValue* pInputItemValue)
+{
+ ScPivotFuncData& rInputFunctionData = pInputItemValue->maFunctionData;
+
+ bool bFoundDuplicate = false;
+
+ rInputFunctionData.mnDupCount = 0;
+ sal_uInt8 nMaxDuplicateCount = 0;
+
+ std::unique_ptr<weld::TreeIter> xEachEntry(mxControl->make_iterator());
+ if (!mxControl->get_iter_first(*xEachEntry))
+ return;
+ do
+ {
+ ScItemValue* pItemValue = weld::fromId<ScItemValue*>(mxControl->get_id(*xEachEntry));
+ if (pItemValue == pInputItemValue)
+ continue;
+
+ ScPivotFuncData& rFunctionData = pItemValue->maFunctionData;
+
+ if (rFunctionData.mnCol == rInputFunctionData.mnCol &&
+ rFunctionData.mnFuncMask == rInputFunctionData.mnFuncMask)
+ {
+ bFoundDuplicate = true;
+ if(rFunctionData.mnDupCount > nMaxDuplicateCount)
+ nMaxDuplicateCount = rFunctionData.mnDupCount;
+ }
+ } while (mxControl->iter_next(*xEachEntry));
+
+ if(bFoundDuplicate)
+ {
+ rInputFunctionData.mnDupCount = nMaxDuplicateCount + 1;
+ }
+}
+
+IMPL_LINK(ScPivotLayoutTreeListData, KeyInputHdl, const KeyEvent&, rKeyEvent, bool)
+{
+ vcl::KeyCode aCode = rKeyEvent.GetKeyCode();
+ sal_uInt16 nCode = aCode.GetCode();
+
+ if (nCode == KEY_DELETE)
+ {
+ int nEntry = mxControl->get_cursor_index();
+ if (nEntry != -1)
+ mxControl->remove(nEntry);
+ return true;
+ }
+
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/PivotLayoutTreeListLabel.cxx b/sc/source/ui/dbgui/PivotLayoutTreeListLabel.cxx
new file mode 100644
index 000000000..e4a2276da
--- /dev/null
+++ b/sc/source/ui/dbgui/PivotLayoutTreeListLabel.cxx
@@ -0,0 +1,85 @@
+/* -*- 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:
+ */
+
+#include <memory>
+#include <PivotLayoutTreeListLabel.hxx>
+#include <PivotLayoutDialog.hxx>
+
+#include <vcl/event.hxx>
+#include <pivot.hxx>
+
+ScPivotLayoutTreeListLabel::ScPivotLayoutTreeListLabel(std::unique_ptr<weld::TreeView> xControl)
+ : ScPivotLayoutTreeListBase(std::move(xControl), LABEL_LIST)
+ , maDataItem(0)
+{
+ mxControl->connect_key_press(LINK(this, ScPivotLayoutTreeListLabel, KeyInputHdl));
+}
+
+ScPivotLayoutTreeListLabel::~ScPivotLayoutTreeListLabel()
+{}
+
+void ScPivotLayoutTreeListLabel::FillLabelFields(ScDPLabelDataVector& rLabelVector)
+{
+ mxControl->clear();
+ maItemValues.clear();
+
+ for (std::unique_ptr<ScDPLabelData> const & pLabelData : rLabelVector)
+ {
+ ScItemValue* pValue = new ScItemValue(pLabelData->maName, pLabelData->mnCol, pLabelData->mnFuncMask);
+ maItemValues.push_back(std::unique_ptr<ScItemValue>(pValue));
+ if (pLabelData->mbDataLayout)
+ {
+ maDataItem = maItemValues.size() - 1;
+ }
+
+ if (pLabelData->mnOriginalDim < 0 && !pLabelData->mbDataLayout)
+ {
+ mxControl->append(weld::toId(pValue), pLabelData->maName);
+ }
+ }
+}
+
+void ScPivotLayoutTreeListLabel::InsertEntryForSourceTarget(weld::TreeView& rSource, int /*nTarget*/)
+{
+ if (&rSource == mxControl.get())
+ return;
+ rSource.remove(rSource.get_selected_index());
+}
+
+bool ScPivotLayoutTreeListLabel::IsDataElement(SCCOL nColumn)
+{
+ return (nColumn == PIVOT_DATA_FIELD || nColumn == maDataItem);
+}
+
+ScItemValue* ScPivotLayoutTreeListLabel::GetItem(SCCOL nColumn)
+{
+ if (nColumn == PIVOT_DATA_FIELD)
+ return maItemValues[maDataItem].get();
+ return maItemValues[nColumn].get();
+}
+
+IMPL_LINK(ScPivotLayoutTreeListLabel, KeyInputHdl, const KeyEvent&, rKeyEvent, bool)
+{
+ vcl::KeyCode aCode = rKeyEvent.GetKeyCode();
+ sal_uInt16 nCode = aCode.GetCode();
+
+ if (nCode == KEY_DELETE)
+ {
+ int nEntry = mxControl->get_cursor_index();
+ if (nEntry != -1)
+ mxControl->remove(nEntry);
+ return true;
+ }
+
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/asciiopt.cxx b/sc/source/ui/dbgui/asciiopt.cxx
new file mode 100644
index 000000000..933491efb
--- /dev/null
+++ b/sc/source/ui/dbgui/asciiopt.cxx
@@ -0,0 +1,296 @@
+/* -*- 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 <global.hxx>
+#include <asciiopt.hxx>
+#include <comphelper/string.hxx>
+#include <osl/thread.h>
+#include <o3tl/string_view.hxx>
+
+constexpr std::u16string_view pStrFix = u"FIX";
+constexpr std::u16string_view pStrMrg = u"MRG";
+
+ScAsciiOptions::ScAsciiOptions() :
+ bFixedLen ( false ),
+ aFieldSeps ( OUString(';') ),
+ bMergeFieldSeps ( false ),
+ bRemoveSpace ( false ),
+ bQuotedFieldAsText(false),
+ bDetectSpecialNumber(false),
+ bEvaluateFormulas(true),
+ bSkipEmptyCells(false),
+ bSaveAsShown(true),
+ bSaveFormulas(false),
+ cTextSep ( cDefaultTextSep ),
+ eCharSet ( osl_getThreadTextEncoding() ),
+ eLang ( LANGUAGE_SYSTEM ),
+ bCharSetSystem ( false ),
+ nStartRow ( 1 )
+{
+}
+
+void ScAsciiOptions::SetColumnInfo( const ScCsvExpDataVec& rDataVec )
+{
+ sal_uInt16 nInfoCount = static_cast< sal_uInt16 >( rDataVec.size() );
+ mvColStart.resize(nInfoCount);
+ mvColFormat.resize(nInfoCount);
+ for( sal_uInt16 nIx = 0; nIx < nInfoCount; ++nIx )
+ {
+ mvColStart[ nIx ] = rDataVec[ nIx ].mnIndex;
+ mvColFormat[ nIx ] = rDataVec[ nIx ].mnType;
+ }
+}
+
+static OUString lcl_decodeSepString( std::u16string_view rSepNums, bool & o_bMergeFieldSeps )
+{
+ if ( rSepNums.empty() )
+ return OUString();
+
+ OUStringBuffer aFieldSeps;
+ sal_Int32 nPos = 0;
+ do
+ {
+ const std::u16string_view aCode = o3tl::getToken(rSepNums, 0, '/', nPos );
+ if ( aCode == pStrMrg )
+ o_bMergeFieldSeps = true;
+ else
+ {
+ sal_Int32 nVal = o3tl::toInt32(aCode);
+ if ( nVal )
+ aFieldSeps.append(sal_Unicode(nVal));
+ }
+ }
+ while ( nPos >= 0 );
+
+ return aFieldSeps.makeStringAndClear();
+}
+
+// The options string must not contain semicolons (because of the pick list),
+// use comma as separator.
+
+void ScAsciiOptions::ReadFromString( std::u16string_view rString )
+{
+ sal_Int32 nPos = rString.empty() ? -1 : 0;
+
+ // Token 0: Field separator.
+ if ( nPos >= 0 )
+ {
+ bFixedLen = bMergeFieldSeps = false;
+
+ const std::u16string_view aToken = o3tl::getToken(rString, 0, ',', nPos);
+ if ( aToken == pStrFix )
+ bFixedLen = true;
+ aFieldSeps = lcl_decodeSepString( aToken, bMergeFieldSeps);
+ }
+
+ // Token 1: Text separator.
+ if ( nPos >= 0 )
+ {
+ const sal_Int32 nVal = o3tl::toInt32(o3tl::getToken(rString, 0, ',', nPos));
+ cTextSep = static_cast<sal_Unicode>(nVal);
+ }
+
+ // Token 2: Text encoding.
+ if ( nPos >= 0 )
+ {
+ eCharSet = ScGlobal::GetCharsetValue( o3tl::getToken(rString, 0, ',', nPos) );
+ }
+
+ // Token 3: Number of start row.
+ if ( nPos >= 0 )
+ {
+ nStartRow = o3tl::toInt32(o3tl::getToken(rString, 0, ',', nPos));
+ }
+
+ // Token 4: Column info.
+ if ( nPos >= 0 )
+ {
+ const std::u16string_view aToken = o3tl::getToken(rString, 0, ',', nPos);
+ const sal_Int32 nInfoCount = comphelper::string::getTokenCount(aToken, '/')/2;
+ mvColStart.resize(nInfoCount);
+ mvColFormat.resize(nInfoCount);
+ sal_Int32 nP = 0;
+ for (sal_Int32 nInfo=0; nInfo<nInfoCount; ++nInfo)
+ {
+ mvColStart[nInfo] = o3tl::toInt32(o3tl::getToken(aToken, 0, '/', nP));
+ mvColFormat[nInfo] = static_cast<sal_uInt8>(o3tl::toInt32(o3tl::getToken(aToken, 0, '/', nP)));
+ }
+ }
+
+ // Token 5: Language.
+ if (nPos >= 0)
+ {
+ eLang = static_cast<LanguageType>(o3tl::toInt32(o3tl::getToken(rString, 0, ',', nPos)));
+ }
+
+ // Token 6: Import quoted field as text.
+ if (nPos >= 0)
+ {
+ bQuotedFieldAsText = o3tl::getToken(rString, 0, ',', nPos) == u"true";
+ }
+
+ // Token 7: Detect special numbers.
+ if (nPos >= 0)
+ {
+ bDetectSpecialNumber = o3tl::getToken(rString, 0, ',', nPos) == u"true";
+ }
+ else
+ bDetectSpecialNumber = true; // default of versions that didn't add the parameter
+
+ // Token 8: used for "Save as shown" in export options
+ if ( nPos >= 0 )
+ {
+ bSaveAsShown = o3tl::getToken(rString, 0, ',', nPos) == u"true";
+ }
+ else
+ bSaveAsShown = true; // default value
+
+ // Token 9: used for "Save cell formulas" in export options
+ if ( nPos >= 0 )
+ {
+ bSaveFormulas = o3tl::getToken(rString, 0, ',', nPos) == u"true";
+ }
+ else
+ bSaveFormulas = false;
+
+ // Token 10: Boolean for Trim spaces.
+ if (nPos >= 0)
+ {
+ bRemoveSpace = o3tl::getToken(rString, 0, ',', nPos) == u"true";
+ }
+ else
+ bRemoveSpace = false;
+
+ // Token 11: sheet to export for --convert-to csv
+ // Does not need to be evaluated here but may be present.
+ if (nPos >= 0)
+ {
+ o3tl::getToken(rString, 0, ',', nPos);
+ }
+
+ // Token 12: evaluate formulas.
+ if (nPos >= 0)
+ {
+ // If present, defaults to "false".
+ bEvaluateFormulas = o3tl::getToken(rString, 0, ',', nPos) == u"true";
+ }
+ else
+ bEvaluateFormulas = true; // default of versions that didn't add the parameter
+}
+
+OUString ScAsciiOptions::WriteToString() const
+{
+ OUStringBuffer aOutStr;
+
+ // Token 0: Field separator.
+ if ( bFixedLen )
+ aOutStr.append(pStrFix);
+ else if ( aFieldSeps.isEmpty() )
+ aOutStr.append("0");
+ else
+ {
+ sal_Int32 nLen = aFieldSeps.getLength();
+ for (sal_Int32 i=0; i<nLen; i++)
+ {
+ if (i)
+ aOutStr.append("/");
+ aOutStr.append(OUString::number(aFieldSeps[i]));
+ }
+ if ( bMergeFieldSeps )
+ {
+ aOutStr.append("/");
+ aOutStr.append(pStrMrg);
+ }
+ }
+
+ // Token 1: Text Quote character.
+ aOutStr.append("," + OUString::number(cTextSep) + ",");
+
+ //Token 2: Text encoding.
+ if ( bCharSetSystem ) // force "SYSTEM"
+ aOutStr.append(ScGlobal::GetCharsetString( RTL_TEXTENCODING_DONTKNOW ));
+ else
+ aOutStr.append(ScGlobal::GetCharsetString( eCharSet ));
+
+ //Token 3: Number of start row.
+ aOutStr.append("," + OUString::number(nStartRow) + ",");
+
+ //Token 4: Column info.
+ for (size_t nInfo=0; nInfo<mvColStart.size(); nInfo++)
+ {
+ if (nInfo)
+ aOutStr.append("/");
+ aOutStr.append(OUString::number(mvColStart[nInfo]) +
+ "/" +
+ OUString::number(mvColFormat[nInfo]));
+ }
+
+ // #i112025# the options string is used in macros and linked sheets,
+ // so new options must be added at the end, to remain compatible
+ // Always keep in sync with ScImportOptions.
+
+ aOutStr.append("," +
+ // Token 5: Language
+ OUString::number(static_cast<sal_uInt16>(eLang)) + "," +
+ // Token 6: Import quoted field as text.
+ OUString::boolean( bQuotedFieldAsText ) + "," +
+ // Token 7: Detect special numbers.
+ OUString::boolean( bDetectSpecialNumber ) + "," +
+ // Token 8: used for "Save as shown" in export options
+ OUString::boolean( bSaveAsShown ) +"," +
+ // Token 9: used for "Save cell formulas" in export options
+ OUString::boolean( bSaveFormulas ) + "," +
+ // Token 10: Trim Space
+ OUString::boolean( bRemoveSpace ) +
+ // Token 11: sheet to export, always 0 for current sheet
+ ",0," +
+ // Token 12: evaluate formulas in import
+ OUString::boolean( bEvaluateFormulas )
+ );
+ return aOutStr.makeStringAndClear();
+}
+
+// static
+sal_Unicode ScAsciiOptions::GetWeightedFieldSep( const OUString & rFieldSeps, bool bDecodeNumbers )
+{
+ bool bMergeFieldSeps = false;
+ OUString aFieldSeps( bDecodeNumbers ? lcl_decodeSepString( rFieldSeps, bMergeFieldSeps) : rFieldSeps);
+ if (aFieldSeps.isEmpty())
+ {
+ return 0;
+ }
+ else if (aFieldSeps.getLength() == 1)
+ return aFieldSeps[0];
+ else
+ {
+ // There can be only one separator for output. See also fdo#53449
+ if (aFieldSeps.indexOf(',') != -1)
+ return ',';
+ else if (aFieldSeps.indexOf('\t') != -1)
+ return '\t';
+ else if (aFieldSeps.indexOf(';') != -1)
+ return ';';
+ else if (aFieldSeps.indexOf(' ') != -1)
+ return ' ';
+ else
+ return aFieldSeps[0];
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/consdlg.cxx b/sc/source/ui/dbgui/consdlg.cxx
new file mode 100644
index 000000000..1dde53c18
--- /dev/null
+++ b/sc/source/ui/dbgui/consdlg.cxx
@@ -0,0 +1,535 @@
+/* -*- 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 <tabvwsh.hxx>
+#include <uiitems.hxx>
+#include <dbdata.hxx>
+#include <rangenam.hxx>
+#include <rangeutl.hxx>
+#include <reffact.hxx>
+#include <document.hxx>
+#include <scresid.hxx>
+
+#include <globstr.hrc>
+#include <strings.hrc>
+
+#include <consdlg.hxx>
+#include <o3tl/safeint.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+
+namespace
+{
+ void INFOBOX(weld::Window* pWindow, TranslateId id)
+ {
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(pWindow,
+ VclMessageType::Info, VclButtonsType::Ok,
+ ScResId(id)));
+ xInfoBox->run();
+ }
+}
+
+class ScAreaData
+{
+public:
+ ScAreaData()
+ {
+ }
+
+ void Set( const OUString& rName, const OUString& rArea )
+ {
+ aStrName = rName;
+ aStrArea = rArea;
+ }
+
+ OUString aStrName;
+ OUString aStrArea;
+};
+
+ScConsolidateDlg::ScConsolidateDlg(SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent,
+ const SfxItemSet& rArgSet)
+
+ : ScAnyRefDlgController(pB, pCW, pParent, "modules/scalc/ui/consolidatedialog.ui", "ConsolidateDialog")
+ , aStrUndefined ( ScResId( SCSTR_UNDEFINED ) )
+ , theConsData ( static_cast<const ScConsolidateItem&>(
+ rArgSet.Get( rArgSet.GetPool()->
+ GetWhich( SID_CONSOLIDATE ) )
+ ).GetData() )
+ , rViewData ( static_cast<ScTabViewShell*>(SfxViewShell::Current())->
+ GetViewData() )
+ , rDoc ( static_cast<ScTabViewShell*>(SfxViewShell::Current())->
+ GetViewData().GetDocument() )
+ , nAreaDataCount ( 0 )
+ , nWhichCons ( rArgSet.GetPool()->GetWhich( SID_CONSOLIDATE ) )
+ , bDlgLostFocus ( false )
+ , m_xLbFunc(m_xBuilder->weld_combo_box("func"))
+ , m_xLbConsAreas(m_xBuilder->weld_tree_view("consareas"))
+ , m_xLbDataArea(m_xBuilder->weld_combo_box("lbdataarea"))
+ , m_xEdDataArea(new formula::RefEdit(m_xBuilder->weld_entry("eddataarea")))
+ , m_xRbDataArea(new formula::RefButton(m_xBuilder->weld_button("rbdataarea")))
+ , m_xLbDestArea(m_xBuilder->weld_combo_box("lbdestarea"))
+ , m_xEdDestArea(new formula::RefEdit(m_xBuilder->weld_entry("eddestarea")))
+ , m_xRbDestArea(new formula::RefButton(m_xBuilder->weld_button("rbdestarea")))
+ , m_xExpander(m_xBuilder->weld_expander("more"))
+ , m_xBtnByRow(m_xBuilder->weld_check_button("byrow"))
+ , m_xBtnByCol(m_xBuilder->weld_check_button("bycol"))
+ , m_xBtnRefs(m_xBuilder->weld_check_button("refs"))
+ , m_xBtnOk(m_xBuilder->weld_button("ok"))
+ , m_xBtnCancel(m_xBuilder->weld_button("cancel"))
+ , m_xBtnAdd(m_xBuilder->weld_button("add"))
+ , m_xBtnRemove(m_xBuilder->weld_button("delete"))
+ , m_xDataFT(m_xBuilder->weld_label("ftdataarea"))
+ , m_xDestFT(m_xBuilder->weld_label("ftdestarea"))
+{
+ m_pRefInputEdit = m_xEdDataArea.get();
+ Init();
+}
+
+ScConsolidateDlg::~ScConsolidateDlg()
+{
+}
+
+void ScConsolidateDlg::Init()
+{
+ OUString aStr;
+ sal_uInt16 i=0;
+
+ m_xRbDataArea->SetReferences(this, m_xEdDataArea.get());
+ m_xEdDataArea->SetReferences(this, m_xDataFT.get());
+ m_xRbDestArea->SetReferences(this, m_xEdDestArea.get());
+ m_xEdDestArea->SetReferences(this, m_xDestFT.get());
+
+ m_xEdDataArea->SetGetFocusHdl( LINK( this, ScConsolidateDlg, GetEditFocusHdl ) );
+ m_xEdDestArea->SetGetFocusHdl( LINK( this, ScConsolidateDlg, GetEditFocusHdl ) );
+ m_xLbDataArea->connect_focus_in( LINK( this, ScConsolidateDlg, GetFocusHdl ) );
+ m_xLbDestArea->connect_focus_in( LINK( this, ScConsolidateDlg, GetFocusHdl ) );
+ m_xEdDataArea->SetModifyHdl( LINK( this, ScConsolidateDlg, ModifyHdl ) );
+ m_xEdDestArea->SetModifyHdl( LINK( this, ScConsolidateDlg, ModifyHdl ) );
+ m_xLbConsAreas->connect_changed( LINK( this, ScConsolidateDlg, SelectTVHdl ) );
+ m_xLbDataArea->connect_changed( LINK( this, ScConsolidateDlg, SelectCBHdl ) );
+ m_xLbDestArea->connect_changed( LINK( this, ScConsolidateDlg, SelectCBHdl ) );
+ m_xBtnOk->connect_clicked( LINK( this, ScConsolidateDlg, OkHdl ) );
+ m_xBtnCancel->connect_clicked( LINK( this, ScConsolidateDlg, ClickHdl ) );
+ m_xBtnAdd->connect_clicked( LINK( this, ScConsolidateDlg, ClickHdl ) );
+ m_xBtnRemove->connect_clicked( LINK( this, ScConsolidateDlg, ClickHdl ) );
+
+ m_xBtnAdd->set_sensitive(false);
+ m_xBtnRemove->set_sensitive(false);
+
+ m_xBtnByRow->set_active( theConsData.bByRow );
+ m_xBtnByCol->set_active( theConsData.bByCol );
+ m_xBtnRefs->set_active( theConsData.bReferenceData );
+
+ m_xLbFunc->set_active( FuncToLbPos( theConsData.eFunction ) );
+
+ m_xLbConsAreas->set_selection_mode(SelectionMode::Multiple);
+ m_xLbConsAreas->set_size_request(m_xLbConsAreas->get_approximate_digit_width() * 16,
+ m_xLbConsAreas->get_height_rows(5));
+
+ // read consolidation areas
+ m_xLbConsAreas->clear();
+ const formula::FormulaGrammar::AddressConvention eConv = rDoc.GetAddressConvention();
+ for ( i=0; i<theConsData.nDataAreaCount; i++ )
+ {
+ const ScArea& rArea = theConsData.pDataAreas[i];
+ if ( rArea.nTab < rDoc.GetTableCount() )
+ {
+ aStr = ScRange( rArea.nColStart, rArea.nRowStart, rArea.nTab,
+ rArea.nColEnd, rArea.nRowEnd, rArea.nTab ).Format( rDoc,
+ ScRefFlags::RANGE_ABS_3D, eConv );
+ m_xLbConsAreas->append_text(aStr);
+ }
+ }
+
+ if ( theConsData.nTab < rDoc.GetTableCount() )
+ {
+ aStr = ScAddress( theConsData.nCol, theConsData.nRow, theConsData.nTab
+ ).Format( ScRefFlags::ADDR_ABS_3D, &rDoc, eConv );
+ m_xEdDestArea->SetText( aStr );
+ }
+ else
+ m_xEdDestArea->SetText(OUString());
+
+ // Use the ScAreaData helper class to save those range names from the
+ // RangeNames and database ranges that appear in the ListBoxes.
+
+ ScRangeName* pRangeNames = rDoc.GetRangeName();
+ ScDBCollection* pDbNames = rDoc.GetDBCollection();
+ size_t nRangeCount = pRangeNames ? pRangeNames->size() : 0;
+ size_t nDbCount = pDbNames ? pDbNames->getNamedDBs().size() : 0;
+
+ nAreaDataCount = nRangeCount+nDbCount;
+ pAreaData = nullptr;
+
+ if ( nAreaDataCount > 0 )
+ {
+ pAreaData.reset( new ScAreaData[nAreaDataCount] );
+
+ OUString aStrName;
+ sal_uInt16 nAt = 0;
+ ScRange aRange;
+ ScAreaNameIterator aIter( rDoc );
+ while ( aIter.Next( aStrName, aRange ) )
+ {
+ OUString aStrArea(aRange.Format(rDoc, ScRefFlags::ADDR_ABS_3D, eConv));
+ pAreaData[nAt++].Set( aStrName, aStrArea );
+ }
+ }
+
+ FillAreaLists();
+ ModifyHdl( *m_xEdDestArea );
+ m_xLbDataArea->set_active( 0 );
+ m_xEdDataArea->SetText(OUString());
+ m_xEdDataArea->GrabFocus();
+
+ //aFlSep.SetStyle( aFlSep.GetStyle() | WB_VERT );
+
+ //@BugID 54702 enable/disable only in base class
+ //SFX_APPWINDOW->set_sensitive(true);
+}
+
+void ScConsolidateDlg::FillAreaLists()
+{
+ m_xLbDataArea->clear();
+ m_xLbDestArea->clear();
+ m_xLbDataArea->append_text( aStrUndefined );
+ m_xLbDestArea->append_text( aStrUndefined );
+
+ if ( pAreaData && (nAreaDataCount > 0) )
+ {
+ for ( size_t i=0;
+ (i<nAreaDataCount) && (!pAreaData[i].aStrName.isEmpty());
+ i++ )
+ {
+ m_xLbDataArea->append_text(pAreaData[i].aStrName);
+ m_xLbDestArea->append_text(pAreaData[i].aStrName);
+ }
+ }
+}
+
+// Handover of a range within a table that has been selected by the mouse.
+// This range is then shown in the reference window as new selection.
+
+void ScConsolidateDlg::SetReference( const ScRange& rRef, ScDocument& rDocP )
+{
+ if ( !m_pRefInputEdit )
+ return;
+
+ if ( rRef.aStart != rRef.aEnd )
+ RefInputStart( m_pRefInputEdit );
+
+ OUString aStr;
+ ScRefFlags nFmt = ScRefFlags::RANGE_ABS_3D; //!!! nCurTab is still missing
+ const formula::FormulaGrammar::AddressConvention eConv = rDocP.GetAddressConvention();
+
+ if ( rRef.aStart.Tab() != rRef.aEnd.Tab() )
+ nFmt |= ScRefFlags::TAB2_3D;
+
+ if ( m_pRefInputEdit == m_xEdDataArea.get())
+ aStr = rRef.Format(rDocP, nFmt, eConv);
+ else if ( m_pRefInputEdit == m_xEdDestArea.get() )
+ aStr = rRef.aStart.Format(nFmt, &rDocP, eConv);
+
+ m_pRefInputEdit->SetRefString( aStr );
+ ModifyHdl( *m_pRefInputEdit );
+}
+
+void ScConsolidateDlg::Close()
+{
+ DoClose( ScConsolidateDlgWrapper::GetChildWindowId() );
+}
+
+void ScConsolidateDlg::SetActive()
+{
+ if ( bDlgLostFocus )
+ {
+ bDlgLostFocus = false;
+
+ if ( m_pRefInputEdit )
+ {
+ m_pRefInputEdit->GrabFocus();
+ ModifyHdl( *m_pRefInputEdit );
+ }
+ }
+ else
+ m_xDialog->grab_focus();
+
+ RefInputDone();
+}
+
+void ScConsolidateDlg::Deactivate()
+{
+ bDlgLostFocus = true;
+}
+
+bool ScConsolidateDlg::VerifyEdit( formula::RefEdit* pEd )
+{
+ if (pEd != m_xEdDataArea.get() && pEd != m_xEdDestArea.get())
+ return false;
+
+ SCTAB nTab = rViewData.GetTabNo();
+ bool bEditOk = false;
+ OUString theCompleteStr;
+ const formula::FormulaGrammar::AddressConvention eConv = rDoc.GetAddressConvention();
+
+ if ( pEd == m_xEdDataArea.get() )
+ {
+ bEditOk = ScRangeUtil::IsAbsArea( pEd->GetText(), rDoc,
+ nTab, &theCompleteStr, nullptr, nullptr, eConv );
+ }
+ else if ( pEd == m_xEdDestArea.get() )
+ {
+ OUString aPosStr;
+
+ ScRangeUtil::CutPosString( pEd->GetText(), aPosStr );
+ bEditOk = ScRangeUtil::IsAbsPos( aPosStr, rDoc,
+ nTab, &theCompleteStr, nullptr, eConv );
+ }
+
+ if ( bEditOk )
+ pEd->SetText( theCompleteStr );
+
+ return bEditOk;
+}
+
+// Handler:
+
+IMPL_LINK( ScConsolidateDlg, GetEditFocusHdl, formula::RefEdit&, rControl, void )
+{
+ m_pRefInputEdit = &rControl;
+}
+
+IMPL_LINK( ScConsolidateDlg, GetFocusHdl, weld::Widget&, rControl, void )
+{
+ if (&rControl == m_xLbDataArea.get())
+ m_pRefInputEdit = m_xEdDataArea.get();
+ else if (&rControl == m_xLbDestArea.get())
+ m_pRefInputEdit = m_xEdDestArea.get();
+}
+
+IMPL_LINK_NOARG(ScConsolidateDlg, OkHdl, weld::Button&, void)
+{
+ const sal_Int32 nDataAreaCount = m_xLbConsAreas->n_children();
+
+ if ( nDataAreaCount > 0 )
+ {
+ ScRefAddress aDestAddress;
+ SCTAB nTab = rViewData.GetTabNo();
+ OUString aDestPosStr( m_xEdDestArea->GetText() );
+ const formula::FormulaGrammar::AddressConvention eConv = rDoc.GetAddressConvention();
+
+ if ( ScRangeUtil::IsAbsPos( aDestPosStr, rDoc, nTab, nullptr, &aDestAddress, eConv ) )
+ {
+ ScConsolidateParam theOutParam( theConsData );
+ std::unique_ptr<ScArea[]> pDataAreas(new ScArea[nDataAreaCount]);
+
+ for ( sal_Int32 i=0; i<nDataAreaCount; ++i )
+ {
+ ScRangeUtil::MakeArea(m_xLbConsAreas->get_text(i),
+ pDataAreas[i], rDoc, nTab, eConv);
+ }
+
+ theOutParam.nCol = aDestAddress.Col();
+ theOutParam.nRow = aDestAddress.Row();
+ theOutParam.nTab = aDestAddress.Tab();
+ theOutParam.eFunction = LbPosToFunc( m_xLbFunc->get_active() );
+ theOutParam.bByCol = m_xBtnByCol->get_active();
+ theOutParam.bByRow = m_xBtnByRow->get_active();
+ theOutParam.bReferenceData = m_xBtnRefs->get_active();
+ theOutParam.SetAreas( std::move(pDataAreas), nDataAreaCount );
+
+ ScConsolidateItem aOutItem( nWhichCons, &theOutParam );
+
+ SetDispatcherLock( false );
+ SwitchToDocument();
+ GetBindings().GetDispatcher()->ExecuteList(SID_CONSOLIDATE,
+ SfxCallMode::SLOT | SfxCallMode::RECORD,
+ { &aOutItem });
+ response(RET_OK);
+ }
+ else
+ {
+ INFOBOX(m_xDialog.get(), STR_INVALID_TABREF);
+ m_xEdDestArea->GrabFocus();
+ }
+ }
+ else
+ response(RET_CANCEL); // no area defined -> Cancel
+}
+
+IMPL_LINK( ScConsolidateDlg, ClickHdl, weld::Button&, rBtn, void )
+{
+ if ( &rBtn == m_xBtnCancel.get() )
+ response(RET_CANCEL);
+ else if ( &rBtn == m_xBtnAdd.get() )
+ {
+ if ( !m_xEdDataArea->GetText().isEmpty() )
+ {
+ OUString aNewEntry( m_xEdDataArea->GetText() );
+ std::unique_ptr<ScArea[]> ppAreas;
+ sal_uInt16 nAreaCount = 0;
+ const formula::FormulaGrammar::AddressConvention eConv = rDoc.GetAddressConvention();
+
+ if ( ScRangeUtil::IsAbsTabArea( aNewEntry, &rDoc, &ppAreas, &nAreaCount, true, eConv ) )
+ {
+ // IsAbsTabArea() creates an array of ScArea pointers,
+ // which have been created dynamically as well.
+ // These objects need to be deleted here.
+
+ for ( sal_uInt16 i=0; i<nAreaCount; i++ )
+ {
+ const ScArea& rArea = ppAreas[i];
+ OUString aNewArea = ScRange( rArea.nColStart, rArea.nRowStart, rArea.nTab,
+ rArea.nColEnd, rArea.nRowEnd, rArea.nTab
+ ).Format(rDoc, ScRefFlags::RANGE_ABS_3D, eConv);
+
+ if (m_xLbConsAreas->find_text(aNewArea) == -1)
+ {
+ m_xLbConsAreas->append_text( aNewArea );
+ }
+ }
+ }
+ else if ( VerifyEdit( m_xEdDataArea.get() ) )
+ {
+ OUString aNewArea( m_xEdDataArea->GetText() );
+
+ if (m_xLbConsAreas->find_text(aNewArea) == -1)
+ m_xLbConsAreas->append_text(aNewArea);
+ else
+ INFOBOX(m_xDialog.get(), STR_AREA_ALREADY_INSERTED);
+ }
+ else
+ {
+ INFOBOX(m_xDialog.get(), STR_INVALID_TABREF);
+ m_xEdDataArea->GrabFocus();
+ }
+ }
+ }
+ else if ( &rBtn == m_xBtnRemove.get() )
+ {
+ std::vector<int> aSelectedRows(m_xLbConsAreas->get_selected_rows());
+ std::sort(aSelectedRows.begin(), aSelectedRows.end());
+ for (auto it = aSelectedRows.rbegin(); it != aSelectedRows.rend(); ++it)
+ m_xLbConsAreas->remove(*it);
+ m_xBtnRemove->set_sensitive(false);
+ }
+}
+
+IMPL_LINK( ScConsolidateDlg, SelectTVHdl, weld::TreeView&, rLb, void )
+{
+ if (rLb.get_selected_index() != -1)
+ m_xBtnRemove->set_sensitive(true);
+ else
+ m_xBtnRemove->set_sensitive(false);
+}
+
+IMPL_LINK( ScConsolidateDlg, SelectCBHdl, weld::ComboBox&, rLb, void )
+{
+ formula::RefEdit* pEd = (&rLb == m_xLbDataArea.get()) ? m_xEdDataArea.get() : m_xEdDestArea.get();
+ const sal_Int32 nSelPos = rLb.get_active();
+
+ if ( (nSelPos > 0)
+ && (nAreaDataCount > 0)
+ && (pAreaData != nullptr) )
+ {
+ if ( o3tl::make_unsigned(nSelPos) <= nAreaDataCount )
+ {
+ OUString aString( pAreaData[nSelPos-1].aStrArea );
+
+ if ( &rLb == m_xLbDestArea.get() )
+ ScRangeUtil::CutPosString( aString, aString );
+
+ pEd->SetText( aString );
+
+ if ( pEd == m_xEdDataArea.get() )
+ m_xBtnAdd->set_sensitive(true);
+ }
+ }
+ else
+ {
+ pEd->SetText( OUString() );
+ if ( pEd == m_xEdDataArea.get() )
+ m_xBtnAdd->set_sensitive(true);
+ }
+}
+
+IMPL_LINK( ScConsolidateDlg, ModifyHdl, formula::RefEdit&, rEd, void )
+{
+ if ( &rEd == m_xEdDataArea.get() )
+ {
+ OUString aAreaStr( rEd.GetText() );
+ if ( !aAreaStr.isEmpty() )
+ m_xBtnAdd->set_sensitive(true);
+ else
+ m_xBtnAdd->set_sensitive(false);
+ }
+ else if ( &rEd == m_xEdDestArea.get() )
+ {
+ m_xLbDestArea->set_active(0);
+ }
+}
+
+// TODO: generalize!
+// Resource of the ListBox and these two conversion methods are also in
+// tpsubt and everywhere, where StarCalc functions are selectable.
+
+ScSubTotalFunc ScConsolidateDlg::LbPosToFunc( sal_Int32 nPos )
+{
+ switch ( nPos )
+ {
+ case 2: return SUBTOTAL_FUNC_AVE;
+ case 6: return SUBTOTAL_FUNC_CNT;
+ case 1: return SUBTOTAL_FUNC_CNT2;
+ case 3: return SUBTOTAL_FUNC_MAX;
+ case 4: return SUBTOTAL_FUNC_MIN;
+ case 5: return SUBTOTAL_FUNC_PROD;
+ case 7: return SUBTOTAL_FUNC_STD;
+ case 8: return SUBTOTAL_FUNC_STDP;
+ case 9: return SUBTOTAL_FUNC_VAR;
+ case 10: return SUBTOTAL_FUNC_VARP;
+ case 0:
+ default:
+ return SUBTOTAL_FUNC_SUM;
+ }
+}
+
+sal_Int32 ScConsolidateDlg::FuncToLbPos( ScSubTotalFunc eFunc )
+{
+ switch ( eFunc )
+ {
+ case SUBTOTAL_FUNC_AVE: return 2;
+ case SUBTOTAL_FUNC_CNT: return 6;
+ case SUBTOTAL_FUNC_CNT2: return 1;
+ case SUBTOTAL_FUNC_MAX: return 3;
+ case SUBTOTAL_FUNC_MIN: return 4;
+ case SUBTOTAL_FUNC_PROD: return 5;
+ case SUBTOTAL_FUNC_STD: return 7;
+ case SUBTOTAL_FUNC_STDP: return 8;
+ case SUBTOTAL_FUNC_VAR: return 9;
+ case SUBTOTAL_FUNC_VARP: return 10;
+ case SUBTOTAL_FUNC_NONE:
+ case SUBTOTAL_FUNC_SUM:
+ default:
+ return 0;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/csvcontrol.cxx b/sc/source/ui/dbgui/csvcontrol.cxx
new file mode 100644
index 000000000..409e898b4
--- /dev/null
+++ b/sc/source/ui/dbgui/csvcontrol.cxx
@@ -0,0 +1,284 @@
+/* -*- 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 <csvcontrol.hxx>
+#include <vcl/keycodes.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/settings.hxx>
+
+ScCsvLayoutData::ScCsvLayoutData() :
+ mnPosCount( 1 ),
+ mnPosOffset( 0 ),
+ mnWinWidth( 1 ),
+ mnHdrWidth( 0 ),
+ mnCharWidth( 1 ),
+ mnLineCount( 1 ),
+ mnLineOffset( 0 ),
+ mnWinHeight( 1 ),
+ mnHdrHeight( 0 ),
+ mnLineHeight( 1 ),
+ mnPosCursor( CSV_POS_INVALID ),
+ mnColCursor( 0 ),
+ mnNoRepaint( 0 ),
+ mbAppRTL( AllSettings::GetLayoutRTL() )
+{
+}
+
+ScCsvDiff ScCsvLayoutData::GetDiff( const ScCsvLayoutData& rData ) const
+{
+ ScCsvDiff nRet = ScCsvDiff::Equal;
+ if( mnPosCount != rData.mnPosCount ) nRet |= ScCsvDiff::PosCount;
+ if( mnPosOffset != rData.mnPosOffset ) nRet |= ScCsvDiff::PosOffset;
+ if( mnHdrWidth != rData.mnHdrWidth ) nRet |= ScCsvDiff::HeaderWidth;
+ if( mnCharWidth != rData.mnCharWidth ) nRet |= ScCsvDiff::CharWidth;
+ if( mnLineCount != rData.mnLineCount ) nRet |= ScCsvDiff::LineCount;
+ if( mnLineOffset != rData.mnLineOffset ) nRet |= ScCsvDiff::LineOffset;
+ if( mnHdrHeight != rData.mnHdrHeight ) nRet |= ScCsvDiff::HeaderHeight;
+ if( mnLineHeight != rData.mnLineHeight ) nRet |= ScCsvDiff::LineHeight;
+ if( mnPosCursor != rData.mnPosCursor ) nRet |= ScCsvDiff::RulerCursor;
+ if( mnColCursor != rData.mnColCursor ) nRet |= ScCsvDiff::GridCursor;
+ return nRet;
+}
+
+ScCsvControl::ScCsvControl(const ScCsvLayoutData& rData)
+ : mrData(rData)
+ , mbValidGfx(false)
+{
+}
+
+ScCsvControl::~ScCsvControl()
+{
+ if( mxAccessible.is() )
+ mxAccessible->dispose();
+ mxAccessible = nullptr; // lp#1566050: prevent cyclic reference zombies
+}
+
+// event handling -------------------------------------------------------------
+
+void ScCsvControl::GetFocus()
+{
+ weld::CustomWidgetController::GetFocus();
+ AccSendFocusEvent( true );
+}
+
+void ScCsvControl::LoseFocus()
+{
+ weld::CustomWidgetController::LoseFocus();
+ AccSendFocusEvent( false );
+}
+
+void ScCsvControl::AccSendFocusEvent( bool bFocused )
+{
+ if( mxAccessible.is() )
+ mxAccessible->SendFocusEvent( bFocused );
+}
+
+void ScCsvControl::AccSendCaretEvent()
+{
+ if( mxAccessible.is() )
+ mxAccessible->SendCaretEvent();
+}
+
+void ScCsvControl::AccSendVisibleEvent()
+{
+ if( mxAccessible.is() )
+ mxAccessible->SendVisibleEvent();
+}
+
+void ScCsvControl::AccSendSelectionEvent()
+{
+ if( mxAccessible.is() )
+ mxAccessible->SendSelectionEvent();
+}
+
+void ScCsvControl::AccSendTableUpdateEvent( sal_uInt32 nFirstColumn, sal_uInt32 nLastColumn, bool bAllRows )
+{
+ if( mxAccessible.is() )
+ mxAccessible->SendTableUpdateEvent( nFirstColumn, nLastColumn, bAllRows );
+}
+
+void ScCsvControl::AccSendInsertColumnEvent( sal_uInt32 nFirstColumn, sal_uInt32 nLastColumn )
+{
+ if( mxAccessible.is() )
+ mxAccessible->SendInsertColumnEvent( nFirstColumn, nLastColumn );
+}
+
+void ScCsvControl::AccSendRemoveColumnEvent( sal_uInt32 nFirstColumn, sal_uInt32 nLastColumn )
+{
+ if( mxAccessible.is() )
+ mxAccessible->SendRemoveColumnEvent( nFirstColumn, nLastColumn );
+}
+
+// repaint helpers ------------------------------------------------------------
+
+void ScCsvControl::Repaint( bool bInvalidate )
+{
+ if( bInvalidate )
+ InvalidateGfx();
+ if( !IsNoRepaint() )
+ Execute( CSVCMD_REPAINT );
+}
+
+void ScCsvControl::DisableRepaint()
+{
+ ++mrData.mnNoRepaint;
+}
+
+void ScCsvControl::EnableRepaint()
+{
+ OSL_ENSURE( IsNoRepaint(), "ScCsvControl::EnableRepaint - invalid call" );
+ --mrData.mnNoRepaint;
+ Repaint();
+}
+
+// command handling -----------------------------------------------------------
+
+void ScCsvControl::Execute( ScCsvCmdType eType, sal_Int32 nParam1, sal_Int32 nParam2 )
+{
+ maCmd.Set( eType, nParam1, nParam2 );
+ maCmdHdl.Call( *this );
+}
+
+// layout helpers -------------------------------------------------------------
+
+sal_Int32 ScCsvControl::GetVisPosCount() const
+{
+ return (mrData.mnWinWidth - GetHdrWidth()) / GetCharWidth();
+}
+
+sal_Int32 ScCsvControl::GetMaxPosOffset() const
+{
+ return std::max<sal_Int32>( GetPosCount() - GetVisPosCount() + 2, 0 );
+}
+
+bool ScCsvControl::IsValidSplitPos( sal_Int32 nPos ) const
+{
+ return (0 < nPos) && (nPos < GetPosCount() );
+}
+
+bool ScCsvControl::IsVisibleSplitPos( sal_Int32 nPos ) const
+{
+ return IsValidSplitPos( nPos ) && (GetFirstVisPos() <= nPos) && (nPos <= GetLastVisPos());
+}
+
+sal_Int32 ScCsvControl::GetHdrX() const
+{
+ return IsRTL() ? (mrData.mnWinWidth - GetHdrWidth()) : 0;
+}
+
+sal_Int32 ScCsvControl::GetFirstX() const
+{
+ return IsRTL() ? 0 : GetHdrWidth();
+}
+
+sal_Int32 ScCsvControl::GetLastX() const
+{
+ return mrData.mnWinWidth - (IsRTL() ? GetHdrWidth() : 0) - 1;
+}
+
+sal_Int32 ScCsvControl::GetX( sal_Int32 nPos ) const
+{
+ return GetFirstX() + (nPos - GetFirstVisPos()) * GetCharWidth();
+}
+
+sal_Int32 ScCsvControl::GetPosFromX( sal_Int32 nX ) const
+{
+ return (nX - GetFirstX() + GetCharWidth() / 2) / GetCharWidth() + GetFirstVisPos();
+}
+
+sal_Int32 ScCsvControl::GetVisLineCount() const
+{
+ return (mrData.mnWinHeight - GetHdrHeight() - 2) / GetLineHeight() + 1;
+}
+
+sal_Int32 ScCsvControl::GetLastVisLine() const
+{
+ return std::min( GetFirstVisLine() + GetVisLineCount(), GetLineCount() ) - 1;
+}
+
+sal_Int32 ScCsvControl::GetMaxLineOffset() const
+{
+ return std::max<sal_Int32>( GetLineCount() - GetVisLineCount() + 1, 0 );
+}
+
+bool ScCsvControl::IsValidLine( sal_Int32 nLine ) const
+{
+ return (0 <= nLine) && (nLine < GetLineCount());
+}
+
+bool ScCsvControl::IsVisibleLine( sal_Int32 nLine ) const
+{
+ return IsValidLine( nLine ) && (GetFirstVisLine() <= nLine) && (nLine <= GetLastVisLine());
+}
+
+sal_Int32 ScCsvControl::GetY( sal_Int32 nLine ) const
+{
+ return GetHdrHeight() + (nLine - GetFirstVisLine()) * GetLineHeight();
+}
+
+sal_Int32 ScCsvControl::GetLineFromY( sal_Int32 nY ) const
+{
+ return (nY - GetHdrHeight()) / GetLineHeight() + GetFirstVisLine();
+}
+
+// static helpers -------------------------------------------------------------
+
+void ScCsvControl::ImplInvertRect( OutputDevice& rOutDev, const tools::Rectangle& rRect )
+{
+ rOutDev.Push( vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR | vcl::PushFlags::RASTEROP );
+ rOutDev.SetLineColor( COL_BLACK );
+ rOutDev.SetFillColor( COL_BLACK );
+ rOutDev.SetRasterOp( RasterOp::Invert );
+ rOutDev.DrawRect( rRect );
+ rOutDev.Pop();
+}
+
+ScMoveMode ScCsvControl::GetHorzDirection( sal_uInt16 nCode, bool bHomeEnd )
+{
+ switch( nCode )
+ {
+ case KEY_LEFT: return MOVE_PREV;
+ case KEY_RIGHT: return MOVE_NEXT;
+ }
+ if( bHomeEnd ) switch( nCode )
+ {
+ case KEY_HOME: return MOVE_FIRST;
+ case KEY_END: return MOVE_LAST;
+ }
+ return MOVE_NONE;
+}
+
+ScMoveMode ScCsvControl::GetVertDirection( sal_uInt16 nCode, bool bHomeEnd )
+{
+ switch( nCode )
+ {
+ case KEY_UP: return MOVE_PREV;
+ case KEY_DOWN: return MOVE_NEXT;
+ case KEY_PAGEUP: return MOVE_PREVPAGE;
+ case KEY_PAGEDOWN: return MOVE_NEXTPAGE;
+ }
+ if( bHomeEnd ) switch( nCode )
+ {
+ case KEY_HOME: return MOVE_FIRST;
+ case KEY_END: return MOVE_LAST;
+ }
+ return MOVE_NONE;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/csvgrid.cxx b/sc/source/ui/dbgui/csvgrid.cxx
new file mode 100644
index 000000000..6752fcb78
--- /dev/null
+++ b/sc/source/ui/dbgui/csvgrid.cxx
@@ -0,0 +1,1418 @@
+/* -*- 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 <csvgrid.hxx>
+#include <csvtablebox.hxx>
+
+#include <algorithm>
+#include <memory>
+
+#include <svtools/colorcfg.hxx>
+#include <sal/macros.h>
+#include <tools/poly.hxx>
+#include <scmod.hxx>
+#include <asciiopt.hxx>
+#include <impex.hxx>
+#include <AccessibleCsvControl.hxx>
+
+// *** edit engine ***
+#include <editeng/eeitem.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/event.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/weldutils.hxx>
+
+#include <editeng/colritem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/langitem.hxx>
+#include <svl/itempool.hxx>
+#include <svl/itemset.hxx>
+#include <editutil.hxx>
+// *** edit engine ***
+
+namespace {
+
+struct Func_SetType
+{
+ sal_Int32 mnType;
+ explicit Func_SetType( sal_Int32 nType ) : mnType( nType ) {}
+ void operator()( ScCsvColState& rState ) const
+ { rState.mnType = mnType; }
+};
+
+struct Func_Select
+{
+ bool mbSelect;
+ explicit Func_Select( bool bSelect ) : mbSelect( bSelect ) {}
+ void operator()( ScCsvColState& rState ) const
+ { rState.Select( mbSelect ); }
+};
+
+}
+
+ScCsvGrid::ScCsvGrid(const ScCsvLayoutData& rData, std::unique_ptr<weld::Menu> xPopup, ScCsvTableBox* pTableBox)
+ : ScCsvControl(rData)
+ , mpTableBox(pTableBox)
+ , mpBackgrDev( VclPtr<VirtualDevice>::Create() )
+ , mpGridDev( VclPtr<VirtualDevice>::Create() )
+ , mxPopup(std::move(xPopup))
+ , mpColorConfig( nullptr )
+ , mpEditEngine( new ScEditEngineDefaulter( EditEngine::CreatePool().get(), true ) )
+ , maColStates( 1 )
+ , maTypeNames( 1 )
+ , mnFirstImpLine( 0 )
+ , mnRecentSelCol( CSV_COLUMN_INVALID )
+ , mnMTCurrCol( SAL_MAX_UINT32 )
+ , mbTracking( false )
+ , mbMTSelecting( false )
+{
+ mpEditEngine->SetRefDevice( mpBackgrDev.get() );
+ mpEditEngine->SetRefMapMode( MapMode( MapUnit::MapPixel ) );
+ maEdEngSize = mpEditEngine->GetPaperSize();
+}
+
+void ScCsvGrid::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ OutputDevice& rRefDevice = pDrawingArea->get_ref_device();
+ maHeaderFont = Application::GetSettings().GetStyleSettings().GetAppFont();
+
+ // expand the point size of the desired font to the equivalent pixel size
+ weld::SetPointFont(rRefDevice, maHeaderFont);
+ maHeaderFont = rRefDevice.GetFont();
+
+ // Because this is an always LeftToRight layout widget the initial size of
+ // this widget needs to be smaller than the size of the parent scrolling
+ // window (ScCsvTableBox ctor) because in RTL mode the alignment is against
+ // the right edge of the parent, and if larger than the scrolling window
+ // the left edge will be lost. If this widget is smaller than the scrolling
+ // window it is stretched to fit the parent and the problem doesn't arise.
+ Size aInitialSize(10, 10);
+ ScCsvControl::SetDrawingArea(pDrawingArea);
+ pDrawingArea->set_size_request(aInitialSize.Width(), aInitialSize.Height());
+ SetOutputSizePixel(aInitialSize);
+
+ EnableRTL( false ); // RTL
+
+ InitFonts();
+ ImplClearSplits();
+}
+
+ScCsvGrid::~ScCsvGrid()
+{
+ OSL_ENSURE(mpColorConfig, "the object hasn't been initialized properly");
+ if (mpColorConfig)
+ mpColorConfig->RemoveListener(this);
+ mpBackgrDev.disposeAndClear();
+ mpGridDev.disposeAndClear();
+}
+
+void
+ScCsvGrid::Init()
+{
+ OSL_PRECOND(!mpColorConfig, "the object has already been initialized");
+ mpColorConfig = &SC_MOD()->GetColorConfig();
+ InitColors();
+ mpColorConfig->AddListener(this);
+}
+
+// common grid handling -------------------------------------------------------
+
+void ScCsvGrid::UpdateLayoutData()
+{
+ DisableRepaint();
+ OutputDevice& rRefDevice = GetDrawingArea()->get_ref_device();
+ rRefDevice.SetFont(maMonoFont);
+ Execute(CSVCMD_SETCHARWIDTH, rRefDevice.GetTextWidth(OUString('X')));
+ Execute(CSVCMD_SETLINEHEIGHT, rRefDevice.GetTextHeight() + 1);
+ rRefDevice.SetFont(maHeaderFont);
+ Execute(CSVCMD_SETHDRHEIGHT, rRefDevice.GetTextHeight() + 1);
+ UpdateOffsetX();
+ EnableRepaint();
+}
+
+void ScCsvGrid::UpdateOffsetX()
+{
+ sal_Int32 nLastLine = GetLastVisLine() + 1;
+ sal_Int32 nDigits = 2;
+ for (;;)
+ {
+ nLastLine /= 10;
+ if (!nLastLine)
+ break;
+ ++nDigits;
+ }
+ nDigits = std::max( nDigits, sal_Int32( 3 ) );
+ Execute(CSVCMD_SETHDRWIDTH, GetDrawingArea()->get_approximate_digit_width() * nDigits);
+}
+
+void ScCsvGrid::ApplyLayout( const ScCsvLayoutData& rOldData )
+{
+ ScCsvDiff nDiff = GetLayoutData().GetDiff( rOldData );
+ if( nDiff == ScCsvDiff::Equal ) return;
+
+ DisableRepaint();
+
+ if( nDiff & ScCsvDiff::RulerCursor )
+ {
+ ImplInvertCursor( rOldData.mnPosCursor );
+ ImplInvertCursor( GetRulerCursorPos() );
+ }
+
+ if( nDiff & ScCsvDiff::PosCount )
+ {
+ if( GetPosCount() < rOldData.mnPosCount )
+ {
+ SelectAll( false );
+ maSplits.RemoveRange( GetPosCount(), rOldData.mnPosCount );
+ }
+ else
+ maSplits.Remove( rOldData.mnPosCount );
+ maSplits.Insert( GetPosCount() );
+ maColStates.resize( maSplits.Count() - 1 );
+ }
+
+ if( nDiff & ScCsvDiff::LineOffset )
+ {
+ Execute( CSVCMD_UPDATECELLTEXTS );
+ UpdateOffsetX();
+ }
+
+ ScCsvDiff nHVDiff = nDiff & (ScCsvDiff::HorizontalMask | ScCsvDiff::VerticalMask);
+ if( nHVDiff == ScCsvDiff::PosOffset )
+ ImplDrawHorzScrolled( rOldData.mnPosOffset );
+ else if( nHVDiff != ScCsvDiff::Equal )
+ InvalidateGfx();
+
+ EnableRepaint();
+
+ if( nDiff & (ScCsvDiff::PosOffset | ScCsvDiff::LineOffset) )
+ AccSendVisibleEvent();
+}
+
+void ScCsvGrid::SetFirstImportedLine( sal_Int32 nLine )
+{
+ ImplDrawFirstLineSep( false );
+ mnFirstImpLine = nLine;
+ ImplDrawFirstLineSep( true );
+ ImplDrawGridDev();
+ Repaint();
+}
+
+sal_Int32 ScCsvGrid::GetNoScrollCol( sal_Int32 nPos ) const
+{
+ sal_Int32 nNewPos = nPos;
+ if( nNewPos != CSV_POS_INVALID )
+ {
+ if( nNewPos < GetFirstVisPos() + CSV_SCROLL_DIST )
+ {
+ sal_Int32 nScroll = (GetFirstVisPos() > 0) ? CSV_SCROLL_DIST : 0;
+ nNewPos = GetFirstVisPos() + nScroll;
+ }
+ else if( nNewPos > GetLastVisPos() - CSV_SCROLL_DIST - 1 )
+ {
+ sal_Int32 nScroll = (GetFirstVisPos() < GetMaxPosOffset()) ? CSV_SCROLL_DIST : 0;
+ nNewPos = GetLastVisPos() - nScroll - 1;
+ }
+ }
+ return nNewPos;
+}
+
+void ScCsvGrid::InitColors()
+{
+ OSL_PRECOND(mpColorConfig, "the object hasn't been initialized properly");
+ if ( !mpColorConfig )
+ return;
+ maBackColor = mpColorConfig->GetColorValue( ::svtools::DOCCOLOR ).nColor;
+ maGridColor = mpColorConfig->GetColorValue( ::svtools::CALCGRID ).nColor;
+ maGridPBColor = mpColorConfig->GetColorValue( ::svtools::CALCPAGEBREAK ).nColor;
+ maAppBackColor = mpColorConfig->GetColorValue( ::svtools::APPBACKGROUND ).nColor;
+ maTextColor = mpColorConfig->GetColorValue( ::svtools::FONTCOLOR ).nColor;
+
+ const StyleSettings& rSett = Application::GetSettings().GetStyleSettings();
+ maHeaderBackColor = rSett.GetFaceColor();
+ maHeaderGridColor = rSett.GetDarkShadowColor();
+ maHeaderTextColor = rSett.GetButtonTextColor();
+ maSelectColor = rSett.GetActiveColor();
+
+ InvalidateGfx();
+}
+
+void ScCsvGrid::InitFonts()
+{
+ maMonoFont = OutputDevice::GetDefaultFont( DefaultFontType::FIXED, LANGUAGE_ENGLISH_US, GetDefaultFontFlags::NONE );
+ maMonoFont.SetFontSize( Size( maMonoFont.GetFontSize().Width(), maHeaderFont.GetFontSize().Height() ) );
+
+ /* *** Set edit engine defaults ***
+ maMonoFont for Latin script, smaller default font for Asian and Complex script. */
+
+ // get default fonts
+ SvxFontItem aLatinItem( EE_CHAR_FONTINFO );
+ SvxFontItem aAsianItem( EE_CHAR_FONTINFO_CJK );
+ SvxFontItem aComplexItem( EE_CHAR_FONTINFO_CTL );
+ ::GetDefaultFonts( aLatinItem, aAsianItem, aComplexItem );
+
+ // create item set for defaults
+ SfxItemSet aDefSet( mpEditEngine->GetEmptyItemSet() );
+ EditEngine::SetFontInfoInItemSet( aDefSet, maMonoFont );
+ aDefSet.Put( aAsianItem );
+ aDefSet.Put( aComplexItem );
+
+ // set Asian/Complex font size to height of character in Latin font
+ sal_uLong nFontHt = static_cast< sal_uLong >( maMonoFont.GetFontSize().Height() );
+ aDefSet.Put( SvxFontHeightItem( nFontHt, 100, EE_CHAR_FONTHEIGHT_CJK ) );
+ aDefSet.Put( SvxFontHeightItem( nFontHt, 100, EE_CHAR_FONTHEIGHT_CTL ) );
+
+ // copy other items from default font
+ const SfxPoolItem& rWeightItem = aDefSet.Get( EE_CHAR_WEIGHT );
+ std::unique_ptr<SfxPoolItem> pNewItem(rWeightItem.Clone());
+ pNewItem->SetWhich(EE_CHAR_WEIGHT_CJK);
+ aDefSet.Put( *pNewItem );
+ pNewItem->SetWhich(EE_CHAR_WEIGHT_CTL);
+ aDefSet.Put( *pNewItem );
+ const SfxPoolItem& rItalicItem = aDefSet.Get( EE_CHAR_ITALIC );
+ pNewItem.reset(rItalicItem.Clone());
+ pNewItem->SetWhich(EE_CHAR_ITALIC_CJK);
+ aDefSet.Put( *pNewItem );
+ pNewItem->SetWhich(EE_CHAR_ITALIC_CTL);
+ aDefSet.Put( *pNewItem );
+ const SfxPoolItem& rLangItem = aDefSet.Get( EE_CHAR_LANGUAGE );
+ pNewItem.reset(rLangItem.Clone());
+ pNewItem->SetWhich(EE_CHAR_LANGUAGE_CJK);
+ aDefSet.Put( *pNewItem );
+ pNewItem->SetWhich(EE_CHAR_LANGUAGE_CTL);
+ aDefSet.Put( *pNewItem );
+
+ mpEditEngine->SetDefaults( aDefSet );
+ InvalidateGfx();
+}
+
+void ScCsvGrid::InitSizeData()
+{
+ maWinSize = GetOutputSizePixel();
+ mpBackgrDev->SetOutputSizePixel( maWinSize );
+ mpGridDev->SetOutputSizePixel( maWinSize );
+ InvalidateGfx();
+}
+
+// split handling -------------------------------------------------------------
+
+void ScCsvGrid::InsertSplit( sal_Int32 nPos )
+{
+ if( ImplInsertSplit( nPos ) )
+ {
+ DisableRepaint();
+ Execute( CSVCMD_EXPORTCOLUMNTYPE );
+ Execute( CSVCMD_UPDATECELLTEXTS );
+ sal_uInt32 nColIx = GetColumnFromPos( nPos );
+ ImplDrawColumn( nColIx - 1 );
+ ImplDrawColumn( nColIx );
+ ValidateGfx(); // performance: do not redraw all columns
+ EnableRepaint();
+ }
+}
+
+void ScCsvGrid::RemoveSplit( sal_Int32 nPos )
+{
+ if( ImplRemoveSplit( nPos ) )
+ {
+ DisableRepaint();
+ Execute( CSVCMD_EXPORTCOLUMNTYPE );
+ Execute( CSVCMD_UPDATECELLTEXTS );
+ ImplDrawColumn( GetColumnFromPos( nPos ) );
+ ValidateGfx(); // performance: do not redraw all columns
+ EnableRepaint();
+ }
+}
+
+void ScCsvGrid::MoveSplit( sal_Int32 nPos, sal_Int32 nNewPos )
+{
+ sal_uInt32 nColIx = GetColumnFromPos( nPos );
+ if( nColIx == CSV_COLUMN_INVALID )
+ return;
+
+ DisableRepaint();
+ if( (GetColumnPos( nColIx - 1 ) < nNewPos) && (nNewPos < GetColumnPos( nColIx + 1 )) )
+ {
+ // move a split in the range between 2 others -> keep selection state of both columns
+ maSplits.Remove( nPos );
+ maSplits.Insert( nNewPos );
+ Execute( CSVCMD_UPDATECELLTEXTS );
+ ImplDrawColumn( nColIx - 1 );
+ ImplDrawColumn( nColIx );
+ ValidateGfx(); // performance: do not redraw all columns
+ AccSendTableUpdateEvent( nColIx - 1, nColIx );
+ }
+ else
+ {
+ ImplRemoveSplit( nPos );
+ ImplInsertSplit( nNewPos );
+ Execute( CSVCMD_EXPORTCOLUMNTYPE );
+ Execute( CSVCMD_UPDATECELLTEXTS );
+ }
+ EnableRepaint();
+}
+
+void ScCsvGrid::RemoveAllSplits()
+{
+ DisableRepaint();
+ ImplClearSplits();
+ Execute( CSVCMD_EXPORTCOLUMNTYPE );
+ Execute( CSVCMD_UPDATECELLTEXTS );
+ EnableRepaint();
+}
+
+void ScCsvGrid::SetSplits( const ScCsvSplits& rSplits )
+{
+ DisableRepaint();
+ ImplClearSplits();
+ sal_uInt32 nCount = rSplits.Count();
+ for( sal_uInt32 nIx = 0; nIx < nCount; ++nIx )
+ maSplits.Insert( rSplits[ nIx ] );
+ maColStates.clear();
+ maColStates.resize( maSplits.Count() - 1 );
+ Execute( CSVCMD_EXPORTCOLUMNTYPE );
+ Execute( CSVCMD_UPDATECELLTEXTS );
+ EnableRepaint();
+}
+
+bool ScCsvGrid::ImplInsertSplit( sal_Int32 nPos )
+{
+ sal_uInt32 nColIx = GetColumnFromPos( nPos );
+ bool bRet = (nColIx < GetColumnCount()) && maSplits.Insert( nPos );
+ if( bRet )
+ {
+ ScCsvColState aState( GetColumnType( nColIx ) );
+ aState.Select( IsSelected( nColIx ) && IsSelected( nColIx + 1 ) );
+ maColStates.insert( maColStates.begin() + nColIx + 1, aState );
+ AccSendInsertColumnEvent( nColIx + 1, nColIx + 1 );
+ AccSendTableUpdateEvent( nColIx, nColIx );
+ }
+ return bRet;
+}
+
+bool ScCsvGrid::ImplRemoveSplit( sal_Int32 nPos )
+{
+ bool bRet = maSplits.Remove( nPos );
+ if( bRet )
+ {
+ sal_uInt32 nColIx = GetColumnFromPos( nPos );
+ bool bSel = IsSelected( nColIx ) || IsSelected( nColIx + 1 );
+ maColStates.erase( maColStates.begin() + nColIx + 1 );
+ maColStates[ nColIx ].Select( bSel );
+ AccSendRemoveColumnEvent( nColIx + 1, nColIx + 1 );
+ AccSendTableUpdateEvent( nColIx, nColIx );
+ }
+ return bRet;
+}
+
+void ScCsvGrid::ImplClearSplits()
+{
+ sal_uInt32 nColumns = GetColumnCount();
+ maSplits.Clear();
+ maSplits.Insert( 0 );
+ maSplits.Insert( GetPosCount() );
+ maColStates.resize( 1 );
+ InvalidateGfx();
+ AccSendRemoveColumnEvent( 1, nColumns - 1 );
+}
+
+// columns/column types -------------------------------------------------------
+
+sal_uInt32 ScCsvGrid::GetFirstVisColumn() const
+{
+ return GetColumnFromPos( GetFirstVisPos() );
+}
+
+sal_uInt32 ScCsvGrid::GetLastVisColumn() const
+{
+ return GetColumnFromPos( std::min( GetLastVisPos(), GetPosCount() ) - 1 );
+}
+
+bool ScCsvGrid::IsValidColumn( sal_uInt32 nColIndex ) const
+{
+ return nColIndex < GetColumnCount();
+}
+
+bool ScCsvGrid::IsVisibleColumn( sal_uInt32 nColIndex ) const
+{
+ return IsValidColumn( nColIndex ) &&
+ (GetColumnPos( nColIndex ) < GetLastVisPos()) &&
+ (GetFirstVisPos() < GetColumnPos( nColIndex + 1 ));
+}
+
+sal_Int32 ScCsvGrid::GetColumnX( sal_uInt32 nColIndex ) const
+{
+ return GetX( GetColumnPos( nColIndex ) );
+}
+
+sal_uInt32 ScCsvGrid::GetColumnFromX( sal_Int32 nX ) const
+{
+ sal_Int32 nPos = (nX - GetFirstX()) / GetCharWidth() + GetFirstVisPos();
+ return ((GetFirstVisPos() <= nPos) && (nPos <= GetLastVisPos())) ?
+ GetColumnFromPos( nPos ) : CSV_COLUMN_INVALID;
+}
+
+sal_uInt32 ScCsvGrid::GetColumnFromPos( sal_Int32 nPos ) const
+{
+ return maSplits.UpperBound( nPos );
+}
+
+sal_Int32 ScCsvGrid::GetColumnWidth( sal_uInt32 nColIndex ) const
+{
+ return IsValidColumn( nColIndex ) ? (GetColumnPos( nColIndex + 1 ) - GetColumnPos( nColIndex )) : 0;
+}
+
+void ScCsvGrid::SetColumnStates( ScCsvColStateVec&& rStates )
+{
+ maColStates = std::move(rStates);
+ maColStates.resize( maSplits.Count() - 1 );
+ Execute( CSVCMD_EXPORTCOLUMNTYPE );
+ AccSendTableUpdateEvent( 0, GetColumnCount(), false );
+ AccSendSelectionEvent();
+}
+
+sal_Int32 ScCsvGrid::GetColumnType( sal_uInt32 nColIndex ) const
+{
+ return IsValidColumn( nColIndex ) ? maColStates[ nColIndex ].mnType : CSV_TYPE_NOSELECTION;
+}
+
+void ScCsvGrid::SetColumnType( sal_uInt32 nColIndex, sal_Int32 nColType )
+{
+ if( IsValidColumn( nColIndex ) )
+ {
+ maColStates[ nColIndex ].mnType = nColType;
+ AccSendTableUpdateEvent( nColIndex, nColIndex, false );
+ }
+}
+
+sal_Int32 ScCsvGrid::GetSelColumnType() const
+{
+ sal_uInt32 nColIx = GetFirstSelected();
+ if( nColIx == CSV_COLUMN_INVALID )
+ return CSV_TYPE_NOSELECTION;
+
+ sal_Int32 nType = GetColumnType( nColIx );
+ while( (nColIx != CSV_COLUMN_INVALID) && (nType != CSV_TYPE_MULTI) )
+ {
+ if( nType != GetColumnType( nColIx ) )
+ nType = CSV_TYPE_MULTI;
+ nColIx = GetNextSelected( nColIx );
+ }
+ return nType;
+}
+
+void ScCsvGrid::SetSelColumnType( sal_Int32 nType )
+{
+ if( (nType != CSV_TYPE_MULTI) && (nType != CSV_TYPE_NOSELECTION) )
+ {
+ for( sal_uInt32 nColIx = GetFirstSelected(); nColIx != CSV_COLUMN_INVALID; nColIx = GetNextSelected( nColIx ) )
+ SetColumnType( nColIx, nType );
+ Repaint( true );
+ Execute( CSVCMD_EXPORTCOLUMNTYPE );
+ }
+}
+
+void ScCsvGrid::SetTypeNames( std::vector<OUString>&& rTypeNames )
+{
+ OSL_ENSURE( !rTypeNames.empty(), "ScCsvGrid::SetTypeNames - vector is empty" );
+ maTypeNames = std::move(rTypeNames);
+ Repaint( true );
+
+ mxPopup->clear();
+ sal_uInt32 nCount = maTypeNames.size();
+ for (sal_uInt32 nIx = 0; nIx < nCount; ++nIx)
+ mxPopup->append(OUString::number(nIx), maTypeNames[nIx]);
+
+ ::std::for_each( maColStates.begin(), maColStates.end(), Func_SetType( CSV_TYPE_DEFAULT ) );
+}
+
+OUString ScCsvGrid::GetColumnTypeName( sal_uInt32 nColIndex ) const
+{
+ sal_uInt32 nTypeIx = static_cast< sal_uInt32 >( GetColumnType( nColIndex ) );
+ return (nTypeIx < maTypeNames.size()) ? maTypeNames[ nTypeIx ] : OUString();
+}
+
+static sal_uInt8 lcl_GetExtColumnType( sal_Int32 nIntType )
+{
+ static const sal_uInt8 pExtTypes[] =
+ { SC_COL_STANDARD, SC_COL_TEXT, SC_COL_DMY, SC_COL_MDY, SC_COL_YMD, SC_COL_ENGLISH, SC_COL_SKIP };
+ static const sal_Int32 nExtTypeCount = SAL_N_ELEMENTS(pExtTypes);
+ return pExtTypes[ ((0 <= nIntType) && (nIntType < nExtTypeCount)) ? nIntType : 0 ];
+}
+
+void ScCsvGrid::FillColumnDataSep( ScAsciiOptions& rOptions ) const
+{
+ sal_uInt32 nCount = GetColumnCount();
+ ScCsvExpDataVec aDataVec;
+
+ for( sal_uInt32 nColIx = 0; nColIx < nCount; ++nColIx )
+ {
+ if( GetColumnType( nColIx ) != CSV_TYPE_DEFAULT )
+ // 1-based column index
+ aDataVec.emplace_back(
+ static_cast< sal_Int32 >( nColIx + 1 ),
+ lcl_GetExtColumnType( GetColumnType( nColIx ) ) );
+ }
+ rOptions.SetColumnInfo( aDataVec );
+}
+
+void ScCsvGrid::FillColumnDataFix( ScAsciiOptions& rOptions ) const
+{
+ sal_uInt32 nCount = std::min( GetColumnCount(), static_cast<sal_uInt32>(MAXCOLCOUNT) );
+ ScCsvExpDataVec aDataVec( nCount + 1 );
+
+ for( sal_uInt32 nColIx = 0; nColIx < nCount; ++nColIx )
+ {
+ ScCsvExpData& rData = aDataVec[ nColIx ];
+ rData.mnIndex = GetColumnPos( nColIx );
+ rData.mnType = lcl_GetExtColumnType( GetColumnType( nColIx ) );
+ }
+ aDataVec[ nCount ].mnIndex = SAL_MAX_INT32;
+ aDataVec[ nCount ].mnType = SC_COL_SKIP;
+ rOptions.SetColumnInfo( aDataVec );
+}
+
+void ScCsvGrid::ScrollVertRel( ScMoveMode eDir )
+{
+ sal_Int32 nLine = GetFirstVisLine();
+ switch( eDir )
+ {
+ case MOVE_PREV: --nLine; break;
+ case MOVE_NEXT: ++nLine; break;
+ case MOVE_FIRST: nLine = 0; break;
+ case MOVE_LAST: nLine = GetMaxLineOffset(); break;
+ case MOVE_PREVPAGE: nLine -= GetVisLineCount() - 2; break;
+ case MOVE_NEXTPAGE: nLine += GetVisLineCount() - 2; break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ Execute( CSVCMD_SETLINEOFFSET, nLine );
+}
+
+void ScCsvGrid::ExecutePopup( const Point& rPos )
+{
+ OString sItemId = mxPopup->popup_at_rect(GetDrawingArea(), tools::Rectangle(rPos, Size(1, 1)));
+ if (!sItemId.isEmpty()) // empty = cancelled
+ Execute(CSVCMD_SETCOLUMNTYPE, sItemId.toInt32());
+}
+
+// selection handling ---------------------------------------------------------
+
+bool ScCsvGrid::IsSelected( sal_uInt32 nColIndex ) const
+{
+ return IsValidColumn( nColIndex ) && maColStates[ nColIndex ].IsSelected();
+}
+
+sal_uInt32 ScCsvGrid::GetFirstSelected() const
+{
+ return IsSelected( 0 ) ? 0 : GetNextSelected( 0 );
+}
+
+sal_uInt32 ScCsvGrid::GetNextSelected( sal_uInt32 nFromIndex ) const
+{
+ sal_uInt32 nColCount = GetColumnCount();
+ for( sal_uInt32 nColIx = nFromIndex + 1; nColIx < nColCount; ++nColIx )
+ if( IsSelected( nColIx ) )
+ return nColIx;
+ return CSV_COLUMN_INVALID;
+}
+
+void ScCsvGrid::Select( sal_uInt32 nColIndex, bool bSelect )
+{
+ if( IsValidColumn( nColIndex ) )
+ {
+ maColStates[ nColIndex ].Select( bSelect );
+ ImplDrawColumnSelection( nColIndex );
+ Repaint();
+ Execute( CSVCMD_EXPORTCOLUMNTYPE );
+ if( bSelect )
+ mnRecentSelCol = nColIndex;
+ AccSendSelectionEvent();
+ }
+}
+
+void ScCsvGrid::ToggleSelect( sal_uInt32 nColIndex )
+{
+ Select( nColIndex, !IsSelected( nColIndex ) );
+}
+
+void ScCsvGrid::SelectRange( sal_uInt32 nColIndex1, sal_uInt32 nColIndex2, bool bSelect )
+{
+ if( nColIndex1 == CSV_COLUMN_INVALID )
+ Select( nColIndex2 );
+ else if( nColIndex2 == CSV_COLUMN_INVALID )
+ Select( nColIndex1 );
+ else if( nColIndex1 > nColIndex2 )
+ {
+ SelectRange( nColIndex2, nColIndex1, bSelect );
+ if( bSelect )
+ mnRecentSelCol = nColIndex1;
+ }
+ else if( IsValidColumn( nColIndex1 ) && IsValidColumn( nColIndex2 ) )
+ {
+ for( sal_uInt32 nColIx = nColIndex1; nColIx <= nColIndex2; ++nColIx )
+ {
+ maColStates[ nColIx ].Select( bSelect );
+ ImplDrawColumnSelection( nColIx );
+ }
+ Repaint();
+ Execute( CSVCMD_EXPORTCOLUMNTYPE );
+ if( bSelect )
+ mnRecentSelCol = nColIndex1;
+ AccSendSelectionEvent();
+ }
+}
+
+void ScCsvGrid::SelectAll( bool bSelect )
+{
+ SelectRange( 0, GetColumnCount() - 1, bSelect );
+}
+
+void ScCsvGrid::MoveCursor( sal_uInt32 nColIndex )
+{
+ DisableRepaint();
+ if( IsValidColumn( nColIndex ) )
+ {
+ sal_Int32 nPosBeg = GetColumnPos( nColIndex );
+ sal_Int32 nPosEnd = GetColumnPos( nColIndex + 1 );
+ sal_Int32 nMinPos = std::max( nPosBeg - CSV_SCROLL_DIST, sal_Int32( 0 ) );
+ sal_Int32 nMaxPos = std::min( nPosEnd - GetVisPosCount() + CSV_SCROLL_DIST + sal_Int32( 1 ), nMinPos );
+ if( nPosBeg - CSV_SCROLL_DIST + 1 <= GetFirstVisPos() )
+ Execute( CSVCMD_SETPOSOFFSET, nMinPos );
+ else if( nPosEnd + CSV_SCROLL_DIST >= GetLastVisPos() )
+ Execute( CSVCMD_SETPOSOFFSET, nMaxPos );
+ }
+ Execute( CSVCMD_MOVEGRIDCURSOR, GetColumnPos( nColIndex ) );
+ EnableRepaint();
+}
+
+void ScCsvGrid::MoveCursorRel( ScMoveMode eDir )
+{
+ if( GetFocusColumn() == CSV_COLUMN_INVALID )
+ return;
+
+ switch( eDir )
+ {
+ case MOVE_FIRST:
+ MoveCursor( 0 );
+ break;
+ case MOVE_LAST:
+ MoveCursor( GetColumnCount() - 1 );
+ break;
+ case MOVE_PREV:
+ if( GetFocusColumn() > 0 )
+ MoveCursor( GetFocusColumn() - 1 );
+ break;
+ case MOVE_NEXT:
+ if( GetFocusColumn() < GetColumnCount() - 1 )
+ MoveCursor( GetFocusColumn() + 1 );
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+}
+
+void ScCsvGrid::ImplClearSelection()
+{
+ ::std::for_each( maColStates.begin(), maColStates.end(), Func_Select( false ) );
+ ImplDrawGridDev();
+}
+
+void ScCsvGrid::DoSelectAction( sal_uInt32 nColIndex, sal_uInt16 nModifier )
+{
+ if( !(nModifier & KEY_MOD1) )
+ ImplClearSelection();
+ if( nModifier & KEY_SHIFT ) // SHIFT always expands
+ SelectRange( mnRecentSelCol, nColIndex );
+ else if( !(nModifier & KEY_MOD1) ) // no SHIFT/CTRL always selects 1 column
+ Select( nColIndex );
+ else if( mbTracking ) // CTRL in tracking does not toggle
+ Select( nColIndex, mbMTSelecting );
+ else // CTRL only toggles
+ ToggleSelect( nColIndex );
+ Execute( CSVCMD_MOVEGRIDCURSOR, GetColumnPos( nColIndex ) );
+}
+
+// cell contents --------------------------------------------------------------
+
+void ScCsvGrid::ImplSetTextLineSep(
+ sal_Int32 nLine, const OUString& rTextLine,
+ const OUString& rSepChars, sal_Unicode cTextSep, bool bMergeSep, bool bRemoveSpace )
+{
+ if( nLine < GetFirstVisLine() ) return;
+
+ sal_uInt32 nLineIx = nLine - GetFirstVisLine();
+ while( maTexts.size() <= nLineIx )
+ maTexts.emplace_back( );
+ std::vector<OUString>& rStrVec = maTexts[ nLineIx ];
+ rStrVec.clear();
+
+ // scan for separators
+ OUString aCellText;
+ const sal_Unicode* pSepChars = rSepChars.getStr();
+ const sal_Unicode* pChar = rTextLine.getStr();
+ sal_uInt32 nColIx = 0;
+
+ while( *pChar && (nColIx < sal::static_int_cast<sal_uInt32>(CSV_MAXCOLCOUNT)) )
+ {
+ // scan for next cell text
+ bool bIsQuoted = false;
+ bool bOverflowCell = false;
+ pChar = ScImportExport::ScanNextFieldFromString( pChar, aCellText,
+ cTextSep, pSepChars, bMergeSep, bIsQuoted, bOverflowCell, bRemoveSpace );
+ /* TODO: signal overflow somewhere in UI */
+
+ // update column width
+ sal_Int32 nWidth = std::max( CSV_MINCOLWIDTH, ScImportExport::CountVisualWidth( aCellText ) + 1 );
+ if( IsValidColumn( nColIx ) )
+ {
+ // expand existing column
+ sal_Int32 nDiff = nWidth - GetColumnWidth( nColIx );
+ if( nDiff > 0 )
+ {
+ Execute( CSVCMD_SETPOSCOUNT, GetPosCount() + nDiff );
+ for( sal_uInt32 nSplitIx = GetColumnCount() - 1; nSplitIx > nColIx; --nSplitIx )
+ {
+ sal_Int32 nPos = maSplits[ nSplitIx ];
+ maSplits.Remove( nPos );
+ maSplits.Insert( nPos + nDiff );
+ }
+ }
+ }
+ else
+ {
+ // append new column
+ sal_Int32 nLastPos = GetPosCount();
+ Execute( CSVCMD_SETPOSCOUNT, nLastPos + nWidth );
+ ImplInsertSplit( nLastPos );
+ }
+
+ if( aCellText.getLength() <= CSV_MAXSTRLEN )
+ rStrVec.push_back( aCellText );
+ else
+ rStrVec.push_back( aCellText.copy( 0, CSV_MAXSTRLEN ) );
+ ++nColIx;
+ }
+ InvalidateGfx();
+}
+
+void ScCsvGrid::ImplSetTextLineFix( sal_Int32 nLine, const OUString& rTextLine )
+{
+ if( nLine < GetFirstVisLine() ) return;
+
+ sal_Int32 nWidth = ScImportExport::CountVisualWidth( rTextLine );
+ if( nWidth > GetPosCount() )
+ Execute( CSVCMD_SETPOSCOUNT, nWidth );
+
+ sal_uInt32 nLineIx = nLine - GetFirstVisLine();
+ while( maTexts.size() <= nLineIx )
+ maTexts.emplace_back( );
+
+ std::vector<OUString>& rStrVec = maTexts[ nLineIx ];
+ rStrVec.clear();
+ sal_uInt32 nColCount = GetColumnCount();
+ sal_Int32 nStrLen = rTextLine.getLength();
+ sal_Int32 nStrIx = 0;
+ for( sal_uInt32 nColIx = 0; (nColIx < nColCount) && (nStrIx < nStrLen); ++nColIx )
+ {
+ sal_Int32 nColWidth = GetColumnWidth( nColIx );
+ sal_Int32 nLastIx = nStrIx;
+ ScImportExport::CountVisualWidth( rTextLine, nLastIx, nColWidth );
+ sal_Int32 nLen = std::min( CSV_MAXSTRLEN, nLastIx - nStrIx );
+ rStrVec.push_back( rTextLine.copy( nStrIx, nLen ) );
+ nStrIx = nStrIx + nLen;
+ }
+ InvalidateGfx();
+}
+
+OUString ScCsvGrid::GetCellText( sal_uInt32 nColIndex, sal_Int32 nLine ) const
+{
+ if( nLine < GetFirstVisLine() ) return OUString();
+
+ sal_uInt32 nLineIx = nLine - GetFirstVisLine();
+ if( nLineIx >= maTexts.size() ) return OUString();
+
+ const std::vector<OUString>& rStrVec = maTexts[ nLineIx ];
+ if( nColIndex >= rStrVec.size() ) return OUString();
+
+ return rStrVec[ nColIndex ];
+}
+
+// event handling -------------------------------------------------------------
+
+void ScCsvGrid::Resize()
+{
+ mpTableBox->InitControls();
+
+ ScCsvControl::Resize();
+ InitSizeData();
+ Execute( CSVCMD_UPDATECELLTEXTS );
+}
+
+void ScCsvGrid::GetFocus()
+{
+ ScCsvControl::GetFocus();
+ Execute( CSVCMD_MOVEGRIDCURSOR, GetNoScrollCol( GetGridCursorPos() ) );
+ Repaint();
+}
+
+void ScCsvGrid::LoseFocus()
+{
+ ScCsvControl::LoseFocus();
+ Repaint();
+}
+
+bool ScCsvGrid::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ DisableRepaint();
+ if( !HasFocus() )
+ GrabFocus();
+
+ Point aPos( rMEvt.GetPosPixel() );
+ sal_uInt32 nColIx = GetColumnFromX( aPos.X() );
+
+ if( rMEvt.IsLeft() )
+ {
+ if( (GetFirstX() > aPos.X()) || (aPos.X() > GetLastX()) ) // in header column
+ {
+ if( aPos.Y() <= GetHdrHeight() )
+ SelectAll();
+ }
+ else if( IsValidColumn( nColIx ) )
+ {
+ DoSelectAction( nColIx, rMEvt.GetModifier() );
+ mnMTCurrCol = nColIx;
+ mbMTSelecting = IsSelected( nColIx );
+ mbTracking = true;
+ }
+ }
+ EnableRepaint();
+ return true;
+}
+
+bool ScCsvGrid::MouseButtonUp( const MouseEvent& )
+{
+ mbTracking = false;
+ return true;
+}
+
+bool ScCsvGrid::MouseMove( const MouseEvent& rMEvt )
+{
+ if (!mbTracking)
+ return true;
+
+ DisableRepaint();
+
+ sal_Int32 nPos = (rMEvt.GetPosPixel().X() - GetFirstX()) / GetCharWidth() + GetFirstVisPos();
+ // on mouse tracking: keep position valid
+ nPos = std::clamp( nPos, sal_Int32(0), GetPosCount() - 1 );
+ Execute( CSVCMD_MAKEPOSVISIBLE, nPos );
+
+ sal_uInt32 nColIx = GetColumnFromPos( nPos );
+ if( mnMTCurrCol != nColIx )
+ {
+ DoSelectAction( nColIx, rMEvt.GetModifier() );
+ mnMTCurrCol = nColIx;
+ }
+ EnableRepaint();
+
+ return true;
+}
+
+bool ScCsvGrid::KeyInput( const KeyEvent& rKEvt )
+{
+ const vcl::KeyCode& rKCode = rKEvt.GetKeyCode();
+ sal_uInt16 nCode = rKCode.GetCode();
+ bool bShift = rKCode.IsShift();
+ bool bMod1 = rKCode.IsMod1();
+
+ if( !rKCode.IsMod2() )
+ {
+ ScMoveMode eHDir = GetHorzDirection( nCode, !bMod1 );
+ ScMoveMode eVDir = GetVertDirection( nCode, bMod1 );
+
+ if( eHDir != MOVE_NONE )
+ {
+ DisableRepaint();
+ MoveCursorRel( eHDir );
+ if( !bMod1 )
+ ImplClearSelection();
+ if( bShift )
+ SelectRange( mnRecentSelCol, GetFocusColumn() );
+ else if( !bMod1 )
+ Select( GetFocusColumn() );
+ EnableRepaint();
+ }
+ else if( eVDir != MOVE_NONE )
+ ScrollVertRel( eVDir );
+ else if( nCode == KEY_SPACE )
+ {
+ if( !bMod1 )
+ ImplClearSelection();
+ if( bShift )
+ SelectRange( mnRecentSelCol, GetFocusColumn() );
+ else if( bMod1 )
+ ToggleSelect( GetFocusColumn() );
+ else
+ Select( GetFocusColumn() );
+ }
+ else if( !bShift && bMod1 )
+ {
+ if( nCode == KEY_A )
+ SelectAll();
+ else if( (KEY_1 <= nCode) && (nCode <= KEY_9) )
+ {
+ sal_uInt32 nType = nCode - KEY_1;
+ if( nType < maTypeNames.size() )
+ Execute( CSVCMD_SETCOLUMNTYPE, nType );
+ }
+ }
+ }
+
+ return rKCode.GetGroup() == KEYGROUP_CURSOR;
+}
+
+bool ScCsvGrid::Command( const CommandEvent& rCEvt )
+{
+ bool bConsumed = true;
+ switch( rCEvt.GetCommand() )
+ {
+ case CommandEventId::ContextMenu:
+ {
+ if( rCEvt.IsMouseEvent() )
+ {
+ Point aPos( rCEvt.GetMousePosPixel() );
+ sal_uInt32 nColIx = GetColumnFromX( aPos.X() );
+ if( IsValidColumn( nColIx ) && (GetFirstX() <= aPos.X()) && (aPos.X() <= GetLastX()) )
+ {
+ if( !IsSelected( nColIx ) )
+ DoSelectAction( nColIx, 0 ); // focus & select
+ ExecutePopup( aPos );
+ }
+ }
+ else
+ {
+ sal_uInt32 nColIx = GetFocusColumn();
+ if( !IsSelected( nColIx ) )
+ Select( nColIx );
+ sal_Int32 nX1 = std::max( GetColumnX( nColIx ), GetFirstX() );
+ sal_Int32 nX2 = std::min( GetColumnX( nColIx + 1 ), GetWidth() );
+ ExecutePopup( Point( (nX1 + nX2) / 2, GetHeight() / 2 ) );
+ }
+ break;
+ }
+ case CommandEventId::Wheel:
+ {
+ tools::Rectangle aRect( Point(), maWinSize );
+ if( aRect.Contains( rCEvt.GetMousePosPixel() ) )
+ {
+ const CommandWheelData* pData = rCEvt.GetWheelData();
+ if( pData && (pData->GetMode() == CommandWheelMode::SCROLL) && !pData->IsHorz() )
+ Execute( CSVCMD_SETLINEOFFSET, GetFirstVisLine() - pData->GetNotchDelta() );
+ }
+ break;
+ }
+ default:
+ bConsumed = false;
+ break;
+ }
+ return bConsumed;
+}
+
+void ScCsvGrid::StyleUpdated()
+{
+ InitColors();
+ InitFonts();
+ UpdateLayoutData();
+ Execute( CSVCMD_UPDATECELLTEXTS );
+
+ ScCsvControl::StyleUpdated();
+}
+
+void ScCsvGrid::ConfigurationChanged( utl::ConfigurationBroadcaster*, ConfigurationHints )
+{
+ InitColors();
+ Repaint();
+}
+
+// painting -------------------------------------------------------------------
+
+void ScCsvGrid::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ ImplRedraw(rRenderContext);
+}
+
+void ScCsvGrid::ImplRedraw(vcl::RenderContext& rRenderContext)
+{
+ if( IsVisible() )
+ {
+ if( !IsValidGfx() )
+ {
+ ValidateGfx();
+ ImplDrawBackgrDev();
+ ImplDrawGridDev();
+ }
+ rRenderContext.DrawOutDev( Point(), maWinSize, Point(), maWinSize, *mpGridDev );
+ }
+}
+
+EditEngine* ScCsvGrid::GetEditEngine()
+{
+ return mpEditEngine.get();
+}
+
+void ScCsvGrid::ImplSetColumnClipRegion( OutputDevice& rOutDev, sal_uInt32 nColIndex )
+{
+ rOutDev.SetClipRegion( vcl::Region( tools::Rectangle(
+ std::max( GetColumnX( nColIndex ), GetFirstX() ) + 1, 0,
+ std::min( GetColumnX( nColIndex + 1 ), GetLastX() ), GetHeight() - 1 ) ) );
+}
+
+void ScCsvGrid::ImplDrawColumnHeader( OutputDevice& rOutDev, sal_uInt32 nColIndex, Color aFillColor )
+{
+ sal_Int32 nX1 = GetColumnX( nColIndex ) + 1;
+ sal_Int32 nX2 = GetColumnX( nColIndex + 1 );
+ sal_Int32 nHdrHt = GetHdrHeight();
+
+ rOutDev.SetLineColor();
+ rOutDev.SetFillColor( aFillColor );
+ rOutDev.DrawRect( tools::Rectangle( nX1, 0, nX2, nHdrHt ) );
+
+ rOutDev.SetFont( maHeaderFont );
+ rOutDev.SetTextColor( maHeaderTextColor );
+ rOutDev.SetTextFillColor();
+ rOutDev.DrawText( Point( nX1 + 1, 0 ), GetColumnTypeName( nColIndex ) );
+
+ rOutDev.SetLineColor( maHeaderGridColor );
+ rOutDev.DrawLine( Point( nX1, nHdrHt ), Point( nX2, nHdrHt ) );
+ rOutDev.DrawLine( Point( nX2, 0 ), Point( nX2, nHdrHt ) );
+}
+
+void ScCsvGrid::ImplDrawCellText( const Point& rPos, const OUString& rText )
+{
+ OUString aPlainText = rText.replaceAll( "\t", " " );
+ aPlainText = aPlainText.replaceAll( "\n", " " );
+ mpEditEngine->SetPaperSize( maEdEngSize );
+
+ /* #i60296# If string contains mixed script types, the space character
+ U+0020 may be drawn with a wrong width (from non-fixed-width Asian or
+ Complex font). Now we draw every non-space portion separately. */
+ sal_Int32 nCharIxInt {aPlainText.isEmpty() ? -1 : 0};
+ while (nCharIxInt>=0)
+ {
+ sal_Int32 nBeginIx = nCharIxInt;
+ const OUString aToken = aPlainText.getToken( 0, ' ', nCharIxInt );
+ if( !aToken.isEmpty() )
+ {
+ sal_Int32 nX = rPos.X() + GetCharWidth() * nBeginIx;
+ mpEditEngine->SetTextCurrentDefaults( aToken );
+ mpEditEngine->Draw(*mpBackgrDev, Point(nX, rPos.Y()));
+ }
+ }
+
+ sal_Int32 nCharIx = 0;
+ while( (nCharIx = rText.indexOf( '\t', nCharIx )) != -1 )
+ {
+ sal_Int32 nX1 = rPos.X() + GetCharWidth() * nCharIx;
+ sal_Int32 nX2 = nX1 + GetCharWidth() - 2;
+ sal_Int32 nY = rPos.Y() + GetLineHeight() / 2;
+ Color aColor( maTextColor );
+ mpBackgrDev->SetLineColor( aColor );
+ mpBackgrDev->DrawLine( Point( nX1, nY ), Point( nX2, nY ) );
+ mpBackgrDev->DrawLine( Point( nX2 - 2, nY - 2 ), Point( nX2, nY ) );
+ mpBackgrDev->DrawLine( Point( nX2 - 2, nY + 2 ), Point( nX2, nY ) );
+ ++nCharIx;
+ }
+ nCharIx = 0;
+ while( (nCharIx = rText.indexOf( '\n', nCharIx )) != -1 )
+ {
+ sal_Int32 nX1 = rPos.X() + GetCharWidth() * nCharIx;
+ sal_Int32 nX2 = nX1 + GetCharWidth() - 2;
+ sal_Int32 nY = rPos.Y() + GetLineHeight() / 2;
+ Color aColor( maTextColor );
+ mpBackgrDev->SetLineColor( aColor );
+ mpBackgrDev->DrawLine( Point( nX1, nY ), Point( nX2, nY ) );
+ mpBackgrDev->DrawLine( Point( nX1 + 2, nY - 2 ), Point( nX1, nY ) );
+ mpBackgrDev->DrawLine( Point( nX1 + 2, nY + 2 ), Point( nX1, nY ) );
+ mpBackgrDev->DrawLine( Point( nX2, nY - 2 ), Point( nX2, nY ) );
+ ++nCharIx;
+ }
+}
+
+void ScCsvGrid::ImplDrawFirstLineSep( bool bSet )
+{
+ if( IsVisibleLine( mnFirstImpLine ) && (mnFirstImpLine != GetFirstVisLine() ) )
+ {
+ sal_Int32 nY = GetY( mnFirstImpLine );
+ sal_Int32 nX = std::min( GetColumnX( GetLastVisColumn() + 1 ), GetLastX() );
+ mpBackgrDev->SetLineColor( bSet ? maGridPBColor : maGridColor );
+ mpBackgrDev->DrawLine( Point( GetFirstX() + 1, nY ), Point( nX, nY ) );
+ }
+}
+
+void ScCsvGrid::ImplDrawColumnBackgr( sal_uInt32 nColIndex )
+{
+ if( !IsVisibleColumn( nColIndex ) )
+ return;
+
+ ImplSetColumnClipRegion( *mpBackgrDev, nColIndex );
+
+ // grid
+ mpBackgrDev->SetLineColor();
+ mpBackgrDev->SetFillColor( maBackColor );
+ sal_Int32 nX1 = GetColumnX( nColIndex ) + 1;
+ sal_Int32 nX2 = GetColumnX( nColIndex + 1 );
+ sal_Int32 nY2 = GetY( GetLastVisLine() + 1 );
+ sal_Int32 nHdrHt = GetHdrHeight();
+ tools::Rectangle aRect( nX1, nHdrHt, nX2, nY2 );
+ mpBackgrDev->DrawRect( aRect );
+ mpBackgrDev->SetLineColor( maGridColor );
+ mpBackgrDev->DrawGrid( aRect, Size( 1, GetLineHeight() ), DrawGridFlags::HorzLines );
+ mpBackgrDev->DrawLine( Point( nX2, nHdrHt ), Point( nX2, nY2 ) );
+ ImplDrawFirstLineSep( true );
+
+ // cell texts
+ mpEditEngine->SetDefaultItem( SvxColorItem( maTextColor, EE_CHAR_COLOR ) );
+ size_t nLineCount = ::std::min( static_cast< size_t >( GetLastVisLine() - GetFirstVisLine() + 1 ), maTexts.size() );
+ // #i67432# cut string to avoid edit engine performance problems with very large strings
+ sal_Int32 nFirstVisPos = ::std::max( GetColumnPos( nColIndex ), GetFirstVisPos() );
+ sal_Int32 nLastVisPos = ::std::min( GetColumnPos( nColIndex + 1 ), GetLastVisPos() );
+ sal_Int32 nStrPos = nFirstVisPos - GetColumnPos( nColIndex );
+ sal_Int32 nStrLen = nLastVisPos - nFirstVisPos + 1;
+ sal_Int32 nStrX = GetX( nFirstVisPos );
+ for( size_t nLine = 0; nLine < nLineCount; ++nLine )
+ {
+ std::vector<OUString>& rStrVec = maTexts[ nLine ];
+ if( (nColIndex < rStrVec.size()) && (rStrVec[ nColIndex ].getLength() > nStrPos) )
+ {
+ const OUString& rStr = rStrVec[ nColIndex ];
+ OUString aText = rStr.copy( nStrPos, ::std::min( nStrLen, rStr.getLength() - nStrPos) );
+ ImplDrawCellText( Point( nStrX, GetY( GetFirstVisLine() + nLine ) ), aText );
+ }
+ }
+
+ // header
+ ImplDrawColumnHeader( *mpBackgrDev, nColIndex, maHeaderBackColor );
+
+ mpBackgrDev->SetClipRegion();
+}
+
+void ScCsvGrid::ImplDrawRowHeaders()
+{
+ mpBackgrDev->SetLineColor();
+ mpBackgrDev->SetFillColor( maAppBackColor );
+ Point aPoint( GetHdrX(), 0 );
+ tools::Rectangle aRect( aPoint, Size( GetHdrWidth() + 1, GetHeight() ) );
+ mpBackgrDev->DrawRect( aRect );
+
+ mpBackgrDev->SetFillColor( maHeaderBackColor );
+ aRect.SetBottom( GetY( GetLastVisLine() + 1 ) );
+ mpBackgrDev->DrawRect( aRect );
+
+ // line numbers
+ mpBackgrDev->SetFont( maHeaderFont );
+ mpBackgrDev->SetTextColor( maHeaderTextColor );
+ mpBackgrDev->SetTextFillColor();
+ sal_Int32 nLastLine = GetLastVisLine();
+ for( sal_Int32 nLine = GetFirstVisLine(); nLine <= nLastLine; ++nLine )
+ {
+ OUString aText( OUString::number( nLine + 1 ) );
+ sal_Int32 nX = GetHdrX() + (GetHdrWidth() - mpBackgrDev->GetTextWidth( aText )) / 2;
+ mpBackgrDev->DrawText( Point( nX, GetY( nLine ) ), aText );
+ }
+
+ // grid
+ mpBackgrDev->SetLineColor( maHeaderGridColor );
+ if( IsRTL() )
+ {
+ mpBackgrDev->DrawLine( Point( 0, 0 ), Point( 0, GetHeight() - 1 ) );
+ mpBackgrDev->DrawLine( aRect.TopLeft(), aRect.BottomLeft() );
+ }
+ else
+ mpBackgrDev->DrawLine( aRect.TopRight(), aRect.BottomRight() );
+ aRect.SetTop( GetHdrHeight() );
+ mpBackgrDev->DrawGrid( aRect, Size( 1, GetLineHeight() ), DrawGridFlags::HorzLines );
+}
+
+void ScCsvGrid::ImplDrawBackgrDev()
+{
+ mpBackgrDev->SetLineColor();
+ mpBackgrDev->SetFillColor( maAppBackColor );
+ mpBackgrDev->DrawRect( tools::Rectangle(
+ Point( GetFirstX() + 1, 0 ), Size( GetWidth() - GetHdrWidth(), GetHeight() ) ) );
+
+ sal_uInt32 nLastCol = GetLastVisColumn();
+ if (nLastCol == CSV_COLUMN_INVALID)
+ return;
+ for( sal_uInt32 nColIx = GetFirstVisColumn(); nColIx <= nLastCol; ++nColIx )
+ ImplDrawColumnBackgr( nColIx );
+
+ ImplDrawRowHeaders();
+}
+
+void ScCsvGrid::ImplDrawColumnSelection( sal_uInt32 nColIndex )
+{
+ ImplInvertCursor( GetRulerCursorPos() );
+ ImplSetColumnClipRegion( *mpGridDev, nColIndex );
+ mpGridDev->DrawOutDev( Point(), maWinSize, Point(), maWinSize, *mpBackgrDev );
+
+ if( IsSelected( nColIndex ) )
+ {
+ sal_Int32 nX1 = GetColumnX( nColIndex ) + 1;
+ sal_Int32 nX2 = GetColumnX( nColIndex + 1 );
+
+ // header
+ tools::Rectangle aRect( nX1, 0, nX2, GetHdrHeight() );
+ mpGridDev->SetLineColor();
+ if( maHeaderBackColor.IsDark() )
+ // redraw with light gray background in dark mode
+ ImplDrawColumnHeader( *mpGridDev, nColIndex, COL_LIGHTGRAY );
+ else
+ {
+ // use transparent active color
+ mpGridDev->SetFillColor( maSelectColor );
+ mpGridDev->DrawTransparent( tools::PolyPolygon( tools::Polygon( aRect ) ), CSV_HDR_TRANSPARENCY );
+ }
+
+ // column selection
+ aRect = tools::Rectangle( nX1, GetHdrHeight() + 1, nX2, GetY( GetLastVisLine() + 1 ) - 1 );
+ ImplInvertRect( *mpGridDev, aRect );
+ }
+
+ mpGridDev->SetClipRegion();
+ ImplInvertCursor( GetRulerCursorPos() );
+}
+
+void ScCsvGrid::ImplDrawGridDev()
+{
+ mpGridDev->DrawOutDev( Point(), maWinSize, Point(), maWinSize, *mpBackgrDev );
+ sal_uInt32 nLastCol = GetLastVisColumn();
+ if (nLastCol == CSV_COLUMN_INVALID)
+ return;
+ for( sal_uInt32 nColIx = GetFirstVisColumn(); nColIx <= nLastCol; ++nColIx )
+ ImplDrawColumnSelection( nColIx );
+}
+
+void ScCsvGrid::ImplDrawColumn( sal_uInt32 nColIndex )
+{
+ ImplDrawColumnBackgr( nColIndex );
+ ImplDrawColumnSelection( nColIndex );
+}
+
+void ScCsvGrid::ImplDrawHorzScrolled( sal_Int32 nOldPos )
+{
+ sal_Int32 nPos = GetFirstVisPos();
+ if( !IsValidGfx() || (nPos == nOldPos) )
+ return;
+ if( std::abs( nPos - nOldPos ) > GetVisPosCount() / 2 )
+ {
+ ImplDrawBackgrDev();
+ ImplDrawGridDev();
+ return;
+ }
+
+ Point aSrc, aDest;
+ sal_uInt32 nFirstColIx, nLastColIx;
+ if( nPos < nOldPos )
+ {
+ aSrc = Point( GetFirstX() + 1, 0 );
+ aDest = Point( GetFirstX() + GetCharWidth() * (nOldPos - nPos) + 1, 0 );
+ nFirstColIx = GetColumnFromPos( nPos );
+ nLastColIx = GetColumnFromPos( nOldPos );
+ }
+ else
+ {
+ aSrc = Point( GetFirstX() + GetCharWidth() * (nPos - nOldPos) + 1, 0 );
+ aDest = Point( GetFirstX() + 1, 0 );
+ nFirstColIx = GetColumnFromPos( std::min( nOldPos + GetVisPosCount(), GetPosCount() ) - 1 );
+ nLastColIx = GetColumnFromPos( std::min( nPos + GetVisPosCount(), GetPosCount() ) - 1 );
+ }
+
+ ImplInvertCursor( GetRulerCursorPos() + (nPos - nOldPos) );
+ tools::Rectangle aRectangle( GetFirstX(), 0, GetLastX(), GetHeight() - 1 );
+ vcl::Region aClipReg( aRectangle );
+ mpBackgrDev->SetClipRegion( aClipReg );
+ mpBackgrDev->CopyArea( aDest, aSrc, maWinSize );
+ mpBackgrDev->SetClipRegion();
+ mpGridDev->SetClipRegion( aClipReg );
+ mpGridDev->CopyArea( aDest, aSrc, maWinSize );
+ mpGridDev->SetClipRegion();
+ ImplInvertCursor( GetRulerCursorPos() );
+
+ for( sal_uInt32 nColIx = nFirstColIx; nColIx <= nLastColIx; ++nColIx )
+ ImplDrawColumn( nColIx );
+
+ sal_Int32 nLastX = GetX( GetPosCount() ) + 1;
+ if( nLastX <= GetLastX() )
+ {
+ tools::Rectangle aRect( nLastX, 0, GetLastX(), GetHeight() - 1 );
+ mpBackgrDev->SetLineColor();
+ mpBackgrDev->SetFillColor( maAppBackColor );
+ mpBackgrDev->DrawRect( aRect );
+ mpGridDev->SetLineColor();
+ mpGridDev->SetFillColor( maAppBackColor );
+ mpGridDev->DrawRect( aRect );
+ }
+}
+
+void ScCsvGrid::ImplInvertCursor( sal_Int32 nPos )
+{
+ if( IsVisibleSplitPos( nPos ) )
+ {
+ sal_Int32 nX = GetX( nPos ) - 1;
+ tools::Rectangle aRect( Point( nX, 0 ), Size( 3, GetHdrHeight() ) );
+ ImplInvertRect( *mpGridDev, aRect );
+ aRect.SetTop( GetHdrHeight() + 1 );
+ aRect.SetBottom( GetY( GetLastVisLine() + 1 ) );
+ ImplInvertRect( *mpGridDev, aRect );
+ }
+}
+
+tools::Rectangle ScCsvGrid::GetFocusRect()
+{
+ auto nColIndex = GetFocusColumn();
+ if( HasFocus() && IsVisibleColumn( nColIndex ) )
+ {
+ sal_Int32 nX1 = std::max( GetColumnX( nColIndex ), GetFirstX() ) + 1;
+ sal_Int32 nX2 = std::min( GetColumnX( nColIndex + 1 ) - sal_Int32( 1 ), GetLastX() );
+ sal_Int32 nY2 = std::min( GetY( GetLastVisLine() + 1 ), GetHeight() ) - 1;
+ return tools::Rectangle( nX1, 0, nX2, nY2 );
+ }
+ return weld::CustomWidgetController::GetFocusRect();
+}
+
+// accessibility ==============================================================
+
+css::uno::Reference<css::accessibility::XAccessible> ScCsvGrid::CreateAccessible()
+{
+ rtl::Reference<ScAccessibleCsvGrid> xRef(new ScAccessibleCsvGrid(*this));
+ mxAccessible = xRef;
+ return xRef;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/csvruler.cxx b/sc/source/ui/dbgui/csvruler.cxx
new file mode 100644
index 000000000..3af7645e2
--- /dev/null
+++ b/sc/source/ui/dbgui/csvruler.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 <csvruler.hxx>
+#include <AccessibleCsvControl.hxx>
+
+#include <optutil.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <vcl/event.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/virdev.hxx>
+#include <o3tl/string_view.hxx>
+
+using namespace com::sun::star::uno;
+
+constexpr OUStringLiteral SEP_PATH = u"Office.Calc/Dialogs/CSVImport";
+constexpr OUStringLiteral FIXED_WIDTH_LIST = u"FixedWidthList";
+
+static void load_FixedWidthList(ScCsvSplits &rSplits)
+{
+ Sequence<Any>aValues;
+ const Any *pProperties;
+ Sequence<OUString> aNames { FIXED_WIDTH_LIST };
+ ScLinkConfigItem aItem( SEP_PATH );
+
+ aValues = aItem.GetProperties( aNames );
+ pProperties = aValues.getConstArray();
+
+ if( !pProperties[0].hasValue() )
+ return;
+
+ rSplits.Clear();
+
+ OUString sFixedWidthLists;
+ pProperties[0] >>= sFixedWidthLists;
+
+ sal_Int32 nIdx {0};
+ for(;;)
+ {
+ const sal_Int32 n = o3tl::toInt32(o3tl::getToken(sFixedWidthLists, 0, ';', nIdx));
+ if (nIdx<0)
+ {
+ // String ends with a semi-colon so there
+ // is no useful 'int' after the last one.
+ // This also works in case of empty string
+ break;
+ }
+ rSplits.Insert(n);
+ }
+}
+static void save_FixedWidthList(const ScCsvSplits& rSplits)
+{
+ OUStringBuffer sSplits;
+ // Create a semi-colon separated string to save the splits
+ sal_uInt32 n = rSplits.Count();
+ for (sal_uInt32 i = 0; i < n; ++i)
+ {
+ sSplits.append(rSplits[i]);
+ sSplits.append(";");
+ }
+
+ OUString sFixedWidthLists = sSplits.makeStringAndClear();
+ Sequence<Any> aValues;
+ Any *pProperties;
+ Sequence<OUString> aNames { FIXED_WIDTH_LIST };
+ ScLinkConfigItem aItem( SEP_PATH );
+
+ aValues = aItem.GetProperties( aNames );
+ pProperties = aValues.getArray();
+ pProperties[0] <<= sFixedWidthLists;
+
+ aItem.PutProperties(aNames, aValues);
+}
+
+ScCsvRuler::ScCsvRuler(const ScCsvLayoutData& rData, ScCsvTableBox* pTableBox)
+ : ScCsvControl(rData)
+ , mpTableBox(pTableBox)
+ , mnPosCursorLast(1)
+ , mnPosMTStart(0)
+ , mnPosMTCurr(0)
+ , mbPosMTMoved(false)
+ , mnSplitSize(0)
+ , mbTracking(false)
+{
+}
+
+void ScCsvRuler::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ ScCsvControl::SetDrawingArea(pDrawingArea);
+
+ UpdateSplitSize();
+
+ Size aSize(1, GetTextHeight() + mnSplitSize + 2);
+ pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
+ SetOutputSizePixel(aSize);
+
+ EnableRTL( false ); // RTL
+ InitColors();
+ InitSizeData();
+
+ OutputDevice& rRefDevice = pDrawingArea->get_ref_device();
+ maBackgrDev->SetFont( rRefDevice.GetFont() );
+ maRulerDev->SetFont( rRefDevice.GetFont() );
+ load_FixedWidthList( maSplits );
+}
+
+ScCsvRuler::~ScCsvRuler()
+{
+ save_FixedWidthList( maSplits );
+}
+
+// common ruler handling ------------------------------------------------------
+
+void ScCsvRuler::ApplyLayout( const ScCsvLayoutData& rOldData )
+{
+ ScCsvDiff nDiff = GetLayoutData().GetDiff( rOldData ) & (ScCsvDiff::HorizontalMask | ScCsvDiff::RulerCursor);
+ if( nDiff == ScCsvDiff::Equal ) return;
+
+ DisableRepaint();
+ if( nDiff & ScCsvDiff::HorizontalMask )
+ {
+ InitSizeData();
+ if( GetRulerCursorPos() >= GetPosCount() )
+ MoveCursor( GetPosCount() - 1 );
+ }
+ if( nDiff & ScCsvDiff::RulerCursor )
+ {
+ ImplInvertCursor( rOldData.mnPosCursor );
+ ImplInvertCursor( GetRulerCursorPos() );
+ }
+ EnableRepaint();
+
+ if( nDiff & ScCsvDiff::PosOffset )
+ AccSendVisibleEvent();
+}
+
+void ScCsvRuler::InitColors()
+{
+ const StyleSettings& rSett = Application::GetSettings().GetStyleSettings();
+ maBackColor = rSett.GetFaceColor();
+ maActiveColor = rSett.GetWindowColor();
+ maTextColor = rSett.GetLabelTextColor();
+ maSplitColor = maBackColor.IsDark() ? maTextColor : COL_LIGHTRED;
+ InvalidateGfx();
+}
+
+void ScCsvRuler::UpdateSplitSize()
+{
+ mnSplitSize = (GetCharWidth() * 3 / 5) | 1; // make an odd number
+}
+
+void ScCsvRuler::InitSizeData()
+{
+ maWinSize = GetOutputSizePixel();
+
+ UpdateSplitSize();
+
+ sal_Int32 nActiveWidth = std::min( GetWidth() - GetHdrWidth(), GetPosCount() * GetCharWidth() );
+ sal_Int32 nActiveHeight = GetTextHeight();
+
+ maActiveRect.SetPos( Point( GetFirstX(), (GetHeight() - nActiveHeight - 1) / 2 ) );
+ maActiveRect.SetSize( Size( nActiveWidth, nActiveHeight ) );
+
+ maBackgrDev->SetOutputSizePixel( maWinSize );
+ maRulerDev->SetOutputSizePixel( maWinSize );
+
+ InvalidateGfx();
+}
+
+void ScCsvRuler::MoveCursor( sal_Int32 nPos, bool bScroll )
+{
+ DisableRepaint();
+ if( bScroll )
+ Execute( CSVCMD_MAKEPOSVISIBLE, nPos );
+ Execute( CSVCMD_MOVERULERCURSOR, IsVisibleSplitPos( nPos ) ? nPos : CSV_POS_INVALID );
+ EnableRepaint();
+ AccSendCaretEvent();
+}
+
+void ScCsvRuler::MoveCursorRel( ScMoveMode eDir )
+{
+ if( GetRulerCursorPos() == CSV_POS_INVALID )
+ return;
+
+ switch( eDir )
+ {
+ case MOVE_FIRST:
+ MoveCursor( 1 );
+ break;
+ case MOVE_LAST:
+ MoveCursor( GetPosCount() - 1 );
+ break;
+ case MOVE_PREV:
+ if( GetRulerCursorPos() > 1 )
+ MoveCursor( GetRulerCursorPos() - 1 );
+ break;
+ case MOVE_NEXT:
+ if( GetRulerCursorPos() < GetPosCount() - 1 )
+ MoveCursor( GetRulerCursorPos() + 1 );
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+}
+
+void ScCsvRuler::MoveCursorToSplit( ScMoveMode eDir )
+{
+ if( GetRulerCursorPos() == CSV_POS_INVALID )
+ return;
+
+ sal_uInt32 nIndex = CSV_VEC_NOTFOUND;
+ switch( eDir )
+ {
+ case MOVE_FIRST: nIndex = maSplits.LowerBound( 0 ); break;
+ case MOVE_LAST: nIndex = maSplits.UpperBound( GetPosCount() ); break;
+ case MOVE_PREV: nIndex = maSplits.UpperBound( GetRulerCursorPos() - 1 ); break;
+ case MOVE_NEXT: nIndex = maSplits.LowerBound( GetRulerCursorPos() + 1 ); break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ sal_Int32 nPos = maSplits[ nIndex ];
+ if( nPos != CSV_POS_INVALID )
+ MoveCursor( nPos );
+}
+
+void ScCsvRuler::ScrollVertRel( ScMoveMode eDir )
+{
+ sal_Int32 nLine = GetFirstVisLine();
+ switch( eDir )
+ {
+ case MOVE_PREV: --nLine; break;
+ case MOVE_NEXT: ++nLine; break;
+ case MOVE_PREVPAGE: nLine -= GetVisLineCount() - 1; break;
+ case MOVE_NEXTPAGE: nLine += GetVisLineCount() - 1; break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ Execute( CSVCMD_SETLINEOFFSET, nLine );
+}
+
+// split handling -------------------------------------------------------------
+
+sal_Int32 ScCsvRuler::GetNoScrollPos( sal_Int32 nPos ) const
+{
+ sal_Int32 nNewPos = nPos;
+ if( nNewPos != CSV_POS_INVALID )
+ {
+ if( nNewPos < GetFirstVisPos() + CSV_SCROLL_DIST )
+ {
+ sal_Int32 nScroll = (GetFirstVisPos() > 0) ? CSV_SCROLL_DIST : 0;
+ nNewPos = std::max( nPos, GetFirstVisPos() + nScroll );
+ }
+ else if( nNewPos > GetLastVisPos() - CSV_SCROLL_DIST - 1 )
+ {
+ sal_Int32 nScroll = (GetFirstVisPos() < GetMaxPosOffset()) ? CSV_SCROLL_DIST : 0;
+ nNewPos = std::min( nNewPos, GetLastVisPos() - nScroll - sal_Int32( 1 ) );
+ }
+ }
+ return nNewPos;
+}
+
+void ScCsvRuler::InsertSplit( sal_Int32 nPos )
+{
+ if( maSplits.Insert( nPos ) )
+ {
+ ImplDrawSplit( nPos );
+ Repaint();
+ }
+}
+
+void ScCsvRuler::RemoveSplit( sal_Int32 nPos )
+{
+ if( maSplits.Remove( nPos ) )
+ {
+ ImplEraseSplit( nPos );
+ Repaint();
+ }
+}
+
+void ScCsvRuler::MoveSplit( sal_Int32 nPos, sal_Int32 nNewPos )
+{
+ bool bRemove = maSplits.Remove( nPos );
+ bool bInsert = maSplits.Insert( nNewPos );
+ if( bRemove || bInsert )
+ {
+ ImplEraseSplit( nPos );
+ ImplDrawSplit( nNewPos );
+ Repaint();
+ }
+}
+
+void ScCsvRuler::RemoveAllSplits()
+{
+ maSplits.Clear();
+ Repaint( true );
+}
+
+sal_Int32 ScCsvRuler::FindEmptyPos( sal_Int32 nPos, ScMoveMode eDir ) const
+{
+ sal_Int32 nNewPos = nPos;
+ if( nNewPos != CSV_POS_INVALID )
+ {
+ switch( eDir )
+ {
+ case MOVE_FIRST:
+ nNewPos = std::min( nPos, FindEmptyPos( 0, MOVE_NEXT ) );
+ break;
+ case MOVE_LAST:
+ nNewPos = std::max( nPos, FindEmptyPos( GetPosCount(), MOVE_PREV ) );
+ break;
+ case MOVE_PREV:
+ while( HasSplit( --nNewPos ) ) ;
+ break;
+ case MOVE_NEXT:
+ while( HasSplit( ++nNewPos ) ) ;
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+ return IsValidSplitPos( nNewPos ) ? nNewPos : CSV_POS_INVALID;
+}
+
+void ScCsvRuler::MoveCurrSplit( sal_Int32 nNewPos )
+{
+ DisableRepaint();
+ Execute( CSVCMD_MOVESPLIT, GetRulerCursorPos(), nNewPos );
+ MoveCursor( nNewPos );
+ EnableRepaint();
+}
+
+void ScCsvRuler::MoveCurrSplitRel( ScMoveMode eDir )
+{
+ if( HasSplit( GetRulerCursorPos() ) )
+ {
+ sal_Int32 nNewPos = FindEmptyPos( GetRulerCursorPos(), eDir );
+ if( nNewPos != CSV_POS_INVALID )
+ MoveCurrSplit( nNewPos );
+ }
+}
+
+// event handling -------------------------------------------------------------
+
+void ScCsvRuler::Resize()
+{
+ ScCsvControl::Resize();
+ InitSizeData();
+ Repaint();
+}
+
+void ScCsvRuler::GetFocus()
+{
+ ScCsvControl::GetFocus();
+ DisableRepaint();
+ if( GetRulerCursorPos() == CSV_POS_INVALID )
+ MoveCursor( GetNoScrollPos( mnPosCursorLast ) );
+ EnableRepaint();
+}
+
+void ScCsvRuler::LoseFocus()
+{
+ ScCsvControl::LoseFocus();
+ mnPosCursorLast = GetRulerCursorPos();
+ MoveCursor( CSV_POS_INVALID );
+}
+
+void ScCsvRuler::StyleUpdated()
+{
+ InitColors();
+ Repaint();
+
+ ScCsvControl::StyleUpdated();
+}
+
+bool ScCsvRuler::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ DisableRepaint();
+ if( !HasFocus() )
+ GrabFocus();
+ if( rMEvt.IsLeft() )
+ {
+ sal_Int32 nPos = GetPosFromX( rMEvt.GetPosPixel().X() );
+ if( IsVisibleSplitPos( nPos ) )
+ StartMouseTracking( nPos );
+ ImplSetMousePointer( nPos );
+ }
+ EnableRepaint();
+ return true;
+}
+
+bool ScCsvRuler::MouseButtonUp( const MouseEvent& )
+{
+ if (mbTracking)
+ {
+ EndMouseTracking();
+ mbTracking = false;
+ }
+ return true;
+}
+
+bool ScCsvRuler::MouseMove( const MouseEvent& rMEvt )
+{
+ if( !rMEvt.IsModifierChanged() )
+ {
+ sal_Int32 nPos = GetPosFromX( rMEvt.GetPosPixel().X() );
+ if( mbTracking )
+ {
+ // on mouse tracking: keep position valid
+ nPos = std::clamp( nPos, sal_Int32(1), GetPosCount() - 1 );
+ MoveMouseTracking( nPos );
+ }
+ else
+ {
+ tools::Rectangle aRect( Point(), maWinSize );
+ if( !IsVisibleSplitPos( nPos ) || !aRect.Contains( rMEvt.GetPosPixel() ) )
+ // if focused, keep old cursor position for key input
+ nPos = HasFocus() ? GetRulerCursorPos() : CSV_POS_INVALID;
+ MoveCursor( nPos, false );
+ }
+ ImplSetMousePointer( nPos );
+ }
+ return true;
+}
+
+bool ScCsvRuler::KeyInput( const KeyEvent& rKEvt )
+{
+ const vcl::KeyCode& rKCode = rKEvt.GetKeyCode();
+ sal_uInt16 nCode = rKCode.GetCode();
+ bool bNoMod = !rKCode.GetModifier();
+ bool bShift = (rKCode.GetModifier() == KEY_SHIFT);
+ bool bJump = (rKCode.GetModifier() == KEY_MOD1);
+ bool bMove = (rKCode.GetModifier() == (KEY_MOD1 | KEY_SHIFT));
+
+ ScMoveMode eHDir = GetHorzDirection( nCode, true );
+ ScMoveMode eVDir = GetVertDirection( nCode, false );
+
+ if( bNoMod )
+ {
+ if( eHDir != MOVE_NONE )
+ MoveCursorRel( eHDir );
+ else if( eVDir != MOVE_NONE )
+ ScrollVertRel( eVDir );
+ else switch( nCode )
+ {
+ case KEY_SPACE: Execute( CSVCMD_TOGGLESPLIT, GetRulerCursorPos() ); break;
+ case KEY_INSERT: Execute( CSVCMD_INSERTSPLIT, GetRulerCursorPos() ); break;
+ case KEY_DELETE: Execute( CSVCMD_REMOVESPLIT, GetRulerCursorPos() ); break;
+ }
+ }
+ else if( bJump && (eHDir != MOVE_NONE) )
+ MoveCursorToSplit( eHDir );
+ else if( bMove && (eHDir != MOVE_NONE) )
+ MoveCurrSplitRel( eHDir );
+ else if( bShift && (nCode == KEY_DELETE) )
+ Execute( CSVCMD_REMOVEALLSPLITS );
+
+ return rKCode.GetGroup() == KEYGROUP_CURSOR;
+}
+
+void ScCsvRuler::StartMouseTracking( sal_Int32 nPos )
+{
+ mnPosMTStart = mnPosMTCurr = nPos;
+ mbPosMTMoved = false;
+ maOldSplits = maSplits;
+ Execute( CSVCMD_INSERTSPLIT, nPos );
+ if( HasSplit( nPos ) )
+ mbTracking = true;
+}
+
+void ScCsvRuler::MoveMouseTracking( sal_Int32 nPos )
+{
+ if( mnPosMTCurr != nPos )
+ {
+ DisableRepaint();
+ MoveCursor( nPos );
+ if( (mnPosMTCurr != mnPosMTStart) && maOldSplits.HasSplit( mnPosMTCurr ) )
+ Execute( CSVCMD_INSERTSPLIT, nPos );
+ else
+ Execute( CSVCMD_MOVESPLIT, mnPosMTCurr, nPos );
+ mnPosMTCurr = nPos;
+ mbPosMTMoved = true;
+ EnableRepaint();
+ }
+}
+
+void ScCsvRuler::EndMouseTracking()
+{
+ // remove on simple click on an existing split
+ if( (mnPosMTCurr == mnPosMTStart) && maOldSplits.HasSplit( mnPosMTCurr ) && !mbPosMTMoved )
+ Execute( CSVCMD_REMOVESPLIT, mnPosMTCurr );
+ mnPosMTStart = CSV_POS_INVALID;
+}
+
+// painting -------------------------------------------------------------------
+
+void ScCsvRuler::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& )
+{
+ ImplRedraw(rRenderContext);
+}
+
+void ScCsvRuler::ImplRedraw(vcl::RenderContext& rRenderContext)
+{
+ if( IsVisible() )
+ {
+ if( !IsValidGfx() )
+ {
+ ValidateGfx();
+ ImplDrawBackgrDev();
+ ImplDrawRulerDev();
+ }
+ rRenderContext.DrawOutDev( Point(), maWinSize, Point(), maWinSize, *maRulerDev );
+ }
+}
+
+tools::Rectangle ScCsvRuler::GetFocusRect()
+{
+ /* Draws directly tracking rectangle to the column with the specified index. */
+ if(HasFocus())
+ return tools::Rectangle(0, 0, GetWidth() - 1, GetHeight() - 2);
+ return weld::CustomWidgetController::GetFocusRect();
+}
+
+void ScCsvRuler::ImplDrawArea( sal_Int32 nPosX, sal_Int32 nWidth )
+{
+ maBackgrDev->SetLineColor();
+ tools::Rectangle aRect( Point( nPosX, 0 ), Size( nWidth, GetHeight() ) );
+ maBackgrDev->SetFillColor( maBackColor );
+ maBackgrDev->DrawRect( aRect );
+
+ aRect = maActiveRect;
+ aRect.SetLeft( std::max( GetFirstX(), nPosX ) );
+ aRect.SetRight( std::min( std::min( GetX( GetPosCount() ), GetLastX() ), nPosX + nWidth - sal_Int32( 1 ) ) );
+ if( aRect.Left() <= aRect.Right() )
+ {
+ maBackgrDev->SetFillColor( maActiveColor );
+ maBackgrDev->DrawRect( aRect );
+ }
+
+ maBackgrDev->SetLineColor( maTextColor );
+ sal_Int32 nY = GetHeight() - 1;
+ maBackgrDev->DrawLine( Point( nPosX, nY ), Point( nPosX + nWidth - 1, nY ) );
+}
+
+void ScCsvRuler::ImplDrawBackgrDev()
+{
+ ImplDrawArea( 0, GetWidth() );
+
+ // scale
+ maBackgrDev->SetLineColor( maTextColor );
+ maBackgrDev->SetFillColor();
+ sal_Int32 nPos;
+
+ sal_Int32 nFirstPos = std::max( GetPosFromX( 0 ) - 1, sal_Int32(0) );
+ sal_Int32 nLastPos = GetPosFromX( GetWidth() );
+ sal_Int32 nY = (maActiveRect.Top() + maActiveRect.Bottom()) / 2;
+ for( nPos = nFirstPos; nPos <= nLastPos; ++nPos )
+ {
+ sal_Int32 nX = GetX( nPos );
+ if( nPos % 5 )
+ maBackgrDev->DrawPixel( Point( nX, nY ) );
+ else
+ maBackgrDev->DrawLine( Point( nX, nY - 1 ), Point( nX, nY + 1 ) );
+ }
+
+ // texts
+ maBackgrDev->SetTextColor( maTextColor );
+ maBackgrDev->SetTextFillColor();
+ for( nPos = ((nFirstPos + 9) / 10) * 10; nPos <= nLastPos; nPos += 10 )
+ {
+ OUString aText( OUString::number( nPos ) );
+ sal_Int32 nTextWidth = maBackgrDev->GetTextWidth( aText );
+ sal_Int32 nTextX = GetX( nPos ) - nTextWidth / 2;
+ ImplDrawArea( nTextX - 1, nTextWidth + 2 );
+ maBackgrDev->DrawText( Point( nTextX, maActiveRect.Top() ), aText );
+ }
+}
+
+void ScCsvRuler::ImplDrawSplit( sal_Int32 nPos )
+{
+ if( IsVisibleSplitPos( nPos ) )
+ {
+ Point aPos( GetX( nPos ) - mnSplitSize / 2, GetHeight() - mnSplitSize - 2 );
+ Size aSize( mnSplitSize, mnSplitSize );
+ maRulerDev->SetLineColor( maTextColor );
+ maRulerDev->SetFillColor( maSplitColor );
+ maRulerDev->DrawEllipse( tools::Rectangle( aPos, aSize ) );
+ maRulerDev->DrawPixel( Point( GetX( nPos ), GetHeight() - 2 ) );
+ }
+}
+
+void ScCsvRuler::ImplEraseSplit( sal_Int32 nPos )
+{
+ if( IsVisibleSplitPos( nPos ) )
+ {
+ ImplInvertCursor( GetRulerCursorPos() );
+ Point aPos( GetX( nPos ) - mnSplitSize / 2, 0 );
+ Size aSize( mnSplitSize, GetHeight() );
+ maRulerDev->DrawOutDev( aPos, aSize, aPos, aSize, *maBackgrDev );
+ ImplInvertCursor( GetRulerCursorPos() );
+ }
+}
+
+void ScCsvRuler::ImplDrawRulerDev()
+{
+ maRulerDev->DrawOutDev( Point(), maWinSize, Point(), maWinSize, *maBackgrDev );
+ ImplInvertCursor( GetRulerCursorPos() );
+
+ sal_uInt32 nFirst = maSplits.LowerBound( GetFirstVisPos() );
+ sal_uInt32 nLast = maSplits.UpperBound( GetLastVisPos() );
+ if( (nFirst != CSV_VEC_NOTFOUND) && (nLast != CSV_VEC_NOTFOUND) )
+ for( sal_uInt32 nIndex = nFirst; nIndex <= nLast; ++nIndex )
+ ImplDrawSplit( GetSplitPos( nIndex ) );
+}
+
+void ScCsvRuler::ImplInvertCursor( sal_Int32 nPos )
+{
+ if( IsVisibleSplitPos( nPos ) )
+ {
+ ImplInvertRect( *maRulerDev, tools::Rectangle( Point( GetX( nPos ) - 1, 0 ), Size( 3, GetHeight() - 1 ) ) );
+ if( HasSplit( nPos ) )
+ ImplDrawSplit( nPos );
+ }
+}
+
+void ScCsvRuler::ImplSetMousePointer( sal_Int32 nPos )
+{
+ SetPointer( HasSplit( nPos ) ? PointerStyle::HSplit : PointerStyle::Arrow );
+}
+
+// accessibility ==============================================================
+
+css::uno::Reference<css::accessibility::XAccessible> ScCsvRuler::CreateAccessible()
+{
+ rtl::Reference<ScAccessibleCsvRuler> xRef(new ScAccessibleCsvRuler(*this));
+ mxAccessible = xRef;
+ return xRef;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/csvsplits.cxx b/sc/source/ui/dbgui/csvsplits.cxx
new file mode 100644
index 000000000..575bd53d0
--- /dev/null
+++ b/sc/source/ui/dbgui/csvsplits.cxx
@@ -0,0 +1,102 @@
+/* -*- 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 <csvsplits.hxx>
+
+#include <algorithm>
+
+#include <sal/log.hxx>
+
+bool ScCsvSplits::Insert( sal_Int32 nPos )
+{
+ if (nPos < 0)
+ return false;
+
+ const auto aIter = ::std::lower_bound( maVec.begin(), maVec.end(), nPos );
+
+ if (aIter != maVec.end() && *aIter == nPos)
+ return false;
+
+ SAL_WARN_IF(maVec.size()>=static_cast<std::size_t>(SAL_MAX_UINT32-1),
+ "sc.ui", "ScCsvSplits::Insert: too many elements in vector");
+
+ maVec.insert( aIter, nPos );
+ return true;
+}
+
+bool ScCsvSplits::Remove( sal_Int32 nPos )
+{
+ sal_uInt32 nIndex = GetIndex( nPos );
+ if (nIndex == CSV_VEC_NOTFOUND)
+ return false;
+
+ maVec.erase( maVec.begin() + nIndex );
+ return true;
+}
+
+void ScCsvSplits::RemoveRange( sal_Int32 nPosStart, sal_Int32 nPosEnd )
+{
+ sal_uInt32 nStartIx = LowerBound( nPosStart );
+ sal_uInt32 nEndIx = UpperBound( nPosEnd );
+ if( (nStartIx != CSV_VEC_NOTFOUND) && (nEndIx != CSV_VEC_NOTFOUND) && (nStartIx <= nEndIx) )
+ maVec.erase( maVec.begin() + nStartIx, maVec.begin() + nEndIx + 1 );
+}
+
+void ScCsvSplits::Clear()
+{
+ maVec.clear();
+}
+
+bool ScCsvSplits::HasSplit( sal_Int32 nPos ) const
+{
+ return GetIndex( nPos ) != CSV_VEC_NOTFOUND;
+}
+
+sal_uInt32 ScCsvSplits::GetIndex( sal_Int32 nPos ) const
+{
+ auto aIter = ::std::lower_bound( maVec.cbegin(), maVec.cend(), nPos );
+ return GetIterIndex( ((aIter != maVec.end()) && (*aIter == nPos)) ? aIter : maVec.end() );
+}
+
+sal_uInt32 ScCsvSplits::LowerBound( sal_Int32 nPos ) const
+{
+ return GetIterIndex( ::std::lower_bound( maVec.begin(), maVec.end(), nPos ) );
+}
+
+sal_uInt32 ScCsvSplits::UpperBound( sal_Int32 nPos ) const
+{
+ sal_uInt32 nIndex = LowerBound( nPos );
+ if( nIndex == CSV_VEC_NOTFOUND )
+ return Count() ? (Count() - 1) : CSV_VEC_NOTFOUND;
+ if( GetPos( nIndex ) == nPos )
+ return nIndex;
+ return nIndex ? (nIndex - 1) : CSV_VEC_NOTFOUND;
+}
+
+sal_Int32 ScCsvSplits::GetPos( sal_uInt32 nIndex ) const
+{
+ return (nIndex < Count()) ? maVec[ nIndex ] : CSV_POS_INVALID;
+}
+
+sal_uInt32 ScCsvSplits::GetIterIndex( const_iterator const & aIter ) const
+{
+ return (aIter == maVec.end()) ? CSV_VEC_NOTFOUND : (aIter - maVec.begin());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/csvtablebox.cxx b/sc/source/ui/dbgui/csvtablebox.cxx
new file mode 100644
index 000000000..10dba1b81
--- /dev/null
+++ b/sc/source/ui/dbgui/csvtablebox.cxx
@@ -0,0 +1,369 @@
+/* -*- 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 <csvtablebox.hxx>
+#include <vcl/settings.hxx>
+
+ScCsvTableBox::ScCsvTableBox(weld::Builder& rBuilder)
+ : mxRuler(new ScCsvRuler(maData, this))
+ , mxGrid(new ScCsvGrid(maData, rBuilder.weld_menu("popup"), this))
+ , mxScroll(rBuilder.weld_scrolled_window("scrolledwindow", true))
+ , mxRulerWeld(new weld::CustomWeld(rBuilder, "csvruler", *mxRuler))
+ , mxGridWeld(new weld::CustomWeld(rBuilder, "csvgrid", *mxGrid))
+ , maEndScrollIdle("ScCsvTableBox maEndScrollIdle")
+{
+ Size aSize(mxScroll->get_approximate_digit_width() * 67,
+ mxScroll->get_text_height() * 10);
+ // this needs to be larger than the ScCsvGrid initial size to get it
+ // to stretch to fit, see ScCsvGrid::SetDrawingArea
+ mxScroll->set_size_request(aSize.Width(), aSize.Height());
+
+ mbFixedMode = false;
+ mnFixedWidth = 1;
+
+ Link<ScCsvControl&,void> aLink = LINK( this, ScCsvTableBox, CsvCmdHdl );
+ mxRuler->SetCmdHdl( aLink );
+ mxGrid->SetCmdHdl( aLink );
+
+ mxScroll->connect_hadjustment_changed(LINK(this, ScCsvTableBox, HScrollHdl));
+ mxScroll->connect_vadjustment_changed(LINK(this, ScCsvTableBox, VScrollHdl));
+
+ maEndScrollIdle.SetPriority(TaskPriority::LOWEST);
+ maEndScrollIdle.SetInvokeHandler(LINK(this,ScCsvTableBox,ScrollEndHdl));
+
+ InitControls();
+}
+
+ScCsvTableBox::~ScCsvTableBox()
+{
+}
+
+// common table box handling --------------------------------------------------
+
+void ScCsvTableBox::SetSeparatorsMode()
+{
+ if( !mbFixedMode )
+ return;
+
+ // rescue data for fixed width mode
+ mnFixedWidth = mxGrid->GetPosCount();
+ maFixColStates = mxGrid->GetColumnStates();
+ // switch to separators mode
+ mbFixedMode = false;
+ // reset and reinitialize controls
+ mxGrid->DisableRepaint();
+ mxGrid->Execute( CSVCMD_SETLINEOFFSET, 0 );
+ mxGrid->Execute( CSVCMD_SETPOSCOUNT, 1 );
+ mxGrid->Execute( CSVCMD_NEWCELLTEXTS );
+ mxGrid->SetColumnStates( std::vector(maSepColStates) );
+ InitControls();
+ mxGrid->EnableRepaint();
+}
+
+void ScCsvTableBox::SetFixedWidthMode()
+{
+ if( mbFixedMode )
+ return;
+
+ // rescue data for separators mode
+ maSepColStates = mxGrid->GetColumnStates();
+ // switch to fixed width mode
+ mbFixedMode = true;
+ // reset and reinitialize controls
+ mxGrid->DisableRepaint();
+ mxGrid->Execute( CSVCMD_SETLINEOFFSET, 0 );
+ mxGrid->Execute( CSVCMD_SETPOSCOUNT, mnFixedWidth );
+ mxGrid->SetSplits( mxRuler->GetSplits() );
+ mxGrid->SetColumnStates( std::vector(maFixColStates) );
+ InitControls();
+ mxGrid->EnableRepaint();
+}
+
+void ScCsvTableBox::Init()
+{
+ mxGrid->Init();
+}
+
+void ScCsvTableBox::InitControls()
+{
+ mxGrid->UpdateLayoutData();
+
+ mxGrid->Show();
+ if (mbFixedMode)
+ mxRuler->Show();
+ else
+ mxRuler->Hide();
+
+ Size aWinSize = mxGrid->GetOutputSizePixel();
+ maData.mnWinWidth = aWinSize.Width();
+ maData.mnWinHeight = aWinSize.Height();
+
+ // scrollbars always visible
+ InitHScrollBar();
+
+ // scrollbars always visible
+ InitVScrollBar();
+
+ // let the controls self-adjust to visible area
+ mxGrid->Execute( CSVCMD_SETPOSOFFSET, mxGrid->GetFirstVisPos() );
+ mxGrid->Execute( CSVCMD_SETLINEOFFSET, mxGrid->GetFirstVisLine() );
+}
+
+void ScCsvTableBox::InitHScrollBar()
+{
+ int nLower = 0;
+ int nValue = mxGrid->GetFirstVisPos();
+ int nUpper = mxGrid->GetPosCount() + 2;
+ int nPageSize = mxGrid->GetVisPosCount();
+
+ // Undo scrollbar RTL
+ if (AllSettings::GetLayoutRTL())
+ nValue = nUpper - (nValue - nLower + nPageSize);
+
+ mxScroll->hadjustment_configure(nValue, nLower, nUpper,
+ 1, mxGrid->GetVisPosCount() * 3 / 4,
+ nPageSize);
+}
+
+void ScCsvTableBox::InitVScrollBar()
+{
+ mxScroll->vadjustment_configure(mxGrid->GetFirstVisLine(), 0, mxGrid->GetLineCount() + 1,
+ 1, mxGrid->GetVisLineCount() - 2,
+ mxGrid->GetVisLineCount());
+}
+
+void ScCsvTableBox::MakePosVisible( sal_Int32 nPos )
+{
+ if( (0 <= nPos) && (nPos < mxGrid->GetPosCount()) )
+ {
+ if( nPos - CSV_SCROLL_DIST + 1 <= mxGrid->GetFirstVisPos() )
+ mxGrid->Execute( CSVCMD_SETPOSOFFSET, nPos - CSV_SCROLL_DIST );
+ else if( nPos + CSV_SCROLL_DIST >= mxGrid->GetLastVisPos() )
+ mxGrid->Execute( CSVCMD_SETPOSOFFSET, nPos - mxGrid->GetVisPosCount() + CSV_SCROLL_DIST );
+ }
+}
+
+// cell contents --------------------------------------------------------------
+
+void ScCsvTableBox::SetUniStrings(
+ const OUString* pTextLines, const OUString& rSepChars,
+ sal_Unicode cTextSep, bool bMergeSep, bool bRemoveSpace )
+{
+ // assuming that pTextLines is a string array with size CSV_PREVIEW_LINES
+ // -> will be dynamic sometime
+ mxGrid->DisableRepaint();
+ sal_Int32 nEndLine = mxGrid->GetFirstVisLine() + CSV_PREVIEW_LINES;
+ const OUString* pString = pTextLines;
+ for( sal_Int32 nLine = mxGrid->GetFirstVisLine(); nLine < nEndLine; ++nLine, ++pString )
+ {
+ if( mbFixedMode )
+ mxGrid->ImplSetTextLineFix( nLine, *pString );
+ else
+ mxGrid->ImplSetTextLineSep( nLine, *pString, rSepChars, cTextSep, bMergeSep, bRemoveSpace );
+ }
+ mxGrid->EnableRepaint();
+}
+
+// column settings ------------------------------------------------------------
+
+void ScCsvTableBox::InitTypes(const weld::ComboBox& rListBox)
+{
+ const sal_Int32 nTypeCount = rListBox.get_count();
+ std::vector<OUString> aTypeNames( nTypeCount );
+ for( sal_Int32 nIndex = 0; nIndex < nTypeCount; ++nIndex )
+ aTypeNames[ nIndex ] = rListBox.get_text( nIndex );
+ mxGrid->SetTypeNames( std::move(aTypeNames) );
+}
+
+void ScCsvTableBox::FillColumnData( ScAsciiOptions& rOptions ) const
+{
+ if( mbFixedMode )
+ mxGrid->FillColumnDataFix( rOptions );
+ else
+ mxGrid->FillColumnDataSep( rOptions );
+}
+
+// event handling -------------------------------------------------------------
+
+IMPL_LINK( ScCsvTableBox, CsvCmdHdl, ScCsvControl&, rCtrl, void )
+{
+ const ScCsvCmd& rCmd = rCtrl.GetCmd();
+ ScCsvCmdType eType = rCmd.GetType();
+ sal_Int32 nParam1 = rCmd.GetParam1();
+ sal_Int32 nParam2 = rCmd.GetParam2();
+
+ bool bFound = true;
+ switch( eType )
+ {
+ case CSVCMD_REPAINT:
+ if( !mxGrid->IsNoRepaint() )
+ {
+ mxGrid->Invalidate();
+ mxRuler->Invalidate();
+ InitHScrollBar();
+ InitVScrollBar();
+ }
+ break;
+ case CSVCMD_MAKEPOSVISIBLE:
+ MakePosVisible( nParam1 );
+ break;
+
+ case CSVCMD_NEWCELLTEXTS:
+ if( mbFixedMode )
+ mxGrid->Execute( CSVCMD_UPDATECELLTEXTS );
+ else
+ {
+ mxGrid->DisableRepaint();
+ ScCsvColStateVec aStates( mxGrid->GetColumnStates() );
+ sal_Int32 nPos = mxGrid->GetFirstVisPos();
+ mxGrid->Execute( CSVCMD_SETPOSCOUNT, 1 );
+ mxGrid->Execute( CSVCMD_UPDATECELLTEXTS );
+ mxGrid->Execute( CSVCMD_SETPOSOFFSET, nPos );
+ mxGrid->SetColumnStates( std::move(aStates) );
+ mxGrid->EnableRepaint();
+ }
+ break;
+ case CSVCMD_UPDATECELLTEXTS:
+ maUpdateTextHdl.Call( *this );
+ break;
+ case CSVCMD_SETCOLUMNTYPE:
+ mxGrid->SetSelColumnType( nParam1 );
+ break;
+ case CSVCMD_EXPORTCOLUMNTYPE:
+ maColTypeHdl.Call( *this );
+ break;
+ case CSVCMD_SETFIRSTIMPORTLINE:
+ mxGrid->SetFirstImportedLine( nParam1 );
+ break;
+
+ case CSVCMD_INSERTSPLIT:
+ OSL_ENSURE( mbFixedMode, "ScCsvTableBox::CsvCmdHdl::InsertSplit - invalid call" );
+ if( mxRuler->GetSplitCount() + 1 < sal::static_int_cast<sal_uInt32>(CSV_MAXCOLCOUNT) )
+ {
+ mxRuler->InsertSplit( nParam1 );
+ mxGrid->InsertSplit( nParam1 );
+ }
+ break;
+ case CSVCMD_REMOVESPLIT:
+ OSL_ENSURE( mbFixedMode, "ScCsvTableBox::CsvCmdHdl::RemoveSplit - invalid call" );
+ mxRuler->RemoveSplit( nParam1 );
+ mxGrid->RemoveSplit( nParam1 );
+ break;
+ case CSVCMD_TOGGLESPLIT:
+ mxGrid->Execute( mxRuler->HasSplit( nParam1 ) ? CSVCMD_REMOVESPLIT : CSVCMD_INSERTSPLIT, nParam1 );
+ break;
+ case CSVCMD_MOVESPLIT:
+ OSL_ENSURE( mbFixedMode, "ScCsvTableBox::CsvCmdHdl::MoveSplit - invalid call" );
+ mxRuler->MoveSplit( nParam1, nParam2 );
+ mxGrid->MoveSplit( nParam1, nParam2 );
+ break;
+ case CSVCMD_REMOVEALLSPLITS:
+ OSL_ENSURE( mbFixedMode, "ScCsvTableBox::CsvCmdHdl::RemoveAllSplits - invalid call" );
+ mxRuler->RemoveAllSplits();
+ mxGrid->RemoveAllSplits();
+ break;
+ default:
+ bFound = false;
+ }
+ if( bFound )
+ return;
+
+ const ScCsvLayoutData aOldData( maData );
+ switch( eType )
+ {
+ case CSVCMD_SETPOSCOUNT:
+ maData.mnPosCount = std::max( nParam1, sal_Int32( 1 ) );
+ ImplSetPosOffset( mxGrid->GetFirstVisPos() );
+ break;
+ case CSVCMD_SETPOSOFFSET:
+ ImplSetPosOffset( nParam1 );
+ break;
+ case CSVCMD_SETHDRWIDTH:
+ maData.mnHdrWidth = std::max( nParam1, sal_Int32( 0 ) );
+ ImplSetPosOffset( mxGrid->GetFirstVisPos() );
+ break;
+ case CSVCMD_SETCHARWIDTH:
+ maData.mnCharWidth = std::max( nParam1, sal_Int32( 1 ) );
+ ImplSetPosOffset( mxGrid->GetFirstVisPos() );
+ break;
+ case CSVCMD_SETLINECOUNT:
+ maData.mnLineCount = std::max( nParam1, sal_Int32( 1 ) );
+ ImplSetLineOffset( mxGrid->GetFirstVisLine() );
+ break;
+ case CSVCMD_SETLINEOFFSET:
+ ImplSetLineOffset( nParam1 );
+ break;
+ case CSVCMD_SETHDRHEIGHT:
+ maData.mnHdrHeight = std::max( nParam1, sal_Int32( 0 ) );
+ ImplSetLineOffset( mxGrid->GetFirstVisLine() );
+ break;
+ case CSVCMD_SETLINEHEIGHT:
+ maData.mnLineHeight = std::max( nParam1, sal_Int32( 1 ) );
+ ImplSetLineOffset( mxGrid->GetFirstVisLine() );
+ break;
+ case CSVCMD_MOVERULERCURSOR:
+ maData.mnPosCursor = mxGrid->IsVisibleSplitPos( nParam1 ) ? nParam1 : CSV_POS_INVALID;
+ break;
+ case CSVCMD_MOVEGRIDCURSOR:
+ maData.mnColCursor = ((0 <= nParam1) && (nParam1 < mxGrid->GetPosCount())) ? nParam1 : CSV_POS_INVALID;
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+
+ if( maData != aOldData )
+ {
+ mxGrid->DisableRepaint();
+ mxRuler->ApplyLayout( aOldData );
+ mxGrid->ApplyLayout( aOldData );
+ mxGrid->EnableRepaint();
+ }
+}
+
+IMPL_LINK(ScCsvTableBox, HScrollHdl, weld::ScrolledWindow&, rScroll, void)
+{
+ int nLower = 0;
+ int nValue = rScroll.hadjustment_get_value();
+ int nUpper = mxGrid->GetPosCount() + 2;
+ int nPageSize = mxGrid->GetVisPosCount();
+
+ // Undo scrollbar RTL
+ if (AllSettings::GetLayoutRTL())
+ nValue = nUpper - (nValue - nLower + nPageSize);
+
+ mxGrid->Execute(CSVCMD_SETPOSOFFSET, nValue);
+ maEndScrollIdle.Start();
+}
+
+IMPL_LINK(ScCsvTableBox, VScrollHdl, weld::ScrolledWindow&, rScroll, void)
+{
+ mxGrid->Execute(CSVCMD_SETLINEOFFSET, rScroll.vadjustment_get_value());
+}
+
+IMPL_LINK_NOARG(ScCsvTableBox, ScrollEndHdl, Timer*, void)
+{
+ if( mxGrid->GetRulerCursorPos() != CSV_POS_INVALID )
+ mxGrid->Execute( CSVCMD_MOVERULERCURSOR, mxRuler->GetNoScrollPos( mxGrid->GetRulerCursorPos() ) );
+ if( mxGrid->GetGridCursorPos() != CSV_POS_INVALID )
+ mxGrid->Execute( CSVCMD_MOVEGRIDCURSOR, mxGrid->GetNoScrollCol( mxGrid->GetGridCursorPos() ) );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/dapidata.cxx b/sc/source/ui/dbgui/dapidata.cxx
new file mode 100644
index 000000000..198d82783
--- /dev/null
+++ b/sc/source/ui/dbgui/dapidata.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/.
+ *
+ * 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 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <comphelper/processfactory.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <com/sun/star/sheet/DataImportMode.hpp>
+#include <com/sun/star/sdbcx/XTablesSupplier.hpp>
+#include <com/sun/star/sdb/DatabaseContext.hpp>
+#include <com/sun/star/sdb/XQueriesSupplier.hpp>
+#include <com/sun/star/sdb/XCompletedConnection.hpp>
+#include <com/sun/star/task/InteractionHandler.hpp>
+
+using namespace com::sun::star;
+
+#include <dapidata.hxx>
+#include <dpsdbtab.hxx>
+
+// entries in the "type" ListBox
+#define DP_TYPELIST_TABLE 0
+#define DP_TYPELIST_QUERY 1
+#define DP_TYPELIST_SQLNAT 3
+
+ScDataPilotDatabaseDlg::ScDataPilotDatabaseDlg(weld::Window* pParent)
+ : GenericDialogController(pParent, "modules/scalc/ui/selectdatasource.ui", "SelectDataSourceDialog")
+ , m_xLbDatabase(m_xBuilder->weld_combo_box("database"))
+ , m_xCbObject(m_xBuilder->weld_combo_box("datasource"))
+ , m_xLbType(m_xBuilder->weld_combo_box("type"))
+{
+ weld::WaitObject aWait(pParent); // initializing the database service the first time takes a while
+
+ try
+ {
+ // get database names
+
+ uno::Reference<sdb::XDatabaseContext> xContext = sdb::DatabaseContext::create(
+ comphelper::getProcessComponentContext() );
+ const uno::Sequence<OUString> aNames = xContext->getElementNames();
+ for( const OUString& aName : aNames )
+ {
+ m_xLbDatabase->append_text(aName);
+ }
+ }
+ catch(uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "sc", "exception in database");
+ }
+
+ m_xLbDatabase->set_active(0);
+ m_xLbType->set_active(0);
+
+ FillObjects();
+
+ m_xLbDatabase->connect_changed( LINK( this, ScDataPilotDatabaseDlg, SelectHdl ) );
+ m_xLbType->connect_changed( LINK( this, ScDataPilotDatabaseDlg, SelectHdl ) );
+}
+
+ScDataPilotDatabaseDlg::~ScDataPilotDatabaseDlg()
+{
+}
+
+void ScDataPilotDatabaseDlg::GetValues( ScImportSourceDesc& rDesc )
+{
+ const sal_Int32 nSelect = m_xLbType->get_active();
+
+ rDesc.aDBName = m_xLbDatabase->get_active_text();
+ rDesc.aObject = m_xCbObject->get_active_text();
+
+ if (rDesc.aDBName.isEmpty() || rDesc.aObject.isEmpty())
+ rDesc.nType = sheet::DataImportMode_NONE;
+ else if ( nSelect == DP_TYPELIST_TABLE )
+ rDesc.nType = sheet::DataImportMode_TABLE;
+ else if ( nSelect == DP_TYPELIST_QUERY )
+ rDesc.nType = sheet::DataImportMode_QUERY;
+ else
+ rDesc.nType = sheet::DataImportMode_SQL;
+
+ rDesc.bNative = ( nSelect == DP_TYPELIST_SQLNAT );
+}
+
+IMPL_LINK_NOARG(ScDataPilotDatabaseDlg, SelectHdl, weld::ComboBox&, void)
+{
+ FillObjects();
+}
+
+void ScDataPilotDatabaseDlg::FillObjects()
+{
+ m_xCbObject->clear();
+
+ OUString aDatabaseName = m_xLbDatabase->get_active_text();
+ if (aDatabaseName.isEmpty())
+ return;
+
+ const int nSelect = m_xLbType->get_active();
+ if ( nSelect > DP_TYPELIST_QUERY )
+ return; // only tables and queries
+
+ try
+ {
+ // open connection (for tables or queries)
+
+ uno::Reference<sdb::XDatabaseContext> xContext = sdb::DatabaseContext::create(
+ comphelper::getProcessComponentContext() );
+
+ uno::Any aSourceAny = xContext->getByName( aDatabaseName );
+ uno::Reference<sdb::XCompletedConnection> xSource(aSourceAny, uno::UNO_QUERY);
+ if ( !xSource.is() ) return;
+
+ uno::Reference<task::XInteractionHandler> xHandler(
+ task::InteractionHandler::createWithParent(comphelper::getProcessComponentContext(), nullptr),
+ uno::UNO_QUERY_THROW);
+
+ uno::Reference<sdbc::XConnection> xConnection = xSource->connectWithCompletion( xHandler );
+
+ uno::Reference<container::XNameAccess> xItems;
+ if ( nSelect == DP_TYPELIST_TABLE )
+ {
+ // get all tables
+
+ uno::Reference<sdbcx::XTablesSupplier> xTablesSupp( xConnection, uno::UNO_QUERY );
+ if ( !xTablesSupp.is() ) return;
+
+ xItems = xTablesSupp->getTables();
+ }
+ else
+ {
+ // get all queries
+
+ uno::Reference<sdb::XQueriesSupplier> xQueriesSupp( xConnection, uno::UNO_QUERY );
+ if ( !xQueriesSupp.is() ) return;
+
+ xItems = xQueriesSupp->getQueries();
+ }
+
+ if ( !xItems.is() ) return;
+
+ // fill list
+ const uno::Sequence<OUString> aNames = xItems->getElementNames();
+ for( const OUString& aName : aNames )
+ {
+ m_xCbObject->append_text(aName);
+ }
+ }
+ catch(uno::Exception&)
+ {
+ // this may happen if an invalid database is selected -> no DBG_ERROR
+ TOOLS_WARN_EXCEPTION( "sc", "exception in database");
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/dapitype.cxx b/sc/source/ui/dbgui/dapitype.cxx
new file mode 100644
index 000000000..9843a2bb9
--- /dev/null
+++ b/sc/source/ui/dbgui/dapitype.cxx
@@ -0,0 +1,153 @@
+/* -*- 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 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <dapitype.hxx>
+#include <comphelper/lok.hxx>
+
+using namespace com::sun::star;
+
+ScDataPilotSourceTypeDlg::ScDataPilotSourceTypeDlg(weld::Window* pParent, bool bEnableExternal)
+ : GenericDialogController(pParent, "modules/scalc/ui/selectsource.ui", "SelectSourceDialog")
+ , m_xBtnSelection(m_xBuilder->weld_radio_button("selection"))
+ , m_xBtnNamedRange(m_xBuilder->weld_radio_button("namedrange"))
+ , m_xBtnDatabase(m_xBuilder->weld_radio_button("database"))
+ , m_xBtnExternal(m_xBuilder->weld_radio_button("external"))
+ , m_xLbNamedRange(m_xBuilder->weld_combo_box("rangelb"))
+ , m_xBtnOk(m_xBuilder->weld_button("ok")) // for LOK jsdialog
+ , m_xBtnCancel(m_xBuilder->weld_button("cancel")) // for LOK jsdialog
+{
+ m_xBtnSelection->connect_toggled( LINK(this, ScDataPilotSourceTypeDlg, RadioClickHdl) );
+ m_xBtnNamedRange->connect_toggled( LINK(this, ScDataPilotSourceTypeDlg, RadioClickHdl) );
+ m_xBtnDatabase->connect_toggled( LINK(this, ScDataPilotSourceTypeDlg, RadioClickHdl) );
+ m_xBtnExternal->connect_toggled( LINK(this, ScDataPilotSourceTypeDlg, RadioClickHdl) );
+
+ m_xBtnOk->connect_clicked( LINK(this, ScDataPilotSourceTypeDlg, ResponseHdl ) );
+ m_xBtnCancel->connect_clicked( LINK(this, ScDataPilotSourceTypeDlg, ResponseHdl ) );
+
+ if (!bEnableExternal)
+ m_xBtnExternal->set_sensitive(false);
+
+ m_xBtnSelection->set_active(true);
+
+ // Disabled unless at least one named range exists.
+ m_xLbNamedRange->set_sensitive(false);
+ m_xBtnNamedRange->set_sensitive(false);
+
+ // Intentionally hide this button to see if anyone complains.
+ m_xBtnExternal->hide();
+
+ if (comphelper::LibreOfficeKit::isActive())
+ m_xBtnDatabase->hide();
+}
+
+IMPL_LINK(ScDataPilotSourceTypeDlg, ResponseHdl, weld::Button&, rButton, void)
+{
+ if (&rButton == m_xBtnOk.get())
+ m_xDialog->response(RET_OK);
+ else
+ m_xDialog->response(RET_CANCEL);
+}
+
+ScDataPilotSourceTypeDlg::~ScDataPilotSourceTypeDlg()
+{
+}
+
+bool ScDataPilotSourceTypeDlg::IsDatabase() const
+{
+ return m_xBtnDatabase->get_active();
+}
+
+bool ScDataPilotSourceTypeDlg::IsExternal() const
+{
+ return m_xBtnExternal->get_active();
+}
+
+bool ScDataPilotSourceTypeDlg::IsNamedRange() const
+{
+ return m_xBtnNamedRange->get_active();
+}
+
+OUString ScDataPilotSourceTypeDlg::GetSelectedNamedRange() const
+{
+ return m_xLbNamedRange->get_active_text();
+}
+
+void ScDataPilotSourceTypeDlg::AppendNamedRange(const OUString& rName)
+{
+ m_xLbNamedRange->append_text(rName);
+ if (m_xLbNamedRange->get_count() == 1)
+ {
+ // Select position 0 only for the first time.
+ m_xLbNamedRange->set_active(0);
+ m_xBtnNamedRange->set_sensitive(true);
+ }
+}
+
+IMPL_LINK_NOARG(ScDataPilotSourceTypeDlg, RadioClickHdl, weld::Toggleable&, void)
+{
+ m_xLbNamedRange->set_sensitive(m_xBtnNamedRange->get_active());
+}
+
+ScDataPilotServiceDlg::ScDataPilotServiceDlg(weld::Window* pParent, const std::vector<OUString>& rServices)
+ : GenericDialogController(pParent, "modules/scalc/ui/dapiservicedialog.ui", "DapiserviceDialog")
+ , m_xLbService(m_xBuilder->weld_combo_box("service"))
+ , m_xEdSource(m_xBuilder->weld_entry("source"))
+ , m_xEdName(m_xBuilder->weld_entry("name"))
+ , m_xEdUser(m_xBuilder->weld_entry("user"))
+ , m_xEdPasswd(m_xBuilder->weld_entry("password"))
+{
+ for (const OUString& aName : rServices)
+ {
+ m_xLbService->append_text(aName);
+ }
+ m_xLbService->set_active(0);
+}
+
+ScDataPilotServiceDlg::~ScDataPilotServiceDlg()
+{
+}
+
+OUString ScDataPilotServiceDlg::GetServiceName() const
+{
+ return m_xLbService->get_active_text();
+}
+
+OUString ScDataPilotServiceDlg::GetParSource() const
+{
+ return m_xEdSource->get_text();
+}
+
+OUString ScDataPilotServiceDlg::GetParName() const
+{
+ return m_xEdName->get_text();
+}
+
+OUString ScDataPilotServiceDlg::GetParUser() const
+{
+ return m_xEdUser->get_text();
+}
+
+OUString ScDataPilotServiceDlg::GetParPass() const
+{
+ return m_xEdPasswd->get_text();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/dbnamdlg.cxx b/sc/source/ui/dbgui/dbnamdlg.cxx
new file mode 100644
index 000000000..a2ac72209
--- /dev/null
+++ b/sc/source/ui/dbgui/dbnamdlg.cxx
@@ -0,0 +1,645 @@
+/* -*- 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 <sal/config.h>
+
+#include <cassert>
+
+#include <comphelper/string.hxx>
+#include <unotools/charclass.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <reffact.hxx>
+#include <document.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <rangenam.hxx>
+#include <globalnames.hxx>
+#include <dbnamdlg.hxx>
+#include <dbdocfun.hxx>
+
+namespace {
+
+class DBSaveData;
+
+}
+
+static std::unique_ptr<DBSaveData> xSaveObj;
+
+namespace
+{
+ void ERRORBOX(weld::Window* pParent, const OUString& rString)
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pParent,
+ VclMessageType::Warning, VclButtonsType::Ok,
+ rString));
+ xBox->run();
+ }
+
+
+class DBSaveData
+{
+public:
+ DBSaveData( formula::RefEdit& rEd, weld::CheckButton& rHdr, weld::CheckButton& rTot, weld::CheckButton& rSize, weld::CheckButton& rFmt,
+ weld::CheckButton& rStrip, ScRange& rArea )
+ : rEdAssign(rEd)
+ , rBtnHeader(rHdr)
+ , rBtnTotals(rTot)
+ , rBtnSize(rSize)
+ , rBtnFormat(rFmt)
+ , rBtnStrip(rStrip)
+ , rCurArea(rArea)
+ , bHeader(false)
+ , bTotals(false)
+ , bSize(false)
+ , bFormat(false)
+ , bStrip(false)
+ , bDirty(false)
+ {
+ }
+ void Save();
+ void Restore();
+
+private:
+ formula::RefEdit& rEdAssign;
+ weld::CheckButton& rBtnHeader;
+ weld::CheckButton& rBtnTotals;
+ weld::CheckButton& rBtnSize;
+ weld::CheckButton& rBtnFormat;
+ weld::CheckButton& rBtnStrip;
+ ScRange& rCurArea;
+ OUString aStr;
+ ScRange aArea;
+ bool bHeader:1;
+ bool bTotals:1;
+ bool bSize:1;
+ bool bFormat:1;
+ bool bStrip:1;
+ bool bDirty:1;
+};
+
+}
+
+void DBSaveData::Save()
+{
+ aArea = rCurArea;
+ aStr = rEdAssign.GetText();
+ bHeader = rBtnHeader.get_active();
+ bTotals = rBtnTotals.get_active();
+ bSize = rBtnSize.get_active();
+ bFormat = rBtnFormat.get_active();
+ bStrip = rBtnStrip.get_active();
+ bDirty = true;
+}
+
+void DBSaveData::Restore()
+{
+ if ( bDirty )
+ {
+ rCurArea = aArea;
+ rEdAssign.SetText( aStr );
+ rBtnHeader.set_active ( bHeader );
+ rBtnTotals.set_active ( bTotals );
+ rBtnSize.set_active ( bSize );
+ rBtnFormat.set_active ( bFormat );
+ rBtnStrip.set_active ( bStrip );
+ bDirty = false;
+ }
+}
+
+
+ScDbNameDlg::ScDbNameDlg(SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent,
+ ScViewData& rViewData)
+ : ScAnyRefDlgController(pB, pCW, pParent,
+ "modules/scalc/ui/definedatabaserangedialog.ui", "DefineDatabaseRangeDialog")
+ , m_rViewData(rViewData)
+ , rDoc(rViewData.GetDocument())
+ , bRefInputMode(false)
+ , aAddrDetails(rDoc.GetAddressConvention(), 0, 0)
+ , aLocalDbCol(*(rDoc.GetDBCollection()))
+ , m_xEdName(m_xBuilder->weld_entry_tree_view("entrygrid", "entry", "entry-list"))
+ , m_xAssignFrame(m_xBuilder->weld_frame("RangeFrame"))
+ , m_xEdAssign(new formula::RefEdit(m_xBuilder->weld_entry("assign")))
+ , m_xRbAssign(new formula::RefButton(m_xBuilder->weld_button("assignrb")))
+ , m_xOptions(m_xBuilder->weld_widget("Options"))
+ , m_xBtnHeader(m_xBuilder->weld_check_button("ContainsColumnLabels"))
+ , m_xBtnTotals(m_xBuilder->weld_check_button("ContainsTotalsRow"))
+ , m_xBtnDoSize(m_xBuilder->weld_check_button("InsertOrDeleteCells"))
+ , m_xBtnKeepFmt(m_xBuilder->weld_check_button("KeepFormatting"))
+ , m_xBtnStripData(m_xBuilder->weld_check_button("DontSaveImportedData"))
+ , m_xFTSource(m_xBuilder->weld_label("Source"))
+ , m_xFTOperations(m_xBuilder->weld_label("Operations"))
+ , m_xBtnOk(m_xBuilder->weld_button("ok"))
+ , m_xBtnCancel(m_xBuilder->weld_button("cancel"))
+ , m_xBtnAdd(m_xBuilder->weld_button("add"))
+ , m_xBtnRemove(m_xBuilder->weld_button("delete"))
+ , m_xModifyPB(m_xBuilder->weld_button("modify"))
+ , m_xInvalidFT(m_xBuilder->weld_label("invalid"))
+ , m_xFrameLabel(m_xAssignFrame->weld_label_widget())
+ , m_xExpander(m_xBuilder->weld_expander("more"))
+{
+ m_xEdName->set_height_request_by_rows(4);
+ m_xEdAssign->SetReferences(this, m_xFrameLabel.get());
+ m_xRbAssign->SetReferences(this, m_xEdAssign.get());
+ aStrAdd = m_xBtnAdd->get_label();
+ aStrModify = m_xModifyPB->get_label();
+ aStrInvalid = m_xInvalidFT->get_label();
+
+ // so that the strings in the resource can stay with fixed texts:
+ aStrSource = m_xFTSource->get_label();
+ aStrOperations = m_xFTOperations->get_label();
+
+ xSaveObj.reset(new DBSaveData( *m_xEdAssign, *m_xBtnHeader, *m_xBtnTotals,
+ *m_xBtnDoSize, *m_xBtnKeepFmt, *m_xBtnStripData, theCurArea ));
+ Init();
+}
+
+ScDbNameDlg::~ScDbNameDlg()
+{
+ xSaveObj.reset();
+}
+
+void ScDbNameDlg::Init()
+{
+ m_xBtnHeader->set_active(true); // Default: with column headers
+ m_xBtnTotals->set_active( false ); // Default: without totals row
+ m_xBtnDoSize->set_active(true);
+ m_xBtnKeepFmt->set_active(true);
+
+ m_xBtnOk->connect_clicked ( LINK( this, ScDbNameDlg, OkBtnHdl ) );
+ m_xBtnCancel->connect_clicked ( LINK( this, ScDbNameDlg, CancelBtnHdl ) );
+ m_xBtnAdd->connect_clicked ( LINK( this, ScDbNameDlg, AddBtnHdl ) );
+ m_xBtnRemove->connect_clicked ( LINK( this, ScDbNameDlg, RemoveBtnHdl ) );
+ m_xEdName->connect_changed( LINK( this, ScDbNameDlg, NameModifyHdl ) );
+ m_xEdAssign->SetModifyHdl ( LINK( this, ScDbNameDlg, AssModifyHdl ) );
+ UpdateNames();
+
+ OUString theAreaStr;
+
+ SCCOL nStartCol = 0;
+ SCROW nStartRow = 0;
+ SCTAB nStartTab = 0;
+ SCCOL nEndCol = 0;
+ SCROW nEndRow = 0;
+ SCTAB nEndTab = 0;
+
+ ScDBCollection* pDBColl = rDoc.GetDBCollection();
+
+ m_rViewData.GetSimpleArea( nStartCol, nStartRow, nStartTab,
+ nEndCol, nEndRow, nEndTab );
+
+ theCurArea = ScRange( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab);
+
+ theAreaStr = theCurArea.Format(rDoc, ScRefFlags::RANGE_ABS_3D, aAddrDetails);
+
+ if ( pDBColl )
+ {
+ // determine if the defined DB area has been marked:
+ ScDBData* pDBData = pDBColl->GetDBAtCursor( nStartCol, nStartRow, nStartTab, ScDBDataPortion::TOP_LEFT );
+ if ( pDBData )
+ {
+ ScAddress& rStart = theCurArea.aStart;
+ ScAddress& rEnd = theCurArea.aEnd;
+ SCCOL nCol1;
+ SCCOL nCol2;
+ SCROW nRow1;
+ SCROW nRow2;
+ SCTAB nTab;
+
+ pDBData->GetArea( nTab, nCol1, nRow1, nCol2, nRow2 );
+
+ if ( (rStart.Tab() == nTab)
+ && (rStart.Col() == nCol1) && (rStart.Row() == nRow1)
+ && (rEnd.Col() == nCol2) && (rEnd.Row() == nRow2 ) )
+ {
+ OUString aDBName = pDBData->GetName();
+ if ( aDBName != STR_DB_LOCAL_NONAME )
+ m_xEdName->set_entry_text(aDBName);
+
+ m_xBtnHeader->set_active( pDBData->HasHeader() );
+ m_xBtnTotals->set_active( pDBData->HasTotals() );
+ m_xBtnDoSize->set_active( pDBData->IsDoSize() );
+ m_xBtnKeepFmt->set_active( pDBData->IsKeepFmt() );
+ m_xBtnStripData->set_active( pDBData->IsStripData() );
+ SetInfoStrings( pDBData );
+ }
+ }
+ }
+
+ m_xEdAssign->SetText( theAreaStr );
+ m_xEdName->grab_focus();
+ bSaved = true;
+ xSaveObj->Save();
+ NameModifyHdl( *m_xEdName );
+ bInvalid = false;
+}
+
+void ScDbNameDlg::SetInfoStrings( const ScDBData* pDBData )
+{
+ OUStringBuffer aBuf;
+ aBuf.append(aStrSource);
+ if (pDBData)
+ {
+ aBuf.append(' ');
+ aBuf.append(pDBData->GetSourceString());
+ }
+ m_xFTSource->set_label(aBuf.makeStringAndClear());
+
+ aBuf.append(aStrOperations);
+ if (pDBData)
+ {
+ aBuf.append(' ');
+ aBuf.append(pDBData->GetOperations());
+ }
+ m_xFTOperations->set_label(aBuf.makeStringAndClear());
+}
+
+// Transfer of a table area selected with the mouse, which is then displayed
+// as a new selection in the reference window.
+
+void ScDbNameDlg::SetReference( const ScRange& rRef, ScDocument& rDocP )
+{
+ if (!m_xEdAssign->GetWidget()->get_sensitive())
+ return;
+
+ if ( rRef.aStart != rRef.aEnd )
+ RefInputStart(m_xEdAssign.get());
+
+ theCurArea = rRef;
+
+ OUString aRefStr(theCurArea.Format(rDocP, ScRefFlags::RANGE_ABS_3D, aAddrDetails));
+ m_xEdAssign->SetRefString( aRefStr );
+ m_xOptions->set_sensitive(true);
+ m_xBtnAdd->set_sensitive(true);
+ bSaved = true;
+ xSaveObj->Save();
+}
+
+void ScDbNameDlg::Close()
+{
+ DoClose( ScDbNameDlgWrapper::GetChildWindowId() );
+}
+
+void ScDbNameDlg::SetActive()
+{
+ m_xEdAssign->GrabFocus();
+
+ // No NameModifyHdl, because otherwise areas can not be changed
+ // (the old content would be displayed again after the reference selection is pulled)
+ // (the selected DB name has not changed either)
+
+ RefInputDone();
+}
+
+void ScDbNameDlg::UpdateNames()
+{
+ typedef ScDBCollection::NamedDBs DBsType;
+
+ const DBsType& rDBs = aLocalDbCol.getNamedDBs();
+
+ m_xEdName->freeze();
+
+ m_xEdName->clear();
+ m_xEdAssign->SetText( OUString() );
+
+ if (!rDBs.empty())
+ {
+ for (const auto& rxDB : rDBs)
+ m_xEdName->append_text(rxDB->GetName());
+ }
+ else
+ {
+ m_xBtnAdd->set_label( aStrAdd );
+ m_xBtnAdd->set_sensitive(false);
+ m_xBtnRemove->set_sensitive(false);
+ }
+
+ m_xEdName->thaw();
+}
+
+void ScDbNameDlg::UpdateDBData( const OUString& rStrName )
+{
+
+ const ScDBData* pData = aLocalDbCol.getNamedDBs().findByUpperName(ScGlobal::getCharClass().uppercase(rStrName));
+
+ if ( pData )
+ {
+ SCCOL nColStart = 0;
+ SCROW nRowStart = 0;
+ SCCOL nColEnd = 0;
+ SCROW nRowEnd = 0;
+ SCTAB nTab = 0;
+
+ pData->GetArea( nTab, nColStart, nRowStart, nColEnd, nRowEnd );
+ theCurArea = ScRange( ScAddress( nColStart, nRowStart, nTab ),
+ ScAddress( nColEnd, nRowEnd, nTab ) );
+ OUString theArea(theCurArea.Format(rDoc, ScRefFlags::RANGE_ABS_3D, aAddrDetails));
+ m_xEdAssign->SetText( theArea );
+ m_xBtnAdd->set_label( aStrModify );
+ m_xBtnHeader->set_active( pData->HasHeader() );
+ m_xBtnTotals->set_active( pData->HasTotals() );
+ m_xBtnDoSize->set_active( pData->IsDoSize() );
+ m_xBtnKeepFmt->set_active( pData->IsKeepFmt() );
+ m_xBtnStripData->set_active( pData->IsStripData() );
+ SetInfoStrings( pData );
+ }
+
+ m_xBtnAdd->set_label( aStrModify );
+ m_xBtnAdd->set_sensitive(true);
+ m_xBtnRemove->set_sensitive(true);
+ m_xOptions->set_sensitive(true);
+}
+
+bool ScDbNameDlg::IsRefInputMode() const
+{
+ return bRefInputMode;
+}
+
+// Handler:
+
+IMPL_LINK_NOARG(ScDbNameDlg, OkBtnHdl, weld::Button&, void)
+{
+ bInvalid = false;
+ AddBtnHdl(*m_xBtnAdd);
+
+ // Pass the changes and the remove list to the view: both are
+ // transferred as a reference only, so that no dead memory can
+ // be created at this point:
+ if (!bInvalid)
+ {
+ ScDBDocFunc aFunc(*m_rViewData.GetDocShell());
+ aFunc.ModifyAllDBData(aLocalDbCol, aRemoveList);
+ response(RET_OK);
+ }
+}
+
+IMPL_LINK_NOARG(ScDbNameDlg, CancelBtnHdl, weld::Button&, void)
+{
+ response(RET_CANCEL);
+}
+
+IMPL_LINK_NOARG(ScDbNameDlg, AddBtnHdl, weld::Button&, void)
+{
+ OUString aNewName = comphelper::string::strip(m_xEdName->get_active_text(), ' ');
+ OUString aNewArea = m_xEdAssign->GetText();
+
+ if ( aNewName.isEmpty() || aNewArea.isEmpty() )
+ return;
+
+ if (ScRangeData::IsNameValid(aNewName, rDoc) == ScRangeData::IsNameValidType::NAME_VALID
+ && aNewName != STR_DB_LOCAL_NONAME)
+ {
+ // because editing can be done now, parsing is needed first
+ ScRange aTmpRange;
+ OUString aText = m_xEdAssign->GetText();
+ if ( aTmpRange.ParseAny( aText, rDoc, aAddrDetails ) & ScRefFlags::VALID )
+ {
+ theCurArea = aTmpRange;
+ ScAddress aStart = theCurArea.aStart;
+ ScAddress aEnd = theCurArea.aEnd;
+
+ ScDBData* pOldEntry = aLocalDbCol.getNamedDBs().findByUpperName(ScGlobal::getCharClass().uppercase(aNewName));
+ if (pOldEntry)
+ {
+ // modify area
+
+ pOldEntry->MoveTo( aStart.Tab(), aStart.Col(), aStart.Row(),
+ aEnd.Col(), aEnd.Row() );
+ pOldEntry->SetByRow( true );
+ pOldEntry->SetHeader( m_xBtnHeader->get_active() );
+ pOldEntry->SetTotals( m_xBtnTotals->get_active() );
+ pOldEntry->SetDoSize( m_xBtnDoSize->get_active() );
+ pOldEntry->SetKeepFmt( m_xBtnKeepFmt->get_active() );
+ pOldEntry->SetStripData( m_xBtnStripData->get_active() );
+ }
+ else
+ {
+ // insert new area
+
+ std::unique_ptr<ScDBData> pNewEntry(new ScDBData( aNewName, aStart.Tab(),
+ aStart.Col(), aStart.Row(),
+ aEnd.Col(), aEnd.Row(),
+ true, m_xBtnHeader->get_active(),
+ m_xBtnTotals->get_active() ));
+ pNewEntry->SetDoSize( m_xBtnDoSize->get_active() );
+ pNewEntry->SetKeepFmt( m_xBtnKeepFmt->get_active() );
+ pNewEntry->SetStripData( m_xBtnStripData->get_active() );
+
+ bool ins = aLocalDbCol.getNamedDBs().insert(std::move(pNewEntry));
+ assert(ins); (void)ins;
+ }
+
+ UpdateNames();
+
+ m_xEdName->set_entry_text( OUString() );
+ m_xEdName->grab_focus();
+ m_xBtnAdd->set_label( aStrAdd );
+ m_xBtnAdd->set_sensitive(false);
+ m_xBtnRemove->set_sensitive(false);
+ m_xEdAssign->SetText( OUString() );
+ m_xBtnHeader->set_active(true); // Default: with column headers
+ m_xBtnTotals->set_active( false ); // Default: without totals row
+ m_xBtnDoSize->set_active( false );
+ m_xBtnKeepFmt->set_active( false );
+ m_xBtnStripData->set_active( false );
+ SetInfoStrings( nullptr ); // empty
+ theCurArea = ScRange();
+ bSaved = true;
+ xSaveObj->Save();
+ NameModifyHdl( *m_xEdName );
+ }
+ else
+ {
+ ERRORBOX(m_xDialog.get(), aStrInvalid);
+ m_xEdAssign->SelectAll();
+ m_xEdAssign->GrabFocus();
+ bInvalid = true;
+ }
+ }
+ else
+ {
+ ERRORBOX(m_xDialog.get(), ScResId(STR_INVALIDNAME));
+ m_xEdName->select_entry_region(0, -1);
+ m_xEdName->grab_focus();
+ bInvalid = true;
+ }
+}
+
+namespace {
+
+class FindByName
+{
+ const OUString& mrName;
+public:
+ explicit FindByName(const OUString& rName) : mrName(rName) {}
+ bool operator() (std::unique_ptr<ScDBData> const& p) const
+ {
+ return p->GetName() == mrName;
+ }
+};
+
+}
+
+IMPL_LINK_NOARG(ScDbNameDlg, RemoveBtnHdl, weld::Button&, void)
+{
+ OUString aStrEntry = m_xEdName->get_active_text();
+ ScDBCollection::NamedDBs& rDBs = aLocalDbCol.getNamedDBs();
+ ScDBCollection::NamedDBs::iterator itr =
+ ::std::find_if(rDBs.begin(), rDBs.end(), FindByName(aStrEntry));
+
+ if (itr == rDBs.end())
+ return;
+
+ OUString aStrDelMsg = ScResId( STR_QUERY_DELENTRY );
+ OUString sMsg{ o3tl::getToken(aStrDelMsg, 0, '#') + aStrEntry + o3tl::getToken(aStrDelMsg, 1, '#') };
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ sMsg));
+ xQueryBox->set_default_response(RET_YES);
+ if (RET_YES != xQueryBox->run())
+ return;
+
+ SCTAB nTab;
+ SCCOL nColStart, nColEnd;
+ SCROW nRowStart, nRowEnd;
+ (*itr)->GetArea( nTab, nColStart, nRowStart, nColEnd, nRowEnd );
+ aRemoveList.emplace_back( ScAddress( nColStart, nRowStart, nTab ),
+ ScAddress( nColEnd, nRowEnd, nTab ) );
+
+ rDBs.erase(itr);
+
+ UpdateNames();
+
+ m_xEdName->set_entry_text( OUString() );
+ m_xEdName->grab_focus();
+ m_xBtnAdd->set_label( aStrAdd );
+ m_xBtnAdd->set_sensitive(false);
+ m_xBtnRemove->set_sensitive(false);
+ m_xEdAssign->SetText( OUString() );
+ theCurArea = ScRange();
+ m_xBtnHeader->set_active(true); // Default: with column headers
+ m_xBtnTotals->set_active( false ); // Default: without totals row
+ m_xBtnDoSize->set_active( false );
+ m_xBtnKeepFmt->set_active( false );
+ m_xBtnStripData->set_active( false );
+ SetInfoStrings( nullptr ); // empty
+ bSaved=false;
+ xSaveObj->Restore();
+ NameModifyHdl( *m_xEdName );
+}
+
+IMPL_LINK_NOARG(ScDbNameDlg, NameModifyHdl, weld::ComboBox&, void)
+{
+ OUString theName = m_xEdName->get_active_text();
+ bool bNameFound = m_xEdName->find_text(theName) != -1;
+
+ if ( theName.isEmpty() )
+ {
+ if (m_xBtnAdd->get_label() != aStrAdd)
+ m_xBtnAdd->set_label( aStrAdd );
+ m_xBtnAdd->set_sensitive(false);
+ m_xBtnRemove->set_sensitive(false);
+ m_xAssignFrame->set_sensitive(false);
+ m_xOptions->set_sensitive(false);
+ //bSaved=sal_False;
+ //xSaveObj->Restore();
+ //@BugID 54702 enable/disable in the base class only
+ //SFX_APPWINDOW->Disable(sal_False); //! general method in ScAnyRefDlg
+ bRefInputMode = false;
+ }
+ else
+ {
+ if ( bNameFound )
+ {
+ if (m_xBtnAdd->get_label() != aStrModify)
+ m_xBtnAdd->set_label( aStrModify );
+
+ if(!bSaved)
+ {
+ bSaved = true;
+ xSaveObj->Save();
+ }
+ UpdateDBData( theName );
+ }
+ else
+ {
+ if (m_xBtnAdd->get_label() != aStrAdd)
+ m_xBtnAdd->set_label( aStrAdd );
+
+ bSaved=false;
+ xSaveObj->Restore();
+
+ if ( !m_xEdAssign->GetText().isEmpty() )
+ {
+ m_xBtnAdd->set_sensitive(true);
+ m_xOptions->set_sensitive(true);
+ }
+ else
+ {
+ m_xBtnAdd->set_sensitive(false);
+ m_xOptions->set_sensitive(false);
+ }
+ m_xBtnRemove->set_sensitive(false);
+ }
+
+ m_xAssignFrame->set_sensitive(true);
+
+ //@BugID 54702 enable/disable in the base class only
+ //SFX_APPWINDOW->set_sensitive(true);
+ bRefInputMode = true;
+ }
+}
+
+IMPL_LINK_NOARG(ScDbNameDlg, AssModifyHdl, formula::RefEdit&, void)
+{
+ // parse here for Save(), etc.
+
+ ScRange aTmpRange;
+ OUString aText = m_xEdAssign->GetText();
+ if ( aTmpRange.ParseAny( aText, rDoc, aAddrDetails ) & ScRefFlags::VALID )
+ theCurArea = aTmpRange;
+
+ if (!aText.isEmpty() && !m_xEdName->get_active_text().isEmpty())
+ {
+ m_xBtnAdd->set_sensitive(true);
+ m_xBtnHeader->set_sensitive(true);
+ m_xBtnTotals->set_sensitive(true);
+ m_xBtnDoSize->set_sensitive(true);
+ m_xBtnKeepFmt->set_sensitive(true);
+ m_xBtnStripData->set_sensitive(true);
+ m_xFTSource->set_sensitive(true);
+ m_xFTOperations->set_sensitive(true);
+ }
+ else
+ {
+ m_xBtnAdd->set_sensitive(false);
+ m_xBtnHeader->set_sensitive(false);
+ m_xBtnTotals->set_sensitive(false);
+ m_xBtnDoSize->set_sensitive(false);
+ m_xBtnKeepFmt->set_sensitive(false);
+ m_xBtnStripData->set_sensitive(false);
+ m_xFTSource->set_sensitive(false);
+ m_xFTOperations->set_sensitive(false);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/dpgroupdlg.cxx b/sc/source/ui/dbgui/dpgroupdlg.cxx
new file mode 100644
index 000000000..550695cc3
--- /dev/null
+++ b/sc/source/ui/dbgui/dpgroupdlg.cxx
@@ -0,0 +1,353 @@
+/* -*- 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 .
+ */
+
+#ifdef SC_DLLIMPLEMENTATION
+#undef SC_DLLIMPLEMENTATION
+#endif
+
+#include <dpgroupdlg.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <editfield.hxx>
+
+#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
+#include <svtools/ctrlbox.hxx>
+
+namespace {
+
+/** Date part flags in order of the list box entries. */
+const sal_Int32 spnDateParts[] =
+{
+ css::sheet::DataPilotFieldGroupBy::SECONDS,
+ css::sheet::DataPilotFieldGroupBy::MINUTES,
+ css::sheet::DataPilotFieldGroupBy::HOURS,
+ css::sheet::DataPilotFieldGroupBy::DAYS,
+ css::sheet::DataPilotFieldGroupBy::MONTHS,
+ css::sheet::DataPilotFieldGroupBy::QUARTERS,
+ css::sheet::DataPilotFieldGroupBy::YEARS
+};
+
+const TranslateId aDatePartResIds[] =
+{
+ STR_DPFIELD_GROUP_BY_SECONDS,
+ STR_DPFIELD_GROUP_BY_MINUTES,
+ STR_DPFIELD_GROUP_BY_HOURS,
+ STR_DPFIELD_GROUP_BY_DAYS,
+ STR_DPFIELD_GROUP_BY_MONTHS,
+ STR_DPFIELD_GROUP_BY_QUARTERS,
+ STR_DPFIELD_GROUP_BY_YEARS
+};
+
+} // namespace
+
+ScDPGroupEditHelper::ScDPGroupEditHelper(weld::RadioButton& rRbAuto, weld::RadioButton& rRbMan, weld::Widget& rEdValue)
+ : mrRbAuto(rRbAuto)
+ , mrRbMan(rRbMan)
+ , mrEdValue(rEdValue)
+{
+ mrRbAuto.connect_toggled( LINK( this, ScDPGroupEditHelper, ToggleHdl ) );
+ mrRbMan.connect_toggled( LINK( this, ScDPGroupEditHelper, ToggleHdl ) );
+}
+
+bool ScDPGroupEditHelper::IsAuto() const
+{
+ return mrRbAuto.get_active();
+}
+
+double ScDPGroupEditHelper::GetValue() const
+{
+ double fValue;
+ if( !ImplGetValue( fValue ) )
+ fValue = 0.0;
+ return fValue;
+}
+
+void ScDPGroupEditHelper::SetValue( bool bAuto, double fValue )
+{
+ if( bAuto )
+ {
+ mrRbAuto.set_active(true);
+ ToggleHdl(mrRbAuto);
+ }
+ else
+ {
+ mrRbMan.set_active(true);
+ ToggleHdl(mrRbMan);
+ }
+ ImplSetValue( fValue );
+}
+
+IMPL_LINK(ScDPGroupEditHelper, ToggleHdl, weld::Toggleable&, rButton, void)
+{
+ if (!rButton.get_active())
+ return;
+
+ if (mrRbAuto.get_active())
+ {
+ // disable edit field on clicking "automatic" radio button
+ mrEdValue.set_sensitive(false);
+ }
+ else if (mrRbMan.get_active())
+ {
+ // enable and set focus to edit field on clicking "manual" radio button
+ mrEdValue.set_sensitive(true);
+ mrEdValue.grab_focus();
+ }
+}
+
+ScDPNumGroupEditHelper::ScDPNumGroupEditHelper(weld::RadioButton& rRbAuto,
+ weld::RadioButton& rRbMan, ScDoubleField& rEdValue)
+ : ScDPGroupEditHelper(rRbAuto, rRbMan, rEdValue.get_widget())
+ , mrEdValue(rEdValue)
+{
+}
+
+bool ScDPNumGroupEditHelper::ImplGetValue( double& rfValue ) const
+{
+ return mrEdValue.GetValue(rfValue);
+}
+
+void ScDPNumGroupEditHelper::ImplSetValue( double fValue )
+{
+ mrEdValue.SetValue(fValue);
+}
+
+ScDPDateGroupEditHelper::ScDPDateGroupEditHelper(weld::RadioButton& rRbAuto, weld::RadioButton& rRbMan,
+ SvtCalendarBox& rEdValue, const Date& rNullDate)
+ : ScDPGroupEditHelper(rRbAuto, rRbMan, rEdValue.get_button())
+ , mrEdValue(rEdValue)
+ , maNullDate(rNullDate)
+{
+}
+
+bool ScDPDateGroupEditHelper::ImplGetValue( double& rfValue ) const
+{
+ rfValue = mrEdValue.get_date() - maNullDate;
+ return true;
+}
+
+void ScDPDateGroupEditHelper::ImplSetValue( double fValue )
+{
+ Date aDate( maNullDate );
+ aDate.AddDays( fValue );
+ mrEdValue.set_date( aDate );
+}
+
+ScDPNumGroupDlg::ScDPNumGroupDlg(weld::Window* pParent, const ScDPNumGroupInfo& rInfo)
+ : GenericDialogController(pParent, "modules/scalc/ui/groupbynumber.ui", "PivotTableGroupByNumber")
+ , mxRbAutoStart(m_xBuilder->weld_radio_button("auto_start"))
+ , mxRbManStart(m_xBuilder->weld_radio_button("manual_start"))
+ , mxEdStart(new ScDoubleField(m_xBuilder->weld_entry("edit_start")))
+ , mxRbAutoEnd(m_xBuilder->weld_radio_button("auto_end"))
+ , mxRbManEnd(m_xBuilder->weld_radio_button("manual_end"))
+ , mxEdEnd(new ScDoubleField(m_xBuilder->weld_entry("edit_end")))
+ , mxEdBy(new ScDoubleField(m_xBuilder->weld_entry("edit_by")))
+ , maStartHelper(*mxRbAutoStart, *mxRbManStart, *mxEdStart)
+ , maEndHelper(*mxRbAutoEnd, *mxRbManEnd, *mxEdEnd)
+{
+ maStartHelper.SetValue( rInfo.mbAutoStart, rInfo.mfStart );
+ maEndHelper.SetValue( rInfo.mbAutoEnd, rInfo.mfEnd );
+ mxEdBy->SetValue( (rInfo.mfStep <= 0.0) ? 1.0 : rInfo.mfStep );
+
+ /* Set the initial focus, currently it is somewhere after calling all the radio
+ button click handlers. Now the first enabled editable control is focused. */
+ if (mxEdStart->get_sensitive())
+ mxEdStart->grab_focus();
+ else if (mxEdEnd->get_sensitive())
+ mxEdEnd->grab_focus();
+ else
+ mxEdBy->grab_focus();
+}
+
+ScDPNumGroupDlg::~ScDPNumGroupDlg()
+{
+}
+
+ScDPNumGroupInfo ScDPNumGroupDlg::GetGroupInfo() const
+{
+ ScDPNumGroupInfo aInfo;
+ aInfo.mbEnable = true;
+ aInfo.mbDateValues = false;
+ aInfo.mbAutoStart = maStartHelper.IsAuto();
+ aInfo.mbAutoEnd = maEndHelper.IsAuto();
+
+ // get values and silently auto-correct them, if they are not valid
+ // TODO: error messages in OK event?
+ aInfo.mfStart = maStartHelper.GetValue();
+ aInfo.mfEnd = maEndHelper.GetValue();
+ if( !mxEdBy->GetValue( aInfo.mfStep ) || (aInfo.mfStep <= 0.0) )
+ aInfo.mfStep = 1.0;
+ if( aInfo.mfEnd <= aInfo.mfStart )
+ aInfo.mfEnd = aInfo.mfStart + aInfo.mfStep;
+
+ return aInfo;
+}
+
+ScDPDateGroupDlg::ScDPDateGroupDlg(weld::Window* pParent,
+ const ScDPNumGroupInfo& rInfo, sal_Int32 nDatePart, const Date& rNullDate)
+ : GenericDialogController(pParent, "modules/scalc/ui/groupbydate.ui", "PivotTableGroupByDate")
+ , mxRbAutoStart(m_xBuilder->weld_radio_button("auto_start"))
+ , mxRbManStart(m_xBuilder->weld_radio_button("manual_start"))
+ , mxEdStart(new SvtCalendarBox(m_xBuilder->weld_menu_button("start_date")))
+ , mxRbAutoEnd(m_xBuilder->weld_radio_button("auto_end"))
+ , mxRbManEnd(m_xBuilder->weld_radio_button("manual_end"))
+ , mxEdEnd(new SvtCalendarBox(m_xBuilder->weld_menu_button("end_date")))
+ , mxRbNumDays(m_xBuilder->weld_radio_button("days"))
+ , mxRbUnits(m_xBuilder->weld_radio_button("intervals"))
+ , mxEdNumDays(m_xBuilder->weld_spin_button("days_value"))
+ , mxLbUnits(m_xBuilder->weld_tree_view("interval_list"))
+ , mxBtnOk(m_xBuilder->weld_button("ok"))
+ , maStartHelper(*mxRbAutoStart, *mxRbManStart, *mxEdStart, rNullDate)
+ , maEndHelper(*mxRbAutoEnd, *mxRbManEnd, *mxEdEnd, rNullDate)
+{
+ maStartHelper.SetValue( rInfo.mbAutoStart, rInfo.mfStart );
+ maEndHelper.SetValue( rInfo.mbAutoEnd, rInfo.mfEnd );
+
+ mxLbUnits->enable_toggle_buttons(weld::ColumnToggleType::Check);
+
+ if( nDatePart == 0 )
+ nDatePart = css::sheet::DataPilotFieldGroupBy::MONTHS;
+ for (size_t nIdx = 0; nIdx < SAL_N_ELEMENTS(aDatePartResIds); ++nIdx)
+ {
+ mxLbUnits->append();
+ mxLbUnits->set_toggle(nIdx, (nDatePart & spnDateParts[ nIdx ]) ? TRISTATE_TRUE : TRISTATE_FALSE);
+ mxLbUnits->set_text(nIdx, ScResId(aDatePartResIds[nIdx]), 0);
+ }
+
+ if( rInfo.mbDateValues )
+ {
+ mxRbNumDays->set_active(true);
+ ToggleHdl(*mxRbNumDays );
+
+ double fNumDays = rInfo.mfStep;
+ if( fNumDays < 1.0 )
+ fNumDays = 1.0;
+ else if( fNumDays > 32767.0 )
+ fNumDays = 32767.0;
+ mxEdNumDays->set_value(fNumDays);
+ }
+ else
+ {
+ mxRbUnits->set_active(true);
+ ToggleHdl(*mxRbUnits);
+ }
+
+ /* Set the initial focus, currently it is somewhere after calling all the radio
+ button click handlers. Now the first enabled editable control is focused. */
+ if( mxEdStart->get_sensitive() )
+ mxEdStart->grab_focus();
+ else if( mxEdEnd->get_sensitive() )
+ mxEdEnd->grab_focus();
+ else if( mxEdNumDays->get_sensitive() )
+ mxEdNumDays->grab_focus();
+ else if( mxLbUnits->get_sensitive() )
+ mxLbUnits->grab_focus();
+
+ mxRbNumDays->connect_toggled( LINK( this, ScDPDateGroupDlg, ToggleHdl ) );
+ mxRbUnits->connect_toggled( LINK( this, ScDPDateGroupDlg, ToggleHdl ) );
+ mxLbUnits->connect_toggled( LINK( this, ScDPDateGroupDlg, CheckHdl ) );
+}
+
+ScDPDateGroupDlg::~ScDPDateGroupDlg()
+{
+}
+
+ScDPNumGroupInfo ScDPDateGroupDlg::GetGroupInfo() const
+{
+ ScDPNumGroupInfo aInfo;
+ aInfo.mbEnable = true;
+ aInfo.mbDateValues = mxRbNumDays->get_active();
+ aInfo.mbAutoStart = maStartHelper.IsAuto();
+ aInfo.mbAutoEnd = maEndHelper.IsAuto();
+
+ // get values and silently auto-correct them, if they are not valid
+ // TODO: error messages in OK event?
+ aInfo.mfStart = maStartHelper.GetValue();
+ aInfo.mfEnd = maEndHelper.GetValue();
+ sal_Int64 nNumDays = mxEdNumDays->get_value();
+ aInfo.mfStep = static_cast<double>( aInfo.mbDateValues ? nNumDays : 0L );
+ if( aInfo.mfEnd <= aInfo.mfStart )
+ aInfo.mfEnd = aInfo.mfStart + nNumDays;
+
+ return aInfo;
+}
+
+sal_Int32 ScDPDateGroupDlg::GetDatePart() const
+{
+ // return DAYS for special "number of days" mode
+ if( mxRbNumDays->get_active() )
+ return css::sheet::DataPilotFieldGroupBy::DAYS;
+
+ // return listbox contents for "units" mode
+ sal_Int32 nDatePart = 0;
+ for (int nIdx = 0, nCount = mxLbUnits->n_children(); nIdx < nCount; ++nIdx )
+ if (mxLbUnits->get_toggle(nIdx) == TRISTATE_TRUE)
+ nDatePart |= spnDateParts[ nIdx ];
+ return nDatePart;
+}
+
+IMPL_LINK(ScDPDateGroupDlg, ToggleHdl, weld::Toggleable&, rButton, void)
+{
+ if (!rButton.get_active())
+ return;
+ if (mxRbNumDays->get_active())
+ {
+ mxLbUnits->set_sensitive(false);
+ // enable and set focus to edit field on clicking "num of days" radio button
+ mxEdNumDays->set_sensitive(true);
+ mxEdNumDays->grab_focus();
+ mxBtnOk->set_sensitive(true);
+ }
+ else if (mxRbUnits->get_active())
+ {
+ mxEdNumDays->set_sensitive(false);
+ // enable and set focus to listbox on clicking "units" radio button
+ mxLbUnits->set_sensitive(true);
+ mxLbUnits->grab_focus();
+ // disable OK button if no date part selected
+ Check();
+ }
+}
+
+namespace
+{
+ bool HasCheckedEntryCount(const weld::TreeView& rView)
+ {
+ for (int i = 0; i < rView.n_children(); ++i)
+ {
+ if (rView.get_toggle(i) == TRISTATE_TRUE)
+ return true;
+ }
+ return false;
+ }
+}
+
+IMPL_LINK_NOARG(ScDPDateGroupDlg, CheckHdl, const weld::TreeView::iter_col&, void)
+{
+ Check();
+}
+
+void ScDPDateGroupDlg::Check()
+{
+ // enable/disable OK button on modifying check list box
+ mxBtnOk->set_sensitive(HasCheckedEntryCount(*mxLbUnits));
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/filtdlg.cxx b/sc/source/ui/dbgui/filtdlg.cxx
new file mode 100644
index 000000000..2af415670
--- /dev/null
+++ b/sc/source/ui/dbgui/filtdlg.cxx
@@ -0,0 +1,1571 @@
+/* -*- 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 <sal/log.hxx>
+
+#include <uiitems.hxx>
+#include <reffact.hxx>
+#include <viewdata.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <scresid.hxx>
+#include <queryentry.hxx>
+
+#include <foptmgr.hxx>
+
+#include <globstr.hrc>
+#include <strings.hrc>
+
+#include <filtdlg.hxx>
+#include <svx/colorwindow.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/weld.hxx>
+#include <svl/numformat.hxx>
+#include <svl/sharedstringpool.hxx>
+
+#include <limits>
+
+#define QUERY_ENTRY_COUNT 4
+#define INVALID_HEADER_POS std::numeric_limits<size_t>::max()
+
+ScFilterDlg::EntryList::EntryList() :
+ mnHeaderPos(INVALID_HEADER_POS) {}
+
+ScFilterDlg::ScFilterDlg(SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent,
+ const SfxItemSet& rArgSet)
+ : ScAnyRefDlgController(pB, pCW, pParent,
+ "modules/scalc/ui/standardfilterdialog.ui", "StandardFilterDialog")
+ , aStrUndefined(ScResId(SCSTR_UNDEFINED))
+ , aStrNone(ScResId(SCSTR_NONE))
+ , aStrEmpty(ScResId(SCSTR_FILTER_EMPTY))
+ , aStrNotEmpty(ScResId(SCSTR_FILTER_NOTEMPTY))
+ , aStrColumn(ScResId(SCSTR_COLUMN_LETTER))
+ , aStrTextColor(ScResId(SCSTR_FILTER_TEXT_COLOR_COND))
+ , aStrBackgroundColor(ScResId(SCSTR_FILTER_BACKGROUND_COLOR_COND))
+ , nWhichQuery(rArgSet.GetPool()->GetWhich(SID_QUERY))
+ , theQueryData(static_cast<const ScQueryItem&>(rArgSet.Get(nWhichQuery)).GetQueryData())
+ , pViewData(nullptr)
+ , pDoc(nullptr)
+ , nSrcTab(0)
+ , bRefInputMode(false)
+ , m_xLbConnect1(m_xBuilder->weld_combo_box("connect1"))
+ , m_xLbField1(m_xBuilder->weld_combo_box("field1"))
+ , m_xLbCond1(m_xBuilder->weld_combo_box("cond1"))
+ , m_xEdVal1(m_xBuilder->weld_combo_box("val1"))
+ , m_xLbColor1(m_xBuilder->weld_combo_box("color1"))
+ , m_xBtnRemove1(m_xBuilder->weld_button("remove1"))
+ , m_xLbConnect2(m_xBuilder->weld_combo_box("connect2"))
+ , m_xLbField2(m_xBuilder->weld_combo_box("field2"))
+ , m_xLbCond2(m_xBuilder->weld_combo_box("cond2"))
+ , m_xEdVal2(m_xBuilder->weld_combo_box("val2"))
+ , m_xLbColor2(m_xBuilder->weld_combo_box("color2"))
+ , m_xBtnRemove2(m_xBuilder->weld_button("remove2"))
+ , m_xLbConnect3(m_xBuilder->weld_combo_box("connect3"))
+ , m_xLbField3(m_xBuilder->weld_combo_box("field3"))
+ , m_xLbCond3(m_xBuilder->weld_combo_box("cond3"))
+ , m_xEdVal3(m_xBuilder->weld_combo_box("val3"))
+ , m_xLbColor3(m_xBuilder->weld_combo_box("color3"))
+ , m_xBtnRemove3(m_xBuilder->weld_button("remove3"))
+ , m_xLbConnect4(m_xBuilder->weld_combo_box("connect4"))
+ , m_xLbField4(m_xBuilder->weld_combo_box("field4"))
+ , m_xLbCond4(m_xBuilder->weld_combo_box("cond4"))
+ , m_xEdVal4(m_xBuilder->weld_combo_box("val4"))
+ , m_xLbColor4(m_xBuilder->weld_combo_box("color4"))
+ , m_xBtnRemove4(m_xBuilder->weld_button("remove4"))
+ , m_xContents(m_xBuilder->weld_widget("grid"))
+ , m_xScrollBar(m_xBuilder->weld_scrolled_window("scrollbar", true))
+ , m_xExpander(m_xBuilder->weld_expander("more"))
+ , m_xBtnClear(m_xBuilder->weld_button("clear"))
+ , m_xBtnOk(m_xBuilder->weld_button("ok"))
+ , m_xBtnCancel(m_xBuilder->weld_button("cancel"))
+ , m_xBtnCase(m_xBuilder->weld_check_button("case"))
+ , m_xBtnRegExp(m_xBuilder->weld_check_button("regexp"))
+ , m_xBtnHeader(m_xBuilder->weld_check_button("header"))
+ , m_xBtnUnique(m_xBuilder->weld_check_button("unique"))
+ , m_xBtnCopyResult(m_xBuilder->weld_check_button("copyresult"))
+ , m_xLbCopyArea(m_xBuilder->weld_combo_box("lbcopyarea"))
+ , m_xEdCopyArea(new formula::RefEdit(m_xBuilder->weld_entry("edcopyarea")))
+ , m_xRbCopyArea(new formula::RefButton(m_xBuilder->weld_button("rbcopyarea")))
+ , m_xBtnDestPers(m_xBuilder->weld_check_button("destpers"))
+ , m_xFtDbAreaLabel(m_xBuilder->weld_label("dbarealabel"))
+ , m_xFtDbArea(m_xBuilder->weld_label("dbarea"))
+{
+ m_xExpander->connect_expanded(LINK(this, ScFilterDlg, MoreExpandedHdl));
+ m_xEdCopyArea->SetReferences(this, m_xFtDbAreaLabel.get());
+ m_xRbCopyArea->SetReferences(this, m_xEdCopyArea.get());
+
+ assert(m_xLbCond1->find_text(aStrTextColor) != -1);
+ assert(m_xLbCond1->find_text(aStrBackgroundColor) != -1);
+
+ Init( rArgSet );
+
+ // Hack: RefInput control
+ pTimer.reset( new Timer("ScFilterTimer") );
+ pTimer->SetTimeout( 50 ); // Wait 50ms
+ pTimer->SetInvokeHandler( LINK( this, ScFilterDlg, TimeOutHdl ) );
+}
+
+ScFilterDlg::~ScFilterDlg()
+{
+ pOptionsMgr.reset();
+ pOutItem.reset();
+
+ // Hack: RefInput control
+ pTimer->Stop();
+ pTimer.reset();
+}
+
+namespace {
+VirtualDevice* lcl_getColorImage(const Color &rColor)
+{
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ Size aImageSize(rStyleSettings.GetListBoxPreviewDefaultPixelSize());
+
+ VclPtrInstance<VirtualDevice> xDevice;
+ xDevice->SetOutputSize(aImageSize);
+ const tools::Rectangle aRect(Point(0, 0), aImageSize);
+ if (rColor == COL_NONE_COLOR)
+ {
+ const Color aW(COL_WHITE);
+ const Color aG(0xef, 0xef, 0xef);
+ xDevice->DrawCheckered(aRect.TopLeft(), aRect.GetSize(), 8, aW, aG);
+ xDevice->SetFillColor();
+ }
+ else
+ {
+ xDevice->SetFillColor(rColor);
+ }
+
+ xDevice->DrawRect(aRect);
+
+ return xDevice.get();
+}
+}
+
+void ScFilterDlg::Init( const SfxItemSet& rArgSet )
+{
+ const ScQueryItem& rQueryItem = static_cast<const ScQueryItem&>(
+ rArgSet.Get( nWhichQuery ));
+
+ m_xBtnClear->connect_clicked ( LINK( this, ScFilterDlg, BtnClearHdl ) );
+ m_xBtnOk->connect_clicked ( LINK( this, ScFilterDlg, EndDlgHdl ) );
+ m_xBtnCancel->connect_clicked ( LINK( this, ScFilterDlg, EndDlgHdl ) );
+ m_xBtnHeader->connect_toggled ( LINK( this, ScFilterDlg, CheckBoxHdl ) );
+ m_xBtnCase->connect_toggled ( LINK( this, ScFilterDlg, CheckBoxHdl ) );
+
+ m_xLbField1->connect_changed ( LINK( this, ScFilterDlg, LbSelectHdl ) );
+ m_xLbField2->connect_changed ( LINK( this, ScFilterDlg, LbSelectHdl ) );
+ m_xLbField3->connect_changed ( LINK( this, ScFilterDlg, LbSelectHdl ) );
+ m_xLbField4->connect_changed ( LINK( this, ScFilterDlg, LbSelectHdl ) );
+ m_xLbConnect1->connect_changed( LINK( this, ScFilterDlg, LbSelectHdl ) );
+ m_xLbConnect2->connect_changed( LINK( this, ScFilterDlg, LbSelectHdl ) );
+ m_xLbConnect3->connect_changed( LINK( this, ScFilterDlg, LbSelectHdl ) );
+ m_xLbConnect4->connect_changed( LINK( this, ScFilterDlg, LbSelectHdl ) );
+
+ m_xLbField1->append_text("0000000000");
+ m_xLbField1->set_active(0);
+ auto nPrefWidth = m_xLbField1->get_preferred_size().Width();
+ m_xLbField1->clear();
+
+ m_xLbField1->set_size_request(nPrefWidth, -1);
+ m_xLbField2->set_size_request(nPrefWidth, -1);
+ m_xLbField3->set_size_request(nPrefWidth, -1);
+ m_xLbField4->set_size_request(nPrefWidth, -1);
+
+ m_xLbCond1->connect_changed( LINK( this, ScFilterDlg, LbSelectHdl ) );
+ m_xLbCond2->connect_changed( LINK( this, ScFilterDlg, LbSelectHdl ) );
+ m_xLbCond3->connect_changed( LINK( this, ScFilterDlg, LbSelectHdl ) );
+ m_xLbCond4->connect_changed( LINK( this, ScFilterDlg, LbSelectHdl ) );
+
+ m_xLbColor1->connect_changed( LINK( this, ScFilterDlg, LbSelectHdl ) );
+ m_xLbColor2->connect_changed( LINK( this, ScFilterDlg, LbSelectHdl ) );
+ m_xLbColor3->connect_changed( LINK( this, ScFilterDlg, LbSelectHdl ) );
+ m_xLbColor4->connect_changed( LINK( this, ScFilterDlg, LbSelectHdl ) );
+
+ m_xBtnRemove1->connect_clicked( LINK( this, ScFilterDlg, BtnRemoveHdl ) );
+ m_xBtnRemove2->connect_clicked( LINK( this, ScFilterDlg, BtnRemoveHdl ) );
+ m_xBtnRemove3->connect_clicked( LINK( this, ScFilterDlg, BtnRemoveHdl ) );
+ m_xBtnRemove4->connect_clicked( LINK( this, ScFilterDlg, BtnRemoveHdl ) );
+
+ pViewData = rQueryItem.GetViewData();
+ pDoc = pViewData ? &pViewData->GetDocument() : nullptr;
+ nSrcTab = pViewData ? pViewData->GetTabNo() : static_cast<SCTAB>(0);
+
+ // for easier access:
+ maFieldLbArr.reserve(QUERY_ENTRY_COUNT);
+ maFieldLbArr.push_back(m_xLbField1.get());
+ maFieldLbArr.push_back(m_xLbField2.get());
+ maFieldLbArr.push_back(m_xLbField3.get());
+ maFieldLbArr.push_back(m_xLbField4.get());
+ maValueEdArr.reserve(QUERY_ENTRY_COUNT);
+ maValueEdArr.push_back(m_xEdVal1.get());
+ maValueEdArr.push_back(m_xEdVal2.get());
+ maValueEdArr.push_back(m_xEdVal3.get());
+ maValueEdArr.push_back(m_xEdVal4.get());
+ maCondLbArr.reserve(QUERY_ENTRY_COUNT);
+ maCondLbArr.push_back(m_xLbCond1.get());
+ maCondLbArr.push_back(m_xLbCond2.get());
+ maCondLbArr.push_back(m_xLbCond3.get());
+ maCondLbArr.push_back(m_xLbCond4.get());
+ maConnLbArr.reserve(QUERY_ENTRY_COUNT);
+ maConnLbArr.push_back(m_xLbConnect1.get());
+ maConnLbArr.push_back(m_xLbConnect2.get());
+ maConnLbArr.push_back(m_xLbConnect3.get());
+ maConnLbArr.push_back(m_xLbConnect4.get());
+ maColorLbArr.reserve(QUERY_ENTRY_COUNT);
+ maColorLbArr.push_back(m_xLbColor1.get());
+ maColorLbArr.push_back(m_xLbColor2.get());
+ maColorLbArr.push_back(m_xLbColor3.get());
+ maColorLbArr.push_back(m_xLbColor4.get());
+ maRemoveBtnArr.reserve(QUERY_ENTRY_COUNT);
+ maRemoveBtnArr.push_back(m_xBtnRemove1.get());
+ maRemoveBtnArr.push_back(m_xBtnRemove2.get());
+ maRemoveBtnArr.push_back(m_xBtnRemove3.get());
+ maRemoveBtnArr.push_back(m_xBtnRemove4.get());
+
+ // Option initialization:
+ pOptionsMgr.reset( new ScFilterOptionsMgr(
+ pViewData,
+ theQueryData,
+ m_xBtnCase.get(),
+ m_xBtnRegExp.get(),
+ m_xBtnHeader.get(),
+ m_xBtnUnique.get(),
+ m_xBtnCopyResult.get(),
+ m_xBtnDestPers.get(),
+ m_xLbCopyArea.get(),
+ m_xEdCopyArea.get(),
+ m_xRbCopyArea.get(),
+ m_xFtDbAreaLabel.get(),
+ m_xFtDbArea.get(),
+ aStrUndefined ) );
+ // Read in field lists and select entries
+
+ FillFieldLists();
+
+ for (size_t i = 0; i < QUERY_ENTRY_COUNT; ++i)
+ {
+ OUString aValStr;
+ size_t nCondPos = 0;
+ size_t nFieldSelPos = 0;
+
+ maColorLbArr[i]->set_visible(false);
+
+ ScQueryEntry& rEntry = theQueryData.GetEntry(i);
+ if ( rEntry.bDoQuery )
+ {
+ nCondPos = static_cast<size_t>(rEntry.eOp);
+ nFieldSelPos = GetFieldSelPos( static_cast<SCCOL>(rEntry.nField) );
+ if (rEntry.IsQueryByEmpty())
+ {
+ aValStr = aStrEmpty;
+ maCondLbArr[i]->set_sensitive(false);
+ }
+ else if (rEntry.IsQueryByNonEmpty())
+ {
+ aValStr = aStrNotEmpty;
+ maCondLbArr[i]->set_sensitive(false);
+ }
+ else if (rEntry.IsQueryByTextColor() || rEntry.IsQueryByBackgroundColor())
+ {
+ nCondPos = maCondLbArr[i]->find_text(
+ rEntry.IsQueryByTextColor() ? aStrTextColor : aStrBackgroundColor);
+ maValueEdArr[i]->set_visible(false);
+ maColorLbArr[i]->set_visible(true);
+ maColorLbArr[i]->set_sensitive(true);
+ }
+ else
+ {
+ const ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
+ OUString aQueryStr = rItem.maString.getString();
+ SetValString(aQueryStr, rItem, aValStr);
+ }
+ }
+ else if ( i == 0 )
+ {
+ nFieldSelPos = pViewData ? GetFieldSelPos(pViewData->GetCurX()) : 0;
+ rEntry.nField = nFieldSelPos ? (theQueryData.nCol1 +
+ static_cast<SCCOL>(nFieldSelPos) - 1) : static_cast<SCCOL>(0);
+ rEntry.bDoQuery=true;
+ if (maRefreshExceptQuery.size() < i + 1)
+ maRefreshExceptQuery.resize(i + 1, false);
+ maRefreshExceptQuery[i] = true;
+
+ }
+ maFieldLbArr[i]->set_active( nFieldSelPos );
+ maCondLbArr [i]->set_active( nCondPos );
+ maValueEdArr[i]->set_entry_text( aValStr );
+ maValueEdArr[i]->set_entry_completion(false);
+ maValueEdArr[i]->connect_changed( LINK( this, ScFilterDlg, ValModifyHdl ) );
+ UpdateValueList(i+1);
+ UpdateColorList(i+1);
+ }
+
+ m_xScrollBar->connect_vadjustment_changed( LINK( this, ScFilterDlg, ScrollHdl ) );
+ m_xScrollBar->vadjustment_configure(0, 0, 8, 1, 3, 4);
+ Size aSize(m_xContents->get_preferred_size());
+ m_xContents->set_size_request(aSize.Width(), aSize.Height());
+
+ m_xLbConnect1->hide();
+ // Disable/Enable Logic:
+
+ (m_xLbField1->get_active() != 0)
+ && (m_xLbField2->get_active() != 0)
+ ? m_xLbConnect2->set_active( static_cast<sal_uInt16>(theQueryData.GetEntry(1).eConnect) )
+ : m_xLbConnect2->set_active(-1);
+
+ (m_xLbField2->get_active() != 0)
+ && (m_xLbField3->get_active() != 0)
+ ? m_xLbConnect3->set_active( static_cast<sal_uInt16>(theQueryData.GetEntry(2).eConnect) )
+ : m_xLbConnect3->set_active(-1);
+
+ (m_xLbField3->get_active() != 0)
+ && (m_xLbField4->get_active() != 0)
+ ? m_xLbConnect4->set_active( static_cast<sal_uInt16>(theQueryData.GetEntry(3).eConnect) )
+ : m_xLbConnect4->set_active(-1);
+ if ( m_xLbField1->get_active() == 0 )
+ {
+ m_xLbConnect2->set_sensitive(false);
+ m_xLbField2->set_sensitive(false);
+ m_xLbCond2->set_sensitive(false);
+ m_xEdVal2->set_sensitive(false);
+ m_xLbColor2->set_sensitive(false);
+ m_xBtnRemove2->set_sensitive(false);
+ }
+ else if ( m_xLbConnect2->get_active() == -1 )
+ {
+ m_xLbField2->set_sensitive(false);
+ m_xLbCond2->set_sensitive(false);
+ m_xEdVal2->set_sensitive(false);
+ m_xLbColor2->set_sensitive(false);
+ m_xBtnRemove2->set_sensitive(false);
+ }
+
+ if ( m_xLbField2->get_active() == 0 )
+ {
+ m_xLbConnect3->set_sensitive(false);
+ m_xLbField3->set_sensitive(false);
+ m_xLbCond3->set_sensitive(false);
+ m_xEdVal3->set_sensitive(false);
+ m_xLbColor3->set_sensitive(false);
+ m_xBtnRemove3->set_sensitive(false);
+ }
+ else if ( m_xLbConnect3->get_active() == -1 )
+ {
+ m_xLbField3->set_sensitive(false);
+ m_xLbCond3->set_sensitive(false);
+ m_xEdVal3->set_sensitive(false);
+ m_xLbColor3->set_sensitive(false);
+ m_xBtnRemove3->set_sensitive(false);
+ }
+ if ( m_xLbField3->get_active() == 0 )
+ {
+ m_xLbConnect4->set_sensitive(false);
+ m_xLbField4->set_sensitive(false);
+ m_xLbCond4->set_sensitive(false);
+ m_xEdVal4->set_sensitive(false);
+ m_xLbColor4->set_sensitive(false);
+ m_xBtnRemove4->set_sensitive(false);
+ }
+ else if ( m_xLbConnect4->get_active() == -1 )
+ {
+ m_xLbField4->set_sensitive(false);
+ m_xLbCond4->set_sensitive(false);
+ m_xEdVal4->set_sensitive(false);
+ m_xLbColor4->set_sensitive(false);
+ m_xBtnRemove4->set_sensitive(false);
+ }
+
+ m_xEdVal1->set_entry_width_chars(10);
+ m_xEdVal2->set_entry_width_chars(10);
+ m_xEdVal3->set_entry_width_chars(10);
+ m_xEdVal4->set_entry_width_chars(10);
+
+ if (pDoc != nullptr && pDoc->GetChangeTrack() != nullptr)
+ m_xBtnCopyResult->set_sensitive(false);
+}
+
+void ScFilterDlg::Close()
+{
+ if (pViewData)
+ pViewData->GetDocShell()->CancelAutoDBRange();
+
+ DoClose( ScFilterDlgWrapper::GetChildWindowId() );
+}
+
+// Mouse-selected cell area becomes the new selection and is shown in the
+// reference text box
+
+void ScFilterDlg::SetReference( const ScRange& rRef, ScDocument& rDocP )
+{
+ if ( bRefInputMode ) // Only possible if in reference edit mode
+ {
+ if ( rRef.aStart != rRef.aEnd )
+ RefInputStart( m_xEdCopyArea.get() );
+ OUString aRefStr(rRef.aStart.Format(ScRefFlags::ADDR_ABS_3D, &rDocP, rDocP.GetAddressConvention()));
+ m_xEdCopyArea->SetRefString( aRefStr );
+ }
+}
+
+void ScFilterDlg::SetActive()
+{
+ if ( bRefInputMode )
+ {
+ m_xEdCopyArea->GrabFocus();
+ m_xEdCopyArea->GetModifyHdl().Call( *m_xEdCopyArea );
+ }
+ else
+ m_xDialog->grab_focus();
+
+ RefInputDone();
+}
+
+void ScFilterDlg::FillFieldLists()
+{
+ m_xLbField1->freeze();
+ m_xLbField2->freeze();
+ m_xLbField3->freeze();
+ m_xLbField4->freeze();
+
+ m_xLbField1->clear();
+ m_xLbField2->clear();
+ m_xLbField3->clear();
+ m_xLbField4->clear();
+ m_xLbField1->append_text( aStrNone );
+ m_xLbField2->append_text( aStrNone );
+ m_xLbField3->append_text( aStrNone );
+ m_xLbField4->append_text( aStrNone );
+
+ if ( pDoc )
+ {
+ OUString aFieldName;
+ SCTAB nTab = nSrcTab;
+ SCCOL nFirstCol = theQueryData.nCol1;
+ SCROW nFirstRow = theQueryData.nRow1;
+ SCCOL nMaxCol = theQueryData.nCol2;
+ SCCOL col = 0;
+
+ for ( col=nFirstCol; col<=nMaxCol; col++ )
+ {
+ aFieldName = pDoc->GetString(col, nFirstRow, nTab);
+ if (!m_xBtnHeader->get_active() || aFieldName.isEmpty())
+ {
+ aFieldName = ScGlobal::ReplaceOrAppend( aStrColumn, u"%1", ScColToAlpha( col ));
+ }
+ m_xLbField1->append_text( aFieldName );
+ m_xLbField2->append_text( aFieldName );
+ m_xLbField3->append_text( aFieldName );
+ m_xLbField4->append_text( aFieldName );
+ }
+ }
+
+ m_xLbField4->thaw();
+ m_xLbField3->thaw();
+ m_xLbField2->thaw();
+ m_xLbField1->thaw();
+}
+
+void ScFilterDlg::UpdateValueList( size_t nList )
+{
+ bool bCaseSens = m_xBtnCase->get_active();
+
+ if (pDoc && nList > 0 && nList <= QUERY_ENTRY_COUNT)
+ {
+ weld::ComboBox* pValList = maValueEdArr[nList-1];
+ const sal_Int32 nFieldSelPos = maFieldLbArr[nList-1]->get_active();
+ OUString aCurValue = pValList->get_active_text();
+
+ std::unique_ptr<weld::WaitObject> xWaiter;
+ std::vector<weld::ComboBoxEntry> aEntries;
+ aEntries.emplace_back(aStrNotEmpty);
+ aEntries.emplace_back(aStrEmpty);
+
+ if (nFieldSelPos)
+ {
+ xWaiter.reset(new weld::WaitObject(m_xDialog.get())); // even if only the list box has content
+ SCCOL nColumn = theQueryData.nCol1 + static_cast<SCCOL>(nFieldSelPos) - 1;
+ EntryList* pList = nullptr;
+ if (!m_EntryLists.count(nColumn))
+ {
+ size_t nOffset = GetSliderPos();
+ SCTAB nTab = nSrcTab;
+ SCROW nFirstRow = theQueryData.nRow1;
+ SCROW nLastRow = theQueryData.nRow2;
+ if (maHasDates.size() < nOffset+nList)
+ maHasDates.resize(nOffset+nList, false);
+ maHasDates[nOffset+nList-1] = false;
+
+ // first without the first line
+ std::pair<EntryListsMap::iterator, bool> r =
+ m_EntryLists.insert(std::make_pair(nColumn, std::make_unique<EntryList>()));
+ if (!r.second)
+ // insertion failed.
+ return;
+
+ pList = r.first->second.get();
+ pDoc->GetFilterEntriesArea(
+ nColumn, nFirstRow+1, nLastRow,
+ nTab, bCaseSens, pList->maFilterEntries);
+ maHasDates[nOffset+nList-1] = pList->maFilterEntries.mbHasDates;
+
+ // Entry for the first line
+ //! Entry (pHdrEntry) doesn't generate collection?
+
+ pList->mnHeaderPos = INVALID_HEADER_POS;
+ ScFilterEntries aHdrColl;
+ pDoc->GetFilterEntriesArea(
+ nColumn, nFirstRow, nFirstRow, nTab, true, aHdrColl );
+ if (!aHdrColl.empty())
+ {
+ // See if the header value is already in the list.
+ std::vector<ScTypedStrData>::iterator itBeg = pList->maFilterEntries.begin(), itEnd = pList->maFilterEntries.end();
+ if (std::none_of(itBeg, itEnd, FindTypedStrData(aHdrColl.front(), bCaseSens)))
+ {
+ // Not in the list. Insert it.
+ pList->maFilterEntries.push_back(aHdrColl.front());
+ if (bCaseSens)
+ std::sort(pList->maFilterEntries.begin(), pList->maFilterEntries.end(), ScTypedStrData::LessCaseSensitive());
+ else
+ std::sort(pList->maFilterEntries.begin(), pList->maFilterEntries.end(), ScTypedStrData::LessCaseInsensitive());
+
+ // Record its position.
+ itBeg = pList->maFilterEntries.begin();
+ itEnd = pList->maFilterEntries.end();
+ auto it = std::find_if(itBeg, itEnd, FindTypedStrData(aHdrColl.front(), bCaseSens));
+ pList->mnHeaderPos = std::distance(itBeg, it);
+ }
+ }
+ }
+ else
+ pList = m_EntryLists[nColumn].get();
+
+ assert(pList);
+
+ for (const auto& rEntry : pList->maFilterEntries)
+ aEntries.emplace_back(rEntry.GetString());
+ }
+ pValList->insert_vector(aEntries, false);
+ pValList->set_entry_text(aCurValue);
+ }
+
+ UpdateHdrInValueList( nList );
+}
+
+void ScFilterDlg::UpdateHdrInValueList( size_t nList )
+{
+ //! GetText / SetText ??
+
+ if (!pDoc)
+ return;
+
+ if (nList == 0 || nList > QUERY_ENTRY_COUNT)
+ return;
+
+ size_t nFieldSelPos = maFieldLbArr[nList-1]->get_active();
+ if (!nFieldSelPos)
+ return;
+
+ SCCOL nColumn = theQueryData.nCol1 + static_cast<SCCOL>(nFieldSelPos) - 1;
+ if (!m_EntryLists.count(nColumn))
+ {
+ OSL_FAIL("column not yet initialized");
+ return;
+ }
+
+ size_t const nPos = m_EntryLists[nColumn]->mnHeaderPos;
+ if (nPos == INVALID_HEADER_POS)
+ return;
+
+ weld::ComboBox* pValList = maValueEdArr[nList-1];
+ int nListPos = nPos + 2; // for "empty" and "non-empty"
+
+ const ScTypedStrData& rHdrEntry = m_EntryLists[nColumn]->maFilterEntries.maStrData[nPos];
+
+ const OUString& aHdrStr = rHdrEntry.GetString();
+ bool bWasThere = nListPos < pValList->get_count() && aHdrStr == pValList->get_text(nListPos);
+ bool bInclude = !m_xBtnHeader->get_active();
+
+ if (bInclude) // Include entry
+ {
+ if (!bWasThere)
+ pValList->insert_text(nListPos, aHdrStr);
+ }
+ else // Omit entry
+ {
+ if (bWasThere)
+ pValList->remove(nListPos);
+ }
+}
+
+void ScFilterDlg::ClearValueList( size_t nList )
+{
+ if (nList > 0 && nList <= QUERY_ENTRY_COUNT)
+ {
+ weld::ComboBox* pValList = maValueEdArr[nList-1];
+ pValList->clear();
+ pValList->append_text( aStrNotEmpty );
+ pValList->append_text( aStrEmpty );
+ pValList->set_entry_text( OUString() );
+ }
+}
+
+void ScFilterDlg::UpdateColorList(size_t nList)
+{
+ if (!pDoc || nList <= 0 || nList > QUERY_ENTRY_COUNT)
+ return;
+
+ size_t nPos = nList - 1;
+ ScQueryEntry& rEntry = theQueryData.GetEntry(nPos);
+ const sal_Int32 nFieldSelPos = maFieldLbArr[nPos]->get_active();
+ if (!nFieldSelPos)
+ return;
+
+ SCCOL nColumn = theQueryData.nCol1 + static_cast<SCCOL>(nFieldSelPos) - 1;
+ EntryList* pList = m_EntryLists[nColumn].get();
+ if (!pList)
+ return;
+
+ std::set<Color> aColors;
+ OUString sSelectedCondition = maCondLbArr[nPos]->get_active_text();
+ if (sSelectedCondition == aStrTextColor)
+ aColors = pList->maFilterEntries.getTextColors();
+ else if (sSelectedCondition == aStrBackgroundColor)
+ aColors = pList->maFilterEntries.getBackgroundColors();
+ else
+ return;
+
+ maColorLbArr[nPos]->clear();
+ for (const auto& rColor : aColors)
+ {
+ OUString sId = rColor.AsRGBHexString();
+ if (rColor == COL_AUTO)
+ {
+ OUString sText = sSelectedCondition == aStrTextColor
+ ? ScResId(SCSTR_FILTER_AUTOMATIC_COLOR)
+ : ScResId(SCSTR_FILTER_NO_FILL);
+ maColorLbArr[nPos]->append(sId, sText);
+ }
+ else
+ {
+ VirtualDevice* pDev = lcl_getColorImage(rColor);
+ maColorLbArr[nPos]->append(sId, OUString(), *pDev);
+ }
+
+ auto aItem = rEntry.GetQueryItem();
+ if (aItem.maColor == rColor
+ && ((sSelectedCondition == aStrTextColor && aItem.meType == ScQueryEntry::ByTextColor)
+ || (sSelectedCondition == aStrBackgroundColor
+ && aItem.meType == ScQueryEntry::ByBackgroundColor)))
+ {
+ maColorLbArr[nPos]->set_active_id(sId);
+ }
+ }
+}
+
+size_t ScFilterDlg::GetFieldSelPos( SCCOL nField )
+{
+ if ( nField >= theQueryData.nCol1 && nField <= theQueryData.nCol2 )
+ return static_cast<size_t>(nField - theQueryData.nCol1 + 1);
+ else
+ return 0;
+}
+
+ScQueryItem* ScFilterDlg::GetOutputItem()
+{
+ ScAddress theCopyPos;
+ ScQueryParam theParam( theQueryData );
+ bool bCopyPosOk = false;
+
+ if ( m_xBtnCopyResult->get_active() )
+ {
+ ScRefFlags nResult = theCopyPos.Parse(
+ m_xEdCopyArea->GetText(), *pDoc, pDoc->GetAddressConvention());
+ bCopyPosOk = (nResult & ScRefFlags::VALID) == ScRefFlags::VALID;
+ }
+
+ if ( m_xBtnCopyResult->get_active() && bCopyPosOk )
+ {
+ theParam.bInplace = false;
+ theParam.nDestTab = theCopyPos.Tab();
+ theParam.nDestCol = theCopyPos.Col();
+ theParam.nDestRow = theCopyPos.Row();
+ }
+ else
+ {
+ theParam.bInplace = true;
+ theParam.nDestTab = 0;
+ theParam.nDestCol = 0;
+ theParam.nDestRow = 0;
+ }
+
+ theParam.bHasHeader = m_xBtnHeader->get_active();
+ theParam.bByRow = true;
+ theParam.bDuplicate = !m_xBtnUnique->get_active();
+ theParam.bCaseSens = m_xBtnCase->get_active();
+ theParam.eSearchType = m_xBtnRegExp->get_active() ? utl::SearchParam::SearchType::Regexp : utl::SearchParam::SearchType::Normal;
+ theParam.bDestPers = m_xBtnDestPers->get_active();
+
+ // only set the three - reset everything else
+
+ pOutItem.reset( new ScQueryItem( nWhichQuery, &theParam ) );
+
+ return pOutItem.get();
+}
+
+bool ScFilterDlg::IsRefInputMode() const
+{
+ return bRefInputMode;
+}
+
+// Handler:
+
+IMPL_LINK( ScFilterDlg, BtnClearHdl, weld::Button&, rBtn, void )
+{
+ if ( &rBtn != m_xBtnClear.get() )
+ return;
+
+ // scroll to the top
+ m_xScrollBar->vadjustment_set_value(0);
+ size_t nOffset = 0;
+ RefreshEditRow( nOffset);
+
+ // clear all conditions
+ m_xLbConnect1->set_active(-1);
+ m_xLbConnect2->set_active(-1);
+ m_xLbConnect3->set_active(-1);
+ m_xLbConnect4->set_active(-1);
+ m_xLbField1->set_active(0);
+ m_xLbField2->set_active(0);
+ m_xLbField3->set_active(0);
+ m_xLbField4->set_active(0);
+ m_xLbCond1->set_active(0);
+ m_xLbCond2->set_active(0);
+ m_xLbCond3->set_active(0);
+ m_xLbCond4->set_active(0);
+ ClearValueList( 1 );
+ ClearValueList( 2 );
+ ClearValueList( 3 );
+ ClearValueList( 4 );
+
+ // disable fields for second row onward
+ m_xLbConnect2->set_sensitive(false);
+ m_xLbConnect3->set_sensitive(false);
+ m_xLbConnect4->set_sensitive(false);
+ m_xLbField2->set_sensitive(false);
+ m_xLbField3->set_sensitive(false);
+ m_xLbField4->set_sensitive(false);
+ m_xLbCond2->set_sensitive(false);
+ m_xLbCond3->set_sensitive(false);
+ m_xLbCond4->set_sensitive(false);
+ m_xEdVal2->set_sensitive(false);
+ m_xEdVal3->set_sensitive(false);
+ m_xEdVal4->set_sensitive(false);
+ m_xLbColor2->set_sensitive(false);
+ m_xLbColor3->set_sensitive(false);
+ m_xLbColor4->set_sensitive(false);
+ m_xBtnRemove2->set_sensitive(false);
+ m_xBtnRemove3->set_sensitive(false);
+ m_xBtnRemove4->set_sensitive(false);
+
+ // clear query data objects
+ SCSIZE nCount = theQueryData.GetEntryCount();
+ if (maRefreshExceptQuery.size() < nCount + 1)
+ maRefreshExceptQuery.resize(nCount + 1, false);
+ for (SCSIZE i = 0; i < nCount; ++i)
+ {
+ theQueryData.GetEntry(i).bDoQuery = false;
+ maRefreshExceptQuery[i] = false;
+ theQueryData.GetEntry(i).nField = static_cast<SCCOL>(0);
+ }
+ maRefreshExceptQuery[0] = true;
+}
+
+IMPL_LINK( ScFilterDlg, EndDlgHdl, weld::Button&, rBtn, void )
+{
+ if ( &rBtn == m_xBtnOk.get() )
+ {
+ bool bAreaInputOk = true;
+
+ if ( m_xBtnCopyResult->get_active() )
+ {
+ if ( !pOptionsMgr->VerifyPosStr( m_xEdCopyArea->GetText() ) )
+ {
+ if (!m_xExpander->get_expanded())
+ m_xExpander->set_expanded(true);
+
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ ScResId(STR_INVALID_TABREF)));
+ xBox->run();
+ m_xEdCopyArea->GrabFocus();
+ bAreaInputOk = false;
+ }
+ }
+
+ if ( bAreaInputOk )
+ {
+ SetDispatcherLock( false );
+ SwitchToDocument();
+ GetBindings().GetDispatcher()->ExecuteList(FID_FILTER_OK,
+ SfxCallMode::SLOT | SfxCallMode::RECORD,
+ { GetOutputItem() });
+ response(RET_OK);
+ }
+ }
+ else if ( &rBtn == m_xBtnCancel.get() )
+ {
+ response(RET_CANCEL);
+ }
+}
+
+IMPL_LINK_NOARG(ScFilterDlg, MoreExpandedHdl, weld::Expander&, void)
+{
+ if ( m_xExpander->get_expanded() )
+ pTimer->Start();
+ else
+ {
+ pTimer->Stop();
+ bRefInputMode = false;
+ //@BugID 54702 Enable/disable only in Basic class
+ //SFX_APPWINDOW->Disable(FALSE); //! general method in ScAnyRefDlg
+ }
+}
+
+IMPL_LINK( ScFilterDlg, TimeOutHdl, Timer*, _pTimer, void )
+{
+ // Check if RefInputMode is still true every 50ms
+ if (_pTimer == pTimer.get() && m_xDialog->has_toplevel_focus())
+ bRefInputMode = (m_xEdCopyArea->GetWidget()->has_focus() || m_xRbCopyArea->GetWidget()->has_focus());
+
+ if ( m_xExpander->get_expanded() )
+ pTimer->Start();
+}
+
+IMPL_LINK(ScFilterDlg, LbSelectHdl, weld::ComboBox&, rLb, void)
+{
+ /*
+ * Handle enable/disable logic depending on which ListBox was selected
+ */
+ sal_uInt16 nOffset = GetSliderPos();
+
+ if ( &rLb == m_xLbConnect1.get() )
+ {
+ m_xLbField1->set_sensitive(true);
+ m_xLbCond1->set_sensitive(true);
+ m_xEdVal1->set_sensitive(true);
+ m_xBtnRemove1->set_sensitive(true);
+
+ const sal_Int32 nConnect1 = m_xLbConnect1->get_active();
+ size_t nQE = nOffset;
+ theQueryData.GetEntry(nQE).eConnect =static_cast<ScQueryConnect>(nConnect1);
+ if (maRefreshExceptQuery.size() < nQE + 1)
+ maRefreshExceptQuery.resize(nQE + 1, false);
+ maRefreshExceptQuery[nQE] = true;
+ }
+ else if ( &rLb == m_xLbConnect2.get() )
+ {
+ m_xLbField2->set_sensitive(true);
+ m_xLbCond2->set_sensitive(true);
+ m_xEdVal2->set_sensitive(true);
+ m_xBtnRemove2->set_sensitive(true);
+
+ const sal_Int32 nConnect2 = m_xLbConnect2->get_active();
+ size_t nQE = 1+nOffset;
+ theQueryData.GetEntry(nQE).eConnect =static_cast<ScQueryConnect>(nConnect2);
+ if (maRefreshExceptQuery.size() < nQE + 1)
+ maRefreshExceptQuery.resize(nQE + 1, false);
+ maRefreshExceptQuery[nQE]=true;
+ }
+ else if ( &rLb == m_xLbConnect3.get() )
+ {
+ m_xLbField3->set_sensitive(true);
+ m_xLbCond3->set_sensitive(true);
+ m_xEdVal3->set_sensitive(true);
+ m_xBtnRemove3->set_sensitive(true);
+
+ const sal_Int32 nConnect3 = m_xLbConnect3->get_active();
+ size_t nQE = 2 + nOffset;
+ theQueryData.GetEntry(nQE).eConnect = static_cast<ScQueryConnect>(nConnect3);
+ if (maRefreshExceptQuery.size() < nQE + 1)
+ maRefreshExceptQuery.resize(nQE + 1, false);
+ maRefreshExceptQuery[nQE] = true;
+
+ }
+ else if ( &rLb == m_xLbConnect4.get() )
+ {
+ m_xLbField4->set_sensitive(true);
+ m_xLbCond4->set_sensitive(true);
+ m_xEdVal4->set_sensitive(true);
+ m_xLbColor4->set_sensitive(true);
+ m_xBtnRemove4->set_sensitive(true);
+
+ const sal_Int32 nConnect4 = m_xLbConnect4->get_active();
+ size_t nQE = 3 + nOffset;
+ theQueryData.GetEntry(nQE).eConnect = static_cast<ScQueryConnect>(nConnect4);
+ if (maRefreshExceptQuery.size() < nQE + 1)
+ maRefreshExceptQuery.resize(nQE + 1, false);
+ maRefreshExceptQuery[nQE] = true;
+ }
+ else if ( &rLb == m_xLbField1.get() )
+ {
+ if ( m_xLbField1->get_active() == 0 )
+ {
+ m_xLbConnect2->set_active(-1);
+ m_xLbConnect3->set_active(-1);
+ m_xLbConnect4->set_active(-1);
+ m_xLbField2->set_active( 0 );
+ m_xLbField3->set_active( 0 );
+ m_xLbField4->set_active( 0 );
+ m_xLbCond2->set_active( 0 );
+ m_xLbCond3->set_active( 0 );
+ m_xLbCond4->set_active( 0 );
+ ClearValueList( 1 );
+ ClearValueList( 2 );
+ ClearValueList( 3 );
+ ClearValueList( 4 );
+
+ m_xLbConnect2->set_sensitive(false);
+ m_xLbConnect3->set_sensitive(false);
+ m_xLbConnect4->set_sensitive(false);
+ m_xLbField2->set_sensitive(false);
+ m_xLbField3->set_sensitive(false);
+ m_xLbField4->set_sensitive(false);
+ m_xLbCond2->set_sensitive(false);
+ m_xLbCond3->set_sensitive(false);
+ m_xLbCond4->set_sensitive(false);
+ m_xEdVal2->set_sensitive(false);
+ m_xEdVal3->set_sensitive(false);
+ m_xEdVal4->set_sensitive(false);
+ m_xLbColor2->set_sensitive(false);
+ m_xLbColor3->set_sensitive(false);
+ m_xLbColor4->set_sensitive(false);
+ m_xBtnRemove2->set_sensitive(false);
+ m_xBtnRemove3->set_sensitive(false);
+ m_xBtnRemove4->set_sensitive(false);
+ SCSIZE nCount = theQueryData.GetEntryCount();
+ if (maRefreshExceptQuery.size() < nCount + 1)
+ maRefreshExceptQuery.resize(nCount + 1, false);
+ for (SCSIZE i = nOffset; i < nCount; ++i)
+ {
+ theQueryData.GetEntry(i).bDoQuery = false;
+ maRefreshExceptQuery[i] = false;
+ theQueryData.GetEntry(i).nField = static_cast<SCCOL>(0);
+ }
+ maRefreshExceptQuery[nOffset] = true;
+ }
+ else
+ {
+ UpdateValueList( 1 );
+ UpdateColorList( 1 );
+ if ( !m_xLbConnect2->get_sensitive() )
+ {
+ m_xLbConnect2->set_sensitive(true);
+ }
+ theQueryData.GetEntry(nOffset).bDoQuery = true;
+ const sal_Int32 nField = rLb.get_active();
+ theQueryData.GetEntry(nOffset).nField = theQueryData.nCol1 + static_cast<SCCOL>(nField) - 1 ;
+ }
+ }
+ else if ( &rLb == m_xLbField2.get() )
+ {
+ if ( m_xLbField2->get_active() == 0 )
+ {
+ m_xLbConnect3->set_active(-1);
+ m_xLbConnect4->set_active(-1);
+ m_xLbField3->set_active( 0 );
+ m_xLbField4->set_active( 0 );
+ m_xLbCond3->set_active( 0 );
+ m_xLbCond4->set_active( 0 );
+ ClearValueList( 2 );
+ ClearValueList( 3 );
+ ClearValueList( 4 );
+
+ m_xLbConnect3->set_sensitive(false);
+ m_xLbConnect4->set_sensitive(false);
+ m_xLbField3->set_sensitive(false);
+ m_xLbField4->set_sensitive(false);
+ m_xLbCond3->set_sensitive(false);
+ m_xLbCond4->set_sensitive(false);
+ m_xEdVal3->set_sensitive(false);
+ m_xEdVal4->set_sensitive(false);
+ m_xLbColor3->set_sensitive(false);
+ m_xLbColor4->set_sensitive(false);
+ m_xBtnRemove3->set_sensitive(false);
+ m_xBtnRemove4->set_sensitive(false);
+
+ sal_uInt16 nTemp=nOffset+1;
+ SCSIZE nCount = theQueryData.GetEntryCount();
+ if (maRefreshExceptQuery.size() < nCount)
+ maRefreshExceptQuery.resize(nCount, false);
+ for (SCSIZE i= nTemp; i< nCount; i++)
+ {
+ theQueryData.GetEntry(i).bDoQuery = false;
+ maRefreshExceptQuery[i] = false;
+ theQueryData.GetEntry(i).nField = static_cast<SCCOL>(0);
+ }
+ maRefreshExceptQuery[nTemp] = true;
+ }
+ else
+ {
+ UpdateValueList( 2 );
+ UpdateColorList( 2 );
+ if ( !m_xLbConnect3->get_sensitive() )
+ {
+ m_xLbConnect3->set_sensitive(true);
+ }
+ const sal_Int32 nField = rLb.get_active();
+ sal_uInt16 nQ=1+nOffset;
+ theQueryData.GetEntry(nQ).bDoQuery = true;
+ theQueryData.GetEntry(nQ).nField = theQueryData.nCol1 + static_cast<SCCOL>(nField) - 1 ;
+ }
+ }
+ else if ( &rLb == m_xLbField3.get() )
+ {
+ if ( m_xLbField3->get_active() == 0 )
+ {
+ m_xLbConnect4->set_active(-1);
+ m_xLbField4->set_active( 0 );
+ m_xLbCond4->set_active( 0 );
+ ClearValueList( 3 );
+ ClearValueList( 4 );
+
+ m_xLbConnect4->set_sensitive(false);
+ m_xLbField4->set_sensitive(false);
+ m_xLbCond4->set_sensitive(false);
+ m_xEdVal4->set_sensitive(false);
+ m_xLbColor4->set_sensitive(false);
+ m_xBtnRemove4->set_sensitive(false);
+
+ sal_uInt16 nTemp=nOffset+2;
+ SCSIZE nCount = theQueryData.GetEntryCount();
+ if (maRefreshExceptQuery.size() < nCount)
+ maRefreshExceptQuery.resize(nCount, false);
+ for (SCSIZE i = nTemp; i < nCount; ++i)
+ {
+ theQueryData.GetEntry(i).bDoQuery = false;
+ maRefreshExceptQuery[i] = false;
+ theQueryData.GetEntry(i).nField = static_cast<SCCOL>(0);
+ }
+ maRefreshExceptQuery[nTemp] = true;
+ }
+ else
+ {
+ UpdateValueList( 3 );
+ UpdateColorList( 3 );
+ if ( !m_xLbConnect4->get_sensitive() )
+ {
+ m_xLbConnect4->set_sensitive(true);
+ }
+
+ const sal_Int32 nField = rLb.get_active();
+ sal_uInt16 nQ=2+nOffset;
+ theQueryData.GetEntry(nQ).bDoQuery = true;
+ theQueryData.GetEntry(nQ).nField = theQueryData.nCol1 + static_cast<SCCOL>(nField) - 1 ;
+
+ }
+ }
+ else if ( &rLb == m_xLbField4.get() )
+ {
+ if ( m_xLbField4->get_active() == 0 )
+ {
+ ClearValueList( 4 );
+ sal_uInt16 nTemp=nOffset+3;
+ SCSIZE nCount = theQueryData.GetEntryCount();
+ if (maRefreshExceptQuery.size() < nCount)
+ maRefreshExceptQuery.resize(nCount, false);
+ for (SCSIZE i = nTemp; i < nCount; ++i)
+ {
+ theQueryData.GetEntry(i).bDoQuery = false;
+ maRefreshExceptQuery[i] = false;
+ theQueryData.GetEntry(i).nField = static_cast<SCCOL>(0);
+ }
+ maRefreshExceptQuery[nTemp] = true;
+ }
+ else
+ {
+ UpdateValueList( 4 );
+ UpdateColorList( 4 );
+ const sal_Int32 nField = rLb.get_active();
+ sal_uInt16 nQ=3+nOffset;
+ theQueryData.GetEntry(nQ).bDoQuery = true;
+ theQueryData.GetEntry(nQ).nField = theQueryData.nCol1 + static_cast<SCCOL>(nField) - 1 ;
+ }
+
+ }
+ else if (&rLb == m_xLbCond1.get() || &rLb == m_xLbCond2.get() || &rLb == m_xLbCond3.get()
+ || &rLb == m_xLbCond4.get())
+ {
+ ScQueryOp op;
+ sal_uInt16 nQ = 0;
+ bool bEnableColorLb = false;
+ if (rLb.get_active_text() == aStrTextColor || rLb.get_active_text() == aStrBackgroundColor)
+ {
+ bEnableColorLb = true;
+ op = SC_EQUAL;
+ }
+ else
+ {
+ op = static_cast<ScQueryOp>(rLb.get_active());
+ }
+
+ if (&rLb == m_xLbCond1.get())
+ {
+ nQ = nOffset;
+ m_xLbColor1->set_visible(bEnableColorLb);
+ m_xLbColor1->set_sensitive(bEnableColorLb);
+ m_xEdVal1->set_visible(!bEnableColorLb);
+ UpdateColorList(1);
+ }
+ else if (&rLb == m_xLbCond2.get())
+ {
+ nQ = 1 + nOffset;
+ m_xLbColor2->set_visible(bEnableColorLb);
+ m_xLbColor2->set_sensitive(bEnableColorLb);
+ m_xEdVal2->set_visible(!bEnableColorLb);
+ UpdateColorList(2);
+ }
+ else if (&rLb == m_xLbCond3.get())
+ {
+ nQ = 2 + nOffset;
+ m_xLbColor3->set_visible(bEnableColorLb);
+ m_xLbColor3->set_sensitive(bEnableColorLb);
+ m_xEdVal3->set_visible(!bEnableColorLb);
+ UpdateColorList(3);
+ }
+ else if (&rLb == m_xLbCond4.get())
+ {
+ nQ = 3 + nOffset;
+ m_xLbColor4->set_visible(bEnableColorLb);
+ m_xLbColor4->set_sensitive(bEnableColorLb);
+ m_xEdVal4->set_visible(!bEnableColorLb);
+ UpdateColorList(4);
+ }
+
+ auto aEntry = theQueryData.GetEntry(nQ);
+ aEntry.eOp = op;
+ }
+ else if (&rLb == m_xLbColor1.get() || &rLb == m_xLbColor2.get() || &rLb == m_xLbColor3.get()
+ || &rLb == m_xLbColor4.get())
+ {
+ sal_uInt16 nQ = 0;
+ if (&rLb == m_xLbColor1.get())
+ {
+ nQ = nOffset;
+ }
+ else if (&rLb == m_xLbColor2.get())
+ {
+ nQ = 1 + nOffset;
+ }
+ else if (&rLb == m_xLbColor3.get())
+ {
+ nQ = 2 + nOffset;
+ }
+ else if (&rLb == m_xLbColor4.get())
+ {
+ nQ = 3 + nOffset;
+ }
+
+ ScQueryEntry& aEntry = theQueryData.GetEntry(nQ);
+ Color aColor = Color::STRtoRGB(maColorLbArr[nQ]->get_active_id());
+ if (maCondLbArr[nQ]->get_active_text() == aStrTextColor)
+ {
+ aEntry.SetQueryByTextColor(aColor);
+ }
+ else if (maCondLbArr[nQ]->get_active_text() == aStrBackgroundColor)
+ {
+ aEntry.SetQueryByBackgroundColor(aColor);
+ }
+ }
+}
+
+IMPL_LINK( ScFilterDlg, CheckBoxHdl, weld::Toggleable&, rBox, void )
+{
+ // Column headers:
+ // Field list: Columnxx <-> column header string
+ // Value list: Column header value not applicable.
+ // Upper/lower case:
+ // Value list: completely new
+
+ if ( &rBox == m_xBtnHeader.get() ) // Field list and value list
+ {
+ const sal_Int32 nCurSel1 = m_xLbField1->get_active();
+ const sal_Int32 nCurSel2 = m_xLbField2->get_active();
+ const sal_Int32 nCurSel3 = m_xLbField3->get_active();
+ const sal_Int32 nCurSel4 = m_xLbField4->get_active();
+ FillFieldLists();
+ m_xLbField1->set_active( nCurSel1 );
+ m_xLbField2->set_active( nCurSel2 );
+ m_xLbField3->set_active( nCurSel3 );
+ m_xLbField4->set_active( nCurSel4 );
+
+ UpdateHdrInValueList( 1 );
+ UpdateHdrInValueList( 2 );
+ UpdateHdrInValueList( 3 );
+ UpdateHdrInValueList( 4 );
+ }
+
+ if ( &rBox != m_xBtnCase.get() ) // Complete value list
+ return;
+
+ m_EntryLists.clear();
+ UpdateValueList( 1 ); // current text is recorded
+ UpdateValueList( 2 );
+ UpdateValueList( 3 );
+ UpdateValueList( 4 );
+
+ UpdateColorList( 1 );
+ UpdateColorList( 2 );
+ UpdateColorList( 3 );
+ UpdateColorList( 4 );
+}
+
+IMPL_LINK( ScFilterDlg, ValModifyHdl, weld::ComboBox&, rEd, void )
+{
+ size_t nOffset = GetSliderPos();
+ size_t i = 0;
+ size_t nQE = i + nOffset;
+ OUString aStrVal = rEd.get_active_text();
+ weld::ComboBox* pLbCond = m_xLbCond1.get();
+ weld::ComboBox* pLbField = m_xLbField1.get();
+ if ( &rEd == m_xEdVal2.get() )
+ {
+ pLbCond = m_xLbCond2.get();
+ pLbField = m_xLbField2.get();
+ i=1;
+ nQE=i+nOffset;
+ }
+ if ( &rEd == m_xEdVal3.get() )
+ {
+ pLbCond = m_xLbCond3.get();
+ pLbField = m_xLbField3.get();
+ i=2;
+ nQE=i+nOffset;
+ }
+ if ( &rEd == m_xEdVal4.get() )
+ {
+ pLbCond = m_xLbCond4.get();
+ pLbField = m_xLbField4.get();
+ i=3;
+ nQE=i+nOffset;
+ }
+
+ if ( aStrEmpty == aStrVal || aStrNotEmpty == aStrVal )
+ {
+ pLbCond->set_active_text(OUString('='));
+ pLbCond->set_sensitive(false);
+ }
+ else
+ pLbCond->set_sensitive(true);
+
+ if (maHasDates.size() < nQE + 1)
+ maHasDates.resize(nQE + 1, false);
+ if (maRefreshExceptQuery.size() < nQE + 1)
+ maRefreshExceptQuery.resize(nQE + 1, false);
+
+ ScQueryEntry& rEntry = theQueryData.GetEntry( nQE );
+ ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
+ bool bDoThis = (pLbField->get_active() != 0);
+ rEntry.bDoQuery = bDoThis;
+
+ if ( !(rEntry.bDoQuery || maRefreshExceptQuery[nQE]) )
+ return;
+
+ bool bByEmptyOrNotByEmpty = false;
+ if ( aStrEmpty == aStrVal )
+ {
+ bByEmptyOrNotByEmpty = true;
+ rEntry.SetQueryByEmpty();
+ }
+ else if ( aStrNotEmpty == aStrVal )
+ {
+ bByEmptyOrNotByEmpty = true;
+ rEntry.SetQueryByNonEmpty();
+ }
+ else
+ {
+ rItem.maString = pDoc->GetSharedStringPool().intern(aStrVal);
+ rItem.mfVal = 0.0;
+ rItem.meType = ScQueryEntry::ByString;
+ }
+
+ const sal_Int32 nField = pLbField->get_active();
+ rEntry.nField = nField ? (theQueryData.nCol1 +
+ static_cast<SCCOL>(nField) - 1) : static_cast<SCCOL>(0);
+
+ ScQueryOp eOp = static_cast<ScQueryOp>(pLbCond->get_active());
+ rEntry.eOp = eOp;
+ if (maHasDates[nQE] && !bByEmptyOrNotByEmpty)
+ rItem.meType = ScQueryEntry::ByDate;
+}
+
+IMPL_LINK( ScFilterDlg, BtnRemoveHdl, weld::Button&, rBtn, void )
+{
+ // Calculate the row to delete
+ sal_uInt16 nOffset = GetSliderPos();
+ int nButtonIndex = 0;
+ if ( &rBtn == m_xBtnRemove2.get() )
+ nButtonIndex = 1;
+ if ( &rBtn == m_xBtnRemove3.get() )
+ nButtonIndex = 2;
+ if ( &rBtn == m_xBtnRemove4.get() )
+ nButtonIndex = 3;
+ SCSIZE nRowToDelete = nOffset + nButtonIndex;
+
+ // Check that the index is sensible
+ SCSIZE nCount = theQueryData.GetEntryCount();
+ if (nRowToDelete >= nCount)
+ {
+ SAL_WARN( "sc", "ScFilterDlg::BtnRemoveHdl: could not delete row - invalid index.");
+ return;
+ }
+
+ // Resize maRefreshExceptQuery
+ if (maRefreshExceptQuery.size() < nCount + 1)
+ maRefreshExceptQuery.resize(nCount + 1, false);
+
+ // Move all the subsequent rows back one position;
+ // also find the last row, which we will delete
+ SCSIZE nRowToClear = nCount-1;
+ for (SCSIZE i = nRowToDelete; i < nCount-1; ++i)
+ {
+ if (theQueryData.GetEntry(i+1).bDoQuery)
+ {
+ theQueryData.GetEntry(i) = theQueryData.GetEntry(i+1);
+ }
+ else
+ {
+ nRowToClear = i;
+ break;
+ }
+ }
+
+ // If the next row is being edited, but not confirmed, move it back
+ // one position
+ if (nRowToClear < nCount-1 && maRefreshExceptQuery[nRowToClear+1])
+ {
+ theQueryData.GetEntry(nRowToClear) = theQueryData.GetEntry(nRowToClear+1);
+ maRefreshExceptQuery[nRowToClear] = true;
+ maRefreshExceptQuery[nRowToClear+1] = false;
+ }
+ else
+ {
+ // Remove the very last one, since everything has moved back
+ theQueryData.GetEntry(nRowToClear).bDoQuery = false;
+ theQueryData.GetEntry(nRowToClear).nField = static_cast<SCCOL>(0);
+ maRefreshExceptQuery[nRowToClear] = false;
+ }
+
+ // Always enable the very first row
+ if (!theQueryData.GetEntry(0).bDoQuery)
+ {
+ maRefreshExceptQuery[0] = true;
+ }
+
+ // Refresh the UI
+ RefreshEditRow( nOffset );
+
+ // Special handling if the very first row was cleared
+ if (!theQueryData.GetEntry(0).bDoQuery)
+ {
+ m_xLbConnect1->set_active(-1);
+ m_xLbField1->set_active(0);
+ m_xLbField1->set_sensitive(true);
+ m_xLbCond1->set_active(0);
+ m_xLbCond1->set_sensitive(true);
+ ClearValueList(1);
+ }
+}
+
+IMPL_LINK_NOARG(ScFilterDlg, ScrollHdl, weld::ScrolledWindow&, void)
+{
+ SliderMoved();
+}
+
+void ScFilterDlg::SliderMoved()
+{
+ size_t nOffset = GetSliderPos();
+ RefreshEditRow( nOffset);
+}
+
+size_t ScFilterDlg::GetSliderPos() const
+{
+ return static_cast<size_t>(m_xScrollBar->vadjustment_get_value());
+}
+
+void ScFilterDlg::RefreshEditRow( size_t nOffset )
+{
+ if (nOffset==0)
+ maConnLbArr[0]->hide();
+ else
+ maConnLbArr[0]->show();
+
+ for (size_t i = 0; i < QUERY_ENTRY_COUNT; ++i)
+ {
+ OUString aValStr;
+ size_t nCondPos = 0;
+ size_t nFieldSelPos = 0;
+ size_t nQE = i + nOffset;
+
+ maColorLbArr[i]->set_visible(false);
+
+ if (maRefreshExceptQuery.size() < nQE + 1)
+ maRefreshExceptQuery.resize(nQE + 1, false);
+
+ ScQueryEntry& rEntry = theQueryData.GetEntry( nQE);
+ if ( rEntry.bDoQuery || maRefreshExceptQuery[nQE] )
+ {
+ nCondPos = static_cast<size_t>(rEntry.eOp);
+ if(rEntry.bDoQuery)
+ nFieldSelPos = GetFieldSelPos( static_cast<SCCOL>(rEntry.nField) );
+
+ const ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
+ OUString aQueryStr = rItem.maString.getString();
+ if (rEntry.IsQueryByEmpty())
+ {
+ aValStr = aStrEmpty;
+ maCondLbArr[i]->set_sensitive(false);
+ }
+ else if (rEntry.IsQueryByNonEmpty())
+ {
+ aValStr = aStrNotEmpty;
+ maCondLbArr[i]->set_sensitive(false);
+ }
+ else if (rEntry.IsQueryByTextColor() || rEntry.IsQueryByBackgroundColor())
+ {
+ nCondPos = maCondLbArr[i]->find_text(
+ rEntry.IsQueryByTextColor() ? aStrTextColor : aStrBackgroundColor);
+
+ maValueEdArr[i]->set_visible(false);
+ maColorLbArr[i]->set_visible(true);
+ maColorLbArr[i]->set_sensitive(true);
+ }
+ else
+ {
+ SetValString(aQueryStr, rItem, aValStr);
+ maCondLbArr[i]->set_sensitive(true);
+ }
+ maFieldLbArr[i]->set_sensitive(true);
+ maValueEdArr[i]->set_sensitive(true);
+ maRemoveBtnArr[i]->set_sensitive(true);
+
+ if (nOffset==0)
+ {
+ if (i<3)
+ {
+ if(rEntry.bDoQuery)
+ maConnLbArr[i+1]->set_sensitive(true);
+ else
+ maConnLbArr[i+1]->set_sensitive(false);
+ size_t nQENext = nQE + 1;
+ if (maRefreshExceptQuery.size() < nQENext + 1)
+ maRefreshExceptQuery.resize(nQENext + 1, false);
+ if (theQueryData.GetEntry(nQENext).bDoQuery || maRefreshExceptQuery[nQENext])
+ maConnLbArr[i+1]->set_active( static_cast<sal_uInt16>(theQueryData.GetEntry(nQENext).eConnect) );
+ else
+ maConnLbArr[i+1]->set_active(-1);
+ }
+ }
+ else
+ {
+ if(theQueryData.GetEntry( nQE-1).bDoQuery)
+ maConnLbArr[i]->set_sensitive(true);
+ else
+ maConnLbArr[i]->set_sensitive(false);
+
+ if (maRefreshExceptQuery.size() < nQE + 1)
+ maRefreshExceptQuery.resize(nQE + 1, false);
+ if(rEntry.bDoQuery || maRefreshExceptQuery[nQE])
+ maConnLbArr[i]->set_active( static_cast<sal_uInt16>(rEntry.eConnect) );
+ else
+ maConnLbArr[i]->set_active(-1);
+ }
+
+ }
+ else
+ {
+ if (nOffset==0)
+ {
+ if(i<3)
+ {
+ maConnLbArr[i+1]->set_active(-1);
+ maConnLbArr[i+1]->set_sensitive(false);
+ }
+ }
+ else
+ {
+ if(theQueryData.GetEntry( nQE-1).bDoQuery)
+ maConnLbArr[i]->set_sensitive(true);
+ else
+ maConnLbArr[i]->set_sensitive(false);
+ maConnLbArr[i]->set_active(-1);
+ }
+ maFieldLbArr[i]->set_sensitive(false);
+ maCondLbArr[i]->set_sensitive(false);
+ maValueEdArr[i]->set_sensitive(false);
+ maRemoveBtnArr[i]->set_sensitive(false);
+ }
+ maFieldLbArr[i]->set_active( nFieldSelPos );
+ maCondLbArr [i]->set_active( nCondPos );
+ maValueEdArr[i]->set_entry_text( aValStr );
+ UpdateValueList(i+1);
+ UpdateColorList(i+1);
+ }
+}
+
+void ScFilterDlg::SetValString( const OUString& rQueryStr, const ScQueryEntry::Item& rItem,
+ OUString& rValStr )
+{
+ if (rQueryStr.isEmpty())
+ {
+ pDoc = pViewData ? &pViewData->GetDocument() : nullptr;
+ if (rItem.meType == ScQueryEntry::ByValue)
+ {
+ if (pDoc)
+ {
+ pDoc->GetFormatTable()->GetInputLineString(rItem.mfVal, 0, rValStr);
+ }
+ }
+ else if (rItem.meType == ScQueryEntry::ByDate)
+ {
+ if (pDoc)
+ {
+ SvNumberFormatter* pFormatter = pDoc->GetFormatTable();
+ pFormatter->GetInputLineString(rItem.mfVal,
+ pFormatter->GetStandardFormat( SvNumFormatType::DATE), rValStr);
+ }
+ }
+ else
+ {
+ SAL_WARN( "sc", "ScFilterDlg::SetValString: empty query string, really?");
+ rValStr = rQueryStr;
+ }
+ }
+ else
+ {
+ // XXX NOTE: if not ByString we just assume this has been
+ // set to a proper string corresponding to the numeric
+ // value earlier!
+ rValStr = rQueryStr;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/foptmgr.cxx b/sc/source/ui/dbgui/foptmgr.cxx
new file mode 100644
index 000000000..decaa622b
--- /dev/null
+++ b/sc/source/ui/dbgui/foptmgr.cxx
@@ -0,0 +1,260 @@
+/* -*- 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 <rangeutl.hxx>
+#include <dbdata.hxx>
+#include <viewdata.hxx>
+#include <document.hxx>
+#include <queryparam.hxx>
+#include <globalnames.hxx>
+
+#include <foptmgr.hxx>
+#include <formula/funcutl.hxx>
+
+// ScFilterOptionsMgr (.ui's option helper)
+
+ScFilterOptionsMgr::ScFilterOptionsMgr(
+ ScViewData* ptrViewData,
+ const ScQueryParam& refQueryData,
+ weld::CheckButton* refBtnCase,
+ weld::CheckButton* refBtnRegExp,
+ weld::CheckButton* refBtnHeader,
+ weld::CheckButton* refBtnUnique,
+ weld::CheckButton* refBtnCopyResult,
+ weld::CheckButton* refBtnDestPers,
+ weld::ComboBox* refLbCopyArea,
+ formula::RefEdit* refEdCopyArea,
+ formula::RefButton* refRbCopyArea,
+ weld::Label* refFtDbAreaLabel,
+ weld::Label* refFtDbArea,
+ const OUString& refStrUndefined )
+
+ : pViewData ( ptrViewData ),
+ pDoc ( ptrViewData ? &ptrViewData->GetDocument() : nullptr ),
+ pBtnCase ( refBtnCase ),
+ pBtnRegExp ( refBtnRegExp ),
+ pBtnHeader ( refBtnHeader ),
+ pBtnUnique ( refBtnUnique ),
+ pBtnCopyResult ( refBtnCopyResult ),
+ pBtnDestPers ( refBtnDestPers ),
+ pLbCopyArea ( refLbCopyArea ),
+ pEdCopyArea ( refEdCopyArea ),
+ pRbCopyArea ( refRbCopyArea ),
+ pFtDbAreaLabel ( refFtDbAreaLabel ),
+ pFtDbArea ( refFtDbArea ),
+ rStrUndefined ( refStrUndefined ),
+ rQueryData ( refQueryData )
+{
+ Init();
+}
+
+void ScFilterOptionsMgr::Init()
+{
+//moggi:TODO
+ OSL_ENSURE( pViewData && pDoc, "Init failed :-/" );
+
+ pLbCopyArea->connect_changed( LINK( this, ScFilterOptionsMgr, LbAreaSelHdl ) );
+ pEdCopyArea->SetModifyHdl ( LINK( this, ScFilterOptionsMgr, EdAreaModifyHdl ) );
+ pBtnCopyResult->connect_toggled( LINK( this, ScFilterOptionsMgr, BtnCopyResultHdl ) );
+
+ pBtnCase->set_active( rQueryData.bCaseSens );
+ pBtnHeader->set_active( rQueryData.bHasHeader );
+ pBtnRegExp->set_active( rQueryData.eSearchType == utl::SearchParam::SearchType::Regexp );
+ pBtnUnique->set_active( !rQueryData.bDuplicate );
+
+ if ( pViewData && pDoc )
+ {
+ OUString theAreaStr;
+ ScRange theCurArea ( ScAddress( rQueryData.nCol1,
+ rQueryData.nRow1,
+ pViewData->GetTabNo() ),
+ ScAddress( rQueryData.nCol2,
+ rQueryData.nRow2,
+ pViewData->GetTabNo() ) );
+ ScDBCollection* pDBColl = pDoc->GetDBCollection();
+ OUString theDbArea;
+ OUString theDbName(STR_DB_LOCAL_NONAME);
+ const formula::FormulaGrammar::AddressConvention eConv = pDoc->GetAddressConvention();
+
+ theAreaStr = theCurArea.Format(*pDoc, ScRefFlags::RANGE_ABS_3D, eConv);
+
+ // fill the target area list
+
+ pLbCopyArea->clear();
+ pLbCopyArea->append_text(rStrUndefined);
+
+ ScAreaNameIterator aIter( *pDoc );
+ OUString aName;
+ ScRange aRange;
+ while ( aIter.Next( aName, aRange ) )
+ {
+ OUString aRefStr(aRange.aStart.Format(ScRefFlags::ADDR_ABS_3D, pDoc, eConv));
+ pLbCopyArea->append(aRefStr, aName);
+ }
+
+ pBtnDestPers->set_active(true); // always on when called
+ pLbCopyArea->set_active( 0 );
+ pEdCopyArea->SetText( OUString() );
+
+ /*
+ * Check whether the transferred area is a database area:
+ */
+
+ theDbArea = theAreaStr;
+
+ if ( pDBColl )
+ {
+ ScAddress& rStart = theCurArea.aStart;
+ ScAddress& rEnd = theCurArea.aEnd;
+ const ScDBData* pDBData = pDBColl->GetDBAtArea(
+ rStart.Tab(), rStart.Col(), rStart.Row(), rEnd.Col(), rEnd.Row());
+
+ if ( pDBData )
+ {
+ pBtnHeader->set_active( pDBData->HasHeader() );
+ theDbName = pDBData->GetName();
+
+ pBtnHeader->set_sensitive(theDbName == STR_DB_LOCAL_NONAME);
+ }
+ }
+
+ if ( theDbName != STR_DB_LOCAL_NONAME )
+ {
+ theDbArea += " (" + theDbName + ")";
+
+ pFtDbArea->set_label( theDbArea );
+ }
+ else
+ {
+ pFtDbAreaLabel->set_label( OUString() );
+ pFtDbArea->set_label( OUString() );
+ }
+
+ // position to copy to:
+
+ if ( !rQueryData.bInplace )
+ {
+ OUString aString =
+ ScAddress( rQueryData.nDestCol,
+ rQueryData.nDestRow,
+ rQueryData.nDestTab
+ ).Format(ScRefFlags::ADDR_ABS_3D, pDoc, eConv);
+
+ pBtnCopyResult->set_active(true);
+ pEdCopyArea->SetText( aString );
+ EdAreaModifyHdl( *pEdCopyArea );
+ pLbCopyArea->set_sensitive(true);
+ pEdCopyArea->GetWidget()->set_sensitive(true);
+ pRbCopyArea->GetWidget()->set_sensitive(true);
+ pBtnDestPers->set_sensitive(true);
+ }
+ else
+ {
+ pBtnCopyResult->set_active( false );
+ pEdCopyArea->SetText( OUString() );
+ pLbCopyArea->set_sensitive(false);
+ pEdCopyArea->GetWidget()->set_sensitive(false);
+ pRbCopyArea->GetWidget()->set_sensitive(false);
+ pBtnDestPers->set_sensitive(false);
+ }
+ }
+ else
+ pEdCopyArea->SetText( OUString() );
+}
+
+bool ScFilterOptionsMgr::VerifyPosStr( const OUString& rPosStr ) const
+{
+ OUString aPosStr( rPosStr );
+ sal_Int32 nColonPos = aPosStr.indexOf( ':' );
+
+ if ( -1 != nColonPos )
+ aPosStr = aPosStr.copy( 0, nColonPos );
+
+ ScRefFlags nResult = ScAddress().Parse( aPosStr, *pDoc, pDoc->GetAddressConvention() );
+
+ return (nResult & ScRefFlags::VALID) == ScRefFlags::VALID;
+}
+
+// Handler:
+
+IMPL_LINK( ScFilterOptionsMgr, LbAreaSelHdl, weld::ComboBox&, rLb, void )
+{
+ if ( &rLb == pLbCopyArea )
+ {
+ OUString aString;
+ const sal_Int32 nSelPos = pLbCopyArea->get_active();
+
+ if ( nSelPos > 0 )
+ aString = pLbCopyArea->get_id(nSelPos);
+
+ pEdCopyArea->SetText( aString );
+ }
+}
+
+IMPL_LINK( ScFilterOptionsMgr, EdAreaModifyHdl, formula::RefEdit&, rEd, void )
+{
+ if ( &rEd != pEdCopyArea )
+ return;
+
+ OUString theCurPosStr = rEd.GetText();
+ ScRefFlags nResult = ScAddress().Parse( theCurPosStr, *pDoc, pDoc->GetAddressConvention() );
+
+ if ( (nResult & ScRefFlags::VALID) == ScRefFlags::VALID)
+ {
+ const sal_Int32 nCount = pLbCopyArea->get_count();
+
+ for ( sal_Int32 i=2; i<nCount; ++i )
+ {
+ OUString aStr = pLbCopyArea->get_id(i);
+ if (theCurPosStr == aStr)
+ {
+ pLbCopyArea->set_active( i );
+ return;
+ }
+ }
+
+ }
+ pLbCopyArea->set_active( 0 );
+}
+
+IMPL_LINK( ScFilterOptionsMgr, BtnCopyResultHdl, weld::Toggleable&, rBox, void )
+{
+ if ( &rBox != pBtnCopyResult )
+ return;
+
+ if ( rBox.get_active() )
+ {
+ pBtnDestPers->set_sensitive(true);
+ pLbCopyArea->set_sensitive(true);
+ pEdCopyArea->GetWidget()->set_sensitive(true);
+ pRbCopyArea->GetWidget()->set_sensitive(true);
+ pEdCopyArea->GrabFocus();
+ }
+ else
+ {
+ pBtnDestPers->set_sensitive(false);
+ pLbCopyArea->set_sensitive(false);
+ pEdCopyArea->GetWidget()->set_sensitive(false);
+ pRbCopyArea->GetWidget()->set_sensitive(false);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/imoptdlg.cxx b/sc/source/ui/dbgui/imoptdlg.cxx
new file mode 100644
index 000000000..b285c6ae9
--- /dev/null
+++ b/sc/source/ui/dbgui/imoptdlg.cxx
@@ -0,0 +1,135 @@
+/* -*- 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 <imoptdlg.hxx>
+#include <asciiopt.hxx>
+#include <comphelper/string.hxx>
+#include <unotools/charclass.hxx>
+#include <osl/thread.h>
+#include <o3tl/string_view.hxx>
+#include <global.hxx>
+
+const char pStrFix[] = "FIX";
+
+// The option string can no longer contain a semicolon (because of pick list),
+// therefore, starting with version 336 comma instead
+
+ScImportOptions::ScImportOptions( std::u16string_view rStr )
+{
+ // Use the same string format as ScAsciiOptions,
+ // because the import options string is passed here when a CSV file is loaded and saved again.
+ // The old format is still supported because it might be used in macros.
+
+ bFixedWidth = false;
+ nFieldSepCode = 0;
+ nTextSepCode = 0;
+ eCharSet = RTL_TEXTENCODING_DONTKNOW;
+ bSaveAsShown = true; // "true" if not in string (after CSV import)
+ bQuoteAllText = false;
+ bSaveNumberAsSuch = true;
+ bSaveFormulas = false;
+ bRemoveSpace = false;
+ nSheetToExport = 0;
+ bEvaluateFormulas = true; // true if not present at all, for compatibility
+ sal_Int32 nTokenCount = comphelper::string::getTokenCount(rStr, ',');
+ if ( nTokenCount < 3 )
+ return;
+
+ sal_Int32 nIdx{ 0 };
+ // first 3 tokens: common
+ OUString aToken( o3tl::getToken(rStr, 0, ',', nIdx ) );
+ if( aToken.equalsIgnoreAsciiCase( pStrFix ) )
+ bFixedWidth = true;
+ else
+ nFieldSepCode = ScAsciiOptions::GetWeightedFieldSep( aToken, true);
+ nTextSepCode = static_cast<sal_Unicode>(o3tl::toInt32(o3tl::getToken(rStr, 0, ',', nIdx)));
+ aStrFont = o3tl::getToken(rStr, 0, ',', nIdx);
+ eCharSet = ScGlobal::GetCharsetValue(aStrFont);
+
+ if ( nTokenCount == 4 )
+ {
+ // compatibility with old options string: "Save as shown" as 4th token, numeric
+ bSaveAsShown = o3tl::toInt32(o3tl::getToken(rStr, 0, ',', nIdx)) != 0;
+ bQuoteAllText = true; // use old default then
+ }
+ else
+ {
+ // look at the same positions as in ScAsciiOptions
+ if ( nTokenCount >= 7 )
+ bQuoteAllText = o3tl::getToken(rStr, 3, ',', nIdx) == u"true"; // 7th token
+ if ( nTokenCount >= 8 )
+ bSaveNumberAsSuch = o3tl::getToken(rStr, 0, ',', nIdx) == u"true";
+ if ( nTokenCount >= 9 )
+ bSaveAsShown = o3tl::getToken(rStr, 0, ',', nIdx) == u"true";
+ if ( nTokenCount >= 10 )
+ bSaveFormulas = o3tl::getToken(rStr, 0, ',', nIdx) == u"true";
+ if ( nTokenCount >= 11 )
+ bRemoveSpace = o3tl::getToken(rStr, 0, ',', nIdx) == u"true";
+ if ( nTokenCount >= 12 )
+ {
+ const OUString aTok(o3tl::getToken(rStr,0, ',', nIdx));
+ if (aTok == "-1")
+ nSheetToExport = -1; // all
+ else if (aTok.isEmpty() || CharClass::isAsciiNumeric(aTok))
+ nSheetToExport = aTok.toInt32();
+ else
+ nSheetToExport = -23; // invalid, force error
+ }
+ if ( nTokenCount >= 13 )
+ // If present, defaults to "false".
+ bEvaluateFormulas = o3tl::getToken(rStr, 0, ',', nIdx) == u"true";
+ }
+}
+
+OUString ScImportOptions::BuildString() const
+{
+ OUString aResult;
+
+ if( bFixedWidth )
+ aResult += pStrFix;
+ else
+ aResult += OUString::number(nFieldSepCode);
+ aResult += "," + OUString::number(nTextSepCode) + "," + aStrFont +
+ // use the same string format as ScAsciiOptions:
+ ",1,,0," + // first row, no column info, default language
+ OUString::boolean( bQuoteAllText ) + // same as "quoted field as text" in ScAsciiOptions
+ "," +
+ OUString::boolean( bSaveNumberAsSuch ) + // "save number as such": not in ScAsciiOptions
+ "," +
+ OUString::boolean( bSaveAsShown ) + // "save as shown": not in ScAsciiOptions
+ "," +
+ OUString::boolean( bSaveFormulas ) + // "save formulas": not in ScAsciiOptions
+ "," +
+ OUString::boolean( bRemoveSpace ) + // same as "Remove space" in ScAsciiOptions
+ "," +
+ OUString::number(nSheetToExport) + // Only available for command line --convert-to
+ "," +
+ OUString::boolean( bEvaluateFormulas ) ; // same as "Evaluate formulas" in ScAsciiOptions
+
+ return aResult;
+}
+
+void ScImportOptions::SetTextEncoding( rtl_TextEncoding nEnc )
+{
+ eCharSet = (nEnc == RTL_TEXTENCODING_DONTKNOW ?
+ osl_getThreadTextEncoding() : nEnc);
+ aStrFont = ScGlobal::GetCharsetString( nEnc );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/pfiltdlg.cxx b/sc/source/ui/dbgui/pfiltdlg.cxx
new file mode 100644
index 000000000..e87d676b5
--- /dev/null
+++ b/sc/source/ui/dbgui/pfiltdlg.cxx
@@ -0,0 +1,507 @@
+/* -*- 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 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <viewdata.hxx>
+#include <document.hxx>
+#include <uiitems.hxx>
+#include <global.hxx>
+#include <globalnames.hxx>
+#include <dbdata.hxx>
+#include <scresid.hxx>
+#include <queryentry.hxx>
+#include <filterentries.hxx>
+
+#include <sc.hrc>
+#include <strings.hrc>
+
+#include <pfiltdlg.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <osl/diagnose.h>
+
+ScPivotFilterDlg::ScPivotFilterDlg(weld::Window* pParent, const SfxItemSet& rArgSet,
+ SCTAB nSourceTab )
+ : GenericDialogController(pParent, "modules/scalc/ui/pivotfilterdialog.ui", "PivotFilterDialog")
+ , aStrNone(ScResId(SCSTR_NONE))
+ , aStrEmpty(ScResId(SCSTR_FILTER_EMPTY))
+ , aStrNotEmpty(ScResId(SCSTR_FILTER_NOTEMPTY))
+ , aStrColumn(ScResId(SCSTR_COLUMN_LETTER))
+ , nWhichQuery(rArgSet.GetPool()->GetWhich(SID_QUERY))
+ , theQueryData(static_cast<const ScQueryItem&>(rArgSet.Get(nWhichQuery)).GetQueryData())
+ , pViewData(nullptr)
+ , pDoc(nullptr)
+ , nSrcTab(nSourceTab) // is not in QueryParam
+ , m_xLbField1(m_xBuilder->weld_combo_box("field1"))
+ , m_xLbCond1(m_xBuilder->weld_combo_box("cond1"))
+ , m_xEdVal1(m_xBuilder->weld_combo_box("val1"))
+ , m_xLbConnect1(m_xBuilder->weld_combo_box("connect1"))
+ , m_xLbField2(m_xBuilder->weld_combo_box("field2"))
+ , m_xLbCond2(m_xBuilder->weld_combo_box("cond2"))
+ , m_xEdVal2(m_xBuilder->weld_combo_box("val2"))
+ , m_xLbConnect2(m_xBuilder->weld_combo_box("connect2"))
+ , m_xLbField3(m_xBuilder->weld_combo_box("field3"))
+ , m_xLbCond3(m_xBuilder->weld_combo_box("cond3"))
+ , m_xEdVal3(m_xBuilder->weld_combo_box("val3"))
+ , m_xBtnCase(m_xBuilder->weld_check_button("case"))
+ , m_xBtnRegExp(m_xBuilder->weld_check_button("regexp"))
+ , m_xBtnUnique(m_xBuilder->weld_check_button("unique"))
+ , m_xFtDbArea(m_xBuilder->weld_label("dbarea"))
+{
+ Init( rArgSet );
+}
+
+ScPivotFilterDlg::~ScPivotFilterDlg()
+{
+}
+
+void ScPivotFilterDlg::Init( const SfxItemSet& rArgSet )
+{
+ const ScQueryItem& rQueryItem = static_cast<const ScQueryItem&>(
+ rArgSet.Get( nWhichQuery ));
+
+ m_xBtnCase->connect_toggled( LINK( this, ScPivotFilterDlg, CheckBoxHdl ) );
+
+ m_xLbField1->connect_changed ( LINK( this, ScPivotFilterDlg, LbSelectHdl ) );
+ m_xLbField2->connect_changed ( LINK( this, ScPivotFilterDlg, LbSelectHdl ) );
+ m_xLbField3->connect_changed ( LINK( this, ScPivotFilterDlg, LbSelectHdl ) );
+ m_xLbConnect1->connect_changed( LINK( this, ScPivotFilterDlg, LbSelectHdl ) );
+ m_xLbConnect2->connect_changed( LINK( this, ScPivotFilterDlg, LbSelectHdl ) );
+
+ m_xBtnCase->set_active( theQueryData.bCaseSens );
+ m_xBtnRegExp->set_active( theQueryData.eSearchType == utl::SearchParam::SearchType::Regexp );
+ m_xBtnUnique->set_active( !theQueryData.bDuplicate );
+
+ pViewData = rQueryItem.GetViewData();
+ pDoc = pViewData ? &pViewData->GetDocument() : nullptr;
+
+ // for easier access:
+ aFieldLbArr [0] = m_xLbField1.get();
+ aFieldLbArr [1] = m_xLbField2.get();
+ aFieldLbArr [2] = m_xLbField3.get();
+ aValueEdArr [0] = m_xEdVal1.get();
+ aValueEdArr [1] = m_xEdVal2.get();
+ aValueEdArr [2] = m_xEdVal3.get();
+ aCondLbArr [0] = m_xLbCond1.get();
+ aCondLbArr [1] = m_xLbCond2.get();
+ aCondLbArr [2] = m_xLbCond3.get();
+
+ if ( pViewData && pDoc )
+ {
+ ScRange theCurArea ( ScAddress( theQueryData.nCol1,
+ theQueryData.nRow1,
+ nSrcTab ),
+ ScAddress( theQueryData.nCol2,
+ theQueryData.nRow2,
+ nSrcTab ) );
+ ScDBCollection* pDBColl = pDoc->GetDBCollection();
+ OUString theDbName = STR_DB_LOCAL_NONAME;
+
+ // Check if the passed range is a database range
+
+ if ( pDBColl )
+ {
+ ScAddress& rStart = theCurArea.aStart;
+ ScAddress& rEnd = theCurArea.aEnd;
+ ScDBData* pDBData = pDBColl->GetDBAtArea( rStart.Tab(),
+ rStart.Col(), rStart.Row(),
+ rEnd.Col(), rEnd.Row() );
+ if ( pDBData )
+ theDbName = pDBData->GetName();
+ }
+
+ OUString sLabel = " (" + theDbName + ")";
+ m_xFtDbArea->set_label(sLabel);
+ }
+ else
+ {
+ m_xFtDbArea->set_label(OUString());
+ }
+
+ // Read the field lists and select the entries:
+
+ FillFieldLists();
+
+ for ( SCSIZE i=0; i<3; i++ )
+ {
+ if ( theQueryData.GetEntry(i).bDoQuery )
+ {
+ const ScQueryEntry& rEntry = theQueryData.GetEntry(i);
+ const ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
+ OUString aValStr = rItem.maString.getString();
+ if (rEntry.IsQueryByEmpty())
+ aValStr = aStrEmpty;
+ else if (rEntry.IsQueryByNonEmpty())
+ aValStr = aStrNotEmpty;
+ sal_uInt16 nCondPos = static_cast<sal_uInt16>(rEntry.eOp);
+ sal_uInt16 nFieldSelPos = GetFieldSelPos( static_cast<SCCOL>(rEntry.nField) );
+
+ aFieldLbArr[i]->set_active( nFieldSelPos );
+ aCondLbArr [i]->set_active( nCondPos );
+ UpdateValueList( static_cast<sal_uInt16>(i+1) );
+ aValueEdArr[i]->set_entry_text(aValStr);
+ if (aValStr == aStrEmpty || aValStr == aStrNotEmpty)
+ aCondLbArr[i]->set_sensitive(false);
+ }
+ else
+ {
+ aFieldLbArr[i]->set_active( 0 ); // "none" selected
+ aCondLbArr [i]->set_active( 0 ); // "=" selected
+ UpdateValueList( static_cast<sal_uInt16>(i) );
+ aValueEdArr[i]->set_entry_text(OUString());
+ }
+ aValueEdArr[i]->connect_changed( LINK( this, ScPivotFilterDlg, ValModifyHdl ) );
+ }
+
+ // disable/enable logic:
+
+ if (m_xLbField1->get_active() != 0 && m_xLbField2->get_active() != 0)
+ m_xLbConnect1->set_active( static_cast<sal_uInt16>(theQueryData.GetEntry(1).eConnect) );
+ else
+ m_xLbConnect1->set_active(-1);
+
+ if (m_xLbField2->get_active() != 0 && m_xLbField3->get_active() != 0)
+ m_xLbConnect2->set_active( static_cast<sal_uInt16>(theQueryData.GetEntry(2).eConnect) );
+ else
+ m_xLbConnect2->set_active(-1);
+
+ if (m_xLbField1->get_active() == 0)
+ {
+ m_xLbConnect1->set_sensitive(false);
+ m_xLbField2->set_sensitive(false);
+ m_xLbCond2->set_sensitive(false);
+ m_xEdVal2->set_sensitive(false);
+ }
+ else if (m_xLbConnect1->get_active() == -1)
+ {
+ m_xLbField2->set_sensitive(false);
+ m_xLbCond2->set_sensitive(false);
+ m_xEdVal2->set_sensitive(false);
+ }
+
+ if (m_xLbField2->get_active() == 0)
+ {
+ m_xLbConnect2->set_sensitive(false);
+ m_xLbField3->set_sensitive(false);
+ m_xLbCond3->set_sensitive(false);
+ m_xEdVal3->set_sensitive(false);
+ }
+ else if (m_xLbConnect2->get_active() == -1)
+ {
+ m_xLbField3->set_sensitive(false);
+ m_xLbCond3->set_sensitive(false);
+ m_xEdVal3->set_sensitive(false);
+ }
+}
+
+void ScPivotFilterDlg::FillFieldLists()
+{
+ m_xLbField1->clear();
+ m_xLbField2->clear();
+ m_xLbField3->clear();
+ m_xLbField1->append_text(aStrNone);
+ m_xLbField2->append_text(aStrNone);
+ m_xLbField3->append_text(aStrNone);
+
+ if ( !pDoc )
+ return;
+
+ OUString aFieldName;
+ SCTAB nTab = nSrcTab;
+ SCCOL nFirstCol = theQueryData.nCol1;
+ SCROW nFirstRow = theQueryData.nRow1;
+ SCCOL nMaxCol = theQueryData.nCol2;
+ SCCOL col = 0;
+
+ for ( col=nFirstCol; col<=nMaxCol; col++ )
+ {
+ aFieldName = pDoc->GetString(col, nFirstRow, nTab);
+ if ( aFieldName.isEmpty() )
+ {
+ aFieldName = ScGlobal::ReplaceOrAppend( aStrColumn, u"%1", ScColToAlpha( col ));
+ }
+ m_xLbField1->append_text(aFieldName);
+ m_xLbField2->append_text(aFieldName);
+ m_xLbField3->append_text(aFieldName);
+ }
+}
+
+void ScPivotFilterDlg::UpdateValueList( sal_uInt16 nList )
+{
+ if ( !(pDoc && nList>0 && nList<=3) )
+ return;
+
+ weld::ComboBox* pValList = aValueEdArr[nList-1];
+ sal_Int32 nFieldSelPos = aFieldLbArr[nList-1]->get_active();
+ OUString aCurValue = pValList->get_active_text();
+
+ pValList->clear();
+ pValList->append_text(aStrNotEmpty);
+ pValList->append_text(aStrEmpty);
+
+ if ( pDoc && nFieldSelPos )
+ {
+ SCCOL nColumn = theQueryData.nCol1 + static_cast<SCCOL>(nFieldSelPos) - 1;
+ if (!m_pEntryLists[nColumn])
+ {
+ weld::WaitObject aWaiter(m_xDialog.get());
+
+ SCTAB nTab = nSrcTab;
+ SCROW nFirstRow = theQueryData.nRow1;
+ SCROW nLastRow = theQueryData.nRow2;
+ nFirstRow++;
+ bool bCaseSens = m_xBtnCase->get_active();
+ m_pEntryLists[nColumn].reset( new ScFilterEntries);
+ pDoc->GetFilterEntriesArea(
+ nColumn, nFirstRow, nLastRow, nTab, bCaseSens, *m_pEntryLists[nColumn]);
+ }
+
+ const ScFilterEntries* pColl = m_pEntryLists[nColumn].get();
+ for (const auto& rEntry : *pColl)
+ {
+ pValList->append_text(rEntry.GetString());
+ }
+ }
+ pValList->set_entry_text(aCurValue);
+}
+
+void ScPivotFilterDlg::ClearValueList( sal_uInt16 nList )
+{
+ if ( nList>0 && nList<=3 )
+ {
+ weld::ComboBox* pValList = aValueEdArr[nList-1];
+ pValList->clear();
+ pValList->append_text(aStrNotEmpty);
+ pValList->append_text(aStrEmpty);
+ pValList->set_entry_text(OUString());
+ }
+}
+
+sal_uInt16 ScPivotFilterDlg::GetFieldSelPos( SCCOL nField )
+{
+ if ( nField >= theQueryData.nCol1 && nField <= theQueryData.nCol2 )
+ return static_cast<sal_uInt16>(nField - theQueryData.nCol1 + 1);
+ else
+ return 0;
+}
+
+const ScQueryItem& ScPivotFilterDlg::GetOutputItem()
+{
+ ScQueryParam theParam( theQueryData );
+ sal_Int32 nConnect1 = m_xLbConnect1->get_active();
+ sal_Int32 nConnect2 = m_xLbConnect2->get_active();
+
+ svl::SharedStringPool& rPool = pViewData->GetDocument().GetSharedStringPool();
+
+ for ( SCSIZE i=0; i<3; i++ )
+ {
+ const sal_Int32 nField = aFieldLbArr[i]->get_active();
+ ScQueryOp eOp = static_cast<ScQueryOp>(aCondLbArr[i]->get_active());
+
+ bool bDoThis = (aFieldLbArr[i]->get_active() != 0);
+ theParam.GetEntry(i).bDoQuery = bDoThis;
+
+ if ( bDoThis )
+ {
+ ScQueryEntry& rEntry = theParam.GetEntry(i);
+ ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
+
+ OUString aStrVal = aValueEdArr[i]->get_active_text();
+
+ /*
+ * The dialog returns the specific field values "empty"/"non empty"
+ * as constant in nVal in connection with the bQueryByString switch
+ * set to false
+ */
+ if ( aStrVal == aStrEmpty )
+ {
+ OSL_ASSERT(eOp == SC_EQUAL);
+ rEntry.SetQueryByEmpty();
+ }
+ else if ( aStrVal == aStrNotEmpty )
+ {
+ OSL_ASSERT(eOp == SC_EQUAL);
+ rEntry.SetQueryByNonEmpty();
+ }
+ else
+ {
+ rItem.maString = rPool.intern(aStrVal);
+ rItem.mfVal = 0.0;
+ rItem.meType = ScQueryEntry::ByString;
+ }
+
+ rEntry.nField = nField ? (theQueryData.nCol1 +
+ static_cast<SCCOL>(nField) - 1) : static_cast<SCCOL>(0);
+ rEntry.eOp = eOp;
+ }
+ }
+
+ theParam.GetEntry(1).eConnect = (nConnect1 != -1)
+ ? static_cast<ScQueryConnect>(nConnect1)
+ : SC_AND;
+ theParam.GetEntry(2).eConnect = (nConnect2 != -1)
+ ? static_cast<ScQueryConnect>(nConnect2)
+ : SC_AND;
+
+ theParam.bInplace = false;
+ theParam.nDestTab = 0; // Where do those values come from?
+ theParam.nDestCol = 0;
+ theParam.nDestRow = 0;
+
+ theParam.bDuplicate = !m_xBtnUnique->get_active();
+ theParam.bCaseSens = m_xBtnCase->get_active();
+ theParam.eSearchType = m_xBtnRegExp->get_active() ? utl::SearchParam::SearchType::Regexp : utl::SearchParam::SearchType::Normal;
+
+ pOutItem.reset( new ScQueryItem( nWhichQuery, &theParam ) );
+
+ return *pOutItem;
+}
+
+// Handler:
+
+IMPL_LINK( ScPivotFilterDlg, LbSelectHdl, weld::ComboBox&, rLb, void )
+{
+ /*
+ * Handling the enable/disable logic based on which ListBox was touched:
+ */
+ if (&rLb == m_xLbConnect1.get())
+ {
+ if ( !m_xLbField2->get_sensitive() )
+ {
+ m_xLbField2->set_sensitive(true);
+ m_xLbCond2->set_sensitive(true);
+ m_xEdVal2->set_sensitive(true);
+ }
+ }
+ else if (&rLb == m_xLbConnect2.get())
+ {
+ if ( !m_xLbField3->get_sensitive() )
+ {
+ m_xLbField3->set_sensitive(true);
+ m_xLbCond3->set_sensitive(true);
+ m_xEdVal3->set_sensitive(true);
+ }
+ }
+ else if (&rLb == m_xLbField1.get())
+ {
+ if ( m_xLbField1->get_active() == 0 )
+ {
+ m_xLbConnect1->set_active(-1);
+ m_xLbConnect2->set_active(-1);
+ m_xLbField2->set_active( 0 );
+ m_xLbField3->set_active( 0 );
+ m_xLbCond2->set_active( 0 );
+ m_xLbCond3->set_active( 0 );
+ ClearValueList( 1 );
+ ClearValueList( 2 );
+ ClearValueList( 3 );
+
+ m_xLbConnect1->set_sensitive(false);
+ m_xLbConnect2->set_sensitive(false);
+ m_xLbField2->set_sensitive(false);
+ m_xLbField3->set_sensitive(false);
+ m_xLbCond2->set_sensitive(false);
+ m_xLbCond3->set_sensitive(false);
+ m_xEdVal2->set_sensitive(false);
+ m_xEdVal3->set_sensitive(false);
+ }
+ else
+ {
+ UpdateValueList( 1 );
+ if ( !m_xLbConnect1->get_sensitive() )
+ {
+ m_xLbConnect1->set_sensitive(true);
+ }
+ }
+ }
+ else if (&rLb == m_xLbField2.get())
+ {
+ if ( m_xLbField2->get_active() == 0 )
+ {
+ m_xLbConnect2->set_active(-1);
+ m_xLbField3->set_active( 0 );
+ m_xLbCond3->set_active( 0 );
+ ClearValueList( 2 );
+ ClearValueList( 3 );
+
+ m_xLbConnect2->set_sensitive(false);
+ m_xLbField3->set_sensitive(false);
+ m_xLbCond3->set_sensitive(false);
+ m_xEdVal3->set_sensitive(false);
+ }
+ else
+ {
+ UpdateValueList( 2 );
+ if (!m_xLbConnect2->get_sensitive())
+ {
+ m_xLbConnect2->set_sensitive(true);
+ }
+ }
+ }
+ else if (&rLb == m_xLbField3.get())
+ {
+ if (m_xLbField3->get_active() == 0)
+ ClearValueList(3);
+ else
+ UpdateValueList(3);
+ }
+}
+
+IMPL_LINK(ScPivotFilterDlg, CheckBoxHdl, weld::Toggleable&, rBox, void)
+{
+ // update the value lists when dealing with uppercase/lowercase
+
+ if (&rBox != m_xBtnCase.get()) // value lists
+ return;
+
+ for (auto& a : m_pEntryLists)
+ a.reset();
+
+ OUString aCurVal1 = m_xEdVal1->get_active_text();
+ OUString aCurVal2 = m_xEdVal2->get_active_text();
+ OUString aCurVal3 = m_xEdVal3->get_active_text();
+ UpdateValueList( 1 );
+ UpdateValueList( 2 );
+ UpdateValueList( 3 );
+ m_xEdVal1->set_entry_text(aCurVal1);
+ m_xEdVal2->set_entry_text(aCurVal2);
+ m_xEdVal3->set_entry_text(aCurVal3);
+}
+
+IMPL_LINK( ScPivotFilterDlg, ValModifyHdl, weld::ComboBox&, rEd, void )
+{
+ OUString aStrVal = rEd.get_active_text();
+ weld::ComboBox* pLb = m_xLbCond1.get();
+
+ if ( &rEd == m_xEdVal2.get() ) pLb = m_xLbCond2.get();
+ else if ( &rEd == m_xEdVal3.get() ) pLb = m_xLbCond3.get();
+
+ // if cond of the special values "empty"/"non-empty" was chosen only the
+ // =-operand makes sense:
+
+ if ( aStrEmpty == aStrVal || aStrNotEmpty == aStrVal )
+ {
+ pLb->set_active_text(OUString('='));
+ pLb->set_sensitive(false);
+ }
+ else
+ pLb->set_sensitive(true);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/pvfundlg.cxx b/sc/source/ui/dbgui/pvfundlg.cxx
new file mode 100644
index 000000000..90a13e920
--- /dev/null
+++ b/sc/source/ui/dbgui/pvfundlg.cxx
@@ -0,0 +1,973 @@
+/* -*- 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 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <pvfundlg.hxx>
+
+#include <com/sun/star/sheet/DataPilotFieldReferenceType.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReferenceItemType.hpp>
+#include <com/sun/star/sheet/DataPilotFieldLayoutMode.hpp>
+#include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
+#include <com/sun/star/sheet/DataPilotFieldShowItemsMode.hpp>
+
+#include <osl/diagnose.h>
+
+#include <scresid.hxx>
+#include <dpobject.hxx>
+#include <dpsave.hxx>
+#include <pvfundlg.hrc>
+#include <globstr.hrc>
+#include <dputil.hxx>
+
+#include <vector>
+
+using namespace ::com::sun::star::sheet;
+
+using ::com::sun::star::uno::Sequence;
+using ::std::vector;
+
+namespace {
+
+/** Appends all strings from the Sequence to the list box.
+
+ Empty strings are replaced by a localized "(empty)" entry and inserted at
+ the specified position.
+
+ @return true = The passed string list contains an empty string entry.
+ */
+
+bool lclFillListBox(weld::ComboBox& rLBox, const Sequence< OUString >& rStrings)
+{
+ bool bEmpty = false;
+ for (const OUString& str : rStrings)
+ {
+ if (!str.isEmpty())
+ rLBox.append_text(str);
+ else
+ {
+ rLBox.append_text(ScResId(STR_EMPTYDATA));
+ bEmpty = true;
+ }
+ }
+ return bEmpty;
+}
+
+bool lclFillListBox(weld::ComboBox& rLBox, const vector<ScDPLabelData::Member>& rMembers, int nEmptyPos)
+{
+ bool bEmpty = false;
+ vector<ScDPLabelData::Member>::const_iterator itr = rMembers.begin(), itrEnd = rMembers.end();
+ for (; itr != itrEnd; ++itr)
+ {
+ OUString aName = itr->getDisplayName();
+ if (!aName.isEmpty())
+ rLBox.append_text(aName);
+ else
+ {
+ rLBox.insert_text(nEmptyPos, ScResId(STR_EMPTYDATA));
+ bEmpty = true;
+ }
+ }
+ return bEmpty;
+}
+
+bool lclFillListBox(weld::TreeView& rLBox, const vector<ScDPLabelData::Member>& rMembers)
+{
+ bool bEmpty = false;
+ for (const auto& rMember : rMembers)
+ {
+ rLBox.append();
+ int pos = rLBox.n_children() - 1;
+ rLBox.set_toggle(pos, TRISTATE_FALSE);
+ OUString aName = rMember.getDisplayName();
+ if (!aName.isEmpty())
+ rLBox.set_text(pos, aName, 0);
+ else
+ {
+ rLBox.set_text(pos, ScResId(STR_EMPTYDATA), 0);
+ bEmpty = true;
+ }
+ }
+ return bEmpty;
+}
+
+/** This table represents the order of the strings in the resource string array. */
+const PivotFunc spnFunctions[] =
+{
+ PivotFunc::Sum,
+ PivotFunc::Count,
+ PivotFunc::Average,
+ PivotFunc::Median,
+ PivotFunc::Max,
+ PivotFunc::Min,
+ PivotFunc::Product,
+ PivotFunc::CountNum,
+ PivotFunc::StdDev,
+ PivotFunc::StdDevP,
+ PivotFunc::StdVar,
+ PivotFunc::StdVarP
+};
+
+const sal_uInt16 SC_BASEITEM_PREV_POS = 0;
+const sal_uInt16 SC_BASEITEM_NEXT_POS = 1;
+const sal_uInt16 SC_BASEITEM_USER_POS = 2;
+
+const sal_uInt16 SC_SORTNAME_POS = 0;
+const sal_uInt16 SC_SORTDATA_POS = 1;
+
+const tools::Long SC_SHOW_DEFAULT = 10;
+
+} // namespace
+
+ScDPFunctionListBox::ScDPFunctionListBox(std::unique_ptr<weld::TreeView> xControl)
+ : m_xControl(std::move(xControl))
+{
+ FillFunctionNames();
+}
+
+void ScDPFunctionListBox::SetSelection( PivotFunc nFuncMask )
+{
+ if( (nFuncMask == PivotFunc::NONE) || (nFuncMask == PivotFunc::Auto) )
+ m_xControl->unselect_all();
+ else
+ {
+ for( sal_Int32 nEntry = 0, nCount = m_xControl->n_children(); nEntry < nCount; ++nEntry )
+ {
+ if (bool(nFuncMask & spnFunctions[ nEntry ]))
+ m_xControl->select(nEntry);
+ else
+ m_xControl->unselect(nEntry);
+ }
+ }
+}
+
+PivotFunc ScDPFunctionListBox::GetSelection() const
+{
+ PivotFunc nFuncMask = PivotFunc::NONE;
+ std::vector<int> aRows = m_xControl->get_selected_rows();
+ for (int nSel : aRows)
+ nFuncMask |= spnFunctions[nSel];
+ return nFuncMask;
+}
+
+void ScDPFunctionListBox::FillFunctionNames()
+{
+ OSL_ENSURE( !m_xControl->n_children(), "ScDPMultiFuncListBox::FillFunctionNames - do not add texts to resource" );
+ m_xControl->clear();
+ m_xControl->freeze();
+ for (size_t nIndex = 0; nIndex < SAL_N_ELEMENTS(SCSTR_DPFUNCLISTBOX); ++nIndex)
+ m_xControl->append_text(ScResId(SCSTR_DPFUNCLISTBOX[nIndex]));
+ m_xControl->thaw();
+ assert(m_xControl->n_children() == SAL_N_ELEMENTS(spnFunctions));
+}
+
+namespace
+{
+ int FromDataPilotFieldReferenceType(int eMode)
+ {
+ switch (eMode)
+ {
+ case DataPilotFieldReferenceType::NONE:
+ return 0;
+ case DataPilotFieldReferenceType::ITEM_DIFFERENCE:
+ return 1;
+ case DataPilotFieldReferenceType::ITEM_PERCENTAGE:
+ return 2;
+ case DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE:
+ return 3;
+ case DataPilotFieldReferenceType::RUNNING_TOTAL:
+ return 4;
+ case DataPilotFieldReferenceType::ROW_PERCENTAGE:
+ return 5;
+ case DataPilotFieldReferenceType::COLUMN_PERCENTAGE:
+ return 6;
+ case DataPilotFieldReferenceType::TOTAL_PERCENTAGE:
+ return 7;
+ case DataPilotFieldReferenceType::INDEX:
+ return 8;
+ }
+ return -1;
+ }
+
+ int ToDataPilotFieldReferenceType(int nPos)
+ {
+ switch (nPos)
+ {
+ case 0:
+ return DataPilotFieldReferenceType::NONE;
+ case 1:
+ return DataPilotFieldReferenceType::ITEM_DIFFERENCE;
+ case 2:
+ return DataPilotFieldReferenceType::ITEM_PERCENTAGE;
+ case 3:
+ return DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE;
+ case 4:
+ return DataPilotFieldReferenceType::RUNNING_TOTAL;
+ case 5:
+ return DataPilotFieldReferenceType::ROW_PERCENTAGE;
+ case 6:
+ return DataPilotFieldReferenceType::COLUMN_PERCENTAGE;
+ case 7:
+ return DataPilotFieldReferenceType::TOTAL_PERCENTAGE;
+ case 8:
+ return DataPilotFieldReferenceType::INDEX;
+ }
+ return DataPilotFieldReferenceType::NONE;
+
+ }
+}
+
+ScDPFunctionDlg::ScDPFunctionDlg(
+ weld::Widget* pParent, const ScDPLabelDataVector& rLabelVec,
+ const ScDPLabelData& rLabelData, const ScPivotFuncData& rFuncData)
+ : GenericDialogController(pParent, "modules/scalc/ui/datafielddialog.ui", "DataFieldDialog")
+ , mxLbFunc(new ScDPFunctionListBox(m_xBuilder->weld_tree_view("functions")))
+ , mxFtName(m_xBuilder->weld_label("name"))
+ , mxLbType(m_xBuilder->weld_combo_box("type"))
+ , mxFtBaseField(m_xBuilder->weld_label("basefieldft"))
+ , mxLbBaseField(m_xBuilder->weld_combo_box("basefield"))
+ , mxFtBaseItem(m_xBuilder->weld_label("baseitemft"))
+ , mxLbBaseItem(m_xBuilder->weld_combo_box("baseitem"))
+ , mxBtnOk(m_xBuilder->weld_button("ok"))
+ , mxBtnCancel(m_xBuilder->weld_button("cancel"))
+ , mxExpander(m_xBuilder->weld_expander("expander"))
+ , mrLabelVec(rLabelVec)
+ , mbEmptyItem(false)
+{
+ mxLbFunc->set_size_request(-1, mxLbFunc->get_height_rows(8));
+
+ Init(rLabelData, rFuncData);
+}
+
+ScDPFunctionDlg::~ScDPFunctionDlg()
+{
+}
+
+PivotFunc ScDPFunctionDlg::GetFuncMask() const
+{
+ return mxLbFunc->GetSelection();
+}
+
+DataPilotFieldReference ScDPFunctionDlg::GetFieldRef() const
+{
+ DataPilotFieldReference aRef;
+
+ aRef.ReferenceType = ToDataPilotFieldReferenceType(mxLbType->get_active());
+ aRef.ReferenceField = GetBaseFieldName(mxLbBaseField->get_active_text());
+
+ sal_Int32 nBaseItemPos = mxLbBaseItem->get_active();
+ switch( nBaseItemPos )
+ {
+ case SC_BASEITEM_PREV_POS:
+ aRef.ReferenceItemType = DataPilotFieldReferenceItemType::PREVIOUS;
+ break;
+ case SC_BASEITEM_NEXT_POS:
+ aRef.ReferenceItemType = DataPilotFieldReferenceItemType::NEXT;
+ break;
+ default:
+ {
+ aRef.ReferenceItemType = DataPilotFieldReferenceItemType::NAMED;
+ if( !mbEmptyItem || (nBaseItemPos > SC_BASEITEM_USER_POS) )
+ aRef.ReferenceItemName = GetBaseItemName(mxLbBaseItem->get_active_text());
+ }
+ }
+
+ return aRef;
+}
+
+void ScDPFunctionDlg::Init( const ScDPLabelData& rLabelData, const ScPivotFuncData& rFuncData )
+{
+ mxBtnOk->connect_clicked( LINK( this, ScDPFunctionDlg, ButtonClicked ) );
+ mxBtnCancel->connect_clicked( LINK( this, ScDPFunctionDlg, ButtonClicked ) );
+
+ // list box
+ PivotFunc nFuncMask = (rFuncData.mnFuncMask == PivotFunc::NONE) ? PivotFunc::Sum : rFuncData.mnFuncMask;
+ mxLbFunc->SetSelection( nFuncMask );
+
+ // field name
+ mxFtName->set_label(rLabelData.getDisplayName());
+
+ // handlers
+ mxLbFunc->connect_row_activated( LINK( this, ScDPFunctionDlg, DblClickHdl ) );
+ mxLbType->connect_changed( LINK( this, ScDPFunctionDlg, SelectHdl ) );
+ mxLbBaseField->connect_changed( LINK( this, ScDPFunctionDlg, SelectHdl ) );
+
+ // base field list box
+ OUString aSelectedEntry;
+ for( const auto& rxLabel : mrLabelVec )
+ {
+ mxLbBaseField->append_text(rxLabel->getDisplayName());
+ maBaseFieldNameMap.emplace(rxLabel->getDisplayName(), rxLabel->maName);
+ if (rxLabel->maName == rFuncData.maFieldRef.ReferenceField)
+ aSelectedEntry = rxLabel->getDisplayName();
+ }
+
+ // select field reference type
+ mxLbType->set_active(FromDataPilotFieldReferenceType(rFuncData.maFieldRef.ReferenceType));
+ SelectHdl( *mxLbType ); // enables base field/item list boxes
+
+ // select base field
+ mxLbBaseField->set_active_text(aSelectedEntry);
+ if (mxLbBaseField->get_active() == -1)
+ mxLbBaseField->set_active(0);
+ SelectHdl( *mxLbBaseField ); // fills base item list, selects base item
+
+ // select base item
+ switch( rFuncData.maFieldRef.ReferenceItemType )
+ {
+ case DataPilotFieldReferenceItemType::PREVIOUS:
+ mxLbBaseItem->set_active( SC_BASEITEM_PREV_POS );
+ break;
+ case DataPilotFieldReferenceItemType::NEXT:
+ mxLbBaseItem->set_active( SC_BASEITEM_NEXT_POS );
+ break;
+ default:
+ {
+ if( mbEmptyItem && rFuncData.maFieldRef.ReferenceItemName.isEmpty() )
+ {
+ // select special "(empty)" entry added before other items
+ mxLbBaseItem->set_active( SC_BASEITEM_USER_POS );
+ }
+ else
+ {
+ sal_Int32 nStartPos = mbEmptyItem ? (SC_BASEITEM_USER_POS + 1) : SC_BASEITEM_USER_POS;
+ sal_Int32 nPos = FindBaseItemPos( rFuncData.maFieldRef.ReferenceItemName, nStartPos );
+ if( nPos == -1)
+ nPos = (mxLbBaseItem->get_count() > SC_BASEITEM_USER_POS) ? SC_BASEITEM_USER_POS : SC_BASEITEM_PREV_POS;
+ mxLbBaseItem->set_active( nPos );
+ }
+ }
+ }
+}
+
+const OUString& ScDPFunctionDlg::GetBaseFieldName(const OUString& rLayoutName) const
+{
+ NameMapType::const_iterator itr = maBaseFieldNameMap.find(rLayoutName);
+ return itr == maBaseFieldNameMap.end() ? rLayoutName : itr->second;
+}
+
+const OUString& ScDPFunctionDlg::GetBaseItemName(const OUString& rLayoutName) const
+{
+ NameMapType::const_iterator itr = maBaseItemNameMap.find(rLayoutName);
+ return itr == maBaseItemNameMap.end() ? rLayoutName : itr->second;
+}
+
+sal_Int32 ScDPFunctionDlg::FindBaseItemPos( std::u16string_view rEntry, sal_Int32 nStartPos ) const
+{
+ sal_Int32 nPos = nStartPos;
+ bool bFound = false;
+ while (nPos < mxLbBaseItem->get_count())
+ {
+ // translate the displayed field name back to its original field name.
+ const OUString& rInName = mxLbBaseItem->get_text(nPos);
+ const OUString& rName = GetBaseItemName(rInName);
+ if (rName == rEntry)
+ {
+ bFound = true;
+ break;
+ }
+ ++nPos;
+ }
+ return bFound ? nPos : -1;
+}
+
+IMPL_LINK( ScDPFunctionDlg, SelectHdl, weld::ComboBox&, rLBox, void )
+{
+ if (&rLBox == mxLbType.get())
+ {
+ bool bEnableField, bEnableItem;
+ switch (ToDataPilotFieldReferenceType(mxLbType->get_active()))
+ {
+ case DataPilotFieldReferenceType::ITEM_DIFFERENCE:
+ case DataPilotFieldReferenceType::ITEM_PERCENTAGE:
+ case DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE:
+ bEnableField = bEnableItem = true;
+ break;
+
+ case DataPilotFieldReferenceType::RUNNING_TOTAL:
+ bEnableField = true;
+ bEnableItem = false;
+ break;
+
+ default:
+ bEnableField = bEnableItem = false;
+ }
+
+ bEnableField &= (mxLbBaseField->get_count() > 0);
+ mxFtBaseField->set_sensitive( bEnableField );
+ mxLbBaseField->set_sensitive( bEnableField );
+
+ bEnableItem &= bEnableField;
+ mxFtBaseItem->set_sensitive( bEnableItem );
+ mxLbBaseItem->set_sensitive( bEnableItem );
+ }
+ else if (&rLBox == mxLbBaseField.get())
+ {
+ // keep "previous" and "next" entries
+ while (mxLbBaseItem->get_count() > SC_BASEITEM_USER_POS)
+ mxLbBaseItem->remove(SC_BASEITEM_USER_POS);
+
+ // update item list for current base field
+ mbEmptyItem = false;
+ size_t nBasePos = mxLbBaseField->get_active();
+ if (nBasePos < mrLabelVec.size())
+ {
+ const vector<ScDPLabelData::Member>& rMembers = mrLabelVec[nBasePos]->maMembers;
+ mbEmptyItem = lclFillListBox(*mxLbBaseItem, rMembers, SC_BASEITEM_USER_POS);
+ // build cache for base names.
+ NameMapType aMap;
+ for (const auto& rMember : rMembers)
+ aMap.emplace(rMember.getDisplayName(), rMember.maName);
+ maBaseItemNameMap.swap(aMap);
+ }
+
+ // select base item
+ sal_uInt16 nItemPos = (mxLbBaseItem->get_count() > SC_BASEITEM_USER_POS) ? SC_BASEITEM_USER_POS : SC_BASEITEM_PREV_POS;
+ mxLbBaseItem->set_active( nItemPos );
+ }
+}
+
+IMPL_LINK(ScDPFunctionDlg, ButtonClicked, weld::Button&, rButton, void)
+{
+ if (&rButton == mxBtnOk.get())
+ response(RET_OK);
+ else
+ response(RET_CANCEL);
+}
+
+IMPL_LINK_NOARG(ScDPFunctionDlg, DblClickHdl, weld::TreeView&, bool)
+{
+ m_xDialog->response(RET_OK);
+ return true;
+}
+
+ScDPSubtotalDlg::ScDPSubtotalDlg(weld::Widget* pParent, ScDPObject& rDPObj,
+ const ScDPLabelData& rLabelData, const ScPivotFuncData& rFuncData,
+ const ScDPNameVec& rDataFields, bool bEnableLayout)
+ : GenericDialogController(pParent, "modules/scalc/ui/pivotfielddialog.ui", "PivotFieldDialog")
+ , mrDPObj(rDPObj)
+ , mrDataFields(rDataFields)
+ , maLabelData(rLabelData)
+ , mbEnableLayout(bEnableLayout)
+ , mxRbNone(m_xBuilder->weld_radio_button("none"))
+ , mxRbAuto(m_xBuilder->weld_radio_button("auto"))
+ , mxRbUser(m_xBuilder->weld_radio_button("user"))
+ , mxLbFunc(new ScDPFunctionListBox(m_xBuilder->weld_tree_view("functions")))
+ , mxFtName(m_xBuilder->weld_label("name"))
+ , mxCbShowAll(m_xBuilder->weld_check_button("showall"))
+ , mxBtnOk(m_xBuilder->weld_button("ok"))
+ , mxBtnCancel(m_xBuilder->weld_button("cancel"))
+ , mxBtnOptions(m_xBuilder->weld_button("options"))
+{
+ mxLbFunc->set_selection_mode(SelectionMode::Multiple);
+ mxLbFunc->set_size_request(-1, mxLbFunc->get_height_rows(8));
+ Init(rLabelData, rFuncData);
+}
+
+ScDPSubtotalDlg::~ScDPSubtotalDlg()
+{
+ CloseSubdialog();
+}
+
+void ScDPSubtotalDlg::CloseSubdialog()
+{
+ if (mxOptionsDlg && mxOptionsDlg->getDialog())
+ {
+ mxOptionsDlg->getDialog()->response(RET_CANCEL);
+ mxOptionsDlg = nullptr;
+ }
+}
+
+PivotFunc ScDPSubtotalDlg::GetFuncMask() const
+{
+ PivotFunc nFuncMask = PivotFunc::NONE;
+
+ if (mxRbAuto->get_active())
+ nFuncMask = PivotFunc::Auto;
+ else if (mxRbUser->get_active())
+ nFuncMask = mxLbFunc->GetSelection();
+
+ return nFuncMask;
+}
+
+void ScDPSubtotalDlg::FillLabelData( ScDPLabelData& rLabelData ) const
+{
+ rLabelData.mnFuncMask = GetFuncMask();
+ rLabelData.mnUsedHier = maLabelData.mnUsedHier;
+ rLabelData.mbShowAll = mxCbShowAll->get_active();
+ rLabelData.maMembers = maLabelData.maMembers;
+ rLabelData.maSortInfo = maLabelData.maSortInfo;
+ rLabelData.maLayoutInfo = maLabelData.maLayoutInfo;
+ rLabelData.maShowInfo = maLabelData.maShowInfo;
+ rLabelData.mbRepeatItemLabels = maLabelData.mbRepeatItemLabels;
+}
+
+void ScDPSubtotalDlg::Init( const ScDPLabelData& rLabelData, const ScPivotFuncData& rFuncData )
+{
+ mxBtnOk->connect_clicked( LINK( this, ScDPSubtotalDlg, ButtonClicked ) );
+ mxBtnCancel->connect_clicked( LINK( this, ScDPSubtotalDlg, ButtonClicked ) );
+
+ // field name
+ mxFtName->set_label(rLabelData.getDisplayName());
+
+ // radio buttons
+ mxRbNone->connect_toggled( LINK( this, ScDPSubtotalDlg, RadioClickHdl ) );
+ mxRbAuto->connect_toggled( LINK( this, ScDPSubtotalDlg, RadioClickHdl ) );
+ mxRbUser->connect_toggled( LINK( this, ScDPSubtotalDlg, RadioClickHdl ) );
+
+ weld::RadioButton* pRBtn = nullptr;
+ switch( rFuncData.mnFuncMask )
+ {
+ case PivotFunc::NONE: pRBtn = mxRbNone.get(); break;
+ case PivotFunc::Auto: pRBtn = mxRbAuto.get(); break;
+ default: pRBtn = mxRbUser.get();
+ }
+ pRBtn->set_active(true);
+ RadioClickHdl(*pRBtn);
+
+ // list box
+ mxLbFunc->SetSelection( rFuncData.mnFuncMask );
+ mxLbFunc->connect_row_activated( LINK( this, ScDPSubtotalDlg, DblClickHdl ) );
+
+ // show all
+ mxCbShowAll->set_active( rLabelData.mbShowAll );
+
+ // options
+ mxBtnOptions->connect_clicked( LINK( this, ScDPSubtotalDlg, ClickHdl ) );
+}
+
+IMPL_LINK(ScDPSubtotalDlg, ButtonClicked, weld::Button&, rButton, void)
+{
+ CloseSubdialog();
+
+ if (&rButton == mxBtnOk.get())
+ response(RET_OK);
+ else
+ response(RET_CANCEL);
+}
+
+IMPL_LINK(ScDPSubtotalDlg, RadioClickHdl, weld::Toggleable&, rBtn, void)
+{
+ if (!rBtn.get_active())
+ return;
+ mxLbFunc->set_sensitive(mxRbUser->get_active());
+}
+
+IMPL_LINK_NOARG(ScDPSubtotalDlg, DblClickHdl, weld::TreeView&, bool)
+{
+ m_xDialog->response(RET_OK);
+ return true;
+}
+
+IMPL_LINK(ScDPSubtotalDlg, ClickHdl, weld::Button&, rBtn, void)
+{
+ if (&rBtn == mxBtnOptions.get())
+ {
+ mxOptionsDlg = std::make_shared<ScDPSubtotalOptDlg>(m_xDialog.get(), mrDPObj, maLabelData, mrDataFields, mbEnableLayout);
+
+ weld::DialogController::runAsync(mxOptionsDlg, [this](int nResult) {
+ if (nResult == RET_OK)
+ mxOptionsDlg->FillLabelData(maLabelData);
+ mxOptionsDlg = nullptr;
+ });
+ }
+}
+
+namespace
+{
+ int FromDataPilotFieldLayoutMode(int eMode)
+ {
+ switch (eMode)
+ {
+ case DataPilotFieldLayoutMode::TABULAR_LAYOUT:
+ return 0;
+ case DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_TOP:
+ return 1;
+ case DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_BOTTOM:
+ return 2;
+ }
+ return -1;
+ }
+
+ int ToDataPilotFieldLayoutMode(int nPos)
+ {
+ switch (nPos)
+ {
+ case 0:
+ return DataPilotFieldLayoutMode::TABULAR_LAYOUT;
+ case 1:
+ return DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_TOP;
+ case 2:
+ return DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_BOTTOM;
+ }
+ return DataPilotFieldLayoutMode::TABULAR_LAYOUT;
+ }
+
+ int FromDataPilotFieldShowItemsMode(int eMode)
+ {
+ switch (eMode)
+ {
+ case DataPilotFieldShowItemsMode::FROM_TOP:
+ return 0;
+ case DataPilotFieldShowItemsMode::FROM_BOTTOM:
+ return 1;
+ }
+ return -1;
+ }
+
+ int ToDataPilotFieldShowItemsMode(int nPos)
+ {
+ switch (nPos)
+ {
+ case 0:
+ return DataPilotFieldShowItemsMode::FROM_TOP;
+ case 1:
+ return DataPilotFieldShowItemsMode::FROM_BOTTOM;
+ }
+ return DataPilotFieldShowItemsMode::FROM_TOP;
+ }
+}
+
+ScDPSubtotalOptDlg::ScDPSubtotalOptDlg(weld::Window* pParent, ScDPObject& rDPObj,
+ const ScDPLabelData& rLabelData, const ScDPNameVec& rDataFields,
+ bool bEnableLayout )
+ : GenericDialogController(pParent, "modules/scalc/ui/datafieldoptionsdialog.ui",
+ "DataFieldOptionsDialog")
+ , m_xLbSortBy(m_xBuilder->weld_combo_box("sortby"))
+ , m_xRbSortAsc(m_xBuilder->weld_radio_button("ascending"))
+ , m_xRbSortDesc(m_xBuilder->weld_radio_button("descending"))
+ , m_xRbSortMan(m_xBuilder->weld_radio_button("manual"))
+ , m_xLayoutFrame(m_xBuilder->weld_widget("layoutframe"))
+ , m_xLbLayout(m_xBuilder->weld_combo_box("layout"))
+ , m_xCbLayoutEmpty(m_xBuilder->weld_check_button("emptyline"))
+ , m_xCbRepeatItemLabels(m_xBuilder->weld_check_button("repeatitemlabels"))
+ , m_xCbShow(m_xBuilder->weld_check_button("show"))
+ , m_xNfShow(m_xBuilder->weld_spin_button("items"))
+ , m_xFtShow(m_xBuilder->weld_label("showft"))
+ , m_xFtShowFrom(m_xBuilder->weld_label("showfromft"))
+ , m_xLbShowFrom(m_xBuilder->weld_combo_box("from"))
+ , m_xFtShowUsing(m_xBuilder->weld_label("usingft"))
+ , m_xLbShowUsing(m_xBuilder->weld_combo_box("using"))
+ , m_xHideFrame(m_xBuilder->weld_widget("hideframe"))
+ , m_xLbHide(m_xBuilder->weld_tree_view("hideitems"))
+ , m_xFtHierarchy(m_xBuilder->weld_label("hierarchyft"))
+ , m_xLbHierarchy(m_xBuilder->weld_combo_box("hierarchy"))
+ , m_xBtnOk(m_xBuilder->weld_button("ok"))
+ , m_xBtnCancel(m_xBuilder->weld_button("cancel"))
+ , mrDPObj(rDPObj)
+ , maLabelData(rLabelData)
+{
+ m_xLbHide->enable_toggle_buttons(weld::ColumnToggleType::Check);
+
+ m_xLbSortBy->set_size_request(m_xLbSortBy->get_approximate_digit_width() * 18, -1);
+ m_xLbHide->set_size_request(-1, m_xLbHide->get_height_rows(5));
+ Init(rDataFields, bEnableLayout);
+}
+
+ScDPSubtotalOptDlg::~ScDPSubtotalOptDlg()
+{
+}
+
+void ScDPSubtotalOptDlg::FillLabelData( ScDPLabelData& rLabelData ) const
+{
+ // *** SORTING ***
+
+ if (m_xRbSortMan->get_active())
+ rLabelData.maSortInfo.Mode = DataPilotFieldSortMode::MANUAL;
+ else if (m_xLbSortBy->get_active() == SC_SORTNAME_POS)
+ rLabelData.maSortInfo.Mode = DataPilotFieldSortMode::NAME;
+ else
+ rLabelData.maSortInfo.Mode = DataPilotFieldSortMode::DATA;
+
+ ScDPName aFieldName = GetFieldName(m_xLbSortBy->get_active_text());
+ if (!aFieldName.maName.isEmpty())
+ {
+ rLabelData.maSortInfo.Field =
+ ScDPUtil::createDuplicateDimensionName(aFieldName.maName, aFieldName.mnDupCount);
+ rLabelData.maSortInfo.IsAscending = m_xRbSortAsc->get_active();
+ }
+
+ // *** LAYOUT MODE ***
+
+ rLabelData.maLayoutInfo.LayoutMode = ToDataPilotFieldLayoutMode(m_xLbLayout->get_active());
+ rLabelData.maLayoutInfo.AddEmptyLines = m_xCbLayoutEmpty->get_active();
+ rLabelData.mbRepeatItemLabels = m_xCbRepeatItemLabels->get_active();
+
+ // *** AUTO SHOW ***
+
+ aFieldName = GetFieldName(m_xLbShowUsing->get_active_text());
+ if (!aFieldName.maName.isEmpty())
+ {
+ rLabelData.maShowInfo.IsEnabled = m_xCbShow->get_active();
+ rLabelData.maShowInfo.ShowItemsMode = ToDataPilotFieldShowItemsMode(m_xLbShowFrom->get_active());
+ rLabelData.maShowInfo.ItemCount = sal::static_int_cast<sal_Int32>( m_xNfShow->get_value() );
+ rLabelData.maShowInfo.DataField =
+ ScDPUtil::createDuplicateDimensionName(aFieldName.maName, aFieldName.mnDupCount);
+ }
+
+ // *** HIDDEN ITEMS ***
+
+ rLabelData.maMembers = maLabelData.maMembers;
+ int nVisCount = m_xLbHide->n_children();
+ for (int nPos = 0; nPos < nVisCount; ++nPos)
+ rLabelData.maMembers[nPos].mbVisible = m_xLbHide->get_toggle(nPos) == TRISTATE_FALSE;
+
+ // *** HIERARCHY ***
+
+ rLabelData.mnUsedHier = m_xLbHierarchy->get_active() != -1 ? m_xLbHierarchy->get_active() : 0;
+}
+
+void ScDPSubtotalOptDlg::Init( const ScDPNameVec& rDataFields, bool bEnableLayout )
+{
+ m_xBtnOk->connect_clicked(LINK(this, ScDPSubtotalOptDlg, ButtonClicked));
+ m_xBtnCancel->connect_clicked(LINK(this, ScDPSubtotalOptDlg, ButtonClicked));
+
+ // *** SORTING ***
+
+ sal_Int32 nSortMode = maLabelData.maSortInfo.Mode;
+
+ // sort fields list box
+ m_xLbSortBy->append_text(maLabelData.getDisplayName());
+
+ for( const auto& rDataField : rDataFields )
+ {
+ // Cache names for later lookup.
+ maDataFieldNameMap.emplace(rDataField.maLayoutName, rDataField);
+
+ m_xLbSortBy->append_text(rDataField.maLayoutName);
+ m_xLbShowUsing->append_text(rDataField.maLayoutName); // for AutoShow
+ }
+
+ sal_Int32 nSortPos = SC_SORTNAME_POS;
+ if( nSortMode == DataPilotFieldSortMode::DATA )
+ {
+ nSortPos = FindListBoxEntry( *m_xLbSortBy, maLabelData.maSortInfo.Field, SC_SORTDATA_POS );
+ if( nSortPos == -1 )
+ {
+ nSortPos = SC_SORTNAME_POS;
+ nSortMode = DataPilotFieldSortMode::MANUAL;
+ }
+ }
+ m_xLbSortBy->set_active(nSortPos);
+
+ // sorting mode
+ m_xRbSortAsc->connect_toggled( LINK( this, ScDPSubtotalOptDlg, RadioClickHdl ) );
+ m_xRbSortDesc->connect_toggled( LINK( this, ScDPSubtotalOptDlg, RadioClickHdl ) );
+ m_xRbSortMan->connect_toggled( LINK( this, ScDPSubtotalOptDlg, RadioClickHdl ) );
+
+ weld::RadioButton* pRBtn = nullptr;
+ switch( nSortMode )
+ {
+ case DataPilotFieldSortMode::NONE:
+ case DataPilotFieldSortMode::MANUAL:
+ pRBtn = m_xRbSortMan.get();
+ break;
+ default:
+ pRBtn = maLabelData.maSortInfo.IsAscending ? m_xRbSortAsc.get() : m_xRbSortDesc.get();
+ }
+ pRBtn->set_active(true);
+ RadioClickHdl(*pRBtn);
+
+ // *** LAYOUT MODE ***
+
+ m_xLayoutFrame->set_sensitive(bEnableLayout);
+
+ m_xLbLayout->set_active(FromDataPilotFieldLayoutMode(maLabelData.maLayoutInfo.LayoutMode));
+ m_xCbLayoutEmpty->set_active( maLabelData.maLayoutInfo.AddEmptyLines );
+ m_xCbRepeatItemLabels->set_active( maLabelData.mbRepeatItemLabels );
+
+ // *** AUTO SHOW ***
+
+ m_xCbShow->set_active( maLabelData.maShowInfo.IsEnabled );
+ m_xCbShow->connect_toggled( LINK( this, ScDPSubtotalOptDlg, CheckHdl ) );
+
+ m_xLbShowFrom->set_active(FromDataPilotFieldShowItemsMode(maLabelData.maShowInfo.ShowItemsMode));
+ tools::Long nCount = static_cast< tools::Long >( maLabelData.maShowInfo.ItemCount );
+ if( nCount < 1 )
+ nCount = SC_SHOW_DEFAULT;
+ m_xNfShow->set_value( nCount );
+
+ // m_xLbShowUsing already filled above
+ m_xLbShowUsing->set_active_text(maLabelData.maShowInfo.DataField);
+ if (m_xLbShowUsing->get_active() == -1)
+ m_xLbShowUsing->set_active(0);
+
+ CheckHdl(*m_xCbShow); // enable/disable dependent controls
+
+ // *** HIDDEN ITEMS ***
+
+ InitHideListBox();
+
+ // *** HIERARCHY ***
+
+ if( maLabelData.maHiers.getLength() > 1 )
+ {
+ lclFillListBox(*m_xLbHierarchy, maLabelData.maHiers);
+ sal_Int32 nHier = maLabelData.mnUsedHier;
+ if( (nHier < 0) || (nHier >= maLabelData.maHiers.getLength()) ) nHier = 0;
+ m_xLbHierarchy->set_active( nHier );
+ m_xLbHierarchy->connect_changed( LINK( this, ScDPSubtotalOptDlg, SelectHdl ) );
+ }
+ else
+ {
+ m_xFtHierarchy->set_sensitive(false);
+ m_xLbHierarchy->set_sensitive(false);
+ }
+}
+
+void ScDPSubtotalOptDlg::InitHideListBox()
+{
+ m_xLbHide->clear();
+ lclFillListBox(*m_xLbHide, maLabelData.maMembers);
+ size_t n = maLabelData.maMembers.size();
+ for (size_t i = 0; i < n; ++i)
+ m_xLbHide->set_toggle(i, maLabelData.maMembers[i].mbVisible ? TRISTATE_FALSE : TRISTATE_TRUE);
+ bool bEnable = m_xLbHide->n_children() > 0;
+ m_xHideFrame->set_sensitive(bEnable);
+}
+
+ScDPName ScDPSubtotalOptDlg::GetFieldName(const OUString& rLayoutName) const
+{
+ NameMapType::const_iterator itr = maDataFieldNameMap.find(rLayoutName);
+ return itr == maDataFieldNameMap.end() ? ScDPName() : itr->second;
+}
+
+sal_Int32 ScDPSubtotalOptDlg::FindListBoxEntry(
+ const weld::ComboBox& rLBox, std::u16string_view rEntry, sal_Int32 nStartPos ) const
+{
+ sal_Int32 nPos = nStartPos;
+ bool bFound = false;
+ while (nPos < rLBox.get_count())
+ {
+ // translate the displayed field name back to its original field name.
+ ScDPName aName = GetFieldName(rLBox.get_text(nPos));
+ OUString aUnoName = ScDPUtil::createDuplicateDimensionName(aName.maName, aName.mnDupCount);
+ if (aUnoName == rEntry)
+ {
+ bFound = true;
+ break;
+ }
+ ++nPos;
+ }
+ return bFound ? nPos : -1;
+}
+
+IMPL_LINK(ScDPSubtotalOptDlg, ButtonClicked, weld::Button&, rButton, void)
+{
+ if (&rButton == m_xBtnOk.get())
+ response(RET_OK);
+ else
+ response(RET_CANCEL);
+}
+
+IMPL_LINK(ScDPSubtotalOptDlg, RadioClickHdl, weld::Toggleable&, rBtn, void)
+{
+ if (!rBtn.get_active())
+ return;
+
+ m_xLbSortBy->set_sensitive(m_xRbSortMan->get_active());
+}
+
+IMPL_LINK(ScDPSubtotalOptDlg, CheckHdl, weld::Toggleable&, rCBox, void)
+{
+ if (&rCBox == m_xCbShow.get())
+ {
+ bool bEnable = m_xCbShow->get_active();
+ m_xNfShow->set_sensitive( bEnable );
+ m_xFtShow->set_sensitive( bEnable );
+ m_xFtShowFrom->set_sensitive( bEnable );
+ m_xLbShowFrom->set_sensitive( bEnable );
+
+ bool bEnableUsing = bEnable && (m_xLbShowUsing->get_count() > 0);
+ m_xFtShowUsing->set_sensitive(bEnableUsing);
+ m_xLbShowUsing->set_sensitive(bEnableUsing);
+ }
+}
+
+IMPL_LINK_NOARG(ScDPSubtotalOptDlg, SelectHdl, weld::ComboBox&, void)
+{
+ mrDPObj.GetMembers(maLabelData.mnCol, m_xLbHierarchy->get_active(), maLabelData.maMembers);
+ InitHideListBox();
+}
+
+ScDPShowDetailDlg::ScDPShowDetailDlg(weld::Window* pParent, ScDPObject& rDPObj, css::sheet::DataPilotFieldOrientation nOrient)
+ : GenericDialogController(pParent, "modules/scalc/ui/showdetaildialog.ui", "ShowDetail")
+ , mrDPObj(rDPObj)
+ , mxLbDims(m_xBuilder->weld_tree_view("dimsTreeview"))
+{
+ ScDPSaveData* pSaveData = rDPObj.GetSaveData();
+ tools::Long nDimCount = rDPObj.GetDimCount();
+ for (tools::Long nDim=0; nDim<nDimCount; nDim++)
+ {
+ bool bIsDataLayout;
+ sal_Int32 nDimFlags = 0;
+ OUString aName = rDPObj.GetDimName( nDim, bIsDataLayout, &nDimFlags );
+ if ( !bIsDataLayout && !rDPObj.IsDuplicated( nDim ) && ScDPObject::IsOrientationAllowed( nOrient, nDimFlags ) )
+ {
+ const ScDPSaveDimension* pDimension = pSaveData ? pSaveData->GetExistingDimensionByName(aName) : nullptr;
+ if ( !pDimension || (pDimension->GetOrientation() != nOrient) )
+ {
+ if (pDimension)
+ {
+ const std::optional<OUString> & pLayoutName = pDimension->GetLayoutName();
+ if (pLayoutName)
+ aName = *pLayoutName;
+ }
+ mxLbDims->append_text(aName);
+ maNameIndexMap.emplace(aName, nDim);
+ }
+ }
+ }
+ if (mxLbDims->n_children())
+ mxLbDims->select(0);
+
+ mxLbDims->connect_row_activated(LINK(this, ScDPShowDetailDlg, DblClickHdl));
+}
+
+ScDPShowDetailDlg::~ScDPShowDetailDlg()
+{
+}
+
+short ScDPShowDetailDlg::run()
+{
+ return mxLbDims->n_children() ? GenericDialogController::run() : static_cast<short>(RET_CANCEL);
+}
+
+OUString ScDPShowDetailDlg::GetDimensionName() const
+{
+ // Look up the internal dimension name which may be different from the
+ // displayed field name.
+ OUString aSelectedName = mxLbDims->get_selected_text();
+ DimNameIndexMap::const_iterator itr = maNameIndexMap.find(aSelectedName);
+ if (itr == maNameIndexMap.end())
+ // This should never happen!
+ return aSelectedName;
+
+ tools::Long nDim = itr->second;
+ bool bIsDataLayout = false;
+ return mrDPObj.GetDimName(nDim, bIsDataLayout);
+}
+
+IMPL_LINK_NOARG(ScDPShowDetailDlg, DblClickHdl, weld::TreeView&, bool)
+{
+ m_xDialog->response(RET_OK);
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/scendlg.cxx b/sc/source/ui/dbgui/scendlg.cxx
new file mode 100644
index 000000000..543914e14
--- /dev/null
+++ b/sc/source/ui/dbgui/scendlg.cxx
@@ -0,0 +1,163 @@
+/* -*- 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 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <comphelper/string.hxx>
+#include <svx/colorbox.hxx>
+#include <unotools/useroptions.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <unotools/localedatawrapper.hxx>
+
+#include <global.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <tabvwsh.hxx>
+#include <viewdata.hxx>
+#include <document.hxx>
+#include <scendlg.hxx>
+
+ScNewScenarioDlg::ScNewScenarioDlg(weld::Window* pParent, const OUString& rName, bool bEdit, bool bSheetProtected)
+ : GenericDialogController(pParent, "modules/scalc/ui/scenariodialog.ui", "ScenarioDialog")
+ , aDefScenarioName(rName)
+ , bIsEdit(bEdit)
+ , m_xEdName(m_xBuilder->weld_entry("name"))
+ , m_xEdComment(m_xBuilder->weld_text_view("comment"))
+ , m_xCbShowFrame(m_xBuilder->weld_check_button("showframe"))
+ , m_xLbColor(new ColorListBox(m_xBuilder->weld_menu_button("bordercolor"), [this] { return m_xDialog.get(); }))
+ , m_xCbTwoWay(m_xBuilder->weld_check_button("copyback"))
+ , m_xCbCopyAll(m_xBuilder->weld_check_button("copysheet"))
+ , m_xCbProtect(m_xBuilder->weld_check_button("preventchanges"))
+ , m_xBtnOk(m_xBuilder->weld_button("ok"))
+ , m_xAltTitle(m_xBuilder->weld_label("alttitle"))
+ , m_xCreatedFt(m_xBuilder->weld_label("createdft"))
+ , m_xOnFt(m_xBuilder->weld_label("onft"))
+{
+ m_xEdComment->set_size_request(m_xEdComment->get_approximate_digit_width() * 60,
+ m_xEdComment->get_height_rows(6));
+
+ if (bIsEdit)
+ m_xDialog->set_title(m_xAltTitle->get_label());
+
+ SvtUserOptions aUserOpt;
+
+ OUString sCreatedBy(m_xCreatedFt->get_label());
+ OUString sOn(m_xOnFt->get_label());
+
+ OUString aComment(sCreatedBy + " " + aUserOpt.GetFirstName() + " " +aUserOpt.GetLastName()
+ + ", " + sOn + " " + ScGlobal::getLocaleData().getDate(Date(Date::SYSTEM))
+ + ", " + ScGlobal::getLocaleData().getTime(tools::Time(tools::Time::SYSTEM)));
+
+ m_xEdComment->set_text(aComment);
+ m_xEdName->set_text(rName);
+ m_xBtnOk->connect_clicked(LINK(this, ScNewScenarioDlg, OkHdl));
+ m_xCbShowFrame->connect_toggled(LINK(this, ScNewScenarioDlg, EnableHdl));
+
+ m_xLbColor->SelectEntry( COL_LIGHTGRAY );
+ m_xCbShowFrame->set_active(true);
+ m_xCbTwoWay->set_active(true);
+ m_xCbCopyAll->set_active(false);
+ m_xCbProtect->set_active(true);
+
+ if (bIsEdit)
+ m_xCbCopyAll->set_active(false);
+ // If the Sheet is protected then we disable the Scenario Protect input
+ // and default it to true above. Note we are in 'Add' mode here as: if
+ // Sheet && scenario protection are true, then we cannot edit this dialog.
+ if (bSheetProtected)
+ m_xCbProtect->set_active(false);
+}
+
+ScNewScenarioDlg::~ScNewScenarioDlg()
+{
+}
+
+void ScNewScenarioDlg::GetScenarioData( OUString& rName, OUString& rComment,
+ Color& rColor, ScScenarioFlags& rFlags ) const
+{
+ rComment = m_xEdComment->get_text();
+ rName = m_xEdName->get_text();
+
+ if (rName.isEmpty())
+ rName = aDefScenarioName;
+
+ rColor = m_xLbColor->GetSelectEntryColor();
+ ScScenarioFlags nBits = ScScenarioFlags::NONE;
+ if (m_xCbShowFrame->get_active())
+ nBits |= ScScenarioFlags::ShowFrame;
+ if (m_xCbTwoWay->get_active())
+ nBits |= ScScenarioFlags::TwoWay;
+ if (m_xCbCopyAll->get_active())
+ nBits |= ScScenarioFlags::CopyAll;
+ if (m_xCbProtect->get_active())
+ nBits |= ScScenarioFlags::Protected;
+ rFlags = nBits;
+}
+
+void ScNewScenarioDlg::SetScenarioData(const OUString& rName, const OUString& rComment,
+ const Color& rColor, ScScenarioFlags nFlags)
+{
+ m_xEdComment->set_text(rComment);
+ m_xEdName->set_text(rName);
+ m_xLbColor->SelectEntry(rColor);
+
+ m_xCbShowFrame->set_active( (nFlags & ScScenarioFlags::ShowFrame) != ScScenarioFlags::NONE );
+ EnableHdl(*m_xCbShowFrame);
+ m_xCbTwoWay->set_active( (nFlags & ScScenarioFlags::TwoWay) != ScScenarioFlags::NONE );
+ // not CopyAll
+ m_xCbProtect->set_active( (nFlags & ScScenarioFlags::Protected) != ScScenarioFlags::NONE );
+}
+
+IMPL_LINK_NOARG(ScNewScenarioDlg, OkHdl, weld::Button&, void)
+{
+ OUString aName = comphelper::string::strip(m_xEdName->get_text(), ' ');
+ ScDocument& rDoc = static_cast<ScTabViewShell*>(SfxViewShell::Current())->GetViewData().GetDocument();
+
+ m_xEdName->set_text(aName);
+
+ if ( !ScDocument::ValidTabName( aName ) )
+ {
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Info, VclButtonsType::Ok,
+ ScResId(STR_INVALIDTABNAME)));
+ xInfoBox->run();
+ m_xEdName->grab_focus();
+ }
+ else if ( !bIsEdit && !rDoc.ValidNewTabName( aName ) )
+ {
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Info, VclButtonsType::Ok,
+ ScResId(STR_NEWTABNAMENOTUNIQUE)));
+ xInfoBox->run();
+ m_xEdName->grab_focus();
+ }
+ else
+ m_xDialog->response(RET_OK);
+
+ //! when editing, test whether another table has the name!
+}
+
+IMPL_LINK(ScNewScenarioDlg, EnableHdl, weld::Toggleable&, rBox, void)
+{
+ if (&rBox == m_xCbShowFrame.get())
+ m_xLbColor->set_sensitive(m_xCbShowFrame->get_active());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/scuiasciiopt.cxx b/sc/source/ui/dbgui/scuiasciiopt.cxx
new file mode 100644
index 000000000..126689d7b
--- /dev/null
+++ b/sc/source/ui/dbgui/scuiasciiopt.cxx
@@ -0,0 +1,943 @@
+/* -*- 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 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <svx/txencbox.hxx>
+
+#include <global.hxx>
+#include <scresid.hxx>
+#include <impex.hxx>
+#include <scuiasciiopt.hxx>
+#include <strings.hrc>
+#include <strings.hxx>
+#include <csvtablebox.hxx>
+#include <osl/thread.h>
+#include <unotools/transliterationwrapper.hxx>
+
+#include <optutil.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <miscuno.hxx>
+#include <osl/diagnose.h>
+#include <vcl/svapp.hxx>
+#include <comphelper/lok.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <unicode/ucsdet.h>
+
+//! TODO make dynamic
+const SCSIZE ASCIIDLG_MAXROWS = MAXROWCOUNT;
+
+// Maximum number of source lines to concatenate while generating the preview
+// for one logical line. This may result in a wrong preview if the actual
+// number of embedded line feeds is greater, but a number too high would take
+// too much time (loop excessively if unlimited and large data) if none of the
+// selected separators are actually used in data but a field at start of line
+// is quoted.
+constexpr sal_uInt32 kMaxEmbeddedLinefeeds = 500;
+
+using namespace com::sun::star::uno;
+
+namespace {
+
+// Defines - CSV Import Preserve Options
+// For usage of index order see lcl_CreatePropertiesNames() below.
+enum CSVImportOptionsIndex
+{
+ CSVIO_MergeDelimiters = 0,
+ CSVIO_Separators,
+ CSVIO_TextSeparators,
+ CSVIO_FixedWidth,
+ CSVIO_RemoveSpace,
+ CSVIO_EvaluateFormulas,
+ // Settings for *all* dialog invocations above.
+ // Settings not for SC_TEXTTOCOLUMNS below.
+ CSVIO_FromRow,
+ CSVIO_Text2ColSkipEmptyCells = CSVIO_FromRow,
+ CSVIO_CharSet,
+ CSVIO_QuotedAsText,
+ CSVIO_DetectSpecialNum,
+ CSVIO_Language,
+ // Plus one not for SC_IMPORTFILE.
+ CSVIO_PasteSkipEmptyCells
+};
+
+}
+
+// Config items for all three paths are defined in
+// officecfg/registry/schema/org/openoffice/Office/Calc.xcs
+// If not, options are neither loaded nor saved.
+const ::std::vector<OUString> CSVImportOptionNames =
+{
+ "MergeDelimiters",
+ "Separators",
+ "TextSeparators",
+ "FixedWidth",
+ "RemoveSpace",
+ "EvaluateFormulas",
+ "FromRow",
+ "CharSet",
+ "QuotedFieldAsText",
+ "DetectSpecialNumbers",
+ "Language",
+ "SkipEmptyCells"
+};
+constexpr OUStringLiteral aSep_Path = u"Office.Calc/Dialogs/CSVImport";
+constexpr OUStringLiteral aSep_Path_Clpbrd = u"Office.Calc/Dialogs/ClipboardTextImport";
+constexpr OUStringLiteral aSep_Path_Text2Col = u"Office.Calc/Dialogs/TextToColumnsImport";
+
+namespace {
+CSVImportOptionsIndex getSkipEmptyCellsIndex( ScImportAsciiCall eCall )
+{
+ return eCall == SC_TEXTTOCOLUMNS ? CSVIO_Text2ColSkipEmptyCells : CSVIO_PasteSkipEmptyCells;
+}
+}
+
+static void lcl_FillCombo(weld::ComboBox& rCombo, std::u16string_view rList, sal_Unicode cSelect)
+{
+ OUString aStr;
+ if (!rList.empty())
+ {
+ sal_Int32 nIdx {0};
+ do
+ {
+ const OUString sEntry {o3tl::getToken(rList, 0, '\t', nIdx)};
+ rCombo.append_text(sEntry);
+ if (nIdx>0 && static_cast<sal_Unicode>(o3tl::toInt32(o3tl::getToken(rList, 0, '\t', nIdx))) == cSelect)
+ aStr = sEntry;
+ }
+ while (nIdx>0);
+ }
+
+ if ( cSelect )
+ {
+ if (aStr.isEmpty())
+ aStr = OUString(cSelect); // Ascii
+
+ rCombo.set_entry_text(aStr);
+ }
+}
+
+static sal_Unicode lcl_CharFromCombo(const weld::ComboBox& rCombo, std::u16string_view rList)
+{
+ sal_Unicode c = 0;
+ OUString aStr = rCombo.get_active_text();
+ if ( !aStr.isEmpty() && !rList.empty() )
+ {
+ sal_Int32 nIdx {0};
+ OUString sToken {o3tl::getToken(rList, 0, '\t', nIdx)};
+ while (nIdx>0)
+ {
+ if ( ScGlobal::GetTransliteration().isEqual( aStr, sToken ) )
+ {
+ sal_Int32 nTmpIdx {nIdx};
+ c = static_cast<sal_Unicode>(o3tl::toInt32(o3tl::getToken(rList, 0, '\t', nTmpIdx)));
+ }
+ // Skip to next token at even position
+ sToken = o3tl::getToken(rList, 1, '\t', nIdx);
+ }
+ if (!c)
+ {
+ sal_Unicode cFirst = aStr[0];
+ // #i24235# first try the first character of the string directly
+ if( (aStr.getLength() == 1) || (cFirst < '0') || (cFirst > '9') )
+ c = cFirst;
+ else // keep old behaviour for compatibility (i.e. "39" -> "'")
+ c = static_cast<sal_Unicode>(aStr.toInt32()); // Ascii
+ }
+ }
+ return c;
+}
+
+static void lcl_CreatePropertiesNames ( OUString& rSepPath, Sequence<OUString>& rNames, ScImportAsciiCall eCall )
+{
+ sal_Int32 nProperties = 0;
+
+ switch(eCall)
+ {
+ case SC_IMPORTFILE:
+ rSepPath = aSep_Path;
+ nProperties = 11;
+ break;
+ case SC_PASTETEXT:
+ rSepPath = aSep_Path_Clpbrd;
+ nProperties = 12;
+ break;
+ case SC_TEXTTOCOLUMNS:
+ default:
+ rSepPath = aSep_Path_Text2Col;
+ nProperties = 7;
+ break;
+ }
+ rNames.realloc( nProperties );
+ OUString* pNames = rNames.getArray();
+ pNames[ CSVIO_MergeDelimiters ] = CSVImportOptionNames[ CSVIO_MergeDelimiters ];
+ pNames[ CSVIO_Separators ] = CSVImportOptionNames[ CSVIO_Separators ];
+ pNames[ CSVIO_TextSeparators ] = CSVImportOptionNames[ CSVIO_TextSeparators ];
+ pNames[ CSVIO_FixedWidth ] = CSVImportOptionNames[ CSVIO_FixedWidth ];
+ pNames[ CSVIO_RemoveSpace ] = CSVImportOptionNames[ CSVIO_RemoveSpace ];
+ pNames[ CSVIO_EvaluateFormulas ] = CSVImportOptionNames[ CSVIO_EvaluateFormulas ];
+ if (eCall != SC_TEXTTOCOLUMNS)
+ {
+ pNames[ CSVIO_FromRow ] = CSVImportOptionNames[ CSVIO_FromRow ];
+ pNames[ CSVIO_CharSet ] = CSVImportOptionNames[ CSVIO_CharSet ];
+ pNames[ CSVIO_QuotedAsText ] = CSVImportOptionNames[ CSVIO_QuotedAsText ];
+ pNames[ CSVIO_DetectSpecialNum ] = CSVImportOptionNames[ CSVIO_DetectSpecialNum ];
+ pNames[ CSVIO_Language ] = CSVImportOptionNames[ CSVIO_Language ];
+ }
+ if (eCall != SC_IMPORTFILE)
+ {
+ const sal_Int32 nSkipEmptyCells = getSkipEmptyCellsIndex(eCall);
+ assert( nSkipEmptyCells < rNames.getLength());
+ pNames[ nSkipEmptyCells ] = CSVImportOptionNames[ CSVIO_PasteSkipEmptyCells ];
+ }
+}
+
+static void lcl_LoadSeparators( OUString& rFieldSeparators, OUString& rTextSeparators,
+ bool& rMergeDelimiters, bool& rQuotedAsText, bool& rDetectSpecialNum,
+ bool& rFixedWidth, sal_Int32& rFromRow, sal_Int32& rCharSet,
+ sal_Int32& rLanguage, bool& rSkipEmptyCells, bool& rRemoveSpace,
+ bool& rEvaluateFormulas, ScImportAsciiCall eCall )
+{
+ Sequence<Any>aValues;
+ const Any *pProperties;
+ Sequence<OUString> aNames;
+ OUString aSepPath;
+ lcl_CreatePropertiesNames ( aSepPath, aNames, eCall);
+ ScLinkConfigItem aItem( aSepPath );
+ aValues = aItem.GetProperties( aNames );
+ pProperties = aValues.getConstArray();
+
+ if( pProperties[ CSVIO_MergeDelimiters ].hasValue() )
+ rMergeDelimiters = ScUnoHelpFunctions::GetBoolFromAny( pProperties[ CSVIO_MergeDelimiters ] );
+
+ if( pProperties[ CSVIO_RemoveSpace ].hasValue() )
+ rRemoveSpace = ScUnoHelpFunctions::GetBoolFromAny( pProperties[ CSVIO_RemoveSpace ] );
+
+ if( pProperties[ CSVIO_Separators ].hasValue() )
+ pProperties[ CSVIO_Separators ] >>= rFieldSeparators;
+
+ if( pProperties[ CSVIO_TextSeparators ].hasValue() )
+ pProperties[ CSVIO_TextSeparators ] >>= rTextSeparators;
+
+ if( pProperties[ CSVIO_FixedWidth ].hasValue() )
+ rFixedWidth = ScUnoHelpFunctions::GetBoolFromAny( pProperties[ CSVIO_FixedWidth ] );
+
+ if( pProperties[ CSVIO_EvaluateFormulas ].hasValue() )
+ rEvaluateFormulas = ScUnoHelpFunctions::GetBoolFromAny( pProperties[ CSVIO_EvaluateFormulas ] );
+
+ if (eCall != SC_TEXTTOCOLUMNS)
+ {
+ if( pProperties[ CSVIO_FromRow ].hasValue() )
+ pProperties[ CSVIO_FromRow ] >>= rFromRow;
+
+ if( pProperties[ CSVIO_CharSet ].hasValue() )
+ pProperties[ CSVIO_CharSet ] >>= rCharSet;
+
+ if ( pProperties[ CSVIO_QuotedAsText ].hasValue() )
+ pProperties[ CSVIO_QuotedAsText ] >>= rQuotedAsText;
+
+ if ( pProperties[ CSVIO_DetectSpecialNum ].hasValue() )
+ pProperties[ CSVIO_DetectSpecialNum ] >>= rDetectSpecialNum;
+
+ if ( pProperties[ CSVIO_Language ].hasValue() )
+ pProperties[ CSVIO_Language ] >>= rLanguage;
+ }
+ if (eCall != SC_IMPORTFILE)
+ {
+ const sal_Int32 nSkipEmptyCells = getSkipEmptyCellsIndex(eCall);
+ assert( nSkipEmptyCells < aValues.getLength());
+ if ( pProperties[nSkipEmptyCells].hasValue() )
+ rSkipEmptyCells = ScUnoHelpFunctions::GetBoolFromAny( pProperties[nSkipEmptyCells] );
+ }
+}
+
+static void lcl_SaveSeparators(
+ const OUString& sFieldSeparators, const OUString& sTextSeparators, bool bMergeDelimiters, bool bQuotedAsText,
+ bool bDetectSpecialNum, bool bFixedWidth, sal_Int32 nFromRow,
+ sal_Int32 nCharSet, sal_Int32 nLanguage, bool bSkipEmptyCells, bool bRemoveSpace, bool bEvaluateFormulas,
+ ScImportAsciiCall eCall )
+{
+ Sequence<Any> aValues;
+ Any *pProperties;
+ Sequence<OUString> aNames;
+ OUString aSepPath;
+ lcl_CreatePropertiesNames ( aSepPath, aNames, eCall );
+ ScLinkConfigItem aItem( aSepPath );
+ aValues = aItem.GetProperties( aNames );
+ pProperties = aValues.getArray();
+
+ pProperties[ CSVIO_MergeDelimiters ] <<= bMergeDelimiters;
+ pProperties[ CSVIO_RemoveSpace ] <<= bRemoveSpace;
+ pProperties[ CSVIO_Separators ] <<= sFieldSeparators;
+ pProperties[ CSVIO_TextSeparators ] <<= sTextSeparators;
+ pProperties[ CSVIO_FixedWidth ] <<= bFixedWidth;
+ pProperties[ CSVIO_EvaluateFormulas ] <<= bEvaluateFormulas;
+ if (eCall != SC_TEXTTOCOLUMNS)
+ {
+ pProperties[ CSVIO_FromRow ] <<= nFromRow;
+ pProperties[ CSVIO_CharSet ] <<= nCharSet;
+ pProperties[ CSVIO_QuotedAsText ] <<= bQuotedAsText;
+ pProperties[ CSVIO_DetectSpecialNum ] <<= bDetectSpecialNum;
+ pProperties[ CSVIO_Language ] <<= nLanguage;
+ }
+ if (eCall != SC_IMPORTFILE)
+ {
+ const sal_Int32 nSkipEmptyCells = getSkipEmptyCellsIndex(eCall);
+ assert( nSkipEmptyCells < aValues.getLength());
+ pProperties[ nSkipEmptyCells ] <<= bSkipEmptyCells;
+ }
+
+ aItem.PutProperties(aNames, aValues);
+}
+
+ScImportAsciiDlg::ScImportAsciiDlg(weld::Window* pParent, const OUString& aDatName,
+ SvStream* pInStream, ScImportAsciiCall eCall)
+ : GenericDialogController(pParent, "modules/scalc/ui/textimportcsv.ui", "TextImportCsvDialog")
+ , mpDatStream(pInStream)
+ , mnStreamPos(pInStream ? pInStream->Tell() : 0)
+ , mnRowPosCount(0)
+ , mcTextSep(ScAsciiOptions::cDefaultTextSep)
+ , meCall(eCall)
+ , mbDetectSep(eCall != SC_TEXTTOCOLUMNS)
+ , mxFtCharSet(m_xBuilder->weld_label("textcharset"))
+ , mxLbCharSet(new SvxTextEncodingBox(m_xBuilder->weld_combo_box("charset")))
+ , mxFtCustomLang(m_xBuilder->weld_label("textlanguage"))
+ , mxLbCustomLang(new SvxLanguageBox(m_xBuilder->weld_combo_box("language")))
+ , mxFtRow(m_xBuilder->weld_label("textfromrow"))
+ , mxNfRow(m_xBuilder->weld_spin_button("fromrow"))
+ , mxRbFixed(m_xBuilder->weld_radio_button("tofixedwidth"))
+ , mxRbSeparated(m_xBuilder->weld_radio_button("toseparatedby"))
+ , mxCkbTab(m_xBuilder->weld_check_button("tab"))
+ , mxCkbSemicolon(m_xBuilder->weld_check_button("semicolon"))
+ , mxCkbComma(m_xBuilder->weld_check_button("comma"))
+ , mxCkbRemoveSpace(m_xBuilder->weld_check_button("removespace"))
+ , mxCkbSpace(m_xBuilder->weld_check_button("space"))
+ , mxCkbOther(m_xBuilder->weld_check_button("other"))
+ , mxEdOther(m_xBuilder->weld_entry("inputother"))
+ , mxCkbAsOnce(m_xBuilder->weld_check_button("mergedelimiters"))
+ , mxFtTextSep(m_xBuilder->weld_label("texttextdelimiter"))
+ , mxCbTextSep(m_xBuilder->weld_combo_box("textdelimiter"))
+ , mxCkbQuotedAsText(m_xBuilder->weld_check_button("quotedfieldastext"))
+ , mxCkbDetectNumber(m_xBuilder->weld_check_button("detectspecialnumbers"))
+ , mxCkbEvaluateFormulas(m_xBuilder->weld_check_button("evaluateformulas"))
+ , mxCkbSkipEmptyCells(m_xBuilder->weld_check_button("skipemptycells"))
+ , mxFtType(m_xBuilder->weld_label("textcolumntype"))
+ , mxLbType(m_xBuilder->weld_combo_box("columntype"))
+ , mxAltTitle(m_xBuilder->weld_label("textalttitle"))
+ , mxTableBox(new ScCsvTableBox(*m_xBuilder))
+{
+ OUString aName = m_xDialog->get_title();
+ switch (meCall)
+ {
+ case SC_TEXTTOCOLUMNS:
+ m_xDialog->set_title(mxAltTitle->get_label());
+ break;
+ case SC_IMPORTFILE:
+ if (!comphelper::LibreOfficeKit::isActive())
+ {
+ aName += " - [" + aDatName + "]";
+ m_xDialog->set_title(aName);
+ }
+ break;
+ default:
+ break;
+ }
+
+ // To be able to prefill the correct values based on the file extension
+ bool bIsTSV = (aDatName.endsWithIgnoreAsciiCase(".tsv") || aDatName.endsWithIgnoreAsciiCase(".tab"));
+
+ // Default options are set in officecfg/registry/schema/org/openoffice/Office/Calc.xcs
+ OUString sFieldSeparators(",;\t");
+ OUString sTextSeparators(mcTextSep);
+ bool bMergeDelimiters = false;
+ bool bFixedWidth = false;
+ bool bQuotedFieldAsText = false;
+ bool bDetectSpecialNum = true;
+ bool bEvaluateFormulas = (meCall != SC_IMPORTFILE);
+ bool bSkipEmptyCells = true;
+ bool bRemoveSpace = false;
+ sal_Int32 nFromRow = 1;
+ sal_Int32 nCharSet = -1;
+ sal_Int32 nLanguage = 0;
+ lcl_LoadSeparators (sFieldSeparators, sTextSeparators, bMergeDelimiters,
+ bQuotedFieldAsText, bDetectSpecialNum, bFixedWidth, nFromRow,
+ nCharSet, nLanguage, bSkipEmptyCells, bRemoveSpace, bEvaluateFormulas, meCall);
+ // load from saved settings
+ maFieldSeparators = sFieldSeparators;
+
+ if( bMergeDelimiters && !bIsTSV )
+ mxCkbAsOnce->set_active(true);
+ if (bQuotedFieldAsText)
+ mxCkbQuotedAsText->set_active(true);
+ if (bRemoveSpace)
+ mxCkbRemoveSpace->set_active(true);
+ if (bDetectSpecialNum)
+ mxCkbDetectNumber->set_active(true);
+ if (bEvaluateFormulas)
+ mxCkbEvaluateFormulas->set_active(true);
+ if (bSkipEmptyCells)
+ mxCkbSkipEmptyCells->set_active(true);
+ if (bFixedWidth && !bIsTSV)
+ mxRbFixed->set_active(true);
+ if (nFromRow != 1)
+ mxNfRow->set_value(nFromRow);
+
+ // Clipboard is always Unicode, else detect.
+ rtl_TextEncoding ePreselectUnicode = (meCall == SC_IMPORTFILE ?
+ RTL_TEXTENCODING_DONTKNOW : RTL_TEXTENCODING_UNICODE);
+ // Sniff for Unicode / not
+ if( ePreselectUnicode == RTL_TEXTENCODING_DONTKNOW && mpDatStream )
+ {
+ mpDatStream->Seek( 0 );
+ constexpr size_t buffsize = 4096;
+ sal_Int8 bytes[buffsize] = { 0 };
+ sal_Int32 nRead = mpDatStream->ReadBytes( bytes, buffsize );
+ mpDatStream->Seek( 0 );
+
+ if ( nRead > 0 )
+ {
+ UErrorCode uerr = U_ZERO_ERROR;
+ UCharsetDetector* ucd = ucsdet_open( &uerr );
+ ucsdet_setText( ucd, reinterpret_cast<const char*>(bytes), nRead, &uerr );
+
+ if ( const UCharsetMatch* match = ucsdet_detect(ucd, &uerr) )
+ {
+ const char* pEncodingName = ucsdet_getName( match, &uerr );
+
+ if ( U_SUCCESS(uerr) && !strcmp("UTF-8", pEncodingName) )
+ {
+ ePreselectUnicode = RTL_TEXTENCODING_UTF8; // UTF-8
+ mpDatStream->StartReadingUnicodeText( RTL_TEXTENCODING_UTF8 );
+ }
+ else if ( U_SUCCESS(uerr) && !strcmp("UTF-16LE", pEncodingName) )
+ {
+ ePreselectUnicode = RTL_TEXTENCODING_UNICODE; // UTF-16LE
+ mpDatStream->SetEndian( SvStreamEndian::LITTLE );
+ mpDatStream->StartReadingUnicodeText( RTL_TEXTENCODING_UNICODE );
+ }
+ else if ( U_SUCCESS(uerr) && !strcmp("UTF-16BE", pEncodingName) )
+ {
+ ePreselectUnicode = RTL_TEXTENCODING_UNICODE; // UTF-16BE
+ mpDatStream->SetEndian( SvStreamEndian::BIG );
+ mpDatStream->StartReadingUnicodeText( RTL_TEXTENCODING_UNICODE );
+ }
+ else // other
+ mpDatStream->StartReadingUnicodeText( RTL_TEXTENCODING_DONTKNOW );
+ }
+
+ ucsdet_close( ucd );
+ }
+
+ mnStreamPos = mpDatStream->Tell();
+ }
+
+ if (bIsTSV)
+ SetSeparators('\t');
+ else
+ {
+ // Some MS-Excel convention is the first line containing the field
+ // separator as "sep=|" (without quotes and any field separator
+ // character). The second possibility seems to be it is present *with*
+ // quotes so it shows up as cell content *including* the separator and
+ // can be preserved during round trips. Check for an exact match of
+ // any such and set separator.
+ /* TODO: it is debatable whether the unquoted form should rather be
+ * treated special to actually include the separator in the field data.
+ * Currently it does not. */
+ sal_Unicode cSep = 0;
+ OUString aLine;
+ // Try to read one more character, if more than 7 it can't be an exact
+ // match of any.
+ mpDatStream->ReadUniOrByteStringLine( aLine, mpDatStream->GetStreamCharSet(), 8);
+ mpDatStream->Seek(mnStreamPos);
+ if (aLine.getLength() == 8)
+ ; // nothing
+ else if (aLine.getLength() == 5 && aLine.startsWithIgnoreAsciiCase("sep="))
+ cSep = aLine[4];
+ else if (aLine.getLength() == 7 && aLine[6] == '"' && aLine.startsWithIgnoreAsciiCase("\"sep="))
+ cSep = aLine[5];
+
+ // Set Separators in the dialog from maFieldSeparators (empty are not
+ // set) or an optionally defined by file content field separator.
+ SetSeparators(cSep);
+ }
+
+ // Get Separators from the dialog (empty are set from default)
+ maFieldSeparators = GetSeparators();
+
+ mxNfRow->connect_value_changed( LINK( this, ScImportAsciiDlg, FirstRowHdl ) );
+
+ // *** Separator characters ***
+ lcl_FillCombo( *mxCbTextSep, SCSTR_TEXTSEP, mcTextSep );
+ mxCbTextSep->set_entry_text(sTextSeparators);
+ // tdf#69207 - use selected text delimiter to parse the provided data
+ mcTextSep = lcl_CharFromCombo(*mxCbTextSep, SCSTR_TEXTSEP);
+
+ Link<weld::Toggleable&,void> aSeparatorClickHdl =LINK( this, ScImportAsciiDlg, SeparatorClickHdl );
+ mxCbTextSep->connect_changed( LINK( this, ScImportAsciiDlg, SeparatorComboBoxHdl ) );
+ mxCkbTab->connect_toggled( aSeparatorClickHdl );
+ mxCkbSemicolon->connect_toggled( aSeparatorClickHdl );
+ mxCkbComma->connect_toggled( aSeparatorClickHdl );
+ mxCkbAsOnce->connect_toggled( aSeparatorClickHdl );
+ mxCkbQuotedAsText->connect_toggled( aSeparatorClickHdl );
+ mxCkbDetectNumber->connect_toggled( aSeparatorClickHdl );
+ mxCkbEvaluateFormulas->connect_toggled( aSeparatorClickHdl );
+ mxCkbSkipEmptyCells->connect_toggled( aSeparatorClickHdl );
+ mxCkbSpace->connect_toggled( aSeparatorClickHdl );
+ mxCkbRemoveSpace->connect_toggled( aSeparatorClickHdl );
+ mxCkbOther->connect_toggled( aSeparatorClickHdl );
+ mxEdOther->connect_changed(LINK(this, ScImportAsciiDlg, SeparatorEditHdl));
+
+ // *** text encoding ListBox ***
+ // all encodings allowed, including Unicode, but subsets are excluded
+ mxLbCharSet->FillFromTextEncodingTable( true );
+ // Insert one "SYSTEM" entry for compatibility in AsciiOptions and system
+ // independent document linkage.
+ mxLbCharSet->InsertTextEncoding( RTL_TEXTENCODING_DONTKNOW, ScResId( SCSTR_CHARSET_USER ) );
+ if ( ePreselectUnicode == RTL_TEXTENCODING_DONTKNOW )
+ {
+ rtl_TextEncoding eSystemEncoding = osl_getThreadTextEncoding();
+ // Prefer UTF-8, as UTF-16 would have already been detected from the stream.
+ // This gives a better chance that the file is going to be opened correctly.
+ if ( ( eSystemEncoding == RTL_TEXTENCODING_UNICODE ) && mpDatStream )
+ eSystemEncoding = RTL_TEXTENCODING_UTF8;
+ mxLbCharSet->SelectTextEncoding( eSystemEncoding );
+ }
+ else
+ {
+ mxLbCharSet->SelectTextEncoding( ePreselectUnicode );
+ }
+
+ if (nCharSet >= 0 && ePreselectUnicode == RTL_TEXTENCODING_DONTKNOW)
+ mxLbCharSet->set_active(nCharSet);
+
+ SetSelectedCharSet();
+ mxLbCharSet->connect_changed( LINK( this, ScImportAsciiDlg, CharSetHdl ) );
+
+ mxLbCustomLang->SetLanguageList(
+ SvxLanguageListFlags::ALL | SvxLanguageListFlags::ONLY_KNOWN, false, false);
+ mxLbCustomLang->InsertLanguage(LANGUAGE_SYSTEM);
+ mxLbCustomLang->set_active_id(static_cast<LanguageType>(nLanguage));
+
+ // *** column type ListBox ***
+ OUString aColumnUser( ScResId( SCSTR_COLUMN_USER ) );
+ for (sal_Int32 nIdx {0}; nIdx>=0; )
+ {
+ mxLbType->append_text(aColumnUser.getToken(0, ';', nIdx));
+ }
+
+ mxLbType->connect_changed( LINK( this, ScImportAsciiDlg, LbColTypeHdl ) );
+ mxFtType->set_sensitive(false);
+ mxLbType->set_sensitive(false);
+
+ // *** table box preview ***
+ mxTableBox->Init();
+ mxTableBox->SetUpdateTextHdl( LINK( this, ScImportAsciiDlg, UpdateTextHdl ) );
+ mxTableBox->InitTypes( *mxLbType );
+ mxTableBox->SetColTypeHdl( LINK( this, ScImportAsciiDlg, ColTypeHdl ) );
+
+ mxRbSeparated->connect_toggled( LINK( this, ScImportAsciiDlg, RbSepFixHdl ) );
+ mxRbFixed->connect_toggled( LINK( this, ScImportAsciiDlg, RbSepFixHdl ) );
+
+ SetupSeparatorCtrls();
+ RbSepFix();
+
+ UpdateVertical();
+
+ mxTableBox->GetGrid().Execute( CSVCMD_NEWCELLTEXTS );
+
+ if (meCall == SC_TEXTTOCOLUMNS)
+ {
+ mxFtCharSet->set_sensitive(false);
+ mxLbCharSet->set_sensitive(false);
+ mxFtCustomLang->set_sensitive(false);
+ mxLbCustomLang->set_active_id(LANGUAGE_SYSTEM);
+ mxLbCustomLang->set_sensitive(false);
+ mxFtRow->set_sensitive(false);
+ mxNfRow->set_sensitive(false);
+
+ // Quoted field as text option is not used for text-to-columns mode.
+ mxCkbQuotedAsText->set_active(false);
+ mxCkbQuotedAsText->set_sensitive(false);
+
+ // Always detect special numbers for text-to-columns mode.
+ mxCkbDetectNumber->set_active(true);
+ mxCkbDetectNumber->set_sensitive(false);
+ }
+ if (meCall == SC_IMPORTFILE)
+ {
+ //Empty cells in imported file are empty
+ mxCkbSkipEmptyCells->set_active(false);
+ mxCkbSkipEmptyCells->hide();
+ }
+ m_xDialog->SetInstallLOKNotifierHdl(LINK(this, ScImportAsciiDlg, InstallLOKNotifierHdl));
+}
+
+IMPL_STATIC_LINK_NOARG(ScImportAsciiDlg, InstallLOKNotifierHdl, void*, vcl::ILibreOfficeKitNotifier*)
+{
+ return GetpApp();
+}
+
+ScImportAsciiDlg::~ScImportAsciiDlg()
+{
+}
+
+bool ScImportAsciiDlg::GetLine( sal_uLong nLine, OUString &rText, sal_Unicode& rcDetectSep )
+{
+ if (nLine >= ASCIIDLG_MAXROWS || !mpDatStream)
+ return false;
+
+ bool bRet = true;
+ bool bFixed = mxRbFixed->get_active();
+
+ if (!mpRowPosArray)
+ mpRowPosArray.reset( new sal_uLong[ASCIIDLG_MAXROWS + 2] );
+
+ if (!mnRowPosCount) // complete re-fresh
+ {
+ memset( mpRowPosArray.get(), 0, sizeof(mpRowPosArray[0]) * (ASCIIDLG_MAXROWS+2));
+
+ Seek(0);
+ mpDatStream->StartReadingUnicodeText( mpDatStream->GetStreamCharSet() );
+
+ mnStreamPos = mpDatStream->Tell();
+ mpRowPosArray[mnRowPosCount] = mnStreamPos;
+ }
+
+ if (nLine >= mnRowPosCount)
+ {
+ // need to work out some more line information
+ do
+ {
+ if (!Seek(mpRowPosArray[mnRowPosCount]) || !mpDatStream->good())
+ {
+ bRet = false;
+ break;
+ }
+ rText = ReadCsvLine(*mpDatStream, !bFixed, maFieldSeparators,
+ mcTextSep, rcDetectSep, kMaxEmbeddedLinefeeds);
+ mnStreamPos = mpDatStream->Tell();
+ mpRowPosArray[++mnRowPosCount] = mnStreamPos;
+ } while (nLine >= mnRowPosCount && mpDatStream->good());
+ if (mpDatStream->eof() &&
+ mnStreamPos == mpRowPosArray[mnRowPosCount-1])
+ {
+ // the very end, not even an empty line read
+ bRet = false;
+ --mnRowPosCount;
+ }
+ }
+ else
+ {
+ Seek( mpRowPosArray[nLine]);
+ rText = ReadCsvLine(*mpDatStream, !bFixed, maFieldSeparators, mcTextSep, rcDetectSep, kMaxEmbeddedLinefeeds);
+ mnStreamPos = mpDatStream->Tell();
+ }
+
+ // If the file content isn't unicode, ReadUniStringLine
+ // may try to seek beyond the file's end and cause a CANTSEEK error
+ // (depending on the stream type). The error code has to be cleared,
+ // or further read operations (including non-unicode) will fail.
+ if ( mpDatStream->GetError() == ERRCODE_IO_CANTSEEK )
+ mpDatStream->ResetError();
+
+ ScImportExport::EmbeddedNullTreatment( rText);
+
+ return bRet;
+}
+
+void ScImportAsciiDlg::GetOptions( ScAsciiOptions& rOpt )
+{
+ rOpt.SetCharSet( meCharSet );
+ rOpt.SetCharSetSystem( mbCharSetSystem );
+ rOpt.SetLanguage(mxLbCustomLang->get_active_id());
+ rOpt.SetFixedLen( mxRbFixed->get_active() );
+ rOpt.SetStartRow( mxNfRow->get_value() );
+ mxTableBox->FillColumnData( rOpt );
+ if( mxRbSeparated->get_active() )
+ {
+ rOpt.SetFieldSeps( GetSeparators() );
+ rOpt.SetMergeSeps( mxCkbAsOnce->get_active() );
+ rOpt.SetRemoveSpace( mxCkbRemoveSpace->get_active() );
+ rOpt.SetTextSep( lcl_CharFromCombo( *mxCbTextSep, SCSTR_TEXTSEP ) );
+ }
+
+ rOpt.SetQuotedAsText(mxCkbQuotedAsText->get_active());
+ rOpt.SetDetectSpecialNumber(mxCkbDetectNumber->get_active());
+ rOpt.SetEvaluateFormulas(mxCkbEvaluateFormulas->get_active());
+ rOpt.SetSkipEmptyCells(mxCkbSkipEmptyCells->get_active());
+}
+
+void ScImportAsciiDlg::SaveParameters()
+{
+ lcl_SaveSeparators( maFieldSeparators, mxCbTextSep->get_active_text(), mxCkbAsOnce->get_active(),
+ mxCkbQuotedAsText->get_active(), mxCkbDetectNumber->get_active(),
+ mxRbFixed->get_active(),
+ mxNfRow->get_value(),
+ mxLbCharSet->get_active(),
+ static_cast<sal_uInt16>(mxLbCustomLang->get_active_id()),
+ mxCkbSkipEmptyCells->get_active(), mxCkbRemoveSpace->get_active(),
+ mxCkbEvaluateFormulas->get_active(), meCall );
+}
+
+void ScImportAsciiDlg::SetSeparators( sal_Unicode cSep )
+{
+ if (cSep)
+ {
+ // Exclusively set a separator, maFieldSeparators needs not be
+ // modified, it's obtained by GetSeparators() after this call.
+ constexpr sal_Unicode aSeps[] = { '\t', ';', ',', ' ' };
+ for (const sal_Unicode c : aSeps)
+ {
+ const bool bSet = (c == cSep);
+ switch (c)
+ {
+ case '\t': mxCkbTab->set_active(bSet); break;
+ case ';': mxCkbSemicolon->set_active(bSet); break;
+ case ',': mxCkbComma->set_active(bSet); break;
+ case ' ': mxCkbSpace->set_active(bSet); break;
+ }
+ if (bSet)
+ cSep = 0;
+ }
+ if (cSep)
+ {
+ mxCkbOther->set_active(true);
+ mxEdOther->set_text(OUStringChar(cSep));
+ }
+ }
+ else
+ {
+ for (sal_Int32 i = 0; i < maFieldSeparators.getLength(); ++i)
+ {
+ switch (maFieldSeparators[i])
+ {
+ case '\t': mxCkbTab->set_active(true); break;
+ case ';': mxCkbSemicolon->set_active(true); break;
+ case ',': mxCkbComma->set_active(true); break;
+ case ' ': mxCkbSpace->set_active(true); break;
+ default:
+ mxCkbOther->set_active(true);
+ mxEdOther->set_text(mxEdOther->get_text() + OUStringChar(maFieldSeparators[i]));
+ }
+ }
+ }
+}
+
+void ScImportAsciiDlg::SetSelectedCharSet()
+{
+ meCharSet = mxLbCharSet->GetSelectTextEncoding();
+ mbCharSetSystem = (meCharSet == RTL_TEXTENCODING_DONTKNOW);
+ if( mbCharSetSystem )
+ meCharSet = osl_getThreadTextEncoding();
+}
+
+OUString ScImportAsciiDlg::GetSeparators() const
+{
+ OUString aSepChars;
+ if( mxCkbTab->get_active() )
+ aSepChars += "\t";
+ if( mxCkbSemicolon->get_active() )
+ aSepChars += ";";
+ if( mxCkbComma->get_active() )
+ aSepChars += ",";
+ if( mxCkbSpace->get_active() )
+ aSepChars += " ";
+ if( mxCkbOther->get_active() )
+ aSepChars += mxEdOther->get_text();
+ return aSepChars;
+}
+
+void ScImportAsciiDlg::SetupSeparatorCtrls()
+{
+ bool bEnable = mxRbSeparated->get_active();
+ mxCkbTab->set_sensitive( bEnable );
+ mxCkbSemicolon->set_sensitive( bEnable );
+ mxCkbComma->set_sensitive( bEnable );
+ mxCkbSpace->set_sensitive( bEnable );
+ mxCkbRemoveSpace->set_sensitive( bEnable );
+ mxCkbOther->set_sensitive( bEnable );
+ mxEdOther->set_sensitive( bEnable );
+ mxCkbAsOnce->set_sensitive( bEnable );
+ mxFtTextSep->set_sensitive( bEnable );
+ mxCbTextSep->set_sensitive( bEnable );
+}
+
+void ScImportAsciiDlg::UpdateVertical()
+{
+ mnRowPosCount = 0;
+ if (mpDatStream)
+ mpDatStream->SetStreamCharSet(meCharSet);
+}
+
+void ScImportAsciiDlg::RbSepFix()
+{
+ weld::WaitObject aWaitObj(m_xDialog.get());
+ if( mxRbFixed->get_active() )
+ mxTableBox->SetFixedWidthMode();
+ else
+ mxTableBox->SetSeparatorsMode();
+ SetupSeparatorCtrls();
+}
+
+IMPL_LINK(ScImportAsciiDlg, RbSepFixHdl, weld::Toggleable&, rButton, void)
+{
+ if (!rButton.get_active())
+ return;
+ RbSepFix();
+}
+
+IMPL_LINK(ScImportAsciiDlg, SeparatorClickHdl, weld::Toggleable&, rCtrl, void)
+{
+ SeparatorHdl(&rCtrl);
+}
+
+IMPL_LINK( ScImportAsciiDlg, SeparatorComboBoxHdl, weld::ComboBox&, rCtrl, void )
+{
+ SeparatorHdl(&rCtrl);
+}
+
+IMPL_LINK( ScImportAsciiDlg, SeparatorEditHdl, weld::Entry&, rEdit, void )
+{
+ SeparatorHdl(&rEdit);
+}
+
+void ScImportAsciiDlg::SeparatorHdl(const weld::Widget* pCtrl)
+{
+ OSL_ENSURE( pCtrl, "ScImportAsciiDlg::SeparatorHdl - missing sender" );
+ OSL_ENSURE( !mxRbFixed->get_active(), "ScImportAsciiDlg::SeparatorHdl - not allowed in fixed width" );
+
+ /* #i41550# First update state of the controls. The GetSeparators()
+ function needs final state of the check boxes. */
+ if (pCtrl == mxCkbOther.get() && mxCkbOther->get_active())
+ mxEdOther->grab_focus();
+ else if (pCtrl == mxEdOther.get())
+ mxCkbOther->set_active(!mxEdOther->get_text().isEmpty());
+
+ OUString aOldFldSeps( maFieldSeparators);
+ maFieldSeparators = GetSeparators();
+ sal_Unicode cOldSep = mcTextSep;
+ mcTextSep = lcl_CharFromCombo( *mxCbTextSep, SCSTR_TEXTSEP );
+ // Any separator changed may result in completely different lines due to
+ // embedded line breaks.
+ if (cOldSep != mcTextSep || aOldFldSeps != maFieldSeparators)
+ UpdateVertical();
+
+ mxTableBox->GetGrid().Execute( CSVCMD_NEWCELLTEXTS );
+}
+
+IMPL_LINK_NOARG(ScImportAsciiDlg, CharSetHdl, weld::ComboBox&, void)
+{
+ if (mxLbCharSet->get_active() != -1)
+ {
+ weld::WaitObject aWaitObj(m_xDialog.get());
+ rtl_TextEncoding eOldCharSet = meCharSet;
+ SetSelectedCharSet();
+ // switching char-set invalidates 8bit -> String conversions
+ if (eOldCharSet != meCharSet)
+ UpdateVertical();
+
+ mxTableBox->GetGrid().Execute( CSVCMD_NEWCELLTEXTS );
+ }
+}
+
+IMPL_LINK(ScImportAsciiDlg, FirstRowHdl, weld::SpinButton&, rNumField, void)
+{
+ mxTableBox->GetGrid().Execute( CSVCMD_SETFIRSTIMPORTLINE, rNumField.get_value() - 1);
+}
+
+IMPL_LINK(ScImportAsciiDlg, LbColTypeHdl, weld::ComboBox&, rListBox, void)
+{
+ if (&rListBox == mxLbType.get())
+ mxTableBox->GetGrid().Execute(CSVCMD_SETCOLUMNTYPE, rListBox.get_active());
+}
+
+IMPL_LINK_NOARG(ScImportAsciiDlg, UpdateTextHdl, ScCsvTableBox&, void)
+{
+ // Checking the separator can only be done once for the very first time
+ // when the dialog wasn't already presented to the user.
+ // As a side effect this has the benefit that the check is only done on the
+ // first set of visible lines.
+ mbDetectSep = (mbDetectSep && !mxRbFixed->get_active()
+ && (!mxCkbTab->get_active() || !mxCkbSemicolon->get_active()
+ || !mxCkbComma->get_active() || !mxCkbSpace->get_active()));
+ sal_Unicode cDetectSep = (mbDetectSep ? 0 : 0xffff);
+
+ sal_Int32 nBaseLine = mxTableBox->GetGrid().GetFirstVisLine();
+ sal_Int32 nRead = mxTableBox->GetGrid().GetVisLineCount();
+ // If mnRowPosCount==0, this is an initializing call, read ahead for row
+ // count and resulting scroll bar size and position to be able to scroll at
+ // all. When adding lines, read only the amount of next lines to be
+ // displayed.
+ if (!mnRowPosCount || nRead > CSV_PREVIEW_LINES)
+ nRead = CSV_PREVIEW_LINES;
+
+ sal_Int32 i;
+ for (i = 0; i < nRead; i++)
+ {
+ if (!GetLine( nBaseLine + i, maPreviewLine[i], cDetectSep))
+ break;
+ }
+ for (; i < CSV_PREVIEW_LINES; i++)
+ maPreviewLine[i].clear();
+
+ if (mbDetectSep)
+ {
+ mbDetectSep = false;
+ if (cDetectSep)
+ {
+ // Expect separator to be appended by now so all subsequent
+ // GetLine()/ReadCsvLine() actually used it.
+ assert(maFieldSeparators.endsWith(OUStringChar(cDetectSep)));
+ // Preselect separator in UI.
+ switch (cDetectSep)
+ {
+ case '\t': mxCkbTab->set_active(true); break;
+ case ';': mxCkbSemicolon->set_active(true); break;
+ case ',': mxCkbComma->set_active(true); break;
+ case ' ': mxCkbSpace->set_active(true); break;
+ }
+ }
+ }
+
+ mxTableBox->GetGrid().Execute( CSVCMD_SETLINECOUNT, mnRowPosCount);
+ bool bMergeSep = mxCkbAsOnce->get_active();
+ bool bRemoveSpace = mxCkbRemoveSpace->get_active();
+ mxTableBox->SetUniStrings( maPreviewLine, maFieldSeparators, mcTextSep, bMergeSep, bRemoveSpace );
+}
+
+IMPL_LINK( ScImportAsciiDlg, ColTypeHdl, ScCsvTableBox&, rTableBox, void )
+{
+ sal_Int32 nType = rTableBox.GetSelColumnType();
+ sal_Int32 nTypeCount = mxLbType->get_count();
+ bool bEmpty = (nType == CSV_TYPE_MULTI);
+ bool bEnable = ((0 <= nType) && (nType < nTypeCount)) || bEmpty;
+
+ mxFtType->set_sensitive( bEnable );
+ mxLbType->set_sensitive( bEnable );
+
+ if (bEmpty)
+ mxLbType->set_active(-1);
+ else if (bEnable)
+ mxLbType->set_active(nType);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/scuiimoptdlg.cxx b/sc/source/ui/dbgui/scuiimoptdlg.cxx
new file mode 100644
index 000000000..e04f0b672
--- /dev/null
+++ b/sc/source/ui/dbgui/scuiimoptdlg.cxx
@@ -0,0 +1,340 @@
+/* -*- 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 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <scuiimoptdlg.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+#include <strings.hxx>
+#include <officecfg/Office/Calc.hxx>
+#include <osl/thread.h>
+#include <rtl/tencinfo.h>
+#include <imoptdlg.hxx>
+#include <svx/txencbox.hxx>
+#include <o3tl/string_view.hxx>
+
+// ScDelimiterTable
+
+class ScDelimiterTable
+{
+public:
+ explicit ScDelimiterTable( const OUString& rDelTab )
+ : theDelTab ( rDelTab ),
+ nDelIdx ( 0 )
+ {}
+
+ sal_uInt16 GetCode( std::u16string_view rDelimiter ) const;
+ OUString GetDelimiter( sal_Unicode nCode ) const;
+
+ OUString FirstDel() { nDelIdx = 0; return theDelTab.getToken( 0, cSep, nDelIdx ); }
+ OUString NextDel() { return theDelTab.getToken( 1, cSep, nDelIdx ); }
+
+private:
+ const OUString theDelTab;
+ static constexpr sal_Unicode cSep {'\t'};
+ sal_Int32 nDelIdx;
+};
+
+sal_uInt16 ScDelimiterTable::GetCode( std::u16string_view rDel ) const
+{
+ if (!theDelTab.isEmpty())
+ {
+ sal_Int32 nIdx {0};
+
+ // Check even tokens: start from 0 and then skip 1 token at each iteration
+ if (rDel != o3tl::getToken(theDelTab, 0, cSep, nIdx ))
+ while (nIdx>0 && rDel != o3tl::getToken(theDelTab, 1, cSep, nIdx ));
+
+ if (nIdx>0)
+ return static_cast<sal_Unicode>(o3tl::toInt32(o3tl::getToken(theDelTab, 0, cSep, nIdx )));
+ }
+
+ return 0;
+}
+
+OUString ScDelimiterTable::GetDelimiter( sal_Unicode nCode ) const
+{
+ if (!theDelTab.isEmpty())
+ {
+ sal_Int32 nIdx {0};
+ // Check odd tokens: start from 1 and then skip 1 token at each iteration
+ do
+ {
+ sal_Int32 nPrevIdx {nIdx};
+ if (nCode == static_cast<sal_Unicode>(o3tl::toInt32(o3tl::getToken(theDelTab, 1, cSep, nIdx ))))
+ return theDelTab.getToken( 0, cSep, nPrevIdx );
+ }
+ while (nIdx>0);
+ }
+
+ return OUString();
+}
+
+void ScImportOptionsDlg::FillFromTextEncodingTable(bool bExcludeImportSubsets, sal_uInt32 nExcludeInfoFlags)
+{
+ if (m_bIsAsciiImport)
+ m_xLbCharset->FillFromTextEncodingTable(bExcludeImportSubsets, nExcludeInfoFlags);
+ else
+ m_xTvCharset->FillFromTextEncodingTable(bExcludeImportSubsets, nExcludeInfoFlags);
+}
+
+void ScImportOptionsDlg::FillFromDbTextEncodingMap(bool bExcludeImportSubsets, sal_uInt32 nExcludeInfoFlags)
+{
+ if (m_bIsAsciiImport)
+ m_xLbCharset->FillFromDbTextEncodingMap(bExcludeImportSubsets, nExcludeInfoFlags);
+ else
+ m_xTvCharset->FillFromDbTextEncodingMap(bExcludeImportSubsets, nExcludeInfoFlags);
+}
+
+// ScImportOptionsDlg
+ScImportOptionsDlg::ScImportOptionsDlg(weld::Window* pParent, bool bAscii,
+ const ScImportOptions* pOptions,
+ const OUString* pStrTitle,
+ bool bMultiByte, bool bOnlyDbtoolsEncodings,
+ bool bImport)
+ : GenericDialogController(pParent, "modules/scalc/ui/imoptdialog.ui", "ImOptDialog")
+ , m_bIsAsciiImport(bAscii)
+ , m_xFieldFrame(m_xBuilder->weld_frame("fieldframe"))
+ , m_xFtCharset(m_xBuilder->weld_label("charsetft"))
+ , m_xEncGrid(m_xBuilder->weld_widget("grid2"))
+ , m_xFtFieldSep(m_xBuilder->weld_label("fieldft"))
+ , m_xEdFieldSep(m_xBuilder->weld_combo_box("field"))
+ , m_xFtTextSep(m_xBuilder->weld_label("textft"))
+ , m_xEdTextSep(m_xBuilder->weld_combo_box("text"))
+ , m_xCbShown(m_xBuilder->weld_check_button("asshown"))
+ , m_xCbFormulas(m_xBuilder->weld_check_button("formulas"))
+ , m_xCbQuoteAll(m_xBuilder->weld_check_button("quoteall"))
+ , m_xCbFixed(m_xBuilder->weld_check_button("fixedwidth"))
+ , m_xBtnOk(m_xBuilder->weld_button("ok"))
+ , m_xLbCharset(new SvxTextEncodingBox(m_xBuilder->weld_combo_box("charsetdropdown")))
+ , m_xTvCharset(new SvxTextEncodingTreeView(m_xBuilder->weld_tree_view("charsetlist")))
+{
+ if (bAscii)
+ {
+ m_xDialog->set_help_id(m_xDialog->get_help_id() + "?config=NonTextImport");
+ m_xLbCharset->show();
+ m_xTvCharset->hide();
+ }
+ else
+ {
+ m_xTvCharset->set_size_request(-1, m_xTvCharset->get_height_rows(6));
+ m_xEncGrid->set_vexpand(true);
+ m_xLbCharset->hide();
+ m_xTvCharset->show();
+ }
+
+ OUString sFieldSep(SCSTR_FIELDSEP);
+ sFieldSep = sFieldSep.replaceFirst( "%TAB", ScResId(SCSTR_FIELDSEP_TAB) );
+ sFieldSep = sFieldSep.replaceFirst( "%SPACE", ScResId(SCSTR_FIELDSEP_SPACE) );
+
+ // not possible in the Ctor initializer (MSC cannot do that):
+ pFieldSepTab.reset( new ScDelimiterTable(sFieldSep) );
+ pTextSepTab.reset( new ScDelimiterTable(SCSTR_TEXTSEP) );
+
+ OUString aStr = pFieldSepTab->FirstDel();
+
+ while (!aStr.isEmpty())
+ {
+ m_xEdFieldSep->append_text(aStr);
+ aStr = pFieldSepTab->NextDel();
+ }
+
+ aStr = pTextSepTab->FirstDel();
+
+ while (!aStr.isEmpty())
+ {
+ m_xEdTextSep->append_text(aStr);
+ aStr = pTextSepTab->NextDel();
+ }
+
+ m_xEdFieldSep->set_active(0);
+ m_xEdTextSep->set_active(0);
+
+ if ( bOnlyDbtoolsEncodings )
+ {
+ // Even dBase export allows multibyte now
+ if ( bMultiByte )
+ FillFromDbTextEncodingMap( bImport );
+ else
+ FillFromDbTextEncodingMap( bImport, RTL_TEXTENCODING_INFO_MULTIBYTE );
+ }
+ else if ( !bAscii )
+ { //!TODO: Unicode would need work in each filter
+ if ( bMultiByte )
+ FillFromTextEncodingTable( bImport, RTL_TEXTENCODING_INFO_UNICODE );
+ else
+ FillFromTextEncodingTable( bImport, RTL_TEXTENCODING_INFO_UNICODE |
+ RTL_TEXTENCODING_INFO_MULTIBYTE );
+ }
+ else
+ {
+ if ( pOptions )
+ {
+ sal_Unicode nCode = pOptions->nFieldSepCode;
+ aStr = pFieldSepTab->GetDelimiter( nCode );
+
+ if ( aStr.isEmpty() )
+ m_xEdFieldSep->set_entry_text(OUString(nCode));
+ else
+ m_xEdFieldSep->set_entry_text(aStr);
+
+ nCode = pOptions->nTextSepCode;
+ aStr = pTextSepTab->GetDelimiter( nCode );
+
+ if ( aStr.isEmpty() )
+ m_xEdTextSep->set_entry_text(OUString(nCode));
+ else
+ m_xEdTextSep->set_entry_text(aStr);
+ }
+ // all encodings allowed, even Unicode
+ FillFromTextEncodingTable( bImport );
+ }
+
+ if( bAscii )
+ {
+ sal_Int32 nCharSet = officecfg::Office::Calc::Dialogs::CSVExport::CharSet::get();
+ OUString strFieldSeparator = officecfg::Office::Calc::Dialogs::CSVExport::FieldSeparator::get();
+ OUString strTextSeparator = officecfg::Office::Calc::Dialogs::CSVExport::TextSeparator::get();
+ bool bSaveTrueCellContent = officecfg::Office::Calc::Dialogs::CSVExport::SaveTrueCellContent::get();
+ bool bSaveCellFormulas = officecfg::Office::Calc::Dialogs::CSVExport::SaveCellFormulas::get();
+ bool bQuoteAllTextCells = officecfg::Office::Calc::Dialogs::CSVExport::QuoteAllTextCells::get();
+ bool bFixedWidth = officecfg::Office::Calc::Dialogs::CSVExport::FixedWidth::get();
+
+ m_xCbFixed->show();
+ m_xCbFixed->connect_toggled(LINK(this, ScImportOptionsDlg, FixedWidthHdl));
+ m_xCbFixed->set_active( bFixedWidth );
+ FixedWidthHdl(*m_xCbFixed);
+ m_xCbShown->show();
+ m_xCbShown->set_active( bSaveTrueCellContent );
+ m_xCbQuoteAll->show();
+ m_xCbQuoteAll->set_active( bQuoteAllTextCells );
+ m_xCbFormulas->show();
+ // default option for "save formulas" no longer taken from view shell but from persisted dialog settings
+ m_xCbFormulas->set_active( bSaveCellFormulas );
+ // if no charset, text separator or field separator exist, keep the values from dialog initialization
+ if (strFieldSeparator.getLength() > 0)
+ m_xEdFieldSep->set_entry_text(strFieldSeparator);
+ if (strTextSeparator.getLength() > 0)
+ m_xEdTextSep->set_entry_text(strTextSeparator);
+ if (nCharSet < 0 || nCharSet == RTL_TEXTENCODING_DONTKNOW )
+ m_xLbCharset->SelectTextEncoding(pOptions ? pOptions->eCharSet : osl_getThreadTextEncoding());
+ else
+ m_xLbCharset->SelectTextEncoding(nCharSet);
+ }
+ else
+ {
+ m_xFieldFrame->set_label(m_xFtCharset->get_label());
+ m_xFtFieldSep->hide();
+ m_xFtTextSep->hide();
+ m_xFtCharset->hide();
+ m_xEdFieldSep->hide();
+ m_xEdTextSep->hide();
+ m_xCbFixed->hide();
+ m_xCbShown->hide();
+ m_xCbQuoteAll->hide();
+ m_xCbFormulas->hide();
+ m_xTvCharset->grab_focus();
+ m_xTvCharset->connect_row_activated(LINK(this, ScImportOptionsDlg, DoubleClickHdl));
+ m_xTvCharset->SelectTextEncoding(pOptions ? pOptions->eCharSet : osl_getThreadTextEncoding());
+ }
+
+ // optional title:
+ if (pStrTitle)
+ m_xDialog->set_title(*pStrTitle);
+}
+
+ScImportOptionsDlg::~ScImportOptionsDlg()
+{
+}
+
+void ScImportOptionsDlg::GetImportOptions( ScImportOptions& rOptions ) const
+{
+ auto nEncoding = m_bIsAsciiImport ? m_xLbCharset->GetSelectTextEncoding() : m_xTvCharset->GetSelectTextEncoding();
+ rOptions.SetTextEncoding(nEncoding);
+
+ if (m_xCbFixed->get_visible())
+ {
+ rOptions.nFieldSepCode = GetCodeFromCombo( *m_xEdFieldSep );
+ rOptions.nTextSepCode = GetCodeFromCombo( *m_xEdTextSep );
+ rOptions.bFixedWidth = m_xCbFixed->get_active();
+ rOptions.bSaveAsShown = m_xCbShown->get_active();
+ rOptions.bQuoteAllText = m_xCbQuoteAll->get_active();
+ rOptions.bSaveFormulas = m_xCbFormulas->get_active();
+ }
+}
+
+sal_uInt16 ScImportOptionsDlg::GetCodeFromCombo(const weld::ComboBox& rEd) const
+{
+ ScDelimiterTable* pTab;
+ OUString aStr( rEd.get_active_text() );
+ sal_uInt16 nCode;
+
+ if (&rEd == m_xEdTextSep.get())
+ pTab = pTextSepTab.get();
+ else
+ pTab = pFieldSepTab.get();
+
+ if ( aStr.isEmpty() )
+ {
+ nCode = 0; // no separator
+ }
+ else
+ {
+ nCode = pTab->GetCode( aStr );
+
+ if ( nCode == 0 )
+ nCode = static_cast<sal_uInt16>(aStr[0]);
+ }
+
+ return nCode;
+}
+
+IMPL_LINK_NOARG(ScImportOptionsDlg, FixedWidthHdl, weld::Toggleable&, void)
+{
+ bool bEnable = !m_xCbFixed->get_active();
+ m_xFtFieldSep->set_sensitive( bEnable );
+ m_xEdFieldSep->set_sensitive( bEnable );
+ m_xFtTextSep->set_sensitive( bEnable );
+ m_xEdTextSep->set_sensitive( bEnable );
+ m_xCbShown->set_sensitive( bEnable );
+ m_xCbQuoteAll->set_sensitive( bEnable );
+}
+
+IMPL_LINK_NOARG(ScImportOptionsDlg, DoubleClickHdl, weld::TreeView&, bool)
+{
+ m_xDialog->response(RET_OK);
+ return true;
+}
+
+void ScImportOptionsDlg::SaveImportOptions() const
+{
+ std::shared_ptr < comphelper::ConfigurationChanges > batch(comphelper::ConfigurationChanges::create());
+ auto nEncoding = m_bIsAsciiImport ? m_xLbCharset->GetSelectTextEncoding() : m_xTvCharset->GetSelectTextEncoding();
+ officecfg::Office::Calc::Dialogs::CSVExport::CharSet::set(nEncoding, batch);
+ officecfg::Office::Calc::Dialogs::CSVExport::FieldSeparator::set(m_xEdFieldSep->get_active_text(), batch);
+ officecfg::Office::Calc::Dialogs::CSVExport::TextSeparator::set(m_xEdTextSep->get_active_text(), batch);
+ officecfg::Office::Calc::Dialogs::CSVExport::FixedWidth::set(m_xCbFixed->get_active(), batch);
+ officecfg::Office::Calc::Dialogs::CSVExport::SaveCellFormulas::set(m_xCbFormulas->get_active(), batch);
+ officecfg::Office::Calc::Dialogs::CSVExport::SaveTrueCellContent::set(m_xCbShown->get_active(), batch);
+ officecfg::Office::Calc::Dialogs::CSVExport::QuoteAllTextCells::set(m_xCbQuoteAll->get_active(), batch);
+ batch->commit();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/sfiltdlg.cxx b/sc/source/ui/dbgui/sfiltdlg.cxx
new file mode 100644
index 000000000..3aeb31923
--- /dev/null
+++ b/sc/source/ui/dbgui/sfiltdlg.cxx
@@ -0,0 +1,435 @@
+/* -*- 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 <uiitems.hxx>
+#include <rangenam.hxx>
+#include <reffact.hxx>
+#include <viewdata.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <scresid.hxx>
+
+#include <foptmgr.hxx>
+
+#include <globstr.hrc>
+#include <strings.hrc>
+
+#include <filtdlg.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+
+// DEFINE --------------------------------------------------------------------
+
+namespace
+{
+ void ERRORBOX(weld::Window* pParent, TranslateId rid)
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pParent,
+ VclMessageType::Warning, VclButtonsType::Ok,
+ ScResId(rid)));
+ xBox->run();
+ }
+}
+
+
+ScSpecialFilterDlg::ScSpecialFilterDlg( SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent,
+ const SfxItemSet& rArgSet )
+
+ : ScAnyRefDlgController(pB, pCW, pParent, "modules/scalc/ui/advancedfilterdialog.ui", "AdvancedFilterDialog")
+ , aStrUndefined ( ScResId(SCSTR_UNDEFINED) )
+ , nWhichQuery ( rArgSet.GetPool()->GetWhich( SID_QUERY ) )
+ , theQueryData ( static_cast<const ScQueryItem&>(
+ rArgSet.Get( nWhichQuery )).GetQueryData() )
+ , pViewData(nullptr)
+ , pDoc(nullptr)
+ , bRefInputMode(false)
+ , m_pRefInputEdit(nullptr)
+ , m_xLbFilterArea(m_xBuilder->weld_combo_box("lbfilterarea"))
+ , m_xEdFilterArea(new formula::RefEdit(m_xBuilder->weld_entry("edfilterarea")))
+ , m_xRbFilterArea(new formula::RefButton(m_xBuilder->weld_button("rbfilterarea")))
+ , m_xExpander(m_xBuilder->weld_expander("more"))
+ , m_xBtnCase(m_xBuilder->weld_check_button("case"))
+ , m_xBtnRegExp(m_xBuilder->weld_check_button("regexp"))
+ , m_xBtnHeader(m_xBuilder->weld_check_button("header"))
+ , m_xBtnUnique(m_xBuilder->weld_check_button("unique"))
+ , m_xBtnCopyResult(m_xBuilder->weld_check_button("copyresult"))
+ , m_xLbCopyArea(m_xBuilder->weld_combo_box("lbcopyarea"))
+ , m_xEdCopyArea(new formula::RefEdit(m_xBuilder->weld_entry("edcopyarea")))
+ , m_xRbCopyArea(new formula::RefButton(m_xBuilder->weld_button("rbcopyarea")))
+ , m_xBtnDestPers(m_xBuilder->weld_check_button("destpers"))
+ , m_xFtDbAreaLabel(m_xBuilder->weld_label("dbarealabel"))
+ , m_xFtDbArea(m_xBuilder->weld_label("dbarea"))
+ , m_xBtnOk(m_xBuilder->weld_button("ok"))
+ , m_xBtnCancel(m_xBuilder->weld_button("cancel"))
+ , m_xFilterFrame(m_xBuilder->weld_frame("filterframe"))
+ , m_xFilterLabel(m_xFilterFrame->weld_label_widget())
+{
+ m_xEdFilterArea->SetReferences(this, m_xFilterLabel.get());
+ m_xRbFilterArea->SetReferences(this, m_xEdFilterArea.get());
+ m_xEdCopyArea->SetReferences(this, m_xFtDbAreaLabel.get());
+ m_xRbCopyArea->SetReferences(this, m_xEdCopyArea.get());
+
+ Init( rArgSet );
+
+ Link<formula::RefEdit&, void> aLinkEdit = LINK(this, ScSpecialFilterDlg, RefInputEditHdl);
+ Link<formula::RefButton&, void> aLinkButton = LINK(this, ScSpecialFilterDlg, RefInputButtonHdl);
+ m_xEdCopyArea->SetGetFocusHdl(aLinkEdit);
+ m_xRbCopyArea->SetGetFocusHdl(aLinkButton);
+ m_xEdFilterArea->SetGetFocusHdl(aLinkEdit);
+ m_xRbFilterArea->SetGetFocusHdl(aLinkButton);
+ m_xEdCopyArea->SetLoseFocusHdl(aLinkEdit);
+ m_xRbCopyArea->SetLoseFocusHdl(aLinkButton);
+ m_xEdFilterArea->SetLoseFocusHdl(aLinkEdit);
+ m_xRbFilterArea->SetLoseFocusHdl(aLinkButton);
+
+ m_xEdFilterArea->GrabFocus();
+}
+
+ScSpecialFilterDlg::~ScSpecialFilterDlg()
+{
+ pOptionsMgr.reset();
+
+ pOutItem.reset();
+}
+
+void ScSpecialFilterDlg::Init( const SfxItemSet& rArgSet )
+{
+ const ScQueryItem& rQueryItem = static_cast<const ScQueryItem&>(
+ rArgSet.Get( nWhichQuery ));
+
+ m_xBtnOk->connect_clicked( LINK( this, ScSpecialFilterDlg, EndDlgHdl ) );
+ m_xBtnCancel->connect_clicked( LINK( this, ScSpecialFilterDlg, EndDlgHdl ) );
+ m_xLbFilterArea->connect_changed( LINK( this, ScSpecialFilterDlg, FilterAreaSelHdl ) );
+ m_xEdFilterArea->SetModifyHdl ( LINK( this, ScSpecialFilterDlg, FilterAreaModHdl ) );
+
+ pViewData = rQueryItem.GetViewData();
+ pDoc = pViewData ? &pViewData->GetDocument() : nullptr;
+
+ m_xEdFilterArea->SetText( OUString() ); // may be overwritten below
+
+ if ( pViewData && pDoc )
+ {
+ if(pDoc->GetChangeTrack()!=nullptr) m_xBtnCopyResult->set_sensitive(false);
+
+ ScRangeName* pRangeNames = pDoc->GetRangeName();
+ m_xLbFilterArea->clear();
+ m_xLbFilterArea->append_text(aStrUndefined);
+
+ for (const auto& rEntry : *pRangeNames)
+ {
+ if (!rEntry.second->HasType(ScRangeData::Type::Criteria))
+ continue;
+
+ OUString aSymbol = rEntry.second->GetSymbol();
+ m_xLbFilterArea->append(aSymbol, rEntry.second->GetName());
+ }
+
+ // is there a stored source range?
+
+ ScRange aAdvSource;
+ if (rQueryItem.GetAdvancedQuerySource(aAdvSource))
+ {
+ OUString aRefStr(aAdvSource.Format(*pDoc, ScRefFlags::RANGE_ABS_3D, pDoc->GetAddressConvention()));
+ m_xEdFilterArea->SetRefString( aRefStr );
+ }
+ }
+
+ m_xLbFilterArea->set_active( 0 );
+
+ // let options be initialized:
+
+ pOptionsMgr.reset( new ScFilterOptionsMgr(
+ pViewData,
+ theQueryData,
+ m_xBtnCase.get(),
+ m_xBtnRegExp.get(),
+ m_xBtnHeader.get(),
+ m_xBtnUnique.get(),
+ m_xBtnCopyResult.get(),
+ m_xBtnDestPers.get(),
+ m_xLbCopyArea.get(),
+ m_xEdCopyArea.get(),
+ m_xRbCopyArea.get(),
+ m_xFtDbAreaLabel.get(),
+ m_xFtDbArea.get(),
+ aStrUndefined ) );
+
+ // special filter always needs column headers
+ m_xBtnHeader->set_active(true);
+ m_xBtnHeader->set_sensitive(false);
+
+ // turn on modal mode
+ // SetDispatcherLock( true );
+ //@BugID 54702 enable/disable in base class only
+ //SFX_APPWINDOW->Disable(false); //! general method in ScAnyRefDlg
+}
+
+void ScSpecialFilterDlg::Close()
+{
+ if (pViewData)
+ pViewData->GetDocShell()->CancelAutoDBRange();
+
+ DoClose( ScSpecialFilterDlgWrapper::GetChildWindowId() );
+}
+
+// Transfer of a table area selected with the mouse, which is then displayed
+// as a new selection in the reference edit.
+
+void ScSpecialFilterDlg::SetReference( const ScRange& rRef, ScDocument& rDocP )
+{
+ if ( !(bRefInputMode && m_pRefInputEdit) ) // only possible if in the reference edit mode
+ return;
+
+ if ( rRef.aStart != rRef.aEnd )
+ RefInputStart( m_pRefInputEdit );
+
+ OUString aRefStr;
+ const formula::FormulaGrammar::AddressConvention eConv = rDocP.GetAddressConvention();
+
+ if (m_pRefInputEdit == m_xEdCopyArea.get())
+ aRefStr = rRef.aStart.Format(ScRefFlags::ADDR_ABS_3D, &rDocP, eConv);
+ else if (m_pRefInputEdit == m_xEdFilterArea.get())
+ aRefStr = rRef.Format(rDocP, ScRefFlags::RANGE_ABS_3D, eConv);
+
+ m_pRefInputEdit->SetRefString( aRefStr );
+}
+
+void ScSpecialFilterDlg::SetActive()
+{
+ if ( bRefInputMode )
+ {
+ if (m_pRefInputEdit == m_xEdCopyArea.get())
+ {
+ m_xEdCopyArea->GrabFocus();
+ m_xEdCopyArea->GetModifyHdl().Call( *m_xEdCopyArea );
+ }
+ else if (m_pRefInputEdit == m_xEdFilterArea.get())
+ {
+ m_xEdFilterArea->GrabFocus();
+ FilterAreaModHdl( *m_xEdFilterArea );
+ }
+ }
+ else
+ m_xDialog->grab_focus();
+
+ RefInputDone();
+}
+
+ScQueryItem* ScSpecialFilterDlg::GetOutputItem( const ScQueryParam& rParam,
+ const ScRange& rSource )
+{
+ pOutItem.reset(new ScQueryItem( nWhichQuery, &rParam ));
+ pOutItem->SetAdvancedQuerySource( &rSource );
+ return pOutItem.get();
+}
+
+bool ScSpecialFilterDlg::IsRefInputMode() const
+{
+ return bRefInputMode;
+}
+
+// Handler:
+
+IMPL_LINK(ScSpecialFilterDlg, EndDlgHdl, weld::Button&, rBtn, void)
+{
+ OSL_ENSURE( pDoc && pViewData, "Document or ViewData not found. :-/" );
+
+ if (&rBtn == m_xBtnOk.get() && pDoc && pViewData)
+ {
+ OUString theCopyStr( m_xEdCopyArea->GetText() );
+ OUString theAreaStr( m_xEdFilterArea->GetText() );
+ ScQueryParam theOutParam( theQueryData );
+ ScAddress theAdrCopy;
+ bool bEditInputOk = true;
+ bool bQueryOk = false;
+ ScRange theFilterArea;
+ const formula::FormulaGrammar::AddressConvention eConv = pDoc->GetAddressConvention();
+
+ if ( m_xBtnCopyResult->get_active() )
+ {
+ sal_Int32 nColonPos = theCopyStr.indexOf( ':' );
+
+ if ( -1 != nColonPos )
+ theCopyStr = theCopyStr.copy( 0, nColonPos );
+
+ ScRefFlags nResult = theAdrCopy.Parse( theCopyStr, *pDoc, eConv );
+
+ if ( (nResult & ScRefFlags::VALID) == ScRefFlags::ZERO )
+ {
+ if (!m_xExpander->get_expanded())
+ m_xExpander->set_expanded(true);
+
+ ERRORBOX(m_xDialog.get(), STR_INVALID_TABREF);
+ m_xEdCopyArea->GrabFocus();
+ bEditInputOk = false;
+ }
+ }
+
+ if ( bEditInputOk )
+ {
+ ScRefFlags nResult = ScRange().Parse( theAreaStr, *pDoc, eConv );
+
+ if ( (nResult & ScRefFlags::VALID) == ScRefFlags::ZERO )
+ {
+ ERRORBOX(m_xDialog.get(), STR_INVALID_TABREF);
+ m_xEdFilterArea->GrabFocus();
+ bEditInputOk = false;
+ }
+ }
+
+ if ( bEditInputOk )
+ {
+ /*
+ * All edit fields contain valid areas. Now try to create
+ * a ScQueryParam from the filter area:
+ */
+
+ ScRefFlags nResult = theFilterArea.Parse( theAreaStr, *pDoc, eConv );
+
+ if ( (nResult & ScRefFlags::VALID) == ScRefFlags::VALID )
+ {
+ ScAddress& rStart = theFilterArea.aStart;
+ ScAddress& rEnd = theFilterArea.aEnd;
+
+ if ( m_xBtnCopyResult->get_active() )
+ {
+ theOutParam.bInplace = false;
+ theOutParam.nDestTab = theAdrCopy.Tab();
+ theOutParam.nDestCol = theAdrCopy.Col();
+ theOutParam.nDestRow = theAdrCopy.Row();
+ }
+ else
+ {
+ theOutParam.bInplace = true;
+ theOutParam.nDestTab = 0;
+ theOutParam.nDestCol = 0;
+ theOutParam.nDestRow = 0;
+ }
+
+ theOutParam.bHasHeader = m_xBtnHeader->get_active();
+ theOutParam.bByRow = true;
+ theOutParam.bCaseSens = m_xBtnCase->get_active();
+ theOutParam.eSearchType = m_xBtnRegExp->get_active() ? utl::SearchParam::SearchType::Regexp :
+ utl::SearchParam::SearchType::Normal;
+ theOutParam.bDuplicate = !m_xBtnUnique->get_active();
+ theOutParam.bDestPers = m_xBtnDestPers->get_active();
+
+ bQueryOk = pDoc->CreateQueryParam(ScRange(rStart,rEnd), theOutParam);
+ }
+ }
+
+ if ( bQueryOk )
+ {
+ SetDispatcherLock( false );
+ SwitchToDocument();
+ GetBindings().GetDispatcher()->ExecuteList(FID_FILTER_OK,
+ SfxCallMode::SLOT | SfxCallMode::RECORD,
+ { GetOutputItem(theOutParam, theFilterArea) });
+ response(RET_OK);
+ }
+ else
+ {
+ ERRORBOX(m_xDialog.get(), STR_INVALID_QUERYAREA);
+ m_xEdFilterArea->GrabFocus();
+ }
+ }
+ else if (&rBtn == m_xBtnCancel.get())
+ {
+ response(RET_CANCEL);
+ }
+}
+
+IMPL_LINK_NOARG(ScSpecialFilterDlg, RefInputEditHdl, formula::RefEdit&, void)
+{
+ RefInputHdl();
+}
+
+IMPL_LINK_NOARG(ScSpecialFilterDlg, RefInputButtonHdl, formula::RefButton&, void)
+{
+ RefInputHdl();
+}
+
+void ScSpecialFilterDlg::RefInputHdl()
+{
+ if (!m_xDialog->has_toplevel_focus())
+ return;
+
+ if( m_xEdCopyArea->GetWidget()->has_focus() || m_xRbCopyArea->GetWidget()->has_focus() )
+ {
+ m_pRefInputEdit = m_xEdCopyArea.get();
+ bRefInputMode = true;
+ }
+ else if( m_xEdFilterArea->GetWidget()->has_focus() || m_xRbFilterArea->GetWidget()->has_focus() )
+ {
+ m_pRefInputEdit = m_xEdFilterArea.get();
+ bRefInputMode = true;
+ }
+ else if( bRefInputMode )
+ {
+ m_pRefInputEdit = nullptr;
+ bRefInputMode = false;
+ }
+}
+
+IMPL_LINK(ScSpecialFilterDlg, FilterAreaSelHdl, weld::ComboBox&, rLb, void)
+{
+ if (&rLb == m_xLbFilterArea.get())
+ {
+ OUString aString;
+ const sal_Int32 nSelPos = m_xLbFilterArea->get_active();
+
+ if ( nSelPos > 0 )
+ aString = m_xLbFilterArea->get_id(nSelPos);
+
+ m_xEdFilterArea->SetText( aString );
+ }
+}
+
+IMPL_LINK( ScSpecialFilterDlg, FilterAreaModHdl, formula::RefEdit&, rEd, void )
+{
+ if (&rEd != m_xEdFilterArea.get())
+ return;
+
+ if ( pDoc && pViewData )
+ {
+ OUString theCurAreaStr = rEd.GetText();
+ ScRefFlags nResult = ScRange().Parse( theCurAreaStr, *pDoc );
+
+ if ( (nResult & ScRefFlags::VALID) == ScRefFlags::VALID )
+ {
+ const sal_Int32 nCount = m_xLbFilterArea->get_count();
+ for (sal_Int32 i = 1; i < nCount; ++i)
+ {
+ OUString aStr = m_xLbFilterArea->get_id(i);
+ if (theCurAreaStr == aStr)
+ {
+ m_xLbFilterArea->set_active( i );
+ return;
+ }
+ }
+ m_xLbFilterArea->set_active( 0 );
+ }
+ }
+ else
+ m_xLbFilterArea->set_active( 0 );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/sortdlg.cxx b/sc/source/ui/dbgui/sortdlg.cxx
new file mode 100644
index 000000000..22af2230b
--- /dev/null
+++ b/sc/source/ui/dbgui/sortdlg.cxx
@@ -0,0 +1,65 @@
+/* -*- 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 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <scui_def.hxx>
+#include <tpsort.hxx>
+#include <sortdlg.hxx>
+
+ScSortDlg::ScSortDlg(weld::Window* pParent, const SfxItemSet* pArgSet)
+ : SfxTabDialogController(pParent, "modules/scalc/ui/sortdialog.ui", "SortDialog", pArgSet)
+{
+ AddTabPage("criteria", ScTabPageSortFields::Create, nullptr);
+ AddTabPage("options", ScTabPageSortOptions::Create, nullptr);
+}
+
+ScSortWarningDlg::ScSortWarningDlg(weld::Window* pParent,
+ std::u16string_view rExtendText, std::u16string_view rCurrentText)
+ : GenericDialogController(pParent, "modules/scalc/ui/sortwarning.ui", "SortWarning")
+ , m_xFtText(m_xBuilder->weld_label("sorttext"))
+ , m_xBtnExtSort(m_xBuilder->weld_button("extend"))
+ , m_xBtnCurSort(m_xBuilder->weld_button("current"))
+{
+ OUString sTextName = m_xFtText->get_label();
+ sTextName = sTextName.replaceFirst("%1", rExtendText);
+ sTextName = sTextName.replaceFirst("%2", rCurrentText);
+ m_xFtText->set_label(sTextName);
+
+ m_xBtnExtSort->connect_clicked( LINK( this, ScSortWarningDlg, BtnHdl ) );
+ m_xBtnCurSort->connect_clicked( LINK( this, ScSortWarningDlg, BtnHdl ) );
+}
+
+ScSortWarningDlg::~ScSortWarningDlg()
+{
+}
+
+IMPL_LINK(ScSortWarningDlg, BtnHdl, weld::Button&, rBtn, void)
+{
+ if (&rBtn == m_xBtnExtSort.get())
+ {
+ m_xDialog->response(BTN_EXTEND_RANGE);
+ }
+ else if(&rBtn == m_xBtnCurSort.get())
+ {
+ m_xDialog->response(BTN_CURRENT_SELECTION);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/sortkeydlg.cxx b/sc/source/ui/dbgui/sortkeydlg.cxx
new file mode 100644
index 000000000..599280735
--- /dev/null
+++ b/sc/source/ui/dbgui/sortkeydlg.cxx
@@ -0,0 +1,77 @@
+/* -*- 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 <memory>
+#include <sortkeydlg.hxx>
+#include <vcl/svapp.hxx>
+
+#include <scresid.hxx>
+#include <strings.hrc>
+
+ScSortKeyItem::ScSortKeyItem(weld::Container* pParent)
+ : m_xBuilder(Application::CreateBuilder(pParent, "modules/scalc/ui/sortkey.ui"))
+ , m_xFrame(m_xBuilder->weld_frame("SortKeyFrame"))
+ , m_xLbSort(m_xBuilder->weld_combo_box("sortlb"))
+ , m_xBtnUp(m_xBuilder->weld_radio_button("up"))
+ , m_xBtnDown(m_xBuilder->weld_radio_button("down"))
+ , m_xLabel(m_xBuilder->weld_label("lbColRow"))
+ , m_pParent(pParent)
+{
+ // tdf#136155 let the other elements in the dialog determine the width of the
+ // combobox
+ m_xLbSort->set_size_request(m_xLbSort->get_approximate_digit_width() * 12, -1);
+ // keep the UI static when switching the labels
+ const sal_Int32 nChars = std::max( ScResId(SCSTR_COLUMN).getLength(), ScResId(SCSTR_ROW).getLength() ) + 2; // +2 to avoid cut-off labels on kf5/gen
+ m_xLabel->set_size_request( m_xLabel->get_approximate_digit_width() * nChars, -1);
+}
+
+ScSortKeyItem::~ScSortKeyItem()
+{
+ m_pParent->move(m_xFrame.get(), nullptr);
+}
+
+void ScSortKeyItem::DisableField()
+{
+ m_xFrame->set_sensitive(false);
+}
+
+void ScSortKeyItem::EnableField()
+{
+ m_xFrame->set_sensitive(true);
+}
+
+ScSortKeyWindow::ScSortKeyWindow(weld::Container* pBox)
+ : m_pBox(pBox)
+{
+}
+
+ScSortKeyWindow::~ScSortKeyWindow()
+{
+}
+
+void ScSortKeyWindow::AddSortKey( sal_uInt16 nItemNumber )
+{
+ ScSortKeyItem* pSortKeyItem = new ScSortKeyItem(m_pBox);
+
+ // Set Sort key number
+ OUString aLine = pSortKeyItem->m_xFrame->get_label() +
+ OUString::number( nItemNumber );
+ pSortKeyItem->m_xFrame->set_label(aLine);
+
+ // for ui-testing. Distinguish the sort keys
+ if ( m_aSortKeyItems.size() > 0 )
+ {
+ pSortKeyItem->m_xLbSort->set_buildable_name(
+ pSortKeyItem->m_xLbSort->get_buildable_name() + OString::number(m_aSortKeyItems.size() + 1));
+ }
+
+ m_aSortKeyItems.push_back(std::unique_ptr<ScSortKeyItem>(pSortKeyItem));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/subtdlg.cxx b/sc/source/ui/dbgui/subtdlg.cxx
new file mode 100644
index 000000000..924716a6f
--- /dev/null
+++ b/sc/source/ui/dbgui/subtdlg.cxx
@@ -0,0 +1,46 @@
+/* -*- 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 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <tpsubt.hxx>
+#include <subtdlg.hxx>
+#include <scui_def.hxx>
+
+ScSubTotalDlg::ScSubTotalDlg(weld::Window* pParent, const SfxItemSet& rArgSet)
+ : SfxTabDialogController(pParent, "modules/scalc/ui/subtotaldialog.ui", "SubTotalDialog", &rArgSet)
+ , m_xBtnRemove(m_xBuilder->weld_button("remove"))
+{
+ AddTabPage("1stgroup", ScTpSubTotalGroup1::Create, nullptr);
+ AddTabPage("2ndgroup", ScTpSubTotalGroup2::Create, nullptr);
+ AddTabPage("3rdgroup", ScTpSubTotalGroup3::Create, nullptr);
+ AddTabPage("options", ScTpSubTotalOptions::Create, nullptr);
+ m_xBtnRemove->connect_clicked( LINK( this, ScSubTotalDlg, RemoveHdl ) );
+}
+
+ScSubTotalDlg::~ScSubTotalDlg()
+{
+}
+
+IMPL_LINK_NOARG(ScSubTotalDlg, RemoveHdl, weld::Button&, void)
+{
+ m_xDialog->response(SCRET_REMOVE);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/textimportoptions.cxx b/sc/source/ui/dbgui/textimportoptions.cxx
new file mode 100644
index 000000000..99ddc425d
--- /dev/null
+++ b/sc/source/ui/dbgui/textimportoptions.cxx
@@ -0,0 +1,96 @@
+/* -*- 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 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <textimportoptions.hxx>
+#include <svx/langbox.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <i18nlangtag/languagetag.hxx>
+
+ScTextImportOptionsDlg::ScTextImportOptionsDlg(weld::Window* pParent)
+ : GenericDialogController(pParent, "modules/scalc/ui/textimportoptions.ui", "TextImportOptionsDialog")
+ , m_xBtnOk(m_xBuilder->weld_button("ok"))
+ , m_xRbAutomatic(m_xBuilder->weld_radio_button("automatic"))
+ , m_xRbCustom(m_xBuilder->weld_radio_button("custom"))
+ , m_xBtnConvertDate(m_xBuilder->weld_check_button("convertdata"))
+ , m_xBtnKeepAsking(m_xBuilder->weld_check_button("keepasking"))
+ , m_xLbCustomLang(new SvxLanguageBox(m_xBuilder->weld_combo_box("lang")))
+{
+ init();
+}
+
+ScTextImportOptionsDlg::~ScTextImportOptionsDlg()
+{
+}
+
+LanguageType ScTextImportOptionsDlg::getLanguageType() const
+{
+ if (m_xRbAutomatic->get_active())
+ return LANGUAGE_SYSTEM;
+
+ return m_xLbCustomLang->get_active_id();
+}
+
+bool ScTextImportOptionsDlg::isDateConversionSet() const
+{
+ return m_xBtnConvertDate->get_active();
+}
+
+bool ScTextImportOptionsDlg::isKeepAskingSet() const
+{
+ return m_xBtnKeepAsking->get_active();
+}
+
+void ScTextImportOptionsDlg::init()
+{
+ m_xBtnOk->connect_clicked(LINK(this, ScTextImportOptionsDlg, OKHdl));
+ Link<weld::Toggleable&,void> aLink = LINK(this, ScTextImportOptionsDlg, RadioHdl);
+ m_xRbAutomatic->connect_toggled(aLink);
+ m_xRbCustom->connect_toggled(aLink);
+
+ m_xRbAutomatic->set_active(true);
+
+ m_xLbCustomLang->SetLanguageList(
+ SvxLanguageListFlags::ALL | SvxLanguageListFlags::ONLY_KNOWN, false);
+
+ LanguageType eLang = Application::GetSettings().GetLanguageTag().getLanguageType();
+ m_xLbCustomLang->set_active_id(eLang);
+ m_xLbCustomLang->set_sensitive(false);
+}
+
+IMPL_LINK_NOARG(ScTextImportOptionsDlg, OKHdl, weld::Button&, void)
+{
+ m_xDialog->response(RET_OK);
+}
+
+IMPL_LINK(ScTextImportOptionsDlg, RadioHdl, weld::Toggleable&, rBtn, void)
+{
+ if (&rBtn == m_xRbAutomatic.get())
+ {
+ m_xLbCustomLang->set_sensitive(false);
+ }
+ else if (&rBtn == m_xRbCustom.get())
+ {
+ m_xLbCustomLang->set_sensitive(true);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/tpsort.cxx b/sc/source/ui/dbgui/tpsort.cxx
new file mode 100644
index 000000000..35a24e633
--- /dev/null
+++ b/sc/source/ui/dbgui/tpsort.cxx
@@ -0,0 +1,855 @@
+/* -*- 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 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <svtools/collatorres.hxx>
+#include <unotools/collatorwrapper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <osl/diagnose.h>
+
+#include <scitems.hxx>
+#include <uiitems.hxx>
+#include <viewdata.hxx>
+#include <document.hxx>
+#include <global.hxx>
+#include <dbdata.hxx>
+#include <userlist.hxx>
+#include <rangeutl.hxx>
+#include <scresid.hxx>
+#include <sc.hrc>
+#include <strings.hrc>
+#include <globstr.hrc>
+
+#include <sortkeydlg.hxx>
+
+#include <sortdlg.hxx>
+
+#include <tpsort.hxx>
+
+using namespace com::sun::star;
+
+/*
+ * Since the settings on the second Tab Page (Options) effects
+ * the first Tab Page, there must be a way for it to communicate with the
+ * other Page.
+ *
+ * At the moment this problem is solved through using two data members of the
+ * Tab Pages. If a page is enabled / disabled, it compares this data member
+ * with its own state (-> Activate() / Deactivate()).
+ *
+ * In the meantime the class SfxTabPage offers the following method:
+ *
+ * virtual sal_Bool HasExchangeSupport() const; -> return sal_True;
+ * virtual void ActivatePage(const SfxItemSet &);
+ * virtual int DeactivatePage(SfxItemSet * = 0);
+ *
+ * This still needs to be changed!
+ */
+
+// Sort Criteria Tab page
+
+ScTabPageSortFields::ScTabPageSortFields(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rArgSet)
+ : SfxTabPage(pPage, pController, "modules/scalc/ui/sortcriteriapage.ui", "SortCriteriaPage", &rArgSet)
+ ,
+
+ m_aIdle("ScTabPageSortFields Scroll To End Idle"),
+ aStrUndefined ( ScResId( SCSTR_UNDEFINED ) ),
+ aStrColumn ( ScResId( SCSTR_COLUMN ) ),
+ aStrRow ( ScResId( SCSTR_ROW ) ),
+ aStrRowLabel ( ScResId( SCSTR_ROW_LABEL ) ),
+ aStrColLabel ( ScResId( SCSTR_COL_LABEL ) ),
+
+ nWhichSort ( rArgSet.GetPool()->GetWhich( SID_SORT ) ),
+ pViewData ( nullptr ),
+ aSortData ( rArgSet.Get( nWhichSort ).GetSortData() ),
+ nFieldCount ( 0 ),
+ // show actual size of the sorting keys without limiting them to the default size
+ nSortKeyCount(std::max(aSortData.GetSortKeyCount(), static_cast<sal_uInt16>(DEFSORT)))
+
+ , m_xTop(m_xBuilder->weld_container("TopWindow"))
+ , m_xBtnHeader(m_xBuilder->weld_check_button("cbHeader"))
+ , m_xBtnTopDown(m_xBuilder->weld_radio_button("rbTopDown"))
+ , m_xBtnLeftRight(m_xBuilder->weld_radio_button("rbLeftRight"))
+ , m_xScrolledWindow(m_xBuilder->weld_scrolled_window("SortCriteriaPage"))
+ , m_xBox(m_xBuilder->weld_container("SortKeyWindow"))
+ , m_aSortWin(m_xBox.get())
+{
+ // tdf#147722 set some nominal small default height so the height adapts
+ // to all the other contents and the natural height of this widget isn't
+ // an input into the overall size
+ m_xScrolledWindow->set_size_request(-1, 42);
+
+ Init();
+
+ m_aIdle.SetInvokeHandler(LINK(this, ScTabPageSortFields, ScrollToEndHdl));
+
+ SetExchangeSupport();
+}
+
+ScTabPageSortFields::~ScTabPageSortFields()
+{
+ m_aSortWin.m_aSortKeyItems.clear();
+ m_xBox.reset();
+ m_xScrolledWindow.reset();
+}
+
+void ScTabPageSortFields::Init()
+{
+ // Check whether the field that is passed on is a database field:
+ ScDocument* pDoc = pViewData ? &pViewData->GetDocument() : nullptr;
+ if ( pDoc )
+ {
+ ScDBCollection* pDBColl = pDoc->GetDBCollection();
+ const SCTAB nCurTab = pViewData->GetTabNo();
+ if ( pDBColl )
+ {
+ ScDBData* pDBData
+ = pDBColl->GetDBAtArea( nCurTab,
+ aSortData.nCol1, aSortData.nRow1,
+ aSortData.nCol2, aSortData.nRow2 );
+ if ( pDBData )
+ {
+ m_xBtnHeader->set_active(pDBData->HasHeader());
+ }
+ }
+ }
+ m_xBtnHeader->set_label(aStrColLabel);
+
+ Link<weld::Toggleable&,void> aLink = LINK(this, ScTabPageSortFields, SortDirHdl );
+ m_xBtnTopDown->connect_toggled( aLink );
+ m_xBtnLeftRight->connect_toggled( aLink );
+ m_xBtnHeader->connect_toggled( aLink );
+
+ const ScSortItem& rSortItem = GetItemSet().Get( nWhichSort );
+
+ pViewData = rSortItem.GetViewData();
+ OSL_ENSURE( pViewData, "ViewData not found!" );
+
+ nFieldArr.push_back( 0 );
+
+ // Create three sort key dialogs by default
+ for ( sal_uInt16 i=0; i<nSortKeyCount; i++ )
+ {
+ AddSortKey(i+1);
+ m_aSortWin.m_aSortKeyItems[i]->m_xLbSort->connect_changed(LINK(this, ScTabPageSortFields, SelectHdl));
+ }
+}
+
+std::unique_ptr<SfxTabPage> ScTabPageSortFields::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* pArgSet)
+{
+ return std::make_unique<ScTabPageSortFields>(pPage, pController, *pArgSet);
+}
+
+void ScTabPageSortFields::Reset( const SfxItemSet* /* rArgSet */ )
+{
+ m_xBtnHeader->set_active( aSortData.bHasHeader );
+ m_xBtnTopDown->set_active( aSortData.bByRow );
+ m_xBtnLeftRight->set_active( !aSortData.bByRow );
+
+ if (m_aSortWin.m_aSortKeyItems[0]->m_xLbSort->get_count() == 0)
+ FillFieldLists(0);
+
+ // ListBox selection:
+ if (!aSortData.maKeyState.empty() && aSortData.maKeyState[0].bDoSort)
+ {
+ // Make sure that the all sort keys are reset
+ for ( sal_uInt16 i=nSortKeyCount; i<aSortData.GetSortKeyCount(); i++ )
+ {
+ AddSortKey(i+1);
+ m_aSortWin.m_aSortKeyItems[i]->m_xLbSort->connect_changed( LINK( this,
+ ScTabPageSortFields, SelectHdl ) );
+ }
+ nSortKeyCount = aSortData.GetSortKeyCount();
+ FillFieldLists(0);
+
+ for ( sal_uInt16 i=0; i<nSortKeyCount; i++ )
+ {
+ if (aSortData.maKeyState[i].bDoSort )
+ {
+ m_aSortWin.m_aSortKeyItems[i]->m_xLbSort->set_active( GetFieldSelPos(
+ aSortData.maKeyState[i].nField ) );
+ (aSortData.maKeyState[i].bAscending)
+ ? m_aSortWin.m_aSortKeyItems[i]->m_xBtnUp->set_active(true)
+ : m_aSortWin.m_aSortKeyItems[i]->m_xBtnDown->set_active(true);
+ }
+ else
+ {
+ m_aSortWin.m_aSortKeyItems[i]->m_xLbSort->set_active(0); // Select none
+ m_aSortWin.m_aSortKeyItems[i]->m_xBtnUp->set_active(true);
+ }
+ }
+
+ // Enable or disable field depending on preceding Listbox selection
+ m_aSortWin.m_aSortKeyItems[0]->EnableField();
+ for ( sal_uInt16 i=1; i<nSortKeyCount; i++ )
+ if ( m_aSortWin.m_aSortKeyItems[i - 1]->m_xLbSort->get_active() == 0 )
+ m_aSortWin.m_aSortKeyItems[i]->DisableField();
+ else
+ m_aSortWin.m_aSortKeyItems[i]->EnableField();
+ }
+ else
+ {
+ SCCOL nCol = pViewData->GetCurX();
+
+ if( nCol < aSortData.nCol1 )
+ nCol = aSortData.nCol1;
+ else if( nCol > aSortData.nCol2 )
+ nCol = aSortData.nCol2;
+
+ sal_uInt16 nSort1Pos = nCol - aSortData.nCol1+1;
+
+ m_aSortWin.m_aSortKeyItems[0]->m_xLbSort->set_active(nSort1Pos);
+ for ( sal_uInt16 i=1; i<nSortKeyCount; i++ )
+ m_aSortWin.m_aSortKeyItems[i]->m_xLbSort->set_active(0);
+
+ for ( sal_uInt16 i=0; i<nSortKeyCount; i++ )
+ m_aSortWin.m_aSortKeyItems[i]->m_xBtnUp->set_active(true);
+
+ m_aSortWin.m_aSortKeyItems[0]->EnableField();
+ m_aSortWin.m_aSortKeyItems[1]->EnableField();
+ for ( sal_uInt16 i=2; i<nSortKeyCount; i++ )
+ m_aSortWin.m_aSortKeyItems[i]->DisableField();
+ }
+
+ // Make sure that there is always a last undefined sort key
+ if (m_aSortWin.m_aSortKeyItems[nSortKeyCount - 1]->m_xLbSort->get_active() > 0)
+ SetLastSortKey( nSortKeyCount );
+}
+
+bool ScTabPageSortFields::FillItemSet( SfxItemSet* rArgSet )
+{
+ ScSortParam aNewSortData = aSortData;
+
+ const SfxItemSet* pExample = GetDialogExampleSet();
+ if (pExample)
+ {
+ if (const ScSortItem* pItem = pExample->GetItemIfSet(nWhichSort))
+ {
+ ScSortParam aTempData = pItem->GetSortData();
+ aTempData.maKeyState = aNewSortData.maKeyState;
+ aNewSortData = aTempData;
+ }
+ }
+ aNewSortData.bByRow = m_xBtnTopDown->get_active();
+ aNewSortData.bHasHeader = m_xBtnHeader->get_active();
+
+ std::vector<sal_Int32> nSortPos;
+
+ for ( sal_uInt16 i=0; i<nSortKeyCount; i++ )
+ {
+ nSortPos.push_back(m_aSortWin.m_aSortKeyItems[i]->m_xLbSort->get_active());
+ if (nSortPos[i] == -1) nSortPos[i] = 0;
+ }
+
+ if( nSortKeyCount >= aNewSortData.GetSortKeyCount() )
+ aNewSortData.maKeyState.resize(nSortKeyCount);
+
+ if ( nSortPos[0] > 0 )
+ {
+ for ( sal_uInt16 i=0; i<nSortKeyCount; i++ )
+ {
+ aNewSortData.maKeyState[i].bDoSort = (nSortPos[i] > 0);
+ aNewSortData.maKeyState[i].nField = nFieldArr[nSortPos[i]];
+ aNewSortData.maKeyState[i].bAscending = m_aSortWin.m_aSortKeyItems[i]->m_xBtnUp->get_active();
+ }
+ }
+ else
+ {
+ for ( sal_uInt16 i=0; i<nSortKeyCount; i++ )
+ aNewSortData.maKeyState[i].bDoSort = false;
+ }
+
+ rArgSet->Put( ScSortItem( SCITEM_SORTDATA, nullptr, &aNewSortData ) );
+
+ return true;
+}
+
+// for data exchange without dialogue detour:
+void ScTabPageSortFields::ActivatePage( const SfxItemSet& rSet )
+{
+ // Refresh local copy with shared data
+ aSortData = rSet.Get( SCITEM_SORTDATA ).GetSortData();
+
+ m_xBtnHeader->set_active( aSortData.bHasHeader );
+ m_xBtnTopDown->set_active( aSortData.bByRow );
+ m_xBtnLeftRight->set_active( !aSortData.bByRow );
+}
+
+DeactivateRC ScTabPageSortFields::DeactivatePage( SfxItemSet* pSetP )
+{
+ if ( pSetP )
+ FillItemSet( pSetP );
+
+ return DeactivateRC::LeavePage;
+}
+
+void ScTabPageSortFields::FillFieldLists( sal_uInt16 nStartField )
+{
+ if ( !pViewData )
+ return;
+
+ ScDocument& rDoc = pViewData->GetDocument();
+
+ for (sal_uInt16 j = nStartField; j < nSortKeyCount; ++j)
+ {
+ m_aSortWin.m_aSortKeyItems[j]->m_xLabel->set_label(aSortData.bByRow ? aStrColumn : aStrRow);
+ m_aSortWin.m_aSortKeyItems[j]->m_xLbSort->clear();
+ m_aSortWin.m_aSortKeyItems[j]->m_xLbSort->freeze();
+ m_aSortWin.m_aSortKeyItems[j]->m_xLbSort->append_text(aStrUndefined);
+ }
+
+ SCCOL nFirstSortCol = aSortData.nCol1;
+ SCROW nFirstSortRow = aSortData.nRow1;
+ SCTAB nTab = pViewData->GetTabNo();
+ sal_uInt16 i = 1;
+ nFieldArr.clear();
+ nFieldArr.push_back(0);
+
+ if ( aSortData.bByRow )
+ {
+ OUString aFieldName;
+ SCCOL nMaxCol = rDoc.ClampToAllocatedColumns(nTab, aSortData.nCol2);
+ SCCOL col;
+
+ for ( col=nFirstSortCol; col<=nMaxCol && i<SC_MAXFIELDS(rDoc.GetSheetLimits()); col++ )
+ {
+ aFieldName = rDoc.GetString(col, nFirstSortRow, nTab);
+ if ( !aSortData.bHasHeader || aFieldName.isEmpty() )
+ aFieldName = ScColToAlpha( col );
+ nFieldArr.push_back( col );
+
+ for ( sal_uInt16 j=nStartField; j<nSortKeyCount; j++ )
+ m_aSortWin.m_aSortKeyItems[j]->m_xLbSort->insert_text(i, aFieldName);
+
+ i++;
+ }
+ }
+ else
+ {
+ OUString aFieldName;
+ SCROW nMaxRow = aSortData.nRow2;
+ SCROW row;
+
+ for ( row=nFirstSortRow; row<=nMaxRow && i<SC_MAXFIELDS(rDoc.GetSheetLimits()); row++ )
+ {
+ aFieldName = rDoc.GetString(nFirstSortCol, row, nTab);
+ if ( !aSortData.bHasHeader || aFieldName.isEmpty() )
+ aFieldName = OUString::number( row+1);
+ nFieldArr.push_back( row );
+
+ for ( sal_uInt16 j=nStartField; j<nSortKeyCount; j++ )
+ m_aSortWin.m_aSortKeyItems[j]->m_xLbSort->insert_text(i, aFieldName);
+
+ i++;
+ }
+ }
+
+ for (sal_uInt16 j=nStartField; j < nSortKeyCount; ++j)
+ {
+ m_aSortWin.m_aSortKeyItems[j]->m_xLbSort->thaw();
+ }
+
+ nFieldCount = i;
+}
+
+sal_uInt16 ScTabPageSortFields::GetFieldSelPos( SCCOLROW nField )
+{
+ sal_uInt16 nFieldPos = 0;
+ bool bFound = false;
+
+ for ( sal_uInt16 n=1; n<nFieldCount && !bFound; n++ )
+ {
+ if ( nFieldArr[n] == nField )
+ {
+ nFieldPos = n;
+ bFound = true;
+ }
+ }
+
+ return nFieldPos;
+}
+
+void ScTabPageSortFields::SetLastSortKey( sal_uInt16 nItem )
+{
+ // Extend local SortParam copy
+ const ScSortKeyState atempKeyState = { 0, false, true };
+ aSortData.maKeyState.push_back( atempKeyState );
+
+ // Add Sort Key Item
+ ++nSortKeyCount;
+ AddSortKey( nSortKeyCount );
+ m_aSortWin.m_aSortKeyItems[nItem]->m_xLbSort->connect_changed(
+ LINK( this, ScTabPageSortFields, SelectHdl ) );
+
+ FillFieldLists( nItem );
+
+ // Set Status
+ m_aSortWin.m_aSortKeyItems[nItem]->m_xBtnUp->set_active(true);
+ m_aSortWin.m_aSortKeyItems[nItem]->m_xLbSort->set_active(0);
+}
+
+// Handler:
+
+IMPL_LINK_NOARG(ScTabPageSortFields, SortDirHdl, weld::Toggleable&, void)
+{
+ if ( (m_xBtnTopDown->get_active() != aSortData.bByRow) || (m_xBtnHeader->get_active() != aSortData.bHasHeader))
+ {
+ if (m_xBtnTopDown->get_active())
+ m_xBtnHeader->set_label(aStrColLabel);
+ else
+ m_xBtnHeader->set_label(aStrRowLabel);
+
+ aSortData.bByRow = m_xBtnTopDown->get_active();
+ aSortData.bHasHeader = m_xBtnHeader->get_active();
+
+ // remember selection
+ std::vector<sal_uInt16> nCurSel;
+ for ( sal_uInt16 i=0; i<nSortKeyCount; i++ )
+ nCurSel.push_back( m_aSortWin.m_aSortKeyItems[i]->m_xLbSort->get_active() );
+
+ FillFieldLists(0);
+
+ for ( sal_uInt16 i=0; i<nSortKeyCount; i++ )
+ m_aSortWin.m_aSortKeyItems[i]->m_xLbSort->set_active(nCurSel[i]);
+ }
+}
+
+IMPL_LINK( ScTabPageSortFields, SelectHdl, weld::ComboBox&, rLb, void )
+{
+ OUString aSelEntry = rLb.get_active_text();
+ ScSortKeyItems::iterator pIter;
+
+ // If last listbox is enabled add one item
+ if (m_aSortWin.m_aSortKeyItems.back()->m_xLbSort.get() == &rLb)
+ {
+ if ( aSelEntry != aStrUndefined )
+ {
+ SetLastSortKey( nSortKeyCount );
+ return;
+ }
+ }
+
+ // Find selected listbox
+ pIter = std::find_if(m_aSortWin.m_aSortKeyItems.begin(), m_aSortWin.m_aSortKeyItems.end(),
+ [&rLb](const ScSortKeyItems::value_type& rItem) { return rItem->m_xLbSort.get() == &rLb; });
+
+ if (pIter == m_aSortWin.m_aSortKeyItems.end())
+ return;
+
+ // If not selecting the last Listbox, modify the succeeding ones
+ ++pIter;
+ if ( std::distance(m_aSortWin.m_aSortKeyItems.begin(), pIter) >= nSortKeyCount )
+ return;
+
+ if ( aSelEntry == aStrUndefined )
+ {
+ for ( ; pIter != m_aSortWin.m_aSortKeyItems.end(); ++pIter )
+ {
+ (*pIter)->m_xLbSort->set_active(0);
+
+ (*pIter)->DisableField();
+ }
+ }
+ else
+ {
+ (*pIter)->EnableField();
+ }
+}
+
+IMPL_LINK_NOARG(ScTabPageSortFields, ScrollToEndHdl, Timer*, void)
+{
+ m_xScrolledWindow->vadjustment_set_value(m_xScrolledWindow->vadjustment_get_upper());
+}
+
+void ScTabPageSortFields::AddSortKey( sal_uInt16 nItem )
+{
+ m_aSortWin.AddSortKey(nItem);
+ m_aIdle.Start();
+}
+
+// Sort option Tab Page:
+
+ScTabPageSortOptions::ScTabPageSortOptions(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rArgSet)
+ : SfxTabPage(pPage, pController, "modules/scalc/ui/sortoptionspage.ui", "SortOptionsPage", &rArgSet)
+ , aStrUndefined(ScResId(SCSTR_UNDEFINED))
+ , nWhichSort(rArgSet.GetPool()->GetWhich(SID_SORT))
+ , aSortData(rArgSet.Get(nWhichSort).GetSortData())
+ , pViewData(nullptr)
+ , pDoc(nullptr)
+ , m_xBtnCase(m_xBuilder->weld_check_button("case"))
+ , m_xBtnFormats(m_xBuilder->weld_check_button("formats"))
+ , m_xBtnNaturalSort(m_xBuilder->weld_check_button("naturalsort"))
+ , m_xBtnCopyResult(m_xBuilder->weld_check_button("copyresult"))
+ , m_xLbOutPos(m_xBuilder->weld_combo_box("outarealb"))
+ , m_xEdOutPos(m_xBuilder->weld_entry("outareaed"))
+ , m_xBtnSortUser(m_xBuilder->weld_check_button("sortuser"))
+ , m_xLbSortUser(m_xBuilder->weld_combo_box("sortuserlb"))
+ , m_xLbLanguage(new SvxLanguageBox(m_xBuilder->weld_combo_box("language")))
+ , m_xFtAlgorithm(m_xBuilder->weld_label("algorithmft"))
+ , m_xLbAlgorithm(m_xBuilder->weld_combo_box("algorithmlb"))
+ , m_xBtnIncComments(m_xBuilder->weld_check_button("includenotes"))
+ , m_xBtnIncImages(m_xBuilder->weld_check_button("includeimages"))
+{
+ m_xLbSortUser->set_size_request(m_xLbSortUser->get_approximate_digit_width() * 50, -1);
+ m_xLbSortUser->set_accessible_description(ScResId(STR_A11Y_DESC_SORTUSER));
+ Init();
+ SetExchangeSupport();
+}
+
+void ScTabPageSortOptions::Init()
+{
+ // CollatorResource has user-visible names for sort algorithms
+ m_xColRes.reset(new CollatorResource);
+
+ //! use CollatorWrapper from document?
+ m_xColWrap.reset(new CollatorWrapper(comphelper::getProcessComponentContext()));
+
+ const ScSortItem& rSortItem = GetItemSet().Get( nWhichSort );
+
+ m_xLbOutPos->connect_changed( LINK( this, ScTabPageSortOptions, SelOutPosHdl ) );
+ m_xBtnCopyResult->connect_toggled( LINK( this, ScTabPageSortOptions, EnableHdl ) );
+ m_xBtnSortUser->connect_toggled( LINK( this, ScTabPageSortOptions, EnableHdl ) );
+ m_xLbLanguage->connect_changed( LINK( this, ScTabPageSortOptions, FillAlgorHdl ) );
+
+ pViewData = rSortItem.GetViewData();
+ pDoc = pViewData ? &pViewData->GetDocument() : nullptr;
+
+ OSL_ENSURE( pViewData, "ViewData not found! :-/" );
+
+ if ( pViewData && pDoc )
+ {
+ const formula::FormulaGrammar::AddressConvention eConv = pDoc->GetAddressConvention();
+
+ m_xLbOutPos->clear();
+ m_xLbOutPos->append_text(aStrUndefined);
+ m_xLbOutPos->set_sensitive(false);
+
+ ScAreaNameIterator aIter( *pDoc );
+ OUString aName;
+ ScRange aRange;
+ while ( aIter.Next( aName, aRange ) )
+ {
+ OUString aRefStr(aRange.aStart.Format(ScRefFlags::ADDR_ABS_3D, pDoc, eConv));
+ m_xLbOutPos->append(aRefStr, aName);
+ }
+
+ m_xLbOutPos->set_active(0);
+ m_xEdOutPos->set_text(OUString());
+ }
+
+ FillUserSortListBox();
+
+ // get available languages
+
+ m_xLbLanguage->SetLanguageList( SvxLanguageListFlags::ALL | SvxLanguageListFlags::ONLY_KNOWN, false );
+ m_xLbLanguage->InsertLanguage( LANGUAGE_SYSTEM );
+}
+
+std::unique_ptr<SfxTabPage> ScTabPageSortOptions::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rArgSet)
+{
+ return std::make_unique<ScTabPageSortOptions>(pPage, pController, *rArgSet);
+}
+
+void ScTabPageSortOptions::Reset( const SfxItemSet* /* rArgSet */ )
+{
+ if ( aSortData.bUserDef )
+ {
+ m_xBtnSortUser->set_active(true);
+ m_xLbSortUser->set_sensitive(true);
+ m_xLbSortUser->set_active(aSortData.nUserIndex);
+ }
+ else
+ {
+ m_xBtnSortUser->set_active(false);
+ m_xLbSortUser->set_sensitive(false);
+ m_xLbSortUser->set_active(0);
+ }
+
+ m_xBtnCase->set_active( aSortData.bCaseSens );
+ m_xBtnFormats->set_active( aSortData.aDataAreaExtras.mbCellFormats );
+ m_xBtnNaturalSort->set_active( aSortData.bNaturalSort );
+ m_xBtnIncComments->set_active( aSortData.aDataAreaExtras.mbCellNotes );
+ m_xBtnIncImages->set_active( aSortData.aDataAreaExtras.mbCellDrawObjects );
+
+ LanguageType eLang = LanguageTag::convertToLanguageType( aSortData.aCollatorLocale, false);
+ if ( eLang == LANGUAGE_DONTKNOW )
+ eLang = LANGUAGE_SYSTEM;
+ m_xLbLanguage->set_active_id(eLang);
+ FillAlgor(); // get algorithms, select default
+ if ( !aSortData.aCollatorAlgorithm.isEmpty() )
+ m_xLbAlgorithm->set_active_text(m_xColRes->GetTranslation(aSortData.aCollatorAlgorithm));
+
+ if ( pDoc && !aSortData.bInplace )
+ {
+ ScRefFlags nFormat = (aSortData.nDestTab != pViewData->GetTabNo())
+ ? ScRefFlags::RANGE_ABS_3D
+ : ScRefFlags::RANGE_ABS;
+
+ theOutPos.Set( aSortData.nDestCol,
+ aSortData.nDestRow,
+ aSortData.nDestTab );
+
+ OUString aStr(theOutPos.Format(nFormat, pDoc, pDoc->GetAddressConvention()));
+ m_xBtnCopyResult->set_active(true);
+ m_xLbOutPos->set_sensitive(true);
+ m_xEdOutPos->set_sensitive(true);
+ m_xEdOutPos->set_text( aStr );
+ EdOutPosModHdl();
+ m_xEdOutPos->grab_focus();
+ m_xEdOutPos->select_region(0, -1);
+ }
+ else
+ {
+ m_xBtnCopyResult->set_active( false );
+ m_xLbOutPos->set_sensitive(false);
+ m_xEdOutPos->set_sensitive(false);
+ m_xEdOutPos->set_text( OUString() );
+ }
+}
+
+bool ScTabPageSortOptions::FillItemSet( SfxItemSet* rArgSet )
+{
+ // Create local copy of ScParam
+ ScSortParam aNewSortData = aSortData;
+
+ const SfxItemSet* pExample = GetDialogExampleSet();
+ if (pExample)
+ {
+ if (const ScSortItem* pSortItem = pExample->GetItemIfSet(nWhichSort))
+ aNewSortData = pSortItem->GetSortData();
+ }
+ aNewSortData.bCaseSens = m_xBtnCase->get_active();
+ aNewSortData.bNaturalSort = m_xBtnNaturalSort->get_active();
+ aNewSortData.aDataAreaExtras.mbCellNotes = m_xBtnIncComments->get_active();
+ aNewSortData.aDataAreaExtras.mbCellDrawObjects = m_xBtnIncImages->get_active();
+ aNewSortData.aDataAreaExtras.mbCellFormats = m_xBtnFormats->get_active();
+ aNewSortData.bInplace = !m_xBtnCopyResult->get_active();
+ aNewSortData.nDestCol = theOutPos.Col();
+ aNewSortData.nDestRow = theOutPos.Row();
+ aNewSortData.nDestTab = theOutPos.Tab();
+ aNewSortData.bUserDef = m_xBtnSortUser->get_active();
+ aNewSortData.nUserIndex = (m_xBtnSortUser->get_active())
+ ? m_xLbSortUser->get_active()
+ : 0;
+
+ // get locale
+ LanguageType eLang = m_xLbLanguage->get_active_id();
+ aNewSortData.aCollatorLocale = LanguageTag::convertToLocale( eLang, false);
+
+ // get algorithm
+ OUString sAlg;
+ if ( eLang != LANGUAGE_SYSTEM )
+ {
+ uno::Sequence<OUString> aAlgos = m_xColWrap->listCollatorAlgorithms(
+ aNewSortData.aCollatorLocale );
+ const int nSel = m_xLbAlgorithm->get_active();
+ if ( nSel < aAlgos.getLength() )
+ sAlg = aAlgos[nSel];
+ }
+ aNewSortData.aCollatorAlgorithm = sAlg;
+
+ rArgSet->Put( ScSortItem( SCITEM_SORTDATA, &aNewSortData ) );
+
+ return true;
+}
+
+// for data exchange without dialogue detour:
+void ScTabPageSortOptions::ActivatePage( const SfxItemSet& rSet )
+{
+ // Refresh local copy with shared data
+ aSortData = rSet.Get( SCITEM_SORTDATA ).GetSortData();
+ ScSortDlg* pDlg = static_cast<ScSortDlg*>(GetDialogController());
+ if (!pDlg)
+ return;
+}
+
+DeactivateRC ScTabPageSortOptions::DeactivatePage( SfxItemSet* pSetP )
+{
+ bool bPosInputOk = true;
+
+ if ( m_xBtnCopyResult->get_active() )
+ {
+ OUString thePosStr = m_xEdOutPos->get_text();
+ ScAddress thePos;
+ sal_Int32 nColonPos = thePosStr.indexOf( ':' );
+
+ if ( -1 != nColonPos )
+ thePosStr = thePosStr.copy( 0, nColonPos );
+
+ if ( pViewData )
+ {
+ // visible table is default for input without table
+ // must be changed to GetRefTabNo when sorting has RefInput!
+ thePos.SetTab( pViewData->GetTabNo() );
+ }
+
+ ScRefFlags nResult = thePos.Parse( thePosStr, *pDoc, pDoc->GetAddressConvention() );
+
+ bPosInputOk = (nResult & ScRefFlags::VALID) == ScRefFlags::VALID;
+
+ if ( !bPosInputOk )
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetFrameWeld(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ ScResId(STR_INVALID_TABREF)));
+ xBox->run();
+ m_xEdOutPos->grab_focus();
+ m_xEdOutPos->select_region(0, -1);
+ theOutPos.Set(0,0,0);
+ }
+ else
+ {
+ m_xEdOutPos->set_text(thePosStr);
+ theOutPos = thePos;
+ }
+ }
+
+ if ( pSetP && bPosInputOk )
+ FillItemSet( pSetP );
+
+ return bPosInputOk ? DeactivateRC::LeavePage : DeactivateRC::KeepPage;
+}
+
+void ScTabPageSortOptions::FillUserSortListBox()
+{
+ ScUserList* pUserLists = ScGlobal::GetUserList();
+
+ m_xLbSortUser->clear();
+ if ( pUserLists )
+ {
+ size_t nCount = pUserLists->size();
+ for (size_t i=0; i<nCount; ++i)
+ m_xLbSortUser->append_text((*pUserLists)[i].GetString());
+ }
+}
+
+// Handler:
+
+IMPL_LINK( ScTabPageSortOptions, EnableHdl, weld::Toggleable&, rButton, void )
+{
+ if (&rButton == m_xBtnCopyResult.get())
+ {
+ if (rButton.get_active())
+ {
+ m_xLbOutPos->set_sensitive(true);
+ m_xEdOutPos->set_sensitive(true);
+ m_xEdOutPos->grab_focus();
+ }
+ else
+ {
+ m_xLbOutPos->set_sensitive(false);
+ m_xEdOutPos->set_sensitive(false);
+ }
+ }
+ else if (&rButton == m_xBtnSortUser.get())
+ {
+ if (rButton.get_active())
+ {
+ m_xLbSortUser->set_sensitive(true);
+ m_xLbSortUser->grab_focus();
+ }
+ else
+ m_xLbSortUser->set_sensitive(false);
+ }
+}
+
+IMPL_LINK(ScTabPageSortOptions, SelOutPosHdl, weld::ComboBox&, rLb, void)
+{
+ if (&rLb == m_xLbOutPos.get())
+ {
+ OUString aString;
+ const int nSelPos = m_xLbOutPos->get_active();
+
+ if (nSelPos > 0)
+ aString = m_xLbOutPos->get_id(nSelPos);
+
+ m_xEdOutPos->set_text(aString);
+ }
+}
+
+void ScTabPageSortOptions::EdOutPosModHdl()
+{
+ OUString theCurPosStr = m_xEdOutPos->get_text();
+ ScRefFlags nResult = ScAddress().Parse( theCurPosStr, *pDoc, pDoc->GetAddressConvention() );
+
+ if ( (nResult & ScRefFlags::VALID) != ScRefFlags::VALID )
+ return;
+
+ bool bFound = false;
+ sal_Int32 i = 0;
+ const int nCount = m_xLbOutPos->get_count();
+
+ for ( i=2; i<nCount && !bFound; i++ )
+ {
+ OUString aStr = m_xLbOutPos->get_id(i);
+ bFound = (theCurPosStr == aStr);
+ }
+
+ if ( bFound )
+ m_xLbOutPos->set_active(--i);
+ else
+ m_xLbOutPos->set_active(0);
+}
+
+void ScTabPageSortOptions::FillAlgor()
+{
+ tools::Long nCount = 0;
+
+ m_xLbAlgorithm->freeze();
+ m_xLbAlgorithm->clear();
+
+ LanguageType eLang = m_xLbLanguage->get_active_id();
+ if ( eLang == LANGUAGE_SYSTEM )
+ {
+ // for LANGUAGE_SYSTEM no algorithm can be selected because
+ // it wouldn't necessarily exist for other languages
+ // -> leave list box empty if LANGUAGE_SYSTEM is selected
+ m_xFtAlgorithm->set_sensitive( false ); // nothing to select
+ m_xLbAlgorithm->set_sensitive( false ); // nothing to select
+ }
+ else
+ {
+ lang::Locale aLocale( LanguageTag::convertToLocale( eLang ));
+ const uno::Sequence<OUString> aAlgos = m_xColWrap->listCollatorAlgorithms( aLocale );
+
+ nCount = aAlgos.getLength();
+ for (const OUString& sAlg : aAlgos)
+ {
+ OUString sUser = m_xColRes->GetTranslation( sAlg );
+ m_xLbAlgorithm->append_text(sUser);
+ }
+ }
+
+ m_xLbAlgorithm->thaw();
+
+ m_xLbAlgorithm->set_active(nCount ? 0 : -1); // first entry is default
+ m_xFtAlgorithm->set_sensitive(nCount > 1); // enable only if there is a choice
+ m_xLbAlgorithm->set_sensitive(nCount > 1); // enable only if there is a choice
+}
+
+IMPL_LINK_NOARG(ScTabPageSortOptions, FillAlgorHdl, weld::ComboBox&, void)
+{
+ FillAlgor();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/tpsubt.cxx b/sc/source/ui/dbgui/tpsubt.cxx
new file mode 100644
index 000000000..01431c815
--- /dev/null
+++ b/sc/source/ui/dbgui/tpsubt.cxx
@@ -0,0 +1,621 @@
+/* -*- 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 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <scitems.hxx>
+#include <uiitems.hxx>
+#include <global.hxx>
+#include <userlist.hxx>
+#include <viewdata.hxx>
+#include <document.hxx>
+#include <scresid.hxx>
+#include <sc.hrc>
+#include <strings.hrc>
+#include <subtotals.hrc>
+
+#include <tpsubt.hxx>
+#include <tpsort.hxx>
+#include <memory>
+
+#include <osl/diagnose.h>
+
+// Subtotals group tabpage:
+
+ScTpSubTotalGroup::ScTpSubTotalGroup(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rArgSet, const sal_uInt16& rTabNumber)
+ : SfxTabPage(pPage, pController, "modules/scalc/ui/subtotalgrppage.ui", "SubTotalGrpPage", &rArgSet)
+ , aStrNone(ScResId(SCSTR_NONE))
+ , aStrColumn(ScResId(SCSTR_COLUMN_LETTER))
+ , pViewData(nullptr)
+ , pDoc(nullptr)
+ , nWhichSubTotals(rArgSet.GetPool()->GetWhich(SID_SUBTOTALS))
+ , rSubTotalData(rArgSet.Get(nWhichSubTotals).GetSubTotalData())
+ , nFieldCount(0)
+ , mxLbGroup(m_xBuilder->weld_combo_box("group_by"))
+ , mxLbColumns(m_xBuilder->weld_tree_view("columns"))
+ , mxLbFunctions(m_xBuilder->weld_tree_view("functions"))
+ , mxLbSelectAllColumns(m_xBuilder->weld_check_button("select_all_columns_button"))
+{
+ for (size_t i = 0; i < SAL_N_ELEMENTS(SCSTR_SUBTOTALS); ++i)
+ mxLbFunctions->append_text(ScResId(SCSTR_SUBTOTALS[i]));
+
+ auto nHeight = mxLbColumns->get_height_rows(14);
+ mxLbColumns->set_size_request(-1, nHeight);
+ mxLbFunctions->set_size_request(-1, nHeight);
+
+ mxLbColumns->enable_toggle_buttons(weld::ColumnToggleType::Check);
+
+ Init();
+
+ // UI tests
+ mxLbGroup->set_buildable_name(mxLbGroup->get_buildable_name() + OString::number(rTabNumber));
+ mxLbColumns->set_buildable_name(mxLbColumns->get_buildable_name() + OString::number(rTabNumber));
+}
+
+ScTpSubTotalGroup::~ScTpSubTotalGroup()
+{
+}
+
+void ScTpSubTotalGroup::Init()
+{
+ const ScSubTotalItem& rSubTotalItem = GetItemSet().Get( nWhichSubTotals );
+
+ pViewData = rSubTotalItem.GetViewData();
+ assert(pViewData && "CreateScSubTotalDlg aArgSet must contain a ScSubTotalItem with ViewData set");
+ pDoc = &pViewData->GetDocument();
+ assert(pDoc && "Document not found :-(");
+
+ mxLbGroup->connect_changed( LINK( this, ScTpSubTotalGroup, SelectListBoxHdl ) );
+ mxLbColumns->connect_changed( LINK( this, ScTpSubTotalGroup, SelectTreeListBoxHdl ) );
+ mxLbColumns->connect_toggled( LINK( this, ScTpSubTotalGroup, CheckHdl ) );
+ mxLbFunctions->connect_changed( LINK( this, ScTpSubTotalGroup, SelectTreeListBoxHdl) );
+ mxLbSelectAllColumns->connect_toggled( LINK( this, ScTpSubTotalGroup, CheckBoxHdl ) );
+
+ mnFieldArr.resize(SC_MAXFIELDS(pDoc->GetSheetLimits()));
+ mnFieldArr[0] = 0;
+ FillListBoxes();
+}
+
+namespace
+{
+ int GetCheckedEntryCount(weld::TreeView& rTreeView)
+ {
+ int nRet = 0;
+
+ rTreeView.all_foreach([&](const weld::TreeIter& rEntry) {
+ if ( rTreeView.get_toggle(rEntry) == TRISTATE_TRUE )
+ ++nRet;
+ return false;
+ });
+
+ return nRet;
+ }
+}
+
+bool ScTpSubTotalGroup::DoReset( sal_uInt16 nGroupNo,
+ const SfxItemSet& rArgSet )
+{
+ sal_uInt16 nGroupIdx = 0;
+
+ OSL_ENSURE( (nGroupNo<=3) && (nGroupNo>0), "Invalid group" );
+
+ if ( (nGroupNo > 3) || (nGroupNo == 0) )
+ return false;
+ else
+ nGroupIdx = nGroupNo-1;
+
+ // first we have to clear the listboxes...
+ for (int nLbEntry = 0, nCount = mxLbColumns->n_children(); nLbEntry < nCount; ++nLbEntry)
+ {
+ mxLbColumns->set_toggle(nLbEntry, TRISTATE_FALSE);
+ mxLbColumns->set_id(nLbEntry, "0");
+ }
+ mxLbFunctions->select(0);
+
+ const ScSubTotalParam & theSubTotalData( rArgSet.Get( nWhichSubTotals ).GetSubTotalData() );
+
+ if ( theSubTotalData.bGroupActive[nGroupIdx] )
+ {
+ SCCOL nField = theSubTotalData.nField[nGroupIdx];
+ SCCOL nSubTotals = theSubTotalData.nSubTotals[nGroupIdx];
+ SCCOL* pSubTotals = theSubTotalData.pSubTotals[nGroupIdx].get();
+ ScSubTotalFunc* pFunctions = theSubTotalData.pFunctions[nGroupIdx].get();
+
+ mxLbGroup->set_active( GetFieldSelPos( nField )+1 );
+
+ sal_uInt16 nFirstChecked = 0;
+ for ( sal_uInt16 i=0; i<nSubTotals; i++ )
+ {
+ sal_uInt16 nCheckPos = GetFieldSelPos( pSubTotals[i] );
+
+ mxLbColumns->set_toggle(nCheckPos, TRISTATE_TRUE);
+ mxLbColumns->set_id(nCheckPos, OUString::number(FuncToLbPos(pFunctions[i])));
+
+ if (i == 0 || nCheckPos < nFirstChecked)
+ nFirstChecked = nCheckPos;
+ }
+ // Select the first checked field from the top.
+ mxLbColumns->select(nFirstChecked);
+ }
+ else
+ {
+ mxLbGroup->set_active( (nGroupNo == 1) ? 1 : 0 );
+ mxLbColumns->select( 0 );
+ mxLbFunctions->select( 0 );
+ }
+
+ if ( mxLbColumns->n_children() == GetCheckedEntryCount(*mxLbColumns) )
+ mxLbSelectAllColumns->set_active( true );
+ else
+ mxLbSelectAllColumns->set_active( false );
+
+ return true;
+}
+
+bool ScTpSubTotalGroup::DoFillItemSet( sal_uInt16 nGroupNo,
+ SfxItemSet& rArgSet )
+{
+ sal_uInt16 nGroupIdx = 0;
+
+ OSL_ENSURE( (nGroupNo<=3) && (nGroupNo>0), "Invalid group" );
+ OSL_ENSURE( (mxLbGroup->get_count() > 0)
+ && (mxLbColumns->n_children() > 0)
+ && (mxLbFunctions->n_children() > 0),
+ "Non-initialized Lists" );
+
+ if ( (nGroupNo > 3) || (nGroupNo == 0)
+ || (mxLbGroup->get_count() == 0)
+ || (mxLbColumns->n_children() == 0)
+ || (mxLbFunctions->n_children() == 0)
+ )
+ return false;
+ else
+ nGroupIdx = nGroupNo-1;
+
+ ScSubTotalParam theSubTotalData; // read out, if already partly filled
+ const SfxItemSet* pExample = GetDialogExampleSet();
+ if (pExample)
+ {
+ if (const ScSubTotalItem* pItem = pExample->GetItemIfSet(nWhichSubTotals))
+ theSubTotalData = pItem->GetSubTotalData();
+ }
+
+ std::unique_ptr<ScSubTotalFunc[]> pFunctions;
+ std::unique_ptr<SCCOL[]> pSubTotals;
+ const sal_Int32 nGroup = mxLbGroup->get_active();
+ const sal_Int32 nEntryCount = mxLbColumns->n_children();
+ const sal_Int32 nCheckCount = GetCheckedEntryCount(*mxLbColumns);
+
+ theSubTotalData.nCol1 = rSubTotalData.nCol1;
+ theSubTotalData.nRow1 = rSubTotalData.nRow1;
+ theSubTotalData.nCol2 = rSubTotalData.nCol2;
+ theSubTotalData.nRow2 = rSubTotalData.nRow2;
+ theSubTotalData.bGroupActive[nGroupIdx] = (nGroup != 0);
+ theSubTotalData.nField[nGroupIdx] = (nGroup != 0)
+ ? mnFieldArr[nGroup-1]
+ : static_cast<SCCOL>(0);
+
+ if ( nEntryCount>0 && nCheckCount>0 && nGroup!=0 )
+ {
+ sal_uInt16 nFunction = 0;
+
+ pSubTotals.reset(new SCCOL [nCheckCount]);
+ pFunctions.reset(new ScSubTotalFunc [nCheckCount]);
+
+ for ( sal_Int32 i=0, nCheck=0; i<nEntryCount; i++ )
+ {
+ if (mxLbColumns->get_toggle(i) == TRISTATE_TRUE)
+ {
+ OSL_ENSURE( nCheck <= nCheckCount,
+ "Range error :-(" );
+ nFunction = mxLbColumns->get_id(i).toUInt32();
+ pSubTotals[nCheck] = mnFieldArr[i];
+ pFunctions[nCheck] = LbPosToFunc( nFunction );
+ nCheck++;
+ }
+ }
+ theSubTotalData.SetSubTotals( nGroupNo, // group number
+ pSubTotals.get(),
+ pFunctions.get(),
+ nCheckCount ); // number of array elements
+
+ }
+
+ rArgSet.Put( ScSubTotalItem( SCITEM_SUBTDATA, nullptr, &theSubTotalData ) );
+
+ return true;
+}
+
+void ScTpSubTotalGroup::FillListBoxes()
+{
+ assert(pViewData && pDoc && "CreateScSubTotalDlg aArgSet must contain a ScSubTotalItem with ViewData set");
+
+ SCCOL nFirstCol = rSubTotalData.nCol1;
+ SCROW nFirstRow = rSubTotalData.nRow1;
+ SCTAB nTab = pViewData->GetTabNo();
+ SCCOL nMaxCol = rSubTotalData.nCol2;
+ SCCOL col;
+ OUString aFieldName;
+
+ mxLbGroup->clear();
+ mxLbColumns->clear();
+ mxLbGroup->insert_text(0, aStrNone );
+
+ mxLbColumns->freeze();
+ sal_uInt16 i=0;
+ for ( col=nFirstCol; col<=nMaxCol && i<SC_MAXFIELDS(pDoc->GetSheetLimits()); col++ )
+ {
+ aFieldName = pDoc->GetString(col, nFirstRow, nTab);
+ if ( aFieldName.isEmpty() )
+ {
+ aFieldName = ScGlobal::ReplaceOrAppend( aStrColumn, u"%1", ScColToAlpha( col ));
+ }
+ mnFieldArr[i] = col;
+ mxLbGroup->insert_text(i+1, aFieldName);
+ mxLbColumns->insert(i);
+ mxLbColumns->set_toggle(i, TRISTATE_FALSE);
+ mxLbColumns->set_text(i, aFieldName, 0);
+ mxLbColumns->set_id(i, "0");
+ i++;
+ }
+ mxLbColumns->thaw();
+
+ // subsequent initialization of the constant:
+ nFieldCount = i;
+}
+
+sal_uInt16 ScTpSubTotalGroup::GetFieldSelPos( SCCOL nField )
+{
+ sal_uInt16 nFieldPos = 0;
+ bool bFound = false;
+
+ for ( sal_uInt16 n=0; n<nFieldCount && !bFound; n++ )
+ {
+ if ( mnFieldArr[n] == nField )
+ {
+ nFieldPos = n;
+ bFound = true;
+ }
+ }
+
+ return nFieldPos;
+}
+
+ScSubTotalFunc ScTpSubTotalGroup::LbPosToFunc( sal_uInt16 nPos )
+{
+ switch ( nPos )
+ {
+// case 0: return SUBTOTAL_FUNC_NONE;
+ case 2: return SUBTOTAL_FUNC_AVE;
+ case 6: return SUBTOTAL_FUNC_CNT;
+ case 1: return SUBTOTAL_FUNC_CNT2;
+ case 3: return SUBTOTAL_FUNC_MAX;
+ case 4: return SUBTOTAL_FUNC_MIN;
+ case 5: return SUBTOTAL_FUNC_PROD;
+ case 7: return SUBTOTAL_FUNC_STD;
+ case 8: return SUBTOTAL_FUNC_STDP;
+ case 0: return SUBTOTAL_FUNC_SUM;
+ case 9: return SUBTOTAL_FUNC_VAR;
+ case 10: return SUBTOTAL_FUNC_VARP;
+ default:
+ OSL_FAIL( "ScTpSubTotalGroup::LbPosToFunc" );
+ return SUBTOTAL_FUNC_NONE;
+ }
+}
+
+sal_uInt16 ScTpSubTotalGroup::FuncToLbPos( ScSubTotalFunc eFunc )
+{
+ switch ( eFunc )
+ {
+// case SUBTOTAL_FUNC_NONE: return 0;
+ case SUBTOTAL_FUNC_AVE: return 2;
+ case SUBTOTAL_FUNC_CNT: return 6;
+ case SUBTOTAL_FUNC_CNT2: return 1;
+ case SUBTOTAL_FUNC_MAX: return 3;
+ case SUBTOTAL_FUNC_MIN: return 4;
+ case SUBTOTAL_FUNC_PROD: return 5;
+ case SUBTOTAL_FUNC_STD: return 7;
+ case SUBTOTAL_FUNC_STDP: return 8;
+ case SUBTOTAL_FUNC_SUM: return 0;
+ case SUBTOTAL_FUNC_VAR: return 9;
+ case SUBTOTAL_FUNC_VARP: return 10;
+ default:
+ OSL_FAIL( "ScTpSubTotalGroup::FuncToLbPos" );
+ return 0;
+ }
+}
+
+// Handler:
+
+IMPL_LINK(ScTpSubTotalGroup, SelectTreeListBoxHdl, weld::TreeView&, rLb, void)
+{
+ SelectHdl(&rLb);
+
+ if ( mxLbColumns->n_children() == GetCheckedEntryCount(*mxLbColumns) )
+ mxLbSelectAllColumns->set_active( true );
+ else
+ mxLbSelectAllColumns->set_active( false );
+}
+
+IMPL_LINK(ScTpSubTotalGroup, SelectListBoxHdl, weld::ComboBox&, rLb, void)
+{
+ SelectHdl(&rLb);
+}
+
+void ScTpSubTotalGroup::SelectHdl(const weld::Widget *pLb)
+{
+ const sal_Int32 nColumn = mxLbColumns->get_selected_index();
+ if (nColumn == -1)
+ return;
+
+ const sal_Int32 nFunction = mxLbFunctions->get_selected_index();
+ sal_uInt16 nOldFunction = mxLbColumns->get_id(nColumn).toUInt32();
+
+ if ( pLb == mxLbColumns.get() )
+ {
+ mxLbFunctions->select(nOldFunction);
+ }
+ else if ( pLb == mxLbFunctions.get() )
+ {
+ mxLbColumns->set_id(nColumn, OUString::number(nFunction));
+ mxLbColumns->set_toggle(nColumn, TRISTATE_TRUE);
+ }
+}
+
+IMPL_LINK( ScTpSubTotalGroup, CheckHdl, const weld::TreeView::iter_col&, rRowCol, void )
+{
+ mxLbColumns->select(rRowCol.first);
+ SelectHdl(mxLbColumns.get());
+
+ if ( mxLbColumns->n_children() == GetCheckedEntryCount(*mxLbColumns) )
+ mxLbSelectAllColumns->set_active( true );
+ else
+ mxLbSelectAllColumns->set_active( false );
+}
+
+// Derived Group TabPages:
+
+std::unique_ptr<SfxTabPage> ScTpSubTotalGroup1::Create( weld::Container* pPage, weld::DialogController* pController,
+ const SfxItemSet* rArgSet )
+{
+ return std::make_unique<ScTpSubTotalGroup1>( pPage, pController, *rArgSet );
+}
+
+std::unique_ptr<SfxTabPage> ScTpSubTotalGroup2::Create( weld::Container* pPage, weld::DialogController* pController,
+ const SfxItemSet* rArgSet )
+{
+ return std::make_unique<ScTpSubTotalGroup2>( pPage, pController, *rArgSet );
+}
+
+std::unique_ptr<SfxTabPage> ScTpSubTotalGroup3::Create( weld::Container* pPage, weld::DialogController* pController,
+ const SfxItemSet* rArgSet )
+{
+ return std::make_unique<ScTpSubTotalGroup3>( pPage, pController, *rArgSet );
+}
+
+ScTpSubTotalGroup1::ScTpSubTotalGroup1( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rArgSet ) :
+ ScTpSubTotalGroup( pPage, pController, rArgSet, 1 )
+{}
+
+ScTpSubTotalGroup2::ScTpSubTotalGroup2( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rArgSet ) :
+ ScTpSubTotalGroup( pPage, pController, rArgSet, 2 )
+{}
+
+ScTpSubTotalGroup3::ScTpSubTotalGroup3( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rArgSet ) :
+ ScTpSubTotalGroup( pPage, pController, rArgSet, 3 )
+{}
+
+#define RESET(i) (ScTpSubTotalGroup::DoReset( (i), *rArgSet ))
+void ScTpSubTotalGroup1::Reset( const SfxItemSet* rArgSet ) { RESET(1); }
+void ScTpSubTotalGroup2::Reset( const SfxItemSet* rArgSet ) { RESET(2); }
+void ScTpSubTotalGroup3::Reset( const SfxItemSet* rArgSet ) { RESET(3); }
+#undef RESET
+
+#define FILLSET(i) (ScTpSubTotalGroup::DoFillItemSet( (i), *rArgSet ))
+bool ScTpSubTotalGroup1::FillItemSet( SfxItemSet* rArgSet ) { return FILLSET(1); }
+bool ScTpSubTotalGroup2::FillItemSet( SfxItemSet* rArgSet ) { return FILLSET(2); }
+bool ScTpSubTotalGroup3::FillItemSet( SfxItemSet* rArgSet ) { return FILLSET(3); }
+#undef FILL
+
+// options tab page:
+
+ScTpSubTotalOptions::ScTpSubTotalOptions(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rArgSet)
+
+ : SfxTabPage ( pPage, pController,
+ "modules/scalc/ui/subtotaloptionspage.ui", "SubTotalOptionsPage",
+ &rArgSet ),
+ pViewData ( nullptr ),
+ pDoc ( nullptr ),
+ nWhichSubTotals ( rArgSet.GetPool()->GetWhich( SID_SUBTOTALS ) ),
+ rSubTotalData ( rArgSet.Get( nWhichSubTotals ).GetSubTotalData() )
+ , m_xBtnPagebreak(m_xBuilder->weld_check_button("pagebreak"))
+ , m_xBtnCase(m_xBuilder->weld_check_button("case"))
+ , m_xBtnSort(m_xBuilder->weld_check_button("sort"))
+ , m_xFlSort(m_xBuilder->weld_label("label2"))
+ , m_xBtnAscending(m_xBuilder->weld_radio_button("ascending"))
+ , m_xBtnDescending(m_xBuilder->weld_radio_button("descending"))
+ , m_xBtnFormats(m_xBuilder->weld_check_button("formats"))
+ , m_xBtnUserDef(m_xBuilder->weld_check_button("btnuserdef"))
+ , m_xLbUserDef(m_xBuilder->weld_combo_box("lbuserdef"))
+{
+ m_xLbUserDef->set_accessible_description(ScResId(STR_A11Y_DESC_USERDEF));
+ m_xBtnUserDef->set_accessible_description(ScResId(STR_A11Y_DESC_USERDEF));
+ Init();
+}
+
+ScTpSubTotalOptions::~ScTpSubTotalOptions()
+{
+}
+
+void ScTpSubTotalOptions::Init()
+{
+ const ScSubTotalItem& rSubTotalItem = GetItemSet().Get( nWhichSubTotals );
+
+ pViewData = rSubTotalItem.GetViewData();
+ assert(pViewData && "CreateScSubTotalDlg aArgSet must contain a ScSubTotalItem with ViewData set");
+ pDoc = &pViewData->GetDocument();
+ assert(pDoc && "Document not found!");
+
+ m_xBtnSort->connect_toggled( LINK( this, ScTpSubTotalOptions, CheckHdl ) );
+ m_xBtnUserDef->connect_toggled( LINK( this, ScTpSubTotalOptions, CheckHdl ) );
+
+ FillUserSortListBox();
+}
+
+std::unique_ptr<SfxTabPage> ScTpSubTotalOptions::Create(weld::Container* pPage, weld::DialogController* pController,
+ const SfxItemSet* rArgSet)
+{
+ return std::make_unique<ScTpSubTotalOptions>(pPage, pController, *rArgSet);
+}
+
+void ScTpSubTotalOptions::Reset( const SfxItemSet* /* rArgSet */ )
+{
+ m_xBtnPagebreak->set_active( rSubTotalData.bPagebreak );
+ m_xBtnCase->set_active( rSubTotalData.bCaseSens );
+ m_xBtnFormats->set_active( rSubTotalData.bIncludePattern );
+ m_xBtnSort->set_active( rSubTotalData.bDoSort );
+ m_xBtnAscending->set_active( rSubTotalData.bAscending );
+ m_xBtnDescending->set_active( !rSubTotalData.bAscending );
+
+ if ( rSubTotalData.bUserDef )
+ {
+ m_xBtnUserDef->set_active(true);
+ m_xLbUserDef->set_sensitive(true);
+ m_xLbUserDef->set_active(rSubTotalData.nUserIndex);
+ }
+ else
+ {
+ m_xBtnUserDef->set_active( false );
+ m_xLbUserDef->set_sensitive(false);
+ m_xLbUserDef->set_active(0);
+ }
+
+ CheckHdl(*m_xBtnSort);
+}
+
+bool ScTpSubTotalOptions::FillItemSet( SfxItemSet* rArgSet )
+{
+ ScSubTotalParam theSubTotalData; // read out, if already partly filled
+ const SfxItemSet* pExample = GetDialogExampleSet();
+ if (pExample)
+ {
+ if (const ScSubTotalItem* pItem = pExample->GetItemIfSet(nWhichSubTotals))
+ theSubTotalData = pItem->GetSubTotalData();
+ }
+
+ theSubTotalData.bPagebreak = m_xBtnPagebreak->get_active();
+ theSubTotalData.bReplace = true;
+ theSubTotalData.bCaseSens = m_xBtnCase->get_active();
+ theSubTotalData.bIncludePattern = m_xBtnFormats->get_active();
+ theSubTotalData.bDoSort = m_xBtnSort->get_active();
+ theSubTotalData.bAscending = m_xBtnAscending->get_active();
+ theSubTotalData.bUserDef = m_xBtnUserDef->get_active();
+ theSubTotalData.nUserIndex = (m_xBtnUserDef->get_active())
+ ? m_xLbUserDef->get_active()
+ : 0;
+
+ rArgSet->Put( ScSubTotalItem( nWhichSubTotals, nullptr, &theSubTotalData ) );
+
+ return true;
+}
+
+void ScTpSubTotalOptions::FillUserSortListBox()
+{
+ ScUserList* pUserLists = ScGlobal::GetUserList();
+
+ m_xLbUserDef->freeze();
+ m_xLbUserDef->clear();
+ if ( pUserLists )
+ {
+ size_t nCount = pUserLists->size();
+ for ( size_t i=0; i<nCount; ++i )
+ m_xLbUserDef->append_text((*pUserLists)[i].GetString() );
+ }
+ m_xLbUserDef->thaw();
+}
+
+// Handler:
+
+IMPL_LINK(ScTpSubTotalOptions, CheckHdl, weld::Toggleable&, rBox, void)
+{
+ if (&rBox == m_xBtnSort.get())
+ {
+ if ( m_xBtnSort->get_active() )
+ {
+ m_xFlSort->set_sensitive(true);
+ m_xBtnFormats->set_sensitive(true);
+ m_xBtnUserDef->set_sensitive(true);
+ m_xBtnAscending->set_sensitive(true);
+ m_xBtnDescending->set_sensitive(true);
+
+ if ( m_xBtnUserDef->get_active() )
+ m_xLbUserDef->set_sensitive(true);
+ }
+ else
+ {
+ m_xFlSort->set_sensitive(false);
+ m_xBtnFormats->set_sensitive(false);
+ m_xBtnUserDef->set_sensitive(false);
+ m_xBtnAscending->set_sensitive(false);
+ m_xBtnDescending->set_sensitive(false);
+ m_xLbUserDef->set_sensitive(false);
+ }
+ }
+ else if (&rBox == m_xBtnUserDef.get())
+ {
+ if ( m_xBtnUserDef->get_active() )
+ {
+ m_xLbUserDef->set_sensitive(true);
+ m_xLbUserDef->grab_focus();
+ }
+ else
+ m_xLbUserDef->set_sensitive(false);
+ }
+}
+
+IMPL_LINK(ScTpSubTotalGroup, CheckBoxHdl, weld::Toggleable&, rBox, void)
+{
+ if (&rBox != mxLbSelectAllColumns.get())
+ return;
+
+ bool bChecked = mxLbSelectAllColumns->get_active();
+
+ mxLbColumns->all_foreach([&](const weld::TreeIter& rEntry) {
+ if ( bChecked )
+ mxLbColumns->set_toggle(rEntry, TRISTATE_TRUE);
+ else
+ mxLbColumns->set_toggle(rEntry, TRISTATE_FALSE);
+
+ return false;
+ });
+}
+
+ScTpSubTotalGroup1::~ScTpSubTotalGroup1()
+{
+}
+
+ScTpSubTotalGroup2::~ScTpSubTotalGroup2()
+{
+}
+
+ScTpSubTotalGroup3::~ScTpSubTotalGroup3()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/validate.cxx b/sc/source/ui/dbgui/validate.cxx
new file mode 100644
index 000000000..d194fdc10
--- /dev/null
+++ b/sc/source/ui/dbgui/validate.cxx
@@ -0,0 +1,927 @@
+/* -*- 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 .
+ */
+
+#ifdef SC_DLLIMPLEMENTATION
+#undef SC_DLLIMPLEMENTATION
+#endif
+
+#include <com/sun/star/sheet/TableValidationVisibility.hpp>
+#include <comphelper/string.hxx>
+#include <svl/stritem.hxx>
+#include <svl/eitem.hxx>
+#include <svl/intitem.hxx>
+#include <sfx2/app.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <scresid.hxx>
+#include <strings.hrc>
+
+#include <stringutil.hxx>
+#include <validat.hxx>
+#include <validate.hxx>
+#include <compiler.hxx>
+#include <formula/opcode.hxx>
+
+// cell range picker
+#include <tabvwsh.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/childwin.hxx>
+#include <reffact.hxx>
+#include <comphelper/lok.hxx>
+
+
+#define IS_MOBILE (comphelper::LibreOfficeKit::isActive() && SfxViewShell::Current() && SfxViewShell::Current()->isLOKMobilePhone())
+
+/* Position indexes for "Allow" list box.
+ They do not map directly to ScValidationMode and can safely be modified to
+ change the order of the list box entries. */
+#define SC_VALIDDLG_ALLOW_ANY 0
+#define SC_VALIDDLG_ALLOW_WHOLE 1
+#define SC_VALIDDLG_ALLOW_DECIMAL 2
+#define SC_VALIDDLG_ALLOW_DATE 3
+#define SC_VALIDDLG_ALLOW_TIME 4
+#define SC_VALIDDLG_ALLOW_RANGE 5
+#define SC_VALIDDLG_ALLOW_LIST 6
+#define SC_VALIDDLG_ALLOW_TEXTLEN 7
+#define SC_VALIDDLG_ALLOW_CUSTOM 8
+
+/* Position indexes for "Data" list box.
+ They do not map directly to ScConditionMode and can safely be modified to
+ change the order of the list box entries. */
+#define SC_VALIDDLG_DATA_EQUAL 0
+#define SC_VALIDDLG_DATA_LESS 1
+#define SC_VALIDDLG_DATA_GREATER 2
+#define SC_VALIDDLG_DATA_EQLESS 3
+#define SC_VALIDDLG_DATA_EQGREATER 4
+#define SC_VALIDDLG_DATA_NOTEQUAL 5
+#define SC_VALIDDLG_DATA_VALIDRANGE 6
+#define SC_VALIDDLG_DATA_INVALIDRANGE 7
+#define SC_VALIDDLG_DATA_DIRECT 8
+
+namespace ValidListType = css::sheet::TableValidationVisibility;
+
+const WhichRangesContainer ScTPValidationValue::pValueRanges(svl::Items<
+ FID_VALID_LISTTYPE, FID_VALID_LISTTYPE,
+ FID_VALID_MODE, FID_VALID_ERRTEXT
+>);
+
+ScValidationDlg::ScValidationDlg(weld::Window* pParent, const SfxItemSet* pArgSet,
+ ScTabViewShell *pTabViewSh)
+ : ScValidationDlgBase(pParent,
+ "modules/scalc/ui/validationdialog.ui", "ValidationDialog", pArgSet, nullptr)
+ , m_pTabVwSh(pTabViewSh)
+ , m_sValuePageId("criteria")
+ , m_bOwnRefHdlr(false)
+ , m_bRefInputting(false)
+ , m_xHBox(m_xBuilder->weld_container("refinputbox"))
+{
+ AddTabPage(m_sValuePageId, ScTPValidationValue::Create, nullptr);
+ AddTabPage("inputhelp", ScTPValidationHelp::Create, nullptr);
+ AddTabPage("erroralert", ScTPValidationError::Create, nullptr);
+
+ if (IS_MOBILE)
+ {
+ m_xBuilder->weld_button("cancel")->hide();
+ m_xBuilder->weld_button("help")->hide();
+ }
+}
+
+void ScValidationDlg::EndDialog(int nResponse)
+{
+ // tdf#137215 ensure original modality of true is restored before dialog loop ends
+ if (m_bOwnRefHdlr)
+ RemoveRefDlg(true);
+ ScValidationDlgBase::EndDialog(nResponse);
+}
+
+ScValidationDlg::~ScValidationDlg()
+{
+ if (m_bOwnRefHdlr)
+ RemoveRefDlg(false);
+}
+
+void ScTPValidationValue::SetReferenceHdl( const ScRange&rRange , const ScDocument& rDoc )
+{
+ if ( rRange.aStart != rRange.aEnd )
+ if ( ScValidationDlg *pValidationDlg = GetValidationDlg() )
+ if( m_pRefEdit )
+ pValidationDlg->RefInputStart( m_pRefEdit );
+
+ if ( m_pRefEdit )
+ {
+ OUString aStr(rRange.Format(rDoc, ScRefFlags::RANGE_ABS_3D, rDoc.GetAddressConvention()));
+ m_pRefEdit->SetRefString( aStr );
+ }
+}
+
+void ScTPValidationValue:: SetActiveHdl()
+{
+ if ( m_pRefEdit ) m_pRefEdit->GrabFocus();
+
+ if ( ScValidationDlg *pValidationDlg = GetValidationDlg() )
+ if( m_pRefEdit )
+ {
+ pValidationDlg->RefInputDone();
+ }
+}
+
+void ScTPValidationValue::RefInputStartPreHdl( formula::RefEdit* pEdit, const formula::RefButton* pButton )
+{
+ ScValidationDlg *pValidationDlg = GetValidationDlg();
+ if (!pValidationDlg)
+ return;
+
+ weld::Container* pNewParent = pValidationDlg->get_refinput_shrink_parent();
+ if (pEdit == m_pRefEdit && pNewParent != m_pRefEditParent)
+ {
+ m_xRefGrid->move(m_pRefEdit->GetWidget(), pNewParent);
+ m_pRefEditParent = pNewParent;
+ }
+
+ if (pNewParent != m_pBtnRefParent)
+ {
+ // if Edit SetParent but button not, the tab order will be
+ // incorrect, so move button anyway, and restore
+ // parent later in order to restore the tab order. But
+ // hide it if it's moved but unwanted.
+ m_xRefGrid->move(m_xBtnRef->GetWidget(), pNewParent);
+ m_xBtnRef->GetWidget()->set_visible(pButton == m_xBtnRef.get());
+ m_pBtnRefParent = pNewParent;
+ }
+
+ pNewParent->show();
+}
+
+void ScTPValidationValue::RefInputDonePostHdl()
+{
+ if (ScValidationDlg *pValidationDlg = GetValidationDlg())
+ {
+ weld::Container* pOldParent = pValidationDlg->get_refinput_shrink_parent();
+
+ if (m_pRefEdit && m_pRefEditParent != m_xRefGrid.get())
+ {
+ pOldParent->move(m_pRefEdit->GetWidget(), m_xRefGrid.get());
+ m_pRefEditParent = m_xRefGrid.get();
+ }
+
+ if (m_pBtnRefParent != m_xRefGrid.get())
+ {
+ pOldParent->move(m_xBtnRef->GetWidget(), m_xRefGrid.get());
+ m_xBtnRef->GetWidget()->show();
+ m_pBtnRefParent = m_xRefGrid.get();
+ }
+
+ pOldParent->hide();
+ ScViewData& rViewData = pValidationDlg->GetTabViewShell()->GetViewData();
+ SCTAB nCurTab = rViewData.GetTabNo();
+ SCTAB nRefTab = rViewData.GetRefTabNo();
+ // If RefInput switched to a different sheet from the data sheet,
+ // switch back: fdo#53920
+ if ( nCurTab != nRefTab )
+ {
+ rViewData.GetViewShell()->SetTabNo( nRefTab );
+ }
+ }
+
+ if (m_pRefEdit && !m_pRefEdit->GetWidget()->has_focus())
+ m_pRefEdit->GrabFocus();
+}
+
+namespace {
+
+/** Converts the passed ScValidationMode to the position in the list box. */
+sal_uInt16 lclGetPosFromValMode( ScValidationMode eValMode )
+{
+ sal_uInt16 nLbPos = SC_VALIDDLG_ALLOW_ANY;
+ switch( eValMode )
+ {
+ case SC_VALID_ANY: nLbPos = SC_VALIDDLG_ALLOW_ANY; break;
+ case SC_VALID_WHOLE: nLbPos = SC_VALIDDLG_ALLOW_WHOLE; break;
+ case SC_VALID_DECIMAL: nLbPos = SC_VALIDDLG_ALLOW_DECIMAL; break;
+ case SC_VALID_DATE: nLbPos = SC_VALIDDLG_ALLOW_DATE; break;
+ case SC_VALID_TIME: nLbPos = SC_VALIDDLG_ALLOW_TIME; break;
+ case SC_VALID_TEXTLEN: nLbPos = SC_VALIDDLG_ALLOW_TEXTLEN; break;
+ case SC_VALID_LIST: nLbPos = SC_VALIDDLG_ALLOW_RANGE; break;
+ case SC_VALID_CUSTOM: nLbPos = SC_VALIDDLG_ALLOW_CUSTOM; break;
+ default: OSL_FAIL( "lclGetPosFromValMode - unknown validity mode" );
+ }
+ return nLbPos;
+}
+
+/** Converts the passed list box position to an ScValidationMode. */
+ScValidationMode lclGetValModeFromPos( sal_uInt16 nLbPos )
+{
+ ScValidationMode eValMode = SC_VALID_ANY;
+ switch( nLbPos )
+ {
+ case SC_VALIDDLG_ALLOW_ANY: eValMode = SC_VALID_ANY; break;
+ case SC_VALIDDLG_ALLOW_WHOLE: eValMode = SC_VALID_WHOLE; break;
+ case SC_VALIDDLG_ALLOW_DECIMAL: eValMode = SC_VALID_DECIMAL; break;
+ case SC_VALIDDLG_ALLOW_DATE: eValMode = SC_VALID_DATE; break;
+ case SC_VALIDDLG_ALLOW_TIME: eValMode = SC_VALID_TIME; break;
+ case SC_VALIDDLG_ALLOW_RANGE: eValMode = SC_VALID_LIST; break;
+ case SC_VALIDDLG_ALLOW_LIST: eValMode = SC_VALID_LIST; break;
+ case SC_VALIDDLG_ALLOW_TEXTLEN: eValMode = SC_VALID_TEXTLEN; break;
+ case SC_VALIDDLG_ALLOW_CUSTOM: eValMode = SC_VALID_CUSTOM; break;
+ default: OSL_FAIL( "lclGetValModeFromPos - invalid list box position" );
+ }
+ return eValMode;
+}
+
+/** Converts the passed ScConditionMode to the position in the list box. */
+sal_uInt16 lclGetPosFromCondMode( ScConditionMode eCondMode )
+{
+ sal_uInt16 nLbPos = SC_VALIDDLG_DATA_EQUAL;
+ switch( eCondMode )
+ {
+ case ScConditionMode::NONE: // may occur in old XML files after Excel import
+ case ScConditionMode::Equal: nLbPos = SC_VALIDDLG_DATA_EQUAL; break;
+ case ScConditionMode::Less: nLbPos = SC_VALIDDLG_DATA_LESS; break;
+ case ScConditionMode::Greater: nLbPos = SC_VALIDDLG_DATA_GREATER; break;
+ case ScConditionMode::EqLess: nLbPos = SC_VALIDDLG_DATA_EQLESS; break;
+ case ScConditionMode::EqGreater: nLbPos = SC_VALIDDLG_DATA_EQGREATER; break;
+ case ScConditionMode::NotEqual: nLbPos = SC_VALIDDLG_DATA_NOTEQUAL; break;
+ case ScConditionMode::Between: nLbPos = SC_VALIDDLG_DATA_VALIDRANGE; break;
+ case ScConditionMode::NotBetween: nLbPos = SC_VALIDDLG_DATA_INVALIDRANGE; break;
+ case ScConditionMode::Direct: nLbPos = SC_VALIDDLG_DATA_DIRECT; break;
+ default: OSL_FAIL( "lclGetPosFromCondMode - unknown condition mode" );
+ }
+ return nLbPos;
+}
+
+/** Converts the passed list box position to an ScConditionMode. */
+ScConditionMode lclGetCondModeFromPos( sal_uInt16 nLbPos )
+{
+ ScConditionMode eCondMode = ScConditionMode::Equal;
+ switch( nLbPos )
+ {
+ case SC_VALIDDLG_DATA_EQUAL: eCondMode = ScConditionMode::Equal; break;
+ case SC_VALIDDLG_DATA_LESS: eCondMode = ScConditionMode::Less; break;
+ case SC_VALIDDLG_DATA_GREATER: eCondMode = ScConditionMode::Greater; break;
+ case SC_VALIDDLG_DATA_EQLESS: eCondMode = ScConditionMode::EqLess; break;
+ case SC_VALIDDLG_DATA_EQGREATER: eCondMode = ScConditionMode::EqGreater; break;
+ case SC_VALIDDLG_DATA_NOTEQUAL: eCondMode = ScConditionMode::NotEqual; break;
+ case SC_VALIDDLG_DATA_VALIDRANGE: eCondMode = ScConditionMode::Between; break;
+ case SC_VALIDDLG_DATA_INVALIDRANGE: eCondMode = ScConditionMode::NotBetween; break;
+ case SC_VALIDDLG_DATA_DIRECT: eCondMode = ScConditionMode::Direct; break;
+ default: OSL_FAIL( "lclGetCondModeFromPos - invalid list box position" );
+ }
+ return eCondMode;
+}
+
+/** Converts line feed separated string to a formula with strings separated by semicolons.
+ @descr Keeps all empty strings.
+ Example: abc\ndef\n\nghi -> "abc";"def";"";"ghi".
+ @param rFmlaStr (out-param) The converted formula string. */
+void lclGetFormulaFromStringList( OUString& rFmlaStr, std::u16string_view rStringList, sal_Unicode cFmlaSep )
+{
+ rFmlaStr.clear();
+ if (!rStringList.empty())
+ {
+ sal_Int32 nIdx {0};
+ do
+ {
+ OUString aToken {o3tl::getToken(rStringList, 0, '\n', nIdx )};
+ ScGlobal::AddQuotes( aToken, '"' );
+ rFmlaStr = ScGlobal::addToken(rFmlaStr, aToken, cFmlaSep);
+ }
+ while (nIdx>0);
+ }
+ if( rFmlaStr.isEmpty() )
+ rFmlaStr = "\"\"";
+}
+
+/** Converts formula with strings separated by semicolons to line feed separated string.
+ @descr Keeps all empty strings. Ignores all empty tokens (multiple semicolons).
+ Example: "abc";;;"def";"";"ghi" -> abc\ndef\n\nghi.
+ @param rStringList (out-param) The converted line feed separated string list.
+ @return true = Conversion successful. */
+bool lclGetStringListFromFormula( OUString& rStringList, const OUString& rFmlaStr, sal_Unicode cFmlaSep )
+{
+ static const OUStringLiteral aQuotes( u"\"\"" );
+
+ rStringList.clear();
+ bool bIsStringList = !rFmlaStr.isEmpty();
+ bool bTokenAdded = false;
+
+ for ( sal_Int32 nStringIx = 0; bIsStringList && nStringIx>=0; )
+ {
+ OUString aToken( ScStringUtil::GetQuotedToken(rFmlaStr, 0, aQuotes, cFmlaSep, nStringIx ) );
+ aToken = comphelper::string::strip(aToken, ' ');
+ if( !aToken.isEmpty() ) // ignore empty tokens, i.e. "a";;"b"
+ {
+ bIsStringList = ScGlobal::IsQuoted( aToken, '"' );
+ if( bIsStringList )
+ {
+ ScGlobal::EraseQuotes( aToken, '"' );
+ rStringList = ScGlobal::addToken(rStringList, aToken, '\n', 1, bTokenAdded);
+ bTokenAdded = true;
+ }
+ }
+ }
+
+ return bIsStringList;
+}
+
+} // namespace
+
+ScTPValidationValue::ScTPValidationValue(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rArgSet)
+ : SfxTabPage(pPage, pController, "modules/scalc/ui/validationcriteriapage.ui",
+ "ValidationCriteriaPage", &rArgSet)
+ , maStrMin(ScResId(SCSTR_VALID_MINIMUM))
+ , maStrMax(ScResId(SCSTR_VALID_MAXIMUM))
+ , maStrValue(ScResId(SCSTR_VALID_VALUE))
+ , maStrFormula(ScResId(SCSTR_VALID_FORMULA))
+ , maStrRange(ScResId(SCSTR_VALID_RANGE))
+ , maStrList(ScResId(SCSTR_VALID_LIST))
+ , m_pRefEdit(nullptr)
+ , m_xLbAllow(m_xBuilder->weld_combo_box("allow"))
+ , m_xCbAllow(m_xBuilder->weld_check_button("allowempty"))
+ , m_xCbShow(m_xBuilder->weld_check_button("showlist"))
+ , m_xCbSort(m_xBuilder->weld_check_button("sortascend"))
+ , m_xFtValue(m_xBuilder->weld_label("valueft"))
+ , m_xLbValue(m_xBuilder->weld_combo_box("data"))
+ , m_xFtMin(m_xBuilder->weld_label("minft"))
+ , m_xMinGrid(m_xBuilder->weld_widget("mingrid"))
+ , m_xEdMin(new formula::RefEdit(m_xBuilder->weld_entry("min")))
+ , m_xEdList(m_xBuilder->weld_text_view("minlist"))
+ , m_xFtMax(m_xBuilder->weld_label("maxft"))
+ , m_xEdMax(new formula::RefEdit(m_xBuilder->weld_entry("max")))
+ , m_xFtHint(m_xBuilder->weld_label("hintft"))
+ , m_xBtnRef(new formula::RefButton(m_xBuilder->weld_button("validref")))
+ , m_xRefGrid(m_xBuilder->weld_container("refgrid"))
+ , m_pRefEditParent(m_xRefGrid.get())
+ , m_pBtnRefParent(m_xRefGrid.get())
+{
+ m_xEdMin->SetReferences(nullptr, m_xFtMin.get());
+
+ Size aSize(m_xEdList->get_approximate_digit_width() * 40,
+ m_xEdList->get_height_rows(10));
+ m_xEdList->set_size_request(aSize.Width(), aSize.Height());
+ m_xEdMax->SetReferences(nullptr, m_xFtMax.get());
+
+ m_xBtnRef->SetClickHdl(LINK(this, ScTPValidationValue, ClickHdl));
+
+ //lock in the max size initial config
+ aSize = m_xContainer->get_preferred_size();
+ m_xContainer->set_size_request(aSize.Width(), aSize.Height());
+
+ Init();
+
+ // list separator in formulas
+ OUString aListSep = ::ScCompiler::GetNativeSymbol( ocSep );
+ OSL_ENSURE( aListSep.getLength() == 1, "ScTPValidationValue::ScTPValidationValue - list separator error" );
+ mcFmlaSep = aListSep.getLength() ? aListSep[0] : ';';
+ m_xBtnRef->GetWidget()->hide(); // cell range picker
+}
+
+ScTPValidationValue::~ScTPValidationValue()
+{
+ m_xEdMin.reset();
+ m_xEdMax.reset();
+ m_xBtnRef.reset();
+}
+
+void ScTPValidationValue::Init()
+{
+ m_xLbAllow->connect_changed( LINK( this, ScTPValidationValue, SelectHdl ) );
+ m_xLbValue->connect_changed( LINK( this, ScTPValidationValue, SelectHdl ) );
+ m_xCbShow->connect_toggled( LINK( this, ScTPValidationValue, CheckHdl ) );
+
+ // cell range picker
+ m_xEdMin->SetGetFocusHdl( LINK( this, ScTPValidationValue, EditSetFocusHdl ) );
+ m_xEdMin->SetLoseFocusHdl( LINK( this, ScTPValidationValue, KillEditFocusHdl ) );
+ m_xEdMax->SetGetFocusHdl( LINK( this, ScTPValidationValue, EditSetFocusHdl ) );
+ m_xEdMax->SetLoseFocusHdl( LINK( this, ScTPValidationValue, KillEditFocusHdl ) );
+ m_xBtnRef->SetLoseFocusHdl( LINK( this, ScTPValidationValue, KillButtonFocusHdl ) );
+
+ m_xLbAllow->set_active( SC_VALIDDLG_ALLOW_ANY );
+ m_xLbValue->set_active( SC_VALIDDLG_DATA_EQUAL );
+
+ SelectHdl( *m_xLbAllow );
+ CheckHdl( *m_xCbShow );
+}
+
+std::unique_ptr<SfxTabPage> ScTPValidationValue::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rArgSet)
+{
+ return std::make_unique<ScTPValidationValue>(pPage, pController, *rArgSet);
+}
+
+void ScTPValidationValue::Reset( const SfxItemSet* rArgSet )
+{
+ sal_uInt16 nLbPos = SC_VALIDDLG_ALLOW_ANY;
+ if( const SfxUInt16Item* pItem = rArgSet->GetItemIfSet( FID_VALID_MODE ) )
+ nLbPos = lclGetPosFromValMode( static_cast< ScValidationMode >( pItem->GetValue() ) );
+ m_xLbAllow->set_active( nLbPos );
+
+ nLbPos = SC_VALIDDLG_DATA_EQUAL;
+ if( const SfxUInt16Item* pItem = rArgSet->GetItemIfSet( FID_VALID_CONDMODE ) )
+ nLbPos = lclGetPosFromCondMode( static_cast< ScConditionMode >( pItem->GetValue() ) );
+ m_xLbValue->set_active( nLbPos );
+
+ // *** check boxes ***
+ bool bCheck = true;
+ if( const SfxBoolItem* pItem = rArgSet->GetItemIfSet( FID_VALID_BLANK ) )
+ bCheck = pItem->GetValue();
+ m_xCbAllow->set_active( bCheck );
+
+ sal_Int32 nListType = ValidListType::UNSORTED;
+ if( const SfxInt16Item* pItem = rArgSet->GetItemIfSet( FID_VALID_LISTTYPE ) )
+ nListType = pItem->GetValue();
+ m_xCbShow->set_active( nListType != ValidListType::INVISIBLE );
+ m_xCbSort->set_active( nListType == ValidListType::SORTEDASCENDING );
+
+ // *** formulas ***
+ OUString aFmlaStr;
+ if ( const SfxStringItem* pItem = rArgSet->GetItemIfSet( FID_VALID_VALUE1 ) )
+ aFmlaStr = pItem->GetValue();
+ SetFirstFormula( aFmlaStr );
+
+ aFmlaStr.clear();
+ if ( const SfxStringItem* pItem = rArgSet->GetItemIfSet( FID_VALID_VALUE2 ) )
+ aFmlaStr = pItem->GetValue();
+ SetSecondFormula( aFmlaStr );
+
+ SelectHdl( *m_xLbAllow );
+ CheckHdl( *m_xCbShow );
+}
+
+bool ScTPValidationValue::FillItemSet( SfxItemSet* rArgSet )
+{
+ sal_Int16 nListType = m_xCbShow->get_active() ?
+ (m_xCbSort->get_active() ? ValidListType::SORTEDASCENDING : ValidListType::UNSORTED) :
+ ValidListType::INVISIBLE;
+
+ const sal_Int32 nLbPos = m_xLbAllow->get_active();
+ bool bCustom = (nLbPos == SC_VALIDDLG_ALLOW_CUSTOM);
+ ScConditionMode eCondMode = bCustom ?
+ ScConditionMode::Direct : lclGetCondModeFromPos( m_xLbValue->get_active() );
+
+ rArgSet->Put( SfxUInt16Item( FID_VALID_MODE, sal::static_int_cast<sal_uInt16>(
+ lclGetValModeFromPos( nLbPos ) ) ) );
+ rArgSet->Put( SfxUInt16Item( FID_VALID_CONDMODE, sal::static_int_cast<sal_uInt16>( eCondMode ) ) );
+ rArgSet->Put( SfxStringItem( FID_VALID_VALUE1, GetFirstFormula() ) );
+ rArgSet->Put( SfxStringItem( FID_VALID_VALUE2, GetSecondFormula() ) );
+ rArgSet->Put( SfxBoolItem( FID_VALID_BLANK, m_xCbAllow->get_active() ) );
+ rArgSet->Put( SfxInt16Item( FID_VALID_LISTTYPE, nListType ) );
+ return true;
+}
+
+OUString ScTPValidationValue::GetFirstFormula() const
+{
+ OUString aFmlaStr;
+ if( m_xLbAllow->get_active() == SC_VALIDDLG_ALLOW_LIST )
+ lclGetFormulaFromStringList( aFmlaStr, m_xEdList->get_text(), mcFmlaSep );
+ else
+ aFmlaStr = m_xEdMin->GetText();
+ return aFmlaStr;
+}
+
+OUString ScTPValidationValue::GetSecondFormula() const
+{
+ return m_xEdMax->GetText();
+}
+
+void ScTPValidationValue::SetFirstFormula( const OUString& rFmlaStr )
+{
+ // try if formula is a string list, validation mode must already be set
+ OUString aStringList;
+ if( (m_xLbAllow->get_active() == SC_VALIDDLG_ALLOW_RANGE) &&
+ lclGetStringListFromFormula( aStringList, rFmlaStr, mcFmlaSep ) )
+ {
+ m_xEdList->set_text( aStringList );
+ m_xEdMin->SetText( OUString() );
+ // change validation mode to string list
+ m_xLbAllow->set_active( SC_VALIDDLG_ALLOW_LIST );
+ }
+ else
+ {
+ m_xEdMin->SetText( rFmlaStr );
+ m_xEdList->set_text( OUString() );
+ }
+}
+
+void ScTPValidationValue::SetSecondFormula( const OUString& rFmlaStr )
+{
+ m_xEdMax->SetText( rFmlaStr );
+}
+
+ScValidationDlg * ScTPValidationValue::GetValidationDlg()
+{
+ return dynamic_cast<ScValidationDlg*>(GetDialogController());
+}
+
+void ScTPValidationValue::SetupRefDlg()
+{
+ ScValidationDlg *pValidationDlg = GetValidationDlg();
+ if( !pValidationDlg )
+ return;
+
+ if( !pValidationDlg->SetupRefDlg() )
+ return;
+
+ pValidationDlg->SetHandler( this );
+ pValidationDlg->SetSetRefHdl( static_cast<ScRefHandlerHelper::PFUNCSETREFHDLTYPE>( &ScTPValidationValue::SetReferenceHdl ) );
+ pValidationDlg->SetSetActHdl( static_cast<ScRefHandlerHelper::PCOMMONHDLTYPE>( &ScTPValidationValue::SetActiveHdl ) );
+ pValidationDlg->SetRefInputStartPreHdl( static_cast<ScRefHandlerHelper::PINPUTSTARTDLTYPE>( &ScTPValidationValue::RefInputStartPreHdl ) );
+ pValidationDlg->SetRefInputDonePostHdl( static_cast<ScRefHandlerHelper::PCOMMONHDLTYPE>( &ScTPValidationValue::RefInputDonePostHdl ) );
+
+ weld::Label* pLabel = nullptr;
+
+ if (m_xEdMax->GetWidget()->get_visible())
+ {
+ m_pRefEdit = m_xEdMax.get();
+ pLabel = m_xFtMax.get();
+ }
+ else if (m_xEdMin->GetWidget()->get_visible())
+ {
+ m_pRefEdit = m_xEdMin.get();
+ pLabel = m_xFtMin.get();
+ }
+
+ if (m_pRefEdit && !m_pRefEdit->GetWidget()->has_focus())
+ m_pRefEdit->GrabFocus();
+
+ if( m_pRefEdit )
+ m_pRefEdit->SetReferences( pValidationDlg, pLabel );
+
+ m_xBtnRef->SetReferences( pValidationDlg, m_pRefEdit );
+}
+
+void ScTPValidationValue::RemoveRefDlg(bool bRestoreModal)
+{
+ ScValidationDlg *pValidationDlg = GetValidationDlg();
+ if( !pValidationDlg )
+ return;
+
+ if( !pValidationDlg->RemoveRefDlg(bRestoreModal) )
+ return;
+
+ pValidationDlg->SetHandler( nullptr );
+ pValidationDlg->SetSetRefHdl( nullptr );
+ pValidationDlg->SetSetActHdl( nullptr );
+ pValidationDlg->SetRefInputStartPreHdl( nullptr );
+ pValidationDlg->SetRefInputDonePostHdl( nullptr );
+
+ if( m_pRefEdit )
+ m_pRefEdit->SetReferences( nullptr, nullptr );
+ m_pRefEdit = nullptr;
+
+ m_xBtnRef->SetReferences( nullptr, nullptr );
+}
+
+IMPL_LINK_NOARG(ScTPValidationValue, EditSetFocusHdl, formula::RefEdit&, void)
+{
+ const sal_Int32 nPos = m_xLbAllow->get_active();
+
+ if ( nPos == SC_VALIDDLG_ALLOW_RANGE )
+ {
+ SetupRefDlg();
+ }
+}
+
+IMPL_LINK( ScTPValidationValue, KillEditFocusHdl, formula::RefEdit&, rWnd, void )
+{
+ if (&rWnd != m_pRefEdit)
+ return;
+ if( ScValidationDlg *pValidationDlg = GetValidationDlg() )
+ {
+ if (pValidationDlg->IsChildFocus() && !pValidationDlg->IsRefInputting())
+ {
+ if( ( !m_pRefEdit || !m_pRefEdit->GetWidget()->has_focus()) && !m_xBtnRef->GetWidget()->has_focus() )
+ {
+ RemoveRefDlg(true);
+ }
+ }
+ }
+}
+
+IMPL_LINK( ScTPValidationValue, KillButtonFocusHdl, formula::RefButton&, rWnd, void )
+{
+ if( &rWnd != m_xBtnRef.get())
+ return;
+ if( ScValidationDlg *pValidationDlg = GetValidationDlg() )
+ if (pValidationDlg->IsChildFocus() && !pValidationDlg->IsRefInputting())
+ if( ( !m_pRefEdit || !m_pRefEdit->GetWidget()->has_focus()) && !m_xBtnRef->GetWidget()->has_focus() )
+ {
+ RemoveRefDlg(true);
+ }
+}
+
+IMPL_LINK_NOARG(ScTPValidationValue, SelectHdl, weld::ComboBox&, void)
+{
+ const sal_Int32 nLbPos = m_xLbAllow->get_active();
+ bool bEnable = (nLbPos != SC_VALIDDLG_ALLOW_ANY);
+ bool bRange = (nLbPos == SC_VALIDDLG_ALLOW_RANGE);
+ bool bList = (nLbPos == SC_VALIDDLG_ALLOW_LIST);
+ bool bCustom = (nLbPos == SC_VALIDDLG_ALLOW_CUSTOM);
+
+ m_xCbAllow->set_sensitive( bEnable ); // Empty cell
+ m_xFtValue->set_sensitive( bEnable );
+ m_xLbValue->set_sensitive( bEnable );
+ m_xFtMin->set_sensitive( bEnable );
+ m_xEdMin->GetWidget()->set_sensitive( bEnable );
+ m_xEdList->set_sensitive( bEnable );
+ m_xFtMax->set_sensitive( bEnable );
+ m_xEdMax->GetWidget()->set_sensitive( bEnable );
+
+ bool bShowMax = false;
+
+ if( bRange )
+ m_xFtMin->set_label( maStrRange );
+ else if( bList )
+ m_xFtMin->set_label( maStrList );
+ else if( bCustom )
+ m_xFtMin->set_label( maStrFormula );
+ else
+ {
+ switch( m_xLbValue->get_active() )
+ {
+ case SC_VALIDDLG_DATA_EQUAL:
+ case SC_VALIDDLG_DATA_NOTEQUAL: m_xFtMin->set_label( maStrValue ); break;
+
+ case SC_VALIDDLG_DATA_LESS:
+ case SC_VALIDDLG_DATA_EQLESS: m_xFtMin->set_label( maStrMax ); break;
+
+ case SC_VALIDDLG_DATA_VALIDRANGE:
+ case SC_VALIDDLG_DATA_INVALIDRANGE: bShowMax = true;
+ [[fallthrough]];
+ case SC_VALIDDLG_DATA_GREATER:
+ case SC_VALIDDLG_DATA_EQGREATER: m_xFtMin->set_label( maStrMin ); break;
+
+ default:
+ OSL_FAIL( "ScTPValidationValue::SelectHdl - unknown condition mode" );
+ }
+ }
+
+ m_xCbShow->set_visible( bRange || bList );
+ m_xCbSort->set_visible( bRange || bList );
+ m_xFtValue->set_visible( !bRange && !bList && !bCustom);
+ m_xLbValue->set_visible( !bRange && !bList && !bCustom );
+ m_xEdMin->GetWidget()->set_visible( !bList );
+ m_xEdList->set_visible( bList );
+ m_xMinGrid->set_vexpand( bList );
+ m_xFtMax->set_visible( bShowMax );
+ m_xEdMax->GetWidget()->set_visible( bShowMax );
+ m_xFtHint->set_visible( bRange );
+ m_xBtnRef->GetWidget()->set_visible( bRange ); // cell range picker
+}
+
+IMPL_LINK_NOARG(ScTPValidationValue, CheckHdl, weld::Toggleable&, void)
+{
+ m_xCbSort->set_sensitive( m_xCbShow->get_active() );
+}
+
+// Input Help Page
+
+ScTPValidationHelp::ScTPValidationHelp(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rArgSet)
+ : SfxTabPage(pPage, pController, IS_MOBILE ? OUString("modules/scalc/ui/validationhelptabpage-mobile.ui")
+ : OUString("modules/scalc/ui/validationhelptabpage.ui"), "ValidationHelpTabPage", &rArgSet)
+ , m_xTsbHelp(m_xBuilder->weld_check_button("tsbhelp"))
+ , m_xEdtTitle(m_xBuilder->weld_entry("title"))
+ , m_xEdInputHelp(m_xBuilder->weld_text_view("inputhelp"))
+{
+ m_xEdInputHelp->set_size_request(m_xEdInputHelp->get_approximate_digit_width() * 40, m_xEdInputHelp->get_height_rows(13));
+}
+
+ScTPValidationHelp::~ScTPValidationHelp()
+{
+}
+
+std::unique_ptr<SfxTabPage> ScTPValidationHelp::Create(weld::Container* pPage, weld::DialogController* pController,
+ const SfxItemSet* rArgSet)
+{
+ return std::make_unique<ScTPValidationHelp>(pPage, pController, *rArgSet);
+}
+
+void ScTPValidationHelp::Reset( const SfxItemSet* rArgSet )
+{
+ if ( const SfxBoolItem* pItem = rArgSet->GetItemIfSet( FID_VALID_SHOWHELP ) )
+ m_xTsbHelp->set_state( pItem->GetValue() ? TRISTATE_TRUE : TRISTATE_FALSE );
+ else
+ m_xTsbHelp->set_state( TRISTATE_FALSE );
+
+ if ( const SfxStringItem* pItem = rArgSet->GetItemIfSet( FID_VALID_HELPTITLE ) )
+ m_xEdtTitle->set_text( pItem->GetValue() );
+ else
+ m_xEdtTitle->set_text( OUString() );
+
+ if ( const SfxStringItem* pItem = rArgSet->GetItemIfSet( FID_VALID_HELPTEXT ) )
+ m_xEdInputHelp->set_text( pItem->GetValue() );
+ else
+ m_xEdInputHelp->set_text( OUString() );
+}
+
+bool ScTPValidationHelp::FillItemSet( SfxItemSet* rArgSet )
+{
+ rArgSet->Put( SfxBoolItem( FID_VALID_SHOWHELP, m_xTsbHelp->get_state() == TRISTATE_TRUE ) );
+ rArgSet->Put( SfxStringItem( FID_VALID_HELPTITLE, m_xEdtTitle->get_text() ) );
+ rArgSet->Put( SfxStringItem( FID_VALID_HELPTEXT, m_xEdInputHelp->get_text() ) );
+
+ return true;
+}
+
+// Error Alert Page
+
+ScTPValidationError::ScTPValidationError(weld::Container* pPage, weld::DialogController* pController,
+ const SfxItemSet& rArgSet)
+
+ : SfxTabPage ( pPage, pController,
+ IS_MOBILE ? OUString("modules/scalc/ui/erroralerttabpage-mobile.ui")
+ : OUString("modules/scalc/ui/erroralerttabpage.ui"), "ErrorAlertTabPage",
+ &rArgSet )
+ , m_xTsbShow(m_xBuilder->weld_check_button("tsbshow"))
+ , m_xLbAction(m_xBuilder->weld_combo_box("actionCB"))
+ , m_xBtnSearch(m_xBuilder->weld_button("browseBtn"))
+ , m_xEdtTitle(m_xBuilder->weld_entry("erroralert_title"))
+ , m_xFtError(m_xBuilder->weld_label("errormsg_label"))
+ , m_xEdError(m_xBuilder->weld_text_view("errorMsg"))
+{
+ m_xEdError->set_size_request(m_xEdError->get_approximate_digit_width() * 40, m_xEdError->get_height_rows(12));
+ Init();
+}
+
+ScTPValidationError::~ScTPValidationError()
+{
+}
+
+void ScTPValidationError::Init()
+{
+ m_xLbAction->connect_changed(LINK(this, ScTPValidationError, SelectActionHdl));
+ m_xBtnSearch->connect_clicked(LINK( this, ScTPValidationError, ClickSearchHdl));
+
+ m_xLbAction->set_active(0);
+
+ SelectActionHdl(*m_xLbAction);
+}
+
+std::unique_ptr<SfxTabPage> ScTPValidationError::Create(weld::Container* pPage, weld::DialogController* pController,
+ const SfxItemSet* rArgSet)
+{
+ return std::make_unique<ScTPValidationError>(pPage, pController, *rArgSet);
+}
+
+void ScTPValidationError::Reset( const SfxItemSet* rArgSet )
+{
+ if ( const SfxBoolItem* pItem = rArgSet->GetItemIfSet( FID_VALID_SHOWERR ) )
+ m_xTsbShow->set_state( pItem->GetValue() ? TRISTATE_TRUE : TRISTATE_FALSE );
+ else
+ m_xTsbShow->set_state( TRISTATE_TRUE ); // check by default
+
+ if ( const SfxUInt16Item* pItem = rArgSet->GetItemIfSet( FID_VALID_ERRSTYLE ) )
+ m_xLbAction->set_active( pItem->GetValue() );
+ else
+ m_xLbAction->set_active( 0 );
+
+ if ( const SfxStringItem* pItem = rArgSet->GetItemIfSet( FID_VALID_ERRTITLE ) )
+ m_xEdtTitle->set_text( pItem->GetValue() );
+ else
+ m_xEdtTitle->set_text( OUString() );
+
+ if ( const SfxStringItem* pItem = rArgSet->GetItemIfSet( FID_VALID_ERRTEXT ) )
+ m_xEdError->set_text( pItem->GetValue() );
+ else
+ m_xEdError->set_text( OUString() );
+
+ SelectActionHdl(*m_xLbAction);
+}
+
+bool ScTPValidationError::FillItemSet( SfxItemSet* rArgSet )
+{
+ rArgSet->Put( SfxBoolItem( FID_VALID_SHOWERR, m_xTsbShow->get_state() == TRISTATE_TRUE ) );
+ rArgSet->Put( SfxUInt16Item( FID_VALID_ERRSTYLE, m_xLbAction->get_active() ) );
+ rArgSet->Put( SfxStringItem( FID_VALID_ERRTITLE, m_xEdtTitle->get_text() ) );
+ rArgSet->Put( SfxStringItem( FID_VALID_ERRTEXT, m_xEdError->get_text() ) );
+
+ return true;
+}
+
+IMPL_LINK_NOARG(ScTPValidationError, SelectActionHdl, weld::ComboBox&, void)
+{
+ ScValidErrorStyle eStyle = static_cast<ScValidErrorStyle>(m_xLbAction->get_active());
+ bool bMacro = ( eStyle == SC_VALERR_MACRO );
+
+ m_xBtnSearch->set_sensitive( bMacro );
+ m_xFtError->set_sensitive( !bMacro );
+ m_xEdError->set_sensitive( !bMacro );
+}
+
+IMPL_LINK_NOARG(ScTPValidationError, ClickSearchHdl, weld::Button&, void)
+{
+ // Use static SfxApplication method to bring up selector dialog for
+ // choosing a script
+ OUString aScriptURL = SfxApplication::ChooseScript(GetFrameWeld());
+
+ if ( !aScriptURL.isEmpty() )
+ {
+ m_xEdtTitle->set_text( aScriptURL );
+ }
+}
+
+bool ScValidationDlg::EnterRefStatus()
+{
+ ScTabViewShell *pTabViewShell = GetTabViewShell();
+
+ if( !pTabViewShell ) return false;
+
+ sal_uInt16 nId = SLOTID;
+ SfxViewFrame* pViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = pViewFrm->GetChildWindow( nId );
+
+ if (pWnd && pWnd->GetController().get() != this) pWnd = nullptr;
+
+ SC_MOD()->SetRefDialog( nId, pWnd == nullptr );
+
+ return true;
+}
+
+bool ScValidationDlg::LeaveRefStatus()
+{
+ ScTabViewShell *pTabViewShell = GetTabViewShell();
+
+ if( !pTabViewShell ) return false;
+
+ sal_uInt16 nId = SLOTID;
+ SfxViewFrame* pViewFrm = pTabViewShell->GetViewFrame();
+ if ( pViewFrm->GetChildWindow( nId ) )
+ {
+ DoClose( nId );
+ }
+ return true;
+}
+
+bool ScValidationDlg::SetupRefDlg()
+{
+ if ( m_bOwnRefHdlr ) return false;
+ if( EnterRefMode() )
+ {
+ SetModal( false );
+ m_bOwnRefHdlr = true;
+ return EnterRefStatus();
+ }
+
+ return false;
+}
+
+bool ScValidationDlg::RemoveRefDlg( bool bRestoreModal /* = true */ )
+{
+ bool bVisLock = false;
+ bool bFreeWindowLock = false;
+
+ ScTabViewShell *pTabVwSh = GetTabViewShell();
+
+ if( !pTabVwSh ) return false;
+
+ if ( SfxChildWindow* pWnd = pTabVwSh->GetViewFrame()->GetChildWindow( SID_VALIDITY_REFERENCE ) )
+ {
+ bVisLock = static_cast<ScValidityRefChildWin*>(pWnd)->LockVisible( true );
+ bFreeWindowLock = static_cast<ScValidityRefChildWin*>(pWnd)->LockFreeWindow( true );
+ }
+
+ if ( !m_bOwnRefHdlr ) return false;
+ if( LeaveRefStatus() && LeaveRefMode() )
+ {
+ m_bOwnRefHdlr = false;
+
+ if( bRestoreModal )
+ {
+ SetModal( true );
+ }
+ }
+
+ if ( SfxChildWindow* pWnd = pTabVwSh->GetViewFrame()->GetChildWindow( SID_VALIDITY_REFERENCE ) )
+ {
+ static_cast<ScValidityRefChildWin*>(pWnd)->LockVisible( bVisLock );
+ static_cast<ScValidityRefChildWin*>(pWnd)->LockFreeWindow( bFreeWindowLock );
+ }
+
+ return true;
+}
+
+IMPL_LINK_NOARG(ScTPValidationValue, ClickHdl, formula::RefButton&, void)
+{
+ SetupRefDlg();
+}
+
+bool ScValidationDlg::IsChildFocus() const
+{
+ return m_xDialog->has_toplevel_focus();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */