/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sc.hxx"

//----------------------------------------------------------------------------

#include "pvlaydlg.hxx"
#include "dbdocfun.hxx"
#include "dpuiglobal.hxx"

#include <sfx2/dispatch.hxx>
#include <vcl/msgbox.hxx>

#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
#include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>

#include "uiitems.hxx"
#include "rangeutl.hxx"
#include "document.hxx"
#include "viewdata.hxx"
#include "tabvwsh.hxx"
#include "reffact.hxx"
#include "scresid.hxx"
#include "globstr.hrc"
#include "pivot.hrc"
#include "dpobject.hxx"
#include "dpsave.hxx"
#include "dpshttab.hxx"
#include "scmod.hxx"

#include "sc.hrc"
#include "scabstdlg.hxx"

using namespace com::sun::star;
using ::rtl::OUString;
using ::std::vector;
using ::std::for_each;

//----------------------------------------------------------------------------

#define FSTR(index) aFuncNameArr[index-1]
#define STD_FORMAT   SCA_VALID | SCA_TAB_3D \
                    | SCA_COL_ABSOLUTE | SCA_ROW_ABSOLUTE | SCA_TAB_ABSOLUTE

//============================================================================

namespace {

PointerStyle lclGetPointerForField( ScDPFieldType eType )
{
    switch( eType )
    {
        case TYPE_PAGE:     return POINTER_PIVOT_FIELD;
        case TYPE_COL:      return POINTER_PIVOT_COL;
        case TYPE_ROW:      return POINTER_PIVOT_ROW;
        case TYPE_DATA:     return POINTER_PIVOT_FIELD;
        case TYPE_SELECT:   return POINTER_PIVOT_FIELD;
    }
    return POINTER_ARROW;
}

} // namespace

//============================================================================

//----------------------------------------------------------------------------

ScDPLayoutDlg::ScDPLayoutDlg( SfxBindings* pB, SfxChildWindow* pCW, Window* pParent,
                                    const ScDPObject& rDPObject, bool bNewOutput )
    :	ScAnyRefDlg	( pB, pCW, pParent, RID_SCDLG_PIVOT_LAYOUT ),
        aFlLayout       ( this, ScResId( FL_LAYOUT ) ),
        aFtPage         ( this, ScResId( FT_PAGE ) ),
        aWndPage        ( this, ScResId( WND_PAGE ), &aFtPage ),
        aFtCol          ( this, ScResId( FT_COL ) ),
        aWndCol         ( this, ScResId( WND_COL ), &aFtCol ),
        aFtRow          ( this, ScResId( FT_ROW ) ),
        aWndRow         ( this, ScResId( WND_ROW ), &aFtRow ),
        aFtData         ( this, ScResId( FT_DATA ) ),
        aWndData        ( this, ScResId( WND_DATA ), &aFtData ),
        aWndSelect      ( this, ScResId( WND_SELECT ), NULL ),
        aFtInfo			( this, ScResId( FT_INFO ) ),

        aFlAreas        ( this, ScResId( FL_OUTPUT ) ),

        aFtInArea		( this, ScResId( FT_INAREA) ),
        aEdInPos		( this, ScResId( ED_INAREA) ),
        aRbInPos		( this, ScResId( RB_INAREA ), &aEdInPos, this ),

        aLbOutPos		( this, ScResId( LB_OUTAREA ) ),
        aFtOutArea		( this, ScResId( FT_OUTAREA ) ),
        aEdOutPos       ( this, this, ScResId( ED_OUTAREA ) ),
        aRbOutPos		( this, ScResId( RB_OUTAREA ), &aEdOutPos, this ),
        aBtnIgnEmptyRows( this, ScResId( BTN_IGNEMPTYROWS ) ),
        aBtnDetectCat	( this, ScResId( BTN_DETECTCAT ) ),
        aBtnTotalCol	( this, ScResId( BTN_TOTALCOL ) ),
        aBtnTotalRow	( this, ScResId( BTN_TOTALROW ) ),
        aBtnFilter      ( this, ScResId( BTN_FILTER ) ),
        aBtnDrillDown   ( this, ScResId( BTN_DRILLDOWN ) ),

        aBtnOk			( this, ScResId( BTN_OK ) ),
        aBtnCancel		( this, ScResId( BTN_CANCEL ) ),
        aBtnHelp		( this, ScResId( BTN_HELP ) ),
        aBtnRemove      ( this, ScResId( BTN_REMOVE ) ),
        aBtnOptions     ( this, ScResId( BTN_OPTIONS ) ),
        aBtnMore		( this, ScResId( BTN_MORE ) ),

        aStrUndefined	( ScResId( SCSTR_UNDEFINED ) ),
        aStrNewTable	( ScResId( SCSTR_NEWTABLE ) ),

        bIsDrag			( false ),

        pEditActive     ( NULL ),

        eLastActiveType ( TYPE_SELECT ),
        nOffset			( 0 ),
        //
        xDlgDPObject    ( new ScDPObject( rDPObject ) ),
        pViewData		( ((ScTabViewShell*)SfxViewShell::Current())->
                                GetViewData() ),
        pDoc			( ((ScTabViewShell*)SfxViewShell::Current())->
                                GetViewData()->GetDocument() ),
        bRefInputMode   (false)
{
    xDlgDPObject->SetAlive( true );     // needed to get structure information
    xDlgDPObject->FillOldParam( thePivotData );
    xDlgDPObject->FillLabelData( thePivotData );

    Init(bNewOutput);
    FreeResource();
}

//----------------------------------------------------------------------------

ScDPLayoutDlg::~ScDPLayoutDlg()
{
    sal_uInt16 nEntries = aLbOutPos.GetEntryCount();
    sal_uInt16 i;

    for ( i=2; i<nEntries; i++ )
        delete (String*)aLbOutPos.GetEntryData( i );
}

//----------------------------------------------------------------------------

