/**************************************************************
 *
 * 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
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
 *************************************************************/



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

#include "pvlaydlg.hxx"

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

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

#include "dbdocfun.hxx"
#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" //CHINA001
#include "scabstdlg.hxx" //CHINA001

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

using namespace ::com::sun::star;
using ::rtl::OUString;

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

namespace {

const sal_uInt16 STD_FORMAT = sal_uInt16( SCA_VALID | SCA_TAB_3D | SCA_COL_ABSOLUTE | SCA_ROW_ABSOLUTE | SCA_TAB_ABSOLUTE );

OUString lclGetNameWithoutMnemonic( const FixedText& rFixedText )
{
    return MnemonicGenerator::EraseAllMnemonicChars( rFixedText.GetText() );
}

} // namespace

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

ScPivotLayoutDlg::ScPivotLayoutDlg( SfxBindings* pB, SfxChildWindow* pCW, Window* pParent, const ScDPObject& rDPObject ) :

	ScAnyRefDlg( pB, pCW, pParent, RID_SCDLG_PIVOT_LAYOUT ),

    maFlLayout( this, ScResId( FL_LAYOUT ) ),
	maScrPage( this, ScResId( SCROLL_PAGE ) ),
    maFtPage( this, ScResId( FT_PAGE ) ),
    maWndPage( this, ScResId( WND_PAGE ), maScrPage, &maFtPage, lclGetNameWithoutMnemonic( maFtPage ), PIVOTFIELDTYPE_PAGE, HID_SC_DPLAY_PAGE, POINTER_PIVOT_FIELD, 5, 2, 1, 0 ),
	maScrCol( this, ScResId( SCROLL_COL ) ),
    maFtCol( this, ScResId( FT_COL ) ),
    maWndCol( this, ScResId( WND_COL ), maScrCol, &maFtCol, lclGetNameWithoutMnemonic( maFtCol ), PIVOTFIELDTYPE_COL, HID_SC_DPLAY_COLUMN, POINTER_PIVOT_COL, 4, 2, 1, 0 ),
	maScrRow( this, ScResId( SCROLL_ROW ) ),
    maFtRow( this, ScResId( FT_ROW ) ),
    maWndRow( this, ScResId( WND_ROW ), maScrRow, &maFtRow, lclGetNameWithoutMnemonic( maFtRow ), PIVOTFIELDTYPE_ROW, HID_SC_DPLAY_ROW, POINTER_PIVOT_ROW, 1, 8, 1, 0 ),
	maScrData( this, ScResId( SCROLL_DATA ) ),
    maFtData( this, ScResId( FT_DATA ) ),
    maWndData( this, ScResId( WND_DATA ), maScrData, &maFtData, lclGetNameWithoutMnemonic( maFtData ), PIVOTFIELDTYPE_DATA, HID_SC_DPLAY_DATA, POINTER_PIVOT_FIELD, 1, 8, 4, 0 ),
    maFlSelect( this, ScResId( FL_SELECT ) ),
	maScrSelect( this, ScResId( WND_HSCROLL ) ),
    maWndSelect( this, ScResId( WND_SELECT ), maScrSelect, 0, String( ScResId( STR_SELECT ) ), PIVOTFIELDTYPE_SELECT, HID_SC_DPLAY_SELECT, POINTER_PIVOT_FIELD, 2, 10, 1, 2 ),
	maFtInfo( this, ScResId( FT_INFO ) ),

    maFlAreas( this, ScResId( FL_OUTPUT ) ),
    maFtInArea( this, ScResId( FT_INAREA) ),
    maEdInPos( this, this, ScResId( ED_INAREA) ),
    maRbInPos( this, ScResId( RB_INAREA ), &maEdInPos, this ),
	maLbOutPos( this, ScResId( LB_OUTAREA ) ),
	maFtOutArea( this, ScResId( FT_OUTAREA ) ),
    maEdOutPos( this, this, ScResId( ED_OUTAREA ) ),
	maRbOutPos( this, ScResId( RB_OUTAREA ), &maEdOutPos, this ),
	maBtnIgnEmptyRows( this, ScResId( BTN_IGNEMPTYROWS ) ),
	maBtnDetectCat( this, ScResId( BTN_DETECTCAT ) ),
	maBtnTotalCol( this, ScResId( BTN_TOTALCOL ) ),
	maBtnTotalRow( this, ScResId( BTN_TOTALROW ) ),
    maBtnFilter( this, ScResId( BTN_FILTER ) ),
    maBtnDrillDown( this, ScResId( BTN_DRILLDOWN ) ),

	maBtnOk( this, ScResId( BTN_OK ) ),
	maBtnCancel( this, ScResId( BTN_CANCEL ) ),
	maBtnHelp( this, ScResId( BTN_HELP ) ),
    maBtnRemove( this, ScResId( BTN_REMOVE ) ),
    maBtnOptions( this, ScResId( BTN_OPTIONS ) ),
	maBtnMore( this, ScResId( BTN_MORE ) ),

    mxDlgDPObject( new ScDPObject( rDPObject ) ),
	mpViewData( ((ScTabViewShell*)SfxViewShell::Current())->GetViewData() ),
	mpDoc( ((ScTabViewShell*)SfxViewShell::Current())->GetViewData()->GetDocument() ),
	mpFocusWindow( 0 ),
	mpTrackingWindow( 0 ),
    mpDropWindow( 0 ),
    mpActiveEdit( 0 ),
	mbRefInputMode( false )
{
	DBG_ASSERT( mpViewData && mpDoc, "ScPivotLayoutDlg::ScPivotLayoutDlg - missing document or view data" );

    mxDlgDPObject->SetAlive( true );     // needed to get structure information
    mxDlgDPObject->FillOldParam( maPivotData );
    mxDlgDPObject->FillLabelData( maPivotData );

    maBtnRemove.SetClickHdl( LINK( this, ScPivotLayoutDlg, ClickHdl ) );
    maBtnOptions.SetClickHdl( LINK( this, ScPivotLayoutDlg, ClickHdl ) );

    // PIVOT_MAXFUNC defined in sc/inc/dpglobal.hxx
    maFuncNames.reserve( PIVOT_MAXFUNC );
    for( sal_uInt16 i = 1; i <= PIVOT_MAXFUNC; ++i )
        maFuncNames.push_back( String( ScResId( i ) ) );

    maBtnMore.AddWindow( &maFlAreas );
    maBtnMore.AddWindow( &maFtInArea );
    maBtnMore.AddWindow( &maEdInPos );
    maBtnMore.AddWindow( &maRbInPos );
	maBtnMore.AddWindow( &maFtOutArea );
	maBtnMore.AddWindow( &maLbOutPos );
	maBtnMore.AddWindow( &maEdOutPos );
    maBtnMore.AddWindow( &maRbOutPos );
	maBtnMore.AddWindow( &maBtnIgnEmptyRows );
	maBtnMore.AddWindow( &maBtnDetectCat );
	maBtnMore.AddWindow( &maBtnTotalCol );
	maBtnMore.AddWindow( &maBtnTotalRow );
    maBtnMore.AddWindow( &maBtnFilter );
    maBtnMore.AddWindow( &maBtnDrillDown );
    maBtnMore.SetClickHdl( LINK( this, ScPivotLayoutDlg, MoreClickHdl ) );

    if( mxDlgDPObject->GetSheetDesc() )
	{
        maEdInPos.Enable();
        maRbInPos.Enable();
        ScRange aRange = mxDlgDPObject->GetSheetDesc()->aSourceRange;
        String aString;
        aRange.Format( aString, SCR_ABS_3D, mpDoc, mpDoc->GetAddressConvention() );
        maEdInPos.SetText( aString );
	}
	else
    {
        // data is not reachable, so could be a remote database
        maEdInPos.Disable();
        maRbInPos.Disable();
    }

    // #i29203# align right border of page window with data window
    long nPagePosX = maWndData.GetPosPixel().X() + maWndData.GetSizePixel().Width() - maWndPage.GetSizePixel().Width();
    maWndPage.SetPosPixel( Point( nPagePosX, maWndPage.GetPosPixel().Y() ) );
    maScrPage.SetPosPixel( Point( maScrData.GetPosPixel().X(), maScrPage.GetPosPixel().Y() ) );

    InitFieldWindows();

    maLbOutPos.SetSelectHdl( LINK( this, ScPivotLayoutDlg, SelAreaHdl ) );
    maEdOutPos.SetModifyHdl( LINK( this, ScPivotLayoutDlg, EdOutModifyHdl ) );
    maEdInPos.SetModifyHdl( LINK( this, ScPivotLayoutDlg, EdInModifyHdl ) );
    maBtnOk.SetClickHdl( LINK( this, ScPivotLayoutDlg, OkHdl ) );
    maBtnCancel.SetClickHdl( LINK( this, ScPivotLayoutDlg, CancelHdl ) );

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

		maLbOutPos.Clear();
		maLbOutPos.InsertEntry( String( ScResId( SCSTR_UNDEFINED ) ), 0 );
		maLbOutPos.InsertEntry( String( ScResId( SCSTR_NEWTABLE ) ), 1 );

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

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

	if ( maPivotData.nTab != MAXTAB+1 )
	{
		String aStr;
		ScAddress( maPivotData.nCol,
				   maPivotData.nRow,
				   maPivotData.nTab ).Format( aStr, STD_FORMAT, mpDoc, mpDoc->GetAddressConvention() );
		maEdOutPos.SetText( aStr );
		EdOutModifyHdl( 0 );
	}
	else
	{
		maLbOutPos.SelectEntryPos( maLbOutPos.GetEntryCount()-1 );
		SelAreaHdl(NULL);
	}

	maBtnIgnEmptyRows.Check( maPivotData.bIgnoreEmptyRows );
	maBtnDetectCat.Check( maPivotData.bDetectCategories );
	maBtnTotalCol.Check( maPivotData.bMakeTotalCol );
	maBtnTotalRow.Check( maPivotData.bMakeTotalRow );

    const ScDPSaveData* pSaveData = mxDlgDPObject->GetSaveData();
    maBtnFilter.Check( !pSaveData || pSaveData->GetFilterButton() );
    maBtnDrillDown.Check( !pSaveData || pSaveData->GetDrillDown() );

    // child event listener handles field movement when keyboard shortcut is pressed
    AddChildEventListener( LINK( this, ScPivotLayoutDlg, ChildEventListener ) );
    GrabFieldFocus( maWndSelect );

	FreeResource();
}

