/**************************************************************
 * 
 * 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 <sfx2/dispatch.hxx>
#include <vcl/help.hxx>
#include <tools/poly.hxx>
#include <svtools/colorcfg.hxx>

#include "scresid.hxx"
#include "sc.hrc"
#include "tabvwsh.hxx"
#include "hdrcont.hxx"
#include "scmod.hxx"		// Optionen
#include "inputopt.hxx"		// Optionen
#include "gridmerg.hxx"
#include "document.hxx"

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

#define SC_DRAG_MIN		2

//	passes in paint
//	(selection left/right must be first because the continuous lines
//	are partly overwritten later)

#define SC_HDRPAINT_SEL_RIGHT	0
#define SC_HDRPAINT_SEL_LEFT	1
#define SC_HDRPAINT_TOP			2
#define SC_HDRPAINT_SEL_TOP		3
#define SC_HDRPAINT_SEL_BOTTOM	4
#define SC_HDRPAINT_BOTTOM		5
#define SC_HDRPAINT_TEXT		6
#define SC_HDRPAINT_COUNT		7

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

ScHeaderControl::ScHeaderControl( Window* pParent, SelectionEngine* pSelectionEngine,
									SCCOLROW nNewSize, sal_uInt16 nNewFlags ) :
			Window		( pParent ),
			pSelEngine	( pSelectionEngine ),
			nFlags		( nNewFlags ),
			bVertical	( (nNewFlags & HDR_VERTICAL) != 0 ),
			nSize		( nNewSize ),
			nMarkStart	( 0 ),
			nMarkEnd	( 0 ),
			bMarkRange	( sal_False ),
			bDragging	( sal_False ),
			bIgnoreMove	( sal_False )
{
    // --- RTL --- no default mirroring for this window, the spreadsheet itself
    // is also not mirrored
    // #107811# mirror the vertical window for correct border drawing
    // #106948# table layout depends on sheet format, not UI setting, so the
	// borders of the vertical window have to be handled manually, too.
    EnableRTL( sal_False );

	aNormFont = GetFont();
	aNormFont.SetTransparent( sal_True );		//! WEIGHT_NORMAL hart setzen ???
	aBoldFont = aNormFont;
	aBoldFont.SetWeight( WEIGHT_BOLD );

	SetFont(aBoldFont);
	bBoldSet = sal_True;

	Size aSize = LogicToPixel( Size(
		GetTextWidth( String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("8888")) ),
		GetTextHeight() ) );
	aSize.Width()  += 4;	// Platz fuer hervorgehobene Umrandung
	aSize.Height() += 3;
	SetSizePixel( aSize );

	nWidth = nSmallWidth = aSize.Width();
	nBigWidth = LogicToPixel( Size( GetTextWidth(
		String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("8888888")) ), 0 ) ).Width() + 5;

	SetBackground();	// sonst Probleme auf OS/2 !?!?!
}

void ScHeaderControl::SetWidth( long nNew )
{
	DBG_ASSERT( bVertical, "SetDigits nur fuer Zeilenkoepfe erlaubt" );
	if ( nNew != nWidth )
	{
		Size aSize( nNew, GetSizePixel().Height() );	// Hoehe nicht aendern
		SetSizePixel( aSize );

		nWidth = nNew;

		Invalidate();		// neu zentrieren
	}
}

ScHeaderControl::~ScHeaderControl()
{
}

void ScHeaderControl::DoPaint( SCCOLROW nStart, SCCOLROW nEnd )
{
	sal_Bool bLayoutRTL = IsLayoutRTL();
	long nLayoutSign = bLayoutRTL ? -1 : 1;

	Rectangle aRect( Point(0,0), GetOutputSizePixel() );
	if ( bVertical )
	{
        aRect.Top() = GetScrPos( nStart )-nLayoutSign;      // extra pixel for line at top of selection
		aRect.Bottom() = GetScrPos( nEnd+1 )-nLayoutSign;
	}
	else
	{
        aRect.Left() = GetScrPos( nStart )-nLayoutSign;     // extra pixel for line left of selection
		aRect.Right() = GetScrPos( nEnd+1 )-nLayoutSign;
	}
	Invalidate(aRect);
}

void ScHeaderControl::SetMark( sal_Bool bNewSet, SCCOLROW nNewStart, SCCOLROW nNewEnd )
{
	sal_Bool bEnabled = SC_MOD()->GetInputOptions().GetMarkHeader();	//! cachen?
	if (!bEnabled)
		bNewSet = sal_False;

	//	Variablen setzen

	sal_Bool bOldSet	 = bMarkRange;
	SCCOLROW nOldStart = nMarkStart;
	SCCOLROW nOldEnd	 = nMarkEnd;
	PutInOrder( nNewStart, nNewEnd );
	bMarkRange = bNewSet;
	nMarkStart = nNewStart;
	nMarkEnd   = nNewEnd;

	//	Paint

	if ( bNewSet )
	{
		if ( bOldSet )
		{
			if ( nNewStart == nOldStart )
			{
				if ( nNewEnd != nOldEnd )
					DoPaint( Min( nNewEnd, nOldEnd ) + 1, Max( nNewEnd, nOldEnd ) );
				// sonst nix
			}
			else if ( nNewEnd == nOldEnd )
				DoPaint( Min( nNewStart, nOldStart ), Max( nNewStart, nOldStart ) - 1 );
			else if ( nNewStart > nOldEnd || nNewEnd < nOldStart )
			{
				//	zwei Bereiche...
				DoPaint( nOldStart, nOldEnd );
				DoPaint( nNewStart, nNewEnd );
			}
			else				//	irgendwie ueberlappend... (kommt eh nicht oft vor)
				DoPaint( Min( nNewStart, nOldStart ), Max( nNewEnd, nOldEnd ) );
		}
		else
			DoPaint( nNewStart, nNewEnd );		//	komplett neu
	}
	else if ( bOldSet )
		DoPaint( nOldStart, nOldEnd );			//	komplett aufheben

	//	sonst war nix, is nix
}

long ScHeaderControl::GetScrPos( SCCOLROW nEntryNo )
{
	long nScrPos;

	long nMax = ( bVertical ? GetOutputSizePixel().Height() : GetOutputSizePixel().Width() ) + 1;
	if (nEntryNo >= nSize)
		nScrPos = nMax;
	else
	{
		nScrPos = 0;
		for (SCCOLROW i=GetPos(); i<nEntryNo && nScrPos<nMax; i++)
		{
			sal_uInt16 nAdd = GetEntrySize(i);
			if (nAdd)
				nScrPos += nAdd;
			else
			{
				SCCOLROW nHidden = GetHiddenCount(i);
				if (nHidden > 0)
					i += nHidden - 1;
			}
		}
	}

	if ( IsLayoutRTL() )
		nScrPos = nMax - nScrPos - 2;

	return nScrPos;
}

// draw a rectangle across the window's width/height, with the outer part in a lighter color

void ScHeaderControl::DrawShadedRect( long nStart, long nEnd, const Color& rBaseColor )
{
    Color aWhite( COL_WHITE );

    Color aInner( rBaseColor );             // highlight color, unchanged
    Color aCenter( rBaseColor );
    aCenter.Merge( aWhite, 0xd0 );          // lighten up a bit
    Color aOuter( rBaseColor );
    aOuter.Merge( aWhite, 0xa0 );           // lighten up more

    if ( IsMirrored() )
        std::swap( aInner, aOuter );        // just swap colors instead of positions

    Size aWinSize = GetSizePixel();
    long nBarSize = bVertical ? aWinSize.Width() : aWinSize.Height();
    long nCenterPos = (nBarSize / 2) - 1;

    SetLineColor();
    SetFillColor( aOuter );
    if (bVertical)
        DrawRect( Rectangle( 0, nStart, nCenterPos-1, nEnd ) );
    else
        DrawRect( Rectangle( nStart, 0, nEnd, nCenterPos-1 ) );
    SetFillColor( aCenter );
    if (bVertical)
        DrawRect( Rectangle( nCenterPos, nStart, nCenterPos, nEnd ) );
    else
        DrawRect( Rectangle( nStart, nCenterPos, nEnd, nCenterPos ) );
    SetFillColor( aInner );
    if (bVertical)
        DrawRect( Rectangle( nCenterPos+1, nStart, nBarSize-1, nEnd ) );
    else
        DrawRect( Rectangle( nStart, nCenterPos+1, nEnd, nBarSize-1 ) );
}

//
//		Paint
//

void ScHeaderControl::Paint( const Rectangle& rRect )
{
	//	fuer VCL ist es wichtig, wenig Aufrufe zu haben, darum werden die aeusseren
	//	Linien zusammengefasst

	const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
    sal_Bool bHighContrast = rStyleSettings.GetHighContrastMode();
	sal_Bool bDark = rStyleSettings.GetFaceColor().IsDark();
	// Use the same distinction for bDark as in Window::DrawSelectionBackground

	Color aTextColor = rStyleSettings.GetButtonTextColor();
	Color aSelTextColor = rStyleSettings.GetHighlightTextColor();
	aNormFont.SetColor( aTextColor );
    if ( bHighContrast )
        aBoldFont.SetColor( aTextColor );
    else
        aBoldFont.SetColor( aSelTextColor );
	SetTextColor( ( bBoldSet && !bHighContrast ) ? aSelTextColor : aTextColor );

    Color aBlack( COL_BLACK );
    Color aSelLineColor = rStyleSettings.GetHighlightColor();
    aSelLineColor.Merge( aBlack, 0xe0 );        // darken just a little bit

	sal_Bool bLayoutRTL = IsLayoutRTL();
	long nLayoutSign = bLayoutRTL ? -1 : 1;
	sal_Bool bMirrored = IsMirrored();

//	const FunctionSet*	pFuncSet = pSelEngine->GetFunctionSet();
	String				aString;
	sal_uInt16				nBarSize;
	Point				aScrPos;
	Size				aTextSize;
//	Size				aSize = GetOutputSizePixel();

	if (bVertical)
		nBarSize = (sal_uInt16) GetSizePixel().Width();
	else
		nBarSize = (sal_uInt16) GetSizePixel().Height();

	SCCOLROW	nPos = GetPos();

	long nPStart = bVertical ? rRect.Top() : rRect.Left();
	long nPEnd = bVertical ? rRect.Bottom() : rRect.Right();

	long nTransStart = nPEnd + 1;
	long nTransEnd = 0;

	long nInitScrPos = 0;
	if ( bLayoutRTL )
	{
		long nTemp = nPStart;		// swap nPStart / nPEnd
		nPStart = nPEnd;
		nPEnd = nTemp;
		nTemp = nTransStart;		// swap nTransStart / nTransEnd
		nTransStart = nTransEnd;
		nTransEnd = nTemp;
		if ( bVertical )			// start loops from the end
			nInitScrPos = GetSizePixel().Height() - 1;
		else
			nInitScrPos = GetSizePixel().Width() - 1;
	}

	//	aeussere Linien komplett durchzeichnen
	//	Zuerst Ende der letzten Zelle finden

//	long nLineEnd = -1;
	long nLineEnd = nInitScrPos - nLayoutSign;

	for (SCCOLROW i=nPos; i<nSize; i++)
	{
		sal_uInt16 nSizePix = GetEntrySize( i );
		if (nSizePix)
		{
			nLineEnd += nSizePix * nLayoutSign;

			if ( bMarkRange && i >= nMarkStart && i <= nMarkEnd )
			{
				long nLineStart = nLineEnd - ( nSizePix - 1 ) * nLayoutSign;
				if ( nLineStart * nLayoutSign < nTransStart * nLayoutSign )
					nTransStart = nLineStart;
				if ( nLineEnd * nLayoutSign > nTransEnd * nLayoutSign )
					nTransEnd = nLineEnd;
			}

			if ( nLineEnd * nLayoutSign > nPEnd * nLayoutSign )
			{
				nLineEnd = nPEnd;
				break;
			}
		}
		else
		{
			SCCOLROW nHidden = GetHiddenCount(i);
			if (nHidden > 0)
				i += nHidden - 1;
		}
	}

	//	background is different for entry area and behind the entries

	Rectangle aFillRect;
	SetLineColor();

	if ( nLineEnd * nLayoutSign >= nInitScrPos * nLayoutSign )
	{
        if ( bHighContrast )
        {
            // high contrast: single-color background
            SetFillColor( rStyleSettings.GetFaceColor() );
            if ( bVertical )
                aFillRect = Rectangle( 0, nInitScrPos, nBarSize-1, nLineEnd );
            else
                aFillRect = Rectangle( nInitScrPos, 0, nLineEnd, nBarSize-1 );
            DrawRect( aFillRect );
        }
        else
        {
            // normal: 3-part background
            DrawShadedRect( nInitScrPos, nLineEnd, rStyleSettings.GetFaceColor() );
        }
	}

	if ( nLineEnd * nLayoutSign < nPEnd * nLayoutSign )
	{
        SetFillColor( SC_MOD()->GetColorConfig().GetColorValue(svtools::APPBACKGROUND).nColor );
		if ( bVertical )
			aFillRect = Rectangle( 0, nLineEnd+nLayoutSign, nBarSize-1, nPEnd );
		else
			aFillRect = Rectangle( nLineEnd+nLayoutSign, 0, nPEnd, nBarSize-1 );
		DrawRect( aFillRect );
	}

	if ( nLineEnd * nLayoutSign >= nPStart * nLayoutSign )
	{
        if ( nTransEnd * nLayoutSign >= nTransStart * nLayoutSign )
        {
            if ( bHighContrast )
            {
                if ( bDark )
                {
                    //	solid grey background for dark face color is drawn before lines

                    SetLineColor();
                    SetFillColor( COL_LIGHTGRAY );
                    if (bVertical)
                        DrawRect( Rectangle( 0, nTransStart, nBarSize-1, nTransEnd ) );
                    else
                        DrawRect( Rectangle( nTransStart, 0, nTransEnd, nBarSize-1 ) );
                }
            }
            else
            {
                // background for selection

                DrawShadedRect( nTransStart, nTransEnd, rStyleSettings.GetHighlightColor() );
            }
        }

#if 0
		// 3D border is no longer used
		SetLineColor( rStyleSettings.GetLightColor() );
		if (bVertical)
			DrawLine( Point( 0, nPStart ), Point( 0, nLineEnd ) );
		else
			DrawLine( Point( nPStart, 0 ), Point( nLineEnd, 0 ) );
#endif

		SetLineColor( rStyleSettings.GetDarkShadowColor() );
		if (bVertical)
		{
			long nDarkPos = bMirrored ? 0 : nBarSize-1;
			DrawLine( Point( nDarkPos, nPStart ), Point( nDarkPos, nLineEnd ) );
		}
		else
			DrawLine( Point( nPStart, nBarSize-1 ), Point( nLineEnd, nBarSize-1 ) );

        // line in different color for selection
        if ( nTransEnd * nLayoutSign >= nTransStart * nLayoutSign && !bHighContrast )
        {
            SetLineColor( aSelLineColor );
            if (bVertical)
            {
                long nDarkPos = bMirrored ? 0 : nBarSize-1;
                DrawLine( Point( nDarkPos, nTransStart ), Point( nDarkPos, nTransEnd ) );
            }
            else
                DrawLine( Point( nTransStart, nBarSize-1 ), Point( nTransEnd, nBarSize-1 ) );
        }
	}

	//
	//	loop through entries several times to avoid changing the line color too often
	//	and to allow merging of lines
	//

	ScGridMerger aGrid( this, 1, 1 );

	//	start at SC_HDRPAINT_BOTTOM instead of 0 - selection doesn't get different
	//	borders, light border at top isn't used anymore
    //  use SC_HDRPAINT_SEL_BOTTOM for different color

    for (sal_uInt16 nPass = SC_HDRPAINT_SEL_BOTTOM; nPass < SC_HDRPAINT_COUNT; nPass++)
	{
		//	set line color etc. before entry loop
		switch ( nPass )
		{
            case SC_HDRPAINT_SEL_BOTTOM:
                // same as non-selected for high contrast
                SetLineColor( bHighContrast ? rStyleSettings.GetDarkShadowColor() : aSelLineColor );
                break;
			case SC_HDRPAINT_BOTTOM:
				SetLineColor( rStyleSettings.GetDarkShadowColor() );
				break;
			case SC_HDRPAINT_TEXT:
                // DrawSelectionBackground is used only for high contrast on light background
                if ( nTransEnd * nLayoutSign >= nTransStart * nLayoutSign && bHighContrast && !bDark )
				{
					//	Transparent selection background is drawn after lines, before text.
					//	#109814# Use DrawSelectionBackground to make sure there is a visible
					//	difference. The case of a dark face color, where DrawSelectionBackground
					//	would just paint over the lines, is handled separately (bDark).
					//	Otherwise, GetHighlightColor is used with 80% transparency.
					//	The window's background color (SetBackground) has to be the background
					//	of the cell area, for the contrast comparison in DrawSelectionBackground.

					Rectangle aTransRect;
					if (bVertical)
						aTransRect = Rectangle( 0, nTransStart, nBarSize-1, nTransEnd );
					else
						aTransRect = Rectangle( nTransStart, 0, nTransEnd, nBarSize-1 );
					SetBackground( Color( rStyleSettings.GetFaceColor() ) );
					DrawSelectionBackground( aTransRect, 0, sal_True, sal_False, sal_False );
					SetBackground();
				}
				break;
		}

		SCCOLROW	nCount=0;
		long	nScrPos=nInitScrPos;
		do
		{
			if (bVertical)
				aScrPos = Point( 0, nScrPos );
			else
				aScrPos = Point( nScrPos, 0 );

			SCCOLROW	nEntryNo = nCount + nPos;
			if ( nEntryNo >= nSize )				// MAXCOL/MAXROW
				nScrPos = nPEnd + nLayoutSign;		//	beyond nPEnd -> stop
			else
			{
				sal_uInt16 nSizePix = GetEntrySize( nEntryNo );

				if (nSizePix == 0)
				{
					SCCOLROW nHidden = GetHiddenCount(nEntryNo);
					if (nHidden > 0)
						nCount += nHidden - 1;
				}
				else if ((nScrPos+nSizePix*nLayoutSign)*nLayoutSign >= nPStart*nLayoutSign)
				{
					Point aEndPos(aScrPos);
					if (bVertical)
						aEndPos = Point( aScrPos.X()+nBarSize-1, aScrPos.Y()+(nSizePix-1)*nLayoutSign );
					else
						aEndPos = Point( aScrPos.X()+(nSizePix-1)*nLayoutSign, aScrPos.Y()+nBarSize-1 );

					sal_Bool bMark = bMarkRange && nEntryNo >= nMarkStart && nEntryNo <= nMarkEnd;
                    sal_Bool bNextToMark = bMarkRange && nEntryNo + 1 >= nMarkStart && nEntryNo <= nMarkEnd;

					switch ( nPass )
					{
                        case SC_HDRPAINT_SEL_BOTTOM:
						case SC_HDRPAINT_BOTTOM:
                            if ( nPass == ( bNextToMark ? SC_HDRPAINT_SEL_BOTTOM : SC_HDRPAINT_BOTTOM ) )
                            {
                                if (bVertical)
                                    aGrid.AddHorLine( aScrPos.X(), aEndPos.X(), aEndPos.Y() );
                                else
                                    aGrid.AddVerLine( aEndPos.X(), aScrPos.Y(), aEndPos.Y() );

                                //  thick bottom for hidden rows
                                //  (drawn directly, without aGrid)
                                if ( nEntryNo+1 < nSize )
                                    if ( GetEntrySize(nEntryNo+1)==0 )
                                    {
                                        if (bVertical)
                                            DrawLine( Point(aScrPos.X(),aEndPos.Y()-nLayoutSign),
                                                      Point(aEndPos.X(),aEndPos.Y()-nLayoutSign) );
                                        else
                                            DrawLine( Point(aEndPos.X()-nLayoutSign,aScrPos.Y()),
                                                      Point(aEndPos.X()-nLayoutSign,aEndPos.Y()) );
                                    }
                            }
							break;

						case SC_HDRPAINT_TEXT:
							if ( nSizePix > 1 )		// minimal check for small columns/rows
							{
								if ( bMark != bBoldSet )
								{
									if (bMark)
										SetFont(aBoldFont);
									else
										SetFont(aNormFont);
									bBoldSet = bMark;
								}
								aString = GetEntryText( nEntryNo );
								aTextSize.Width() = GetTextWidth( aString );
								aTextSize.Height() = GetTextHeight();

								Point aTxtPos(aScrPos);
								if (bVertical)
								{
									aTxtPos.X() += (nBarSize-aTextSize.Width())/2;
									aTxtPos.Y() += (nSizePix*nLayoutSign-aTextSize.Height())/2;
									if ( bMirrored )
										aTxtPos.X() += 1;	// dark border is left instead of right
								}
								else
								{
									aTxtPos.X() += (nSizePix*nLayoutSign-aTextSize.Width()+1)/2;
                                    aTxtPos.Y() += (nBarSize-aTextSize.Height())/2;
								}
								DrawText( aTxtPos, aString );
							}
							break;
					}

					//	bei Selektion der ganzen Zeile/Spalte:
					//	InvertRect( Rectangle( aScrPos, aEndPos ) );
				}
				nScrPos += nSizePix * nLayoutSign;		// also if before the visible area
			}
			++nCount;
		}
		while ( nScrPos * nLayoutSign <= nPEnd * nLayoutSign );

		aGrid.Flush();
	}
}

//
//		Maus - Handling
//

SCCOLROW ScHeaderControl::GetMousePos( const MouseEvent& rMEvt, sal_Bool& rBorder )
{
	sal_Bool	bFound=sal_False;
	SCCOLROW	nCount = 1;
	SCCOLROW	nPos = GetPos();
	SCCOLROW	nHitNo = nPos;
	long	nScrPos;
	long	nMousePos = bVertical ? rMEvt.GetPosPixel().Y() : rMEvt.GetPosPixel().X();
	long	nDif;
	Size	aSize = GetOutputSizePixel();
	long	nWinSize = bVertical ? aSize.Height() : aSize.Width();

	sal_Bool bLayoutRTL = IsLayoutRTL();
	long nLayoutSign = bLayoutRTL ? -1 : 1;
	long nEndPos = bLayoutRTL ? -1 : nWinSize;

	nScrPos = GetScrPos( nPos ) - nLayoutSign;
	do
	{
		SCCOLROW nEntryNo = nCount + nPos;

//		nScrPos = GetScrPos( nEntryNo ) - 1;

        if (nEntryNo > nSize)
            nScrPos = nEndPos + nLayoutSign;
		else
			nScrPos += GetEntrySize( nEntryNo - 1 ) * nLayoutSign;		//! GetHiddenCount() ??

		nDif = nMousePos - nScrPos;
		if (nDif >= -2 && nDif <= 2 && nCount > 0)
		{
			bFound=sal_True;
			nHitNo=nEntryNo-1;
		}
        else if (nDif * nLayoutSign >= 0 && nEntryNo < nSize)
			nHitNo = nEntryNo;
		++nCount;
	}
	while ( nScrPos * nLayoutSign < nEndPos * nLayoutSign && nDif * nLayoutSign > 0 );

	rBorder = bFound;
	return nHitNo;
}

bool ScHeaderControl::IsSelectionAllowed(SCCOLROW nPos) const
{
    ScTabViewShell* pViewSh = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
    if (!pViewSh)
        return false;

    ScViewData* pViewData = pViewSh->GetViewData();
    sal_uInt16 nTab = pViewData->GetTabNo();
    ScDocument* pDoc = pViewData->GetDocument();
    const ScTableProtection* pProtect = pDoc->GetTabProtection(nTab);
    bool bSelectAllowed = true;
    if ( pProtect && pProtect->isProtected() )
    {
        // This sheet is protected.  Check if a context menu is allowed on this cell.
        bool bCellsProtected = false;
        if (bVertical)
        {    
            // row header
            SCROW nRPos = static_cast<SCROW>(nPos);
            bCellsProtected = pDoc->HasAttrib(0, nRPos, nTab, MAXCOL, nRPos, nTab, HASATTR_PROTECTED);
        }
        else
        {    
            // column header
            SCCOL nCPos = static_cast<SCCOL>(nPos);
            bCellsProtected = pDoc->HasAttrib(nCPos, 0, nTab, nCPos, MAXROW, nTab, HASATTR_PROTECTED);
        }

        bool bSelProtected   = pProtect->isOptionEnabled(ScTableProtection::SELECT_LOCKED_CELLS);
        bool bSelUnprotected = pProtect->isOptionEnabled(ScTableProtection::SELECT_UNLOCKED_CELLS);

        if (bCellsProtected)
            bSelectAllowed = bSelProtected;
        else
            bSelectAllowed = bSelUnprotected;
    }
    return bSelectAllowed;
}

void ScHeaderControl::MouseButtonDown( const MouseEvent& rMEvt )
{
	if (IsDisabled())
		return;

	bIgnoreMove = sal_False;
	SelectWindow();

	sal_Bool bFound;
	SCCOLROW nHitNo = GetMousePos( rMEvt, bFound );
    if (!IsSelectionAllowed(nHitNo))
        return;

	if ( bFound && rMEvt.IsLeft() && ResizeAllowed() )
	{
		nDragNo = nHitNo;
		sal_uInt16 nClicks = rMEvt.GetClicks();
		if ( nClicks && nClicks%2==0 )
		{
			SetEntrySize( nDragNo, HDR_SIZE_OPTIMUM );
			SetPointer( Pointer( POINTER_ARROW ) );
		}
		else
		{
			if (bVertical)
				nDragStart = rMEvt.GetPosPixel().Y();
			else
				nDragStart = rMEvt.GetPosPixel().X();
			nDragPos = nDragStart;
			ShowDragHelp();
			DrawInvert( nDragPos );

			// CaptureMouse();
			StartTracking();
			bDragging = sal_True;
			bDragMoved = sal_False;
		}
	}
	else if (rMEvt.IsLeft())
	{
		pSelEngine->SetWindow( this );
		Point aPoint;
		Rectangle aVis( aPoint,GetOutputSizePixel() );
		if (bVertical)
			aVis.Left() = LONG_MIN, aVis.Right() = LONG_MAX;
		else
			aVis.Top() = LONG_MIN, aVis.Bottom() = LONG_MAX;
		pSelEngine->SetVisibleArea( aVis );

		SetMarking( sal_True );		//	muss vor SelMouseButtonDown sein
		pSelEngine->SelMouseButtonDown( rMEvt );

		//	#74215# In column/row headers a simple click already is a selection.
		//	-> Call SelMouseMove to ensure CreateAnchor is called (and DestroyAnchor
		//	if the next click is somewhere else with Control key).
		pSelEngine->SelMouseMove( rMEvt );

		if (IsMouseCaptured())
		{
			//	Tracking statt CaptureMouse, damit sauber abgebrochen werden kann
			//!	Irgendwann sollte die SelectionEngine selber StartTracking rufen!?!
			ReleaseMouse();
			StartTracking();
		}
	}
}

void ScHeaderControl::MouseButtonUp( const MouseEvent& rMEvt )
{
	if ( IsDisabled() )
		return;

	SetMarking( sal_False );
	bIgnoreMove = sal_False;
//    sal_Bool bFound;
//    SCCOLROW nHitNo = GetMousePos( rMEvt, bFound );

	if ( bDragging )
	{
		DrawInvert( nDragPos );
		ReleaseMouse();
		bDragging	= sal_False;

		long nScrPos	= GetScrPos( nDragNo );
		long nMousePos 	= bVertical ? rMEvt.GetPosPixel().Y() : rMEvt.GetPosPixel().X();
		sal_Bool bLayoutRTL = IsLayoutRTL();
		long nNewWidth  = bLayoutRTL ? ( nScrPos - nMousePos + 1 )
									 : ( nMousePos + 2 - nScrPos );

		if ( nNewWidth < 0 /* && !IsSelected(nDragNo) */ )
		{
            SCCOLROW nStart = 0;
			SCCOLROW nEnd = nDragNo;
			while (nNewWidth < 0)
			{
				nStart = nDragNo;
				if (nDragNo>0)
				{
					--nDragNo;
					nNewWidth += GetEntrySize( nDragNo );	//! GetHiddenCount() ???
				}
				else
					nNewWidth = 0;
			}
			HideEntries( nStart, nEnd );
		}
		else
		{
			if (nNewWidth<0) nNewWidth=0;
			if (bDragMoved)
				SetEntrySize( nDragNo, (sal_uInt16) nNewWidth );
		}
	}
	else
	{
		pSelEngine->SelMouseButtonUp( rMEvt );
		ReleaseMouse();
	}
}

