/**************************************************************
 *
 * 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/debug.hxx"
#include "tools/rc.h"

#include "vcl/svapp.hxx"
#include "vcl/help.hxx"
#include "vcl/event.hxx"
#include "vcl/menu.hxx"
#include "vcl/button.hxx"
#include "vcl/tabpage.hxx"
#include "vcl/tabctrl.hxx"
#include "vcl/controllayout.hxx"
#include "vcl/sound.hxx"
#include "vcl/lstbox.hxx"

#include "controldata.hxx"
#include "svdata.hxx"
#include "window.h"

#include <hash_map>
#include <vector>

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

struct ImplTabItem
{
	sal_uInt16				mnId;
	sal_uInt16				mnTabPageResId;
	TabPage*			mpTabPage;
	String				maText;
	String				maFormatText;
	String				maHelpText;
	rtl::OString		maHelpId;
	Rectangle			maRect;
	sal_uInt16				mnLine;
	bool				mbFullVisible;
	bool				mbEnabled;
	Image				maTabImage;

	ImplTabItem()
	: mnId( 0 ), mnTabPageResId( 0 ), mpTabPage( NULL ),
	  mnLine( 0 ), mbFullVisible( sal_False ), mbEnabled( true )
	  {}
};

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

struct ImplTabCtrlData
{
	std::hash_map< int, int >		maLayoutPageIdToLine;
	std::hash_map< int, int >		maLayoutLineToPageId;
	std::vector< Rectangle >		maTabRectangles;
	Point							maItemsOffset; // offset of the tabitems
	std::vector< ImplTabItem >		maItemList;
	ListBox*						mpListBox;
	Size							maMinSize;
};

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

#define TAB_OFFSET			3
#define TAB_TABOFFSET_X		3
#define TAB_TABOFFSET_Y		3
#define TAB_EXTRASPACE_X	6
#define TAB_BORDER_LEFT		1
#define TAB_BORDER_TOP		1
#define TAB_BORDER_RIGHT	2
#define TAB_BORDER_BOTTOM	2

// Für die Ermittlung von den Tab-Positionen
#define TAB_PAGERECT		0xFFFF

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

void TabControl::ImplInit( Window* pParent, WinBits nStyle )
{
	if ( !(nStyle & WB_NOTABSTOP) )
		nStyle |= WB_TABSTOP;
	if ( !(nStyle & WB_NOGROUP) )
		nStyle |= WB_GROUP;
	if ( !(nStyle & WB_NODIALOGCONTROL) )
		nStyle |= WB_DIALOGCONTROL;

	Control::ImplInit( pParent, nStyle, NULL );

	mnLastWidth					= 0;
	mnLastHeight				= 0;
	mnBtnSize					= 0;
	mnMaxPageWidth				= 0;
	mnActPageId					= 0;
	mnCurPageId					= 0;
	mbFormat					= sal_True;
	mbRestoreHelpId				= sal_False;
	mbRestoreUnqId				= sal_False;
	mbSmallInvalidate			= sal_False;
	mbExtraSpace				= sal_False;
	mpTabCtrlData				= new ImplTabCtrlData;
	mpTabCtrlData->mpListBox	= NULL;

	ImplInitSettings( sal_True, sal_True, sal_True );

	if( (nStyle & WB_DROPDOWN) )
	{
		mpTabCtrlData->mpListBox = new ListBox( this, WB_DROPDOWN );
		mpTabCtrlData->mpListBox->SetPosSizePixel( Point( 0, 0 ), Size( 200, 20 ) );
		mpTabCtrlData->mpListBox->SetSelectHdl( LINK( this, TabControl, ImplListBoxSelectHdl ) );
		mpTabCtrlData->mpListBox->Show();
	}

	// if the tabcontrol is drawn (i.e. filled) by a native widget, make sure all controls will have transparent background
	// otherwise they will paint with a wrong background
	if( IsNativeControlSupported(CTRL_TAB_PANE, PART_ENTIRE_CONTROL) )
		EnableChildTransparentMode( sal_True );

	if ( pParent->IsDialog() )
		pParent->AddChildEventListener( LINK( this, TabControl, ImplWindowEventListener ) );
}

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

const Font& TabControl::GetCanonicalFont( const StyleSettings& _rStyle ) const
{
	return _rStyle.GetAppFont();
}

// -----------------------------------------------------------------
const Color& TabControl::GetCanonicalTextColor( const StyleSettings& _rStyle ) const
{
	return _rStyle.GetButtonTextColor();
}

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

void TabControl::ImplInitSettings( sal_Bool bFont,
								   sal_Bool bForeground, sal_Bool bBackground )
{
	Control::ImplInitSettings( bFont, bForeground );

	if ( bBackground )
	{
		Window* pParent = GetParent();
		if ( !IsControlBackground() &&
			 (pParent->IsChildTransparentModeEnabled()
			 || IsNativeControlSupported(CTRL_TAB_PANE, PART_ENTIRE_CONTROL)
			 || IsNativeControlSupported(CTRL_TAB_ITEM, PART_ENTIRE_CONTROL) ) )

		{
			// set transparent mode for NWF tabcontrols to have
			// the background always cleared properly
			EnableChildTransparentMode( sal_True );
			SetParentClipMode( PARENTCLIPMODE_NOCLIP );
			SetPaintTransparent( sal_True );
			SetBackground();
			ImplGetWindowImpl()->mbUseNativeFocus = ImplGetSVData()->maNWFData.mbNoFocusRects;
		}
		else
		{
			EnableChildTransparentMode( sal_False );
			SetParentClipMode( 0 );
			SetPaintTransparent( sal_False );

			if ( IsControlBackground() )
				SetBackground( GetControlBackground() );
			else
				SetBackground( pParent->GetBackground() );
		}
	}
}

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

void TabControl::ImplFreeLayoutData()
{
	if( HasLayoutData() )
	{
		ImplClearLayoutData();
		mpTabCtrlData->maLayoutPageIdToLine.clear();
		mpTabCtrlData->maLayoutLineToPageId.clear();
	}
}

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

TabControl::TabControl( Window* pParent, WinBits nStyle ) :
	Control( WINDOW_TABCONTROL )
{
	ImplInit( pParent, nStyle );
}

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

TabControl::TabControl( Window* pParent, const ResId& rResId ) :
	Control( WINDOW_TABCONTROL )
{
	rResId.SetRT( RSC_TABCONTROL );
	WinBits nStyle = ImplInitRes( rResId );
	ImplInit( pParent, nStyle );
	ImplLoadRes( rResId );

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

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

void TabControl::ImplLoadRes( const ResId& rResId )
{
	Control::ImplLoadRes( rResId );

	sal_uLong nObjMask = ReadLongRes();

	if ( nObjMask & RSC_TABCONTROL_ITEMLIST )
	{
		sal_uLong nEle = ReadLongRes();

		// add item
		for( sal_uLong i = 0; i < nEle; i++ )
		{
			InsertPage( ResId( (RSHEADER_TYPE *)GetClassRes(), *rResId.GetResMgr() ) );
			IncrementRes( GetObjSizeRes( (RSHEADER_TYPE *)GetClassRes() ) );
		}
	}
}

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

TabControl::~TabControl()
{
	if ( GetParent()->IsDialog() )
		GetParent()->RemoveChildEventListener( LINK( this, TabControl, ImplWindowEventListener ) );

	ImplFreeLayoutData();

	// TabCtrl-Daten löschen
	if ( mpTabCtrlData )
	{
		if( mpTabCtrlData->mpListBox )
			delete mpTabCtrlData->mpListBox;
		delete mpTabCtrlData;
	}
}

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

ImplTabItem* TabControl::ImplGetItem( sal_uInt16 nId ) const
{
	for( std::vector< ImplTabItem >::iterator it = mpTabCtrlData->maItemList.begin();
		it != mpTabCtrlData->maItemList.end(); ++it )
	{
		if( it->mnId == nId )
			return &(*it);
	}

	return NULL;
}

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

Size TabControl::ImplGetItemSize( ImplTabItem* pItem, long nMaxWidth )
{
	pItem->maFormatText = pItem->maText;
	Size aSize( GetCtrlTextWidth( pItem->maFormatText ), GetTextHeight() );
	Size aImageSize( 0, 0 );
	if( !!pItem->maTabImage )
	{
		aImageSize = pItem->maTabImage.GetSizePixel();
		if( pItem->maFormatText.Len() )
			aImageSize.Width() += GetTextHeight()/4;
	}
	aSize.Width() += aImageSize.Width();
	if( aImageSize.Height() > aSize.Height() )
		aSize.Height() = aImageSize.Height();

	aSize.Width()  += TAB_TABOFFSET_X*2;
	aSize.Height() += TAB_TABOFFSET_Y*2;

	Rectangle aCtrlRegion( Point( 0, 0 ), aSize );
	Rectangle aBoundingRgn, aContentRgn;
	const ImplControlValue aControlValue;
	if(GetNativeControlRegion( CTRL_TAB_ITEM, PART_ENTIRE_CONTROL, aCtrlRegion,
											CTRL_STATE_ENABLED, aControlValue, rtl::OUString(),
											aBoundingRgn, aContentRgn ) )
	{
		return aContentRgn.GetSize();
	}

	// For systems without synthetic bold support
	if ( mbExtraSpace )
		aSize.Width() += TAB_EXTRASPACE_X;
	// For languages with short names (e.g. Chinese), because the space is
	// normally only one pixel per char
	else if ( pItem->maFormatText.Len() < TAB_EXTRASPACE_X )
		aSize.Width() += TAB_EXTRASPACE_X-pItem->maFormatText.Len();

	// Evtl. den Text kürzen
	if ( aSize.Width()+4 >= nMaxWidth )
	{
		XubString aAppendStr( RTL_CONSTASCII_USTRINGPARAM( "..." ) );
		pItem->maFormatText += aAppendStr;
		do
		{
			pItem->maFormatText.Erase( pItem->maFormatText.Len()-aAppendStr.Len()-1, 1 );
			aSize.Width() = GetCtrlTextWidth( pItem->maFormatText );
			aSize.Width() += aImageSize.Width();
			aSize.Width() += TAB_TABOFFSET_X*2;
		}
		while ( (aSize.Width()+4 >= nMaxWidth) && (pItem->maFormatText.Len() > aAppendStr.Len()) );
		if ( aSize.Width()+4 >= nMaxWidth )
		{
			pItem->maFormatText.Assign( '.' );
			aSize.Width() = 1;
		}
	}

	if( pItem->maFormatText.Len() == 0 )
	{
		if( aSize.Height() < aImageSize.Height()+4 ) // leave space for focus rect
			aSize.Height() = aImageSize.Height()+4;
	}

	return aSize;
}

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

Rectangle TabControl::ImplGetTabRect( sal_uInt16 nItemPos, long nWidth, long nHeight )
{
	Size aWinSize = Control::GetOutputSizePixel();
	if ( nWidth < 0 )
		nWidth = aWinSize.Width();
	if ( nHeight < 0 )
		nHeight = aWinSize.Height();

	if ( mpTabCtrlData->maItemList.empty() )
	{
		long nW = nWidth-TAB_OFFSET*2;
		long nH = nHeight-TAB_OFFSET*2;
		return (nW > 0 && nH > 0)
		? Rectangle( Point( TAB_OFFSET, TAB_OFFSET ), Size( nW, nH ) )
		: Rectangle();
	}

	if ( nItemPos == TAB_PAGERECT )
	{
		sal_uInt16 nLastPos;
		if ( mnCurPageId )
			nLastPos = GetPagePos( mnCurPageId );
		else
			nLastPos = 0;

		Rectangle aRect = ImplGetTabRect( nLastPos, nWidth, nHeight );
		long nW = nWidth-TAB_OFFSET*2;
		long nH = nHeight-aRect.Bottom()-TAB_OFFSET*2;
		aRect = (nW > 0 && nH > 0)
		? Rectangle( Point( TAB_OFFSET, aRect.Bottom()+TAB_OFFSET ), Size( nW, nH ) )
		: Rectangle();
		return aRect;
	}

	nWidth -= 1;

	if ( (nWidth <= 0) || (nHeight <= 0) )
		return Rectangle();

	if ( mbFormat || (mnLastWidth != nWidth) || (mnLastHeight != nHeight) )
	{
		Font aFont( GetFont() );
		Font aLightFont = aFont;
		aFont.SetTransparent( sal_True );
		aFont.SetWeight( (!ImplGetSVData()->maNWFData.mbNoBoldTabFocus) ? WEIGHT_BOLD : WEIGHT_LIGHT );
		aLightFont.SetTransparent( sal_True );
		aLightFont.SetWeight( WEIGHT_LIGHT );

		// If Bold and none Bold strings have the same width, we
		// add in the calculation extra space, so that the tabs
		// looks better. This could be the case on systems without
		// an bold UI font and without synthetic bold support
		XubString aTestStr( RTL_CONSTASCII_USTRINGPARAM( "Abc." ) );
		SetFont( aLightFont );
		long nTextWidth1 = GetTextWidth( aTestStr );
		SetFont( aFont );
		long nTextWidth2 = GetTextWidth( aTestStr );
		mbExtraSpace = (nTextWidth1 == nTextWidth2);

		Size			aSize;
		const long		nOffsetX = 2 + GetItemsOffset().X();
		const long		nOffsetY = 2 + GetItemsOffset().Y();
		long			nX = nOffsetX;
		long			nY = nOffsetY;
		long			nMaxWidth = nWidth;
		sal_uInt16			nPos = 0;

		if ( (mnMaxPageWidth > 0) && (mnMaxPageWidth < nMaxWidth) )
			nMaxWidth = mnMaxPageWidth;
		nMaxWidth -= GetItemsOffset().X();

		sal_uInt16			nLines = 0;
		sal_uInt16			nCurLine = 0;
		long			nLineWidthAry[100];
		sal_uInt16			nLinePosAry[101];

		nLineWidthAry[0] = 0;
		nLinePosAry[0] = 0;
		for( std::vector<ImplTabItem>::iterator it = mpTabCtrlData->maItemList.begin();
			 it != mpTabCtrlData->maItemList.end(); ++it )
		{
			aSize = ImplGetItemSize( &(*it), nMaxWidth );

			if ( ((nX+aSize.Width()) > nWidth - 2) && (nWidth > 2+nOffsetX) )
			{
				if ( nLines == 99 )
					break;

				nX  = nOffsetX;
				nY += aSize.Height();
				nLines++;
				nLineWidthAry[nLines] = 0;
				nLinePosAry[nLines] = nPos;
			}

			Rectangle aNewRect( Point( nX, nY ), aSize );
			if ( mbSmallInvalidate && (it->maRect != aNewRect) )
				mbSmallInvalidate = sal_False;
			it->maRect = aNewRect;
			it->mnLine = nLines;
			it->mbFullVisible = sal_True;

			nLineWidthAry[nLines] += aSize.Width();
			nX += aSize.Width();

			if ( it->mnId == mnCurPageId )
				nCurLine = nLines;

			nPos++;
		}

		if ( nLines && !mpTabCtrlData->maItemList.empty() )
		{
			long	nDX = 0;
			long	nModDX = 0;
			long	nIDX = 0;
			sal_uInt16	i;
			sal_uInt16	n;
			long	nLineHeightAry[100];
			long	nIH = mpTabCtrlData->maItemList[0].maRect.Bottom()-2;

			i = 0;
			while ( i < nLines+1 )
			{
				if ( i <= nCurLine )
					nLineHeightAry[i] = nIH*(nLines-(nCurLine-i)) + GetItemsOffset().Y();
				else
					nLineHeightAry[i] = nIH*(i-nCurLine-1) + GetItemsOffset().Y();
				i++;
			}

			i = 0;
			n = 0;
			nLinePosAry[nLines+1] = (sal_uInt16)mpTabCtrlData->maItemList.size();
			for( std::vector< ImplTabItem >::iterator it = mpTabCtrlData->maItemList.begin();
				 it != mpTabCtrlData->maItemList.end(); ++it )
			{
				if ( i == nLinePosAry[n] )
				{
					if ( n == nLines+1 )
						break;

					nIDX = 0;
					if( nLinePosAry[n+1]-i > 0 )
					{
						nDX = (nWidth-nOffsetX-nLineWidthAry[n]) / (nLinePosAry[n+1]-i);
						nModDX = (nWidth-nOffsetX-nLineWidthAry[n]) % (nLinePosAry[n+1]-i);
					}
					else
					{
						// FIXME: this is a bad case of tabctrl way too small
						nDX = 0;
						nModDX = 0;
					}
					n++;
				}

				it->maRect.Left()   += nIDX;
				it->maRect.Right()  += nIDX+nDX;
				it->maRect.Top()     = nLineHeightAry[n-1];
				it->maRect.Bottom()  = nLineHeightAry[n-1]+nIH;
				nIDX += nDX;

				if ( nModDX )
				{
					nIDX++;
					it->maRect.Right()++;
					nModDX--;
				}

				i++;
			}
		}
		else
		{// only one line
			if(ImplGetSVData()->maNWFData.mbCenteredTabs)
			{
				int nRightSpace=nMaxWidth;// space left on the right by the tabs
				for( std::vector< ImplTabItem >::iterator it = mpTabCtrlData->maItemList.begin();
					it != mpTabCtrlData->maItemList.end(); ++it )
				{
					nRightSpace-=it->maRect.Right()-it->maRect.Left();
				}
				for( std::vector< ImplTabItem >::iterator it = mpTabCtrlData->maItemList.begin();
					it != mpTabCtrlData->maItemList.end(); ++it )
				{
					it->maRect.Left()+=(int) (nRightSpace/2);
					it->maRect.Right()+=(int) (nRightSpace/2);
				}
			}
		}

		mnLastWidth		= nWidth;
		mnLastHeight	= nHeight;
		mbFormat		= sal_False;
	}

	return size_t(nItemPos) < mpTabCtrlData->maItemList.size() ? mpTabCtrlData->maItemList[nItemPos].maRect : Rectangle();
}

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

void TabControl::ImplChangeTabPage( sal_uInt16 nId, sal_uInt16 nOldId )
{
	ImplFreeLayoutData();

	ImplTabItem*	pOldItem = ImplGetItem( nOldId );
	ImplTabItem*	pItem = ImplGetItem( nId );
	TabPage*		pOldPage = (pOldItem) ? pOldItem->mpTabPage : NULL;
	TabPage*		pPage = (pItem) ? pItem->mpTabPage : NULL;
	Window*		pCtrlParent = GetParent();

	if ( IsReallyVisible() && IsUpdateMode() )
	{
		sal_uInt16 nPos = GetPagePos( nId );
		Rectangle aRect = ImplGetTabRect( nPos );

		if ( !pOldItem || (pOldItem->mnLine != pItem->mnLine) )
		{
			aRect.Left() = 0;
			aRect.Top() = 0;
			aRect.Right() = Control::GetOutputSizePixel().Width();
		}
		else
		{
			aRect.Left()    -= 3;
			aRect.Top()     -= 2;
			aRect.Right()   += 3;
			Invalidate( aRect );
			nPos = GetPagePos( nOldId );
			aRect = ImplGetTabRect( nPos );
			aRect.Left()    -= 3;
			aRect.Top()     -= 2;
			aRect.Right()   += 3;
		}
		Invalidate( aRect );
	}

	if ( pOldPage == pPage )
		return;

	Rectangle aRect = ImplGetTabRect( TAB_PAGERECT );

	if ( pOldPage )
	{
		if ( mbRestoreHelpId )
			pCtrlParent->SetHelpId( rtl::OString() );
		if ( mbRestoreUnqId )
			pCtrlParent->SetUniqueId( rtl::OString() );
		pOldPage->DeactivatePage();
	}

	if ( pPage )
	{
		pPage->SetPosSizePixel( aRect.TopLeft(), aRect.GetSize() );

		// activate page here so the controls can be switched
		// also set the help id of the parent window to that of the tab page
		if ( !GetHelpId().getLength() )
		{
			mbRestoreHelpId = sal_True;
			pCtrlParent->SetHelpId( pPage->GetHelpId() );
		}
		if ( !pCtrlParent->GetUniqueId().getLength() )
		{
			mbRestoreUnqId = sal_True;
			pCtrlParent->SetUniqueId( pPage->GetUniqueId() );
		}

		pPage->ActivatePage();
		pPage->Show();

		if ( pOldPage && pOldPage->HasChildPathFocus() )
		{
			sal_uInt16	n = 0;
			Window* pFirstChild = pPage->ImplGetDlgWindow( n, DLGWINDOW_FIRST );
			if ( pFirstChild )
				pFirstChild->ImplControlFocus( GETFOCUS_INIT );
			else
				GrabFocus();
		}

		// pPage->Show();
	}

	if ( pOldPage )
		pOldPage->Hide();

	// Invalidate the same region that will be send to NWF
	// to always allow for bitmap caching
	// see Window::DrawNativeControl()
	if( IsNativeControlSupported( CTRL_TAB_PANE, PART_ENTIRE_CONTROL ) )
	{
		aRect.Left()   -= TAB_OFFSET;
		aRect.Top()    -= TAB_OFFSET;
		aRect.Right()  += TAB_OFFSET;
		aRect.Bottom() += TAB_OFFSET;
	}

	Invalidate( aRect );
}

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

sal_Bool TabControl::ImplPosCurTabPage()
{
	// Aktuelle TabPage resizen/positionieren
	ImplTabItem* pItem = ImplGetItem( GetCurPageId() );
	if ( pItem && pItem->mpTabPage )
	{
		Rectangle aRect = ImplGetTabRect( TAB_PAGERECT );
		pItem->mpTabPage->SetPosSizePixel( aRect.TopLeft(), aRect.GetSize() );
		return sal_True;
	}

	return sal_False;
}

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

void TabControl::ImplActivateTabPage( sal_Bool bNext )
{
	sal_uInt16 nCurPos = GetPagePos( GetCurPageId() );

	if ( bNext )
		nCurPos = (nCurPos + 1) % GetPageCount();
	else
	{
		if ( !nCurPos )
			nCurPos = GetPageCount()-1;
		else
			nCurPos--;
	}

	SelectTabPage( GetPageId( nCurPos ) );
}

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

void TabControl::ImplShowFocus()
{
	if ( !GetPageCount() || mpTabCtrlData->mpListBox )
		return;

	// make sure the focused item rect is computed using a bold font
	// the font may have changed meanwhile due to mouse over

	Font aOldFont( GetFont() );
	Font aFont( aOldFont );
	aFont.SetWeight( (!ImplGetSVData()->maNWFData.mbNoBoldTabFocus) ? WEIGHT_BOLD : WEIGHT_LIGHT );
	SetFont( aFont );

	sal_uInt16					nCurPos		= GetPagePos( mnCurPageId );
	Rectangle				aRect		= ImplGetTabRect( nCurPos );
	const ImplTabItem&		rItem		= mpTabCtrlData->maItemList[ nCurPos ];
	Size					aTabSize	= aRect.GetSize();
	Size aImageSize( 0, 0 );
	long					nTextHeight	= GetTextHeight();
	long					nTextWidth	= GetCtrlTextWidth( rItem.maFormatText );
	sal_uInt16					nOff;

	if ( !(GetSettings().GetStyleSettings().GetOptions() & STYLE_OPTION_MONO) )
		nOff = 1;
	else
		nOff = 0;

	if( !! rItem.maTabImage )
	{
		aImageSize = rItem.maTabImage.GetSizePixel();
		if( rItem.maFormatText.Len() )
			aImageSize.Width() += GetTextHeight()/4;
	}

	if( rItem.maFormatText.Len() )
	{
		// show focus around text
		aRect.Left()   = aRect.Left()+aImageSize.Width()+((aTabSize.Width()-nTextWidth-aImageSize.Width())/2)-nOff-1-1;
		aRect.Top()    = aRect.Top()+((aTabSize.Height()-nTextHeight)/2)-1-1;
		aRect.Right()  = aRect.Left()+nTextWidth+2;
		aRect.Bottom() = aRect.Top()+nTextHeight+2;
	}
	else
	{
		// show focus around image
		long nXPos = aRect.Left()+((aTabSize.Width()-nTextWidth-aImageSize.Width())/2)-nOff-1;
		long nYPos = aRect.Top();
		if( aImageSize.Height() < aRect.GetHeight() )
			nYPos += (aRect.GetHeight() - aImageSize.Height())/2;

		aRect.Left() = nXPos - 2;
		aRect.Top() = nYPos - 2;
		aRect.Right() = aRect.Left() + aImageSize.Width() + 4;
		aRect.Bottom() = aRect.Top() + aImageSize.Height() + 4;
	}
	ShowFocus( aRect );

	SetFont( aOldFont );
}

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

void TabControl::ImplDrawItem( ImplTabItem* pItem, const Rectangle& rCurRect, bool bLayout, bool bFirstInGroup, bool bLastInGroup, bool bIsCurrentItem )
{
	if ( pItem->maRect.IsEmpty() )
		return;

	if( bLayout )
	{
		if( !HasLayoutData() )
		{
			mpControlData->mpLayoutData = new vcl::ControlLayoutData();
			mpTabCtrlData->maLayoutLineToPageId.clear();
			mpTabCtrlData->maLayoutPageIdToLine.clear();
			mpTabCtrlData->maTabRectangles.clear();
		}
	}

	const StyleSettings&	rStyleSettings = GetSettings().GetStyleSettings();
	Rectangle				aRect = pItem->maRect;
	long					nLeftBottom = aRect.Bottom();
	long					nRightBottom = aRect.Bottom();
	sal_Bool					bLeftBorder = sal_True;
	sal_Bool					bRightBorder = sal_True;
	sal_uInt16					nOff;
	sal_Bool					bNativeOK = sal_False;

	sal_uInt16 nOff2 = 0;
	sal_uInt16 nOff3 = 0;

	if ( !(rStyleSettings.GetOptions() & STYLE_OPTION_MONO) )
		nOff = 1;
	else
		nOff = 0;

	// Wenn wir die aktuelle Page sind, müssen wir etwas mehr zeichnen
	if ( pItem->mnId == mnCurPageId )
	{
		nOff2 = 2;
		if( ! ImplGetSVData()->maNWFData.mbNoActiveTabTextRaise )
			nOff3 = 1;
	}
	else
	{
		Point aLeftTestPos = aRect.BottomLeft();
		Point aRightTestPos = aRect.BottomRight();
		if ( aLeftTestPos.Y() == rCurRect.Bottom() )
		{
			aLeftTestPos.X() -= 2;
			if ( rCurRect.IsInside( aLeftTestPos ) )
				bLeftBorder = sal_False;
			aRightTestPos.X() += 2;
			if ( rCurRect.IsInside( aRightTestPos ) )
				bRightBorder = sal_False;
		}
		else
		{
			if ( rCurRect.IsInside( aLeftTestPos ) )
				nLeftBottom -= 2;
			if ( rCurRect.IsInside( aRightTestPos ) )
				nRightBottom -= 2;
		}
	}

	if( !bLayout && (bNativeOK = IsNativeControlSupported(CTRL_TAB_ITEM, PART_ENTIRE_CONTROL)) == sal_True )
	{
		Rectangle			aCtrlRegion( pItem->maRect );
		ControlState		nState = 0;

		if( pItem->mnId == mnCurPageId )
		{
			nState |= CTRL_STATE_SELECTED;
			// only the selected item can be focused
			if ( HasFocus() )
				nState |= CTRL_STATE_FOCUSED;
		}
		if ( IsEnabled() )
			nState |= CTRL_STATE_ENABLED;
		if( IsMouseOver() && pItem->maRect.IsInside( GetPointerPosPixel() ) )
		{
			nState |= CTRL_STATE_ROLLOVER;
			for( std::vector< ImplTabItem >::iterator it = mpTabCtrlData->maItemList.begin();
				 it != mpTabCtrlData->maItemList.end(); ++it )
			{
				if( (&(*it) != pItem) && (it->maRect.IsInside( GetPointerPosPixel() ) ) )
				{
					nState &= ~CTRL_STATE_ROLLOVER; // avoid multiple highlighted tabs
					break;
				}
			}
		}

		TabitemValue tiValue;
		if(pItem->maRect.Left() < 5)
			tiValue.mnAlignment |= TABITEM_LEFTALIGNED;
		if(pItem->maRect.Right() > mnLastWidth - 5)
			tiValue.mnAlignment |= TABITEM_RIGHTALIGNED;
		if ( bFirstInGroup )
			tiValue.mnAlignment |= TABITEM_FIRST_IN_GROUP;
		if ( bLastInGroup )
			tiValue.mnAlignment |= TABITEM_LAST_IN_GROUP;

		bNativeOK = DrawNativeControl( CTRL_TAB_ITEM, PART_ENTIRE_CONTROL, aCtrlRegion, nState,
					tiValue, rtl::OUString() );
	}

	if( ! bLayout && !bNativeOK )
	{
		if ( !(rStyleSettings.GetOptions() & STYLE_OPTION_MONO) )
		{
			SetLineColor( rStyleSettings.GetLightColor() );
			DrawPixel( Point( aRect.Left()+1-nOff2, aRect.Top()+1-nOff2 ) ); // diagonally indented top-left pixel
			if ( bLeftBorder )
			{
				DrawLine( Point( aRect.Left()-nOff2, aRect.Top()+2-nOff2 ),
						  Point( aRect.Left()-nOff2, nLeftBottom-1 ) );
			}
			DrawLine( Point( aRect.Left()+2-nOff2, aRect.Top()-nOff2 ), // top line starting 2px from left border
					  Point( aRect.Right()+nOff2-3, aRect.Top()-nOff2 ) ); // ending 3px from right border

			if ( bRightBorder )
			{
				SetLineColor( rStyleSettings.GetShadowColor() );
				DrawLine( Point( aRect.Right()+nOff2-2, aRect.Top()+1-nOff2 ),
						  Point( aRect.Right()+nOff2-2, nRightBottom-1 ) );

				SetLineColor( rStyleSettings.GetDarkShadowColor() );
				DrawLine( Point( aRect.Right()+nOff2-1, aRect.Top()+3-nOff2 ),
						  Point( aRect.Right()+nOff2-1, nRightBottom-1 ) );
			}
		}
		else
		{
			SetLineColor( Color( COL_BLACK ) );
			DrawPixel( Point( aRect.Left()+1-nOff2, aRect.Top()+1-nOff2 ) );
			DrawPixel( Point( aRect.Right()+nOff2-2, aRect.Top()+1-nOff2 ) );
			if ( bLeftBorder )
			{
				DrawLine( Point( aRect.Left()-nOff2, aRect.Top()+2-nOff2 ),
						  Point( aRect.Left()-nOff2, nLeftBottom-1 ) );
			}
			DrawLine( Point( aRect.Left()+2-nOff2, aRect.Top()-nOff2 ),
					  Point( aRect.Right()-3, aRect.Top()-nOff2 ) );
			if ( bRightBorder )
			{
			DrawLine( Point( aRect.Right()+nOff2-1, aRect.Top()+2-nOff2 ),
					  Point( aRect.Right()+nOff2-1, nRightBottom-1 ) );
			}
		}
	}

	if( bLayout )
	{
		int nLine = mpControlData->mpLayoutData->m_aLineIndices.size();
		mpControlData->mpLayoutData->m_aLineIndices.push_back( mpControlData->mpLayoutData->m_aDisplayText.Len() );
		mpTabCtrlData->maLayoutPageIdToLine[ (int)pItem->mnId ] = nLine;
		mpTabCtrlData->maLayoutLineToPageId[ nLine ] = (int)pItem->mnId;
		mpTabCtrlData->maTabRectangles.push_back( aRect );
	}

	// set font accordingly, current item is painted bold
	// we set the font attributes always before drawing to be re-entrant (DrawNativeControl may trigger additional paints)
	Font aFont( GetFont() );
	aFont.SetTransparent( sal_True );
	aFont.SetWeight( ((bIsCurrentItem) && (!ImplGetSVData()->maNWFData.mbNoBoldTabFocus)) ? WEIGHT_BOLD : WEIGHT_LIGHT );
	SetFont( aFont );

	Size aTabSize = aRect.GetSize();
	Size aImageSize( 0, 0 );
	long nTextHeight = GetTextHeight();
	long nTextWidth = GetCtrlTextWidth( pItem->maFormatText );
	if( !! pItem->maTabImage )
	{
		aImageSize = pItem->maTabImage.GetSizePixel();
		if( pItem->maFormatText.Len() )
			aImageSize.Width() += GetTextHeight()/4;
	}
	long nXPos = aRect.Left()+((aTabSize.Width()-nTextWidth-aImageSize.Width())/2)-nOff-nOff3;
	long nYPos = aRect.Top()+((aTabSize.Height()-nTextHeight)/2)-nOff3;
	if( pItem->maFormatText.Len() )
	{
		sal_uInt16 nStyle = TEXT_DRAW_MNEMONIC;
		if( ! pItem->mbEnabled )
			nStyle |= TEXT_DRAW_DISABLE;
		DrawCtrlText( Point( nXPos + aImageSize.Width(), nYPos ),
					  pItem->maFormatText,
					  0, STRING_LEN, nStyle,
					  bLayout ? &mpControlData->mpLayoutData->m_aUnicodeBoundRects : NULL,
					  bLayout ? &mpControlData->mpLayoutData->m_aDisplayText : NULL
					);
	}

	if( !! pItem->maTabImage )
	{
		Point aImgTL( nXPos, aRect.Top() );
		if( aImageSize.Height() < aRect.GetHeight() )
			aImgTL.Y() += (aRect.GetHeight() - aImageSize.Height())/2;
		DrawImage( aImgTL, pItem->maTabImage, pItem->mbEnabled ? 0 : IMAGE_DRAW_DISABLE );
	}
}

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

long TabControl::ImplHandleKeyEvent( const KeyEvent& rKeyEvent )
{
	long nRet = 0;

	if ( GetPageCount() > 1 )
	{
		KeyCode			aKeyCode = rKeyEvent.GetKeyCode();
		sal_uInt16			nKeyCode = aKeyCode.GetCode();

		if ( aKeyCode.IsMod1() )
		{
			if ( aKeyCode.IsShift() || (nKeyCode == KEY_PAGEUP) )
			{
				if ( (nKeyCode == KEY_TAB) || (nKeyCode == KEY_PAGEUP) )
				{
					ImplActivateTabPage( sal_False );
					nRet = 1;
				}
			}
			else
			{
				if ( (nKeyCode == KEY_TAB) || (nKeyCode == KEY_PAGEDOWN) )
				{
					ImplActivateTabPage( sal_True );
					nRet = 1;
				}
			}
		}
	}

	return nRet;
}

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

IMPL_LINK( TabControl, ImplListBoxSelectHdl, ListBox*, EMPTYARG )
{
	SelectTabPage( GetPageId( mpTabCtrlData->mpListBox->GetSelectEntryPos() ) );
	return 0;
}

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

IMPL_LINK( TabControl, ImplWindowEventListener, VclSimpleEvent*, pEvent )
{
	if ( pEvent && pEvent->ISA( VclWindowEvent ) && (pEvent->GetId() == VCLEVENT_WINDOW_KEYINPUT) )
	{
		VclWindowEvent* pWindowEvent = static_cast< VclWindowEvent* >(pEvent);
		// Do not handle events from TabControl or its children, which is done in Notify(), where the events can be consumed.
		if ( !IsWindowOrChild( pWindowEvent->GetWindow() ) )
		{
			KeyEvent* pKeyEvent = static_cast< KeyEvent* >(pWindowEvent->GetData());
			ImplHandleKeyEvent( *pKeyEvent );
		}
	}
	return 0;
}

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

void TabControl::MouseButtonDown( const MouseEvent& rMEvt )
{
	if( mpTabCtrlData->mpListBox == NULL )
	{
		if( rMEvt.IsLeft() )
		{
			sal_uInt16 nPageId = GetPageId( rMEvt.GetPosPixel() );
			ImplTabItem* pItem = ImplGetItem( nPageId );
			if( pItem && pItem->mbEnabled )
				SelectTabPage( nPageId );
		}
	}
}

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

void TabControl::KeyInput( const KeyEvent& rKEvt )
{
	if( mpTabCtrlData->mpListBox )
		mpTabCtrlData->mpListBox->KeyInput( rKEvt );
	else if ( GetPageCount() > 1 )
	{
		KeyCode aKeyCode = rKEvt.GetKeyCode();
		sal_uInt16	nKeyCode = aKeyCode.GetCode();

		if ( (nKeyCode == KEY_LEFT) || (nKeyCode == KEY_RIGHT) )
		{
			sal_Bool bNext = (nKeyCode == KEY_RIGHT);
			ImplActivateTabPage( bNext );
		}
	}

	Control::KeyInput( rKEvt );
}

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

void TabControl::Paint( const Rectangle& rRect )
{
	ImplPaint( rRect, false );
}

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

void TabControl::ImplPaint( const Rectangle& rRect, bool bLayout )
{
	if( ! bLayout )
		HideFocus();

	// Hier wird gegebenenfalls auch neu formatiert
	Rectangle aRect = ImplGetTabRect( TAB_PAGERECT );

	// find current item
	ImplTabItem* pCurItem = NULL;
	for( std::vector< ImplTabItem >::iterator it = mpTabCtrlData->maItemList.begin();
		 it != mpTabCtrlData->maItemList.end(); ++it )
	{
		if ( it->mnId == mnCurPageId )
		{
			pCurItem = &(*it);
			break;
		}
	}

	// Draw the TabPage border
	const StyleSettings&	rStyleSettings = GetSettings().GetStyleSettings();
	Rectangle				aCurRect;
	long					nTopOff = 1;
	aRect.Left()   -= TAB_OFFSET;
	aRect.Top()    -= TAB_OFFSET;
	aRect.Right()  += TAB_OFFSET;
	aRect.Bottom() += TAB_OFFSET;

	// if we have an invisible tabpage or no tabpage at all the tabpage rect should be
	// increased to avoid round corners that might be drawn by a theme
	// in this case we're only interested in the top border of the tabpage because the tabitems are used
	// standalone (e.g. Impress)
	sal_Bool bNoTabPage = sal_False;
	TabPage*		pCurPage = (pCurItem) ? pCurItem->mpTabPage : NULL;
	if( !pCurPage || !pCurPage->IsVisible() )
	{
		bNoTabPage = sal_True;
		aRect.Left()-=10;
		aRect.Right()+=10;
	}

	sal_Bool bNativeOK = sal_False;
	if( ! bLayout && (bNativeOK = IsNativeControlSupported( CTRL_TAB_PANE, PART_ENTIRE_CONTROL) ) == sal_True )
	{
		const ImplControlValue aControlValue;

		ControlState nState = CTRL_STATE_ENABLED;
		int part = PART_ENTIRE_CONTROL;
		if ( !IsEnabled() )
			nState &= ~CTRL_STATE_ENABLED;
		if ( HasFocus() )
			nState |= CTRL_STATE_FOCUSED;

		Region aClipRgn( GetActiveClipRegion() );
		aClipRgn.Intersect( aRect );
		if( !rRect.IsEmpty() )
			aClipRgn.Intersect( rRect );

		if( !aClipRgn.IsEmpty() )
			bNativeOK = DrawNativeControl( CTRL_TAB_PANE, part, aRect, nState,
				aControlValue, rtl::OUString() );
	}
	else
	{
		if ( !(rStyleSettings.GetOptions() & STYLE_OPTION_MONO) )
			SetLineColor( rStyleSettings.GetLightColor() );
		else
			SetLineColor( Color( COL_BLACK ) );
		if ( pCurItem && !pCurItem->maRect.IsEmpty() )
		{
			aCurRect = pCurItem->maRect;
			if( ! bLayout )
				DrawLine( aRect.TopLeft(), Point( aCurRect.Left()-2, aRect.Top() ) );
			if ( aCurRect.Right()+1 < aRect.Right() )
			{
				if( ! bLayout )
					DrawLine( Point( aCurRect.Right(), aRect.Top() ), aRect.TopRight() );
			}
			else
				nTopOff = 0;
		}
		else
			if( ! bLayout )
				DrawLine( aRect.TopLeft(), aRect.TopRight() );

		if( ! bLayout )
		{
			DrawLine( aRect.TopLeft(), aRect.BottomLeft() );

			if ( !(rStyleSettings.GetOptions() & STYLE_OPTION_MONO) )
			{
				// if we have not tab page the bottom line of the tab page
				// directly touches the tab items, so choose a color that fits seamlessly
				if( bNoTabPage )
					SetLineColor( rStyleSettings.GetDialogColor() );
				else
					SetLineColor( rStyleSettings.GetShadowColor() );
				DrawLine( Point( 1, aRect.Bottom()-1 ),
						Point( aRect.Right()-1, aRect.Bottom()-1 ) );
				DrawLine( Point( aRect.Right()-1, aRect.Top()+nTopOff ),
						Point( aRect.Right()-1, aRect.Bottom()-1 ) );
				if( bNoTabPage )
					SetLineColor( rStyleSettings.GetDialogColor() );
				else
					SetLineColor( rStyleSettings.GetDarkShadowColor() );
				DrawLine( Point( 0, aRect.Bottom() ),
						Point( aRect.Right(), aRect.Bottom() ) );
				DrawLine( Point( aRect.Right(), aRect.Top()+nTopOff ),
						Point( aRect.Right(), aRect.Bottom() ) );
			}
			else
			{
				DrawLine( aRect.TopRight(), aRect.BottomRight() );
				DrawLine( aRect.BottomLeft(), aRect.BottomRight() );
			}
		}
	}

	if ( !mpTabCtrlData->maItemList.empty() && mpTabCtrlData->mpListBox == NULL )
	{
		// Some native toolkits (GTK+) draw tabs right-to-left, with an
		// overlap between adjacent tabs
		bool			bDrawTabsRTL = IsNativeControlSupported( CTRL_TAB_ITEM, PART_TABS_DRAW_RTL );
		ImplTabItem *	pFirstTab = NULL;
		ImplTabItem *	pLastTab = NULL;
		size_t idx;

		// Event though there is a tab overlap with GTK+, the first tab is not
		// overlapped on the left side. Other toolkits ignore this option.
		if ( bDrawTabsRTL )
		{
			pFirstTab = &mpTabCtrlData->maItemList.front();
			pLastTab = &mpTabCtrlData->maItemList.back();
			idx = mpTabCtrlData->maItemList.size()-1;
		}
		else
		{
			pLastTab = &mpTabCtrlData->maItemList.back();
			pFirstTab = &mpTabCtrlData->maItemList.front();
			idx = 0;
		}

		while ( idx < mpTabCtrlData->maItemList.size() )
		{
			ImplTabItem* pItem = &mpTabCtrlData->maItemList[idx];
			if ( pItem != pCurItem )
			{
				Region aClipRgn( GetActiveClipRegion() );
				aClipRgn.Intersect( pItem->maRect );
				if( !rRect.IsEmpty() )
					aClipRgn.Intersect( rRect );
				if( bLayout || !aClipRgn.IsEmpty() )
					ImplDrawItem( pItem, aCurRect, bLayout, (pItem==pFirstTab), (pItem==pLastTab), sal_False );
			}

			if ( bDrawTabsRTL )
				idx--;
			else
				idx++;
		}

		if ( pCurItem )
		{
			Region aClipRgn( GetActiveClipRegion() );
			aClipRgn.Intersect( pCurItem->maRect );
			if( !rRect.IsEmpty() )
				aClipRgn.Intersect( rRect );
			if( bLayout || !aClipRgn.IsEmpty() )
				ImplDrawItem( pCurItem, aCurRect, bLayout, (pCurItem==pFirstTab), (pCurItem==pLastTab), sal_True );
		}
	}

	if ( !bLayout && HasFocus() )
		ImplShowFocus();

	if( ! bLayout )
		mbSmallInvalidate = sal_True;
}

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

void TabControl::Resize()
{
	ImplFreeLayoutData();

	if ( !IsReallyShown() )
		return;

	if( mpTabCtrlData->mpListBox )
	{
		// get the listbox' preferred size
		Size aTabCtrlSize( GetSizePixel() );
		long nPrefWidth = mpTabCtrlData->mpListBox->GetOptimalSize( WINDOWSIZE_PREFERRED ).Width();
		if( nPrefWidth > aTabCtrlSize.Width() )
			nPrefWidth = aTabCtrlSize.Width();
		Size aNewSize( nPrefWidth, LogicToPixel( Size( 12, 12 ), MapMode( MAP_APPFONT ) ).Height() );
		Point aNewPos( (aTabCtrlSize.Width() - nPrefWidth) / 2, 0 );
		mpTabCtrlData->mpListBox->SetPosSizePixel( aNewPos, aNewSize );
	}

	mbFormat = sal_True;

	// Aktuelle TabPage resizen/positionieren
	sal_Bool bTabPage = ImplPosCurTabPage();
	// Feststellen, was invalidiert werden muss
	Size aNewSize = Control::GetOutputSizePixel();
	long nNewWidth = aNewSize.Width();
	for( std::vector< ImplTabItem >::iterator it = mpTabCtrlData->maItemList.begin();
		 it != mpTabCtrlData->maItemList.end(); ++it )
	{
		if ( !it->mbFullVisible ||
			 (it->maRect.Right()-2 >= nNewWidth) )
		{
			mbSmallInvalidate = sal_False;
			break;
		}
	}

	if ( mbSmallInvalidate )
	{
		Rectangle aRect = ImplGetTabRect( TAB_PAGERECT );
		aRect.Left()   -= TAB_OFFSET+TAB_BORDER_LEFT;
		aRect.Top()    -= TAB_OFFSET+TAB_BORDER_TOP;
		aRect.Right()  += TAB_OFFSET+TAB_BORDER_RIGHT;
		aRect.Bottom() += TAB_OFFSET+TAB_BORDER_BOTTOM;
		if ( bTabPage )
			Invalidate( aRect, INVALIDATE_NOCHILDREN );
		else
			Invalidate( aRect );

	}
	else
	{
		if ( bTabPage )
			Invalidate( INVALIDATE_NOCHILDREN );
		else
			Invalidate();
	}
}

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

void TabControl::GetFocus()
{
	if( ! mpTabCtrlData->mpListBox )
	{
		ImplShowFocus();
		SetInputContext( InputContext( GetFont() ) );
	}
	else
	{
		if( mpTabCtrlData->mpListBox->IsReallyVisible() )
			mpTabCtrlData->mpListBox->GrabFocus();
	}
	Control::GetFocus();
}

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

void TabControl::LoseFocus()
{
	if( ! mpTabCtrlData->mpListBox )
		HideFocus();
	Control::LoseFocus();
}

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

void TabControl::RequestHelp( const HelpEvent& rHEvt )
{
	sal_uInt16 nItemId = rHEvt.KeyboardActivated() ? mnCurPageId : GetPageId( ScreenToOutputPixel( rHEvt.GetMousePosPixel() ) );

	if ( nItemId )
	{
		if ( rHEvt.GetMode() & HELPMODE_BALLOON )
		{
			XubString aStr = GetHelpText( nItemId );
			if ( aStr.Len() )
			{
				Rectangle aItemRect = ImplGetTabRect( GetPagePos( nItemId ) );
				Point aPt = OutputToScreenPixel( aItemRect.TopLeft() );
				aItemRect.Left()   = aPt.X();
				aItemRect.Top()    = aPt.Y();
				aPt = OutputToScreenPixel( aItemRect.BottomRight() );
				aItemRect.Right()  = aPt.X();
				aItemRect.Bottom() = aPt.Y();
				Help::ShowBalloon( this, aItemRect.Center(), aItemRect, aStr );
				return;
			}
		}
		else if ( rHEvt.GetMode() & HELPMODE_EXTENDED )
		{
			rtl::OUString aHelpId( rtl::OStringToOUString( GetHelpId( nItemId ), RTL_TEXTENCODING_UTF8 ) );
			if ( aHelpId.getLength() )
			{
				// Wenn eine Hilfe existiert, dann auslösen
				Help* pHelp = Application::GetHelp();
				if ( pHelp )
					pHelp->Start( aHelpId, this );
				return;
			}
		}

		// Bei Quick- oder Balloon-Help zeigen wir den Text an,
		// wenn dieser abgeschnitten ist
		if ( rHEvt.GetMode() & (HELPMODE_QUICK | HELPMODE_BALLOON) )
		{
			ImplTabItem* pItem = ImplGetItem( nItemId );
			const XubString& rStr = pItem->maText;
			if ( rStr != pItem->maFormatText )
			{
				Rectangle aItemRect = ImplGetTabRect( GetPagePos( nItemId ) );
				Point aPt = OutputToScreenPixel( aItemRect.TopLeft() );
				aItemRect.Left()   = aPt.X();
				aItemRect.Top()    = aPt.Y();
				aPt = OutputToScreenPixel( aItemRect.BottomRight() );
				aItemRect.Right()  = aPt.X();
				aItemRect.Bottom() = aPt.Y();
				if ( rStr.Len() )
				{
					if ( rHEvt.GetMode() & HELPMODE_BALLOON )
						Help::ShowBalloon( this, aItemRect.Center(), aItemRect, rStr );
					else
						Help::ShowQuickHelp( this, aItemRect, rStr );
					return;
				}
			}
		}

		if ( rHEvt.GetMode() & HELPMODE_QUICK )
		{
			ImplTabItem* pItem = ImplGetItem( nItemId );
			const XubString& rHelpText = pItem->maHelpText;
			// show tooltip if not text but image is set and helptext is available
			if ( rHelpText.Len() > 0 && pItem->maText.Len() == 0 && !!pItem->maTabImage )
			{
				Rectangle aItemRect = ImplGetTabRect( GetPagePos( nItemId ) );
				Point aPt = OutputToScreenPixel( aItemRect.TopLeft() );
				aItemRect.Left()   = aPt.X();
				aItemRect.Top()    = aPt.Y();
				aPt = OutputToScreenPixel( aItemRect.BottomRight() );
				aItemRect.Right()  = aPt.X();
				aItemRect.Bottom() = aPt.Y();
				Help::ShowQuickHelp( this, aItemRect, rHelpText );
				return;
			}
		}
	}

	Control::RequestHelp( rHEvt );
}

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

void TabControl::Command( const CommandEvent& rCEvt )
{
	if( (mpTabCtrlData->mpListBox == NULL) && (rCEvt.GetCommand() == COMMAND_CONTEXTMENU) && (GetPageCount() > 1) )
	{
		Point	aMenuPos;
		sal_Bool	bMenu;
		if ( rCEvt.IsMouseEvent() )
		{
			aMenuPos = rCEvt.GetMousePosPixel();
			bMenu = GetPageId( aMenuPos ) != 0;
		}
		else
		{
			aMenuPos = ImplGetTabRect( GetPagePos( mnCurPageId ) ).Center();
			bMenu = sal_True;
		}

		if ( bMenu )
		{
			PopupMenu aMenu;
			for( std::vector< ImplTabItem >::iterator it = mpTabCtrlData->maItemList.begin();
				it != mpTabCtrlData->maItemList.end(); ++it )
			{
				aMenu.InsertItem( it->mnId, it->maText, MIB_CHECKABLE | MIB_RADIOCHECK );
				if ( it->mnId == mnCurPageId )
					aMenu.CheckItem( it->mnId );
				aMenu.SetHelpId( it->mnId, it->maHelpId );
			}

			sal_uInt16 nId = aMenu.Execute( this, aMenuPos );
			if ( nId && (nId != mnCurPageId) )
				SelectTabPage( nId );
			return;
		}
	}

	Control::Command( rCEvt );
}

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

void TabControl::StateChanged( StateChangedType nType )
{
	Control::StateChanged( nType );

	if ( nType == STATE_CHANGE_INITSHOW )
	{
		ImplPosCurTabPage();
		if( mpTabCtrlData->mpListBox )
			Resize();
	}
	else if ( nType == STATE_CHANGE_UPDATEMODE )
	{
		if ( IsUpdateMode() )
			Invalidate();
	}
	else if ( (nType == STATE_CHANGE_ZOOM) ||
			  (nType == STATE_CHANGE_CONTROLFONT) )
	{
		ImplInitSettings( sal_True, sal_False, sal_False );
		Invalidate();
	}
	else if ( nType == STATE_CHANGE_CONTROLFOREGROUND )
	{
		ImplInitSettings( sal_False, sal_True, sal_False );
		Invalidate();
	}
	else if ( nType == STATE_CHANGE_CONTROLBACKGROUND )
	{
		ImplInitSettings( sal_False, sal_False, sal_True );
		Invalidate();
	}
}

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

void TabControl::DataChanged( const DataChangedEvent& rDCEvt )
{
	Control::DataChanged( rDCEvt );

	if ( (rDCEvt.GetType() == DATACHANGED_FONTS) ||
		 (rDCEvt.GetType() == DATACHANGED_FONTSUBSTITUTION) ||
		 ((rDCEvt.GetType() == DATACHANGED_SETTINGS) &&
		  (rDCEvt.GetFlags() & SETTINGS_STYLE)) )
	{
		ImplInitSettings( sal_True, sal_True, sal_True );
		Invalidate();
	}
}

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

Rectangle* TabControl::ImplFindPartRect( const Point& rPt )
{
	ImplTabItem* pFoundItem = NULL;
	int nFound = 0;
	for( std::vector< ImplTabItem >::iterator it = mpTabCtrlData->maItemList.begin();
		 it != mpTabCtrlData->maItemList.end(); ++it )
	{
		if ( it->maRect.IsInside( rPt ) )
		{
			// assure that only one tab is highlighted at a time
			nFound++;
			pFoundItem = &(*it);
		}
	}
	// assure that only one tab is highlighted at a time
	return nFound == 1 ? &pFoundItem->maRect : NULL;
}

long TabControl::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_TAB_ITEM, PART_ENTIRE_CONTROL) )
			{
				Rectangle* pRect = ImplFindPartRect( GetPointerPosPixel() );
				Rectangle* pLastRect = ImplFindPartRect( GetLastPointerPosPixel() );
				if( pRect != pLastRect || (pMouseEvt->IsLeaveWindow() || pMouseEvt->IsEnterWindow()) )
				{
					Region aClipRgn;
					if( pLastRect )
					{
						// allow for slightly bigger tabitems
						// as used by gtk
						// TODO: query for the correct sizes
						Rectangle aRect(*pLastRect);
						aRect.nLeft-=2;
						aRect.nRight+=2;
						aRect.nTop-=3;
						aClipRgn.Union( aRect );
					}
					if( pRect )
					{
						// allow for slightly bigger tabitems
						// as used by gtk
						// TODO: query for the correct sizes
						Rectangle aRect(*pRect);
						aRect.nLeft-=2;
						aRect.nRight+=2;
						aRect.nTop-=3;
						aClipRgn.Union( aRect );
					}
					if( !aClipRgn.IsEmpty() )
						Invalidate( aClipRgn );
				}
			}
		}
	}

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

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

long TabControl::Notify( NotifyEvent& rNEvt )
{
	long nRet = 0;

	if ( rNEvt.GetType() == EVENT_KEYINPUT )
		nRet = ImplHandleKeyEvent( *rNEvt.GetKeyEvent() );

	return nRet ? nRet : Control::Notify( rNEvt );
}

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

void TabControl::ActivatePage()
{
	maActivateHdl.Call( this );
}

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

long TabControl::DeactivatePage()
{
	if ( maDeactivateHdl.IsSet() )
		return maDeactivateHdl.Call( this );
	else
		return sal_True;
}

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

void TabControl::SetTabPageSizePixel( const Size& rSize )
{
	ImplFreeLayoutData();

	Size aNewSize( rSize );
	aNewSize.Width() += TAB_OFFSET*2;
	Rectangle aRect = ImplGetTabRect( TAB_PAGERECT,
									  aNewSize.Width(), aNewSize.Height() );
	aNewSize.Height() += aRect.Top()+TAB_OFFSET;
	Window::SetOutputSizePixel( aNewSize );
}

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

Size TabControl::GetTabPageSizePixel() const
{
	Rectangle aRect = ((TabControl*)this)->ImplGetTabRect( TAB_PAGERECT );
	return aRect.GetSize();
}

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

void TabControl::InsertPage( const ResId& rResId, sal_uInt16 nPos )
{
	GetRes( rResId.SetRT( RSC_TABCONTROLITEM ) );

	sal_uLong nObjMask = ReadLongRes();
	sal_uInt16 nItemId = 1;

	// ID
	if ( nObjMask & RSC_TABCONTROLITEM_ID )
		nItemId = sal::static_int_cast<sal_uInt16>(ReadLongRes());

	// Text
	XubString aTmpStr;
	if( nObjMask & RSC_TABCONTROLITEM_TEXT )
		aTmpStr = ReadStringRes();
	InsertPage( nItemId, aTmpStr, nPos );

	// PageResID
	if ( nObjMask & RSC_TABCONTROLITEM_PAGERESID )
	{
		ImplTabItem& rItem = mpTabCtrlData->maItemList[ GetPagePos( nItemId ) ];
		rItem.mnTabPageResId = sal::static_int_cast<sal_uInt16>(ReadLongRes());
	}
}

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

void TabControl::InsertPage( sal_uInt16 nPageId, const XubString& rText,
							 sal_uInt16 nPos )
{
	DBG_ASSERT( nPageId, "TabControl::InsertPage(): PageId == 0" );
	DBG_ASSERT( GetPagePos( nPageId ) == TAB_PAGE_NOTFOUND,
				"TabControl::InsertPage(): PageId already exists" );

	// insert new page item
	ImplTabItem* pItem = NULL;
	if( nPos == TAB_APPEND || size_t(nPos) >= mpTabCtrlData->maItemList.size() )
	{
		mpTabCtrlData->maItemList.push_back( ImplTabItem() );
		pItem = &mpTabCtrlData->maItemList.back();
		if( mpTabCtrlData->mpListBox )
			mpTabCtrlData->mpListBox->InsertEntry( rText );
	}
	else
	{
		std::vector< ImplTabItem >::iterator new_it =
			mpTabCtrlData->maItemList.insert( mpTabCtrlData->maItemList.begin() + nPos, ImplTabItem() );
		pItem = &(*new_it);
		if( mpTabCtrlData->mpListBox )
			mpTabCtrlData->mpListBox->InsertEntry( rText, nPos);
	}
	if( mpTabCtrlData->mpListBox )
	{
		if( ! mnCurPageId )
			mpTabCtrlData->mpListBox->SelectEntryPos( 0 );
		mpTabCtrlData->mpListBox->SetDropDownLineCount( mpTabCtrlData->mpListBox->GetEntryCount() );
	}

	// set current page id
	if ( !mnCurPageId )
		mnCurPageId = nPageId;

	// init new page item
	pItem->mnId				= nPageId;
	pItem->mpTabPage		= NULL;
	pItem->mnTabPageResId	= 0;
	pItem->maText			= rText;
	pItem->mbFullVisible	= sal_False;

	mbFormat = sal_True;
	if ( IsUpdateMode() )
		Invalidate();

	ImplFreeLayoutData();
	if( mpTabCtrlData->mpListBox ) // reposition/resize listbox
		Resize();

	ImplCallEventListeners( VCLEVENT_TABPAGE_INSERTED, (void*) (sal_uLong)nPageId );
}

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

void TabControl::RemovePage( sal_uInt16 nPageId )
{
	sal_uInt16 nPos = GetPagePos( nPageId );

	// does the item exist?
	if ( nPos != TAB_PAGE_NOTFOUND )
	{
		//remove page item
		std::vector< ImplTabItem >::iterator it = mpTabCtrlData->maItemList.begin() + nPos;
		bool bIsCurrentPage = (it->mnId == mnCurPageId);
		mpTabCtrlData->maItemList.erase( it );
		if( mpTabCtrlData->mpListBox )
		{
			mpTabCtrlData->mpListBox->RemoveEntry( nPos );
			mpTabCtrlData->mpListBox->SetDropDownLineCount( mpTabCtrlData->mpListBox->GetEntryCount() );
		}

		// If current page is removed, than first page gets the current page
		if ( bIsCurrentPage )
		{
			mnCurPageId = 0;

			if( ! mpTabCtrlData->maItemList.empty() )
			{
				// don't do this by simply setting mnCurPageId to pFirstItem->mnId
				// this leaves a lot of stuff (such trivias as _showing_ the new current page) undone
				// instead, call SetCurPageId
				// without this, the next (outside) call to SetCurPageId with the id of the first page
				// will result in doing nothing (as we assume that nothing changed, then), and the page
				// will never be shown.
				// 86875 - 05/11/2001 - frank.schoenheit@germany.sun.com

				SetCurPageId( mpTabCtrlData->maItemList[0].mnId );
			}
		}

		mbFormat = sal_True;
		if ( IsUpdateMode() )
			Invalidate();

		ImplFreeLayoutData();

		ImplCallEventListeners( VCLEVENT_TABPAGE_REMOVED, (void*) (sal_uLong) nPageId );
	}
}

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

void TabControl::Clear()
{
	// clear item list
	mpTabCtrlData->maItemList.clear();
	mnCurPageId = 0;
	if( mpTabCtrlData->mpListBox )
		mpTabCtrlData->mpListBox->Clear();

	ImplFreeLayoutData();

	mbFormat = sal_True;
	if ( IsUpdateMode() )
		Invalidate();

	ImplCallEventListeners( VCLEVENT_TABPAGE_REMOVEDALL );
}

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

void TabControl::EnablePage( sal_uInt16 i_nPageId, bool i_bEnable )
{
	ImplTabItem* pItem = ImplGetItem( i_nPageId );

	if ( pItem && pItem->mbEnabled != i_bEnable )
	{
		pItem->mbEnabled = i_bEnable;
		mbFormat = sal_True;
		if( mpTabCtrlData->mpListBox )
			mpTabCtrlData->mpListBox->SetEntryFlags( GetPagePos( i_nPageId ),
													 i_bEnable ? 0 : (LISTBOX_ENTRY_FLAG_DISABLE_SELECTION | LISTBOX_ENTRY_FLAG_DRAW_DISABLED) );
		if( pItem->mnId == mnCurPageId )
		{
			// SetCurPageId will change to an enabled page
			SetCurPageId( mnCurPageId );
		}
		else if ( IsUpdateMode() )
			Invalidate();
	}
}

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

sal_uInt16 TabControl::GetPageCount() const
{
	return (sal_uInt16)mpTabCtrlData->maItemList.size();
}

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

sal_uInt16 TabControl::GetPageId( sal_uInt16 nPos ) const
{
	if( size_t(nPos) < mpTabCtrlData->maItemList.size() )
		return mpTabCtrlData->maItemList[ nPos ].mnId;
	return 0;
}

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

sal_uInt16 TabControl::GetPagePos( sal_uInt16 nPageId ) const
{
	for( std::vector< ImplTabItem >::const_iterator it = mpTabCtrlData->maItemList.begin();
		 it != mpTabCtrlData->maItemList.end(); ++it )
	{
		if ( it->mnId == nPageId )
			return (sal_uInt16)(it - mpTabCtrlData->maItemList.begin());
	}

	return TAB_PAGE_NOTFOUND;
}

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

sal_uInt16 TabControl::GetPageId( const Point& rPos ) const
{
	for( size_t i = 0; i < mpTabCtrlData->maItemList.size(); ++i )
	{
		if ( ((TabControl*)this)->ImplGetTabRect( static_cast<sal_uInt16>(i) ).IsInside( rPos ) )
			return mpTabCtrlData->maItemList[ i ].mnId;
	}

	return 0;
}

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

void TabControl::SetCurPageId( sal_uInt16 nPageId )
{
	sal_uInt16 nPos = GetPagePos( nPageId );
	while( nPos != TAB_PAGE_NOTFOUND &&
		   ! mpTabCtrlData->maItemList[nPos].mbEnabled )
	{
		nPos++;
		if( size_t(nPos) >= mpTabCtrlData->maItemList.size() )
			nPos = 0;
		if( mpTabCtrlData->maItemList[nPos].mnId == nPageId )
			break;
	}

	if( nPos != TAB_PAGE_NOTFOUND )
	{
		nPageId = mpTabCtrlData->maItemList[nPos].mnId;
		if ( nPageId == mnCurPageId )
		{
			if ( mnActPageId )
				mnActPageId = nPageId;
			return;
		}

		if ( mnActPageId )
			mnActPageId = nPageId;
		else
		{
			mbFormat = sal_True;
			sal_uInt16 nOldId = mnCurPageId;
			mnCurPageId = nPageId;
			ImplChangeTabPage( nPageId, nOldId );
		}
	}
}

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

sal_uInt16 TabControl::GetCurPageId() const
{
	if ( mnActPageId )
		return mnActPageId;
	else
		return mnCurPageId;
}

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

void TabControl::SelectTabPage( sal_uInt16 nPageId )
{
	if ( nPageId && (nPageId != mnCurPageId) )
	{
		ImplFreeLayoutData();

		ImplCallEventListeners( VCLEVENT_TABPAGE_DEACTIVATE, (void*) (sal_uLong) mnCurPageId );
		if ( DeactivatePage() )
		{
			mnActPageId = nPageId;
			ActivatePage();
			// Page könnte im Activate-Handler umgeschaltet wurden sein
			nPageId = mnActPageId;
			mnActPageId = 0;
			SetCurPageId( nPageId );
			if( mpTabCtrlData->mpListBox )
				mpTabCtrlData->mpListBox->SelectEntryPos( GetPagePos( nPageId ) );
			ImplCallEventListeners( VCLEVENT_TABPAGE_ACTIVATE, (void*) (sal_uLong) nPageId );
		}
	}
}

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

void TabControl::SetTabPage( sal_uInt16 nPageId, TabPage* pTabPage )
{
	ImplTabItem* pItem = ImplGetItem( nPageId );

	if ( pItem && (pItem->mpTabPage != pTabPage) )
	{
		if ( pTabPage )
		{
			DBG_ASSERT( !pTabPage->IsVisible(), "TabControl::SetTabPage() - Page is visible" );

			if ( IsDefaultSize() )
				SetTabPageSizePixel( pTabPage->GetSizePixel() );

			// Erst hier setzen, damit Resize nicht TabPage umpositioniert
			pItem->mpTabPage = pTabPage;
			if ( pItem->mnId == mnCurPageId )
				ImplChangeTabPage( pItem->mnId, 0 );
		}
		else
			pItem->mpTabPage = NULL;
	}
}

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

TabPage* TabControl::GetTabPage( sal_uInt16 nPageId ) const
{
	ImplTabItem* pItem = ImplGetItem( nPageId );

	if ( pItem )
		return pItem->mpTabPage;
	else
		return NULL;
}

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

sal_uInt16 TabControl::GetTabPageResId( sal_uInt16 nPageId ) const
{
	ImplTabItem* pItem = ImplGetItem( nPageId );

	if ( pItem )
		return pItem->mnTabPageResId;
	else
		return 0;
}

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

void TabControl::SetPageText( sal_uInt16 nPageId, const XubString& rText )
{
	ImplTabItem* pItem = ImplGetItem( nPageId );

	if ( pItem && pItem->maText != rText )
	{
		pItem->maText = rText;
		mbFormat = sal_True;
		if( mpTabCtrlData->mpListBox )
		{
			sal_uInt16 nPos = GetPagePos( nPageId );
			mpTabCtrlData->mpListBox->RemoveEntry( nPos );
			mpTabCtrlData->mpListBox->InsertEntry( rText, nPos );
		}
		if ( IsUpdateMode() )
			Invalidate();
		ImplFreeLayoutData();
		ImplCallEventListeners( VCLEVENT_TABPAGE_PAGETEXTCHANGED, (void*) (sal_uLong) nPageId );
	}
}

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

XubString TabControl::GetPageText( sal_uInt16 nPageId ) const
{
	ImplTabItem* pItem = ImplGetItem( nPageId );

	if ( pItem )
		return pItem->maText;
	else
		return ImplGetSVEmptyStr();
}

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

void TabControl::SetHelpText( sal_uInt16 nPageId, const XubString& rText )
{
	ImplTabItem* pItem = ImplGetItem( nPageId );

	if ( pItem )
		pItem->maHelpText = rText;
}

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

const XubString& TabControl::GetHelpText( sal_uInt16 nPageId ) const
{
	ImplTabItem* pItem = ImplGetItem( nPageId );

	if ( pItem )
	{
		if ( !pItem->maHelpText.Len() && pItem->maHelpId.getLength() )
		{
			Help* pHelp = Application::GetHelp();
			if ( pHelp )
				pItem->maHelpText = pHelp->GetHelpText( rtl::OStringToOUString( pItem->maHelpId, RTL_TEXTENCODING_UTF8 ), this );
		}

		return pItem->maHelpText;
	}
	else
		return ImplGetSVEmptyStr();
}

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

void TabControl::SetHelpId( sal_uInt16 nPageId, const rtl::OString& rHelpId )
{
	ImplTabItem* pItem = ImplGetItem( nPageId );

	if ( pItem )
		pItem->maHelpId = rHelpId;
}

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

rtl::OString TabControl::GetHelpId( sal_uInt16 nPageId ) const
{
	rtl::OString aRet;
	ImplTabItem* pItem = ImplGetItem( nPageId );

	if ( pItem )
		aRet = pItem->maHelpId;

	return aRet;
}

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

void TabControl::SetPageImage( sal_uInt16 i_nPageId, const Image& i_rImage )
{
	ImplTabItem* pItem = ImplGetItem( i_nPageId );

	if ( pItem )
	{
		pItem->maTabImage = i_rImage;
		mbFormat = sal_True;
		if ( IsUpdateMode() )
			Invalidate();
	}
}

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

const Image* TabControl::GetPageImage( sal_uInt16 i_nPageId ) const
{
	const ImplTabItem* pItem = ImplGetItem( i_nPageId );
	return pItem ? &pItem->maTabImage : NULL;
}

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

Rectangle TabControl::GetCharacterBounds( sal_uInt16 nPageId, long nIndex ) const
{
	Rectangle aRet;

	if( !HasLayoutData() || ! mpTabCtrlData->maLayoutPageIdToLine.size() )
		FillLayoutData();

	if( HasLayoutData() )
	{
		std::hash_map< int, int >::const_iterator it = mpTabCtrlData->maLayoutPageIdToLine.find( (int)nPageId );
		if( it != mpTabCtrlData->maLayoutPageIdToLine.end() )
		{
			Pair aPair = mpControlData->mpLayoutData->GetLineStartEnd( it->second );
			if( (aPair.B() - aPair.A()) >= nIndex )
				aRet = mpControlData->mpLayoutData->GetCharacterBounds( aPair.A() + nIndex );
		}
	}

	return aRet;
}

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

long TabControl::GetIndexForPoint( const Point& rPoint, sal_uInt16& rPageId ) const
{
	long nRet = -1;

	if( !HasLayoutData() || ! mpTabCtrlData->maLayoutPageIdToLine.size() )
		FillLayoutData();

	if( HasLayoutData() )
	{
		int nIndex = mpControlData->mpLayoutData->GetIndexForPoint( rPoint );
		if( nIndex != -1 )
		{
			// what line (->pageid) is this index in ?
			int nLines = mpControlData->mpLayoutData->GetLineCount();
			int nLine = -1;
			while( ++nLine < nLines )
			{
				Pair aPair = mpControlData->mpLayoutData->GetLineStartEnd( nLine );
				if( aPair.A() <= nIndex && aPair.B() >= nIndex )
				{
					nRet = nIndex - aPair.A();
					rPageId = (sal_uInt16)mpTabCtrlData->maLayoutLineToPageId[ nLine ];
					break;
				}
			}
		}
	}

	return nRet;
}

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

void TabControl::FillLayoutData() const
{
	mpTabCtrlData->maLayoutLineToPageId.clear();
	mpTabCtrlData->maLayoutPageIdToLine.clear();
	const_cast<TabControl*>(this)->ImplPaint( Rectangle(), true );
}

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

Rectangle TabControl::GetTabPageBounds( sal_uInt16 nPage ) const
{
	Rectangle aRet;

	if( !HasLayoutData() || ! mpTabCtrlData->maLayoutPageIdToLine.size() )
		FillLayoutData();

	if( HasLayoutData() )
	{
		std::hash_map< int, int >::const_iterator it = mpTabCtrlData->maLayoutPageIdToLine.find( (int)nPage );
		if( it != mpTabCtrlData->maLayoutPageIdToLine.end() )
		{
			if( it->second >= 0 && it->second < static_cast<int>(mpTabCtrlData->maTabRectangles.size()) )
			{
				aRet = mpTabCtrlData->maTabRectangles[ it->second ];
				aRet.Union( const_cast<TabControl*>(this)->ImplGetTabRect( TAB_PAGERECT ) );
			}
		}
	}

	return aRet;
}

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

Rectangle TabControl::GetTabBounds( sal_uInt16 nPageId ) const
{
	Rectangle aRet;

	ImplTabItem* pItem = ImplGetItem( nPageId );
	if(pItem)
		aRet = pItem->maRect;

	return aRet;
}

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

void TabControl::SetItemsOffset( const Point& rOffs )
{
	if( mpTabCtrlData )
		mpTabCtrlData->maItemsOffset = rOffs;
}

Point TabControl::GetItemsOffset() const
{
	if( mpTabCtrlData )
		return mpTabCtrlData->maItemsOffset;
	else
		return Point();
}

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

Size TabControl::GetOptimalSize(WindowSizeType eType) const
{
	switch (eType) {
	case WINDOWSIZE_MINIMUM:
		return mpTabCtrlData ? mpTabCtrlData->maMinSize : Size();
	default:
		return Control::GetOptimalSize( eType );
	}
}

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

void TabControl::SetMinimumSizePixel( const Size& i_rSize )
{
	if( mpTabCtrlData )
		mpTabCtrlData->maMinSize = i_rSize;
}

/* vim: set noet sw=4 ts=4: */
