/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * 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.
 *
 ************************************************************************/


#include <svtools/tabbar.hxx>
#include <tools/time.hxx>
#include <tools/debug.hxx>
#include <tools/poly.hxx>
#include <vcl/svapp.hxx>
#include <vcl/help.hxx>
#include <vcl/decoview.hxx>
#include <vcl/button.hxx>
#include <vcl/edit.hxx>
#include <vcl/image.hxx>
#include "svtaccessiblefactory.hxx"
#include <filectrl.hrc>
#include <svtools/svtresid.hxx>
#include <svtools/svtools.hrc>
#include <limits>

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

#define TABBAR_OFFSET_X         7
#define TABBAR_OFFSET_X2        2
#define TABBAR_DRAG_SCROLLOFF   5
#define TABBAR_MINSIZE          5

const sal_uInt16 ADDNEWPAGE_AREAWIDTH = 10;

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

struct ImplTabBarItem
{
    sal_uInt16          mnId;
    TabBarPageBits  mnBits;
    XubString       maText;
    XubString       maHelpText;
    Rectangle       maRect;
    long            mnWidth;
    rtl::OString    maHelpId;
    sal_Bool            mbShort;
    sal_Bool            mbSelect;
    sal_Bool            mbEnable;
    Color           maTabBgColor;
    Color           maTabTextColor;

                    ImplTabBarItem( sal_uInt16 nItemId, const XubString& rText,
                                    TabBarPageBits nPageBits ) :
                        maText( rText )
                    {
                        mnId     = nItemId;
                        mnBits   = nPageBits;
                        mnWidth  = 0;
                        mbShort  = sal_False;
                        mbSelect = sal_False;
                        mbEnable = sal_True;
                        maTabBgColor = Color( COL_AUTO );
                        maTabTextColor = Color( COL_AUTO );
                    }

    bool IsDefaultTabBgColor() const
    {
        return maTabBgColor == Color(COL_AUTO);
    }

    bool IsDefaultTabTextColor() const
    {
        return maTabTextColor == Color(COL_AUTO);
    }

    bool IsSelected(ImplTabBarItem* pCurItem) const
    {
        return mbSelect || (pCurItem == this);
    }
};

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

// -----------------
// - ImplTabButton -
// -----------------

class ImplTabButton : public PushButton
{
public:
                    ImplTabButton( TabBar* pParent, WinBits nWinStyle = 0 ) :
                        PushButton( pParent, nWinStyle | WB_RECTSTYLE | WB_SMALLSTYLE | WB_NOLIGHTBORDER | WB_NOPOINTERFOCUS  ) {}

    TabBar*         GetParent() const { return (TabBar*)Window::GetParent(); }

    virtual long    PreNotify( NotifyEvent& rNEvt );
};

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

long ImplTabButton::PreNotify( NotifyEvent& rNEvt )
{
    if ( rNEvt.GetType() == EVENT_MOUSEBUTTONDOWN )
    {
        if ( GetParent()->IsInEditMode() )
        {
            GetParent()->EndEditMode();
            return sal_True;
        }
    }

    return PushButton::PreNotify( rNEvt );
}

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

// ----------------
// - ImplTabSizer -
// ----------------

class ImplTabSizer : public Window
{
public:
                    ImplTabSizer( TabBar* pParent, WinBits nWinStyle = 0 );

    TabBar*         GetParent() const { return (TabBar*)Window::GetParent(); }

private:
    void            ImplTrack( const Point& rScreenPos );

    virtual void    MouseButtonDown( const MouseEvent& rMEvt );
    virtual void    Tracking( const TrackingEvent& rTEvt );
    virtual void    Paint( const Rectangle& rRect );

    Point           maStartPos;
    long            mnStartWidth;
};

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

ImplTabSizer::ImplTabSizer( TabBar* pParent, WinBits nWinStyle )
    : Window( pParent, nWinStyle & WB_3DLOOK )
    , mnStartWidth(0)
{
    SetPointer( Pointer( POINTER_HSIZEBAR ) );
    SetSizePixel( Size( 7, 0 ) );
}

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

void ImplTabSizer::ImplTrack( const Point& rScreenPos )
{
    TabBar* pParent = GetParent();
    long nDiff = rScreenPos.X() - maStartPos.X();
    pParent->mnSplitSize = mnStartWidth + (pParent->IsMirrored() ? -nDiff : nDiff);
    if ( pParent->mnSplitSize < TABBAR_MINSIZE )
        pParent->mnSplitSize = TABBAR_MINSIZE;
    pParent->Split();
    pParent->Update();
}

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

void ImplTabSizer::MouseButtonDown( const MouseEvent& rMEvt )
{
    if ( GetParent()->IsInEditMode() )
    {
        GetParent()->EndEditMode();
        return;
    }

    if ( rMEvt.IsLeft() )
    {
        maStartPos = OutputToScreenPixel( rMEvt.GetPosPixel() );
        mnStartWidth = GetParent()->GetSizePixel().Width();
        StartTracking();
    }
}

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

void ImplTabSizer::Tracking( const TrackingEvent& rTEvt )
{
    if ( rTEvt.IsTrackingEnded() )
    {
        if ( rTEvt.IsTrackingCanceled() )
            ImplTrack( maStartPos );
        GetParent()->mnSplitSize = 0;
    }
    else
        ImplTrack( OutputToScreenPixel( rTEvt.GetMouseEvent().GetPosPixel() ) );
}

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

void ImplTabSizer::Paint( const Rectangle& )
{
    const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
    DecorationView  aDecoView( this );
    long            nOffX = 0;
    Size            aOutputSize = GetOutputSizePixel();

    if ( !(rStyleSettings.GetOptions() & STYLE_OPTION_MONO) )
    {
        SetLineColor( rStyleSettings.GetDarkShadowColor() );
        DrawLine( Point( 0, 0 ), Point( 0, aOutputSize.Height()-1 ) );
        nOffX++;
        aOutputSize.Width()--;
    }
    aDecoView.DrawButton( Rectangle( Point( nOffX, 0 ), aOutputSize ), BUTTON_DRAW_NOLIGHTBORDER );
}

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

// Heisst nicht Impl, da evtl. mal von aussen benutz- und ueberladbar

// --------------
// - TabBarEdit -
// --------------

class TabBarEdit : public Edit
{
private:
    Timer           maLoseFocusTimer;
    sal_Bool            mbPostEvt;

                    DECL_LINK( ImplEndEditHdl, void* );
                    DECL_LINK( ImplEndTimerHdl, void* );

public:
                    TabBarEdit( TabBar* pParent, WinBits nWinStyle = 0 );

    TabBar*         GetParent() const { return (TabBar*)Window::GetParent(); }

    void            SetPostEvent() { mbPostEvt = sal_True; }
    void            ResetPostEvent() { mbPostEvt = sal_False; }

    virtual long    PreNotify( NotifyEvent& rNEvt );
    virtual void    LoseFocus();
};

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

TabBarEdit::TabBarEdit( TabBar* pParent, WinBits nWinStyle ) :
    Edit( pParent, nWinStyle )
{
    mbPostEvt = sal_False;
}

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

long TabBarEdit::PreNotify( NotifyEvent& rNEvt )
{
    if ( rNEvt.GetType() == EVENT_KEYINPUT )
    {
        const KeyEvent* pKEvt = rNEvt.GetKeyEvent();
        if ( !pKEvt->GetKeyCode().GetModifier() )
        {
            if ( pKEvt->GetKeyCode().GetCode() == KEY_RETURN )
            {
                if ( !mbPostEvt )
                {
                    if ( PostUserEvent( LINK( this, TabBarEdit, ImplEndEditHdl ), (void*)sal_False ) )
                        mbPostEvt = sal_True;
                }
                return sal_True;
            }
            else if ( pKEvt->GetKeyCode().GetCode() == KEY_ESCAPE )
            {
                if ( !mbPostEvt )
                {
                    if ( PostUserEvent( LINK( this, TabBarEdit, ImplEndEditHdl ), (void*)sal_True ) )
                        mbPostEvt = sal_True;
                }
                return sal_True;
            }
        }
    }

    return Edit::PreNotify( rNEvt );
}

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

void TabBarEdit::LoseFocus()
{
    if ( !mbPostEvt )
    {
        if ( PostUserEvent( LINK( this, TabBarEdit, ImplEndEditHdl ), (void*)sal_False ) )
            mbPostEvt = sal_True;
    }

    Edit::LoseFocus();
}

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

IMPL_LINK( TabBarEdit, ImplEndEditHdl, void*, pCancel )
{
    ResetPostEvent();
    maLoseFocusTimer.Stop();

    // We need this query, because the edit get a losefous event,
    // when it shows the context menu or the insert symbol dialog
    if ( !HasFocus() && HasChildPathFocus( sal_True ) )
    {
        maLoseFocusTimer.SetTimeout( 30 );
        maLoseFocusTimer.SetTimeoutHdl( LINK( this, TabBarEdit, ImplEndTimerHdl ) );
        maLoseFocusTimer.Start();
    }
    else
        GetParent()->EndEditMode( pCancel != 0 );

    return 0;
}

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

IMPL_LINK_NOARG(TabBarEdit, ImplEndTimerHdl)
{
    if ( HasFocus() )
        return 0;

    // We need this query, because the edit get a losefous event,
    // when it shows the context menu or the insert symbol dialog
    if ( HasChildPathFocus( sal_True ) )
        maLoseFocusTimer.Start();
    else
        GetParent()->EndEditMode( sal_True );

    return 0;
}

// =======================================================================
struct TabBar_Impl
{
    ImplTabSizer*                   mpSizer;
    ::svt::AccessibleFactoryAccess  maAccessibleFactory;

    TabBar_Impl()
        :mpSizer( NULL )
    {
    }
    ~TabBar_Impl()
    {
        delete mpSizer;
    }
};

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

const sal_uInt16 TabBar::APPEND         = ::std::numeric_limits<sal_uInt16>::max();
const sal_uInt16 TabBar::PAGE_NOT_FOUND = ::std::numeric_limits<sal_uInt16>::max();
const sal_uInt16 TabBar::INSERT_TAB_POS = ::std::numeric_limits<sal_uInt16>::max() - 1;