void ScDPLayoutDlg::Init(bool bNewOutput)
{
    DBG_ASSERT( pViewData && pDoc,
                "Ctor-Initialisierung fehlgeschlagen!" );

    aBtnRemove.SetClickHdl( LINK( this, ScDPLayoutDlg, ClickHdl ) );
    aBtnOptions.SetClickHdl( LINK( this, ScDPLayoutDlg, ClickHdl ) );

    aFuncNameArr.reserve( PIVOT_MAXFUNC );
    for ( sal_uInt16 i = 0; i < PIVOT_MAXFUNC; ++i )
        aFuncNameArr.push_back( String( ScResId( i + 1 ) ) );

    aBtnMore.AddWindow( &aFlAreas );
    aBtnMore.AddWindow( &aFtInArea );
    aBtnMore.AddWindow( &aEdInPos );
    aBtnMore.AddWindow( &aRbInPos );
    aBtnMore.AddWindow( &aFtOutArea );
    aBtnMore.AddWindow( &aLbOutPos );
    aBtnMore.AddWindow( &aEdOutPos );
    aBtnMore.AddWindow( &aRbOutPos );
    aBtnMore.AddWindow( &aBtnIgnEmptyRows );
    aBtnMore.AddWindow( &aBtnDetectCat );
    aBtnMore.AddWindow( &aBtnTotalCol );
    aBtnMore.AddWindow( &aBtnTotalRow );
    aBtnMore.AddWindow( &aBtnFilter );
    aBtnMore.AddWindow( &aBtnDrillDown );
    aBtnMore.SetClickHdl( LINK( this, ScDPLayoutDlg, MoreClickHdl ) );

    CalcWndSizes();

    ScRange inRange;
    String inString;
    if (xDlgDPObject->GetSheetDesc())
    {
        aEdInPos.Enable();
        aRbInPos.Enable();
        const ScSheetSourceDesc* p = xDlgDPObject->GetSheetDesc();
        OUString aRangeName = p->GetRangeName();
        if (aRangeName.getLength())
            aEdInPos.SetText(aRangeName);
        else
        {
            aOldRange = p->GetSourceRange();
            aOldRange.Format( inString, SCR_ABS_3D, pDoc, pDoc->GetAddressConvention() );
            aEdInPos.SetText(inString);
        }
    }
    else
    {
        /* Data is not reachable, so could be a remote database */
        aEdInPos.Disable();
        aRbInPos.Disable();
    }

    InitFields();

    aLbOutPos .SetSelectHdl( LINK( this, ScDPLayoutDlg, SelAreaHdl ) );
    aEdOutPos .SetModifyHdl( LINK( this, ScDPLayoutDlg, EdModifyHdl ) );
    aEdInPos  .SetModifyHdl( LINK( this, ScDPLayoutDlg, EdInModifyHdl ) );
    aBtnOk    .SetClickHdl ( LINK( this, ScDPLayoutDlg, OkHdl ) );
    aBtnCancel.SetClickHdl ( LINK( this, ScDPLayoutDlg, CancelHdl ) );
    Link aLink = LINK( this, ScDPLayoutDlg, GetFocusHdl );
    if ( aEdInPos.IsEnabled() )
        // Once disabled it will never get enabled, so no need to handle focus.
        aEdInPos.SetGetFocusHdl( aLink );
    aEdOutPos.SetGetFocusHdl( aLink );

    if ( pViewData && pDoc )
    {
        /*
         * Aus den RangeNames des Dokumentes werden nun die
         * in einem Zeiger-Array gemerkt, bei denen es sich
         * um sinnvolle Bereiche handelt
         */

        aLbOutPos.Clear();
        aLbOutPos.InsertEntry( aStrUndefined, 0 );
        aLbOutPos.InsertEntry( aStrNewTable,  1 );

        ScAreaNameIterator aIter( pDoc );
        String aName;
        ScRange aRange;
        String aRefStr;
        while ( aIter.Next( aName, aRange ) )
        {
            if ( !aIter.WasDBName() )		// hier keine DB-Bereiche !
            {
                sal_uInt16 nInsert = aLbOutPos.InsertEntry( aName );

                aRange.aStart.Format( aRefStr, SCA_ABS_3D, pDoc, pDoc->GetAddressConvention() );
                aLbOutPos.SetEntryData( nInsert, new String( aRefStr ) );
            }
        }
    }

    if (bNewOutput)
    {
        // Output to a new sheet by default for a brand-new output.
        aLbOutPos.SelectEntryPos(1);
        aEdOutPos.Disable();
        aRbOutPos.Disable();
    }
    else
    {
        // Modifying an existing dp output.

        if ( thePivotData.nTab != MAXTAB+1 )
        {
            String aStr;
            ScAddress( thePivotData.nCol,
                       thePivotData.nRow,
                       thePivotData.nTab ).Format( aStr, STD_FORMAT, pDoc, pDoc->GetAddressConvention() );
            aEdOutPos.SetText( aStr );
            EdModifyHdl(0);
        }
        else
        {
            aLbOutPos.SelectEntryPos( aLbOutPos.GetEntryCount()-1 );
            SelAreaHdl(NULL);
        }
    }

    aBtnIgnEmptyRows.Check( thePivotData.bIgnoreEmptyRows );
    aBtnDetectCat   .Check( thePivotData.bDetectCategories );
    aBtnTotalCol	.Check( thePivotData.bMakeTotalCol );
    aBtnTotalRow	.Check( thePivotData.bMakeTotalRow );

    if( const ScDPSaveData* pSaveData = xDlgDPObject->GetSaveData() )
    {
        aBtnFilter.Check( pSaveData->GetFilterButton() );
        aBtnDrillDown.Check( pSaveData->GetDrillDown() );
    }
    else
    {
        aBtnFilter.Check();
        aBtnDrillDown.Check();
    }

    aWndPage.SetHelpId( HID_SC_DPLAY_PAGE );
    aWndCol.SetHelpId( HID_SC_DPLAY_COLUMN );
    aWndRow.SetHelpId( HID_SC_DPLAY_ROW );
    aWndData.SetHelpId( HID_SC_DPLAY_DATA );
    aWndSelect.SetHelpId( HID_SC_DPLAY_SELECT );

    InitFocus();
}

//----------------------------------------------------------------------------

sal_Bool ScDPLayoutDlg::Close()
{
    return DoClose( ScPivotLayoutWrapper::GetChildWindowId() );
}

//----------------------------------------------------------------------------

void ScDPLayoutDlg::StateChanged( StateChangedType nStateChange )
{
    ScAnyRefDlg::StateChanged( nStateChange );

    if ( nStateChange == STATE_CHANGE_INITSHOW )
    {
        // Hiding the FixedTexts and clearing the tab stop style bits
        // has to be done after assigning the mnemonics, but Paint is too late,
        // because the test tool may send key events to the dialog when it isn't visible.
        // Mnemonics are assigned in the Dialog::StateChanged for STATE_CHANGE_INITSHOW,
        // so this can be done immediately afterwards.

        aWndPage.UseMnemonic();
        aWndCol.UseMnemonic();
        aWndRow.UseMnemonic();
        aWndData.UseMnemonic();
    }
}

//----------------------------------------------------------------------------

void ScDPLayoutDlg::InitWndSelect( const vector<ScDPLabelDataRef>& rLabels )
{
    size_t nLabelCount = rLabels.size();
    if (nLabelCount > MAX_LABELS)
        nLabelCount = MAX_LABELS;

    aLabelDataArr.clear();
    aLabelDataArr.reserve( nLabelCount );
    for ( size_t i=0; i < nLabelCount; i++ )
    {
        aLabelDataArr.push_back(*rLabels[i]);
        aWndSelect.AddField(aLabelDataArr[i].getDisplayName(), i);
        ScDPFuncDataRef p(new ScDPFuncData(aLabelDataArr[i].mnCol, aLabelDataArr[i].mnFuncMask));
        aSelectArr.push_back(p);
    }
    aWndSelect.ResetScrollBar();
    aWndSelect.Paint(Rectangle());
}

//----------------------------------------------------------------------------

void ScDPLayoutDlg::InitFieldWindow( const vector<PivotField>& rFields, ScDPFieldType eType )
{
    ScDPFuncDataVec* pInitArr = GetFieldDataArray(eType);
    ScDPFieldControlBase* pInitWnd = GetFieldWindow(eType);

    if (!pInitArr || !pInitWnd)
        return;

    vector<PivotField>::const_iterator itr = rFields.begin(), itrEnd = rFields.end();
    for (; itr != itrEnd; ++itr)
    {
        SCCOL nCol = itr->nCol;
        sal_uInt16 nMask = itr->nFuncMask;
        if (nCol == PIVOT_DATA_FIELD)
            continue;

        size_t nFieldIndex = pInitArr->size();
        ScDPFuncDataRef p(new ScDPFuncData(nCol, nMask, itr->maFieldRef));
        pInitArr->push_back(p);

        if (eType == TYPE_DATA)
        {
            // data field - we need to concatenate function name with the field name.
            ScDPLabelData* pData = GetLabelData(nCol);
            DBG_ASSERT( pData, "ScDPLabelData not found" );
            if (pData)
            {
                OUString aStr = pData->maLayoutName;
                if (!aStr.getLength())
                {
                    sal_uInt16 nInitMask = pInitArr->back()->mnFuncMask;
                    aStr = GetFuncString(nInitMask, pData->mbIsValue);
                    aStr += pData->maName;
                }

                pInitWnd->AddField(aStr, nFieldIndex);
                pData->mnFuncMask = nMask;
            }
        }
        else
            pInitWnd->AddField(GetLabelString(nCol), nFieldIndex);
    }
    pInitWnd->ResetScrollBar();
}

//----------------------------------------------------------------------------

void ScDPLayoutDlg::InitFocus()
{
    if( aWndSelect.IsEmpty() )
    {
        aBtnOk.GrabFocus();
        NotifyFieldFocus( TYPE_SELECT, false );
    }
    else
        aWndSelect.GrabFocus();
}

void ScDPLayoutDlg::InitFields()
{
    InitWndSelect(thePivotData.maLabelArray);
    InitFieldWindow(thePivotData.maPageFields, TYPE_PAGE);
    InitFieldWindow(thePivotData.maColFields, TYPE_COL);
    InitFieldWindow(thePivotData.maRowFields, TYPE_ROW);
    InitFieldWindow(thePivotData.maDataFields, TYPE_DATA);
}

//----------------------------------------------------------------------------

