/**************************************************************
 * 
 * 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_vcl.hxx"

#include "tools/rc.h"

#include "vcl/event.hxx"
#include "vcl/decoview.hxx"
#include "vcl/spin.h"
#include "vcl/spinfld.hxx"

#include "controldata.hxx"
#include "svdata.hxx"

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

void ImplGetSpinbuttonValue( Window *pWin, const Rectangle& rUpperRect,
                            const Rectangle& rLowerRect,
                            sal_Bool bUpperIn, sal_Bool bLowerIn,
                            sal_Bool bUpperEnabled, sal_Bool bLowerEnabled, sal_Bool bHorz,
                            SpinbuttonValue& rValue )
{
    // convert spinbutton data to a SpinbuttonValue structure for native painting

    rValue.maUpperRect = rUpperRect;
    rValue.maLowerRect = rLowerRect;

    Point aPointerPos = pWin->GetPointerPosPixel();

    ControlState nState = CTRL_STATE_ENABLED;
    if ( bUpperIn )
        nState |= CTRL_STATE_PRESSED;
    if ( !pWin->IsEnabled() || !bUpperEnabled )
        nState &= ~CTRL_STATE_ENABLED;
    if ( pWin->HasFocus() )
        nState |= CTRL_STATE_FOCUSED;
    if( pWin->IsMouseOver() && rUpperRect.IsInside( aPointerPos ) )
        nState |= CTRL_STATE_ROLLOVER;
    rValue.mnUpperState = nState;

    nState = CTRL_STATE_ENABLED;
    if ( bLowerIn )
        nState |= CTRL_STATE_PRESSED;
    if ( !pWin->IsEnabled() || !bLowerEnabled )
        nState &= ~CTRL_STATE_ENABLED;
    if ( pWin->HasFocus() )
        nState |= CTRL_STATE_FOCUSED;
    // for overlapping spins: highlight only one
    if( pWin->IsMouseOver() && rLowerRect.IsInside( aPointerPos ) &&
                              !rUpperRect.IsInside( aPointerPos ) )
        nState |= CTRL_STATE_ROLLOVER;
    rValue.mnLowerState = nState;

    rValue.mnUpperPart = bHorz ? PART_BUTTON_LEFT : PART_BUTTON_UP;
    rValue.mnLowerPart = bHorz ? PART_BUTTON_RIGHT : PART_BUTTON_DOWN;
}


sal_Bool ImplDrawNativeSpinfield( Window *pWin, const SpinbuttonValue& rSpinbuttonValue )
{
    sal_Bool bNativeOK = sal_False;

    if( pWin->IsNativeControlSupported(CTRL_SPINBOX, PART_ENTIRE_CONTROL) &&
        // there is just no useful native support for spinfields with dropdown
        !(pWin->GetStyle() & WB_DROPDOWN) )
    {
        if( pWin->IsNativeControlSupported(CTRL_SPINBOX, rSpinbuttonValue.mnUpperPart) && 
            pWin->IsNativeControlSupported(CTRL_SPINBOX, rSpinbuttonValue.mnLowerPart) )
        {
            // only paint the embedded spin buttons, all buttons are painted at once
            bNativeOK = pWin->DrawNativeControl( CTRL_SPINBOX, PART_ALL_BUTTONS, Rectangle(), CTRL_STATE_ENABLED,
                        rSpinbuttonValue, rtl::OUString() );
        }
        else
        {
            // paint the spinbox as a whole, use borderwindow to have proper clipping
            Window *pBorder = pWin->GetWindow( WINDOW_BORDER );

            // to not overwrite everything, set the button region as clipregion to the border window
            Rectangle aClipRect( rSpinbuttonValue.maLowerRect );
            aClipRect.Union( rSpinbuttonValue.maUpperRect );

            // convert from screen space to borderwin space
            aClipRect.SetPos( pBorder->ScreenToOutputPixel(pWin->OutputToScreenPixel(aClipRect.TopLeft())) );

            Region oldRgn( pBorder->GetClipRegion() );
            pBorder->SetClipRegion( Region( aClipRect ) );

            Point aPt;
            Size aSize( pBorder->GetOutputSizePixel() );    // the size of the border window, i.e., the whole control
            Rectangle aBound, aContent;
            Rectangle aNatRgn( aPt, aSize );
            if( ! ImplGetSVData()->maNWFData.mbCanDrawWidgetAnySize &&
                pBorder->GetNativeControlRegion( CTRL_SPINBOX, PART_ENTIRE_CONTROL,
                                                 aNatRgn, 0, rSpinbuttonValue, rtl::OUString(), aBound, aContent) )
            {
                aSize = aContent.GetSize();
            }
            
            Rectangle aRgn( aPt, aSize );
            bNativeOK = pBorder->DrawNativeControl( CTRL_SPINBOX, PART_ENTIRE_CONTROL, aRgn, CTRL_STATE_ENABLED,
                        rSpinbuttonValue, rtl::OUString() );

            pBorder->SetClipRegion( oldRgn );
        }
    }
    return bNativeOK;
}

sal_Bool ImplDrawNativeSpinbuttons( Window *pWin, const SpinbuttonValue& rSpinbuttonValue )
{
    sal_Bool bNativeOK = sal_False;

    if( pWin->IsNativeControlSupported(CTRL_SPINBUTTONS, PART_ENTIRE_CONTROL) )
    {
        // only paint the standalone spin buttons, all buttons are painted at once
        bNativeOK = pWin->DrawNativeControl( CTRL_SPINBUTTONS, PART_ALL_BUTTONS, Rectangle(), CTRL_STATE_ENABLED,
                    rSpinbuttonValue, rtl::OUString() );
    }
    return bNativeOK;
}

void ImplDrawSpinButton( OutputDevice* pOutDev,
						 const Rectangle& rUpperRect,
						 const Rectangle& rLowerRect,
						 sal_Bool bUpperIn, sal_Bool bLowerIn,
						 sal_Bool bUpperEnabled, sal_Bool bLowerEnabled, sal_Bool bHorz, sal_Bool bMirrorHorz )
{
	DecorationView aDecoView( pOutDev );

	sal_uInt16 nStyle = BUTTON_DRAW_NOLEFTLIGHTBORDER;
	sal_uInt16 nSymStyle = 0;

	SymbolType eType1, eType2;

	const StyleSettings& rStyleSettings = pOutDev->GetSettings().GetStyleSettings();
	if ( rStyleSettings.GetOptions() & STYLE_OPTION_SPINARROW )
	{
        // arrows are only use in OS/2 look
		if ( bHorz )
		{
            eType1 = bMirrorHorz ? SYMBOL_ARROW_RIGHT : SYMBOL_ARROW_LEFT;
            eType2 = bMirrorHorz ? SYMBOL_ARROW_LEFT : SYMBOL_ARROW_RIGHT;
		}
		else
		{
			eType1 = SYMBOL_ARROW_UP;
			eType2 = SYMBOL_ARROW_DOWN;
		}
	}
	else
	{
		if ( bHorz )
		{
            eType1 = bMirrorHorz ? SYMBOL_SPIN_RIGHT : SYMBOL_SPIN_LEFT;
            eType2 = bMirrorHorz ? SYMBOL_SPIN_LEFT : SYMBOL_SPIN_RIGHT;
		}
		else
		{
			eType1 = SYMBOL_SPIN_UP;
			eType2 = SYMBOL_SPIN_DOWN;
		}
	}

	// Oberen/linken Button malen
	sal_uInt16 nTempStyle = nStyle;
	if ( bUpperIn )
		nTempStyle |= BUTTON_DRAW_PRESSED;

    sal_Bool bNativeOK = sal_False;
	Rectangle aUpRect;

    if( pOutDev->GetOutDevType() == OUTDEV_WINDOW )
    {
        Window *pWin = (Window*) pOutDev;

        // are we drawing standalone spin buttons or members of a spinfield ?
        ControlType aControl = CTRL_SPINBUTTONS;
        switch( pWin->GetType() )
        {
            case WINDOW_EDIT:
            case WINDOW_MULTILINEEDIT:
            case WINDOW_PATTERNFIELD:
            case WINDOW_METRICFIELD:
            case WINDOW_CURRENCYFIELD:
            case WINDOW_DATEFIELD:
            case WINDOW_TIMEFIELD:
            case WINDOW_LONGCURRENCYFIELD:
            case WINDOW_NUMERICFIELD:
            case WINDOW_SPINFIELD:
                aControl = CTRL_SPINBOX;
                break;
            default:
                aControl = CTRL_SPINBUTTONS;
                break;
        }

        SpinbuttonValue aValue;
        ImplGetSpinbuttonValue( pWin, rUpperRect, rLowerRect,
						        bUpperIn, bLowerIn, bUpperEnabled, bLowerEnabled,
                                bHorz, aValue );

        if( aControl == CTRL_SPINBOX )
            bNativeOK = ImplDrawNativeSpinfield( pWin, aValue );
        else if( aControl == CTRL_SPINBUTTONS )
            bNativeOK = ImplDrawNativeSpinbuttons( pWin, aValue );
    }

    if( !bNativeOK )
        aUpRect = aDecoView.DrawButton( rUpperRect, nTempStyle );

	// Unteren/rechten Button malen
	if ( bLowerIn )
		nStyle |= BUTTON_DRAW_PRESSED;
	Rectangle aLowRect;
    if( !bNativeOK )
	    aLowRect = aDecoView.DrawButton( rLowerRect, nStyle );

	// Zusaetzliche Default-Kante wollen wir auch ausnutzen
	aUpRect.Left()--;
	aUpRect.Top()--;
	aUpRect.Right()++;
	aUpRect.Bottom()++;
	aLowRect.Left()--;
	aLowRect.Top()--;
	aLowRect.Right()++;
	aLowRect.Bottom()++;

	// Wir malen auch in die Kante rein, damit man etwas erkennen kann,
	// wenn das Rechteck zu klein ist
	if ( aUpRect.GetHeight() < 4 )
	{
		aUpRect.Right()++;
		aUpRect.Bottom()++;
		aLowRect.Right()++;
		aLowRect.Bottom()++;
	}

	// Symbolgroesse berechnen
	long nTempSize1 = aUpRect.GetWidth();
	long nTempSize2 = aLowRect.GetWidth();
	if ( Abs( nTempSize1-nTempSize2 ) == 1 )
	{
		if ( nTempSize1 > nTempSize2 )
			aUpRect.Left()++;
		else
			aLowRect.Left()++;
	}
	nTempSize1 = aUpRect.GetHeight();
	nTempSize2 = aLowRect.GetHeight();
	if ( Abs( nTempSize1-nTempSize2 ) == 1 )
	{
		if ( nTempSize1 > nTempSize2 )
			aUpRect.Top()++;
		else
			aLowRect.Top()++;
	}

	nTempStyle = nSymStyle;
	if ( !bUpperEnabled )
		nTempStyle |= SYMBOL_DRAW_DISABLE;
    if( !bNativeOK )
	    aDecoView.DrawSymbol( aUpRect, eType1, rStyleSettings.GetButtonTextColor(), nTempStyle );

	if ( !bLowerEnabled )
		nSymStyle |= SYMBOL_DRAW_DISABLE;
    if( !bNativeOK )
        aDecoView.DrawSymbol( aLowRect, eType2, rStyleSettings.GetButtonTextColor(), nSymStyle );
}

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

void SpinField::ImplInitSpinFieldData()
{
	mpEdit			= NULL;
	mbSpin			= sal_False;
	mbRepeat		= sal_False;
	mbUpperIn		= sal_False;
	mbLowerIn		= sal_False;
	mbInitialUp 	= sal_False;
	mbInitialDown	= sal_False;
	mbNoSelect		= sal_False;
	mbInDropDown	= sal_False;
}

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

void SpinField::ImplInit( Window* pParent, WinBits nWinStyle )
{
	Edit::ImplInit( pParent, nWinStyle );

	if ( nWinStyle & (WB_SPIN|WB_DROPDOWN) )
	{
		mbSpin = sal_True;
		
		// Some themes want external spin buttons, therefore the main
		// spinfield should not overdraw the border between its encapsulated
		// edit field and the spin buttons
		if ( (nWinStyle & WB_SPIN) && ImplUseNativeBorder( nWinStyle ) ) 
		{
			SetBackground();
			mpEdit = new Edit( this, WB_NOBORDER );
			mpEdit->SetBackground();
		}
		else
			mpEdit = new Edit( this, WB_NOBORDER );

		mpEdit->EnableRTL( sal_False );
		mpEdit->SetPosPixel( Point() );
		mpEdit->Show();
		SetSubEdit( mpEdit );

		maRepeatTimer.SetTimeoutHdl( LINK( this, SpinField, ImplTimeout ) );
		maRepeatTimer.SetTimeout( GetSettings().GetMouseSettings().GetButtonStartRepeat() );
		if ( nWinStyle & WB_REPEAT )
			mbRepeat = sal_True;

		SetCompoundControl( sal_True );
	}
}

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

SpinField::SpinField( WindowType nTyp ) :
	Edit( nTyp )
{
	ImplInitSpinFieldData();
}

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

SpinField::SpinField( Window* pParent, WinBits nWinStyle ) :
	Edit( WINDOW_SPINFIELD )
{
	ImplInitSpinFieldData();
	ImplInit( pParent, nWinStyle );
}

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

SpinField::SpinField( Window* pParent, const ResId& rResId ) :
	Edit( WINDOW_SPINFIELD )
{
	ImplInitSpinFieldData();
	rResId.SetRT( RSC_SPINFIELD );
	WinBits nStyle = ImplInitRes( rResId );
	ImplInit( pParent, nStyle );
	ImplLoadRes( rResId );

	if ( !(nStyle & WB_HIDE) )
		Show();
}

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

SpinField::~SpinField()
{
	delete mpEdit;
}

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

void SpinField::Up()
{
    ImplCallEventListenersAndHandler( VCLEVENT_SPINFIELD_UP, maUpHdlLink, this );
}

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

void SpinField::Down()
{
    ImplCallEventListenersAndHandler( VCLEVENT_SPINFIELD_DOWN, maDownHdlLink, this );
}

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

void SpinField::First()
{
    ImplCallEventListenersAndHandler( VCLEVENT_SPINFIELD_FIRST, maFirstHdlLink, this );
}

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

void SpinField::Last()
{
    ImplCallEventListenersAndHandler( VCLEVENT_SPINFIELD_LAST, maLastHdlLink, this );
}

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

void SpinField::MouseButtonDown( const MouseEvent& rMEvt )
{
	if ( !HasFocus() && ( !mpEdit || !mpEdit->HasFocus() ) )
	{
		mbNoSelect = sal_True;
		GrabFocus();
	}

	if ( !IsReadOnly() )
	{
		if ( maUpperRect.IsInside( rMEvt.GetPosPixel() ) )
		{
			mbUpperIn	= sal_True;
			mbInitialUp = sal_True;
			Invalidate( maUpperRect );
		}
		else if ( maLowerRect.IsInside( rMEvt.GetPosPixel() ) )
		{
			mbLowerIn	 = sal_True;
			mbInitialDown = sal_True;
			Invalidate( maLowerRect );
		}
		else if ( maDropDownRect.IsInside( rMEvt.GetPosPixel() ) )
		{
			// Rechts daneben liegt der DropDownButton:
			mbInDropDown = ShowDropDown( mbInDropDown ? sal_False : sal_True );
			Paint( Rectangle( Point(), GetOutputSizePixel() ) );
		}

		if ( mbUpperIn || mbLowerIn )
		{
			Update();
			CaptureMouse();
			if ( mbRepeat )
				maRepeatTimer.Start();
			return;
		}
	}

	Edit::MouseButtonDown( rMEvt );
}

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

void SpinField::MouseButtonUp( const MouseEvent& rMEvt )
{
	ReleaseMouse();
	mbInitialUp = mbInitialDown = sal_False;
	maRepeatTimer.Stop();
	maRepeatTimer.SetTimeout( GetSettings().GetMouseSettings().GetButtonStartRepeat() );

	if ( mbUpperIn )
	{
		mbUpperIn = sal_False;
		Invalidate( maUpperRect );
		Update();
		Up();
	}
	else if ( mbLowerIn )
	{
		mbLowerIn = sal_False;
		Invalidate( maLowerRect );
		Update();
		Down();
	}

	Edit::MouseButtonUp( rMEvt );
}

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

void SpinField::MouseMove( const MouseEvent& rMEvt )
{
	if ( rMEvt.IsLeft() )
	{
		if ( mbInitialUp )
		{
			sal_Bool bNewUpperIn = maUpperRect.IsInside( rMEvt.GetPosPixel() );
			if ( bNewUpperIn != mbUpperIn )
			{
				if ( bNewUpperIn )
				{
					if ( mbRepeat )
						maRepeatTimer.Start();
				}
				else
					maRepeatTimer.Stop();

				mbUpperIn = bNewUpperIn;
				Invalidate( maUpperRect );
				Update();
			}
		}
		else if ( mbInitialDown )
		{
			sal_Bool bNewLowerIn = maLowerRect.IsInside( rMEvt.GetPosPixel() );
			if ( bNewLowerIn != mbLowerIn )
			{
				if ( bNewLowerIn )
				{
					if ( mbRepeat )
						maRepeatTimer.Start();
				}
				else
					maRepeatTimer.Stop();

				mbLowerIn = bNewLowerIn;
				Invalidate( maLowerRect );
				Update();
			}
		}
	}

	Edit::MouseMove( rMEvt );
}

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

long SpinField::Notify( NotifyEvent& rNEvt )
{
	long nDone = 0;
	if( rNEvt.GetType() == EVENT_KEYINPUT )
	{
		const KeyEvent& rKEvt = *rNEvt.GetKeyEvent();
		if ( !IsReadOnly() )
		{
			sal_uInt16 nMod = rKEvt.GetKeyCode().GetModifier();
			switch ( rKEvt.GetKeyCode().GetCode() )
			{
				case KEY_UP:
				{
					if ( !nMod )
					{
						Up();
						nDone = 1;
					}
				}
				break;
				case KEY_DOWN:
				{
					if ( !nMod )
					{
						Down();
						nDone = 1;
					}
					else if ( ( nMod == KEY_MOD2 ) && !mbInDropDown && ( GetStyle() & WB_DROPDOWN ) )
					{
						mbInDropDown = ShowDropDown( sal_True );
						Paint( Rectangle( Point(), GetOutputSizePixel() ) );
						nDone = 1;
					}
				}
				break;
				case KEY_PAGEUP:
				{
					if ( !nMod )
					{
						Last();
						nDone = 1;
					}
				}
				break;
				case KEY_PAGEDOWN:
				{
					if ( !nMod )
					{
						First();
						nDone = 1;
					}
				}
				break;
			}
		}
	}

	if ( rNEvt.GetType() == EVENT_COMMAND )
	{
		if ( ( rNEvt.GetCommandEvent()->GetCommand() == COMMAND_WHEEL ) && !IsReadOnly() )
        {
            sal_uInt16 nWheelBehavior( GetSettings().GetMouseSettings().GetWheelBehavior() );
            if  (   ( nWheelBehavior == MOUSE_WHEEL_ALWAYS )
                ||  (   ( nWheelBehavior == MOUSE_WHEEL_FOCUS_ONLY )
                    &&  HasChildPathFocus()
                    )
                )
            {
                const CommandWheelData* pData = rNEvt.GetCommandEvent()->GetWheelData();
                if ( pData->GetMode() == COMMAND_WHEEL_SCROLL )
                {
                    if ( pData->GetDelta() < 0L )
                        Down();
                    else
                        Up();
                    nDone = 1;
                }
            }
            else
                nDone = 0;  // don't eat this event, let the default handling happen (i.e. scroll the context)
        }
	}

	return nDone ? nDone : Edit::Notify( rNEvt );
}

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

void SpinField::Command( const CommandEvent& rCEvt )
{
	Edit::Command( rCEvt );
}

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

void SpinField::FillLayoutData() const
{
    if( mbSpin )
    {
        mpControlData->mpLayoutData = new vcl::ControlLayoutData();
        AppendLayoutData( *GetSubEdit() );
        GetSubEdit()->SetLayoutDataParent( this );
    }
    else
        Edit::FillLayoutData();
}

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

void SpinField::Paint( const Rectangle& rRect )
{
	if ( mbSpin )
	{
		sal_Bool	bEnable = IsEnabled();
        ImplDrawSpinButton( this, maUpperRect, maLowerRect,
		                    mbUpperIn, mbLowerIn, bEnable, bEnable );
	}

	if ( GetStyle() & WB_DROPDOWN )
	{
		DecorationView aView( this );

		sal_uInt16 nStyle = BUTTON_DRAW_NOLIGHTBORDER;
		if ( mbInDropDown )
			nStyle |= BUTTON_DRAW_PRESSED;
		Rectangle aInnerRect = aView.DrawButton( maDropDownRect, nStyle );

		SymbolType eSymbol = SYMBOL_SPIN_DOWN;
		if ( GetSettings().GetStyleSettings().GetOptions() & STYLE_OPTION_SPINUPDOWN )
			eSymbol = SYMBOL_SPIN_UPDOWN;

		nStyle = IsEnabled() ? 0 : SYMBOL_DRAW_DISABLE;
		aView.DrawSymbol( aInnerRect, eSymbol, GetSettings().GetStyleSettings().GetButtonTextColor(), nStyle );
	}

	Edit::Paint( rRect );
}

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

void SpinField::ImplCalcButtonAreas( OutputDevice* pDev, const Size& rOutSz, Rectangle& rDDArea, Rectangle& rSpinUpArea, Rectangle& rSpinDownArea )
{
	const StyleSettings& rStyleSettings = pDev->GetSettings().GetStyleSettings();

	Size aSize = rOutSz;
	Size aDropDownSize;

	if ( GetStyle() & WB_DROPDOWN )
	{
		long nW = rStyleSettings.GetScrollBarSize();
		nW = GetDrawPixel( pDev, nW );
		aDropDownSize = Size( CalcZoom( nW ), aSize.Height() );
		aSize.Width() -= aDropDownSize.Width();
		rDDArea = Rectangle( Point( aSize.Width(), 0 ), aDropDownSize );
		rDDArea.Top()--;
	}
	else
		rDDArea.SetEmpty();

	// Je nach Hoehe, die groessen Berechnen
	if ( GetStyle() & WB_SPIN )
	{
		long nBottom1 = aSize.Height()/2;
		long nBottom2 = aSize.Height()-1;
		long nTop2 = nBottom1;
		long nTop1 = 0;
		if ( !(aSize.Height() & 0x01) )
			nBottom1--;

        sal_Bool bNativeRegionOK = sal_False;
        Rectangle aContentUp, aContentDown;

		if ( (pDev->GetOutDevType() == OUTDEV_WINDOW) &&
            // there is just no useful native support for spinfields with dropdown
            ! (GetStyle() & WB_DROPDOWN) &&
            IsNativeControlSupported(CTRL_SPINBOX, PART_ENTIRE_CONTROL) )
        {
            Window *pWin = (Window*) pDev;
            Window *pBorder = pWin->GetWindow( WINDOW_BORDER );

            // get the system's spin button size
		    ImplControlValue aControlValue;
		    Rectangle aBound;
		    Point aPoint;

            // use the full extent of the control
		    Rectangle aArea( aPoint, pBorder->GetOutputSizePixel() );

		    bNativeRegionOK = 
                pWin->GetNativeControlRegion(CTRL_SPINBOX, PART_BUTTON_UP,
                    aArea, 0, aControlValue, rtl::OUString(), aBound, aContentUp) &&
                pWin->GetNativeControlRegion(CTRL_SPINBOX, PART_BUTTON_DOWN,
                    aArea, 0, aControlValue, rtl::OUString(), aBound, aContentDown);
                    
            if( bNativeRegionOK )
            {
                // convert back from border space to local coordinates
                aPoint = pBorder->ScreenToOutputPixel( pWin->OutputToScreenPixel( aPoint ) );
                aContentUp.Move(-aPoint.X(), -aPoint.Y());
                aContentDown.Move(-aPoint.X(), -aPoint.Y());
            }
        }

        if( bNativeRegionOK )
        {
		    rSpinUpArea = aContentUp;
		    rSpinDownArea = aContentDown;
        }
        else
        {
		    aSize.Width() -= CalcZoom( GetDrawPixel( pDev, rStyleSettings.GetSpinSize() ) );

		    rSpinUpArea = Rectangle( aSize.Width(), nTop1, rOutSz.Width()-aDropDownSize.Width()-1, nBottom1 );
		    rSpinDownArea = Rectangle( rSpinUpArea.Left(), nTop2, rSpinUpArea.Right(), nBottom2 );
        }
	}
	else
	{
		rSpinUpArea.SetEmpty();
		rSpinDownArea.SetEmpty();
	}
}

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

void SpinField::Resize()
{
	if ( mbSpin )
	{
		Control::Resize();
		Size aSize = GetOutputSizePixel();
        bool bSubEditPositioned = false;

		if ( GetStyle() & (WB_SPIN|WB_DROPDOWN) )
		{
			ImplCalcButtonAreas( this, aSize, maDropDownRect, maUpperRect, maLowerRect );

            ImplControlValue aControlValue;
            Point aPoint;
            Rectangle aContent, aBound;
    
            // use the full extent of the control
            Window *pBorder = GetWindow( WINDOW_BORDER );
            Rectangle aArea( aPoint, pBorder->GetOutputSizePixel() );

            // adjust position and size of the edit field
            if ( GetNativeControlRegion(CTRL_SPINBOX, PART_SUB_EDIT,
                        aArea, 0, aControlValue, rtl::OUString(), aBound, aContent) )
            {
                // convert back from border space to local coordinates
                aPoint = pBorder->ScreenToOutputPixel( OutputToScreenPixel( aPoint ) );
                aContent.Move(-aPoint.X(), -aPoint.Y());

                // use the themes drop down size
                mpEdit->SetPosPixel( aContent.TopLeft() );
                bSubEditPositioned = true;
                aSize = aContent.GetSize();
            }
            else
            {
                if ( maUpperRect.IsEmpty() )
                {
                    DBG_ASSERT( !maDropDownRect.IsEmpty(), "SpinField::Resize: SPIN && DROPDOWN, but all empty rects?" );
                    aSize.Width() = maDropDownRect.Left();
                }
                else
                    aSize.Width() = maUpperRect.Left();
            }
		}

        if( ! bSubEditPositioned )
        {
            // this moves our sub edit if RTL gets switched
            mpEdit->SetPosPixel( Point() );
        }
		mpEdit->SetSizePixel( aSize );

		if ( GetStyle() & WB_SPIN )
			Invalidate( Rectangle( maUpperRect.TopLeft(), maLowerRect.BottomRight() ) );
		if ( GetStyle() & WB_DROPDOWN )
			Invalidate( maDropDownRect );
	}
}

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

void SpinField::StateChanged( StateChangedType nType )
{
	Edit::StateChanged( nType );

	if ( nType == STATE_CHANGE_ENABLE )
	{
		if ( mbSpin || ( GetStyle() & WB_DROPDOWN ) )
		{
			mpEdit->Enable( IsEnabled() );

			if ( mbSpin )
			{
				Invalidate( maLowerRect );
				Invalidate( maUpperRect );
			}
			if ( GetStyle() & WB_DROPDOWN )
				Invalidate( maDropDownRect );
		}
	}
	else if ( nType == STATE_CHANGE_STYLE )
	{
		if ( GetStyle() & WB_REPEAT )
			mbRepeat = sal_True;
		else
			mbRepeat = sal_False;
	}
	else if ( nType == STATE_CHANGE_ZOOM )
	{
		Resize();
		if ( mpEdit )
			mpEdit->SetZoom( GetZoom() );
		Invalidate();
	}
	else if ( nType == STATE_CHANGE_CONTROLFONT )
	{
		if ( mpEdit )
			mpEdit->SetControlFont( GetControlFont() );
		ImplInitSettings( sal_True, sal_False, sal_False );
		Invalidate();
	}
	else if ( nType == STATE_CHANGE_CONTROLFOREGROUND )
	{
		if ( mpEdit )
			mpEdit->SetControlForeground( GetControlForeground() );
		ImplInitSettings( sal_False, sal_True, sal_False );
		Invalidate();
	}
	else if ( nType == STATE_CHANGE_CONTROLBACKGROUND )
	{
		if ( mpEdit )
			mpEdit->SetControlBackground( GetControlBackground() );
		ImplInitSettings( sal_False, sal_False, sal_True );
		Invalidate();
	}
    else if( nType == STATE_CHANGE_MIRRORING )
    {
        if( mpEdit )
            mpEdit->StateChanged( STATE_CHANGE_MIRRORING );
        Resize();
    }
}

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

void SpinField::DataChanged( const DataChangedEvent& rDCEvt )
{
	Edit::DataChanged( rDCEvt );

	if ( (rDCEvt.GetType() == DATACHANGED_SETTINGS) &&
		 (rDCEvt.GetFlags() & SETTINGS_STYLE) )
	{
		Resize();
		Invalidate();
	}
}

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

Rectangle* SpinField::ImplFindPartRect( const Point& rPt )
{
    if( maUpperRect.IsInside( rPt ) )
        return &maUpperRect;
    else if( maLowerRect.IsInside( rPt ) )
        return &maLowerRect;
    else
        return NULL;
}

long SpinField::PreNotify( NotifyEvent& rNEvt )
{
    long nDone = 0;
    const MouseEvent* pMouseEvt = NULL;

    if( (rNEvt.GetType() == EVENT_MOUSEMOVE) && (pMouseEvt = rNEvt.GetMouseEvent()) != NULL )
    {
        if( !pMouseEvt->GetButtons() && !pMouseEvt->IsSynthetic() && !pMouseEvt->IsModifierChanged() )
        {
            // trigger redraw if mouse over state has changed
            if( IsNativeControlSupported(CTRL_SPINBOX, PART_ENTIRE_CONTROL) ||
                IsNativeControlSupported(CTRL_SPINBOX, PART_ALL_BUTTONS) )
            {
                Rectangle* pRect = ImplFindPartRect( GetPointerPosPixel() );
                Rectangle* pLastRect = ImplFindPartRect( GetLastPointerPosPixel() );
                if( pRect != pLastRect || (pMouseEvt->IsLeaveWindow() || pMouseEvt->IsEnterWindow()) )
                {
                    // FIXME: this is currently only on aqua
                    // check for other platforms that need similar handling
                    if( ImplGetSVData()->maNWFData.mbNoFocusRects &&
                        IsNativeWidgetEnabled() &&
                        IsNativeControlSupported( CTRL_EDITBOX, PART_ENTIRE_CONTROL ) )
                    {
                        ImplInvalidateOutermostBorder( this );
                    }
                    else
                    {
                        // paint directly
                        Region aRgn( GetActiveClipRegion() );
                        if( pLastRect )
                        {
                            SetClipRegion( *pLastRect );
                            Paint( *pLastRect );
                            SetClipRegion( aRgn );
                        }
                        if( pRect )
                        {
                            SetClipRegion( *pRect );
                            Paint( *pRect );
                            SetClipRegion( aRgn );
                        }
                    }
                }
            }
        }
    }

    return nDone ? nDone : Edit::PreNotify(rNEvt);
}

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

void SpinField::EndDropDown()
{
	mbInDropDown = sal_False;
	Paint( Rectangle( Point(), GetOutputSizePixel() ) );
}

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

sal_Bool SpinField::ShowDropDown( sal_Bool )
{
	return sal_False;
}

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

Size SpinField::CalcMinimumSize() const
{
	Size aSz = Edit::CalcMinimumSize();

	if ( GetStyle() & WB_DROPDOWN )
		aSz.Width() += GetSettings().GetStyleSettings().GetScrollBarSize();
	if ( GetStyle() & WB_SPIN )
		aSz.Width() += maUpperRect.GetWidth();

	return aSz;
}

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

Size SpinField::GetOptimalSize(WindowSizeType eType) const
{
    switch (eType) {
    case WINDOWSIZE_MINIMUM:
        return CalcMinimumSize();
    default:
        return Edit::GetOptimalSize( eType );
    }
}

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

Size SpinField::CalcSize( sal_uInt16 nChars ) const
{
	Size aSz = Edit::CalcSize( nChars );

	if ( GetStyle() & WB_DROPDOWN )
		aSz.Width() += GetSettings().GetStyleSettings().GetScrollBarSize();
	if ( GetStyle() & WB_SPIN )
		aSz.Width() += GetSettings().GetStyleSettings().GetSpinSize();

	return aSz;
}

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

IMPL_LINK( SpinField, ImplTimeout, Timer*, pTimer )
{
	if ( pTimer->GetTimeout() == GetSettings().GetMouseSettings().GetButtonStartRepeat() )
	{
		pTimer->SetTimeout( GetSettings().GetMouseSettings().GetButtonRepeat() );
		pTimer->Start();
	}
	else
	{
		if ( mbInitialUp )
			Up();
		else
			Down();
	}
	return 0;
}

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

void SpinField::Draw( OutputDevice* pDev, const Point& rPos, const Size& rSize, sal_uLong nFlags )
{
	Edit::Draw( pDev, rPos, rSize, nFlags );

	WinBits nFieldStyle = GetStyle();
	if ( !(nFlags & WINDOW_DRAW_NOCONTROLS ) && ( nFieldStyle & (WB_SPIN|WB_DROPDOWN) ) )
	{
		Point aPos = pDev->LogicToPixel( rPos );
		Size aSize = pDev->LogicToPixel( rSize );
		OutDevType eOutDevType = pDev->GetOutDevType();
		AllSettings aOldSettings = pDev->GetSettings();

		pDev->Push();
		pDev->SetMapMode();

		if ( eOutDevType == OUTDEV_PRINTER )
		{
			StyleSettings aStyleSettings = aOldSettings.GetStyleSettings();
			aStyleSettings.SetFaceColor( COL_LIGHTGRAY );
			aStyleSettings.SetButtonTextColor( COL_BLACK );
			AllSettings aSettings( aOldSettings );
			aSettings.SetStyleSettings( aStyleSettings );
			pDev->SetSettings( aSettings );
		}

		Rectangle aDD, aUp, aDown;
		ImplCalcButtonAreas( pDev, aSize, aDD, aUp, aDown );
		aDD.Move( aPos.X(), aPos.Y() );
		aUp.Move( aPos.X(), aPos.Y() );
		aUp.Top()++;
		aDown.Move( aPos.X(), aPos.Y() );

		Color aButtonTextColor;
		if ( ( nFlags & WINDOW_DRAW_MONO ) || ( eOutDevType == OUTDEV_PRINTER ) )
			aButtonTextColor = Color( COL_BLACK );
		else
			aButtonTextColor = GetSettings().GetStyleSettings().GetButtonTextColor();

		if ( GetStyle() & WB_DROPDOWN )
		{
			DecorationView aView( pDev );
			sal_uInt16 nStyle = BUTTON_DRAW_NOLIGHTBORDER;
			Rectangle aInnerRect = aView.DrawButton( aDD, nStyle );
			SymbolType eSymbol = SYMBOL_SPIN_DOWN;
			if ( GetSettings().GetStyleSettings().GetOptions() & STYLE_OPTION_SPINUPDOWN )
				eSymbol = SYMBOL_SPIN_UPDOWN;

			nStyle = ( IsEnabled() || ( nFlags & WINDOW_DRAW_NODISABLE ) ) ? 0 : SYMBOL_DRAW_DISABLE;
			aView.DrawSymbol( aInnerRect, eSymbol, aButtonTextColor, nStyle );
		}

		if ( GetStyle() & WB_SPIN )
		{
			ImplDrawSpinButton( pDev, aUp, aDown, sal_False, sal_False, sal_True, sal_True );
		}

		pDev->Pop();
		pDev->SetSettings( aOldSettings );
	}
}