/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 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/svdata.hxx"
#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/controldata.hxx"
#include "vcl/sound.hxx"
#include "vcl/lstbox.hxx"
#include "vcl/smartid.hxx"

#include "vcl/window.h"

#include <hash_map>
#include <vector>

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

struct ImplTabItem
{
    USHORT              mnId;
    USHORT              mnTabPageResId;
    TabPage*            mpTabPage;
    String              maText;
    String              maFormatText;
    String              maHelpText;
    ULONG               mnHelpId;
    Rectangle           maRect;
    USHORT              mnLine;
    bool                mbFullVisible;
    bool                mbEnabled;
    Image               maTabImage;

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

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

struct ImplTabCtrlData
{
    PushButton*                     mpLeftBtn;
    PushButton*                     mpRightBtn;
    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;
};

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

#if 0
// not used
#define TABCOLORCOUNT   10

static ColorData aImplTabColorAry[TABCOLORCOUNT] =
{
    RGB_COLORDATA(  80, 216, 248 ),
    RGB_COLORDATA( 128, 216, 168 ),
    RGB_COLORDATA( 128, 144, 248 ),
    RGB_COLORDATA( 208, 180, 168 ),
    RGB_COLORDATA( 248, 252, 168 ),
    RGB_COLORDATA( 168, 144, 168 ),
    RGB_COLORDATA( 248, 144,  80 ),
    RGB_COLORDATA( 248, 216,  80 ),
    RGB_COLORDATA( 248, 180, 168 ),
    RGB_COLORDATA( 248, 216, 168 )
};
#endif

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

#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

// Fuer 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;

    // no single line tabs since NWF
    nStyle &= ~WB_SINGLELINE;

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

    mnLastWidth                 = 0;
    mnLastHeight                = 0;
    mnBtnSize                   = 0;
    mnMaxPageWidth              = 0;
    mnActPageId                 = 0;
    mnCurPageId                 = 0;
    mnFirstPagePos              = 0;
    mnLastFirstPagePos          = 0;
    mbFormat                    = TRUE;
    mbRestoreHelpId             = FALSE;
    mbRestoreUnqId              = FALSE;
    mbSingleLine                = FALSE;
    mbScroll                    = FALSE;
    mbRestoreSmartId            = FALSE;
    mbSmallInvalidate           = FALSE;
    mbExtraSpace                = FALSE;
    mpTabCtrlData               = new ImplTabCtrlData;
    mpTabCtrlData->mpLeftBtn    = NULL;
    mpTabCtrlData->mpRightBtn   = NULL;
    mpTabCtrlData->mpListBox    = NULL;