void ScDPLayoutDlg::AddField( size_t nFromIndex, ScDPFieldType eToType, const Point& rAtPos )
{
    ScDPFuncData        fData( *(aSelectArr[nFromIndex]) );
    size_t nAt = 0;
    ScDPFieldControlBase* toWnd = GetFieldWindow(eToType);
    ScDPFieldControlBase* rmWnd1 = NULL;
    ScDPFieldControlBase* rmWnd2 = NULL;
    GetOtherFieldWindows(eToType, rmWnd1, rmWnd2);

    ScDPFuncDataVec*    toArr = GetFieldDataArray(eToType);
    ScDPFuncDataVec*    rmArr1 = NULL;
    ScDPFuncDataVec*    rmArr2 = NULL;
    GetOtherDataArrays(eToType, rmArr1, rmArr2);

    bool bDataArr = eToType == TYPE_DATA;

    bool bAllowed = IsOrientationAllowed( fData.mnCol, eToType );
    if ( bAllowed && (!Contains( toArr, fData.mnCol, nAt )) )
    {
        // ggF. in anderem Fenster entfernen
        if ( rmArr1 )
        {
            if ( Contains( rmArr1, fData.mnCol, nAt ) )
            {
                rmWnd1->DelField( nAt );
                Remove( rmArr1, nAt );
            }
        }
        if ( rmArr2 )
        {
            if ( Contains( rmArr2, fData.mnCol, nAt ) )
            {
                rmWnd2->DelField( nAt );
                Remove( rmArr2, nAt );
            }
        }

        ScDPLabelData&  rData = aLabelDataArr[nFromIndex+nOffset];
        size_t      nAddedAt = 0;

        if ( !bDataArr )
        {
            if ( toWnd->AddField( rData.getDisplayName(),
                                  DlgPos2WndPos( rAtPos, *toWnd ),
                                  nAddedAt ) )
            {
                Insert( toArr, fData, nAddedAt );
                toWnd->GrabFocus();
            }
        }
        else
        {
            ScDPLabelData* p = GetLabelData(fData.mnCol);
            OUString aStr = p->maLayoutName;
            sal_uInt16 nMask = fData.mnFuncMask;
            if (!aStr.getLength())
            {
                aStr = GetFuncString(nMask);
                aStr += p->maName;
            }

            if ( toWnd->AddField( aStr,
                                  DlgPos2WndPos( rAtPos, *toWnd ),
                                  nAddedAt ) )
            {
                fData.mnFuncMask = nMask;
                Insert( toArr, fData, nAddedAt );
                toWnd->GrabFocus();
            }
        }

    }
}

void ScDPLayoutDlg::AppendField(size_t nFromIndex, ScDPFieldType eToType)
{
    ScDPFuncData aFuncData = *aSelectArr[nFromIndex];

    size_t nAt = 0;
    ScDPFieldControlBase* toWnd = GetFieldWindow(eToType);
    ScDPFieldControlBase* rmWnd1 = NULL;
    ScDPFieldControlBase* rmWnd2 = NULL;
    GetOtherFieldWindows(eToType, rmWnd1, rmWnd2);

    ScDPFuncDataVec*    toArr = GetFieldDataArray(eToType);
    ScDPFuncDataVec*    rmArr1 = NULL;
    ScDPFuncDataVec*    rmArr2 = NULL;
    GetOtherDataArrays(eToType, rmArr1, rmArr2);

    bool bDataArr = eToType == TYPE_DATA;

    if ( (!Contains( toArr, aFuncData.mnCol, nAt )) )
    {
        // ggF. in anderem Fenster entfernen
        if ( rmArr1 )
        {
            if ( Contains( rmArr1, aFuncData.mnCol, nAt ) )
            {
                rmWnd1->DelField( nAt );
                Remove( rmArr1, nAt );
            }
        }
        if ( rmArr2 )
        {
            if ( Contains( rmArr2, aFuncData.mnCol, nAt ) )
            {
                rmWnd2->DelField( nAt );
                Remove( rmArr2, nAt );
            }
        }

        ScDPLabelData&  rData = aLabelDataArr[nFromIndex+nOffset];
        size_t      nAddedAt = 0;

        if ( !bDataArr )
        {
            if ( toWnd->AppendField(rData.getDisplayName(), nAddedAt) )
            {
                Insert( toArr, aFuncData, nAddedAt );
                toWnd->GrabFocus();
            }
        }
        else
        {
            ScDPLabelData* p = GetLabelData(aFuncData.mnCol);
            OUString aStr = p->maLayoutName;
            sal_uInt16 nMask = aFuncData.mnFuncMask;
            if (!aStr.getLength())
            {
                aStr = GetFuncString(nMask);
                aStr += p->maName;
            }

            if ( toWnd->AppendField(aStr, nAddedAt) )
            {
                aFuncData.mnFuncMask = nMask;
                Insert( toArr, aFuncData, nAddedAt );
                toWnd->GrabFocus();
            }
        }
    }
}

//----------------------------------------------------------------------------

void ScDPLayoutDlg::MoveField( ScDPFieldType eFromType, size_t nFromIndex, ScDPFieldType eToType, const Point& rAtPos )
{
    if ( eFromType == TYPE_SELECT )
        AddField( nFromIndex, eToType, rAtPos );
    else if ( eFromType != eToType )
    {
        ScDPFieldControlBase* fromWnd  = GetFieldWindow(eFromType);
        ScDPFieldControlBase* toWnd    = GetFieldWindow(eToType);

        ScDPFieldControlBase* rmWnd1   = NULL;
        ScDPFieldControlBase* rmWnd2   = NULL;
        GetOtherFieldWindows(eToType, rmWnd1, rmWnd2);

        ScDPFuncDataVec*    fromArr  = GetFieldDataArray(eFromType);
        ScDPFuncDataVec*    toArr    = GetFieldDataArray(eToType);

        ScDPFuncDataVec*    rmArr1   = NULL;
        ScDPFuncDataVec*    rmArr2   = NULL;
        GetOtherDataArrays(eToType, rmArr1, rmArr2);

        bool bDataArr = eToType == TYPE_DATA;

        if ( fromArr && toArr && fromWnd && toWnd )
        {
            ScDPFuncData fData( *((*fromArr)[nFromIndex]) );
            bool bAllowed = IsOrientationAllowed( fData.mnCol, eToType );

            size_t nAt = 0;
            if ( bAllowed && Contains( fromArr, fData.mnCol, nAt ) )
            {
                fromWnd->DelField( nAt );
                Remove( fromArr, nAt );

                if (!Contains( toArr, fData.mnCol, nAt ))
                {
                    size_t nAddedAt = 0;
                    if ( !bDataArr )
                    {
                        // ggF. in anderem Fenster entfernen
                        if ( rmArr1 )
                        {
                            if ( Contains( rmArr1, fData.mnCol, nAt ) )
                            {
                                rmWnd1->DelField( nAt );
                                Remove( rmArr1, nAt );
                            }
                        }
                        if ( rmArr2 )
                        {
                            if ( Contains( rmArr2, fData.mnCol, nAt ) )
                            {
                                rmWnd2->DelField( nAt );
                                Remove( rmArr2, nAt );
                            }
                        }

                        if ( toWnd->AddField( GetLabelString( fData.mnCol ),
                                              DlgPos2WndPos( rAtPos, *toWnd ),
                                              nAddedAt ) )
                        {
                            Insert( toArr, fData, nAddedAt );
                            toWnd->GrabFocus();
                        }
                    }
                    else
                    {
                        ScDPLabelData* p = GetLabelData(fData.mnCol);
                        OUString aStr = p->maLayoutName;
                        sal_uInt16 nMask = fData.mnFuncMask;
                        if (!aStr.getLength())
                        {
                            aStr = GetFuncString(nMask);
                            aStr += p->maName;
                        }

                        if ( toWnd->AddField( aStr,
                                              DlgPos2WndPos( rAtPos, *toWnd ),
                                              nAddedAt ) )
                        {
                            fData.mnFuncMask = nMask;
                            Insert( toArr, fData, nAddedAt );
                            toWnd->GrabFocus();
                        }
                    }
                }
            }
        }
    }
    else // -> eFromType == eToType
    {
        ScDPFieldControlBase* theWnd  = GetFieldWindow(eFromType);
        ScDPFuncDataVec*    theArr   = GetFieldDataArray(eFromType);
        size_t              nAt      = 0;
        Point               aToPos;
        sal_Bool                bDataArr = eFromType == TYPE_DATA;

        ScDPFuncData fData( *((*theArr)[nFromIndex]) );

        if ( Contains( theArr, fData.mnCol, nAt ) )
        {
            size_t nToIndex = 0;
            aToPos = DlgPos2WndPos( rAtPos, *theWnd );
            theWnd->GetExistingIndex( aToPos, nToIndex );

            if ( nToIndex != nAt )
            {
                size_t nAddedAt = 0;

                theWnd->DelField( nAt );
                Remove( theArr, nAt );

                if ( !bDataArr )
                {
                    if ( theWnd->AddField( GetLabelString( fData.mnCol ),
                                           aToPos,
                                           nAddedAt ) )
                    {
                        Insert( theArr, fData, nAddedAt );
                    }
                }
                else
                {
                    ScDPLabelData* p = GetLabelData(fData.mnCol);
                    OUString aStr = p->maLayoutName;
                    sal_uInt16 nMask = fData.mnFuncMask;
                    if (!aStr.getLength())
                    {
                        aStr = GetFuncString(nMask);
                        aStr += p->maName;
                    }

                    if ( theWnd->AddField( aStr,
                                           DlgPos2WndPos( rAtPos, *theWnd ),
                                           nAddedAt ) )
                    {
                        fData.mnFuncMask = nMask;
                        Insert( theArr, fData, nAddedAt );
                    }
                }
            }
        }
    }
}