ScPivotLayoutDlg::~ScPivotLayoutDlg()
{
    RemoveChildEventListener( LINK( this, ScPivotLayoutDlg, ChildEventListener ) );

	for( sal_uInt16 i = 2, nEntries = maLbOutPos.GetEntryCount();  i < nEntries; ++i )
		delete (String*)maLbOutPos.GetEntryData( i );
}

ScDPLabelData* ScPivotLayoutDlg::GetLabelData( SCCOL nCol, size_t* pnIndex )
{
    ScDPLabelData* pLabelData = 0;
    for( ScDPLabelDataVector::iterator aIt = maLabelData.begin(), aEnd = maLabelData.end(); !pLabelData && (aIt != aEnd); ++aIt )
    {
        if( aIt->mnCol == nCol )
        {
            pLabelData = &*aIt;
            if( pnIndex )
                *pnIndex = aIt - maLabelData.begin();
        }
    }
    return pLabelData;
}

String ScPivotLayoutDlg::GetFuncString( sal_uInt16& rnFuncMask, bool bIsValue )
{
	String aStr;

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

	return aStr;
}

void ScPivotLayoutDlg::NotifyStartTracking( ScPivotFieldWindow& rSourceWindow )
{
    mpTrackingWindow = &rSourceWindow;
    mpDropWindow = 0;
    rSourceWindow.NotifyStartTracking();
    StartTracking( STARTTRACK_BUTTONREPEAT );
    SetPointer( Pointer( rSourceWindow.GetDropPointerStyle() ) );
}

