/* -*- 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 <subtdlg.hxx>
#include <tpsubt.hxx>
#include <memory>
 
// Subtotals group tabpage:
 
ScTpSubTotalGroup::ScTpSubTotalGroup( vcl::Window* pParent,
                                      const SfxItemSet& rArgSet )
        :   SfxTabPage      ( pParent,
                              "SubTotalGrpPage", "modules/scalc/ui/subtotalgrppage.ui",
                              &rArgSet ),
            aStrNone        ( ScResId( SCSTR_NONE ) ),
            aStrColumn      ( ScResId( SCSTR_COLUMN ) ),
            pViewData       ( nullptr ),
            pDoc            ( nullptr ),
            nWhichSubTotals ( rArgSet.GetPool()->GetWhich( SID_SUBTOTALS ) ),
            rSubTotalData   ( static_cast<const ScSubTotalItem&>(
                              rArgSet.Get( nWhichSubTotals )).
                                GetSubTotalData() ),
            nFieldCount     ( 0 )
{
    get(mpLbGroup, "group_by");
    get(mpLbColumns, "columns");
    get(mpLbFunctions, "functions");
 
    long nHeight = mpLbColumns->GetTextHeight() * 14;
    mpLbColumns->set_height_request(nHeight);
    mpLbFunctions->set_height_request(nHeight);
 
    // Font is correctly initialized by SvTreeListBox ctor
    mpLbColumns->SetSelectionMode( SelectionMode::Single );
    mpLbColumns->SetDragDropMode( DragDropMode::NONE );
    mpLbColumns->SetSpaceBetweenEntries( 0 );
 
    Init ();
}
 
ScTpSubTotalGroup::~ScTpSubTotalGroup()
{
    disposeOnce();
}
 
void ScTpSubTotalGroup::dispose()
{
    sal_uLong  nCount = mpLbColumns->GetEntryCount();
 
    if ( nCount > 0 )
    {
        for ( sal_uLong i=0; i<nCount; i++ )
        {
            sal_uInt16* pData = static_cast<sal_uInt16*>(mpLbColumns->GetEntryData( i ));
            OSL_ENSURE( pData, "EntryData not found" );
 
            delete pData;
        }
    }
    mpLbGroup.clear();
    mpLbColumns.clear();
    mpLbFunctions.clear();
    SfxTabPage::dispose();
}
 
void ScTpSubTotalGroup::Init()
{
    const ScSubTotalItem& rSubTotalItem = static_cast<const ScSubTotalItem&>(
                                          GetItemSet().Get( nWhichSubTotals ));
 
    pViewData   = rSubTotalItem.GetViewData();
    pDoc        = ( pViewData ) ? pViewData->GetDocument() : nullptr;
 
    OSL_ENSURE( pViewData && pDoc, "ViewData or Document not found :-(" );
 
    mpLbGroup->SetSelectHdl       ( LINK( this, ScTpSubTotalGroup, SelectListBoxHdl ) );
    mpLbColumns->SetSelectHdl     ( LINK( this, ScTpSubTotalGroup, SelectTreeListBoxHdl ) );
    mpLbColumns->SetCheckButtonHdl( LINK( this, ScTpSubTotalGroup, CheckHdl ) );
    mpLbFunctions->SetSelectHdl   ( LINK( this, ScTpSubTotalGroup, SelectListBoxHdl ) );
 
    nFieldArr[0] = 0;
    FillListBoxes();
}
 
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 ( sal_uLong nLbEntry = 0; nLbEntry < mpLbColumns->GetEntryCount(); ++nLbEntry )
    {
        mpLbColumns->CheckEntryPos( nLbEntry, false );
        *static_cast<sal_uInt16*>(mpLbColumns->GetEntryData( nLbEntry )) = 0;
    }
    mpLbFunctions->SelectEntryPos( 0 );
 
    ScSubTotalParam theSubTotalData( static_cast<const ScSubTotalItem&>(
                                      rArgSet.Get( nWhichSubTotals )).
                                            GetSubTotalData() );
 
    if ( theSubTotalData.bGroupActive[nGroupIdx] )
    {
        SCCOL           nField      = theSubTotalData.nField[nGroupIdx];
        SCCOL           nSubTotals  = theSubTotalData.nSubTotals[nGroupIdx];
        SCCOL*          pSubTotals  = theSubTotalData.pSubTotals[nGroupIdx];
        ScSubTotalFunc* pFunctions  = theSubTotalData.pFunctions[nGroupIdx];
 
        mpLbGroup->SelectEntryPos( GetFieldSelPos( nField )+1 );
 
        sal_uInt16 nFirstChecked = 0;
        for ( sal_uInt16 i=0; i<nSubTotals; i++ )
        {
            sal_uInt16  nCheckPos = GetFieldSelPos( pSubTotals[i] );
            sal_uInt16* pFunction = static_cast<sal_uInt16*>(mpLbColumns->GetEntryData( nCheckPos ));
 
            mpLbColumns->CheckEntryPos( nCheckPos );
            *pFunction = FuncToLbPos( pFunctions[i] );
 
            if (i == 0 || (i > 0 && nCheckPos < nFirstChecked))
                nFirstChecked = nCheckPos;
        }
        // Select the first checked field from the top.
        mpLbColumns->SelectEntryPos(nFirstChecked);
    }
    else
    {
        mpLbGroup->SelectEntryPos( (nGroupNo == 1) ? 1 : 0 );
        mpLbColumns->SelectEntryPos( 0 );
        mpLbFunctions->SelectEntryPos( 0 );
    }
 
    return true;
}
 
bool ScTpSubTotalGroup::DoFillItemSet( sal_uInt16       nGroupNo,
                                       SfxItemSet&  rArgSet  )
{
    sal_uInt16 nGroupIdx = 0;
 
    OSL_ENSURE( (nGroupNo<=3) && (nGroupNo>0), "Invalid group" );
    OSL_ENSURE(    (mpLbGroup->GetEntryCount() > 0)
                && (mpLbColumns->GetEntryCount() > 0)
                && (mpLbFunctions->GetEntryCount() > 0),
                "Non-initialized Lists" );
 
    if (  (nGroupNo > 3) || (nGroupNo == 0)
        || (mpLbGroup->GetEntryCount() == 0)
        || (mpLbColumns->GetEntryCount() == 0)
        || (mpLbFunctions->GetEntryCount() == 0)
       )
        return false;
    else
        nGroupIdx = nGroupNo-1;
 
    ScSubTotalParam theSubTotalData;            // read out, if already partly filled
    SfxTabDialog* pDlg = GetTabDialog();
    if ( pDlg )
    {
        const SfxItemSet* pExample = pDlg->GetExampleSet();
        const SfxPoolItem* pItem;
        if ( pExample && pExample->GetItemState( nWhichSubTotals, true, &pItem ) == SfxItemState::SET )
            theSubTotalData = static_cast<const ScSubTotalItem*>(pItem)->GetSubTotalData();
    }
 
    std::unique_ptr<ScSubTotalFunc[]> pFunctions;
    std::unique_ptr<SCCOL[]>          pSubTotals;
    const sal_Int32 nGroup      = mpLbGroup->GetSelectedEntryPos();
    const sal_Int32 nEntryCount = mpLbColumns->GetEntryCount();
    const sal_Int32 nCheckCount = mpLbColumns->GetCheckedEntryCount();
 
    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)
                                                ? nFieldArr[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 ( mpLbColumns->IsChecked( i ) )
            {
                OSL_ENSURE( nCheck <= nCheckCount,
                            "Range error :-(" );
                nFunction = *static_cast<sal_uInt16*>(mpLbColumns->GetEntryData( i ));
                pSubTotals[nCheck] = nFieldArr[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, &theSubTotalData ) );
 
    return true;
}
 
void ScTpSubTotalGroup::FillListBoxes()
{
    OSL_ENSURE( pViewData && pDoc, "ViewData or Document not found :-/" );
 
    if ( pViewData && pDoc )
    {
        SCCOL   nFirstCol   = rSubTotalData.nCol1;
        SCROW   nFirstRow   = rSubTotalData.nRow1;
        SCTAB   nTab        = pViewData->GetTabNo();
        SCCOL   nMaxCol     = rSubTotalData.nCol2;
        SCCOL   col;
        OUString  aFieldName;
 
        mpLbGroup->Clear();
        mpLbColumns->Clear();
        mpLbGroup->InsertEntry( aStrNone, 0 );
 
        sal_uInt16 i=0;
        for ( col=nFirstCol; col<=nMaxCol && i<SC_MAXFIELDS; col++ )
        {
            aFieldName = pDoc->GetString(col, nFirstRow, nTab);
            if ( aFieldName.isEmpty() )
            {
                aFieldName = ScGlobal::ReplaceOrAppend( aStrColumn, "%1", ScColToAlpha( col ));
            }
            nFieldArr[i] = col;
            mpLbGroup->InsertEntry( aFieldName, i+1 );
            mpLbColumns->InsertEntry( aFieldName, i );
            mpLbColumns->SetEntryData( i, new sal_uInt16(0) );
            i++;
        }
        // subsequent initialization of the constant:
        const_cast<sal_uInt16&>(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 ( nFieldArr[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, SvTreeListBox*, pLb, void )
{
    SelectHdl(pLb);
}
IMPL_LINK( ScTpSubTotalGroup, SelectListBoxHdl, ListBox&, rLb, void )
{
    SelectHdl(&rLb);
}
void ScTpSubTotalGroup::SelectHdl(const void *pLb)
{
    if (   (mpLbColumns->GetEntryCount() > 0)
        && (mpLbColumns->GetSelectionCount() > 0) )
    {
        const sal_Int32 nFunction   = mpLbFunctions->GetSelectedEntryPos();
        const sal_Int32 nColumn     = mpLbColumns->GetSelectedEntryPos();
        sal_uInt16*     pFunction   = static_cast<sal_uInt16*>(mpLbColumns->GetEntryData( nColumn ));
 
        OSL_ENSURE( pFunction, "EntryData not found!" );
        if ( !pFunction )
            return;
 
        if ( pLb == mpLbColumns )
        {
            mpLbFunctions->SelectEntryPos( *pFunction );
        }
        else if ( pLb == mpLbFunctions )
        {
            *pFunction = static_cast<sal_uInt16>(nFunction);
            mpLbColumns->CheckEntryPos( nColumn );
        }
    }
}
 
IMPL_LINK( ScTpSubTotalGroup, CheckHdl, SvTreeListBox*, pLb, void )
{
    if ( pLb == mpLbColumns )
    {
        SvTreeListEntry* pEntry = mpLbColumns->GetHdlEntry();
 
        if ( pEntry )
        {
            mpLbColumns->SelectEntryPos( static_cast<sal_uInt16>(mpLbColumns->GetModel()->GetAbsPos( pEntry )) );
            SelectHdl( pLb );
        }
    }
}
 
// Derived Group TabPages:
 
VclPtr<SfxTabPage> ScTpSubTotalGroup1::Create( TabPageParent pParent,
                                                 const SfxItemSet*  rArgSet )
{
    return VclPtr<ScTpSubTotalGroup1>::Create( pParent.pParent, *rArgSet );
}
 
VclPtr<SfxTabPage> ScTpSubTotalGroup2::Create( TabPageParent pParent,
                                       const SfxItemSet*    rArgSet )
{
    return VclPtr<ScTpSubTotalGroup2>::Create( pParent.pParent, *rArgSet );
}
 
VclPtr<SfxTabPage> ScTpSubTotalGroup3::Create( TabPageParent pParent,
                                       const SfxItemSet*    rArgSet )
{
    return VclPtr<ScTpSubTotalGroup3>::Create( pParent.pParent, *rArgSet );
}
 
ScTpSubTotalGroup1::ScTpSubTotalGroup1( vcl::Window* pParent, const SfxItemSet& rArgSet ) :
    ScTpSubTotalGroup( pParent, rArgSet )
{}
 
ScTpSubTotalGroup2::ScTpSubTotalGroup2( vcl::Window* pParent, const SfxItemSet& rArgSet ) :
    ScTpSubTotalGroup( pParent, rArgSet )
{}
 
ScTpSubTotalGroup3::ScTpSubTotalGroup3( vcl::Window* pParent, const SfxItemSet& rArgSet ) :
    ScTpSubTotalGroup( pParent, rArgSet )
{}
 
#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( vcl::Window*               pParent,
                                          const SfxItemSet&     rArgSet )
 
        :   SfxTabPage      ( pParent,
                              "SubTotalOptionsPage", "modules/scalc/ui/subtotaloptionspage.ui" ,
                              &rArgSet ),
            pViewData       ( nullptr ),
            pDoc            ( nullptr ),
            nWhichSubTotals ( rArgSet.GetPool()->GetWhich( SID_SUBTOTALS ) ),
            rSubTotalData   ( static_cast<const ScSubTotalItem&>(
                              rArgSet.Get( nWhichSubTotals )).
                                GetSubTotalData() )
{
    get(pBtnPagebreak,"pagebreak");
    get(pBtnCase,"case");
    get(pBtnSort,"sort");
    get(pFlSort,"label2");
    get(pBtnAscending,"ascending");
    get(pBtnDescending,"descending");
    get(pBtnFormats,"formats");
    get(pBtnUserDef,"btnuserdef");
    get(pLbUserDef,"lbuserdef");
 
    Init();
}
 
ScTpSubTotalOptions::~ScTpSubTotalOptions()
{
    disposeOnce();
}
 
void ScTpSubTotalOptions::dispose()
{
    pBtnPagebreak.clear();
    pBtnCase.clear();
    pBtnSort.clear();
    pFlSort.clear();
    pBtnAscending.clear();
    pBtnDescending.clear();
    pBtnFormats.clear();
    pBtnUserDef.clear();
    pLbUserDef.clear();
    SfxTabPage::dispose();
}
 
void ScTpSubTotalOptions::Init()
{
    const ScSubTotalItem& rSubTotalItem = static_cast<const ScSubTotalItem&>(
                                          GetItemSet().Get( nWhichSubTotals ));
 
    pViewData   = rSubTotalItem.GetViewData();
    pDoc        = ( pViewData ) ? pViewData->GetDocument() : nullptr;
 
    OSL_ENSURE( pViewData && pDoc, "ViewData or Document not found!" );
 
    pBtnSort->SetClickHdl    ( LINK( this, ScTpSubTotalOptions, CheckHdl ) );
    pBtnUserDef->SetClickHdl ( LINK( this, ScTpSubTotalOptions, CheckHdl ) );
 
    FillUserSortListBox();
}
 
VclPtr<SfxTabPage> ScTpSubTotalOptions::Create( TabPageParent pParent,
                                                const SfxItemSet* rArgSet )
{
    return VclPtr<ScTpSubTotalOptions>::Create( pParent.pParent, *rArgSet );
}
 
void ScTpSubTotalOptions::Reset( const SfxItemSet* /* rArgSet */ )
{
    pBtnPagebreak->Check ( rSubTotalData.bPagebreak );
    pBtnCase->Check      ( rSubTotalData.bCaseSens );
    pBtnFormats->Check   ( rSubTotalData.bIncludePattern );
    pBtnSort->Check      ( rSubTotalData.bDoSort );
    pBtnAscending->Check ( rSubTotalData.bAscending );
    pBtnDescending->Check( !rSubTotalData.bAscending );
 
    if ( rSubTotalData.bUserDef )
    {
        pBtnUserDef->Check();
        pLbUserDef->Enable();
        pLbUserDef->SelectEntryPos( rSubTotalData.nUserIndex );
    }
    else
    {
        pBtnUserDef->Check( false );
        pLbUserDef->Disable();
        pLbUserDef->SelectEntryPos( 0 );
    }
 
    CheckHdl( pBtnSort );
}
 