void ScHeaderControl::MouseMove( const MouseEvent& rMEvt )
{
	if ( IsDisabled() )
	{
		SetPointer( Pointer( POINTER_ARROW ) );
		return;
	}

	sal_Bool bFound;
    (void)GetMousePos( rMEvt, bFound );

	if ( bDragging )
	{
		long nNewPos = bVertical ? rMEvt.GetPosPixel().Y() : rMEvt.GetPosPixel().X();
		if ( nNewPos != nDragPos )
		{
			DrawInvert( nDragPos );
			nDragPos = nNewPos;
			ShowDragHelp();
			DrawInvert( nDragPos );

			if (nDragPos <= nDragStart-SC_DRAG_MIN || nDragPos >= nDragStart+SC_DRAG_MIN)
				bDragMoved = sal_True;
		}
	}
	else
	{
		if ( bFound && rMEvt.GetButtons()==0 && ResizeAllowed() )
			SetPointer( Pointer( bVertical ? POINTER_VSIZEBAR : POINTER_HSIZEBAR ) );
		else
			SetPointer( Pointer( POINTER_ARROW ) );

		if (!bIgnoreMove)
			pSelEngine->SelMouseMove( rMEvt );
	}
}

void ScHeaderControl::Tracking( const TrackingEvent& rTEvt )
{
	//	Weil die SelectionEngine kein Tracking kennt, die Events nur auf
	//	die verschiedenen MouseHandler verteilen...

	if ( rTEvt.IsTrackingCanceled() )
		StopMarking();
	else if ( rTEvt.IsTrackingEnded() )
		MouseButtonUp( rTEvt.GetMouseEvent() );
	else
		MouseMove( rTEvt.GetMouseEvent() );
}