void ScPivotLayoutDlg::NotifyDoubleClick( ScPivotFieldWindow& rSourceWindow )
{
    // nothing to do on double-click in selection window
    if( rSourceWindow.GetType() == PIVOTFIELDTYPE_SELECT )
        return;

    const ScPivotFuncData* pFuncData = rSourceWindow.GetSelectedFuncData();
    DBG_ASSERT( pFuncData, "ScPivotLayoutDlg::NotifyDoubleClick - invalid selection" );
    if( !pFuncData )
        return;

    ScDPLabelData* pLabelData = GetLabelData( pFuncData->mnCol );
    DBG_ASSERT( pLabelData, "ScPivotLayoutDlg::NotifyDoubleClick - missing label data" );
    if( !pLabelData )
        return;

    ScAbstractDialogFactory* pFactory = ScAbstractDialogFactory::Create();
    DBG_ASSERT( pFactory, "ScPivotLayoutDlg::NotifyDoubleClick - ScAbstractDialogFactory creation failed" );
    if( !pFactory )
        return;

    if( rSourceWindow.GetType() == PIVOTFIELDTYPE_DATA )
    {
        ::std::auto_ptr< AbstractScDPFunctionDlg > xDlg( pFactory->CreateScDPFunctionDlg(
            this, RID_SCDLG_DPDATAFIELD, maLabelData, *pLabelData, *pFuncData ) );
        if( xDlg->Execute() == RET_OK )
        {
            ScPivotFuncData aFuncData( *pFuncData );
            aFuncData.mnFuncMask = pLabelData->mnFuncMask = xDlg->GetFuncMask();
            aFuncData.maFieldRef = xDlg->GetFieldRef();
            rSourceWindow.ModifySelectedField( aFuncData );
        }
    }
    else
    {
        // list of plain names of all data fields
        ScDPNameVec aDataFieldNames;
        maWndData.WriteFieldNames( aDataFieldNames );
        // allow to modify layout options for row fields, if multiple data fields exist, or if it is not the last row field
        bool bLayout = (rSourceWindow.GetType() == PIVOTFIELDTYPE_ROW) && ((aDataFieldNames.size() > 1) || (rSourceWindow.GetSelectedIndex() + 1 < rSourceWindow.GetFieldCount()));

        ::std::auto_ptr< AbstractScDPSubtotalDlg > xDlg( pFactory->CreateScDPSubtotalDlg(
            this, RID_SCDLG_PIVOTSUBT, *mxDlgDPObject, *pLabelData, *pFuncData, aDataFieldNames, bLayout ) );
        if( xDlg->Execute() == RET_OK )
        {
            xDlg->FillLabelData( *pLabelData );
            ScPivotFuncData aFuncData( *pFuncData );
            aFuncData.mnFuncMask = pLabelData->mnFuncMask;
            rSourceWindow.ModifySelectedField( aFuncData );
        }
    }
}