void ScDPLayoutDlg::MoveFieldToEnd( ScDPFieldType eFromType, size_t nFromIndex, ScDPFieldType eToType )
{
    if ( eFromType == TYPE_SELECT )
        AppendField( nFromIndex, eToType );
    else if ( eFromType != eToType )
    {
        ScDPFieldControlBase* fromWnd  = GetFieldWindow(eFromType);
        ScDPFieldControlBase* toWnd    = GetFieldWindow(eToType);

        ScDPFieldControlBase* rmWnd1   = NULL;
        ScDPFieldControlBase* rmWnd2   = NULL;
        GetOtherFieldWindows(eToType, rmWnd1, rmWnd2);

        ScDPFuncDataVec*    fromArr  = GetFieldDataArray(eFromType);
        ScDPFuncDataVec*    toArr    = GetFieldDataArray(eToType);

        ScDPFuncDataVec*    rmArr1   = NULL;
        ScDPFuncDataVec*    rmArr2   = NULL;
        GetOtherDataArrays(eToType, rmArr1, rmArr2);

        bool bDataArr = eToType == TYPE_DATA;

        if ( fromArr && toArr && fromWnd && toWnd )
        {
            ScDPFuncData fData( *((*fromArr)[nFromIndex]) );

            size_t nAt = 0;
            if ( Contains( fromArr, fData.mnCol, nAt ) )
            {
                fromWnd->DelField( nAt );
                Remove( fromArr, nAt );

                if (!Contains( toArr, fData.mnCol, nAt ))
                {
                    size_t nAddedAt = 0;
                    if ( !bDataArr )
                    {
                        // ggF. in anderem Fenster entfernen
                        if ( rmArr1 )
                        {
                            if ( Contains( rmArr1, fData.mnCol, nAt ) )
                            {
                                rmWnd1->DelField( nAt );
                                Remove( rmArr1, nAt );
                            }
                        }
                        if ( rmArr2 )
                        {
                            if ( Contains( rmArr2, fData.mnCol, nAt ) )
                            {
                                rmWnd2->DelField( nAt );
                                Remove( rmArr2, nAt );
                            }
                        }

                        if ( toWnd->AppendField( GetLabelString( fData.mnCol ), nAddedAt ) )
                        {
                            Insert( toArr, fData, nAddedAt );
                            toWnd->GrabFocus();
                        }
                    }
                    else
                    {
                        ScDPLabelData* p = GetLabelData(fData.mnCol);
                        OUString aStr = p->maLayoutName;
                        sal_uInt16 nMask = fData.mnFuncMask;
                        if (!aStr.getLength())
                        {
                            aStr = GetFuncString(nMask);
                            aStr += p->maName;
                        }

                        if ( toWnd->AppendField(aStr, nAddedAt) )
                        {
                            fData.mnFuncMask = nMask;
                            Insert( toArr, fData, nAddedAt );
                            toWnd->GrabFocus();
                        }
                    }
                }
            }
        }
    }
    else // -> eFromType == eToType
    {
        ScDPFieldControlBase* theWnd  = GetFieldWindow(eFromType);
        ScDPFuncDataVec*    theArr   = GetFieldDataArray(eFromType);
        size_t              nAt      = 0;
        Point               aToPos;
        sal_Bool                bDataArr = eFromType == TYPE_DATA;

        ScDPFuncData fData( *((*theArr)[nFromIndex]) );

        if ( Contains( theArr, fData.mnCol, nAt ) )
        {
            size_t nToIndex = 0;
            theWnd->GetExistingIndex( aToPos, nToIndex );

            if ( nToIndex != nAt )
            {
                size_t nAddedAt = 0;

                theWnd->DelField( nAt );
                Remove( theArr, nAt );

                if ( !bDataArr )
                {
                    if ( theWnd->AppendField(GetLabelString( fData.mnCol ), nAddedAt) )
                    {
                        Insert( theArr, fData, nAddedAt );
                    }
                }
                else
                {
                    ScDPLabelData* p = GetLabelData(fData.mnCol);
                    OUString aStr = p->maLayoutName;
                    sal_uInt16 nMask = fData.mnFuncMask;
                    if (!aStr.getLength())
                    {
                        aStr = GetFuncString(nMask);
                        aStr += p->maName;
                    }

                    if ( theWnd->AppendField(aStr, nAddedAt) )
                    {
                        fData.mnFuncMask = nMask;
                        Insert( theArr, fData, nAddedAt );
                    }
                }
            }
        }
    }
}

//----------------------------------------------------------------------------

void ScDPLayoutDlg::RemoveField( ScDPFieldType eFromType, size_t nIndex )
{
    ScDPFuncDataVec* pArr = GetFieldDataArray(eFromType);

    if( pArr )
    {
        ScDPFieldControlBase* pWnd = GetFieldWindow( eFromType );
        if (pWnd)
        {
            pWnd->DelField( nIndex );
            Remove( pArr, nIndex );
            if( pWnd->IsEmpty() ) InitFocus();
        }
    }
}

//----------------------------------------------------------------------------

void ScDPLayoutDlg::NotifyMouseButtonUp( const Point& rAt )
{
    if ( bIsDrag )
    {
        bIsDrag = false;

        ScDPFieldType   eDnDToType = TYPE_SELECT;
        Point           aPos = ScreenToOutputPixel( rAt );
        bool            bDel = false;

        if ( aRectPage.IsInside( aPos ) )
        {
            eDnDToType = TYPE_PAGE;
        }
        else if ( aRectCol.IsInside( aPos ) )
        {
            eDnDToType = TYPE_COL;
        }
        else if ( aRectRow.IsInside( aPos ) )
        {
            eDnDToType = TYPE_ROW;
        }
        else if ( aRectData.IsInside( aPos ) )
        {
            eDnDToType = TYPE_DATA;
        }
        else if ( aRectSelect.IsInside( aPos ) )
        {
            eDnDToType = TYPE_SELECT;
        }
        else
            bDel = true;

        if (bDel)
        {
            // We don't remove any buttons from the select field.
            if (eDnDFromType != TYPE_SELECT)
                RemoveField( eDnDFromType, nDnDFromIndex );
        }
        else
            MoveField( eDnDFromType, nDnDFromIndex, eDnDToType, aPos );
    }
}

//----------------------------------------------------------------------------

PointerStyle ScDPLayoutDlg::NotifyMouseMove( const Point& rAt )
{
    PointerStyle ePtr = POINTER_ARROW;

    if ( bIsDrag )
    {
        Point aPos = ScreenToOutputPixel( rAt );
        ScDPFieldType eCheckTarget = TYPE_SELECT;

        if ( aRectPage.IsInside( aPos ) )
            eCheckTarget = TYPE_PAGE;
        else if ( aRectCol.IsInside( aPos ) )
            eCheckTarget = TYPE_COL;
        else if ( aRectRow.IsInside( aPos ) )
            eCheckTarget = TYPE_ROW;
        else if ( aRectData.IsInside( aPos ) )
            eCheckTarget = TYPE_DATA;
        else if ( eDnDFromType != TYPE_SELECT )
            ePtr = POINTER_PIVOT_DELETE;
        else if ( aRectSelect.IsInside( aPos ) )
            ePtr = lclGetPointerForField( TYPE_SELECT );
        else
            ePtr = POINTER_NOTALLOWED;

        if ( eCheckTarget != TYPE_SELECT )
        {
            // check if the target orientation is allowed for this field
            ScDPFuncDataVec* fromArr = NULL;
            switch ( eDnDFromType )
            {
                case TYPE_PAGE:   fromArr = &aPageArr;   break;
                case TYPE_COL:    fromArr = &aColArr;    break;
                case TYPE_ROW:    fromArr = &aRowArr;    break;
                case TYPE_DATA:   fromArr = &aDataArr;   break;
                case TYPE_SELECT: fromArr = &aSelectArr; break;
            }
            ScDPFuncData fData( *((*fromArr)[nDnDFromIndex]) );
            if (IsOrientationAllowed( fData.mnCol, eCheckTarget ))
                ePtr = lclGetPointerForField( eCheckTarget );
            else
                ePtr = POINTER_NOTALLOWED;
        }
    }

    return ePtr;
}

//----------------------------------------------------------------------------

PointerStyle ScDPLayoutDlg::NotifyMouseButtonDown( ScDPFieldType eType, size_t nFieldIndex )
{
    bIsDrag		  = true;
    eDnDFromType  = eType;
    nDnDFromIndex = nFieldIndex;
    return lclGetPointerForField( eType );
}

//----------------------------------------------------------------------------