void ScHeaderControl::Command( const CommandEvent& rCEvt )
{
	sal_uInt16 nCmd = rCEvt.GetCommand();
	if ( nCmd == COMMAND_CONTEXTMENU )
	{
		StopMarking();		// Selektion / Dragging beenden

		//	Popup ausfuehren

		ScTabViewShell* pViewSh = PTR_CAST( ScTabViewShell,
											SfxViewShell::Current() );
		if ( pViewSh )
		{
            if ( rCEvt.IsMouseEvent() )
            {
                // #i18735# select the column/row under the mouse pointer
                ScViewData* pViewData = pViewSh->GetViewData();

                SelectWindow();     // also deselects drawing objects, stops draw text edit
                if ( pViewData->HasEditView( pViewData->GetActivePart() ) )
                    SC_MOD()->InputEnterHandler();  // always end edit mode

                MouseEvent aMEvt( rCEvt.GetMousePosPixel() );
                sal_Bool bBorder;
                SCCOLROW nPos = GetMousePos( aMEvt, bBorder );
                if (!IsSelectionAllowed(nPos))
                    // Selecting this cell is not allowed, neither is context menu.
                    return;

                SCTAB nTab = pViewData->GetTabNo();
                ScRange aNewRange;
                if ( bVertical )
                    aNewRange = ScRange( 0, sal::static_int_cast<SCROW>(nPos), nTab,
                                         MAXCOL, sal::static_int_cast<SCROW>(nPos), nTab );
                else
                    aNewRange = ScRange( sal::static_int_cast<SCCOL>(nPos), 0, nTab,
                                         sal::static_int_cast<SCCOL>(nPos), MAXROW, nTab );

                // see if any part of the range is already selected
                sal_Bool bSelected = sal_False;
                ScRangeList aRanges;
                pViewData->GetMarkData().FillRangeListWithMarks( &aRanges, sal_False );
                sal_uLong nRangeCount = aRanges.Count();
                for (sal_uLong i=0; i<nRangeCount && !bSelected; i++)
                    if ( aRanges.GetObject(i)->Intersects( aNewRange ) )
                        bSelected = sal_True;

                // select the range if no part of it was selected
                if ( !bSelected )
                    pViewSh->MarkRange( aNewRange );
            }

			ScResId aResId( bVertical ? RID_POPUP_ROWHEADER : RID_POPUP_COLHEADER );
			pViewSh->GetDispatcher()->ExecutePopup( aResId );
		}
	}
	else if ( nCmd == COMMAND_STARTDRAG )
	{
		pSelEngine->Command( rCEvt );
	}
}