void TabBar::ImplInit( WinBits nWinStyle )
{
    mpItemList      = new ImplTabBarList;
    mpFirstBtn      = NULL;
    mpPrevBtn       = NULL;
    mpNextBtn       = NULL;
    mpLastBtn       = NULL;
    mpImpl          = new TabBar_Impl;
    mpEdit          = NULL;
    mnMaxPageWidth  = 0;
    mnCurMaxWidth   = 0;
    mnOffX          = 0;
    mnOffY          = 0;
    mnLastOffX      = 0;
    mnSplitSize     = 0;
    mnSwitchTime    = 0;
    mnWinStyle      = nWinStyle;
    mnCurPageId     = 0;
    mnFirstPos      = 0;
    mnDropPos       = 0;
    mnSwitchId      = 0;
    mnEditId        = 0;
    mbFormat        = sal_True;
    mbFirstFormat   = sal_True;
    mbSizeFormat    = sal_True;
    mbAutoMaxWidth  = sal_True;
    mbInSwitching   = sal_False;
    mbAutoEditMode  = sal_False;
    mbEditCanceled  = sal_False;
    mbDropPos       = sal_False;
    mbInSelect      = sal_False;
    mbSelColor      = sal_False;
    mbSelTextColor  = sal_False;
    mbMirrored      = sal_False;

    if ( nWinStyle & WB_3DTAB )
        mnOffY++;

    ImplInitControls();

    if(mpFirstBtn)
        mpFirstBtn->SetAccessibleName(SVT_RESSTR(STR_TABBAR_PUSHBUTTON_MOVET0HOME));
    if(mpPrevBtn)
        mpPrevBtn->SetAccessibleName(SVT_RESSTR(STR_TABBAR_PUSHBUTTON_MOVELEFT));
    if(mpNextBtn)
        mpNextBtn->SetAccessibleName(SVT_RESSTR(STR_TABBAR_PUSHBUTTON_MOVERIGHT));
    if(mpLastBtn)
        mpLastBtn->SetAccessibleName(SVT_RESSTR(STR_TABBAR_PUSHBUTTON_MOVETOEND));

    SetSizePixel( Size( 100, CalcWindowSizePixel().Height() ) );
    ImplInitSettings( sal_True, sal_True );
}

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

TabBar::TabBar( Window* pParent, WinBits nWinStyle ) :
    Window( pParent, (nWinStyle & WB_3DLOOK) | WB_CLIPCHILDREN )
{
    ImplInit( nWinStyle );
    maCurrentItemList = 0;
}

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

TabBar::~TabBar()
{
    EndEditMode( sal_True );

    // Controls loeschen
    if ( mpPrevBtn )
        delete mpPrevBtn;
    if ( mpNextBtn )
        delete mpNextBtn;
    if ( mpFirstBtn )
        delete mpFirstBtn;
    if ( mpLastBtn )
        delete mpLastBtn;
    delete mpImpl;

    for ( size_t i = 0, n = mpItemList->size(); i < n; ++i ) {
        delete (*mpItemList)[ i ];
    }
    delete mpItemList;
}

// -----------------------------------------------------------------------
ImplTabBarItem* TabBar::seek( size_t i )
{
    if ( i < mpItemList->size() )
    {
        maCurrentItemList = i;
        return (*mpItemList)[ maCurrentItemList ];
    }
    return NULL;
}

ImplTabBarItem* TabBar::prev()
{
    if ( maCurrentItemList > 0 ) {
        return (*mpItemList)[ --maCurrentItemList ];
    }
    return NULL;
}

ImplTabBarItem* TabBar::next()
{
    if ( maCurrentItemList+1 < mpItemList->size() ) {
        return (*mpItemList)[ ++maCurrentItemList ];
    }
    return NULL;
}

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

void TabBar::ImplInitSettings( sal_Bool bFont, sal_Bool bBackground )
{
    const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();

    if ( bFont )
    {
        Font aToolFont;
        aToolFont = rStyleSettings.GetToolFont();
        if ( IsControlFont() )
            aToolFont.Merge( GetControlFont() );
        aToolFont.SetWeight( WEIGHT_BOLD );
        SetZoomedPointFont( aToolFont );

        // Font in der groesse Anpassen, wenn Fenster zu klein?
        while ( GetTextHeight() > (GetOutputSizePixel().Height()-1) )
        {
            Font aFont = GetFont();
            if ( aFont.GetHeight() <= 6 )
                break;
            aFont.SetHeight( aFont.GetHeight()-1 );
            SetFont( aFont );
        }
    }

    if ( bBackground )
    {
        Color aColor;
        if ( IsControlBackground() )
            aColor = GetControlBackground();
        else
            aColor = rStyleSettings.GetFaceColor();
        SetBackground( aColor );
    }
}

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

void TabBar::ImplGetColors( Color& rFaceColor, Color& rFaceTextColor,
                            Color& rSelectColor, Color& rSelectTextColor )
{
    const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();

    if ( IsControlBackground() )
        rFaceColor = GetControlBackground();
    else
        rFaceColor = rStyleSettings.GetInactiveTabColor();
    if ( IsControlForeground() )
        rFaceTextColor = GetControlForeground();
    else
        rFaceTextColor = rStyleSettings.GetButtonTextColor();
    if ( mbSelColor )
        rSelectColor = maSelColor;
    else
        rSelectColor = rStyleSettings.GetActiveTabColor();
    if ( mbSelTextColor )
        rSelectTextColor = maSelTextColor;
    else
        rSelectTextColor = rStyleSettings.GetWindowTextColor();

    // Bei 3D-Tabs wird Selektions- und Face-Farbe umgedreht, da die
    // selektierten Tabs in 3D erscheinen sollen
    if ( mnWinStyle & WB_3DTAB )
    {
        Color aTempColor = rFaceColor;
        rFaceColor = rSelectColor;
        rSelectColor = aTempColor;
        aTempColor = rFaceTextColor;
        rFaceTextColor = rSelectTextColor;
        rSelectTextColor = rFaceTextColor;
    }
}

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

sal_Bool TabBar::ImplCalcWidth()
{
    // Groessen muessen nur ermittelt werden, wenn sich Text aendert oder
    // wenn der Font geaendert wurde
    if ( !mbSizeFormat )
        return sal_False;

    // Breiten der Tabs mit dem fetten Font ermitteln
    Font aFont = GetFont();
    if ( aFont.GetWeight() != WEIGHT_BOLD )
    {
        aFont.SetWeight( WEIGHT_BOLD );
        SetFont( aFont );
    }

    if ( mnMaxPageWidth )
        mnCurMaxWidth = mnMaxPageWidth;
    else if ( mbAutoMaxWidth )
    {
        mnCurMaxWidth = mnLastOffX-mnOffX-
                        TABBAR_OFFSET_X-TABBAR_OFFSET_X-
                        TABBAR_OFFSET_X2-TABBAR_OFFSET_X2-TABBAR_OFFSET_X2;
        if ( mnCurMaxWidth < 1 )
            mnCurMaxWidth = 1;
    }
    else
        mnCurMaxWidth = 0;

    sal_Bool            bChanged = sal_False;
    for ( size_t i = 0, n = mpItemList->size(); i < n; ++i )
    {
        ImplTabBarItem* pItem = (*mpItemList)[ i ];
        long nNewWidth = GetTextWidth( pItem->maText );
        if ( mnCurMaxWidth && (nNewWidth > mnCurMaxWidth) )
        {
            pItem->mbShort = sal_True;
            nNewWidth = mnCurMaxWidth;
        }
        else
            pItem->mbShort = sal_False;
        nNewWidth += TABBAR_OFFSET_X+TABBAR_OFFSET_X2;
        if ( pItem->mnWidth != nNewWidth )
        {
            pItem->mnWidth = nNewWidth;
            if ( !pItem->maRect.IsEmpty() )
                bChanged = sal_True;
        }
    }
    mbSizeFormat = sal_False;
    mbFormat = sal_True;
    return bChanged;
}

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

void TabBar::ImplFormat()
{
    ImplCalcWidth();

    if ( !mbFormat )
        return;

    sal_uInt16 n = 0;
    long x = mnOffX;
    for ( size_t i = 0, nL = mpItemList->size(); i < nL; ++i )
    {
        ImplTabBarItem* pItem = (*mpItemList)[ i ];
        // Bei allen nicht sichtbaren Tabs, wird ein leeres Rechteck
        // gesetzt
        if ( (n+1 < mnFirstPos) || (x > mnLastOffX) )
            pItem->maRect.SetEmpty();
        else
        {
            // Etwas von der Tab vor der ersten sichtbaren Page
            // muss auch zu sehen sein
            if ( n+1 == mnFirstPos )
                pItem->maRect.Left() = x-pItem->mnWidth;
            else
            {
                pItem->maRect.Left() = x;
                x += pItem->mnWidth;
            }
            pItem->maRect.Right() = x+TABBAR_OFFSET_X+TABBAR_OFFSET_X2;
            pItem->maRect.Bottom() = maWinSize.Height()-1;

            if( mbMirrored )
            {
                long nTmp = mnOffX + mnLastOffX - pItem->maRect.Right();
                pItem->maRect.Right() = mnOffX + mnLastOffX - pItem->maRect.Left();
                pItem->maRect.Left() = nTmp;
            }
        }

        n++;
    }

    mbFormat = sal_False;

    // Button enablen/disablen
    ImplEnableControls();
}

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

sal_uInt16 TabBar::ImplGetLastFirstPos()
{
    sal_uInt16 nCount = (sal_uInt16)(mpItemList->size());
    if ( !nCount || mbSizeFormat || mbFormat )
        return 0;

    sal_uInt16  nLastFirstPos = nCount-1;
    long    nWinWidth = mnLastOffX-mnOffX-TABBAR_OFFSET_X-ADDNEWPAGE_AREAWIDTH;
    long    nWidth = (*mpItemList)[ nLastFirstPos ]->mnWidth;
    while ( nLastFirstPos && (nWidth < nWinWidth) )
    {
        nLastFirstPos--;
        nWidth += (*mpItemList)[ nLastFirstPos ]->mnWidth;
    }
    if ( (nLastFirstPos != (sal_uInt16)(mpItemList->size()-1)) &&
         (nWidth > nWinWidth) )
        nLastFirstPos++;
    return nLastFirstPos;
}

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