void ScDPLayoutDlg::NotifyDoubleClick( ScDPFieldType eType, size_t nFieldIndex )
{
    ScDPFuncDataVec* pArr = GetFieldDataArray(eType);

    if ( pArr )
    {
        if ( nFieldIndex >= pArr->size() )
        {
            OSL_FAIL("invalid selection");
            return;
        }

        size_t nArrPos = 0;
        if( ScDPLabelData* pData = GetLabelData( (*pArr)[nFieldIndex]->mnCol, &nArrPos ) )
        {
            ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
            DBG_ASSERT(pFact, "ScAbstractFactory create fail!");

            switch ( eType )
            {
                case TYPE_PAGE:
                case TYPE_COL:
                case TYPE_ROW:
                {
                    // list of names of all data fields
                    vector<ScDPName> aDataFieldNames;
                    for( ScDPFuncDataVec::const_iterator aIt = aDataArr.begin(), aEnd = aDataArr.end();
                            (aIt != aEnd) && aIt->get(); ++aIt )
                    {
                        ScDPLabelData* pDFData = GetLabelData((*aIt)->mnCol);
                        if (!pDFData)
                            continue;

                        if (!pDFData->maName.getLength())
                            continue;

                        OUString aLayoutName = pDFData->maLayoutName;
                        if (!aLayoutName.getLength())
                        {
                            // No layout name exists.  Use the stock name.
                            sal_uInt16 nMask = (*aIt)->mnFuncMask;
                            OUString aFuncStr = GetFuncString(nMask);
                            aLayoutName = aFuncStr + pDFData->maName;
                        }
                        aDataFieldNames.push_back(ScDPName(pDFData->maName, aLayoutName));
                    }

                    bool bLayout = (eType == TYPE_ROW) &&
                        ((aDataFieldNames.size() > 1) || ((nFieldIndex + 1 < pArr->size()) && (*pArr)[nFieldIndex+1].get()));

                    AbstractScDPSubtotalDlg* pDlg = pFact->CreateScDPSubtotalDlg(
                        this, RID_SCDLG_PIVOTSUBT,
                        *xDlgDPObject, *pData, *(*pArr)[nFieldIndex], aDataFieldNames, bLayout );

                    if ( pDlg->Execute() == RET_OK )
                    {
                        pDlg->FillLabelData( *pData );
                        (*pArr)[nFieldIndex]->mnFuncMask = pData->mnFuncMask;
                    }
                    delete pDlg;
                }
                break;

                case TYPE_DATA:
                {
                    AbstractScDPFunctionDlg* pDlg = pFact->CreateScDPFunctionDlg(
                        this, RID_SCDLG_DPDATAFIELD,
                        aLabelDataArr, *pData, *(*pArr)[nFieldIndex] );

                    if ( pDlg->Execute() == RET_OK )
                    {
                        (*pArr)[nFieldIndex]->mnFuncMask = pData->mnFuncMask = pDlg->GetFuncMask();
                        (*pArr)[nFieldIndex]->maFieldRef = pDlg->GetFieldRef();

                        ScDPLabelData* p = GetLabelData(aDataArr[nFieldIndex]->mnCol);
                        OUString aStr = p->maLayoutName;
                        if (!aStr.getLength())
                        {
                            // Layout name is not available.  Use default name.
                            aStr = GetFuncString (aDataArr[nFieldIndex]->mnFuncMask);
                            aStr += p->maName;
                        }
                        aWndData.SetFieldText( aStr, nFieldIndex );
                    }
                    delete pDlg;
                }
                break;

                default:
                {
                    // added to avoid warnings
                }
            }
        }
    }
}

//----------------------------------------------------------------------------

void ScDPLayoutDlg::NotifyFieldFocus( ScDPFieldType eType, sal_Bool bGotFocus )
{
    /*  Enable Remove/Options buttons on GetFocus in field window.
        Enable them also, if dialog is deactivated (click into document).
        The !IsActive() condition handles the case that a LoseFocus event of a
        field window would follow the Deactivate event of this dialog. */
    sal_Bool bEnable = (bGotFocus || !IsActive()) && (eType != TYPE_SELECT);

    // The TestTool may set the focus into an empty field.
    // Then the Remove/Options buttons must be disabled.
    ScDPFieldControlBase* pWnd = GetFieldWindow(eType);
    if ( bEnable && bGotFocus && pWnd && pWnd->IsEmpty() )
        bEnable = false;

    aBtnRemove.Enable( bEnable );
    aBtnOptions.Enable( bEnable );
    if( bGotFocus )
        eLastActiveType = eType;
}

//----------------------------------------------------------------------------

void ScDPLayoutDlg::NotifyMoveFieldToEnd( ScDPFieldType eToType )
{
    ScDPFieldControlBase* pWnd = GetFieldWindow(eLastActiveType);
    ScDPFieldControlBase* pToWnd = GetFieldWindow(eToType);
    if (pWnd && pToWnd && (eToType != TYPE_SELECT) && !pWnd->IsEmpty())
    {
        MoveFieldToEnd(eLastActiveType, pWnd->GetSelectedField(), eToType);

        if( pWnd->IsEmpty() )
            NotifyFieldFocus( eToType, true );
        else
            pWnd->GrabFocus();
        if( eLastActiveType == TYPE_SELECT )
            aWndSelect.SelectNext();
    }
    else
        InitFocus();
}

//----------------------------------------------------------------------------

void ScDPLayoutDlg::NotifyRemoveField( ScDPFieldType eType, size_t nFieldIndex )
{
    if( eType != TYPE_SELECT )
        RemoveField( eType, nFieldIndex );
}

Size ScDPLayoutDlg::GetStdFieldBtnSize() const
{
    // This size is static but is platform dependent.  The field button size
    // is calculated relative to the size of the OK button.
    double w = static_cast<double>(aBtnOk.GetSizePixel().Width()) * 0.70;
    return Size(static_cast<long>(w), FIELD_BTN_HEIGHT);
}

void ScDPLayoutDlg::Deactivate()
{
    /*  If the dialog has been deactivated (click into document), the LoseFocus
        event from field window disables Remove/Options buttons. Re-enable them here by
        simulating a GetFocus event. Event order of LoseFocus and Deactivate is not important.
        The last event will enable the buttons in both cases (see NotifyFieldFocus). */
    NotifyFieldFocus( eLastActiveType, true );
}

//----------------------------------------------------------------------------

sal_Bool ScDPLayoutDlg::Contains( ScDPFuncDataVec* pArr, SCsCOL nCol, size_t& nAt )
{
    if (!pArr || pArr->empty())
        return false;

    ScDPFuncDataVec::const_iterator itr, itrBeg = pArr->begin(), itrEnd = pArr->end();
    for (itr = itrBeg; itr != itrEnd; ++itr)
    {
        if ((*itr)->mnCol == nCol)
        {
            // found!
            nAt = ::std::distance(itrBeg, itr);
            return true;
        }
    }
    return false;
}

//----------------------------------------------------------------------------

void ScDPLayoutDlg::Remove( ScDPFuncDataVec* pArr, size_t nAt )
{
    if ( !pArr || (nAt>=pArr->size()) )
        return;

    pArr->erase( pArr->begin() + nAt );
}

//----------------------------------------------------------------------------

void ScDPLayoutDlg::Insert( ScDPFuncDataVec* pArr, const ScDPFuncData& rFData, size_t nAt )
{
    if (!pArr)
        return;

    ScDPFuncDataRef p (new ScDPFuncData(rFData));
    if (nAt >= pArr->size())
        pArr->push_back(p);
    else
        pArr->insert(pArr->begin() + nAt, p);
}

//----------------------------------------------------------------------------

ScDPLabelData* ScDPLayoutDlg::GetLabelData( SCsCOL nCol, size_t* pnPos )
{
    ScDPLabelData* pData = 0;
    for( ScDPLabelDataVec::iterator aIt = aLabelDataArr.begin(), aEnd = aLabelDataArr.end(); !pData && (aIt != aEnd); ++aIt )
    {
        if( aIt->mnCol == nCol )
        {
            pData = &*aIt;
            if( pnPos ) *pnPos = aIt - aLabelDataArr.begin();
        }
    }
    return pData;
}

//----------------------------------------------------------------------------

String ScDPLayoutDlg::GetLabelString( SCsCOL nCol )
{
    ScDPLabelData* pData = GetLabelData( nCol );
    DBG_ASSERT( pData, "LabelData not found" );
    if (pData)
        return pData->getDisplayName();
    return String();
}

//----------------------------------------------------------------------------