void ScHeaderControl::StopMarking()
{
	if ( bDragging )
	{
		DrawInvert( nDragPos );
		bDragging = sal_False;
	}

	SetMarking( sal_False );
	bIgnoreMove = sal_True;

	//	#86260# don't call pSelEngine->Reset, so selection across the parts of
	//	a split/frozen view is possible

	ReleaseMouse();
}

void ScHeaderControl::ShowDragHelp()
{
	if (Help::IsQuickHelpEnabled())
	{
		long nScrPos	= GetScrPos( nDragNo );
		sal_Bool bLayoutRTL = IsLayoutRTL();
		long nVal = bLayoutRTL ? ( nScrPos - nDragPos + 1 )
							   : ( nDragPos + 2 - nScrPos );

		String aHelpStr = GetDragHelp( nVal );
		Point aPos = OutputToScreenPixel( Point(0,0) );
		Size aSize = GetSizePixel();

		Point aMousePos = OutputToScreenPixel(GetPointerPosPixel());

		Rectangle aRect;
		sal_uInt16 nAlign;
		if (!bVertical)
		{
			//	oberhalb
			aRect.Left() = aMousePos.X();
			aRect.Top()	 = aPos.Y() - 4;
			nAlign		 = QUICKHELP_BOTTOM|QUICKHELP_CENTER;
		}
		else
		{
			//	rechts oben
			aRect.Left() = aPos.X() + aSize.Width() + 8;
			aRect.Top()	 = aMousePos.Y() - 2;
			nAlign		 = QUICKHELP_LEFT|QUICKHELP_BOTTOM;
		}

		aRect.Right() 	= aRect.Left();
		aRect.Bottom()	= aRect.Top();

		Help::ShowQuickHelp(this, aRect, aHelpStr, nAlign);
	}
}

