/* -*- 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 <rangelst.hxx>
#include <o3tl/string_view.hxx>
#include <sfx2/dispatch.hxx>
#include <svl/stritem.hxx>
#include <vcl/svapp.hxx>
#include <vcl/weld.hxx>
#include <unotools/charclass.hxx>
#include <areasdlg.hxx>
#include <rangenam.hxx>
#include <reffact.hxx>
#include <tabvwsh.hxx>
#include <docsh.hxx>
#include <globstr.hrc>
#include <scresid.hxx>
#include <compiler.hxx>
#include <markdata.hxx>
// List box positions for print range (PR)
enum {
SC_AREASDLG_PR_ENTIRE = 1,
SC_AREASDLG_PR_USER = 2,
SC_AREASDLG_PR_SELECT = 3
};
// List box positions for repeat ranges (RR)
enum {
SC_AREASDLG_RR_NONE = 0,
SC_AREASDLG_RR_USER = 1,
SC_AREASDLG_RR_OFFSET = 2
};
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();
}
}
// global functions (->at the end of the file):
static bool lcl_CheckRepeatString( std::u16string_view aStr, const ScDocument& rDoc, bool bIsRow, ScRange* pRange );
static void lcl_GetRepeatRangeString( const std::optional<ScRange>& oRange, const ScDocument& rDoc, bool bIsRow, OUString& rStr );
#if 0
// this method is useful when debugging address flags.
static void printAddressFlags(ScRefFlags nFlag)
{
if ((nFlag & ScRefFlags::COL_ABS ) == ScRefFlags::COL_ABS ) printf("ScRefFlags::COL_ABS \n");
if ((nFlag & ScRefFlags::ROW_ABS ) == ScRefFlags::ROW_ABS ) printf("ScRefFlags::ROW_ABS \n");
if ((nFlag & ScRefFlags::TAB_ABS ) == ScRefFlags::TAB_ABS ) printf("ScRefFlags::TAB_ABS \n");
if ((nFlag & ScRefFlags::TAB_3D ) == ScRefFlags::TAB_3D ) printf("ScRefFlags::TAB_3D \n");
if ((nFlag & ScRefFlags::COL2_ABS ) == ScRefFlags::COL2_ABS ) printf("ScRefFlags::COL2_ABS \n");
if ((nFlag & ScRefFlags::ROW2_ABS ) == ScRefFlags::ROW2_ABS ) printf("ScRefFlags::ROW2_ABS \n");
if ((nFlag & ScRefFlags::TAB2_ABS ) == ScRefFlags::TAB2_ABS ) printf("ScRefFlags::TAB2_ABS \n");
if ((nFlag & ScRefFlags::TAB2_3D ) == ScRefFlags::TAB2_3D ) printf("ScRefFlags::TAB2_3D \n");
if ((nFlag & ScRefFlags::ROW_VALID ) == ScRefFlags::ROW_VALID ) printf("ScRefFlags::ROW_VALID \n");
if ((nFlag & ScRefFlags::COL_VALID ) == ScRefFlags::COL_VALID ) printf("ScRefFlags::COL_VALID \n");
if ((nFlag & ScRefFlags::TAB_VALID ) == ScRefFlags::TAB_VALID ) printf("ScRefFlags::TAB_VALID \n");
if ((nFlag & ScRefFlags::FORCE_DOC ) == ScRefFlags::FORCE_DOC ) printf("ScRefFlags::FORCE_DOC \n");
if ((nFlag & ScRefFlags::ROW2_VALID ) == ScRefFlags::ROW2_VALID ) printf("ScRefFlags::ROW2_VALID \n");
if ((nFlag & ScRefFlags::COL2_VALID ) == ScRefFlags::COL2_VALID ) printf("ScRefFlags::COL2_VALID \n");
if ((nFlag & ScRefFlags::TAB2_VALID ) == ScRefFlags::TAB2_VALID ) printf("ScRefFlags::TAB2_VALID \n");
if ((nFlag & ScRefFlags::VALID ) == ScRefFlags::VALID ) printf("ScRefFlags::VALID \n");
if ((nFlag & ScRefFlags::ADDR_ABS ) == ScRefFlags::ADDR_ABS ) printf("ScRefFlags::ADDR_ABS \n");
if ((nFlag & ScRefFlags::RANGE_ABS ) == ScRefFlags::RANGE_ABS ) printf("ScRefFlags::RANGE_ABS \n");
if ((nFlag & ScRefFlags::ADDR_ABS_3D ) == ScRefFlags::ADDR_ABS_3D ) printf("ScRefFlags::ADDR_ABS_3D \n");
if ((nFlag & ScRefFlags::RANGE_ABS_3D ) == ScRefFlags::RANGE_ABS_3D ) printf("ScRefFlags::RANGE_ABS_3D \n");
}
#endif
ScPrintAreasDlg::ScPrintAreasDlg(SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent)
: ScAnyRefDlgController(pB, pCW, pParent, u"modules/scalc/ui/printareasdialog.ui"_ustr, u"PrintAreasDialog"_ustr)
, bDlgLostFocus(false)
, pDoc(nullptr)
, pViewData(nullptr)
, nCurTab(0)
, m_xLbPrintArea(m_xBuilder->weld_combo_box(u"lbprintarea"_ustr))
, m_xEdPrintArea(new formula::RefEdit(m_xBuilder->weld_entry(u"edprintarea"_ustr)))
, m_xRbPrintArea(new formula::RefButton(m_xBuilder->weld_button(u"rbprintarea"_ustr)))
, m_xLbRepeatRow(m_xBuilder->weld_combo_box(u"lbrepeatrow"_ustr))
, m_xEdRepeatRow(new formula::RefEdit(m_xBuilder->weld_entry(u"edrepeatrow"_ustr)))
, m_xRbRepeatRow(new formula::RefButton(m_xBuilder->weld_button(u"rbrepeatrow"_ustr)))
, m_xLbRepeatCol(m_xBuilder->weld_combo_box(u"lbrepeatcol"_ustr))
, m_xEdRepeatCol(new formula::RefEdit(m_xBuilder->weld_entry(u"edrepeatcol"_ustr)))
, m_xRbRepeatCol(new formula::RefButton(m_xBuilder->weld_button(u"rbrepeatcol"_ustr)))
, m_xBtnOk(m_xBuilder->weld_button(u"ok"_ustr))
, m_xBtnCancel(m_xBuilder->weld_button(u"cancel"_ustr))
, m_xPrintFrame(m_xBuilder->weld_frame(u"printframe"_ustr))
, m_xRowFrame(m_xBuilder->weld_frame(u"rowframe"_ustr))
, m_xColFrame(m_xBuilder->weld_frame(u"colframe"_ustr))
, m_xPrintFrameFT(m_xPrintFrame->weld_label_widget())
, m_xRowFrameFT(m_xRowFrame->weld_label_widget())
, m_xColFrameFT(m_xColFrame->weld_label_widget())
{
m_xEdPrintArea->SetReferences(this, m_xPrintFrameFT.get());
m_pRefInputEdit = m_xEdPrintArea.get();
m_xRbPrintArea->SetReferences(this, m_xEdPrintArea.get());
m_xEdRepeatRow->SetReferences(this, m_xRowFrameFT.get());
m_xRbRepeatRow->SetReferences(this, m_xEdRepeatRow.get());
m_xEdRepeatCol->SetReferences(this, m_xColFrameFT.get());
m_xRbRepeatCol->SetReferences(this, m_xEdRepeatCol.get());
ScTabViewShell* pScViewSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() );
ScDocShell* pScDocSh = dynamic_cast<ScDocShell*>(SfxObjectShell::Current());
assert(pScDocSh && "Current DocumentShell not found :-(");
pDoc = &pScDocSh->GetDocument();
if ( pScViewSh )
{
pViewData = &pScViewSh->GetViewData();
nCurTab = pViewData->GetTabNo();
}
Impl_Reset();
//@BugID 54702 Enable/Disable only in base class
//SFX_APPWINDOW->Enable();
}
ScPrintAreasDlg::~ScPrintAreasDlg()
{
}
void ScPrintAreasDlg::Close()
{
DoClose( ScPrintAreasDlgWrapper::GetChildWindowId() );
}
bool ScPrintAreasDlg::IsTableLocked() const
{
// Printing areas are per table, therefore it makes no sense,
// to switch the table during input
return true;
}
void ScPrintAreasDlg::SetReference( const ScRange& rRef, ScDocument& /* rDoc */ )
{
if ( !m_pRefInputEdit )
return;
if ( rRef.aStart != rRef.aEnd )
RefInputStart( m_pRefInputEdit );
OUString aStr;
const formula::FormulaGrammar::AddressConvention eConv = pDoc->GetAddressConvention();
if (m_xEdPrintArea.get() == m_pRefInputEdit)
{
aStr = rRef.Format(*pDoc, ScRefFlags::RANGE_ABS, eConv);
OUString aVal = m_xEdPrintArea->GetText();
Selection aSel = m_xEdPrintArea->GetSelection();
aSel.Normalize();
aVal = aVal.replaceAt( aSel.Min(), aSel.Len(), aStr );
Selection aNewSel( aSel.Min(), aSel.Min()+aStr.getLength() );
m_xEdPrintArea->SetRefString( aVal );
m_xEdPrintArea->SetSelection( aNewSel );
}
else
{
bool bRow = ( m_xEdRepeatRow.get() == m_pRefInputEdit );
lcl_GetRepeatRangeString(rRef, *pDoc, bRow, aStr);
m_pRefInputEdit->SetRefString( aStr );
}
Impl_ModifyHdl( *m_pRefInputEdit );
}
void ScPrintAreasDlg::AddRefEntry()
{
if (m_pRefInputEdit == m_xEdPrintArea.get())
{
const sal_Unicode sep = ScCompiler::GetNativeSymbolChar(ocSep);
OUString aVal = m_xEdPrintArea->GetText() + OUStringChar(sep);
m_xEdPrintArea->SetText(aVal);
sal_Int32 nLen = aVal.getLength();
m_xEdPrintArea->SetSelection( Selection( nLen, nLen ) );
Impl_ModifyHdl( *m_xEdPrintArea );
}
}
void ScPrintAreasDlg::Deactivate()
{
bDlgLostFocus = true;
}
void ScPrintAreasDlg::SetActive()
{
if ( bDlgLostFocus )
{
bDlgLostFocus = false;
if ( m_pRefInputEdit )
{
m_pRefInputEdit->GrabFocus();
Impl_ModifyHdl( *m_pRefInputEdit );
}
}
else
m_xDialog->grab_focus();
RefInputDone();
}
void ScPrintAreasDlg::Impl_Reset()
{
OUString aStrRange;
std::optional<ScRange> oRepeatColRange = pDoc->GetRepeatColRange( nCurTab );
std::optional<ScRange> oRepeatRowRange = pDoc->GetRepeatRowRange( nCurTab );
m_xEdPrintArea->SetModifyHdl (LINK( this, ScPrintAreasDlg, Impl_ModifyHdl));
m_xEdRepeatRow->SetModifyHdl (LINK( this, ScPrintAreasDlg, Impl_ModifyHdl));
m_xEdRepeatCol->SetModifyHdl (LINK( this, ScPrintAreasDlg, Impl_ModifyHdl));
m_xEdPrintArea->SetGetFocusHdl(LINK( this, ScPrintAreasDlg, Impl_GetEditFocusHdl));
m_xEdRepeatRow->SetGetFocusHdl(LINK( this, ScPrintAreasDlg, Impl_GetEditFocusHdl));
m_xEdRepeatCol->SetGetFocusHdl(LINK( this, ScPrintAreasDlg, Impl_GetEditFocusHdl));
m_xLbPrintArea->connect_focus_in(LINK( this, ScPrintAreasDlg, Impl_GetFocusHdl));
m_xLbRepeatRow->connect_focus_in(LINK( this, ScPrintAreasDlg, Impl_GetFocusHdl));
m_xLbRepeatCol->connect_focus_in(LINK( this, ScPrintAreasDlg, Impl_GetFocusHdl));
m_xLbPrintArea->connect_changed(LINK( this, ScPrintAreasDlg, Impl_SelectHdl));
m_xLbRepeatRow->connect_changed(LINK( this, ScPrintAreasDlg, Impl_SelectHdl));
m_xLbRepeatCol->connect_changed(LINK( this, ScPrintAreasDlg, Impl_SelectHdl));
m_xBtnOk->connect_clicked(LINK( this, ScPrintAreasDlg, Impl_BtnHdl));
m_xBtnCancel->connect_clicked(LINK( this, ScPrintAreasDlg, Impl_BtnHdl));
Impl_FillLists();
// printing area
aStrRange.clear();
const formula::FormulaGrammar::AddressConvention eConv = pDoc->GetAddressConvention();
const sal_Unicode sep = ScCompiler::GetNativeSymbolChar(ocSep);
sal_uInt16 nRangeCount = pDoc->GetPrintRangeCount( nCurTab );
for (sal_uInt16 i=0; i<nRangeCount; i++)
{
const ScRange* pPrintRange = pDoc->GetPrintRange( nCurTab, i );
if (pPrintRange)
{
if ( !aStrRange.isEmpty() )
aStrRange += OUStringChar(sep);
aStrRange += pPrintRange->Format(*pDoc, ScRefFlags::RANGE_ABS, eConv);
}
}
m_xEdPrintArea->SetText( aStrRange );
// repeat row
lcl_GetRepeatRangeString(oRepeatRowRange, *pDoc, true, aStrRange);
m_xEdRepeatRow->SetText( aStrRange );
// repeat column
lcl_GetRepeatRangeString(oRepeatColRange, *pDoc, false, aStrRange);
m_xEdRepeatCol->SetText( aStrRange );
Impl_ModifyHdl( *m_xEdPrintArea );
Impl_ModifyHdl( *m_xEdRepeatRow );
Impl_ModifyHdl( *m_xEdRepeatCol );
if( pDoc->IsPrintEntireSheet( nCurTab ) )
m_xLbPrintArea->set_active(SC_AREASDLG_PR_ENTIRE);
m_xEdPrintArea->SaveValue(); // save for FillItemSet():
m_xEdRepeatRow->SaveValue();
m_xEdRepeatCol->SaveValue();
}
bool ScPrintAreasDlg::Impl_GetItem( const formula::RefEdit* pEd, SfxStringItem& rItem )
{
OUString aRangeStr = pEd->GetText();
bool bDataChanged = pEd->IsValueChangedFromSaved();
if ( !aRangeStr.isEmpty() && m_xEdPrintArea.get() != pEd )
{
ScRange aRange;
const formula::FormulaGrammar::AddressConvention eConv = pDoc->GetAddressConvention();
lcl_CheckRepeatString(aRangeStr, *pDoc, m_xEdRepeatRow.get() == pEd, &aRange);
aRangeStr = aRange.Format(*pDoc, ScRefFlags::RANGE_ABS, eConv);
}
rItem.SetValue( aRangeStr );
return bDataChanged;
}
bool ScPrintAreasDlg::Impl_CheckRefStrings()
{
bool bOk = false;
OUString aStrPrintArea = m_xEdPrintArea->GetText();
OUString aStrRepeatRow = m_xEdRepeatRow->GetText();
OUString aStrRepeatCol = m_xEdRepeatCol->GetText();
bool bPrintAreaOk = true;
if ( !aStrPrintArea.isEmpty() )
{
const ScRefFlags nValidAddr = ScRefFlags::VALID | ScRefFlags::ROW_VALID | ScRefFlags::COL_VALID;
const ScRefFlags nValidRange = nValidAddr | ScRefFlags::ROW2_VALID | ScRefFlags::COL2_VALID;
const formula::FormulaGrammar::AddressConvention eConv = pDoc->GetAddressConvention();
const sal_Unicode sep = ScCompiler::GetNativeSymbolChar(ocSep);
ScAddress aAddr;
ScRange aRange;
for ( sal_Int32 nIdx = 0; nIdx >= 0; )
{
const OUString aOne = aStrPrintArea.getToken(0, sep, nIdx);
ScRefFlags nResult = aRange.Parse( aOne, *pDoc, eConv );
if ((nResult & nValidRange) != nValidRange)
{
ScRefFlags nAddrResult = aAddr.Parse( aOne, *pDoc, eConv );
if ((nAddrResult & nValidAddr) != nValidAddr)
{
bPrintAreaOk = false;
break;
}
}
}
}
bool bRepeatRowOk = aStrRepeatRow.isEmpty();
if ( !bRepeatRowOk )
bRepeatRowOk = lcl_CheckRepeatString(aStrRepeatRow, *pDoc, true, nullptr);
bool bRepeatColOk = aStrRepeatCol.isEmpty();
if ( !bRepeatColOk )
bRepeatColOk = lcl_CheckRepeatString(aStrRepeatCol, *pDoc, false, nullptr);
// error messages
bOk = (bPrintAreaOk && bRepeatRowOk && bRepeatColOk);
if ( !bOk )
{
formula::RefEdit* pEd = nullptr;
if ( !bPrintAreaOk ) pEd = m_xEdPrintArea.get();
else if ( !bRepeatRowOk ) pEd = m_xEdRepeatRow.get();
else if ( !bRepeatColOk ) pEd = m_xEdRepeatCol.get();
ERRORBOX(m_xDialog.get(), STR_INVALID_TABREF);
OSL_ASSERT(pEd);
if (pEd)
pEd->GrabFocus();
}
return bOk;
}
void ScPrintAreasDlg::Impl_FillLists()
{
// Get selection and remember String in PrintArea-ListBox
ScRange aRange;
OUString aStrRange;
bool bSimple = true;
if ( pViewData )
bSimple = (pViewData->GetSimpleArea( aRange ) == SC_MARK_SIMPLE);
formula::FormulaGrammar::AddressConvention eConv = pDoc->GetAddressConvention();
if ( bSimple )
aStrRange = aRange.Format(*pDoc, ScRefFlags::RANGE_ABS, eConv);
else
{
ScRangeListRef aList( new ScRangeList );
pViewData->GetMarkData().FillRangeListWithMarks( aList.get(), false );
aList->Format(aStrRange, ScRefFlags::RANGE_ABS, *pDoc, eConv);
}
m_xLbPrintArea->set_id(SC_AREASDLG_PR_SELECT, aStrRange);
// Get ranges and remember in ListBoxen
ScRangeName* pRangeNames = pDoc->GetRangeName();
if (!pRangeNames || pRangeNames->empty())
// No range names to process.
return;
for (const auto& rEntry : *pRangeNames)
{
if (!rEntry.second->HasType(ScRangeData::Type::AbsArea )
&& !rEntry.second->HasType(ScRangeData::Type::RefArea)
&& !rEntry.second->HasType(ScRangeData::Type::AbsPos ))
continue;
OUString aName = rEntry.second->GetName();
OUString aSymbol = rEntry.second->GetSymbol();
if (aRange.ParseAny(aSymbol, *pDoc, eConv) & ScRefFlags::VALID)
{
if (rEntry.second->HasType(ScRangeData::Type::PrintArea))
{
aSymbol = aRange.Format(*pDoc, ScRefFlags::RANGE_ABS, eConv);
m_xLbPrintArea->append(aSymbol, aName);
}
if (rEntry.second->HasType(ScRangeData::Type::RowHeader))
{
lcl_GetRepeatRangeString(aRange, *pDoc, true, aSymbol);
m_xLbRepeatRow->append(aSymbol, aName);
}
if (rEntry.second->HasType(ScRangeData::Type::ColHeader))
{
lcl_GetRepeatRangeString(aRange, *pDoc, false, aSymbol);
m_xLbRepeatCol->append(aSymbol, aName);
}
}
}
}
// Handler:
IMPL_LINK(ScPrintAreasDlg, Impl_BtnHdl, weld::Button&, rBtn, void)
{
if (m_xBtnOk.get() == &rBtn)
{
if ( Impl_CheckRefStrings() )
{
SfxStringItem aPrintArea( SID_CHANGE_PRINTAREA, u""_ustr );
SfxStringItem aRepeatRow( FN_PARAM_2, u""_ustr );
SfxStringItem aRepeatCol( FN_PARAM_3, u""_ustr );
// Printing area changed?
// first try the list box, if "Entire sheet" is selected
bool bEntireSheet = (m_xLbPrintArea->get_active() == SC_AREASDLG_PR_ENTIRE);
SfxBoolItem aEntireSheet( FN_PARAM_4, bEntireSheet );
bool bDataChanged = bEntireSheet != pDoc->IsPrintEntireSheet( nCurTab );
if( !bEntireSheet )
{
// if new list box selection is not "Entire sheet", get the edit field contents
bDataChanged |= Impl_GetItem( m_xEdPrintArea.get(), aPrintArea );
}
// Repeat row changed?
bDataChanged |= Impl_GetItem( m_xEdRepeatRow.get(), aRepeatRow );
// Repeat column changed?
bDataChanged |= Impl_GetItem( m_xEdRepeatCol.get(), aRepeatCol );
if ( bDataChanged )
{
SetDispatcherLock( false );
SwitchToDocument();
GetBindings().GetDispatcher()->ExecuteList(SID_CHANGE_PRINTAREA,
SfxCallMode::SLOT | SfxCallMode::RECORD,
{ &aPrintArea, &aRepeatRow, &aRepeatCol, &aEntireSheet });
}
response(RET_OK);
}
}
else if (m_xBtnCancel.get() == &rBtn)
response(RET_CANCEL);
}
IMPL_LINK(ScPrintAreasDlg, Impl_GetEditFocusHdl, formula::RefEdit&, rCtrl, void)
{
m_pRefInputEdit = &rCtrl;
}
IMPL_LINK(ScPrintAreasDlg, Impl_GetFocusHdl, weld::Widget&, rCtrl, void)
{
if (&rCtrl == m_xLbPrintArea.get())
m_pRefInputEdit = m_xEdPrintArea.get();
else if (&rCtrl == m_xLbRepeatRow.get())
m_pRefInputEdit = m_xEdRepeatRow.get();
else if (&rCtrl == m_xLbRepeatCol.get())
m_pRefInputEdit = m_xEdRepeatCol.get();
}
IMPL_LINK( ScPrintAreasDlg, Impl_SelectHdl, weld::ComboBox&, rLb, void )
{
const sal_Int32 nSelPos = rLb.get_active();
formula::RefEdit* pEd = nullptr;
// list box positions of specific entries, default to "repeat row/column" list boxes
sal_Int32 nAllSheetPos = SC_AREASDLG_RR_NONE;
sal_Int32 nFirstCustomPos = SC_AREASDLG_RR_OFFSET;
// find edit field for list box, and list box positions
if (&rLb == m_xLbPrintArea.get())
{
pEd = m_xEdPrintArea.get();
nAllSheetPos = SC_AREASDLG_PR_ENTIRE;
nFirstCustomPos = SC_AREASDLG_PR_SELECT; // "Selection" and following
}
else if (&rLb == m_xLbRepeatCol.get())
pEd = m_xEdRepeatCol.get();
else if (&rLb == m_xLbRepeatRow.get())
pEd = m_xEdRepeatRow.get();
else
return;
// fill edit field according to list box selection
if( (nSelPos == 0) || (nSelPos == nAllSheetPos) )
pEd->SetText( OUString() );
else if( nSelPos >= nFirstCustomPos )
pEd->SetText(rLb.get_id(nSelPos));
}
IMPL_LINK( ScPrintAreasDlg, Impl_ModifyHdl, formula::RefEdit&, rEd, void )
{
weld::ComboBox* pLb = nullptr;
// list box positions of specific entries, default to "repeat row/column" list boxes
sal_Int32 nUserDefPos = SC_AREASDLG_RR_USER;
sal_Int32 nFirstCustomPos = SC_AREASDLG_RR_OFFSET;
if( &rEd == m_xEdPrintArea.get() )
{
pLb = m_xLbPrintArea.get();
nUserDefPos = SC_AREASDLG_PR_USER;
nFirstCustomPos = SC_AREASDLG_PR_SELECT; // "Selection" and following
}
else if( &rEd == m_xEdRepeatCol.get() )
pLb = m_xLbRepeatCol.get();
else if( &rEd == m_xEdRepeatRow.get() )
pLb = m_xLbRepeatRow.get();
else
return;
// set list box selection according to edit field
const sal_Int32 nEntryCount = pLb->get_count();
OUString aStrEd( rEd.GetText() );
OUString aEdUpper = aStrEd.toAsciiUpperCase();
if ( (nEntryCount > nFirstCustomPos) && !aStrEd.isEmpty() )
{
bool bFound = false;
sal_Int32 i;
for ( i=nFirstCustomPos; i<nEntryCount && !bFound; i++ )
{
const OUString aSymbol = pLb->get_id(i);
bFound = (aSymbol == aStrEd || aSymbol == aEdUpper);
}
pLb->set_active( bFound ? i-1 : nUserDefPos );
}
else
pLb->set_active( !aStrEd.isEmpty() ? nUserDefPos : 0 );
}
// global functions:
// TODO: It might make sense to move these functions to address.?xx. -kohei
static bool lcl_CheckOne_OOO( const ScDocument& rDoc, const OUString& rStr, bool bIsRow, SCCOLROW& rVal )
{
// Allowed syntax for rStr:
// Row: [$]1-MAXTAB
// Col: [$]A-IV
OUString aStr = rStr;
sal_Int32 nLen = aStr.getLength();
SCCOLROW nNum = 0;
bool bStrOk = ( nLen > 0 ) && ( bIsRow ? ( nLen < 6 ) : ( nLen < 4 ) );
if ( bStrOk )
{
if ( '$' == aStr[0] )
aStr = aStr.copy( 1 );
if ( bIsRow )
{
bStrOk = CharClass::isAsciiNumeric(aStr);
if ( bStrOk )
{
sal_Int32 n = aStr.toInt32();
bStrOk = (n > 0) && ( n <= rDoc.GetSheetLimits().GetMaxRowCount() );
if ( bStrOk )
nNum = static_cast<SCCOLROW>(n - 1);
}
}
else
{
SCCOL nCol = 0;
bStrOk = ::AlphaToCol(rDoc, nCol, aStr);
nNum = nCol;
}
}
if ( bStrOk )
rVal = nNum;
return bStrOk;
}
static bool lcl_CheckOne_XL_A1( const ScDocument& rDoc, const OUString& rStr, bool bIsRow, SCCOLROW& rVal )
{
// XL A1 style is identical to OOO one for print range formats.
return lcl_CheckOne_OOO(rDoc, rStr, bIsRow, rVal);
}
static bool lcl_CheckOne_XL_R1C1( const ScDocument& rDoc, std::u16string_view aStr, bool bIsRow, SCCOLROW& rVal )
{
sal_Int32 nLen = aStr.size();
if (nLen <= 1)
// There must be at least two characters.
return false;
const sal_Unicode preUpper = bIsRow ? 'R' : 'C';
const sal_Unicode preLower = bIsRow ? 'r' : 'c';
if (aStr[0] != preUpper && aStr[0] != preLower)
return false;
std::u16string_view aNumStr = aStr.substr(1);
if (!CharClass::isAsciiNumeric(aNumStr))
return false;
sal_Int32 nNum = o3tl::toInt32(aNumStr);
if (nNum <= 0)
return false;
if ((bIsRow && nNum > rDoc.GetSheetLimits().GetMaxRowCount()) ||
(!bIsRow && nNum > rDoc.GetSheetLimits().GetMaxColCount()))
return false;
rVal = static_cast<SCCOLROW>(nNum-1);
return true;
}
static bool lcl_CheckRepeatOne( const ScDocument& rDoc, const OUString& rStr, formula::FormulaGrammar::AddressConvention eConv, bool bIsRow, SCCOLROW& rVal )
{
switch (eConv)
{
case formula::FormulaGrammar::CONV_OOO:
return lcl_CheckOne_OOO(rDoc, rStr, bIsRow, rVal);
case formula::FormulaGrammar::CONV_XL_A1:
return lcl_CheckOne_XL_A1(rDoc, rStr, bIsRow, rVal);
case formula::FormulaGrammar::CONV_XL_R1C1:
return lcl_CheckOne_XL_R1C1(rDoc, rStr, bIsRow, rVal);
default:
{
// added to avoid warnings
}
}
return false;
}
static bool lcl_CheckRepeatString( std::u16string_view aStr, const ScDocument& rDoc, bool bIsRow, ScRange* pRange )
{
// Row: [valid row] rsep [valid row]
// Col: [valid col] rsep [valid col]
const formula::FormulaGrammar::AddressConvention eConv = rDoc.GetAddressConvention();
const sal_Unicode rsep = ScCompiler::GetNativeSymbolChar(ocRange);
if (pRange)
{
// initialize the range value.
pRange->aStart.SetCol(0);
pRange->aStart.SetRow(0);
pRange->aEnd.SetCol(0);
pRange->aEnd.SetRow(0);
}
OUString aBuf;
SCCOLROW nVal = 0;
sal_Int32 nLen = aStr.size();
bool bEndPos = false;
for( sal_Int32 i = 0; i < nLen; ++i )
{
const sal_Unicode c = aStr[i];
if (c == rsep)
{
if (bEndPos)
// We aren't supposed to have more than one range separator.
return false;
// range separator
if (aBuf.isEmpty())
return false;
bool bRes = lcl_CheckRepeatOne(rDoc, aBuf, eConv, bIsRow, nVal);
if (!bRes)
return false;
if (pRange)
{
if (bIsRow)
{
pRange->aStart.SetRow(static_cast<SCROW>(nVal));
pRange->aEnd.SetRow(static_cast<SCROW>(nVal));
}
else
{
pRange->aStart.SetCol(static_cast<SCCOL>(nVal));
pRange->aEnd.SetCol(static_cast<SCCOL>(nVal));
}
}
aBuf.clear();
bEndPos = true;
}
else
aBuf += OUStringChar(c);
}
if (!aBuf.isEmpty())
{
bool bRes = lcl_CheckRepeatOne(rDoc, aBuf, eConv, bIsRow, nVal);
if (!bRes)
return false;
if (pRange)
{
if (bIsRow)
{
if (!bEndPos)
pRange->aStart.SetRow(static_cast<SCROW>(nVal));
pRange->aEnd.SetRow(static_cast<SCROW>(nVal));
}
else
{
if (!bEndPos)
pRange->aStart.SetCol(static_cast<SCCOL>(nVal));
pRange->aEnd.SetCol(static_cast<SCCOL>(nVal));
}
}
}
return true;
}
static void lcl_GetRepeatRangeString( const std::optional<ScRange>& oRange, const ScDocument& rDoc, bool bIsRow, OUString& rStr )
{
rStr.clear();
if (!oRange)
return;
const formula::FormulaGrammar::AddressConvention eConv = rDoc.GetAddressConvention();
const ScAddress& rStart = oRange->aStart;
const ScAddress& rEnd = oRange->aEnd;
const ScRefFlags nFmt = bIsRow
? (ScRefFlags::ROW_VALID | ScRefFlags::ROW_ABS)
: (ScRefFlags::COL_VALID | ScRefFlags::COL_ABS);
rStr += rStart.Format(nFmt, &rDoc, eConv);
if ((bIsRow && rStart.Row() != rEnd.Row()) || (!bIsRow && rStart.Col() != rEnd.Col()))
{
rStr += ScCompiler::GetNativeSymbol(ocRange);
rStr += rEnd.Format(nFmt, &rDoc, eConv);
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V522 There might be dereferencing of a potential null pointer 'pScDocSh'.