bool ScDPLayoutDlg::IsOrientationAllowed( SCsCOL nCol, ScDPFieldType eType )
{
    bool bAllowed = true;
    ScDPLabelData* pData = GetLabelData( nCol );
    DBG_ASSERT( pData, "LabelData not found" );
    if (pData)
    {
        sheet::DataPilotFieldOrientation eOrient = sheet::DataPilotFieldOrientation_HIDDEN;
        switch (eType)
        {
            case TYPE_PAGE:   eOrient = sheet::DataPilotFieldOrientation_PAGE;   break;
            case TYPE_COL:    eOrient = sheet::DataPilotFieldOrientation_COLUMN; break;
            case TYPE_ROW:    eOrient = sheet::DataPilotFieldOrientation_ROW;    break;
            case TYPE_DATA:   eOrient = sheet::DataPilotFieldOrientation_DATA;   break;
            case TYPE_SELECT: eOrient = sheet::DataPilotFieldOrientation_HIDDEN; break;
        }
        bAllowed = ScDPObject::IsOrientationAllowed( (sal_uInt16)eOrient, pData->mnFlags );
    }
    return bAllowed;
}

//----------------------------------------------------------------------------

String ScDPLayoutDlg::GetFuncString( sal_uInt16& rFuncMask, sal_Bool bIsValue )
{
    String aStr;

    if (   rFuncMask == PIVOT_FUNC_NONE
        || rFuncMask == PIVOT_FUNC_AUTO )
    {
        if ( bIsValue )
        {
            aStr = FSTR(PIVOTSTR_SUM);
            rFuncMask = PIVOT_FUNC_SUM;
        }
        else
        {
            aStr = FSTR(PIVOTSTR_COUNT);
            rFuncMask = PIVOT_FUNC_COUNT;
        }
    }
    else if ( rFuncMask == PIVOT_FUNC_SUM )		  aStr = FSTR(PIVOTSTR_SUM);
    else if ( rFuncMask == PIVOT_FUNC_COUNT )	  aStr = FSTR(PIVOTSTR_COUNT);
    else if ( rFuncMask == PIVOT_FUNC_AVERAGE )	  aStr = FSTR(PIVOTSTR_AVG);
    else if ( rFuncMask == PIVOT_FUNC_MAX )		  aStr = FSTR(PIVOTSTR_MAX);
    else if ( rFuncMask == PIVOT_FUNC_MIN )		  aStr = FSTR(PIVOTSTR_MIN);
    else if ( rFuncMask == PIVOT_FUNC_PRODUCT )	  aStr = FSTR(PIVOTSTR_PROD);
    else if ( rFuncMask == PIVOT_FUNC_COUNT_NUM ) aStr = FSTR(PIVOTSTR_COUNT2);
    else if ( rFuncMask == PIVOT_FUNC_STD_DEV )	  aStr = FSTR(PIVOTSTR_DEV);
    else if ( rFuncMask == PIVOT_FUNC_STD_DEVP )  aStr = FSTR(PIVOTSTR_DEV2);
    else if ( rFuncMask == PIVOT_FUNC_STD_VAR )	  aStr = FSTR(PIVOTSTR_VAR);
    else if ( rFuncMask == PIVOT_FUNC_STD_VARP )  aStr = FSTR(PIVOTSTR_VAR2);
    else
    {
        aStr  = ScGlobal::GetRscString( STR_TABLE_ERGEBNIS );
        aStr.AppendAscii(RTL_CONSTASCII_STRINGPARAM( " - " ));
    }

    return aStr;
}

//----------------------------------------------------------------------------

Point ScDPLayoutDlg::DlgPos2WndPos( const Point& rPt, Window& rWnd )
{
    Point aWndPt( rPt );
    aWndPt.X() = rPt.X()-rWnd.GetPosPixel().X();
    aWndPt.Y() = rPt.Y()-rWnd.GetPosPixel().Y();

    return aWndPt;
}

//----------------------------------------------------------------------------

void ScDPLayoutDlg::CalcWndSizes()
{
    // The pivot.src file only specifies the positions of the controls. Here,
    // we calculate appropriate size of each control based on how they are
    // positioned relative to each other.

    // row/column/data area sizes
    long nFldW = GetStdFieldBtnSize().Width();
    long nFldH = GetStdFieldBtnSize().Height();

    aWndData.SetSizePixel(
        Size(aWndSelect.GetPosPixel().X() - aWndData.GetPosPixel().X() - FIELD_AREA_GAP*4,
             185));

    aWndPage.SetSizePixel(
        Size(aWndData.GetSizePixel().Width() + 85,
             aWndCol.GetPosPixel().Y() - aWndPage.GetPosPixel().Y() - FIELD_AREA_GAP));
    aWndRow.SetSizePixel(
        Size(aWndData.GetPosPixel().X()-aWndRow.GetPosPixel().X() - FIELD_AREA_GAP,
             aWndData.GetSizePixel().Height()));
    aWndCol.SetSizePixel(
        Size(aWndData.GetPosPixel().X() - aWndCol.GetPosPixel().X() + aWndData.GetSizePixel().Width(),
             aWndData.GetPosPixel().Y() - aWndCol.GetPosPixel().Y() - FIELD_AREA_GAP));

    // #i29203# align right border of page window with data window
    long nDataPosX = aWndData.GetPosPixel().X() + aWndData.GetSizePixel().Width();
    aWndPage.SetPosPixel(
        Point(nDataPosX - aWndPage.GetSizePixel().Width(),
              aWndPage.GetPosPixel().Y()));

    // selection area
    long nLineSize = 10; // number of fields per column.
    long nH = OUTER_MARGIN_VER + nLineSize* nFldH + nLineSize * ROW_FIELD_BTN_GAP;
    nH += ROW_FIELD_BTN_GAP;
    nH += GetSettings().GetStyleSettings().GetScrollBarSize() + OUTER_MARGIN_VER;
    aWndSelect.SetSizePixel(
        Size(2 * nFldW + ROW_FIELD_BTN_GAP + 10, nH));

    aRectPage   = Rectangle( aWndPage.GetPosPixel(),    aWndPage.GetSizePixel() );
    aRectRow	= Rectangle( aWndRow.GetPosPixel(),		aWndRow.GetSizePixel() );
    aRectCol	= Rectangle( aWndCol.GetPosPixel(),		aWndCol.GetSizePixel() );
    aRectData	= Rectangle( aWndData.GetPosPixel(),	aWndData.GetSizePixel() );
    aRectSelect = Rectangle( aWndSelect.GetPosPixel(),	aWndSelect.GetSizePixel() );

    aWndPage.CalcSize();
    aWndRow.CalcSize();
    aWndCol.CalcSize();
    aWndData.CalcSize();
    aWndSelect.CalcSize();
}

namespace {

class PivotFieldInserter : public ::std::unary_function<void, boost::shared_ptr<ScDPFuncData> >
{
    vector<PivotField>& mrFields;
public:
    explicit PivotFieldInserter(vector<PivotField>& r, size_t nSize) : mrFields(r)
    {
        mrFields.reserve(nSize);
    }

    PivotFieldInserter(const PivotFieldInserter& r) : mrFields(r.mrFields) {}

    void operator() (const ::boost::shared_ptr<ScDPFuncData>& p)
    {
        PivotField aField;
        aField.nCol = p->mnCol;
        aField.nFuncMask = p->mnFuncMask;
        aField.maFieldRef = p->maFieldRef;
        mrFields.push_back(aField);
    }
};

}

bool ScDPLayoutDlg::GetPivotArrays(
    vector<PivotField>& rPageFields, vector<PivotField>& rColFields,
    vector<PivotField>& rRowFields, vector<PivotField>& rDataFields )
{
    vector<PivotField> aPageFields;
    for_each(aPageArr.begin(), aPageArr.end(), PivotFieldInserter(aPageFields, aPageArr.size()));

    vector<PivotField> aColFields;
    for_each(aColArr.begin(), aColArr.end(), PivotFieldInserter(aColFields, aColArr.size()));

    // default data pilot table always has an extra row field as a data layout field.
    vector<PivotField> aRowFields;
    for_each(aRowArr.begin(), aRowArr.end(), PivotFieldInserter(aRowFields, aRowArr.size()+1));
    aRowFields.push_back(PivotField(PIVOT_DATA_FIELD, 0));

    vector<PivotField> aDataFields;
    for_each(aDataArr.begin(), aDataArr.end(), PivotFieldInserter(aDataFields, aDataArr.size()));

    rPageFields.swap(aPageFields);
    rColFields.swap(aColFields);
    rRowFields.swap(aRowFields);
    rDataFields.swap(aDataFields);

    return true;
}