    ImplInitSettings( TRUE, TRUE, 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 (ie filled) by a native widget, make sure all contols will have transparent background
    // otherwise they will paint with a wrong background
    if( IsNativeControlSupported(CTRL_TAB_PANE, PART_ENTIRE_CONTROL) )
        EnableChildTransparentMode( 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( BOOL bFont,
                                   BOOL bForeground, 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( TRUE );
            SetParentClipMode( PARENTCLIPMODE_NOCLIP );
            SetPaintTransparent( TRUE );
            SetBackground();
            ImplGetWindowImpl()->mbUseNativeFocus = ImplGetSVData()->maNWFData.mbNoFocusRects;
        }
        else
        {
            EnableChildTransparentMode( FALSE );
            SetParentClipMode( 0 );
            SetPaintTransparent( FALSE );

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

    ImplScrollBtnsColor();
}

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

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 );

    ULONG nObjMask = ReadLongRes();

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

        // Item hinzufuegen
        for( 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 loeschen
    if ( mpTabCtrlData )
    {
        if( mpTabCtrlData->mpListBox )
            delete mpTabCtrlData->mpListBox;
        if ( mpTabCtrlData->mpLeftBtn )
            delete mpTabCtrlData->mpLeftBtn;
        if ( mpTabCtrlData->mpRightBtn )
            delete mpTabCtrlData->mpRightBtn;
        delete mpTabCtrlData;
    }
}

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

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

    return NULL;
}

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

void TabControl::ImplScrollBtnsColor()
{
    if ( mpTabCtrlData && mpTabCtrlData->mpLeftBtn )
    {
        mpTabCtrlData->mpLeftBtn->SetControlForeground();
        mpTabCtrlData->mpRightBtn->SetControlForeground();
    }
}

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

void TabControl::ImplSetScrollBtnsState()
{
    if ( mbScroll )
    {
        mpTabCtrlData->mpLeftBtn->Enable( mnFirstPagePos != 0 );
        mpTabCtrlData->mpRightBtn->Enable( mnFirstPagePos < mnLastFirstPagePos );
    }
}

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

void TabControl::ImplPosScrollBtns()
{
    if ( mbScroll )
    {
        if ( !mpTabCtrlData->mpLeftBtn )
        {
            mpTabCtrlData->mpLeftBtn = new PushButton( this, WB_RECTSTYLE | WB_SMALLSTYLE | WB_NOPOINTERFOCUS | WB_REPEAT );
            mpTabCtrlData->mpLeftBtn->SetSymbol( SYMBOL_PREV );
            mpTabCtrlData->mpLeftBtn->SetClickHdl( LINK( this, TabControl, ImplScrollBtnHdl ) );
        }
        if ( !mpTabCtrlData->mpRightBtn )
        {
            mpTabCtrlData->mpRightBtn = new PushButton( this, WB_RECTSTYLE | WB_SMALLSTYLE | WB_NOPOINTERFOCUS | WB_REPEAT );
            mpTabCtrlData->mpRightBtn->SetSymbol( SYMBOL_NEXT );
            mpTabCtrlData->mpRightBtn->SetClickHdl( LINK( this, TabControl, ImplScrollBtnHdl ) );
        }

        Rectangle aRect = ImplGetTabRect( TAB_PAGERECT );
        aRect.Left()   -= TAB_OFFSET;
        aRect.Top()    -= TAB_OFFSET;
        aRect.Right()  += TAB_OFFSET;
        aRect.Bottom() += TAB_OFFSET;
        long nX = aRect.Right()-mnBtnSize+1;
        long nY = aRect.Top()-mnBtnSize;
        mpTabCtrlData->mpRightBtn->SetPosSizePixel( nX, nY, mnBtnSize, mnBtnSize );
        nX -= mnBtnSize;
        mpTabCtrlData->mpLeftBtn->SetPosSizePixel( nX, nY, mnBtnSize, mnBtnSize );
        ImplScrollBtnsColor();
        ImplSetScrollBtnsState();
        mpTabCtrlData->mpLeftBtn->Show();
        mpTabCtrlData->mpRightBtn->Show();
    }
    else
    {
        if ( mpTabCtrlData )
        {
            if ( mpTabCtrlData->mpLeftBtn )
                mpTabCtrlData->mpLeftBtn->Hide();
            if ( mpTabCtrlData->mpRightBtn )
                mpTabCtrlData->mpRightBtn->Hide();
        }
    }
}

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

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();

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

    aSize.Width()  += TAB_TABOFFSET_X*2;
    aSize.Height() += TAB_TABOFFSET_Y*2;
    // 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();

    // Evt. den Text kuerzen
    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( USHORT nItemPos, long nWidth, long nHeight )
{
    Size aWinSize = Control::GetOutputSizePixel();
    if ( nWidth == -1 )
        nWidth = aWinSize.Width();
    if ( nHeight == -1 )
        nHeight = aWinSize.Height();

    if ( mpTabCtrlData->maItemList.empty() )
    {
        return Rectangle( Point( TAB_OFFSET, TAB_OFFSET ),
                          Size( nWidth-TAB_OFFSET*2, nHeight-TAB_OFFSET*2 ) );
    }

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

        Rectangle aRect = ImplGetTabRect( nLastPos, nWidth, nHeight );
        aRect = Rectangle( Point( TAB_OFFSET, aRect.Bottom()+TAB_OFFSET ),
                           Size( nWidth-TAB_OFFSET*2,
                                 nHeight-aRect.Bottom()-TAB_OFFSET*2 ) );
        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( TRUE );
        aFont.SetWeight( (!ImplGetSVData()->maNWFData.mbNoBoldTabFocus) ? WEIGHT_BOLD : WEIGHT_LIGHT );
        aLightFont.SetTransparent( TRUE );
        aLightFont.SetWeight( WEIGHT_LIGHT );

        // If Bold and none Bold strings have the same width, we
        // add in the calcultion extra space, so that the tabs
        // looks better. The 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;
        USHORT          nPos = 0;

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

        mbScroll = FALSE;

        USHORT          nLines = 0;
        USHORT          nCurLine = 0;
        long            nLineWidthAry[100];
        USHORT          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 = FALSE;
            it->maRect = aNewRect;
            it->mnLine = nLines;
            it->mbFullVisible = 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;
            USHORT  i;
            USHORT  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] = (USHORT)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        = FALSE;