void TabBar::ImplInitControls()
{
    if ( mnWinStyle & WB_SIZEABLE )
    {
        if ( !mpImpl->mpSizer )
            mpImpl->mpSizer = new ImplTabSizer( this, mnWinStyle & (WB_DRAG | WB_3DLOOK) );
        mpImpl->mpSizer->Show();
    }
    else
    {
        DELETEZ( mpImpl->mpSizer );
    }

    Link aLink = LINK( this, TabBar, ImplClickHdl );

    if ( mnWinStyle & (WB_MINSCROLL | WB_SCROLL) )
    {
        if ( !mpPrevBtn )
        {
            mpPrevBtn = new ImplTabButton( this, WB_REPEAT );
            mpPrevBtn->SetClickHdl( aLink );
        }
        mpPrevBtn->SetSymbol( mbMirrored ? SYMBOL_NEXT : SYMBOL_PREV );
        mpPrevBtn->Show();

        if ( !mpNextBtn )
        {
            mpNextBtn = new ImplTabButton( this, WB_REPEAT );
            mpNextBtn->SetClickHdl( aLink );
        }
        mpNextBtn->SetSymbol( mbMirrored ? SYMBOL_PREV : SYMBOL_NEXT );
        mpNextBtn->Show();
    }
    else
    {
        DELETEZ( mpPrevBtn );
        DELETEZ( mpNextBtn );
    }

    if ( mnWinStyle & WB_SCROLL )
    {
        if ( !mpFirstBtn )
        {
            mpFirstBtn = new ImplTabButton( this );
            mpFirstBtn->SetClickHdl( aLink );
        }
        mpFirstBtn->SetSymbol( mbMirrored ? SYMBOL_LAST : SYMBOL_FIRST );
        mpFirstBtn->Show();

        if ( !mpLastBtn )
        {
            mpLastBtn = new ImplTabButton( this );
            mpLastBtn->SetClickHdl( aLink );
        }
        mpLastBtn->SetSymbol( mbMirrored ? SYMBOL_FIRST : SYMBOL_LAST );
        mpLastBtn->Show();
    }
    else
    {
        DELETEZ( mpFirstBtn );
        DELETEZ( mpLastBtn );
    }

    mbHasInsertTab  = (mnWinStyle & WB_INSERTTAB);
}

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

void TabBar::ImplEnableControls()
{
    if ( mbSizeFormat || mbFormat )
        return;

    // Buttons enablen/disblen
    sal_Bool bEnableBtn = mnFirstPos > 0;
    if ( mpFirstBtn )
        mpFirstBtn->Enable( bEnableBtn );
    if ( mpPrevBtn )
        mpPrevBtn->Enable( bEnableBtn );

    bEnableBtn = mnFirstPos < ImplGetLastFirstPos();
    if ( mpNextBtn )
        mpNextBtn->Enable( bEnableBtn );
    if ( mpLastBtn )
        mpLastBtn->Enable( bEnableBtn );
}

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

void TabBar::ImplShowPage( sal_uInt16 nPos )
{
    // Breite berechnen
    long nWidth = GetOutputSizePixel().Width();
    if ( nWidth >= TABBAR_OFFSET_X )
        nWidth -= TABBAR_OFFSET_X;
    ImplTabBarItem* pItem = (*mpItemList)[ nPos ];
    if ( nPos < mnFirstPos )
        SetFirstPageId( pItem->mnId );
    else if ( pItem->maRect.Right() > nWidth )
    {
        while ( pItem->maRect.Right() > nWidth )
        {
            sal_uInt16 nNewPos = mnFirstPos+1;
            SetFirstPageId( GetPageId( nNewPos ) );
            ImplFormat();
            if ( nNewPos != mnFirstPos )
                break;
        }
    }
}

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

IMPL_LINK( TabBar, ImplClickHdl, ImplTabButton*, pBtn )
{
    EndEditMode();

    sal_uInt16 nNewPos = mnFirstPos;

    if ( pBtn == mpFirstBtn )
        nNewPos = 0;
    else if ( pBtn == mpPrevBtn )
    {
        if ( mnFirstPos )
            nNewPos = mnFirstPos-1;
    }
    else if ( pBtn == mpNextBtn )
    {
        sal_uInt16 nCount = GetPageCount();
        if ( mnFirstPos <  nCount )
            nNewPos = mnFirstPos+1;
    }
    else
    {
        sal_uInt16 nCount = GetPageCount();
        if ( nCount )
            nNewPos = nCount-1;
    }

    if ( nNewPos != mnFirstPos )
        SetFirstPageId( GetPageId( nNewPos ) );
    return 0;
}

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

void TabBar::MouseMove( const MouseEvent& rMEvt )
{
    if ( rMEvt.IsLeaveWindow() )
        mbInSelect = sal_False;

    Window::MouseMove( rMEvt );
}

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

void TabBar::MouseButtonDown( const MouseEvent& rMEvt )
{
    // Bei Klick in unser Fenster EditModus nur beenden und Klick nicht
    // ausfuehren
    if ( IsInEditMode() )
    {
        EndEditMode();
        return;
    }

    ImplTabBarItem* pItem;
    sal_uInt16          nSelId = GetPageId( rMEvt.GetPosPixel() );

    if ( !rMEvt.IsLeft() )
    {
        Window::MouseButtonDown( rMEvt );
        if ( (nSelId > 0) && (nSelId != mnCurPageId) )
        {
            sal_uInt16 nPos = GetPagePos( nSelId );
            pItem = (*mpItemList)[ nPos ];

            if ( pItem->mbEnable )
            {
                if ( ImplDeactivatePage() )
                {
                    SetCurPageId( nSelId );
                    Update();
                    ImplActivatePage();
                    ImplSelect();
                }
                mbInSelect = sal_True;
            }
        }
        return;
    }

    if ( rMEvt.IsMod2() && mbAutoEditMode && nSelId )
    {
        if ( StartEditMode( nSelId ) )
            return;
    }

    if ( (rMEvt.GetMode() & (MOUSE_MULTISELECT | MOUSE_RANGESELECT)) && (rMEvt.GetClicks() == 1) )
    {
        if ( nSelId )
        {
            sal_uInt16      nPos = GetPagePos( nSelId );
            sal_Bool        bSelectTab = sal_False;
            pItem = (*mpItemList)[ nPos ];

            if ( pItem->mbEnable )
            {
                if ( (rMEvt.GetMode() & MOUSE_MULTISELECT) && (mnWinStyle & WB_MULTISELECT) )
                {
                    if ( nSelId != mnCurPageId )
                    {
                        SelectPage( nSelId, !IsPageSelected( nSelId ) );
                        bSelectTab = sal_True;
                    }
                }
                else if ( mnWinStyle & (WB_MULTISELECT | WB_RANGESELECT) )
                {
                    bSelectTab = sal_True;
                    sal_uInt16 n;
                    sal_Bool   bSelect;
                    sal_uInt16 nCurPos = GetPagePos( mnCurPageId );
                    if ( nPos <= nCurPos )
                    {
                        // Alle Tabs bis zur angeklickten Tab deselektieren
                        // und alle Tabs von der angeklickten Tab bis
                        // zur aktuellen Position selektieren
                        n = 0;
                        while ( n < nCurPos )
                        {
                            pItem = (*mpItemList)[ n ];
                            if ( n < nPos )
                                bSelect = sal_False;
                            else
                                bSelect = sal_True;

                            if ( pItem->mbSelect != bSelect )
                            {
                                pItem->mbSelect = bSelect;
                                if ( !pItem->maRect.IsEmpty() )
                                    Invalidate( pItem->maRect );
                            }

                            n++;
                        }
                    }

                    if ( nPos >= nCurPos )
                    {
                        // Alle Tabs von der aktuellen bis zur angeklickten
                        // Tab selektieren und alle Tabs von der angeklickten
                        // Tab bis zur letzten Tab deselektieren
                        sal_uInt16 nCount = (sal_uInt16)mpItemList->size();
                        n = nCurPos;
                        while ( n < nCount )
                        {
                            pItem = (*mpItemList)[ n ];

                            if ( n <= nPos )
                                bSelect = sal_True;
                            else
                                bSelect = sal_False;

                            if ( pItem->mbSelect != bSelect )
                            {
                                pItem->mbSelect = bSelect;
                                if ( !pItem->maRect.IsEmpty() )
                                    Invalidate( pItem->maRect );
                            }

                            n++;
                        }
                    }
                }

                // Gegebenenfalls muss die selektierte Tab gescrollt werden
                if ( bSelectTab )
                {
                    ImplShowPage( nPos );
                    Update();
                    ImplSelect();
                }
            }
            else
                ImplShowPage( nPos );
            mbInSelect = sal_True;

            return;
        }
    }
    else if ( rMEvt.GetClicks() == 2 )
    {
        // Gegebenenfalls den Double-Click-Handler rufen
        if ( !rMEvt.GetModifier() && (!nSelId || (nSelId == mnCurPageId)) )
        {
            sal_uInt16 nOldCurId = mnCurPageId;
            mnCurPageId = nSelId;
            DoubleClick();
            // Abfrage, da im DoubleClick-Handler die aktuelle Seite
            // umgeschaltet werden konnte
            if ( mnCurPageId == nSelId )
                mnCurPageId = nOldCurId;
        }

        return;
    }
    else
    {
        if ( nSelId )
        {
            // Nur Select ausfuehren, wenn noch nicht aktuelle Page
            if ( nSelId != mnCurPageId )
            {
                sal_uInt16 nPos = GetPagePos( nSelId );
                pItem = (*mpItemList)[ nPos ];

                if ( pItem->mbEnable )
                {
                    if ( !pItem->mbSelect )
                    {
                        // Muss invalidiert werden
                        sal_Bool bUpdate = sal_False;
                        if ( IsReallyVisible() && IsUpdateMode() )
                            bUpdate = sal_True;

                        // Alle selektierten Items deselektieren
                        for ( size_t i = 0, n = mpItemList->size(); i < n; ++i )
                        {
                            pItem = (*mpItemList)[ i ];
                            if ( pItem->mbSelect || (pItem->mnId == mnCurPageId) )
                            {
                                pItem->mbSelect = sal_False;
                                if ( bUpdate )
                                    Invalidate( pItem->maRect );
                            }
                        }
                    }

                    if ( ImplDeactivatePage() )
                    {
                        SetCurPageId( nSelId );
                        Update();
                        ImplActivatePage();
                        ImplSelect();
                    }
                }
                else
                    ImplShowPage( nPos );
                mbInSelect = sal_True;
            }

            return;
        }
    }

    Window::MouseButtonDown( rMEvt );
}

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

void TabBar::MouseButtonUp( const MouseEvent& rMEvt )
{
    mbInSelect = sal_False;
    Window::MouseButtonUp( rMEvt );
}


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