void ScPivotLayoutDlg::NotifyFieldRemoved( ScPivotFieldWindow& rSourceWindow )
{
    // update focus: move to selection window, if source window is empty now
    GrabFieldFocus( rSourceWindow );
}

// protected ------------------------------------------------------------------

void ScPivotLayoutDlg::Tracking( const TrackingEvent& rTEvt )
{
	DBG_ASSERT( mpTrackingWindow, "ScPivotLayoutDlg::Tracking - missing tracking source window" );
    if( !mpTrackingWindow )
        return;

    // find target window
    const Point& rDialogPos = rTEvt.GetMouseEvent().GetPosPixel();
    ScPivotFieldWindow* pTargetWindow = dynamic_cast< ScPivotFieldWindow* >( FindWindow( rDialogPos ) );

    // check if the target orientation is allowed for this field
    if( pTargetWindow && (mpTrackingWindow != pTargetWindow) && !IsInsertAllowed( *mpTrackingWindow, *pTargetWindow ) )
        pTargetWindow = 0;

    // tracking from selection window: do not show "delete" mouse pointer
    PointerStyle eTargetPointer = pTargetWindow ? pTargetWindow->GetDropPointerStyle() :
        ((mpTrackingWindow->GetType() == PIVOTFIELDTYPE_SELECT) ? POINTER_NOTALLOWED : POINTER_PIVOT_DELETE);

    // after calculating pointer style, check if target is selection window
    if( pTargetWindow && (pTargetWindow->GetType() == PIVOTFIELDTYPE_SELECT) )
        pTargetWindow = 0;

    // notify windows about tracking
    if( mpDropWindow != pTargetWindow )
    {
        // tracking window changed
        if( mpDropWindow )
            mpDropWindow->NotifyEndTracking( ENDTRACKING_SUSPEND );
        if( pTargetWindow )
            pTargetWindow->NotifyStartTracking();
        mpDropWindow = pTargetWindow;
    }
    if( mpDropWindow )
        mpDropWindow->NotifyTracking( rDialogPos - pTargetWindow->GetPosPixel() );

    // end tracking: move or remove field
    if( rTEvt.IsTrackingEnded() )
    {
        bool bCancelled = rTEvt.IsTrackingCanceled();
        if( mpDropWindow )
        {
            mpDropWindow->NotifyEndTracking( bCancelled ? ENDTRACKING_CANCEL : ENDTRACKING_DROP );
            if( !bCancelled )
            {
                size_t nInsertIndex = mpDropWindow->GetDropIndex( rDialogPos - mpDropWindow->GetPosPixel() );
                bool bMoved = MoveField( *mpTrackingWindow, *mpDropWindow, nInsertIndex, true );
                // focus drop window, if move was successful, otherwise back to source window
                GrabFieldFocus( bMoved ? *mpDropWindow : *mpTrackingWindow );
            }
        }
        else
        {
            // drop target invalid (outside field windows): remove tracked field
            if( !bCancelled )
                mpTrackingWindow->RemoveSelectedField();
            // focus source window (or another window, if it is empty now)
            GrabFieldFocus( *mpTrackingWindow );
        }
        eTargetPointer = POINTER_ARROW;
        if( mpTrackingWindow != mpDropWindow )
            mpTrackingWindow->NotifyEndTracking( ENDTRACKING_CANCEL );
        mpTrackingWindow = mpDropWindow = 0;
    }
    SetPointer( eTargetPointer );
}