        ImplPosScrollBtns();
    }

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

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

void TabControl::ImplChangeTabPage( USHORT nId, USHORT 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() )
    {
        USHORT 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( 0 );
        if ( mbRestoreUnqId )
            pCtrlParent->SetUniqueId( 0 );
        if( mbRestoreSmartId )
            pCtrlParent->SetSmartHelpId( SmartId() );
        pOldPage->DeactivatePage();
    }

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

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

        pPage->ActivatePage();

        if ( pOldPage && pOldPage->HasChildPathFocus() )
        {
            USHORT  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 );
}

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

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 TRUE;
    }

    return FALSE;
}

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

void TabControl::ImplActivateTabPage( BOOL bNext )
{
    USHORT nCurPos = GetPagePos( GetCurPageId() );

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

    SelectTabPage( GetPageId( nCurPos ) );
}

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

void TabControl::ImplSetFirstPagePos( USHORT )
{
    return; // was only required for single line
}

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

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

    // make sure the focussed 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 );

    USHORT                   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 );
    USHORT                   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();
    BOOL                    bLeftBorder = TRUE;
    BOOL                    bRightBorder = TRUE;
    USHORT                  nOff;
    BOOL                    bNativeOK = FALSE;

    USHORT nOff2 = 0;
    USHORT nOff3 = 0;

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

    // Wenn wir die aktuelle Page sind, muessen 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 = FALSE;
            aRightTestPos.X() += 2;
            if ( rCurRect.IsInside( aRightTestPos ) )
                bRightBorder = FALSE;
        }
        else
        {
            if ( rCurRect.IsInside( aLeftTestPos ) )
                nLeftBottom -= 2;
            if ( rCurRect.IsInside( aRightTestPos ) )
                nRightBottom -= 2;
        }
    }

    if( !bLayout && (bNativeOK = IsNativeControlSupported(CTRL_TAB_ITEM, PART_ENTIRE_CONTROL)) == TRUE )
    {
        ImplControlValue        aControlValue;
        Region              aCtrlRegion( pItem->maRect );
        ControlState        nState = 0;

        if( pItem->mnId == mnCurPageId )
        {
            nState |= CTRL_STATE_SELECTED;
            // only the selected item can be focussed
            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;
        aControlValue.setOptionalVal( (void *)(&tiValue) );

        bNativeOK = DrawNativeControl( CTRL_TAB_ITEM, PART_ENTIRE_CONTROL, aCtrlRegion, nState,
                    aControlValue, 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( 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() )
    {
        USHORT 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();
        USHORT          nKeyCode = aKeyCode.GetCode();

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

    return nRet;
}


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

IMPL_LINK( TabControl, ImplScrollBtnHdl, PushButton*, EMPTYARG )
{
    ImplSetScrollBtnsState();
    return 0;
}

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

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 it's 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() )
        {
            USHORT nPageId = GetPageId( rMEvt.GetPosPixel() );
            ImplTabItem* pItem = ImplGetItem( nPageId );
            if( pItem && pItem->mbEnabled )
                SelectTabPage( nPageId );
            else
                Sound::Beep( SOUND_ERROR, this );
        }
    }
}

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

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

        if ( (nKeyCode == KEY_LEFT) || (nKeyCode == KEY_RIGHT) )
        {
            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 (eg impress)
    BOOL bNoTabPage = FALSE;
    TabPage*        pCurPage = (pCurItem) ? pCurItem->mpTabPage : NULL;
    if( !pCurPage || !pCurPage->IsVisible() )
    {
        bNoTabPage = TRUE;
        aRect.Left()-=10;
        aRect.Right()+=10;
    }

    BOOL bNativeOK = FALSE;
    if( ! bLayout && (bNativeOK = IsNativeControlSupported( CTRL_TAB_PANE, PART_ENTIRE_CONTROL) ) == TRUE )
    {
        const ImplControlValue aControlValue( BUTTONVALUE_DONTKNOW, rtl::OUString(), 0 );

        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 );

        Region aCtrlRegion( aRect );
        Rectangle aClipRect( aClipRgn.GetBoundRect() );
        if( !aClipRgn.IsEmpty() ) //&& aClipRect.getHeight() && aClipRect.getWidth() )
            bNativeOK = DrawNativeControl( CTRL_TAB_PANE, part, aCtrlRegion, 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 tookits 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), 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), TRUE );
        }
    }

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

    if( ! bLayout )
        mbSmallInvalidate = 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 = TRUE;

    // Aktuelle TabPage resizen/positionieren
    BOOL bTabPage = ImplPosCurTabPage();
    // Feststellen, was invalidiert werden muss
    Size aNewSize = Control::GetOutputSizePixel();
    long nNewWidth = aNewSize.Width();
    if ( mbScroll )
        mbSmallInvalidate = FALSE;
    else
    {
        for( std::vector< ImplTabItem >::iterator it = mpTabCtrlData->maItemList.begin();
             it != mpTabCtrlData->maItemList.end(); ++it )
        {
            if ( !it->mbFullVisible ||
                 (it->maRect.Right()-2 >= nNewWidth) )
            {
                mbSmallInvalidate = 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 )
{
    USHORT 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 )
        {
            ULONG nHelpId = GetHelpId( nItemId );
            if ( nHelpId )
            {
                // Wenn eine Hilfe existiert, dann ausloesen
                Help* pHelp = Application::GetHelp();
                if ( pHelp )
                    pHelp->Start( nHelpId, 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;
        BOOL    bMenu;
        if ( rCEvt.IsMouseEvent() )
        {
            aMenuPos = rCEvt.GetMousePosPixel();
            bMenu = GetPageId( aMenuPos ) != 0;
        }
        else
        {
            aMenuPos = ImplGetTabRect( GetPagePos( mnCurPageId ) ).Center();
            bMenu = 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->mnHelpId );
            }

            USHORT 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( TRUE, FALSE, FALSE );
        Invalidate();
    }
    else if ( nType == STATE_CHANGE_CONTROLFOREGROUND )
    {
        ImplInitSettings( FALSE, TRUE, FALSE );
        Invalidate();
    }
    else if ( nType == STATE_CHANGE_CONTROLBACKGROUND )
    {
        ImplInitSettings( FALSE, FALSE, 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( TRUE, TRUE, 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 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, USHORT nPos )
{
    GetRes( rResId.SetRT( RSC_TABCONTROLITEM ) );

    ULONG nObjMask = ReadLongRes();
    USHORT nItemId  = 1;

    // ID
    if ( nObjMask & RSC_TABCONTROLITEM_ID )
        nItemId = sal::static_int_cast<USHORT>(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<USHORT>(ReadLongRes());
    }
}

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

void TabControl::InsertPage( USHORT nPageId, const XubString& rText,
                             USHORT 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->mnHelpId         = 0;
    pItem->maText           = rText;
    pItem->mbFullVisible    = FALSE;

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

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

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

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

void TabControl::RemovePage( USHORT nPageId )
{
    USHORT 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 = TRUE;
        if ( IsUpdateMode() )
            Invalidate();

        ImplFreeLayoutData();

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

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

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

    ImplFreeLayoutData();

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

    ImplCallEventListeners( VCLEVENT_TABPAGE_REMOVEDALL );
}

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

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

    if ( pItem && pItem->mbEnabled != i_bEnable )
    {
        pItem->mbEnabled = i_bEnable;
        mbFormat = 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();
    }
}

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

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

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

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

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

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

    return TAB_PAGE_NOTFOUND;
}

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

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

    return 0;
}

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

void TabControl::SetCurPageId( USHORT nPageId )
{
    USHORT 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 = TRUE;
            USHORT nOldId = mnCurPageId;
            mnCurPageId = nPageId;
            ImplChangeTabPage( nPageId, nOldId );
        }
    }
}

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

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

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

void TabControl::SetFirstPageId( USHORT )
{
    return; // was only required for single line
}

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

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

        ImplCallEventListeners( VCLEVENT_TABPAGE_DEACTIVATE, (void*) (ULONG) mnCurPageId );
        if ( DeactivatePage() )
        {
            mnActPageId = nPageId;
            ActivatePage();
            // Page koennte 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*) (ULONG) nPageId );
        }
    }
}

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

void TabControl::SetTabPage( USHORT 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( USHORT nPageId ) const
{
    ImplTabItem* pItem = ImplGetItem( nPageId );

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

void TabControl::SetHelpId( USHORT nPageId, ULONG nHelpId )
{
    ImplTabItem* pItem = ImplGetItem( nPageId );

    if ( pItem )
        pItem->mnHelpId = nHelpId;
}

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

ULONG TabControl::GetHelpId( USHORT nPageId ) const
{
    ImplTabItem* pItem = ImplGetItem( nPageId );

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

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

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

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

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

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

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

Rectangle TabControl::GetCharacterBounds( USHORT 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, USHORT& 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 = (USHORT)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( USHORT 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( USHORT 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;
}

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