namespace {

class TabBarPaintGuard
{
public:
    explicit TabBarPaintGuard(TabBar& rParent) :
        mrParent(rParent),
        maFont(rParent.GetFont())
    {
        // #i36013# exclude push buttons from painting area
        mrParent.SetClipRegion( Region(mrParent.GetPageArea()) );
    }

    ~TabBarPaintGuard()
    {
        // Restore original font.
        mrParent.SetFont(maFont);
        // remove clip region
        mrParent.SetClipRegion();
    }
private:
    TabBar& mrParent;
    Font    maFont;
};

class TabDrawer
{
public:

    explicit TabDrawer(TabBar& rParent) :
        mrParent(rParent),
        mpStyleSettings(&mrParent.GetSettings().GetStyleSettings()),
        maPoly(4),
        mbSelected(false),
        mbCustomColored(false),
        mbSpecialTab(false),
        mbEnabled(false)
    {
    }

    void drawOutputAreaBorder()
    {
        WinBits nWinStyle = mrParent.GetStyle();

        // Bei Border oben und unten einen Strich extra malen
        if ( (nWinStyle & WB_BORDER) || (nWinStyle & WB_TOPBORDER) )
        {
            Size aOutputSize = mrParent.GetOutputSizePixel();
            Rectangle aOutRect = mrParent.GetPageArea();

            // Bei 3D-Tabs wird auch der Border in 3D gemalt
            if ( nWinStyle & WB_3DTAB )
            {
                mrParent.SetLineColor( mpStyleSettings->GetShadowColor() );
                mrParent.DrawLine( Point( aOutRect.Left(), 0 ), Point( aOutputSize.Width(), 0 ) );
            }

            // Border malen (Strich oben und Strich unten)
            mrParent.SetLineColor( mpStyleSettings->GetDarkShadowColor() );
            mrParent.DrawLine( aOutRect.TopLeft(), Point( aOutputSize.Width()-1, aOutRect.Top() ) );
        }
    }

    void drawOuterFrame()
    {
        mrParent.DrawPolygon(maPoly);
    }

    void drawLeftShadow()
    {
        Point p1 = maPoly[0], p2 = maPoly[1];
        p1.X()++;
        p2.X()++;
        p2.Y()--;
        mrParent.DrawLine(p1, p2);
    }

    void drawRightShadow()
    {
        Point p1 = maPoly[2];
        Point p2 = maPoly[3];
        p1.X()--;
        p2.X()--;
        mrParent.DrawLine(p1, p2);
    }

    void drawTopInnerShadow()
    {
        Point p1 = maPoly[0], p2 = maPoly[3];
        p1.Y()++;
        p2.Y()++;
        mrParent.DrawLine(p1, p2);
    }

    void drawBottomShadow(bool bColored)
    {
        Point p1 = maPoly[1], p2 = maPoly[2];
        p1.X() += 1;
        p1.Y() -= 1;
        p2.X() -= 1;
        p2.Y() -= 1;
        mrParent.DrawLine(p1, p2);
        if (bColored)
        {
            p1 += Point(-1, -1);
            p2 += Point(1, -1);
            mrParent.DrawLine(p1, p2);
        }
    }

    void drawText(const String& aText)
    {
        Rectangle aRect = maRect;
        long nTextWidth = mrParent.GetTextWidth(aText);
        long nTextHeight = mrParent.GetTextHeight();
        Point aPos = aRect.TopLeft();
        aPos.X() += (aRect.getWidth()  - nTextWidth) / 2;
        aPos.Y() += (aRect.getHeight() - nTextHeight) / 2;

        if (mbEnabled)
            mrParent.DrawText(aPos, aText);
        else
            mrParent.DrawCtrlText(
                aPos, aText, 0, STRING_LEN, (TEXT_DRAW_DISABLE | TEXT_DRAW_MNEMONIC));
    }

    void drawOverTopBorder(bool b3DTab)
    {
        Point p1 = maPoly[0], p2 = maPoly[3];
        p1.X() += 1;
        p2.X() -= 1;
        Rectangle aDelRect(p1, p2);
        mrParent.DrawRect(aDelRect);
        if (b3DTab)
        {
            aDelRect.Top()--;
            mrParent.DrawRect(aDelRect);
        }
    }

    void drawTab()
    {
        mrParent.SetLineColor(mpStyleSettings->GetDarkShadowColor());

        // Je nach Status die richtige FillInBrush setzen
        // Set the correct FillInBrush depending upon status
        if ( mbSelected )
        {
            // Currently selected Tab
            mrParent.SetFillColor( maSelectedColor );
        }
        else if ( mbCustomColored )
        {
            mrParent.SetFillColor( maCustomColor );
        }
        else
        {
            mrParent.SetFillColor( maUnselectedColor );
        }

        drawOuterFrame();

        // If this is the current tab, draw the left inner shadow the default color,
        // otherwise make it the same as the custom background color
        Color aColor = mpStyleSettings->GetLightColor();
        if (mbCustomColored && !mbSelected)
            aColor = maCustomColor;

        mrParent.SetLineColor(aColor);
        drawLeftShadow();

        if ( !mbSelected )
            drawTopInnerShadow();

        mrParent.SetLineColor( mpStyleSettings->GetShadowColor() );
        drawRightShadow();
        if ( mbCustomColored && mbSelected )
        {
            mrParent.SetLineColor(maCustomColor);
            drawBottomShadow(true);
        }
        else
            drawBottomShadow(false);

        // Draw the outer frame once more.  In some environments, the outer frame
        // gets overpainted.
        mrParent.SetLineColor( mpStyleSettings->GetDarkShadowColor() );
        mrParent.SetFillColor();
        drawOuterFrame();
    }

    void drawPlusImage()
    {
        SvtResId id( BMP_LIST_ADD );
        Image aPlusImg( id );
        // Center the image within the bounding rectangle.
        Size aSize = aPlusImg.GetSizePixel();
        Point pt = maRect.TopLeft();
        long nXOffSet = (maRect.GetWidth() - aSize.Width()) / 2;
        long nYOffset = (maRect.GetHeight() - aSize.Height()) / 2;
        pt += Point(nXOffSet, nYOffset);
        pt.X() += 1;
        mrParent.DrawImage(pt, aPlusImg);
    }

    void setRect(const Rectangle& rRect)
    {
        maRect = rRect;

        long nOffY = mrParent.GetPageArea().getY();

        // Zuerst geben wir das Polygon gefuellt aus
        maPoly[0] = Point( rRect.Left(), nOffY );
        maPoly[1] = Point( rRect.Left()+TABBAR_OFFSET_X, rRect.Bottom() );
        maPoly[2] = Point( rRect.Right()-TABBAR_OFFSET_X, rRect.Bottom() );
        maPoly[3] = Point( rRect.Right(), nOffY );
    }

    void setSelected(bool b)
    {
        mbSelected = b;
    }

    void setCustomColored(bool b)
    {
        mbCustomColored = b;
    }

    void setSpecialTab(bool b)
    {
        mbSpecialTab = b;
    }

    void setEnabled(bool b)
    {
        mbEnabled = b;
    }

    void setSelectedFillColor(const Color& rColor)
    {
        maSelectedColor = rColor;
    }

    void setUnselectedFillColor(const Color& rColor)
    {
        maUnselectedColor = rColor;
    }

    void setCustomColor(const Color& rColor)
    {
        maCustomColor = rColor;
    }

private:
    TabBar&         mrParent;
    const StyleSettings*  mpStyleSettings;

    Rectangle       maRect;
    Polygon         maPoly;

    Color       maSelectedColor;
    Color       maCustomColor;
    Color       maUnselectedColor;

    bool        mbSelected:1;
    bool        mbCustomColored:1;
    bool        mbSpecialTab:1;
    bool        mbEnabled:1;
};

}

void TabBar::Paint( const Rectangle& )
{
    // Items berechnen und ausgeben
    sal_uInt16 nItemCount = (sal_uInt16)mpItemList->size();
    if (!nItemCount)
        return;

    ImplPrePaint();

    Color aFaceColor, aSelectColor, aFaceTextColor, aSelectTextColor;
    ImplGetColors( aFaceColor, aFaceTextColor, aSelectColor, aSelectTextColor );

    // Font selektieren
    Font aFont = GetFont();
    Font aLightFont = aFont;
    aLightFont.SetWeight( WEIGHT_NORMAL );

    TabBarPaintGuard aGuard(*this);
    TabDrawer aDrawer(*this);
    aDrawer.setSelectedFillColor(aSelectColor);
    aDrawer.setUnselectedFillColor(aFaceColor);
    aDrawer.drawOutputAreaBorder();

    // Now, start drawing the tabs.

    ImplTabBarItem* pItem = ImplGetLastTabBarItem(nItemCount);

    if (pItem && mbHasInsertTab)
    {
        // Draw the insert tab at the right end.
        Rectangle aRect = ImplGetInsertTabRect(pItem);
        aDrawer.setRect(aRect);
        aDrawer.drawTab();
        aDrawer.drawPlusImage();
    }

    const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
    ImplTabBarItem* pCurItem = NULL;
    while ( pItem )
    {
        // CurrentItem als letztes ausgeben, da es alle anderen ueberdeckt
        if ( !pCurItem && (pItem->mnId == mnCurPageId) )
        {
            pCurItem = pItem;
            pItem = prev();
            if ( !pItem )
                pItem = pCurItem;
            continue;
        }

        bool bCurrent = pItem == pCurItem;

        if ( !pItem->maRect.IsEmpty() )
        {
            Rectangle aRect = pItem->maRect;
            bool bSelected = pItem->IsSelected(pCurItem);
            // We disable custom background color in high contrast mode.
            bool bCustomBgColor = !pItem->IsDefaultTabBgColor() && !rStyleSettings.GetHighContrastMode();
            bool bSpecialTab = (pItem->mnBits & TPB_SPECIAL);
            bool bEnabled = pItem->mbEnable;
            String aText = pItem->mbShort ?
                GetEllipsisString(pItem->maText, mnCurMaxWidth, TEXT_DRAW_ENDELLIPSIS) : pItem->maText;

            aDrawer.setRect(aRect);
            aDrawer.setSelected(bSelected);
            aDrawer.setCustomColored(bCustomBgColor);
            aDrawer.setSpecialTab(bSpecialTab);
            aDrawer.setEnabled(bEnabled);
            aDrawer.setCustomColor(pItem->maTabBgColor);
            aDrawer.drawTab();

            // Aktuelle Page wird mit einem fetten Font ausgegeben
            if ( bCurrent )
                SetFont( aFont );
            else
                SetFont( aLightFont );

            // Je nach Status die richtige FillInBrush setzen
            // Set the correct FillInBrush depending upon status
            if ( bSelected )
                SetTextColor( aSelectTextColor );
            else if ( bCustomBgColor )
                SetTextColor( pItem->maTabTextColor );
            else
                SetTextColor( aFaceTextColor );

            // This tab is "special", and a special tab needs a blue text.
            if (bSpecialTab)
                SetTextColor(Color(COL_LIGHTBLUE));

            aDrawer.drawText(aText);

            if ( bCurrent )
            {
                SetLineColor();
                SetFillColor(aSelectColor);
                aDrawer.drawOverTopBorder(mnWinStyle & WB_3DTAB);
                return;
            }

            pItem = prev();
        }
        else
        {
            if ( bCurrent )
                return;

            pItem = NULL;
        }

        if ( !pItem )
            pItem = pCurItem;
    }
}