bool ScTpSubTotalOptions::FillItemSet( SfxItemSet* rArgSet )
{
    ScSubTotalParam theSubTotalData;            // read out, if already partly filled
    SfxTabDialog* pDlg = GetTabDialog();
    if ( pDlg )
    {
        const SfxItemSet* pExample = pDlg->GetExampleSet();
        const SfxPoolItem* pItem;
        if ( pExample && pExample->GetItemState( nWhichSubTotals, true, &pItem ) == SfxItemState::SET )
            theSubTotalData = static_cast<const ScSubTotalItem*>(pItem)->GetSubTotalData();
    }
 
    theSubTotalData.bPagebreak      = pBtnPagebreak->IsChecked();
    theSubTotalData.bReplace        = true;
    theSubTotalData.bCaseSens       = pBtnCase->IsChecked();
    theSubTotalData.bIncludePattern = pBtnFormats->IsChecked();
    theSubTotalData.bDoSort         = pBtnSort->IsChecked();
    theSubTotalData.bAscending      = pBtnAscending->IsChecked();
    theSubTotalData.bUserDef        = pBtnUserDef->IsChecked();
    theSubTotalData.nUserIndex      = (pBtnUserDef->IsChecked())
                                    ? pLbUserDef->GetSelectedEntryPos()
                                    : 0;
 
    rArgSet->Put( ScSubTotalItem( nWhichSubTotals, &theSubTotalData ) );
 
    return true;
}
 