void ScDPLayoutDlg::UpdateSrcRange()
{
    String  aSrcStr = aEdInPos.GetText();
    sal_uInt16  nResult = ScRange().Parse(aSrcStr, pDoc, pDoc->GetAddressConvention());
    DataSrcType eSrcType = SRC_INVALID;
    ScRange aNewRange;

    if (SCA_VALID == (nResult & SCA_VALID))
    {
        // Valid source range.  Take it.
        ScRefAddress start, end;
        ConvertDoubleRef(pDoc, aSrcStr, 1,  start, end, pDoc->GetAddressConvention());
        aNewRange.aStart = start.GetAddress();
        aNewRange.aEnd = end.GetAddress();
        aEdInPos.SetRefValid(true);
        eSrcType = SRC_REF;
    }
    else
    {
        // invalid source range.  Check if this is a valid range name.
        bool bValid = false;
        ScRangeName* pRangeName = pDoc->GetRangeName();
        if (pRangeName)
        {
            OUString aUpper = ScGlobal::pCharClass->upper(aSrcStr);
            const ScRangeData* pData = pRangeName->findByUpperName(aUpper);
            if (pData)
            {
                // range name found.  Check if this is a valid reference.
                bValid = pData->IsReference(aNewRange);
            }
        }

        aEdInPos.SetRefValid(bValid);
        if (!bValid)
        {
            // All attempts have failed.  Give up.
            aBtnOk.Disable();
            return;
        }

        eSrcType = SRC_NAME;
    }

    aBtnOk.Enable();

    // Now update the data src range or range name with the dp object.
    ScSheetSourceDesc inSheet = *xDlgDPObject->GetSheetDesc();

    switch (eSrcType)
    {
        case SRC_REF:
            // data source is a range reference.
            if (inSheet.GetSourceRange() == aNewRange)
                // new range is identical to the current range.  Nothing to do.
                return;
            inSheet.SetSourceRange(aNewRange);
        break;
        case SRC_NAME:
            // data source is a range name.
            inSheet.SetRangeName(aSrcStr);
        break;
        default:
            OSL_FAIL( "Unknown source type.");
            return;
    }

    xDlgDPObject->SetSheetDesc(inSheet);
    xDlgDPObject->FillOldParam( thePivotData );
    xDlgDPObject->FillLabelData(thePivotData);

    aLabelDataArr.clear();
    aWndSelect.ClearFields();
    aWndData.ClearFields();
    aWndRow.ClearFields();
    aWndCol.ClearFields();
    aWndPage.ClearFields();

    aSelectArr.clear();
    aRowArr.clear();
    aColArr.clear();
    aDataArr.clear();
    aPageArr.clear();

    InitFields();
    RepaintFieldWindows();
}

void ScDPLayoutDlg::RepaintFieldWindows()
{
    Rectangle aRect; // currently has no effect whatsoever.
    aWndPage.Paint(aRect);
    aWndCol.Paint(aRect);
    aWndRow.Paint(aRect);
    aWndData.Paint(aRect);
}

ScDPFieldControlBase* ScDPLayoutDlg::GetFieldWindow(ScDPFieldType eType)
{
    switch (eType)
    {
        case TYPE_PAGE:
            return &aWndPage;
        case TYPE_COL:
            return &aWndCol;
        case TYPE_ROW:
            return &aWndRow;
        case TYPE_DATA:
            return &aWndData;
        case TYPE_SELECT:
            return &aWndSelect;
        default:
            ;
    }
    return NULL;
}

void ScDPLayoutDlg::GetOtherFieldWindows(ScDPFieldType eType, ScDPFieldControlBase*& rpWnd1, ScDPFieldControlBase*& rpWnd2)
{
    rpWnd1 = NULL;
    rpWnd2 = NULL;
    switch (eType)
    {
        case TYPE_PAGE:
            rpWnd1 = &aWndRow;
            rpWnd2 = &aWndCol;
            break;
        case TYPE_COL:
            rpWnd1 = &aWndPage;
            rpWnd2 = &aWndRow;
            break;
        case TYPE_ROW:
            rpWnd1 = &aWndPage;
            rpWnd2 = &aWndCol;
            break;
        default:
            ;
    }
}

ScDPLayoutDlg::ScDPFuncDataVec* ScDPLayoutDlg::GetFieldDataArray(ScDPFieldType eType)
{
    switch (eType)
    {
        case TYPE_PAGE:
            return &aPageArr;
        case TYPE_COL:
            return &aColArr;
        case TYPE_ROW:
            return &aRowArr;
        case TYPE_DATA:
            return &aDataArr;
        case TYPE_SELECT:
            return &aSelectArr;
        default:
            ;
    }
    return NULL;
}

void ScDPLayoutDlg::GetOtherDataArrays(
    ScDPFieldType eType, ScDPFuncDataVec*& rpArr1, ScDPFuncDataVec*& rpArr2)
{
    rpArr1 = NULL;
    rpArr2 = NULL;
    switch (eType)
    {
        case TYPE_PAGE:
            rpArr1 = &aRowArr;
            rpArr2 = &aColArr;
            break;
        case TYPE_COL:
            rpArr1 = &aPageArr;
            rpArr2 = &aRowArr;
            break;
        case TYPE_ROW:
            rpArr1 = &aPageArr;
            rpArr2 = &aColArr;
            break;
        default:
            ;
    }
}

//----------------------------------------------------------------------------

void ScDPLayoutDlg::SetReference( const ScRange& rRef, ScDocument* pDocP )
{
    if ( !bRefInputMode || !pEditActive )
        return;

    if ( rRef.aStart != rRef.aEnd )
        RefInputStart( pEditActive );

    if ( pEditActive == &aEdInPos )
    {
        String aRefStr;
        rRef.Format( aRefStr, SCR_ABS_3D, pDocP, pDocP->GetAddressConvention() );
        pEditActive->SetRefString( aRefStr );
    }
    else if ( pEditActive == &aEdOutPos )
    {
        String aRefStr;
        rRef.aStart.Format( aRefStr, STD_FORMAT, pDocP, pDocP->GetAddressConvention() );
        pEditActive->SetRefString( aRefStr );
    }
}

//----------------------------------------------------------------------------

void ScDPLayoutDlg::SetActive()
{
    if ( bRefInputMode )
    {
        if ( pEditActive )
            pEditActive->GrabFocus();

        if ( pEditActive == &aEdInPos )
            EdInModifyHdl( NULL );
        else if ( pEditActive == &aEdOutPos )
        EdModifyHdl( NULL );
    }
    else
    {
        GrabFocus();
    }

    RefInputDone();
}

//----------------------------------------------------------------------------
// Handler:
//----------------------------------------------------------------------------

IMPL_LINK( ScDPLayoutDlg, ClickHdl, PushButton *, pBtn )
{
    ScDPFieldControlBase* pWnd = GetFieldWindow( eLastActiveType );
    if (!pWnd)
        return 0;

    if( pBtn == &aBtnRemove )
    {
        RemoveField( eLastActiveType, pWnd->GetSelectedField() );
        if( !pWnd->IsEmpty() ) pWnd->GrabFocus();
    }
    else if( pBtn == &aBtnOptions )
    {
        NotifyDoubleClick( eLastActiveType, pWnd->GetSelectedField() );
        pWnd->GrabFocus();
    }
    return 0;
}

//----------------------------------------------------------------------------