void ScPivotLayoutDlg::SetReference( const ScRange& rRef, ScDocument* pDocP )
{
    if( !mbRefInputMode || !mpActiveEdit )
        return;

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

    if( mpActiveEdit == &maEdInPos )
    {
        String aRefStr;
        rRef.Format( aRefStr, SCR_ABS_3D, pDocP, pDocP->GetAddressConvention() );
        mpActiveEdit->SetRefString( aRefStr );
    }
    else if( mpActiveEdit == &maEdOutPos )
    {
        String aRefStr;
        rRef.aStart.Format( aRefStr, STD_FORMAT, pDocP, pDocP->GetAddressConvention() );
        mpActiveEdit->SetRefString( aRefStr );
    }
}

sal_Bool ScPivotLayoutDlg::IsRefInputMode() const
{
    return mbRefInputMode;
}

void ScPivotLayoutDlg::SetActive()
{
	if( mbRefInputMode )
	{
        if( mpActiveEdit )
            mpActiveEdit->GrabFocus();

        if( mpActiveEdit == &maEdInPos )
            EdInModifyHdl( 0 );
        else if( mpActiveEdit == &maEdOutPos )
            EdOutModifyHdl( 0 );
	}
	else
	{
		GrabFocus();
	}

	RefInputDone();
}

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

// private --------------------------------------------------------------------

ScPivotFieldWindow& ScPivotLayoutDlg::GetFieldWindow( ScPivotFieldType eFieldType )
{
    switch( eFieldType )
    {
        case PIVOTFIELDTYPE_PAGE:   return maWndPage;
        case PIVOTFIELDTYPE_ROW:    return maWndRow;
        case PIVOTFIELDTYPE_COL:    return maWndCol;
        case PIVOTFIELDTYPE_DATA:   return maWndData;
        default:;
    }
    return maWndSelect;
}

bool ScPivotLayoutDlg::IsInsertAllowed( const ScPivotFieldWindow& rSourceWindow, const ScPivotFieldWindow& rTargetWindow )
{
    if( rTargetWindow.GetType() != PIVOTFIELDTYPE_SELECT )
    {
        const ScPivotFuncData* pSourceData = rSourceWindow.GetSelectedFuncData();
        ScDPLabelData* pLabelData = pSourceData ? GetLabelData( pSourceData->mnCol ) : 0;
        DBG_ASSERT( pLabelData, "ScPivotLayoutDlg::IsInsertAllowed - label data not found" );
        if( pLabelData )
        {
            sheet::DataPilotFieldOrientation eOrient = sheet::DataPilotFieldOrientation_HIDDEN;
            switch( rTargetWindow.GetType() )
            {
                case PIVOTFIELDTYPE_PAGE:   eOrient = sheet::DataPilotFieldOrientation_PAGE;    break;
                case PIVOTFIELDTYPE_COL:    eOrient = sheet::DataPilotFieldOrientation_COLUMN;  break;
                case PIVOTFIELDTYPE_ROW:    eOrient = sheet::DataPilotFieldOrientation_ROW;     break;
                case PIVOTFIELDTYPE_DATA:   eOrient = sheet::DataPilotFieldOrientation_DATA;    break;
                default:                    return false;
            }
            return ScDPObject::IsOrientationAllowed( static_cast< sal_uInt16 >( eOrient ), pLabelData->mnFlags );
        }
    }
    return false;
}