void ScHeaderControl::RequestHelp( const HelpEvent& rHEvt )
{
	//	Wenn eigene QuickHelp angezeigt wird, nicht durch RequestHelp
	//	wieder wegnehmen lassen

	sal_Bool bOwn = bDragging && Help::IsQuickHelpEnabled();
	if (!bOwn)
		Window::RequestHelp(rHEvt);
}

// -----------------------------------------------------------------------
//					Dummys fuer virtuelle Methoden
// -----------------------------------------------------------------------

SCCOLROW ScHeaderControl::GetHiddenCount( SCCOLROW nEntryNo )
{
	SCCOLROW nHidden = 0;
	while ( nEntryNo < nSize && GetEntrySize( nEntryNo ) == 0 )
	{
		++nEntryNo;
		++nHidden;
	}
	return nHidden;
}

sal_Bool ScHeaderControl::IsLayoutRTL()
{
	return sal_False;
}

sal_Bool ScHeaderControl::IsMirrored()
{
	return sal_False;
}

sal_Bool ScHeaderControl::IsDisabled()
{
	return sal_False;
}

sal_Bool ScHeaderControl::ResizeAllowed()
{
	return sal_True;
}

void ScHeaderControl::SelectWindow()
{
}

void ScHeaderControl::DrawInvert( long /* nDragPos */ )
{
}

String ScHeaderControl::GetDragHelp( long /* nVal */ )
{
	return EMPTY_STRING;
}

void ScHeaderControl::SetMarking( sal_Bool /* bSet */ )
{
}