// -----------------------------------------------------------------------
void TabBar::Resize()
{
    Size aNewSize = GetOutputSizePixel();

    long nSizerWidth = 0;
    long nButtonWidth = 0;

    // Sizer anordnen
    if ( mpImpl->mpSizer )
    {
        Size    aSizerSize = mpImpl->mpSizer->GetSizePixel();
        Point   aNewSizerPos( mbMirrored ? 0 : (aNewSize.Width()-aSizerSize.Width()), 0 );
        Size    aNewSizerSize( aSizerSize.Width(), aNewSize.Height() );
        mpImpl->mpSizer->SetPosSizePixel( aNewSizerPos, aNewSizerSize );
        nSizerWidth = aSizerSize.Width();
    }

    // Scroll-Buttons anordnen
    long nHeight = aNewSize.Height();
    // Font in der groesse Anpassen?
    ImplInitSettings( sal_True, sal_False );

    long nX = mbMirrored ? (aNewSize.Width()-nHeight) : 0;
    long nXDiff = mbMirrored ? -nHeight : nHeight;

    Size aBtnSize( nHeight, nHeight );
    if ( mpFirstBtn )
    {
        mpFirstBtn->SetPosSizePixel( Point( nX, 0 ), aBtnSize );
        nX += nXDiff;
        nButtonWidth += nHeight;
    }
    if ( mpPrevBtn )
    {
        mpPrevBtn->SetPosSizePixel( Point( nX, 0 ), aBtnSize );
        nX += nXDiff;
        nButtonWidth += nHeight;
    }
    if ( mpNextBtn )
    {
        mpNextBtn->SetPosSizePixel( Point( nX, 0 ), aBtnSize );
        nX += nXDiff;
        nButtonWidth += nHeight;
    }
    if ( mpLastBtn )
    {
        mpLastBtn->SetPosSizePixel( Point( nX, 0 ), aBtnSize );
        nX += nXDiff;
        nButtonWidth += nHeight;
    }

    // Groesse merken
    maWinSize = aNewSize;

    if( mbMirrored )
    {
        mnOffX = nSizerWidth;
        mnLastOffX = maWinSize.Width() - nButtonWidth - 1;
    }
    else
    {
        mnOffX = nButtonWidth;
        mnLastOffX = maWinSize.Width() - nSizerWidth - 1;
    }

    // Neu formatieren
    mbSizeFormat = sal_True;
    if ( IsReallyVisible() )
    {
        if ( ImplCalcWidth() )
            Invalidate();
        ImplFormat();
    }

    // Button enablen/disablen
    ImplEnableControls();
}

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