void ScPivotLayoutDlg::InitFieldWindows()
{
    maLabelData = maPivotData.maLabelArray;
    maWndSelect.ReadDataLabels( maLabelData );
    maWndPage.ReadPivotFields( maPivotData.maPageArr );
    maWndCol.ReadPivotFields( maPivotData.maColArr );
    maWndRow.ReadPivotFields( maPivotData.maRowArr );
    maWndData.ReadPivotFields( maPivotData.maDataArr );
}

void ScPivotLayoutDlg::GrabFieldFocus( ScPivotFieldWindow& rFieldWindow )
{
    if( rFieldWindow.IsEmpty() )
    {
        if( maWndSelect.IsEmpty() )
            maBtnOk.GrabFocus();
        else
            maWndSelect.GrabFocus();
    }
    else
        rFieldWindow.GrabFocus();
}

namespace {

void lclFindFieldWindow( ScPivotFieldWindow*& rpFieldWindow, const ScPivotFuncData*& rpFuncData, size_t& rnFieldIndex, ScPivotFieldWindow& rFieldWindow )
{
    ScPivotFuncDataEntry aEntry = rFieldWindow.FindFuncDataByCol( rpFuncData->mnCol );
    if( aEntry.first )
    {
        rpFieldWindow = &rFieldWindow;
        rpFuncData = aEntry.first;
        rnFieldIndex = aEntry.second;
    }
}

} // namespace

bool ScPivotLayoutDlg::MoveField( ScPivotFieldWindow& rSourceWindow, ScPivotFieldWindow& rTargetWindow, size_t nInsertIndex, bool bMoveExisting )
{
    // move inside the same window
    if( &rSourceWindow == &rTargetWindow )
        return bMoveExisting && rTargetWindow.MoveSelectedField( nInsertIndex );

    // do not insert if not supported by target window
    if( !IsInsertAllowed( rSourceWindow, rTargetWindow ) )
    {
        rSourceWindow.RemoveSelectedField();
        return false;
    }

    // move from one window to another window
    if( const ScPivotFuncData* pSourceData = rSourceWindow.GetSelectedFuncData() )
    {
        // move to page/col/row window: try to find existing field in another window
        ScPivotFieldWindow* pSourceWindow = &rSourceWindow;
        size_t nSourceIndex = rSourceWindow.GetSelectedIndex();
        if( rTargetWindow.GetType() != PIVOTFIELDTYPE_DATA )
        {
            lclFindFieldWindow( pSourceWindow, pSourceData, nSourceIndex, maWndPage );
            lclFindFieldWindow( pSourceWindow, pSourceData, nSourceIndex, maWndCol );
            lclFindFieldWindow( pSourceWindow, pSourceData, nSourceIndex, maWndRow );
        }

        // found in target window: move to new position
        if( pSourceWindow == &rTargetWindow )
            return bMoveExisting && pSourceWindow->MoveField( nSourceIndex, nInsertIndex );

        // insert field into target window
        rTargetWindow.InsertField( nInsertIndex, *pSourceData );
        // remove field from source window
        pSourceWindow->RemoveField( nSourceIndex );
        // remove field from data window, if it is the original source
        if( (rSourceWindow.GetType() == PIVOTFIELDTYPE_DATA) && (pSourceWindow->GetType() != PIVOTFIELDTYPE_DATA) )
            rSourceWindow.RemoveSelectedField();

        return true;
    }

    return false;
}

// handlers -------------------------------------------------------------------