void ScTpSubTotalOptions::FillUserSortListBox()
{
    ScUserList* pUserLists = ScGlobal::GetUserList();
 
    pLbUserDef->Clear();
    if ( pUserLists )
    {
        size_t nCount = pUserLists->size();
        for ( size_t i=0; i<nCount; ++i )
            pLbUserDef->InsertEntry( (*pUserLists)[i].GetString() );
    }
}
 
// Handler:
 
IMPL_LINK( ScTpSubTotalOptions, CheckHdl, Button*, pBox, void )
{
    if ( pBox == pBtnSort )
    {
        if ( pBtnSort->IsChecked() )
        {
            pFlSort->Enable();
            pBtnFormats->Enable();
            pBtnUserDef->Enable();
            pBtnAscending->Enable();
            pBtnDescending->Enable();
 
            if ( pBtnUserDef->IsChecked() )
                pLbUserDef->Enable();
        }
        else
        {
            pFlSort->Disable();
            pBtnFormats->Disable();
            pBtnUserDef->Disable();
            pBtnAscending->Disable();
            pBtnDescending->Disable();
            pLbUserDef->Disable();
        }
    }
    else if ( pBox == pBtnUserDef )
    {
        if ( pBtnUserDef->IsChecked() )
        {
            pLbUserDef->Enable();
            pLbUserDef->GrabFocus();
        }
        else
            pLbUserDef->Disable();
    }
}
 
ScTpSubTotalGroup1::~ScTpSubTotalGroup1()
{
}
 
ScTpSubTotalGroup2::~ScTpSubTotalGroup2()
{
}
 
ScTpSubTotalGroup3::~ScTpSubTotalGroup3()
{
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V560 A part of conditional expression is always true: i > 0.