IMPL_LINK( ScDPLayoutDlg, OkHdl, OKButton *, EMPTYARG )
{
    String		aOutPosStr( aEdOutPos.GetText() );
    ScAddress	aAdrDest;
    sal_Bool		bToNewTable = (aLbOutPos.GetSelectEntryPos() == 1);
    sal_uInt16		nResult		= !bToNewTable ? aAdrDest.Parse( aOutPosStr, pDoc, pDoc->GetAddressConvention() ) : 0;

    if (!bToNewTable && (aOutPosStr.Len() == 0 || (nResult & SCA_VALID) != SCA_VALID))
    {
        // Invalid reference.  Bail out.
        if ( !aBtnMore.GetState() )
            aBtnMore.SetState(true);

        ErrorBox(this, WinBits(WB_OK | WB_DEF_OK), ScGlobal::GetRscString(STR_INVALID_TABREF)).Execute();
        aEdOutPos.GrabFocus();
        return 0;
    }

    ScPivotParam	theOutParam;
    vector<PivotField> aPageFields;
    vector<PivotField> aColFields;
    vector<PivotField> aRowFields;
    vector<PivotField> aDataFields;

    // Convert an array of function data into an array of pivot field data.
    bool bFit = GetPivotArrays(aPageFields, aColFields, aRowFields, aDataFields);

    if (!bFit)
    {
        // General data pilot table error.  Bail out.
        ErrorBox(this, WinBits(WB_OK | WB_DEF_OK), ScGlobal::GetRscString(STR_PIVOT_ERROR)).Execute();
        return 0;
    }

    ScDPSaveData* pOldSaveData = xDlgDPObject->GetSaveData();

    ScRange aOutRange( aAdrDest );		// bToNewTable is passed separately

    ScDPSaveData aSaveData;
    aSaveData.SetIgnoreEmptyRows( aBtnIgnEmptyRows.IsChecked() );
    aSaveData.SetRepeatIfEmpty( aBtnDetectCat.IsChecked() );
    aSaveData.SetColumnGrand( aBtnTotalCol.IsChecked() );
    aSaveData.SetRowGrand( aBtnTotalRow.IsChecked() );
    aSaveData.SetFilterButton( aBtnFilter.IsChecked() );
    aSaveData.SetDrillDown( aBtnDrillDown.IsChecked() );

    uno::Reference<sheet::XDimensionsSupplier> xSource = xDlgDPObject->GetSource();

    ScDPObject::ConvertOrientation(
        aSaveData, aPageFields, sheet::DataPilotFieldOrientation_PAGE, xSource );
    ScDPObject::ConvertOrientation(
        aSaveData, aColFields, sheet::DataPilotFieldOrientation_COLUMN, xSource );
    ScDPObject::ConvertOrientation(
        aSaveData, aRowFields, sheet::DataPilotFieldOrientation_ROW, xSource );
    ScDPObject::ConvertOrientation(
        aSaveData, aDataFields, sheet::DataPilotFieldOrientation_DATA, xSource,
        &aColFields, &aRowFields, &aPageFields );

    for( ScDPLabelDataVec::const_iterator aIt = aLabelDataArr.begin(), aEnd = aLabelDataArr.end(); aIt != aEnd; ++aIt )
    {
        if( ScDPSaveDimension* pDim = aSaveData.GetExistingDimensionByName( aIt->maName ) )
        {
            pDim->SetUsedHierarchy( aIt->mnUsedHier );
            pDim->SetShowEmpty( aIt->mbShowAll );
            pDim->SetSortInfo( &aIt->maSortInfo );
            pDim->SetLayoutInfo( &aIt->maLayoutInfo );
            pDim->SetAutoShowInfo( &aIt->maShowInfo );
            ScDPSaveDimension* pOldDim = NULL;
            if (pOldSaveData)
            {
                // Transfer the existing layout names to new dimension instance.
                pOldDim = pOldSaveData->GetExistingDimensionByName(aIt->maName);
                if (pOldDim)
                {
                    const OUString* pLayoutName = pOldDim->GetLayoutName();
                    if (pLayoutName)
                        pDim->SetLayoutName(*pLayoutName);

                    const OUString* pSubtotalName = pOldDim->GetSubtotalName();
                    if (pSubtotalName)
                        pDim->SetSubtotalName(*pSubtotalName);
                }
            }

            bool bManualSort = ( aIt->maSortInfo.Mode == sheet::DataPilotFieldSortMode::MANUAL );

            // visibility of members
            for (vector<ScDPLabelData::Member>::const_iterator itr = aIt->maMembers.begin(), itrEnd = aIt->maMembers.end();
                  itr != itrEnd; ++itr)
            {
                ScDPSaveMember* pMember = pDim->GetMemberByName(itr->maName);

                // #i40054# create/access members only if flags are not default
                // (or in manual sorting mode - to keep the order)
                if (bManualSort || !itr->mbVisible || !itr->mbShowDetails)
                {
                    pMember->SetIsVisible(itr->mbVisible);
                    pMember->SetShowDetails(itr->mbShowDetails);
                }
                if (pOldDim)
                {
                    // Transfer the existing layout name.
                    ScDPSaveMember* pOldMember = pOldDim->GetMemberByName(itr->maName);
                    if (pOldMember)
                    {
                        const OUString* pLayoutName = pOldMember->GetLayoutName();
                        if (pLayoutName)
                            pMember->SetLayoutName(*pLayoutName);
                    }
                }
            }
        }
    }
    ScDPSaveDimension* pDim = aSaveData.GetDataLayoutDimension();
    if (pDim && pOldSaveData)
    {
        ScDPSaveDimension* pOldDim = pOldSaveData->GetDataLayoutDimension();
        if (pOldDim)
        {
            const OUString* pLayoutName = pOldDim->GetLayoutName();
            if (pLayoutName)
                pDim->SetLayoutName(*pLayoutName);
        }
    }

    sal_uInt16 nWhichPivot = SC_MOD()->GetPool().GetWhich( SID_PIVOT_TABLE );
    ScPivotItem aOutItem( nWhichPivot, &aSaveData, &aOutRange, bToNewTable );

    bRefInputMode = false;		// to allow deselecting when switching sheets

    SetDispatcherLock( false );
    SwitchToDocument();

    ScTabViewShell* pTabViewShell = pViewData->GetViewShell();
    pTabViewShell->SetDialogDPObject(xDlgDPObject.get());

    //	don't hide the dialog before executing the slot, instead it is used as
    //	parent for message boxes in ScTabViewShell::GetDialogParent

    const SfxPoolItem* pRet = GetBindings().GetDispatcher()->Execute(
        SID_PIVOT_TABLE, SFX_CALLMODE_SLOT | SFX_CALLMODE_RECORD, &aOutItem, 0L, 0L );

    bool bSuccess = true;
    if (pRet)
    {
        const SfxBoolItem* pItem = dynamic_cast<const SfxBoolItem*>(pRet);
        if (pItem)
            bSuccess = pItem->GetValue();
    }
    if (bSuccess)
        // Table successfully inserted.
        Close();
    else
    {
        // Table insertion failed.  Keep the dialog open.
        bRefInputMode = true;
        SetDispatcherLock(true);
    }

    return 0;
}

//----------------------------------------------------------------------------

IMPL_LINK( ScDPLayoutDlg, CancelHdl, CancelButton *, EMPTYARG )
{
    Close();
    return 0;
}

//----------------------------------------------------------------------------

IMPL_LINK( ScDPLayoutDlg, MoreClickHdl, MoreButton *, EMPTYARG )
{
    if ( aBtnMore.GetState() )
    {
        bRefInputMode = true;
        //@BugID 54702 Enablen/Disablen nur noch in Basisklasse
        //SFX_APPWINDOW->Enable();
        if ( aEdInPos.IsEnabled() )
        {
            aEdInPos.Enable();
            aEdInPos.GrabFocus();
            aEdInPos.Enable();
        }
        else
        {
        aEdOutPos.Enable();
        aEdOutPos.GrabFocus();
            aEdOutPos.Enable();
        }
    }
    else
    {
        bRefInputMode = false;
        //@BugID 54702 Enablen/Disablen nur noch in Basisklasse
        //SFX_APPWINDOW->Disable(false);		//! allgemeine Methode im ScAnyRefDlg
    }
    return 0;
}

//----------------------------------------------------------------------------

IMPL_LINK( ScDPLayoutDlg, EdModifyHdl, Edit *, EMPTYARG )
{
    String	theCurPosStr = aEdOutPos.GetText();
    sal_uInt16	nResult = ScAddress().Parse( theCurPosStr, pDoc, pDoc->GetAddressConvention() );

    if ( SCA_VALID == (nResult & SCA_VALID) )
    {
        String*	pStr	= NULL;
        sal_Bool	bFound	= false;
        sal_uInt16	i		= 0;
        sal_uInt16	nCount	= aLbOutPos.GetEntryCount();

        for ( i=2; i<nCount && !bFound; i++ )
        {
            pStr = (String*)aLbOutPos.GetEntryData( i );
            bFound = (theCurPosStr == *pStr);
        }

        if ( bFound )
            aLbOutPos.SelectEntryPos( --i );
        else
            aLbOutPos.SelectEntryPos( 0 );
    }
    return 0;
}

IMPL_LINK( ScDPLayoutDlg, EdInModifyHdl, Edit *, EMPTYARG )
{
    UpdateSrcRange();
    return 0;
}

//----------------------------------------------------------------------------

IMPL_LINK( ScDPLayoutDlg, SelAreaHdl, ListBox *, EMPTYARG )
{
    String	aString;
    sal_uInt16	nSelPos = aLbOutPos.GetSelectEntryPos();

    if ( nSelPos > 1 )
    {
        aString = *(String*)aLbOutPos.GetEntryData( nSelPos );
    }
    else if ( nSelPos == aLbOutPos.GetEntryCount()-1 ) // auf neue Tabelle?
    {
        aEdOutPos.Disable();
        aRbOutPos.Disable();
    }
    else
    {
        aEdOutPos.Enable();
        aRbOutPos.Enable();
    }

    aEdOutPos.SetText( aString );
    return 0;
}

IMPL_LINK( ScDPLayoutDlg, GetFocusHdl, Control*, pCtrl )
{
    pEditActive = NULL;
    if ( pCtrl == &aEdInPos )
        pEditActive = &aEdInPos;
    else if ( pCtrl == &aEdOutPos )
        pEditActive = &aEdOutPos;

    return 0;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
