/**************************************************************
 * 
 * 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 ---------------------------------------------------------------

#include "scitems.hxx"
#include <vcl/msgbox.hxx>
#include <vcl/sound.hxx>

#include "gridwin.hxx"
#include "tabvwsh.hxx"
#include "docsh.hxx"
#include "viewdata.hxx"
#include "pivot.hxx"
//CHINA001 #include "pfiltdlg.hxx"
#include "uiitems.hxx"
#include "scresid.hxx"
#include "sc.hrc"
#include "globstr.hrc"
#include "pagedata.hxx"
#include "dpobject.hxx"
#include "dpsave.hxx"
#include "dpoutput.hxx"		// ScDPPositionData
#include "dpshttab.hxx"
#include "dbdocfun.hxx"
#include "dpcontrol.hxx"
#include "dpcontrol.hrc"
#include "strload.hxx"
#include "userlist.hxx"

#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
#include "scabstdlg.hxx" //CHINA001

#include <vector>
#include <hash_map>

using namespace com::sun::star;
using ::com::sun::star::sheet::DataPilotFieldOrientation;
using ::std::vector;
using ::std::auto_ptr;
using ::std::hash_map;
using ::rtl::OUString;
using ::rtl::OUStringHash;

// STATIC DATA -----------------------------------------------------------

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

DataPilotFieldOrientation ScGridWindow::GetDPFieldOrientation( SCCOL nCol, SCROW nRow ) const
{
    using namespace ::com::sun::star::sheet;

    ScDocument* pDoc = pViewData->GetDocument();
    SCTAB nTab = pViewData->GetTabNo();
    ScDPObject* pDPObj = pDoc->GetDPAtCursor(nCol, nRow, nTab);
    if (!pDPObj)
        return DataPilotFieldOrientation_HIDDEN;

    sal_uInt16 nOrient = DataPilotFieldOrientation_HIDDEN;

    // Check for page field first.
    if (nCol > 0)
    {
        // look for the dimension header left of the drop-down arrow
        long nField = pDPObj->GetHeaderDim( ScAddress( nCol-1, nRow, nTab ), nOrient );
        if ( nField >= 0 && nOrient == DataPilotFieldOrientation_PAGE )
        {
            sal_Bool bIsDataLayout = sal_False;
            String aFieldName = pDPObj->GetDimName( nField, bIsDataLayout );
            if ( aFieldName.Len() && !bIsDataLayout )
                return DataPilotFieldOrientation_PAGE;
        }
    }

    nOrient = sheet::DataPilotFieldOrientation_HIDDEN;

    // Now, check for row/column field.
    long nField = pDPObj->GetHeaderDim(ScAddress(nCol, nRow, nTab), nOrient);
    if (nField >= 0 && (nOrient == DataPilotFieldOrientation_COLUMN || nOrient == DataPilotFieldOrientation_ROW) )
    {
        sal_Bool bIsDataLayout = sal_False;
        String aFieldName = pDPObj->GetDimName(nField, bIsDataLayout);
        if (aFieldName.Len() && !bIsDataLayout)
            return static_cast<DataPilotFieldOrientation>(nOrient);
    }

    return DataPilotFieldOrientation_HIDDEN;
}

// private method for mouse button handling
sal_Bool ScGridWindow::DoPageFieldSelection( SCCOL nCol, SCROW nRow )
{
    if (GetDPFieldOrientation( nCol, nRow ) == sheet::DataPilotFieldOrientation_PAGE)
    {
        LaunchPageFieldMenu( nCol, nRow );
        return sal_True;
    }
	return sal_False;
}

bool ScGridWindow::DoAutoFilterButton( SCCOL nCol, SCROW nRow, const MouseEvent& rMEvt )
{
    ScDocument* pDoc = pViewData->GetDocument();
    SCTAB nTab = pViewData->GetTabNo();
    Point aScrPos  = pViewData->GetScrPos(nCol, nRow, eWhich);
    Point aDiffPix = rMEvt.GetPosPixel();

    aDiffPix -= aScrPos;
    sal_Bool bLayoutRTL = pDoc->IsLayoutRTL( nTab );
    if ( bLayoutRTL )
        aDiffPix.X() = -aDiffPix.X();

    long nSizeX, nSizeY;
    pViewData->GetMergeSizePixel( nCol, nRow, nSizeX, nSizeY );
    Size aScrSize(nSizeX-1, nSizeY-1);

    // Check if the mouse cursor is clicking on the popup arrow box.
    mpFilterButton.reset(new ScDPFieldButton(this, &GetSettings().GetStyleSettings(), &pViewData->GetZoomX(), &pViewData->GetZoomY(), pDoc));
    mpFilterButton->setBoundingBox(aScrPos, aScrSize, bLayoutRTL);
    mpFilterButton->setPopupLeft(bLayoutRTL);   // #i114944# AutoFilter button is left-aligned in RTL
    Point aPopupPos;
    Size aPopupSize;
    mpFilterButton->getPopupBoundingBox(aPopupPos, aPopupSize);
    Rectangle aRec(aPopupPos, aPopupSize);
    if (aRec.IsInside(rMEvt.GetPosPixel()))
    {
        if ( DoPageFieldSelection( nCol, nRow ) )
            return true;

        bool bFilterActive = IsAutoFilterActive(nCol, nRow, nTab);
        mpFilterButton->setHasHiddenMember(bFilterActive);
        mpFilterButton->setDrawBaseButton(false);
        mpFilterButton->setDrawPopupButton(true);
        mpFilterButton->setPopupPressed(true);
        HideCursor();
        mpFilterButton->draw();
        ShowCursor();
        DoAutoFilterMenue(nCol, nRow, false);
        return true;
    }

    return false;
}

void ScGridWindow::DoPushButton( SCCOL nCol, SCROW nRow, const MouseEvent& rMEvt )
{
	ScDocument* pDoc = pViewData->GetDocument();
	SCTAB nTab = pViewData->GetTabNo();

	ScDPObject*	pDPObj	= pDoc->GetDPAtCursor(nCol, nRow, nTab);

	if (pDPObj)
	{
		sal_uInt16 nOrient = sheet::DataPilotFieldOrientation_HIDDEN;
		ScAddress aPos( nCol, nRow, nTab );
		long nField = pDPObj->GetHeaderDim( aPos, nOrient );
		if ( nField >= 0 )
		{
			bDPMouse   = sal_True;
			nDPField   = nField;
			pDragDPObj = pDPObj;

            if (DPTestFieldPopupArrow(rMEvt, aPos, pDPObj))
            {    
                // field name pop up menu has been launched.  Don't activate 
                // field move.
                bDPMouse = false;
                return;
            }

			DPTestMouse( rMEvt, sal_True );
			StartTracking();
		}
		else if ( pDPObj->IsFilterButton(aPos) )
		{
			ReleaseMouse();			// may have been captured in ButtonDown

			ScQueryParam aQueryParam;
			SCTAB nSrcTab = 0;
			const ScSheetSourceDesc* pDesc = pDPObj->GetSheetDesc();
			DBG_ASSERT(pDesc, "no sheet source for filter button");
			if (pDesc)
			{
				aQueryParam = pDesc->aQueryParam;
				nSrcTab = pDesc->aSourceRange.aStart.Tab();
			}

			SfxItemSet aArgSet( pViewData->GetViewShell()->GetPool(),
										SCITEM_QUERYDATA, SCITEM_QUERYDATA );
			aArgSet.Put( ScQueryItem( SCITEM_QUERYDATA, pViewData, &aQueryParam ) );

//CHINA001			ScPivotFilterDlg* pDlg = new ScPivotFilterDlg(
//CHINA001			pViewData->GetViewShell()->GetDialogParent(),
//CHINA001			aArgSet, nSrcTab );
			ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
			DBG_ASSERT(pFact, "ScAbstractFactory create fail!");//CHINA001

			AbstractScPivotFilterDlg* pDlg = pFact->CreateScPivotFilterDlg( pViewData->GetViewShell()->GetDialogParent(), 
																			aArgSet, nSrcTab,
																			RID_SCDLG_PIVOTFILTER);
			DBG_ASSERT(pDlg, "Dialog create fail!");//CHINA001
			if ( pDlg->Execute() == RET_OK )
			{
				ScSheetSourceDesc aNewDesc;
				if (pDesc)
					aNewDesc = *pDesc;

				const ScQueryItem& rQueryItem = pDlg->GetOutputItem();
				aNewDesc.aQueryParam = rQueryItem.GetQueryData();

				ScDPObject aNewObj( *pDPObj );
				aNewObj.SetSheetDesc( aNewDesc );
				ScDBDocFunc aFunc( *pViewData->GetDocShell() );
				aFunc.DataPilotUpdate( pDPObj, &aNewObj, sal_True, sal_False );
				pViewData->GetView()->CursorPosChanged();		// shells may be switched
			}
			delete pDlg;
		}
		else
			Sound::Beep();
	}
	else
	{
		DBG_ERROR("Da is ja garnix");
	}
}

// -----------------------------------------------------------------------
//
//	Data Pilot interaction
//

void ScGridWindow::DPTestMouse( const MouseEvent& rMEvt, sal_Bool bMove )
{
	DBG_ASSERT(pDragDPObj, "pDragDPObj missing");

	//	scroll window if at edges
	//!	move this to separate method

	sal_Bool bTimer = sal_False;
	Point aPixel = rMEvt.GetPosPixel();

	SCsCOL nDx = 0;
	SCsROW nDy = 0;
	if ( aPixel.X() < 0 )
		nDx = -1;
	if ( aPixel.Y() < 0 )
		nDy = -1;
	Size aSize = GetOutputSizePixel();
	if ( aPixel.X() >= aSize.Width() )
		nDx = 1;
	if ( aPixel.Y() >= aSize.Height() )
		nDy = 1;
	if ( nDx != 0 || nDy != 0 )
	{
		UpdateDragRect( sal_False, Rectangle() );

		if ( nDx  != 0)
			pViewData->GetView()->ScrollX( nDx, WhichH(eWhich) );
		if ( nDy != 0 )
			pViewData->GetView()->ScrollY( nDy, WhichV(eWhich) );

		bTimer = sal_True;
	}

	//	---

	SCsCOL	nPosX;
	SCsROW	nPosY;
	pViewData->GetPosFromPixel( aPixel.X(), aPixel.Y(), eWhich, nPosX, nPosY );
	sal_Bool	bMouseLeft;
	sal_Bool	bMouseTop;
	pViewData->GetMouseQuadrant( aPixel, eWhich, nPosX, nPosY, bMouseLeft, bMouseTop );

	ScAddress aPos( nPosX, nPosY, pViewData->GetTabNo() );

	Rectangle aPosRect;
	sal_uInt16 nOrient;
	long nDimPos;
	sal_Bool bHasRange = pDragDPObj->GetHeaderDrag( aPos, bMouseLeft, bMouseTop, nDPField,
												aPosRect, nOrient, nDimPos );
	UpdateDragRect( bHasRange && bMove, aPosRect );

    sal_Bool bIsDataLayout;
    sal_Int32 nDimFlags = 0;
    String aDimName = pDragDPObj->GetDimName( nDPField, bIsDataLayout, &nDimFlags );
    bool bAllowed = !bHasRange || ScDPObject::IsOrientationAllowed( nOrient, nDimFlags );

	if (bMove)			// set mouse pointer
	{
        PointerStyle ePointer = POINTER_PIVOT_DELETE;
        if ( !bAllowed )
            ePointer = POINTER_NOTALLOWED;
        else if ( bHasRange )
			switch (nOrient)
			{
				case sheet::DataPilotFieldOrientation_COLUMN: ePointer = POINTER_PIVOT_COL;	break;
				case sheet::DataPilotFieldOrientation_ROW:	  ePointer = POINTER_PIVOT_ROW;	break;
				case sheet::DataPilotFieldOrientation_PAGE:
				case sheet::DataPilotFieldOrientation_DATA:	  ePointer = POINTER_PIVOT_FIELD;	break;
			}
		SetPointer( ePointer );
	}
	else				// execute change
	{
		if (!bHasRange)
			nOrient = sheet::DataPilotFieldOrientation_HIDDEN;

        if ( bIsDataLayout && ( nOrient != sheet::DataPilotFieldOrientation_COLUMN &&
                                nOrient != sheet::DataPilotFieldOrientation_ROW ) )
		{
			//	removing data layout is not allowed
			pViewData->GetView()->ErrorMessage(STR_PIVOT_MOVENOTALLOWED);
		}
        else if ( bAllowed )
		{
			ScDPSaveData aSaveData( *pDragDPObj->GetSaveData() );

			ScDPSaveDimension* pDim;
			if ( bIsDataLayout )
				pDim = aSaveData.GetDataLayoutDimension();
			else
				pDim = aSaveData.GetDimensionByName(aDimName);
			pDim->SetOrientation( nOrient );
			aSaveData.SetPosition( pDim, nDimPos );

			//!	docfunc method with ScDPSaveData as argument?

			ScDPObject aNewObj( *pDragDPObj );
			aNewObj.SetSaveData( aSaveData );
			ScDBDocFunc aFunc( *pViewData->GetDocShell() );
			// when dragging fields, allow re-positioning (bAllowMove)
			aFunc.DataPilotUpdate( pDragDPObj, &aNewObj, sal_True, sal_False, sal_True );
			pViewData->GetView()->CursorPosChanged();		// shells may be switched
		}
	}

	if (bTimer && bMove)
		pViewData->GetView()->SetTimer( this, rMEvt );			// repeat event
	else
		pViewData->GetView()->ResetTimer();
}

bool ScGridWindow::DPTestFieldPopupArrow(const MouseEvent& rMEvt, const ScAddress& rPos, ScDPObject* pDPObj)
{
    sal_Bool bLayoutRTL = pViewData->GetDocument()->IsLayoutRTL( pViewData->GetTabNo() );

    // Get the geometry of the cell.
    Point aScrPos = pViewData->GetScrPos(rPos.Col(), rPos.Row(), eWhich);
    long nSizeX, nSizeY;
    pViewData->GetMergeSizePixel(rPos.Col(), rPos.Row(), nSizeX, nSizeY);
    Size aScrSize(nSizeX-1, nSizeY-1);

    // Check if the mouse cursor is clicking on the popup arrow box.
    ScDPFieldButton aBtn(this, &GetSettings().GetStyleSettings());
    aBtn.setBoundingBox(aScrPos, aScrSize, bLayoutRTL);
    aBtn.setPopupLeft(false);   // DataPilot popup is always right-aligned for now
    Point aPopupPos;
    Size aPopupSize;
    aBtn.getPopupBoundingBox(aPopupPos, aPopupSize);
    Rectangle aRec(aPopupPos, aPopupSize);
    if (aRec.IsInside(rMEvt.GetPosPixel()))
    {
        // Mouse cursor inside the popup arrow box.  Launch the field menu.
        DPLaunchFieldPopupMenu(OutputToScreenPixel(aScrPos), aScrSize, rPos, pDPObj);
        return true;
    }

    return false;
}

namespace {

struct DPFieldPopupData : public ScDPFieldPopupWindow::ExtendedData
{
    ScPivotParam    maDPParam;
    ScDPObject*     mpDPObj;
    long            mnDim;
};

class DPFieldPopupOKAction : public ScMenuFloatingWindow::Action
{
public:
    explicit DPFieldPopupOKAction(ScGridWindow* p) : 
        mpGridWindow(p) {}

    virtual void execute()
    {
        mpGridWindow->UpdateDPFromFieldPopupMenu();
    }
private:
    ScGridWindow* mpGridWindow;
};

class PopupSortAction : public ScMenuFloatingWindow::Action
{
public:
    enum SortType { ASCENDING, DESCENDING, CUSTOM };

    explicit PopupSortAction(const ScAddress& rPos, SortType eType, sal_uInt16 nUserListIndex, ScTabViewShell* pViewShell) :
        maPos(rPos), meType(eType), mnUserListIndex(nUserListIndex), mpViewShell(pViewShell) {}

    virtual void execute()
    {
        switch (meType)
        {
            case ASCENDING:
                mpViewShell->DataPilotSort(maPos, true);
            break;
            case DESCENDING:
                mpViewShell->DataPilotSort(maPos, false);
            break;
            case CUSTOM:
                mpViewShell->DataPilotSort(maPos, true, &mnUserListIndex);
            break;
            default:
                ;
        }
    }

private:
    ScAddress       maPos;
    SortType        meType;
    sal_uInt16      mnUserListIndex;
    ScTabViewShell* mpViewShell;
};

}

bool lcl_GetLabelIndex( size_t& rLabelIndex, long nDimension, const ScDPLabelDataVector& rLabelArray )
{
    size_t n = rLabelArray.size();
    for (size_t i = 0; i < n; ++i)
        if (static_cast<long>(rLabelArray[i].mnCol) == nDimension)
        {
            rLabelIndex = i;
            return true;
        }
    return false;    
}

void ScGridWindow::DPLaunchFieldPopupMenu(
    const Point& rScrPos, const Size& rScrSize, const ScAddress& rPos, ScDPObject* pDPObj)
{
    // We need to get the list of field members.
    auto_ptr<DPFieldPopupData> pDPData(new DPFieldPopupData);
    pDPObj->FillLabelData(pDPData->maDPParam);
    pDPData->mpDPObj = pDPObj;

    sal_uInt16 nOrient;
    pDPData->mnDim = pDPObj->GetHeaderDim(rPos, nOrient);

    // #i116457# FillLabelData skips empty column names, so mnDim can't be used directly as index into maLabelArray.
    size_t nLabelIndex = 0;
    if (!lcl_GetLabelIndex( nLabelIndex, pDPData->mnDim, pDPData->maDPParam.maLabelArray ))
        return;

    const ScDPLabelData& rLabelData = pDPData->maDPParam.maLabelArray[nLabelIndex];

    mpDPFieldPopup.reset(new ScDPFieldPopupWindow(this, pViewData->GetDocument()));
    mpDPFieldPopup->setName(OUString::createFromAscii("Pivot table field member popup"));
    mpDPFieldPopup->setExtendedData(pDPData.release());
    mpDPFieldPopup->setOKAction(new DPFieldPopupOKAction(this));
    {
        // Populate field members.
        size_t n = rLabelData.maMembers.size();
        mpDPFieldPopup->setMemberSize(n);
        for (size_t i = 0; i < n; ++i)
        {
            const ScDPLabelData::Member& rMem = rLabelData.maMembers[i];
            mpDPFieldPopup->addMember(rMem.getDisplayName(), rMem.mbVisible);
        }
        mpDPFieldPopup->initMembers();
    }

    vector<OUString> aUserSortNames;
    ScUserList* pUserList = ScGlobal::GetUserList();
    if (pUserList)
    {
        sal_uInt16 n = pUserList->GetCount();
        aUserSortNames.reserve(n);
        for (sal_uInt16 i = 0; i < n; ++i)
        {
            ScUserListData* pData = static_cast<ScUserListData*>((*pUserList)[i]);
            aUserSortNames.push_back(pData->GetString());
        }
    }

    // Populate the menus.
    ScTabViewShell* pViewShell = pViewData->GetViewShell();
    mpDPFieldPopup->addMenuItem(
        ScRscStrLoader(RID_POPUP_FILTER, STR_MENU_SORT_ASC).GetString(), true, 
        new PopupSortAction(rPos, PopupSortAction::ASCENDING, 0, pViewShell));
    mpDPFieldPopup->addMenuItem(
        ScRscStrLoader(RID_POPUP_FILTER, STR_MENU_SORT_DESC).GetString(), true,
        new PopupSortAction(rPos, PopupSortAction::DESCENDING, 0, pViewShell));
    ScMenuFloatingWindow* pSubMenu = mpDPFieldPopup->addSubMenuItem(
        ScRscStrLoader(RID_POPUP_FILTER, STR_MENU_SORT_CUSTOM).GetString(), !aUserSortNames.empty());

    if (pSubMenu && !aUserSortNames.empty())
    {
        size_t n = aUserSortNames.size();
        for (size_t i = 0; i < n; ++i)
        {    
            pSubMenu->addMenuItem(
                aUserSortNames[i], true, 
                new PopupSortAction(rPos, PopupSortAction::CUSTOM, static_cast<sal_uInt16>(i), pViewShell));
        }
    }

    sal_Bool bLayoutRTL = pViewData->GetDocument()->IsLayoutRTL( pViewData->GetTabNo() );

    Rectangle aCellRect(rScrPos, rScrSize);
    const Size& rPopupSize = mpDPFieldPopup->getWindowSize();
    if (bLayoutRTL)
    {
        // RTL: rScrPos is logical-left (visual right) position, always right-align with that
        aCellRect.SetPos(Point(rScrPos.X() - rPopupSize.Width() + 1, rScrPos.Y()));
    }
    else if (rScrSize.getWidth() > rPopupSize.getWidth())
    {
        // If the cell width is larger than the popup window width, launch it 
        // right-aligned with the cell.
        long nXOffset = rScrSize.getWidth() - rPopupSize.getWidth();
        aCellRect.SetPos(Point(rScrPos.X() + nXOffset, rScrPos.Y()));
    }
    mpDPFieldPopup->SetPopupModeEndHdl( LINK(this, ScGridWindow, PopupModeEndHdl) );
    mpDPFieldPopup->StartPopupMode(aCellRect, (FLOATWIN_POPUPMODE_DOWN | FLOATWIN_POPUPMODE_GRABFOCUS));
}

void ScGridWindow::UpdateDPFromFieldPopupMenu()
{
    typedef hash_map<OUString, OUString, OUStringHash> MemNameMapType;
    typedef hash_map<OUString, bool, OUStringHash> MemVisibilityType;

    if (!mpDPFieldPopup.get())
        return;

    DPFieldPopupData* pDPData = static_cast<DPFieldPopupData*>(mpDPFieldPopup->getExtendedData());
    if (!pDPData)
        return;

    ScDPObject* pDPObj = pDPData->mpDPObj;
    ScDPObject aNewDPObj(*pDPObj);
    aNewDPObj.BuildAllDimensionMembers();
    ScDPSaveData* pSaveData = aNewDPObj.GetSaveData();

    sal_Bool bIsDataLayout;
    String aDimName = pDPObj->GetDimName(pDPData->mnDim, bIsDataLayout);
    ScDPSaveDimension* pDim = pSaveData->GetDimensionByName(aDimName);
    if (!pDim)
        return;

    size_t nLabelIndex = 0;
    lcl_GetLabelIndex( nLabelIndex, pDPData->mnDim, pDPData->maDPParam.maLabelArray );

    // Build a map of layout names to original names.
    const ScDPLabelData& rLabelData = pDPData->maDPParam.maLabelArray[nLabelIndex];
    MemNameMapType aMemNameMap;
    for (vector<ScDPLabelData::Member>::const_iterator itr = rLabelData.maMembers.begin(), itrEnd = rLabelData.maMembers.end();
           itr != itrEnd; ++itr)
        aMemNameMap.insert(MemNameMapType::value_type(itr->maLayoutName, itr->maName));

    // The raw result may contain a mixture of layout names and original names.
    MemVisibilityType aRawResult;
    mpDPFieldPopup->getResult(aRawResult);

    MemVisibilityType aResult;
    for (MemVisibilityType::const_iterator itr = aRawResult.begin(), itrEnd = aRawResult.end(); itr != itrEnd; ++itr)
    {
        MemNameMapType::const_iterator itrNameMap = aMemNameMap.find(itr->first);
        if (itrNameMap == aMemNameMap.end())
            // This is an original member name.  Use it as-is.
            aResult.insert(MemVisibilityType::value_type(itr->first, itr->second));
        else
        {
            // This is a layout name.  Get the original member name and use it.
            aResult.insert(MemVisibilityType::value_type(itrNameMap->second, itr->second));
        }
    }
    pDim->UpdateMemberVisibility(aResult);

    ScDBDocFunc aFunc(*pViewData->GetDocShell());
    aFunc.DataPilotUpdate(pDPObj, &aNewDPObj, true, false);
}

void ScGridWindow::DPMouseMove( const MouseEvent& rMEvt )
{
	DPTestMouse( rMEvt, sal_True );
}

void ScGridWindow::DPMouseButtonUp( const MouseEvent& rMEvt )
{
	bDPMouse = sal_False;
	ReleaseMouse();

	DPTestMouse( rMEvt, sal_False );
	SetPointer( Pointer( POINTER_ARROW ) );
}

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

void ScGridWindow::UpdateDragRect( sal_Bool bShowRange, const Rectangle& rPosRect )
{
	SCCOL nStartX = ( rPosRect.Left()   >= 0 ) ? static_cast<SCCOL>(rPosRect.Left())   : SCCOL_MAX;
	SCROW nStartY = ( rPosRect.Top()    >= 0 ) ? static_cast<SCROW>(rPosRect.Top())    : SCROW_MAX;
	SCCOL nEndX   = ( rPosRect.Right()  >= 0 ) ? static_cast<SCCOL>(rPosRect.Right())  : SCCOL_MAX;
	SCROW nEndY   = ( rPosRect.Bottom() >= 0 ) ? static_cast<SCROW>(rPosRect.Bottom()) : SCROW_MAX;

	if ( bShowRange == bDragRect && nDragStartX == nStartX && nDragEndX == nEndX &&
									nDragStartY == nStartY && nDragEndY == nEndY )
	{
		return;			// everything unchanged
	}

	// if ( bDragRect )
	//	DrawDragRect( nDragStartX, nDragStartY, nDragEndX, nDragEndY, sal_False );
	if ( bShowRange )
	{
		nDragStartX = nStartX;
		nDragStartY = nStartY;
		nDragEndX = nEndX;
		nDragEndY = nEndY;
		bDragRect = sal_True;
		// DrawDragRect( nDragStartX, nDragStartY, nDragEndX, nDragEndY, sal_False );
	}
	else
		bDragRect = sal_False;

    UpdateDragRectOverlay();
}

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

//	Page-Break-Modus

sal_uInt16 ScGridWindow::HitPageBreak( const Point& rMouse, ScRange* pSource,
									SCCOLROW* pBreak, SCCOLROW* pPrev )
{
	sal_uInt16 nFound = SC_PD_NONE;		// 0
	ScRange aSource;
	SCCOLROW nBreak = 0;
	SCCOLROW nPrev = 0;

	ScPageBreakData* pPageData = pViewData->GetView()->GetPageBreakData();
	if ( pPageData )
	{
		sal_Bool bHori = sal_False;
		sal_Bool bVert = sal_False;
        SCCOL nHitX = 0;
        SCROW nHitY = 0;

		long nMouseX = rMouse.X();
		long nMouseY = rMouse.Y();
        SCsCOL nPosX;
        SCsROW nPosY;
        pViewData->GetPosFromPixel( nMouseX, nMouseY, eWhich, nPosX, nPosY );
		Point aTL = pViewData->GetScrPos( nPosX, nPosY, eWhich );
		Point aBR = pViewData->GetScrPos( nPosX+1, nPosY+1, eWhich );

		//	Horizontal mehr Toleranz als vertikal, weil mehr Platz ist
		if ( nMouseX <= aTL.X() + 4 )
		{
			bHori = sal_True;
			nHitX = nPosX;
		}
		else if ( nMouseX >= aBR.X() - 6 )
		{
			bHori = sal_True;
			nHitX = nPosX+1;					// linker Rand der naechsten Zelle
		}
		if ( nMouseY <= aTL.Y() + 2 )
		{
			bVert = sal_True;
			nHitY = nPosY;
		}
		else if ( nMouseY >= aBR.Y() - 4 )
		{
			bVert = sal_True;
			nHitY = nPosY+1;					// oberer Rand der naechsten Zelle
		}

		if ( bHori || bVert )
		{
            sal_uInt16 nCount = sal::static_int_cast<sal_uInt16>( pPageData->GetCount() );
			for (sal_uInt16 nPos=0; nPos<nCount && !nFound; nPos++)
			{
				ScPrintRangeData& rData = pPageData->GetData(nPos);
				ScRange aRange = rData.GetPrintRange();
				sal_Bool bLHit = ( bHori && nHitX == aRange.aStart.Col() );
				sal_Bool bRHit = ( bHori && nHitX == aRange.aEnd.Col() + 1 );
				sal_Bool bTHit = ( bVert && nHitY == aRange.aStart.Row() );
				sal_Bool bBHit = ( bVert && nHitY == aRange.aEnd.Row() + 1 );
				sal_Bool bInsideH = ( nPosX >= aRange.aStart.Col() && nPosX <= aRange.aEnd.Col() );
				sal_Bool bInsideV = ( nPosY >= aRange.aStart.Row() && nPosY <= aRange.aEnd.Row() );

				if ( bLHit )
				{
					if ( bTHit )
						nFound = SC_PD_RANGE_TL;
					else if ( bBHit )
						nFound = SC_PD_RANGE_BL;
					else if ( bInsideV )
						nFound = SC_PD_RANGE_L;
				}
				else if ( bRHit )
				{
					if ( bTHit )
						nFound = SC_PD_RANGE_TR;
					else if ( bBHit )
						nFound = SC_PD_RANGE_BR;
					else if ( bInsideV )
						nFound = SC_PD_RANGE_R;
				}
				else if ( bTHit && bInsideH )
					nFound = SC_PD_RANGE_T;
				else if ( bBHit && bInsideH )
					nFound = SC_PD_RANGE_B;
				if (nFound)
					aSource = aRange;

				//	Umbrueche

				if ( bVert && bInsideH && !nFound )
				{
					size_t nRowCount = rData.GetPagesY();
					const SCROW* pRowEnd = rData.GetPageEndY();
					for (size_t nRowPos=0; nRowPos+1<nRowCount; nRowPos++)
						if ( pRowEnd[nRowPos]+1 == nHitY )
						{
							nFound = SC_PD_BREAK_V;
							aSource = aRange;
							nBreak = nHitY;
							if ( nRowPos )
								nPrev = pRowEnd[nRowPos-1]+1;
							else
								nPrev = aRange.aStart.Row();
						}
				}
				if ( bHori && bInsideV && !nFound )
				{
					size_t nColCount = rData.GetPagesX();
					const SCCOL* pColEnd = rData.GetPageEndX();
					for (size_t nColPos=0; nColPos+1<nColCount; nColPos++)
						if ( pColEnd[nColPos]+1 == nHitX )
						{
							nFound = SC_PD_BREAK_H;
							aSource = aRange;
							nBreak = nHitX;
							if ( nColPos )
								nPrev = pColEnd[nColPos-1]+1;
							else
								nPrev = aRange.aStart.Col();
						}
				}
			}
		}
	}

	if (pSource)
		*pSource = aSource;		// Druckbereich
	if (pBreak)
		*pBreak = nBreak;		// X/Y Position des verchobenen Seitenumbruchs
	if (pPrev)
		*pPrev = nPrev;			// X/Y Anfang der Seite, die am Umbruch zuende ist
	return nFound;
}

void ScGridWindow::PagebreakMove( const MouseEvent& rMEvt, sal_Bool bUp )
{
	//!	Scrolling und Umschalten mit RFMouseMove zusammenfassen !
	//!	(Weginvertieren vor dem Scrolling ist anders)

	//	Scrolling

	sal_Bool bTimer = sal_False;
	Point aPos = rMEvt.GetPosPixel();
	SCsCOL nDx = 0;
	SCsROW nDy = 0;
	if ( aPos.X() < 0 ) nDx = -1;
	if ( aPos.Y() < 0 ) nDy = -1;
	Size aSize = GetOutputSizePixel();
	if ( aPos.X() >= aSize.Width() )
		nDx = 1;
	if ( aPos.Y() >= aSize.Height() )
		nDy = 1;
	if ( nDx != 0 || nDy != 0 )
	{
		if ( bPagebreakDrawn )			// weginvertieren
		{
			// DrawDragRect( aPagebreakDrag.aStart.Col(), aPagebreakDrag.aStart.Row(),
			//				aPagebreakDrag.aEnd.Col(), aPagebreakDrag.aEnd.Row(), sal_False );
			bPagebreakDrawn = sal_False;
            UpdateDragRectOverlay();
		}

		if ( nDx != 0 ) pViewData->GetView()->ScrollX( nDx, WhichH(eWhich) );
		if ( nDy != 0 ) pViewData->GetView()->ScrollY( nDy, WhichV(eWhich) );
		bTimer = sal_True;
	}

	//	Umschalten bei Fixierung (damit Scrolling funktioniert)

	if ( eWhich == pViewData->GetActivePart() )		//??
	{
		if ( pViewData->GetHSplitMode() == SC_SPLIT_FIX )
			if ( nDx > 0 )
			{
				if ( eWhich == SC_SPLIT_TOPLEFT )
					pViewData->GetView()->ActivatePart( SC_SPLIT_TOPRIGHT );
				else if ( eWhich == SC_SPLIT_BOTTOMLEFT )
					pViewData->GetView()->ActivatePart( SC_SPLIT_BOTTOMRIGHT );
			}

		if ( pViewData->GetVSplitMode() == SC_SPLIT_FIX )
			if ( nDy > 0 )
			{
				if ( eWhich == SC_SPLIT_TOPLEFT )
					pViewData->GetView()->ActivatePart( SC_SPLIT_BOTTOMLEFT );
				else if ( eWhich == SC_SPLIT_TOPRIGHT )
					pViewData->GetView()->ActivatePart( SC_SPLIT_BOTTOMRIGHT );
			}
	}

	//	ab hier neu

	//	gesucht wird eine Position zwischen den Zellen (vor nPosX / nPosY)
	SCsCOL nPosX;
	SCsROW nPosY;
	pViewData->GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
	sal_Bool bLeft, bTop;
	pViewData->GetMouseQuadrant( aPos, eWhich, nPosX, nPosY, bLeft, bTop );
	if ( !bLeft ) ++nPosX;
	if ( !bTop )  ++nPosY;

	sal_Bool bBreak = ( nPagebreakMouse == SC_PD_BREAK_H || nPagebreakMouse == SC_PD_BREAK_V );
	sal_Bool bHide = sal_False;
	sal_Bool bToEnd = sal_False;
	ScRange aDrawRange = aPagebreakSource;
	if ( bBreak )
	{
		if ( nPagebreakMouse == SC_PD_BREAK_H )
		{
			if ( nPosX > aPagebreakSource.aStart.Col() &&
				 nPosX <= aPagebreakSource.aEnd.Col() + 1 )		// ans Ende ist auch erlaubt
			{
				bToEnd = ( nPosX == aPagebreakSource.aEnd.Col() + 1 );
				aDrawRange.aStart.SetCol( nPosX );
				aDrawRange.aEnd.SetCol( nPosX - 1 );
			}
			else
				bHide = sal_True;
		}
		else
		{
			if ( nPosY > aPagebreakSource.aStart.Row() &&
				 nPosY <= aPagebreakSource.aEnd.Row() + 1 )		// ans Ende ist auch erlaubt
			{
				bToEnd = ( nPosY == aPagebreakSource.aEnd.Row() + 1 );
				aDrawRange.aStart.SetRow( nPosY );
				aDrawRange.aEnd.SetRow( nPosY - 1 );
			}
			else
				bHide = sal_True;
		}
	}
	else
	{
		if ( nPagebreakMouse & SC_PD_RANGE_L )
			aDrawRange.aStart.SetCol( nPosX );
		if ( nPagebreakMouse & SC_PD_RANGE_T )
			aDrawRange.aStart.SetRow( nPosY );
		if ( nPagebreakMouse & SC_PD_RANGE_R )
		{
			if ( nPosX > 0 )
				aDrawRange.aEnd.SetCol( nPosX-1 );
			else
				bHide = sal_True;
		}
		if ( nPagebreakMouse & SC_PD_RANGE_B )
		{
			if ( nPosY > 0 )
				aDrawRange.aEnd.SetRow( nPosY-1 );
			else
				bHide = sal_True;
		}
		if ( aDrawRange.aStart.Col() > aDrawRange.aEnd.Col() ||
			 aDrawRange.aStart.Row() > aDrawRange.aEnd.Row() )
			bHide = sal_True;
	}

	if ( !bPagebreakDrawn || bUp || aDrawRange != aPagebreakDrag )
	{
		//	zeichnen...

		if ( bPagebreakDrawn )
		{
			// weginvertieren
			// DrawDragRect( aPagebreakDrag.aStart.Col(), aPagebreakDrag.aStart.Row(),
			//				aPagebreakDrag.aEnd.Col(), aPagebreakDrag.aEnd.Row(), sal_False );
			bPagebreakDrawn = sal_False;
		}
		aPagebreakDrag = aDrawRange;
		if ( !bUp && !bHide )
		{
			// hininvertieren
			// DrawDragRect( aPagebreakDrag.aStart.Col(), aPagebreakDrag.aStart.Row(),
			//				aPagebreakDrag.aEnd.Col(), aPagebreakDrag.aEnd.Row(), sal_False );
			bPagebreakDrawn = sal_True;
		}
        UpdateDragRectOverlay();
	}

	//	bei ButtonUp die Aenderung ausfuehren

	if ( bUp )
	{
		ScViewFunc* pViewFunc = pViewData->GetView();
		ScDocShell* pDocSh = pViewData->GetDocShell();
		ScDocument* pDoc = pDocSh->GetDocument();
		SCTAB nTab = pViewData->GetTabNo();
		sal_Bool bUndo (pDoc->IsUndoEnabled());

		if ( bBreak )
		{
			sal_Bool bColumn = ( nPagebreakMouse == SC_PD_BREAK_H );
			SCCOLROW nNew = bColumn ? static_cast<SCCOLROW>(nPosX) : static_cast<SCCOLROW>(nPosY);
			if ( nNew != nPagebreakBreak )
			{
				if (bUndo)
				{
					String aUndo = ScGlobal::GetRscString( STR_UNDO_DRAG_BREAK );
					pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo );
				}

				sal_Bool bGrow = !bHide && nNew > nPagebreakBreak;
				if ( bColumn )
				{
                    if (pDoc->HasColBreak(static_cast<SCCOL>(nPagebreakBreak), nTab) & BREAK_MANUAL)
					{
						ScAddress aOldAddr( static_cast<SCCOL>(nPagebreakBreak), nPosY, nTab );
						pViewFunc->DeletePageBreak( sal_True, sal_True, &aOldAddr, sal_False );
					}
					if ( !bHide && !bToEnd )	// am Ende nicht
					{
						ScAddress aNewAddr( static_cast<SCCOL>(nNew), nPosY, nTab );
						pViewFunc->InsertPageBreak( sal_True, sal_True, &aNewAddr, sal_False );
					}
					if ( bGrow )
					{
						//	vorigen Break auf hart, und Skalierung aendern
                        bool bManualBreak = (pDoc->HasColBreak(static_cast<SCCOL>(nPagebreakPrev), nTab) & BREAK_MANUAL);
                        if ( static_cast<SCCOL>(nPagebreakPrev) > aPagebreakSource.aStart.Col() && !bManualBreak )
						{
							ScAddress aPrev( static_cast<SCCOL>(nPagebreakPrev), nPosY, nTab );
							pViewFunc->InsertPageBreak( sal_True, sal_True, &aPrev, sal_False );
						}

						if (!pDocSh->AdjustPrintZoom( ScRange(
									  static_cast<SCCOL>(nPagebreakPrev),0,nTab, static_cast<SCCOL>(nNew-1),0,nTab ) ))
							bGrow = sal_False;
					}
				}
				else
				{
                    if (pDoc->HasRowBreak(nPagebreakBreak, nTab) & BREAK_MANUAL)
					{
						ScAddress aOldAddr( nPosX, nPagebreakBreak, nTab );
						pViewFunc->DeletePageBreak( sal_False, sal_True, &aOldAddr, sal_False );
					}
					if ( !bHide && !bToEnd )	// am Ende nicht
					{
						ScAddress aNewAddr( nPosX, nNew, nTab );
						pViewFunc->InsertPageBreak( sal_False, sal_True, &aNewAddr, sal_False );
					}
					if ( bGrow )
					{
						//	vorigen Break auf hart, und Skalierung aendern
                        bool bManualBreak = (pDoc->HasRowBreak(nPagebreakPrev, nTab) & BREAK_MANUAL);
                        if ( nPagebreakPrev > aPagebreakSource.aStart.Row() && !bManualBreak )
						{
							ScAddress aPrev( nPosX, nPagebreakPrev, nTab );
							pViewFunc->InsertPageBreak( sal_False, sal_True, &aPrev, sal_False );
						}

						if (!pDocSh->AdjustPrintZoom( ScRange(
									  0,nPagebreakPrev,nTab, 0,nNew-1,nTab ) ))
							bGrow = sal_False;
					}
				}

				if (bUndo)
				{
					pDocSh->GetUndoManager()->LeaveListAction();
				}

				if (!bGrow)		// sonst in AdjustPrintZoom schon passiert
				{
					pViewFunc->UpdatePageBreakData( sal_True );
					pDocSh->SetDocumentModified();
				}
			}
		}
		else if ( bHide || aPagebreakDrag != aPagebreakSource )
		{
			//	Druckbereich setzen

			String aNewRanges;
			sal_uInt16 nOldCount = pDoc->GetPrintRangeCount( nTab );
			if ( nOldCount )
			{
				for (sal_uInt16 nPos=0; nPos<nOldCount; nPos++)
				{
					const ScRange* pOld = pDoc->GetPrintRange( nTab, nPos );
					if ( pOld )
					{
						String aTemp;
						if ( *pOld != aPagebreakSource )
							pOld->Format( aTemp, SCA_VALID );
						else if ( !bHide )
							aPagebreakDrag.Format( aTemp, SCA_VALID );
						if (aTemp.Len())
						{
							if ( aNewRanges.Len() )
								aNewRanges += ';';
							aNewRanges += aTemp;
						}
					}
				}
			}
			else if (!bHide)
				aPagebreakDrag.Format( aNewRanges, SCA_VALID );

            pViewFunc->SetPrintRanges( pDoc->IsPrintEntireSheet( nTab ), &aNewRanges, NULL, NULL, sal_False );
		}
	}

	//	Timer fuer Scrolling

	if (bTimer && !bUp)
		pViewData->GetView()->SetTimer( this, rMEvt );			// Event wiederholen
	else
		pViewData->GetView()->ResetTimer();
}