IMPL_LINK( ScPivotLayoutDlg, ClickHdl, PushButton *, pBtn )
{
    if( mpFocusWindow )
    {
        /*  Raising sub dialogs (from the NotifyDoubleClick function) triggers
            VCL child window focus events from this sub dialog which may
            invalidate the member mpFocusWindow pointing to the target field
            window. This would cause a crash with the following call to the
            GrabFieldFocus function, if mpFocusWindow is used directly. */
        ScPivotFieldWindow& rTargetWindow = *mpFocusWindow;

        if( pBtn == &maBtnRemove )
        {
            rTargetWindow.RemoveSelectedField();
            // focus back to field window
            GrabFieldFocus( rTargetWindow );
        }
        else if( pBtn == &maBtnOptions )
    	{
            NotifyDoubleClick( rTargetWindow );
            // focus back to field window
            GrabFieldFocus( rTargetWindow );
        }
    }
    return 0;
}

IMPL_LINK( ScPivotLayoutDlg, OkHdl, OKButton *, EMPTYARG )
{
	String aOutPosStr = maEdOutPos.GetText();
	ScAddress aAdrDest;
	bool bToNewTable = maLbOutPos.GetSelectEntryPos() == 1;
	sal_uInt16 nResult = !bToNewTable ? aAdrDest.Parse( aOutPosStr, mpDoc, mpDoc->GetAddressConvention() ) : 0;

	if( bToNewTable || ((aOutPosStr.Len() > 0) && ((nResult & SCA_VALID) == SCA_VALID)) )
	{
        ScPivotFieldVector aPageFields, aColFields, aRowFields, aDataFields;
        maWndPage.WritePivotFields( aPageFields );
        maWndCol.WritePivotFields( aColFields );
        maWndRow.WritePivotFields( aRowFields );
        maWndData.WritePivotFields( aDataFields );

        // TODO: handle data field in dialog field windows?
        aRowFields.resize( aRowFields.size() + 1 );
        aRowFields.back().nCol = PIVOT_DATA_FIELD;

        ScDPSaveData* pOldSaveData = mxDlgDPObject->GetSaveData();

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

		ScDPSaveData aSaveData;
		aSaveData.SetIgnoreEmptyRows( maBtnIgnEmptyRows.IsChecked() );
		aSaveData.SetRepeatIfEmpty( maBtnDetectCat.IsChecked() );
		aSaveData.SetColumnGrand( maBtnTotalCol.IsChecked() );
		aSaveData.SetRowGrand( maBtnTotalRow.IsChecked() );
        aSaveData.SetFilterButton( maBtnFilter.IsChecked() );
        aSaveData.SetDrillDown( maBtnDrillDown.IsChecked() );

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

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

        for( ScDPLabelDataVector::const_iterator aIt = maLabelData.begin(), aEnd = maLabelData.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 (::std::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);
            }
        }

        // also transfer grand total name
        if (pOldSaveData)
        {
            const OUString* pGrandTotalName = pOldSaveData->GetGrandTotalName();
            if (pGrandTotalName)
                aSaveData.SetGrandTotalName(*pGrandTotalName);
        }

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

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

		SetDispatcherLock( false );
		SwitchToDocument();

		//	#95513# 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.
            mbRefInputMode = true;
            SetDispatcherLock(true);
        }
	}
	else
	{
		if( !maBtnMore.GetState() )
			maBtnMore.SetState( true );

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

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

IMPL_LINK( ScPivotLayoutDlg, MoreClickHdl, MoreButton *, EMPTYARG )
{
	if ( maBtnMore.GetState() )
	{
		mbRefInputMode = true;
        if ( maEdInPos.IsEnabled() )
        {
            maEdInPos.Enable();
            maEdInPos.GrabFocus();
            maEdInPos.Enable();
        }
        else
        {
    		maEdOutPos.Enable();
    		maEdOutPos.GrabFocus();
            maEdOutPos.Enable();
        }
	}
	else
	{
		mbRefInputMode = false;
	}
	return 0;
}

IMPL_LINK( ScPivotLayoutDlg, EdOutModifyHdl, Edit *, EMPTYARG )
{
	String theCurPosStr = maEdOutPos.GetText();
	sal_uInt16 nResult = ScAddress().Parse( theCurPosStr, mpDoc, mpDoc->GetAddressConvention() );

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

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

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


IMPL_LINK( ScPivotLayoutDlg, EdInModifyHdl, Edit *, EMPTYARG )
{
    String theCurPosStr = maEdInPos.GetText();
    sal_uInt16 nResult = ScRange().Parse( theCurPosStr, mpDoc, mpDoc->GetAddressConvention() );

    // invalid source range
    if( SCA_VALID != (nResult & SCA_VALID) )
        return 0;

    ScRefAddress start, end;
    ConvertDoubleRef( mpDoc, theCurPosStr, 1, start, end, mpDoc->GetAddressConvention() );
    ScRange aNewRange( start.GetAddress(), end.GetAddress() );
    ScSheetSourceDesc inSheet = *mxDlgDPObject->GetSheetDesc();

    // new range is identical to the current range
    if( inSheet.aSourceRange == aNewRange )
        return 0;

    ScTabViewShell* pTabViewShell = mpViewData->GetViewShell();
    inSheet.aSourceRange = aNewRange;
    mxDlgDPObject->SetSheetDesc( inSheet );
    mxDlgDPObject->FillOldParam( maPivotData );
    mxDlgDPObject->FillLabelData( maPivotData );

    // SetDialogDPObject does not take ownership but makes a copy internally
    pTabViewShell->SetDialogDPObject( mxDlgDPObject.get() );

    // re-initialize the field windows from the new data
    InitFieldWindows();

    return 0;
}

IMPL_LINK( ScPivotLayoutDlg, SelAreaHdl, ListBox *, EMPTYARG )
{
	String aString;
	sal_uInt16 nSelPos = maLbOutPos.GetSelectEntryPos();
	if( nSelPos > 1 )
	{
		aString = *(String*)maLbOutPos.GetEntryData( nSelPos );
	}
	else
	{
        // do not allow to specify output position, if target is "new sheet"
        bool bNewSheet = nSelPos == 1;
		maEdOutPos.Enable( !bNewSheet );
        maRbOutPos.Enable( !bNewSheet );
	}

	maEdOutPos.SetText( aString );
	return 0;
}

IMPL_LINK( ScPivotLayoutDlg, ChildEventListener, VclWindowEvent*, pEvent )
{
    Window* pWindow = pEvent->GetWindow();
    // check that this dialog is the parent of the window, to ignore focus events from sub dialogs
    if( (pEvent->GetId() == VCLEVENT_WINDOW_GETFOCUS) && pWindow && (pWindow->GetParent() == this) )
    {
        // check if old window and/or new window are field windows
        ScPivotFieldWindow* pSourceWindow = mpFocusWindow;
        ScPivotFieldWindow* pTargetWindow = dynamic_cast< ScPivotFieldWindow* >( pWindow );

        /*  Enable or disable the Remove/Options buttons. Do nothing if the
            buttons themselves get the focus.
            #128113# The TestTool may set the focus into an empty window. Then
            the Remove/Options buttons must be disabled. */
        if( (pWindow != &maBtnRemove) && (pWindow != &maBtnOptions) )
        {
            bool bEnableButtons = pTargetWindow && (pTargetWindow->GetType() != PIVOTFIELDTYPE_SELECT) && !pTargetWindow->IsEmpty();
            maBtnRemove.Enable( bEnableButtons );
            maBtnOptions.Enable( bEnableButtons );
            /*  Remember the new focus window (will not be changed, if
                Remove/Option buttons are getting focus, because they need to
                know the field window they are working on). */
            mpFocusWindow = pTargetWindow;
        }

        /*  Move the last selected field to target window, if focus changes via
            keyboard shortcut. */
        if( pSourceWindow && pTargetWindow && (pSourceWindow != pTargetWindow) && ((pTargetWindow->GetGetFocusFlags() & GETFOCUS_MNEMONIC) != 0) )
        {
            // append field in target window
            MoveField( *pSourceWindow, *pTargetWindow, pTargetWindow->GetFieldCount(), false );
            // move cursor in selection window to next field
            if( pSourceWindow->GetType() == PIVOTFIELDTYPE_SELECT )
                pSourceWindow->SelectNextField();
            // return focus to source window (if it is not empty)
            GrabFieldFocus( pSourceWindow->IsEmpty() ? *pTargetWindow : *pSourceWindow );
        }

        mpActiveEdit = dynamic_cast< ::formula::RefEdit* >( pEvent->GetWindow() );
    }
    return 0;
}

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