void TabBar::RequestHelp( const HelpEvent& rHEvt )
{
    sal_uInt16 nItemId = GetPageId( ScreenToOutputPixel( rHEvt.GetMousePosPixel() ) );
    if ( nItemId )
    {
        if ( rHEvt.GetMode() & HELPMODE_BALLOON )
        {
            XubString aStr = GetHelpText( nItemId );
            if ( aStr.Len() )
            {
                Rectangle aItemRect = GetPageRect( 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.isEmpty() )
            {
                // Wenn eine Hilfe existiert, dann ausloesen
                Help* pHelp = Application::GetHelp();
                if ( pHelp )
                    pHelp->Start( aHelpId, this );
                return;
            }
        }

        // Bei Quick- oder Ballloon-Help zeigen wir den Text an,
        // wenn dieser abgeschnitten oder nicht voll sichtbar ist
        if ( rHEvt.GetMode() & (HELPMODE_QUICK | HELPMODE_BALLOON) )
        {
            sal_uInt16 nPos = GetPagePos( nItemId );
            ImplTabBarItem* pItem = (*mpItemList)[ nPos ];
            if ( pItem->mbShort ||
                (pItem->maRect.Right()-TABBAR_OFFSET_X-5 > mnLastOffX) )
            {
                Rectangle aItemRect = GetPageRect( 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();
                XubString aStr = (*mpItemList)[ nPos ]->maText;
                if ( aStr.Len() )
                {
                    if ( rHEvt.GetMode() & HELPMODE_BALLOON )
                        Help::ShowBalloon( this, aItemRect.Center(), aItemRect, aStr );
                    else
                        Help::ShowQuickHelp( this, aItemRect, aStr );
                    return;
                }
            }
        }
    }

    Window::RequestHelp( rHEvt );
}

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

void TabBar::StateChanged( StateChangedType nType )
{
    Window::StateChanged( nType );

    if ( nType == STATE_CHANGE_INITSHOW )
    {
        if ( (mbSizeFormat || mbFormat) && !mpItemList->empty() )
            ImplFormat();
    }
    else if ( (nType == STATE_CHANGE_ZOOM) ||
              (nType == STATE_CHANGE_CONTROLFONT) )
    {
        ImplInitSettings( sal_True, sal_False );
        Invalidate();
    }
    else if ( nType == STATE_CHANGE_CONTROLFOREGROUND )
        Invalidate();
    else if ( nType == STATE_CHANGE_CONTROLBACKGROUND )
    {
        ImplInitSettings( sal_False, sal_True );
        Invalidate();
    }
    else if ( nType == STATE_CHANGE_MIRRORING )
    {
        // reacts on calls of EnableRTL, have to mirror all child controls
        if( mpFirstBtn ) mpFirstBtn->EnableRTL( IsRTLEnabled() );
        if( mpPrevBtn ) mpPrevBtn->EnableRTL( IsRTLEnabled() );
        if( mpNextBtn ) mpNextBtn->EnableRTL( IsRTLEnabled() );
        if( mpLastBtn ) mpLastBtn->EnableRTL( IsRTLEnabled() );
        if( mpImpl->mpSizer ) mpImpl->mpSizer->EnableRTL( IsRTLEnabled() );
        if( mpEdit ) mpEdit->EnableRTL( IsRTLEnabled() );
    }
}

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

void TabBar::DataChanged( const DataChangedEvent& rDCEvt )
{
    Window::DataChanged( rDCEvt );

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

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

void TabBar::ImplSelect()
{
    Select();

    CallEventListeners( VCLEVENT_TABBAR_PAGESELECTED, reinterpret_cast<void*>(sal::static_int_cast<sal_IntPtr>(mnCurPageId)) );
}

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

void TabBar::Select()
{
    maSelectHdl.Call( this );
}

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

void TabBar::DoubleClick()
{
    maDoubleClickHdl.Call( this );
}

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

void TabBar::Split()
{
    maSplitHdl.Call( this );
}

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

void TabBar::ImplActivatePage()
{
    ActivatePage();

    CallEventListeners( VCLEVENT_TABBAR_PAGEACTIVATED, reinterpret_cast<void*>(sal::static_int_cast<sal_IntPtr>(mnCurPageId)) );
}

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

void TabBar::ActivatePage()
{
    maActivatePageHdl.Call( this );
}

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

long TabBar::ImplDeactivatePage()
{
    long nRet = DeactivatePage();

    CallEventListeners( VCLEVENT_TABBAR_PAGEDEACTIVATED, reinterpret_cast<void*>(sal::static_int_cast<sal_IntPtr>(mnCurPageId)) );

    return nRet;
}

void TabBar::ImplPrePaint()
{
    sal_uInt16 nItemCount = (sal_uInt16)mpItemList->size();
    if (!nItemCount)
        return;

    ImplTabBarItem* pItem;

    // TabBar muss formatiert sein
    ImplFormat();

    // Beim ersten Format auch dafuer sorgen, das aktuelle TabPage
    // sichtbar wird
    if ( mbFirstFormat )
    {
        mbFirstFormat = sal_False;

        if ( mnCurPageId && (mnFirstPos == 0) && !mbDropPos )
        {
            pItem = (*mpItemList)[ GetPagePos( mnCurPageId ) ];
            if ( pItem->maRect.IsEmpty() )
            {
                // mbDropPos setzen (bzw. misbrauchen) um Invalidate()
                // zu unterbinden
                mbDropPos = sal_True;
                SetFirstPageId( mnCurPageId );
                mbDropPos = sal_False;
                if ( mnFirstPos != 0 )
                    ImplFormat();
            }
        }
    }
}

ImplTabBarItem* TabBar::ImplGetLastTabBarItem( sal_uInt16 nItemCount )
{
    // letzten sichtbaren Eintrag suchen
    sal_uInt16 n = mnFirstPos+1;
    if ( n >= nItemCount )
        n = nItemCount-1;
    ImplTabBarItem* pItem = seek( n );
    while ( pItem )
    {
        if ( !pItem->maRect.IsEmpty() )
        {
            n++;
            pItem = next();
        }
        else
            break;
    }

    // Alle Tabs ausgeben (von hinten nach vorn und aktuellen zuletzt)
    if ( pItem )
        n--;
    else if ( n >= nItemCount )
        n = nItemCount-1;
    pItem = seek( n );
    return pItem;
}

Rectangle TabBar::ImplGetInsertTabRect(ImplTabBarItem* pItem) const
{
    if (mbHasInsertTab && pItem)
    {
        Rectangle aInsTabRect = pItem->maRect;
        if ( !mbMirrored )
            aInsTabRect.setX(
                aInsTabRect.getX() + aInsTabRect.getWidth() - TABBAR_OFFSET_X - TABBAR_OFFSET_X2);
        else
            aInsTabRect.setX(
                aInsTabRect.getX() - 3*TABBAR_OFFSET_X - TABBAR_OFFSET_X2);
        aInsTabRect.setWidth(32);
        return aInsTabRect;
    }
    return Rectangle();
}

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

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

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

long TabBar::StartRenaming()
{
    if ( maStartRenamingHdl.IsSet() )
        return maStartRenamingHdl.Call( this );
    else
        return sal_True;
}

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

long TabBar::AllowRenaming()
{
    if ( maAllowRenamingHdl.IsSet() )
        return maAllowRenamingHdl.Call( this );
    else
        return sal_True;
}

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

void TabBar::EndRenaming()
{
    maEndRenamingHdl.Call( this );
}

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

void TabBar::Mirror()
{

}

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

void TabBar::InsertPage( sal_uInt16 nPageId, const XubString& rText,
                         TabBarPageBits nBits, sal_uInt16 nPos )
{
    DBG_ASSERT( nPageId, "TabBar::InsertPage(): PageId == 0" );
    DBG_ASSERT( GetPagePos( nPageId ) == PAGE_NOT_FOUND,
                "TabBar::InsertPage(): PageId already exists" );
    DBG_ASSERT( nBits <= TPB_SPECIAL, "TabBar::InsertPage(): nBits is wrong" );

    // PageItem anlegen und in die Item-Liste eintragen
    ImplTabBarItem* pItem = new ImplTabBarItem( nPageId, rText, nBits );
    if ( nPos < mpItemList->size() ) {
        ImplTabBarList::iterator it = mpItemList->begin();
        ::std::advance( it, nPos );
        mpItemList->insert( it, pItem );
    } else {
        mpItemList->push_back( pItem );
    }
    mbSizeFormat = sal_True;

    // CurPageId gegebenenfalls setzen
    if ( !mnCurPageId )
        mnCurPageId = nPageId;

    // Leiste neu ausgeben
    if ( IsReallyVisible() && IsUpdateMode() )
        Invalidate();

    CallEventListeners( VCLEVENT_TABBAR_PAGEINSERTED, reinterpret_cast<void*>(sal::static_int_cast<sal_IntPtr>(nPageId)) );
}

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

Color TabBar::GetTabBgColor( sal_uInt16 nPageId ) const
{
    sal_uInt16 nPos = GetPagePos( nPageId );

    if ( nPos != PAGE_NOT_FOUND )
        return (*mpItemList)[ nPos ]->maTabBgColor;
    else
        return Color( COL_AUTO );
}

void TabBar::SetTabBgColor( sal_uInt16 nPageId, const Color& aTabBgColor )
{
    sal_uInt16 nPos = GetPagePos( nPageId );
    ImplTabBarItem* pItem;
    if ( nPos != PAGE_NOT_FOUND )
    {
        pItem = (*mpItemList)[ nPos ];
        if ( aTabBgColor != Color( COL_AUTO )  )
        {
            pItem->maTabBgColor = aTabBgColor;
            if ( aTabBgColor.GetLuminance() <= 128 ) //Do not use aTabBgColor.IsDark(), because that threshold is way too low...
                pItem->maTabTextColor = Color( COL_WHITE );
            else
                pItem->maTabTextColor = Color( COL_BLACK );
        }
        else
        {
            pItem->maTabBgColor = Color( COL_AUTO );
            pItem->maTabTextColor = Color( COL_AUTO );
        }
    }
}

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

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

    // Existiert Item
    if ( nPos != PAGE_NOT_FOUND )
    {
        if ( mnCurPageId == nPageId )
            mnCurPageId = 0;

        // Testen, ob erste sichtbare Seite verschoben werden muss
        if ( mnFirstPos > nPos )
            mnFirstPos--;

        // Item-Daten loeschen
        ImplTabBarList::iterator it = mpItemList->begin();
        ::std::advance( it, nPos );
        delete *it;
        mpItemList->erase( it );

        // Leiste neu ausgeben
        if ( IsReallyVisible() && IsUpdateMode() )
            Invalidate();

        CallEventListeners( VCLEVENT_TABBAR_PAGEREMOVED, reinterpret_cast<void*>(sal::static_int_cast<sal_IntPtr>(nPageId)) );
    }
}

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

void TabBar::MovePage( sal_uInt16 nPageId, sal_uInt16 nNewPos )
{
    sal_uInt16 nPos = GetPagePos( nPageId );
    Pair aPair( nPos, nNewPos );

    if ( nPos < nNewPos )
        nNewPos--;

    if ( nPos == nNewPos )
        return;

    // Existiert Item
    if ( nPos != PAGE_NOT_FOUND )
    {
        // TabBar-Item in der Liste verschieben
        ImplTabBarList::iterator it = mpItemList->begin();
        ::std::advance( it, nPos );
        ImplTabBarItem* pItem = *it;
        mpItemList->erase( it );
        if ( nNewPos < mpItemList->size() ) {
            it = mpItemList->begin();
            ::std::advance( it, nNewPos );
            mpItemList->insert( it, pItem );
        } else {
            mpItemList->push_back( pItem );
        }

        // Leiste neu ausgeben
        if ( IsReallyVisible() && IsUpdateMode() )
            Invalidate();

        CallEventListeners( VCLEVENT_TABBAR_PAGEMOVED, (void*) &aPair );
    }
}

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

void TabBar::Clear()
{
    // Alle Items loeschen
    for ( size_t i = 0, n = mpItemList->size(); i < n; ++i ) {
        delete (*mpItemList)[ i ];
    }
    mpItemList->clear();

    // Items aus der Liste loeschen
    mbSizeFormat = sal_True;
    mnCurPageId = 0;
    mnFirstPos = 0;
    maCurrentItemList = 0;

    // Leiste neu ausgeben
    if ( IsReallyVisible() && IsUpdateMode() )
        Invalidate();

    CallEventListeners( VCLEVENT_TABBAR_PAGEREMOVED, reinterpret_cast<void*>(sal::static_int_cast<sal_IntPtr>(PAGE_NOT_FOUND)) );
}

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

sal_Bool TabBar::IsPageEnabled( sal_uInt16 nPageId ) const
{
    sal_uInt16 nPos = GetPagePos( nPageId );

    if ( nPos != PAGE_NOT_FOUND )
        return (*mpItemList)[ nPos ]->mbEnable;
    else
        return sal_False;
}

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

void TabBar::SetPageBits( sal_uInt16 nPageId, TabBarPageBits nBits )
{
    sal_uInt16 nPos = GetPagePos( nPageId );

    if ( nPos != PAGE_NOT_FOUND )
    {
        ImplTabBarItem* pItem = (*mpItemList)[ nPos ];

        if ( pItem->mnBits != nBits )
        {
            pItem->mnBits = nBits;

            // Leiste neu ausgeben
            if ( IsReallyVisible() && IsUpdateMode() )
                Invalidate( pItem->maRect );
        }
    }
}

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

TabBarPageBits TabBar::GetPageBits( sal_uInt16 nPageId ) const
{
    sal_uInt16 nPos = GetPagePos( nPageId );

    if ( nPos != PAGE_NOT_FOUND )
        return (*mpItemList)[ nPos ]->mnBits;
    else
        return sal_False;
}

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

sal_uInt16 TabBar::GetPageCount() const
{
    return (sal_uInt16)mpItemList->size();
}

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

sal_uInt16 TabBar::GetPageId( sal_uInt16 nPos ) const
{
    return ( nPos < mpItemList->size() ) ? (*mpItemList)[ nPos ]->mnId : 0;
}

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

sal_uInt16 TabBar::GetPagePos( sal_uInt16 nPageId ) const
{
    for ( size_t i = 0, n = mpItemList->size(); i < n; ++i ) {
        if ( (*mpItemList)[ i ]->mnId == nPageId ) {
            return sal_uInt16( i );
        }
    }
    return PAGE_NOT_FOUND;
}

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

sal_uInt16 TabBar::GetPageId( const Point& rPos, bool bCheckInsTab ) const
{
    for ( size_t i = 0, n = mpItemList->size(); i < n; ++i )
    {
        ImplTabBarItem* pItem = (*mpItemList)[ i ];
        if ( pItem->maRect.IsInside( rPos ) )
            return pItem->mnId;
    }

    if (bCheckInsTab && mbHasInsertTab && !mpItemList->empty())
    {
        ImplTabBarItem* pItem = mpItemList->back();
        if (ImplGetInsertTabRect(pItem).IsInside(rPos))
            return INSERT_TAB_POS;
    }

    return 0;
}

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

Rectangle TabBar::GetPageRect( sal_uInt16 nPageId ) const
{
    sal_uInt16 nPos = GetPagePos( nPageId );

    if ( nPos != PAGE_NOT_FOUND )
        return (*mpItemList)[ nPos ]->maRect;
    else
        return Rectangle();
}

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

void TabBar::SetCurPageId( sal_uInt16 nPageId )
{
    sal_uInt16 nPos = GetPagePos( nPageId );

    // Wenn Item nicht existiert, dann nichts machen
    if ( nPos != PAGE_NOT_FOUND )
    {
        // Wenn sich aktuelle Page nicht geaendert hat, dann muessen wir
        // jetzt nichts mehr machen
        if ( nPageId == mnCurPageId )
            return;

        // Muss invalidiert werden
        sal_Bool bUpdate = sal_False;
        if ( IsReallyVisible() && IsUpdateMode() )
            bUpdate = sal_True;

        ImplTabBarItem* pItem = (*mpItemList)[ nPos ];
        ImplTabBarItem* pOldItem;

        if ( mnCurPageId )
            pOldItem = (*mpItemList)[ GetPagePos( mnCurPageId ) ];
        else
            pOldItem = NULL;

        // Wenn Page nicht selektiert, dann vorher selektierte Seite
        // deselktieren, wenn dies die einzige selektierte Seite ist
        if ( !pItem->mbSelect && pOldItem )
        {
            sal_uInt16 nSelPageCount = GetSelectPageCount();
            if ( nSelPageCount == 1 )
                pOldItem->mbSelect = sal_False;
            pItem->mbSelect = sal_True;
        }

        mnCurPageId = nPageId;
        mbFormat = sal_True;

        // Dafuer sorgen, das aktuelle Page sichtbar wird
        if ( IsReallyVisible() )
        {
            if ( nPos < mnFirstPos )
                SetFirstPageId( nPageId );
            else
            {
                // sichtbare Breite berechnen
                long nWidth = mnLastOffX;
                if ( nWidth > TABBAR_OFFSET_X )
                    nWidth -= TABBAR_OFFSET_X;
                if ( nWidth > ADDNEWPAGE_AREAWIDTH )
                    nWidth -= ADDNEWPAGE_AREAWIDTH;

                if ( pItem->maRect.IsEmpty() )
                    ImplFormat();

                while ( (mbMirrored ? (pItem->maRect.Left() < mnOffX) : (pItem->maRect.Right() > nWidth)) ||
                        pItem->maRect.IsEmpty() )
                {
                    sal_uInt16 nNewPos = mnFirstPos+1;
                    // Dafuer sorgen, das min. die aktuelle TabPages als
                    // erste TabPage sichtbar ist
                    if ( nNewPos >= nPos )
                    {
                        SetFirstPageId( nPageId );
                        break;
                    }
                    else
                        SetFirstPageId( GetPageId( nNewPos ) );
                    ImplFormat();
                    // Falls erste Seite nicht weitergeschaltet wird, dann
                    // koennen wir abbrechen
                    if ( nNewPos != mnFirstPos )
                        break;
                }
            }
        }

        // Leiste neu ausgeben
        if ( bUpdate )
        {
            Invalidate( pItem->maRect );
            if ( pOldItem )
                Invalidate( pOldItem->maRect );
        }
    }
}

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

void TabBar::MakeVisible( sal_uInt16 nPageId )
{
    if ( !IsReallyVisible() )
        return;

    sal_uInt16 nPos = GetPagePos( nPageId );

    // Wenn Item nicht existiert, dann nichts machen
    if ( nPos != PAGE_NOT_FOUND )
    {
        if ( nPos < mnFirstPos )
            SetFirstPageId( nPageId );
        else
        {
            ImplTabBarItem* pItem = (*mpItemList)[ nPos ];

            // sichtbare Breite berechnen
            long nWidth = mnLastOffX;
            if ( nWidth > TABBAR_OFFSET_X )
                nWidth -= TABBAR_OFFSET_X;

            if ( mbFormat || pItem->maRect.IsEmpty() )
            {
                mbFormat = sal_True;
                ImplFormat();
            }

            while ( (pItem->maRect.Right() > nWidth) ||
                    pItem->maRect.IsEmpty() )
            {
                sal_uInt16 nNewPos = mnFirstPos+1;
                // Dafuer sorgen, das min. die aktuelle TabPages als
                // erste TabPage sichtbar ist
                if ( nNewPos >= nPos )
                {
                    SetFirstPageId( nPageId );
                    break;
                }
                else
                    SetFirstPageId( GetPageId( nNewPos ) );
                ImplFormat();
                // Falls erste Seite nicht weitergeschaltet wird, dann
                // koennen wir abbrechen
                if ( nNewPos != mnFirstPos )
                    break;
            }
        }
    }
}

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

void TabBar::SetFirstPageId( sal_uInt16 nPageId )
{
    sal_uInt16 nPos = GetPagePos( nPageId );

    // Wenn Item nicht existiert, dann sal_False zurueckgeben
    if ( nPos != PAGE_NOT_FOUND )
    {
        if ( nPos != mnFirstPos )
        {
            // Dafuer sorgen, das nach Moeglichkteit soviele Pages wie
            // moeglich sichtbar sind
            ImplFormat();
            sal_uInt16 nLastFirstPos = ImplGetLastFirstPos();
            sal_uInt16 nNewPos;
            if ( nPos > nLastFirstPos )
                nNewPos = nLastFirstPos;
            else
                nNewPos = nPos;

            if ( nNewPos != mnFirstPos )
            {
                mnFirstPos = nNewPos;
                mbFormat = sal_True;

                // Leiste neu ausgeben (Achtung: mbDropPos beachten, da wenn
                // dieses Flag gesetzt ist, wird direkt gepaintet)
                if ( IsReallyVisible() && IsUpdateMode() && !mbDropPos )
                    Invalidate();
            }
        }
    }
}

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

void TabBar::SelectPage( sal_uInt16 nPageId, sal_Bool bSelect )
{
    sal_uInt16 nPos = GetPagePos( nPageId );

    if ( nPos != PAGE_NOT_FOUND )
    {
        ImplTabBarItem* pItem = (*mpItemList)[ nPos ];

        if ( pItem->mbSelect != bSelect )
        {
            pItem->mbSelect = bSelect;

            // Leiste neu ausgeben
            if ( IsReallyVisible() && IsUpdateMode() )
                Invalidate( pItem->maRect );
        }
    }
}

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

sal_uInt16 TabBar::GetSelectPageCount() const
{
    sal_uInt16          nSelected = 0;
    for ( size_t i = 0, n = mpItemList->size(); i < n; ++i )
    {
        ImplTabBarItem* pItem = (*mpItemList)[ i ];
        if ( pItem->mbSelect )
            nSelected++;
    }

    return nSelected;
}

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

sal_Bool TabBar::IsPageSelected( sal_uInt16 nPageId ) const
{
    sal_uInt16 nPos = GetPagePos( nPageId );
    if ( nPos != PAGE_NOT_FOUND )
        return (*mpItemList)[ nPos ]->mbSelect;
    else
        return sal_False;
}

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

sal_Bool TabBar::StartEditMode( sal_uInt16 nPageId )
{
    sal_uInt16 nPos = GetPagePos( nPageId );
    if ( mpEdit || (nPos == PAGE_NOT_FOUND) || (mnLastOffX < 8) )
        return sal_False;

    mnEditId = nPageId;
    if ( StartRenaming() )
    {
        ImplShowPage( nPos );
        ImplFormat();
        Update();

        mpEdit = new TabBarEdit( this, WB_CENTER );
        Rectangle aRect = GetPageRect( mnEditId );
        long nX = aRect.Left()+TABBAR_OFFSET_X+(TABBAR_OFFSET_X2/2);
        long nWidth = aRect.GetWidth()-(TABBAR_OFFSET_X*2)-TABBAR_OFFSET_X2;
        if ( mnEditId != GetCurPageId() )
            nX += 1;
        if ( nX+nWidth > mnLastOffX )
            nWidth = mnLastOffX-nX;
        if ( nWidth < 3 )
        {
            nX = aRect.Left();
            nWidth = aRect.GetWidth();
        }
        mpEdit->SetText( GetPageText( mnEditId ) );
        mpEdit->SetPosSizePixel( nX, aRect.Top()+mnOffY+1, nWidth, aRect.GetHeight()-3 );
        Font    aFont = GetPointFont();
        Color   aForegroundColor;
        Color   aBackgroundColor;
        Color   aFaceColor;
        Color   aSelectColor;
        Color   aFaceTextColor;
        Color   aSelectTextColor;
        ImplGetColors( aFaceColor, aFaceTextColor, aSelectColor, aSelectTextColor );
        if ( mnEditId != GetCurPageId() )
            aFont.SetWeight( WEIGHT_LIGHT );
        if ( IsPageSelected( mnEditId ) || (mnEditId == GetCurPageId()) )
        {
            aForegroundColor = aSelectTextColor;
            aBackgroundColor = aSelectColor;
        }
        else
        {
            aForegroundColor = aFaceTextColor;
            aBackgroundColor = aFaceColor;
        }
        if ( GetPageBits( mnEditId ) & TPB_SPECIAL )
            aForegroundColor = Color( COL_LIGHTBLUE );
        mpEdit->SetControlFont( aFont );
        mpEdit->SetControlForeground( aForegroundColor );
        mpEdit->SetControlBackground( aBackgroundColor );
        mpEdit->GrabFocus();
        mpEdit->SetSelection( Selection( 0, mpEdit->GetText().Len() ) );
        mpEdit->Show();
        return sal_True;
    }
    else
    {
        mnEditId = 0;
        return sal_False;
    }
}

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

void TabBar::EndEditMode( sal_Bool bCancel )
{
    if ( mpEdit )
    {
        // call hdl
        sal_Bool bEnd = sal_True;
        mbEditCanceled = bCancel;
        maEditText = mpEdit->GetText();
        mpEdit->SetPostEvent();
        if ( !bCancel )
        {
            long nAllowRenaming = AllowRenaming();
            if ( nAllowRenaming == TABBAR_RENAMING_YES )
                SetPageText( mnEditId, maEditText );
            else if ( nAllowRenaming == TABBAR_RENAMING_NO )
                bEnd = sal_False;
            else // nAllowRenaming == TABBAR_RENAMING_CANCEL
                mbEditCanceled = sal_True;
        }

        // renaming not allowed, than reset edit data
        if ( !bEnd )
        {
            mpEdit->ResetPostEvent();
            mpEdit->GrabFocus();
        }
        else
        {
            // close edit and call end hdl
            delete mpEdit;
            mpEdit = NULL;
            EndRenaming();
            mnEditId = 0;
        }

        // reset
        maEditText.Erase();
        mbEditCanceled = sal_False;
    }
}

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

void TabBar::SetMirrored(bool bMirrored)
{
    if( mbMirrored != bMirrored )
    {
        mbMirrored = bMirrored;
        mbSizeFormat = true;
        ImplInitControls();     // for button images
        Resize();               // recalculates control positions
        Mirror();
    }
}

void TabBar::SetEffectiveRTL( bool bRTL )
{
    SetMirrored( bRTL != Application::GetSettings().GetLayoutRTL() );
}

bool TabBar::IsEffectiveRTL() const
{
    return IsMirrored() != Application::GetSettings().GetLayoutRTL();
}

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

void TabBar::SetMaxPageWidth( long nMaxWidth )
{
    if ( mnMaxPageWidth != nMaxWidth )
    {
        mnMaxPageWidth = nMaxWidth;
        mbSizeFormat = sal_True;

        // Leiste neu ausgeben
        if ( IsReallyVisible() && IsUpdateMode() )
            Invalidate();
    }
}

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

void TabBar::SetPageText( sal_uInt16 nPageId, const XubString& rText )
{
    sal_uInt16 nPos = GetPagePos( nPageId );
    if ( nPos != PAGE_NOT_FOUND )
    {
        (*mpItemList)[ nPos ]->maText = rText;
        mbSizeFormat = sal_True;

        // Leiste neu ausgeben
        if ( IsReallyVisible() && IsUpdateMode() )
            Invalidate();

        CallEventListeners( VCLEVENT_TABBAR_PAGETEXTCHANGED, reinterpret_cast<void*>(sal::static_int_cast<sal_IntPtr>(nPageId)) );
    }
}

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

XubString TabBar::GetPageText( sal_uInt16 nPageId ) const
{
    sal_uInt16 nPos = GetPagePos( nPageId );
    if ( nPos != PAGE_NOT_FOUND )
        return (*mpItemList)[ nPos ]->maText;
    else
        return XubString();
}

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

XubString TabBar::GetHelpText( sal_uInt16 nPageId ) const
{
    sal_uInt16 nPos = GetPagePos( nPageId );
    if ( nPos != PAGE_NOT_FOUND )
    {
        ImplTabBarItem* pItem = (*mpItemList)[ nPos ];
        if ( !pItem->maHelpText.Len() && !pItem->maHelpId.isEmpty() )
        {
            Help* pHelp = Application::GetHelp();
            if ( pHelp )
                pItem->maHelpText = pHelp->GetHelpText( rtl::OStringToOUString( pItem->maHelpId, RTL_TEXTENCODING_UTF8 ), this );
        }

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

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

rtl::OString TabBar::GetHelpId( sal_uInt16 nPageId ) const
{
    sal_uInt16 nPos = GetPagePos( nPageId );
    rtl::OString aRet;
    if ( nPos != PAGE_NOT_FOUND )
        return (*mpItemList)[ nPos ]->maHelpId;
    return aRet;
}

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

sal_Bool TabBar::StartDrag( const CommandEvent& rCEvt, Region& rRegion )
{
    if ( !(mnWinStyle & WB_DRAG) || (rCEvt.GetCommand() != COMMAND_STARTDRAG) )
        return sal_False;

    // Testen, ob angeklickte Seite selektiert ist. Falls dies nicht
    // der Fall ist, setzen wir ihn als aktuellen Eintrag. Falls Drag and
    // Drop auch mal ueber Tastatur ausgeloest werden kann, testen wir
    // dies nur bei einer Mausaktion.
    // Ausserdem machen wir das nur, wenn kein Select() ausgeloest wurde,
    // da der Select schon den Bereich gescrollt haben kann
    if ( rCEvt.IsMouseEvent() && !mbInSelect )
    {
        sal_uInt16 nSelId = GetPageId( rCEvt.GetMousePosPixel() );

        // Falls kein Eintrag angeklickt wurde, starten wir kein Dragging
        if ( !nSelId )
            return sal_False;

        // Testen, ob Seite selektiertiert ist. Falls nicht, als aktuelle
        // Seite setzen und Select rufen.
        if ( !IsPageSelected( nSelId ) )
        {
            if ( ImplDeactivatePage() )
            {
                SetCurPageId( nSelId );
                Update();
                ImplActivatePage();
                ImplSelect();
            }
            else
                return sal_False;
        }
    }
    mbInSelect = sal_False;

    Region aRegion;

    // Region zuweisen
    rRegion = aRegion;

    return sal_True;
}

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

sal_uInt16 TabBar::ShowDropPos( const Point& rPos )
{
    ImplTabBarItem* pItem;
    sal_uInt16      nDropId;
    sal_uInt16      nNewDropPos;
    sal_uInt16      nItemCount = (sal_uInt16)mpItemList->size();
    short       nScroll = 0;

    if ( rPos.X() > mnLastOffX-TABBAR_DRAG_SCROLLOFF )
    {
        pItem = (*mpItemList)[ mpItemList->size()-1 ];
        if ( !pItem->maRect.IsEmpty() && (rPos.X() > pItem->maRect.Right()) )
            nNewDropPos = (sal_uInt16)mpItemList->size();
        else
        {
            nNewDropPos = mnFirstPos+1;
            nScroll = 1;
        }
    }
    else if ( (rPos.X() <= mnOffX) ||
              (!mnOffX && (rPos.X() <= TABBAR_DRAG_SCROLLOFF)) )
    {
        if ( mnFirstPos )
        {
            nNewDropPos = mnFirstPos;
            nScroll = -1;
        }
        else
            nNewDropPos = 0;
    }
    else
    {
        nDropId = GetPageId( rPos );
        if ( nDropId )
        {
            nNewDropPos = GetPagePos( nDropId );
            if ( mnFirstPos && (nNewDropPos == mnFirstPos-1) )
                nScroll = -1;
        }
        else
            nNewDropPos = nItemCount;
    }

    if ( mbDropPos && (nNewDropPos == mnDropPos) && !nScroll )
        return mnDropPos;

    if ( mbDropPos )
        HideDropPos();
    mbDropPos = sal_True;
    mnDropPos = nNewDropPos;

    if ( nScroll )
    {
        sal_uInt16 nOldFirstPos = mnFirstPos;
        SetFirstPageId( GetPageId( mnFirstPos+nScroll ) );

        // Direkt ausgeben, da kein Paint bei Drag and Drop moeglich
        if ( nOldFirstPos != mnFirstPos )
        {
            Rectangle aRect( mnOffX, 0, mnLastOffX, maWinSize.Height() );
            SetFillColor( GetBackground().GetColor() );
            DrawRect( aRect );
            Paint( aRect );
        }
    }

    // Drop-Position-Pfeile ausgeben
    Color       aBlackColor( COL_BLACK );
    long        nX;
    long        nY = (maWinSize.Height()/2)-1;
    sal_uInt16      nCurPos = GetPagePos( mnCurPageId );

    SetLineColor( aBlackColor );
    if ( mnDropPos < nItemCount )
    {
        pItem = (*mpItemList)[ mnDropPos ];
        nX = pItem->maRect.Left()+TABBAR_OFFSET_X;
        if ( mnDropPos == nCurPos )
            nX--;
        else
            nX++;
        if ( !pItem->IsDefaultTabBgColor() && !pItem->mbSelect)
            SetLineColor( pItem->maTabTextColor );
        DrawLine( Point( nX, nY ), Point( nX, nY ) );
        DrawLine( Point( nX+1, nY-1 ), Point( nX+1, nY+1 ) );
        DrawLine( Point( nX+2, nY-2 ), Point( nX+2, nY+2 ) );
        SetLineColor( aBlackColor );
    }
    if ( (mnDropPos > 0) && (mnDropPos < nItemCount+1) )
    {
        pItem = (*mpItemList)[ mnDropPos-1 ];
        nX = pItem->maRect.Right()-TABBAR_OFFSET_X;
        if ( mnDropPos == nCurPos )
            nX++;
        if ( !pItem->IsDefaultTabBgColor() && !pItem->mbSelect)
            SetLineColor( pItem->maTabTextColor );
        DrawLine( Point( nX, nY ), Point( nX, nY ) );
        DrawLine( Point( nX-1, nY-1 ), Point( nX-1, nY+1 ) );
        DrawLine( Point( nX-2, nY-2 ), Point( nX-2, nY+2 ) );
    }

    return mnDropPos;
}

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

void TabBar::HideDropPos()
{
    if ( mbDropPos )
    {
        ImplTabBarItem* pItem;
        long        nX;
        long        nY1 = (maWinSize.Height()/2)-3;
        long        nY2 = nY1 + 5;
        sal_uInt16      nItemCount = (sal_uInt16)mpItemList->size();

        if ( mnDropPos < nItemCount )
        {
            pItem = (*mpItemList)[ mnDropPos ];
            nX = pItem->maRect.Left()+TABBAR_OFFSET_X;
            // Paint direkt aufrufen, da bei Drag and Drop kein Paint
            // moeglich
            Rectangle aRect( nX-1, nY1, nX+3, nY2 );
            Region aRegion( aRect );
            SetClipRegion( aRegion );
            Paint( aRect );
            SetClipRegion();
        }
        if ( (mnDropPos > 0) && (mnDropPos < nItemCount+1) )
        {
            pItem = (*mpItemList)[ mnDropPos-1 ];
            nX = pItem->maRect.Right()-TABBAR_OFFSET_X;
            // Paint direkt aufrufen, da bei Drag and Drop kein Paint
            // moeglich
            Rectangle aRect( nX-2, nY1, nX+1, nY2 );
            Region aRegion( aRect );
            SetClipRegion( aRegion );
            Paint( aRect );
            SetClipRegion();
        }

        mbDropPos = sal_False;
        mnDropPos = 0;
    }
}

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

sal_Bool TabBar::SwitchPage( const Point& rPos )
{
    sal_Bool    bSwitch = sal_False;
    sal_uInt16  nSwitchId = GetPageId( rPos );
    if ( !nSwitchId )
        EndSwitchPage();
    else
    {
        if ( nSwitchId != mnSwitchId )
        {
            mnSwitchId = nSwitchId;
            mnSwitchTime = Time::GetSystemTicks();
        }
        else
        {
            // Erst nach 500 ms umschalten
            if ( mnSwitchId != GetCurPageId() )
            {
                if ( Time::GetSystemTicks() > mnSwitchTime+500 )
                {
                    mbInSwitching = sal_True;
                    if ( ImplDeactivatePage() )
                    {
                        SetCurPageId( mnSwitchId );
                        Update();
                        ImplActivatePage();
                        ImplSelect();
                        bSwitch = sal_True;
                    }
                    mbInSwitching = sal_False;
                }
            }
        }
    }

    return bSwitch;
}

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

void TabBar::EndSwitchPage()
{
    mnSwitchTime    = 0;
    mnSwitchId      = 0;
}

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

void TabBar::SetStyle( WinBits nStyle )
{
    mnWinStyle = nStyle;
    ImplInitControls();
    // Evt. Controls neu anordnen
    if ( IsReallyVisible() && IsUpdateMode() )
        Resize();
}

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

Size TabBar::CalcWindowSizePixel() const
{
    long nWidth = 0;

    if ( mpItemList->size() )
    {
        ((TabBar*)this)->ImplCalcWidth();
        for ( size_t i = 0, n = mpItemList->size(); i < n; ++i )
        {
            ImplTabBarItem* pItem = (*mpItemList)[ i ];
            nWidth += pItem->mnWidth;
        }
        nWidth += TABBAR_OFFSET_X+TABBAR_OFFSET_X2;
    }

    return Size( nWidth, GetSettings().GetStyleSettings().GetScrollBarSize() );
}
// -----------------------------------------------------------------------

Rectangle TabBar::GetPageArea() const
{
    return Rectangle( Point( mnOffX, mnOffY ), Size( mnLastOffX-mnOffX+1, GetSizePixel().Height()-mnOffY ) );
}

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

::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible > TabBar::CreateAccessible()
{
    return mpImpl->maAccessibleFactory.getFactory().createAccessibleTabBar( *this );
}

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

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
