Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/vcl/source/control/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 74 kB image not shown  

Quelle  tabctrl.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */


#include <sal/config.h>

#include <vcl/help.hxx>
#include <vcl/layout.hxx>
#include <vcl/notebookbar/notebookbar.hxx>
#include <vcl/tabctrl.hxx>
#include <vcl/tabpage.hxx>
#include <vcl/toolbox.hxx>
#include <vcl/toolkit/button.hxx>
#include <vcl/mnemonic.hxx>
#include <vcl/toolkit/lstbox.hxx>
#include <vcl/uitest/uiobject.hxx>

#include <bitmaps.hlst>
#include <svdata.hxx>
#include <window.h>

#include <deque>
#include <unordered_map>
#include <vector>

#define TAB_OFFSET          3
/// Space to the left and right of the tabitem
#define TAB_ITEM_OFFSET_X     10
/// Space to the top and bottom of the tabitem
#define TAB_ITEM_OFFSET_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

class ImplTabItem final
{
    sal_uInt16 m_nId;

public:
    VclPtr<TabPage>     mpTabPage;
    OUString            maText;
    OUString            maFormatText;
    OUString            maHelpText;
    OUString            maAccessibleName;
    OUString            maAccessibleDescription;
    OUString             maTabName;
    tools::Rectangle    maRect;
    sal_uInt16          mnLine;
    bool                mbFullVisible;
    bool                m_bEnabled; ///< the tab / page is selectable
    bool                m_bVisible; ///< the tab / page can be visible
    Image               maTabImage;

    ImplTabItem(sal_uInt16 nId);

    sal_uInt16 id() const { return m_nId; }
};

ImplTabItem::ImplTabItem(sal_uInt16 nId)
    : m_nId(nId)
    , mnLine(0)
    , mbFullVisible(false)
    , m_bEnabled(true)
    , m_bVisible(true)
{
}

struct ImplTabCtrlData
{
    std::vector< ImplTabItem >      maItemList;
    VclPtr<ListBox>                 mpListBox;
};

// for the Tab positions
#define TAB_PAGERECT        0xFFFF

void TabControl::ImplInit( vcl::Window* pParent, WinBits nStyle )
{
    mbLayoutDirty = true;

    if ( !(nStyle & WB_NOTABSTOP) )
        nStyle |= WB_TABSTOP;
    if ( !(nStyle & WB_NOGROUP) )
        nStyle |= WB_GROUP;
    if ( !(nStyle & WB_NODIALOGCONTROL) )
        nStyle |= WB_DIALOGCONTROL;

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

    mnLastWidth                 = 0;
    mnLastHeight                = 0;
    mnActPageId                 = 0;
    mnCurPageId                 = 0;
    mbFormat                    = true;
    mbShowTabs                  = true;
    mbRestoreHelpId             = false;
    mbSmallInvalidate           = false;
    mpTabCtrlData.reset(new ImplTabCtrlData);
    mpTabCtrlData->mpListBox    = nullptr;

    ImplInitSettings( true );

    if( nStyle & WB_DROPDOWN )
    {
        mpTabCtrlData->mpListBox = VclPtr<ListBox>::Create( 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 controls will have transparent background
    // otherwise they will paint with a wrong background
    if( IsNativeControlSupported(ControlType::TabPane, ControlPart::Entire) )
        EnableChildTransparentMode();

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

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

const Color& TabControl::GetCanonicalTextColor( const StyleSettings& _rStyle ) const
{
    return _rStyle.GetTabTextColor();
}

void TabControl::ImplInitSettings( bool bBackground )
{
    Control::ImplInitSettings();

    if ( !bBackground )
        return;

    vcl::Window* pParent = GetParent();
    if ( !IsControlBackground() &&
        (pParent->IsChildTransparentModeEnabled()
        || IsNativeControlSupported(ControlType::TabPane, ControlPart::Entire)
        || IsNativeControlSupported(ControlType::TabItem, ControlPart::Entire) ) )

    {
        // set transparent mode for NWF tabcontrols to have
        // the background always cleared properly
        EnableChildTransparentMode();
        SetParentClipMode( ParentClipMode::NoClip );
        SetPaintTransparent( true );
        SetBackground();
        ImplGetWindowImpl()->mbUseNativeFocus = ImplGetSVData()->maNWFData.mbNoFocusRects;

        return;
    }

    EnableChildTransparentMode( false );
    SetParentClipMode();
    SetPaintTransparent( false );

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

TabControl::TabControl( vcl::Window* pParent, WinBits nStyle ) :
    Control( WindowType::TABCONTROL )
{
    ImplInit( pParent, nStyle );
    SAL_INFO( "vcl""*** TABCONTROL no notabs? " << (( GetStyle() & WB_NOBORDER ) ? "true" : "false") );
}

TabControl::~TabControl()
{
    disposeOnce();
}

void TabControl::dispose()
{
    Window *pParent = GetParent();
    if (pParent && pParent->IsDialog())
        GetParent()->RemoveChildEventListener( LINK( this, TabControl, ImplWindowEventListener ) );

    // delete TabCtrl data
    if (mpTabCtrlData)
        mpTabCtrlData->mpListBox.disposeAndClear();
    mpTabCtrlData.reset();
    Control::dispose();
}

ImplTabItem* TabControl::ImplGetItem( sal_uInt16 nId ) const
{
    for (auto & item : mpTabCtrlData->maItemList)
    {
        if (item.id() == nId)
            return &item;
    }

    return nullptr;
}

Size TabControl::ImplGetItemSize( ImplTabItem* pItem, tools::Long nMaxWidth )
{
    pItem->maFormatText = pItem->maText;
    Size aSize( GetOutDev()->GetCtrlTextWidth( pItem->maFormatText ), GetTextHeight() );
    Size aImageSize( 0, 0 );
    if( !!pItem->maTabImage )
    {
        aImageSize = pItem->maTabImage.GetSizePixel();
        if( !pItem->maFormatText.isEmpty() )
            aImageSize.AdjustWidth(GetTextHeight()/4 );
    }
    aSize.AdjustWidth(aImageSize.Width() );
    if( aImageSize.Height() > aSize.Height() )
        aSize.setHeight( aImageSize.Height() );

    aSize.AdjustWidth(TAB_ITEM_OFFSET_X*2 );
    aSize.AdjustHeight(TAB_ITEM_OFFSET_Y*2 );

    tools::Rectangle aCtrlRegion( Point( 0, 0 ), aSize );
    tools::Rectangle aBoundingRgn, aContentRgn;
    const TabitemValue aControlValue(tools::Rectangle(TAB_ITEM_OFFSET_X, TAB_ITEM_OFFSET_Y,
                                                      aSize.Width() - TAB_ITEM_OFFSET_X * 2,
                                                      aSize.Height() - TAB_ITEM_OFFSET_Y * 2),
                                     TabBarPosition::Top);
    if(GetNativeControlRegion( ControlType::TabItem, ControlPart::Entire, aCtrlRegion,
                                           ControlState::ENABLED, aControlValue,
                                           aBoundingRgn, aContentRgn ) )
    {
        return aContentRgn.GetSize();
    }

    // For languages with short names (e.g. Chinese), because the space is
    // normally only one pixel per char
    if ( pItem->maFormatText.getLength() < TAB_EXTRASPACE_X )
        aSize.AdjustWidth(TAB_EXTRASPACE_X-pItem->maFormatText.getLength() );

    // shorten Text if needed
    if ( aSize.Width()+4 >= nMaxWidth )
    {
        OUString aAppendStr(u"..."_ustr);
        pItem->maFormatText += aAppendStr;
        do
        {
            if (pItem->maFormatText.getLength() > aAppendStr.getLength())
                pItem->maFormatText = pItem->maFormatText.replaceAt( pItem->maFormatText.getLength()-aAppendStr.getLength()-1, 1, u"" );
            aSize.setWidth( GetOutDev()->GetCtrlTextWidth( pItem->maFormatText ) );
            aSize.AdjustWidth(aImageSize.Width() );
            aSize.AdjustWidth(TAB_ITEM_OFFSET_X*2 );
        }
        while ( (aSize.Width()+4 >= nMaxWidth) && (pItem->maFormatText.getLength() > aAppendStr.getLength()) );
        if ( aSize.Width()+4 >= nMaxWidth )
        {
            pItem->maFormatText = ".";
            aSize.setWidth( 1 );
        }
    }

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

    return aSize;
}

// Feel free to move this to some more general place for reuse
// http://en.wikipedia.org/wiki/Word_wrap#Minimum_raggedness
// Mostly based on Alexey Frunze's nifty example at
// http://stackoverflow.com/questions/9071205/balanced-word-wrap-minimum-raggedness-in-php
namespace MinimumRaggednessWrap
{
    static std::deque<size_t> GetEndOfLineIndexes(const std::vector<sal_Int32>& rWidthsOf, sal_Int32 nLineWidth)
    {
        ++nLineWidth;

        size_t nWidthsCount = rWidthsOf.size();
        std::vector<sal_Int32> aCosts(nWidthsCount * nWidthsCount);

        // cost function c(i, j) that computes the cost of a line consisting of
        // the words Word[i] to Word[j]
        for (size_t i = 0; i < nWidthsCount; ++i)
        {
            for (size_t j = 0; j < nWidthsCount; ++j)
            {
                if (j >= i)
                {
                    sal_Int32 c = nLineWidth - (j - i);
                    for (size_t k = i; k <= j; ++k)
                        c -= rWidthsOf[k];
                    c = (c >= 0) ? c * c : SAL_MAX_INT32;
                    aCosts[j * nWidthsCount + i] = c;
                }
                else
                {
                    aCosts[j * nWidthsCount + i] = SAL_MAX_INT32;
                }
            }
        }

        std::vector<sal_Int32> aFunction(nWidthsCount);
        std::vector<sal_Int32> aWrapPoints(nWidthsCount);

        // f(j) in aFunction[], collect wrap points in aWrapPoints[]
        for (size_t j = 0; j < nWidthsCount; ++j)
        {
            aFunction[j] = aCosts[j * nWidthsCount];
            if (aFunction[j] == SAL_MAX_INT32)
            {
                for (size_t k = 0; k < j; ++k)
                {
                    sal_Int32 s;
                    if (aFunction[k] == SAL_MAX_INT32 || aCosts[j * nWidthsCount + k + 1] == SAL_MAX_INT32)
                        s = SAL_MAX_INT32;
                    else
                        s = aFunction[k] + aCosts[j * nWidthsCount + k + 1];
                    if (aFunction[j] > s)
                    {
                        aFunction[j] = s;
                        aWrapPoints[j] = k + 1;
                    }
                }
            }
        }

        std::deque<size_t> aSolution;

        // no solution
        if (aFunction[nWidthsCount - 1] == SAL_MAX_INT32)
            return aSolution;

        // optimal solution
        size_t j = nWidthsCount - 1;
        while (true)
        {
            aSolution.push_front(j);
            if (!aWrapPoints[j])
                break;
            j = aWrapPoints[j] - 1;
        }

        return aSolution;
    }
};

static void lcl_AdjustSingleLineTabs(tools::Long nMaxWidth, ImplTabCtrlData *pTabCtrlData)
{
    if (!ImplGetSVData()->maNWFData.mbCenteredTabs)
        return;

    int nRightSpace = nMaxWidth; // space left on the right by the tabs
    for (auto const& item : pTabCtrlData->maItemList)
    {
        if (!item.m_bVisible)
            continue;
        nRightSpace -= item.maRect.GetWidth();
    }
    nRightSpace /= 2;

    for (auto& item : pTabCtrlData->maItemList)
    {
        if (!item.m_bVisible)
            continue;
        item.maRect.AdjustLeft(nRightSpace);
        item.maRect.AdjustRight(nRightSpace);
    }
}

bool TabControl::ImplPlaceTabs( tools::Long nWidth )
{
    if ( nWidth <= 0 )
        return false;
    if ( mpTabCtrlData->maItemList.empty() )
        return false;

    tools::Long nMaxWidth = nWidth;

    const tools::Long nOffsetX = 2;
    const tools::Long nOffsetY = 2;

    //fdo#66435 throw Knuth/Tex minimum raggedness algorithm at the problem
    //of ugly bare tabs on lines of their own

    //collect widths
    std::vector<sal_Int32> aWidths;
    for (auto & item : mpTabCtrlData->maItemList)
    {
        if (!item.m_bVisible)
            continue;
        aWidths.push_back(ImplGetItemSize(&item, nMaxWidth).Width());
    }

    //aBreakIndexes will contain the indexes of the last tab on each row
    std::deque<size_t> aBreakIndexes(MinimumRaggednessWrap::GetEndOfLineIndexes(aWidths, nMaxWidth - nOffsetX - 2));

    tools::Long nX = nOffsetX;
    tools::Long nY = nOffsetY;

    sal_uInt16 nLines = 0;
    sal_uInt16 nCurLine = 0;

    tools::Long nLineWidthAry[100];
    sal_uInt16 nLinePosAry[101];
    nLineWidthAry[0] = 0;
    nLinePosAry[0] = 0;

    size_t nIndex = 0;

    for (auto & item : mpTabCtrlData->maItemList)
    {
        if (!item.m_bVisible)
            continue;

        Size aSize = ImplGetItemSize( &item, nMaxWidth );

        bool bNewLine = false;
        if (!aBreakIndexes.empty() && nIndex > aBreakIndexes.front())
        {
            aBreakIndexes.pop_front();
            bNewLine = true;
        }

        if ( bNewLine && (nWidth > 2+nOffsetX) )
        {
            if ( nLines == 99 )
                break;

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

        tools::Rectangle aNewRect( Point( nX, nY ), aSize );
        if ( mbSmallInvalidate && (item.maRect != aNewRect) )
            mbSmallInvalidate = false;
        item.maRect = aNewRect;
        item.mnLine = nLines;
        item.mbFullVisible = true;

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

        if (item.id() == mnCurPageId)
            nCurLine = nLines;

        ++nIndex;
    }

    if (nLines) // two or more lines
    {
        tools::Long nLineHeightAry[100];
        tools::Long nIH = 0;
        for (const auto& item : mpTabCtrlData->maItemList)
        {
            if (!item.m_bVisible)
                continue;
            nIH = item.maRect.Bottom() - 1;
            break;
        }

        for ( sal_uInt16 i = 0; i < nLines+1; i++ )
        {
            if ( i <= nCurLine )
                nLineHeightAry[i] = nIH*(nLines-(nCurLine-i));
            else
                nLineHeightAry[i] = nIH*(i-nCurLine-1);
        }

        nLinePosAry[nLines+1] = static_cast<sal_uInt16>(mpTabCtrlData->maItemList.size());

        tools::Long nDX = 0;
        tools::Long nModDX = 0;
        tools::Long nIDX = 0;

        sal_uInt16 i = 0;
        sal_uInt16 n = 0;

        for (auto & item : mpTabCtrlData->maItemList)
        {
            if (!item.m_bVisible)
                continue;

            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 case of tabctrl way too small
                    nDX = 0;
                    nModDX = 0;
                }
                n++;
            }

            item.maRect.AdjustLeft(nIDX );
            item.maRect.AdjustRight(nIDX + nDX );
            item.maRect.SetTop( nLineHeightAry[n-1] );
            item.maRect.SetBottom(nLineHeightAry[n-1] + nIH - 1);
            nIDX += nDX;

            if ( nModDX )
            {
                nIDX++;
                item.maRect.AdjustRight( 1 );
                nModDX--;
            }

            i++;
        }
    }
    else // only one line
        lcl_AdjustSingleLineTabs(nMaxWidth, mpTabCtrlData.get());

    return true;
}

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

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

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

        tools::Rectangle aRect = ImplGetTabRect( nLastPos, nWidth, nHeight );
        if (aRect.IsEmpty())
            return aRect;

        // with show-tabs of true (the usual) the page rect is from under the
        // visible tab to the bottom of the TabControl, otherwise it extends
        // from the top of the TabControl
        tools::Long nTabBottom = mbShowTabs ? aRect.Bottom() : 0;

        tools::Long nW = nWidth-TAB_OFFSET*2;
        tools::Long nH = nHeight - nTabBottom - TAB_OFFSET*2;
        return (nW > 0 && nH > 0)
            ? tools::Rectangle( Point( TAB_OFFSET, nTabBottom + TAB_OFFSET ), Size( nW, nH ) )
            : tools::Rectangle();
    }

    ImplTabItem* const pItem = (nItemPos < mpTabCtrlData->maItemList.size())
                               ? &mpTabCtrlData->maItemList[nItemPos] : nullptr;
    return ImplGetTabRect(pItem, nWidth, nHeight);
}

tools::Rectangle TabControl::ImplGetTabRect(const ImplTabItem* pItem, tools::Long nWidth, tools::Long nHeight)
{
    if ((nWidth <= 1) || (nHeight <= 0) || !pItem || !pItem->m_bVisible)
        return tools::Rectangle();

    nWidth -= 1;

    if ( mbFormat || (mnLastWidth != nWidth) || (mnLastHeight != nHeight) )
    {
        vcl::Font aFont( GetFont() );
        aFont.SetTransparent( true );
        SetFont( aFont );

        bool bRet = ImplPlaceTabs( nWidth );
        if ( !bRet )
            return tools::Rectangle();

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

    return pItem->maRect;
}

void TabControl::ImplChangeTabPage( sal_uInt16 nId, sal_uInt16 nOldId )
{
    ImplTabItem*    pOldItem = ImplGetItem( nOldId );
    ImplTabItem*    pItem = ImplGetItem( nId );
    TabPage*        pOldPage = pOldItem ? pOldItem->mpTabPage.get() : nullptr;
    TabPage*        pPage = pItem ? pItem->mpTabPage.get() : nullptr;
    vcl::Window*    pCtrlParent = GetParent();

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

        if ( !pOldItem || !pItem || (pOldItem->mnLine != pItem->mnLine) )
        {
            aRect.SetLeft( 0 );
            aRect.SetTop( 0 );
            aRect.SetRight( Control::GetOutputSizePixel().Width() );
        }
        else
        {
            aRect.AdjustLeft( -3 );
            aRect.AdjustTop( -2 );
            aRect.AdjustRight(3 );
            Invalidate( aRect );
            nPos = GetPagePos( nOldId );
            aRect = ImplGetTabRect( nPos );
            aRect.AdjustLeft( -3 );
            aRect.AdjustTop( -2 );
            aRect.AdjustRight(3 );
        }
        Invalidate( aRect );
    }

    if ( pOldPage == pPage )
        return;

    tools::Rectangle aRect = ImplGetTabRect( TAB_PAGERECT );

    if ( pOldPage )
    {
        if ( mbRestoreHelpId )
            pCtrlParent->SetHelpId({});
    }

    if ( pPage )
    {
        if ( GetStyle() & WB_NOBORDER )
        {
            tools::Rectangle aRectNoTab(Point(0, 0), GetSizePixel());
            pPage->SetPosSizePixel( aRectNoTab.TopLeft(), aRectNoTab.GetSize() );
        }
        else
            pPage->SetPosSizePixel( aRect.TopLeft(), aRect.GetSize() );

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

        pPage->Show();

        if ( pOldPage && pOldPage->HasChildPathFocus() )
        {
            vcl::Window* pFirstChild = pPage->ImplGetDlgWindow( 0, GetDlgWindowType::First );
            if ( pFirstChild )
                pFirstChild->ImplControlFocus( GetFocusFlags::Init );
            else
                GrabFocus();
        }
    }

    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( ControlType::TabPane, ControlPart::Entire ) )
    {
        aRect.AdjustLeft( -(TAB_OFFSET) );
        aRect.AdjustTop( -(TAB_OFFSET) );
        aRect.AdjustRight(TAB_OFFSET );
        aRect.AdjustBottom(TAB_OFFSET );
    }

    Invalidate( aRect );
}

bool TabControl::ImplPosCurTabPage()
{
    // resize/position current TabPage
    ImplTabItem* pItem = ImplGetItem( GetCurPageId() );
    if ( pItem && pItem->mpTabPage )
    {
        if (  GetStyle() & WB_NOBORDER )
        {
            tools::Rectangle aRectNoTab(Point(0, 0), GetSizePixel());
            pItem->mpTabPage->SetPosSizePixel( aRectNoTab.TopLeft(), aRectNoTab.GetSize() );
            return true;
        }
        tools::Rectangle aRect = ImplGetTabRect( TAB_PAGERECT );
        pItem->mpTabPage->SetPosSizePixel( aRect.TopLeft(), aRect.GetSize() );
        return true;
    }

    return false;
}

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

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

    SelectTabPage( GetPageId( nCurPos ) );
}

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

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

    if ( !(GetSettings().GetStyleSettings().GetOptions() & StyleSettingsOptions::Mono) )
        nOff = 1;
    else
        nOff = 0;

    if( !! rItem.maTabImage )
    {
        aImageSize = rItem.maTabImage.GetSizePixel();
        if( !rItem.maFormatText.isEmpty() )
            aImageSize.AdjustWidth(GetTextHeight()/4 );
    }

    if( !rItem.maFormatText.isEmpty() )
    {
        // show focus around text
        aRect.SetLeft( aRect.Left()+aImageSize.Width()+((aTabSize.Width()-nTextWidth-aImageSize.Width())/2)-nOff-1-1 );
        aRect.SetTop( aRect.Top()+((aTabSize.Height()-nTextHeight)/2)-1-1 );
        aRect.SetRight( aRect.Left()+nTextWidth+2 );
        aRect.SetBottom( aRect.Top()+nTextHeight+2 );
    }
    else
    {
        // show focus around image
        tools::Long nXPos = aRect.Left()+((aTabSize.Width()-nTextWidth-aImageSize.Width())/2)-nOff-1;
        tools::Long nYPos = aRect.Top();
        if( aImageSize.Height() < aRect.GetHeight() )
            nYPos += (aRect.GetHeight() - aImageSize.Height())/2;

        aRect.SetLeft( nXPos - 2 );
        aRect.SetTop( nYPos - 2 );
        aRect.SetRight( aRect.Left() + aImageSize.Width() + 4 );
        aRect.SetBottom( aRect.Top() + aImageSize.Height() + 4 );
    }
    ShowFocus( aRect );
}

void TabControl::ImplDrawItem(vcl::RenderContext& rRenderContext, ImplTabItem const * pItem, const tools::Rectangle& rCurRect,
                              bool bFirstInGroup, bool bLastInGroup )
{
    if (!pItem->m_bVisible || pItem->maRect.IsEmpty())
        return;

    const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
    tools::Rectangle aRect = pItem->maRect;
    tools::Long nLeftBottom = aRect.Bottom();
    tools::Long nRightBottom = aRect.Bottom();
    bool bLeftBorder = true;
    bool bRightBorder = true;
    sal_uInt16 nOff;
    bool bNativeOK = false;

    sal_uInt16 nOff2 = 0;
    sal_uInt16 nOff3 = 0;

    if (!(rStyleSettings.GetOptions() & StyleSettingsOptions::Mono))
        nOff = 1;
    else
        nOff = 0;

    // if this is the active Page, we have to draw a little more
    if (pItem->id() == mnCurPageId)
    {
        nOff2 = 2;
        if (!ImplGetSVData()->maNWFData.mbNoActiveTabTextRaise)
            nOff3 = 1;
    }
    else
    {
        Point aLeftTestPos = aRect.BottomLeft();
        Point aRightTestPos = aRect.BottomRight();
        if (aLeftTestPos.Y() == rCurRect.Bottom())
        {
            aLeftTestPos.AdjustX( -2 );
            if (rCurRect.Contains(aLeftTestPos))
                bLeftBorder = false;
            aRightTestPos.AdjustX(2 );
            if (rCurRect.Contains(aRightTestPos))
                bRightBorder = false;
        }
        else
        {
            if (rCurRect.Contains(aLeftTestPos))
                nLeftBottom -= 2;
            if (rCurRect.Contains(aRightTestPos))
                nRightBottom -= 2;
        }
    }

    ControlState nState = ControlState::NONE;

    if (pItem->id() == mnCurPageId)
    {
        nState |= ControlState::SELECTED;
        // only the selected item can be focused
        if (HasFocus())
            nState |= ControlState::FOCUSED;
    }
    if (IsEnabled())
        nState |= ControlState::ENABLED;
    if (IsMouseOver() && pItem->maRect.Contains(GetPointerPosPixel()))
    {
        nState |= ControlState::ROLLOVER;
        for (auto const& item : mpTabCtrlData->maItemList)
            if ((&item != pItem) && item.m_bVisible && item.maRect.Contains(GetPointerPosPixel()))
            {
                nState &= ~ControlState::ROLLOVER; // avoid multiple highlighted tabs
                break;
            }
        assert(nState & ControlState::ROLLOVER);
    }

    bNativeOK = rRenderContext.IsNativeControlSupported(ControlType::TabItem, ControlPart::Entire);
    if ( bNativeOK )
    {
        TabitemValue tiValue(tools::Rectangle(pItem->maRect.Left() + TAB_ITEM_OFFSET_X,
                                              pItem->maRect.Top() + TAB_ITEM_OFFSET_Y,
                                              pItem->maRect.Right() - TAB_ITEM_OFFSET_X,
                                              pItem->maRect.Bottom() - TAB_ITEM_OFFSET_Y),
                             TabBarPosition::Top);
        if (pItem->maRect.Left() < 5)
            tiValue.mnAlignment |= TabitemFlags::LeftAligned;
        if (pItem->maRect.Right() > mnLastWidth - 5)
            tiValue.mnAlignment |= TabitemFlags::RightAligned;
        if (bFirstInGroup)
            tiValue.mnAlignment |= TabitemFlags::FirstInGroup;
        if (bLastInGroup)
            tiValue.mnAlignment |= TabitemFlags::LastInGroup;

        tools::Rectangle aCtrlRegion( pItem->maRect );
        aCtrlRegion.AdjustBottom(TabPaneValue::m_nOverlap);
        bNativeOK = rRenderContext.DrawNativeControl(ControlType::TabItem, ControlPart::Entire,
                                                     aCtrlRegion, nState, tiValue, OUString() );
    }

    if (!bNativeOK)
    {
        if (!(rStyleSettings.GetOptions() & StyleSettingsOptions::Mono))
        {
            rRenderContext.SetLineColor(rStyleSettings.GetLightColor());
            rRenderContext.DrawPixel(Point(aRect.Left() + 1 - nOff2, aRect.Top() + 1 - nOff2)); // diagonally indented top-left pixel
            if (bLeftBorder)
            {
                rRenderContext.DrawLine(Point(aRect.Left() - nOff2, aRect.Top() + 2 - nOff2),
                                        Point(aRect.Left() - nOff2, nLeftBottom - 1));
            }
            rRenderContext.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)
            {
                rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
                rRenderContext.DrawLine(Point(aRect.Right() + nOff2 - 2, aRect.Top() + 1 - nOff2),
                                        Point(aRect.Right() + nOff2 - 2, nRightBottom - 1));

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

    // 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)
    vcl::Font aFont(rRenderContext.GetFont());
    aFont.SetTransparent(true);
    rRenderContext.SetFont(aFont);

    Size aTabSize = aRect.GetSize();
    Size aImageSize(0, 0);
    tools::Long nTextHeight = rRenderContext.GetTextHeight();
    tools::Long nTextWidth = rRenderContext.GetCtrlTextWidth(pItem->maFormatText);
    if (!!pItem->maTabImage)
    {
        aImageSize = pItem->maTabImage.GetSizePixel();
        if (!pItem->maFormatText.isEmpty())
            aImageSize.AdjustWidth(GetTextHeight() / 4 );
    }
    tools::Long nXPos = aRect.Left() + ((aTabSize.Width() - nTextWidth - aImageSize.Width()) / 2) - nOff - nOff3;
    tools::Long nYPos = aRect.Top() + ((aTabSize.Height() - nTextHeight) / 2) - nOff3;
    if (!pItem->maFormatText.isEmpty())
    {
        DrawTextFlags nStyle = DrawTextFlags::Mnemonic;
        if (!pItem->m_bEnabled)
            nStyle |= DrawTextFlags::Disable;

        Color aColor(rStyleSettings.GetTabTextColor());
        if (nState & ControlState::SELECTED)
            aColor = rStyleSettings.GetTabHighlightTextColor();
        else if (nState & ControlState::ROLLOVER)
            aColor = rStyleSettings.GetTabRolloverTextColor();

        Color aOldColor(rRenderContext.GetTextColor());
        rRenderContext.SetTextColor(aColor);

        const tools::Rectangle aOutRect(nXPos + aImageSize.Width(), nYPos,
                                 nXPos + aImageSize.Width() + nTextWidth, nYPos + nTextHeight);
        DrawControlText(rRenderContext, aOutRect, pItem->maFormatText, nStyle,
                        nullptr, nullptr);

        rRenderContext.SetTextColor(aOldColor);
    }

    if (!!pItem->maTabImage)
    {
        Point aImgTL( nXPos, aRect.Top() );
        if (aImageSize.Height() < aRect.GetHeight())
            aImgTL.AdjustY((aRect.GetHeight() - aImageSize.Height()) / 2 );
        rRenderContext.DrawImage(aImgTL, pItem->maTabImage, pItem->m_bEnabled ? DrawImageFlags::NONE : DrawImageFlags::Disable );
    }
}

bool TabControl::ImplHandleKeyEvent( const KeyEvent& rKeyEvent )
{
    bool bRet = false;

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

        if ( aKeyCode.IsMod1() )
        {
            if ( aKeyCode.IsShift() || (nKeyCode == KEY_PAGEUP) )
            {
                if ( (nKeyCode == KEY_TAB) || (nKeyCode == KEY_PAGEUP) )
                {
                    ImplActivateTabPage( false );
                    bRet = true;
                }
            }
            else
            {
                if ( (nKeyCode == KEY_TAB) || (nKeyCode == KEY_PAGEDOWN) )
                {
                    ImplActivateTabPage( true );
                    bRet = true;
                }
            }
        }
    }

    return bRet;
}

IMPL_LINK_NOARG(TabControl, ImplListBoxSelectHdl, ListBox&, void)
{
    SelectTabPage( GetPageId( mpTabCtrlData->mpListBox->GetSelectedEntryPos() ) );
}

IMPL_LINK( TabControl, ImplWindowEventListener, VclWindowEvent&, rEvent, void )
{
    if ( rEvent.GetId() == VclEventId::WindowKeyInput )
    {
        // Do not handle events from TabControl or its children, which is done in Notify(), where the events can be consumed.
        if ( !IsWindowOrChild( rEvent.GetWindow() ) )
        {
            KeyEvent* pKeyEvent = static_cast< KeyEvent* >(rEvent.GetData());
            ImplHandleKeyEvent( *pKeyEvent );
        }
    }
}

void TabControl::MouseButtonDown( const MouseEvent& rMEvt )
{
    if (mpTabCtrlData->mpListBox || !rMEvt.IsLeft())
        return;

    ImplTabItem *pItem = ImplGetItem(rMEvt.GetPosPixel());
    if (pItem && pItem->m_bEnabled)
        SelectTabPage(pItem->id());
}

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

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

    Control::KeyInput( rKEvt );
}

static bool lcl_canPaint(const vcl::RenderContext& rRenderContext, const tools::Rectangle& rDrawRect,
                         const tools::Rectangle& rItemRect)
{
    vcl::Region aClipRgn(rRenderContext.GetActiveClipRegion());
    aClipRgn.Intersect(rItemRect);
    if (!rDrawRect.IsEmpty())
        aClipRgn.Intersect(rDrawRect);
    return !aClipRgn.IsEmpty();
}

void TabControl::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
{
    if (GetStyle() & WB_NOBORDER)
        return;

    Control::Paint(rRenderContext, rRect);

    HideFocus();

    // reformat if needed
    tools::Rectangle aRect = ImplGetTabRect(TAB_PAGERECT);

    // find current item
    ImplTabItem* pCurItem = nullptr;
    for (auto & item : mpTabCtrlData->maItemList)
    {
        if (item.id() == mnCurPageId)
        {
            pCurItem = &item;
            break;
        }
    }

    // Draw the TabPage border
    const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
    tools::Rectangle aCurRect;
    aRect.AdjustLeft( -(TAB_OFFSET) );
    aRect.AdjustTop( -(TAB_OFFSET) );
    aRect.AdjustRight(TAB_OFFSET );
    aRect.AdjustBottom(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.get() : nullptr;
    if (!pCurPage || !pCurPage->IsVisible())
    {
        bNoTabPage = true;
        aRect.AdjustLeft( -10 );
        aRect.AdjustRight(10 );
    }

    if (rRenderContext.IsNativeControlSupported(ControlType::TabPane, ControlPart::Entire))
    {
        const bool bPaneWithHeader = mbShowTabs && rRenderContext.IsNativeControlSupported(ControlType::TabPane, ControlPart::TabPaneWithHeader);
        tools::Rectangle aHeaderRect(aRect.Left(), 0, aRect.Right(), aRect.Top());

        if (!mpTabCtrlData->maItemList.empty())
        {
            tools::Long nLeft = LONG_MAX;
            tools::Long nRight = 0;
            for (const auto &item : mpTabCtrlData->maItemList)
            {
                if (!item.m_bVisible)
                    continue;
                nRight = std::max(nRight, item.maRect.Right());
                nLeft = std::min(nLeft, item.maRect.Left());
            }
            aHeaderRect.SetLeft(nLeft);
            aHeaderRect.SetRight(nRight);
        }

        if (bPaneWithHeader)
            aRect.SetTop(0);

        const TabPaneValue aTabPaneValue(aHeaderRect, pCurItem ? pCurItem->maRect : tools::Rectangle());

        ControlState nState = ControlState::ENABLED;
        if (!IsEnabled())
            nState &= ~ControlState::ENABLED;
        if (HasFocus())
            nState |= ControlState::FOCUSED;

        if (lcl_canPaint(rRenderContext, rRect, aRect))
            rRenderContext.DrawNativeControl(ControlType::TabPane, ControlPart::Entire,
                                             aRect, nState, aTabPaneValue, OUString());

        if (!bPaneWithHeader && rRenderContext.IsNativeControlSupported(ControlType::TabHeader, ControlPart::Entire)
                && lcl_canPaint(rRenderContext, rRect, aHeaderRect))
            rRenderContext.DrawNativeControl(ControlType::TabHeader, ControlPart::Entire,
                                             aHeaderRect, nState, aTabPaneValue, OUString());
    }
    else
    {
        tools::Long nTopOff = 1;
        if (!(rStyleSettings.GetOptions() & StyleSettingsOptions::Mono))
            rRenderContext.SetLineColor(rStyleSettings.GetLightColor());
        else
            rRenderContext.SetLineColor(COL_BLACK);
        if (mbShowTabs && pCurItem && !pCurItem->maRect.IsEmpty())
        {
            aCurRect = pCurItem->maRect;
            rRenderContext.DrawLine(aRect.TopLeft(), Point(aCurRect.Left() - 2, aRect.Top()));
            if (aCurRect.Right() + 1 < aRect.Right())
            {
                rRenderContext.DrawLine(Point(aCurRect.Right(), aRect.Top()), aRect.TopRight());
            }
            else
            {
                nTopOff = 0;
            }
        }
        else
            rRenderContext.DrawLine(aRect.TopLeft(), aRect.TopRight());

        rRenderContext.DrawLine(aRect.TopLeft(), aRect.BottomLeft());

        if (!(rStyleSettings.GetOptions() & StyleSettingsOptions::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)
                rRenderContext.SetLineColor(rStyleSettings.GetDialogColor());
            else
                rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
            rRenderContext.DrawLine(Point(1, aRect.Bottom() - 1), Point(aRect.Right() - 1, aRect.Bottom() - 1));
            rRenderContext.DrawLine(Point(aRect.Right() - 1, aRect.Top() + nTopOff), Point(aRect.Right() - 1, aRect.Bottom() - 1));
            if (bNoTabPage)
                rRenderContext.SetLineColor(rStyleSettings.GetDialogColor());
            else
                rRenderContext.SetLineColor(rStyleSettings.GetDarkShadowColor());
            rRenderContext.DrawLine(Point(0, aRect.Bottom()), Point(aRect.Right(), aRect.Bottom()));
            rRenderContext.DrawLine(Point(aRect.Right(), aRect.Top() + nTopOff), Point(aRect.Right(), aRect.Bottom()));
        }
        else
        {
            rRenderContext.DrawLine(aRect.TopRight(), aRect.BottomRight());
            rRenderContext.DrawLine(aRect.BottomLeft(), aRect.BottomRight());
        }
    }

    const size_t nItemListSize = mpTabCtrlData->maItemList.size();
    if (mbShowTabs && nItemListSize > 0 && mpTabCtrlData->mpListBox == nullptr)
    {
        // Some native toolkits (GTK+) draw tabs right-to-left, with an
        // overlap between adjacent tabs
        bool bDrawTabsRTL = rRenderContext.IsNativeControlSupported(ControlType::TabItem, ControlPart::TabsDrawRtl);
        ImplTabItem* pFirstTab = nullptr;
        ImplTabItem* pLastTab = nullptr;
        size_t idx;

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

        while (idx < nItemListSize)
        {
            ImplTabItem* pItem = &mpTabCtrlData->maItemList[idx];

            if (pItem != pCurItem && pItem->m_bVisible && lcl_canPaint(rRenderContext, rRect, pItem->maRect))
                ImplDrawItem(rRenderContext, pItem, aCurRect, pItem == pFirstTab, pItem == pLastTab);

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

        if (pCurItem && lcl_canPaint(rRenderContext, rRect, pCurItem->maRect))
            ImplDrawItem(rRenderContext, pCurItem, aCurRect, pCurItem == pFirstTab, pCurItem == pLastTab);
    }

    if (HasFocus())
        ImplShowFocus();

    mbSmallInvalidate = true;
}

void TabControl::setAllocation(const Size &rAllocation)
{
    if ( !IsReallyShown() )
        return;

    if( mpTabCtrlData->mpListBox )
    {
        // get the listbox' preferred size
        Size aTabCtrlSize( GetSizePixel() );
        tools::Long nPrefWidth = mpTabCtrlData->mpListBox->get_preferred_size().Width();
        if( nPrefWidth > aTabCtrlSize.Width() )
            nPrefWidth = aTabCtrlSize.Width();
        Size aNewSize( nPrefWidth, LogicToPixel( Size( 12, 12 ), MapMode( MapUnit::MapAppFont ) ).Height() );
        Point aNewPos( (aTabCtrlSize.Width() - nPrefWidth) / 2, 0 );
        mpTabCtrlData->mpListBox->SetPosSizePixel( aNewPos, aNewSize );
    }

    mbFormat = true;

    // resize/position active TabPage
    bool bTabPage = ImplPosCurTabPage();

    // check what needs to be invalidated
    Size aNewSize = rAllocation;
    tools::Long nNewWidth = aNewSize.Width();
    for (auto const& item : mpTabCtrlData->maItemList)
    {
        if (!item.m_bVisible)
            continue;
        if (!item.mbFullVisible || (item.maRect.Right()-2 >= nNewWidth))
        {
            mbSmallInvalidate = false;
            break;
        }
    }

    if ( mbSmallInvalidate )
    {
        tools::Rectangle aRect = ImplGetTabRect( TAB_PAGERECT );
        aRect.AdjustLeft( -(TAB_OFFSET+TAB_BORDER_LEFT) );
        aRect.AdjustTop( -(TAB_OFFSET+TAB_BORDER_TOP) );
        aRect.AdjustRight(TAB_OFFSET+TAB_BORDER_RIGHT );
        aRect.AdjustBottom(TAB_OFFSET+TAB_BORDER_BOTTOM );
        if ( bTabPage )
            Invalidate( aRect, InvalidateFlags::NoChildren );
        else
            Invalidate( aRect );

    }
    else
    {
        if ( bTabPage )
            Invalidate( InvalidateFlags::NoChildren );
        else
            Invalidate();
    }

    mbLayoutDirty = false;
}

void TabControl::SetPosSizePixel(const Point& rNewPos, const Size& rNewSize)
{
    Window::SetPosSizePixel(rNewPos, rNewSize);
    //if size changed, TabControl::Resize got called already
    if (mbLayoutDirty)
        setAllocation(rNewSize);
}

void TabControl::SetSizePixel(const Size& rNewSize)
{
    Window::SetSizePixel(rNewSize);
    //if size changed, TabControl::Resize got called already
    if (mbLayoutDirty)
        setAllocation(rNewSize);
}

void TabControl::SetPosPixel(const Point& rPos)
{
    Window::SetPosPixel(rPos);
    if (mbLayoutDirty)
        setAllocation(GetOutputSizePixel());
}

void TabControl::Resize()
{
    setAllocation(Control::GetOutputSizePixel());
}

void TabControl::GetFocus()
{
    if( ! mpTabCtrlData->mpListBox )
    {
        if (mbShowTabs)
        {
            ImplShowFocus();
            SetInputContext( InputContext( GetFont() ) );
        }
        else
        {
            // no tabs, focus first thing in current page
            ImplTabItem* pItem = ImplGetItem(GetCurPageId());
            if (pItem && pItem->mpTabPage)
            {
                vcl::Window* pFirstChild = pItem->mpTabPage->ImplGetDlgWindow(0, GetDlgWindowType::First);
                if ( pFirstChild )
                    pFirstChild->ImplControlFocus(GetFocusFlags::Init);
            }
        }
    }
    else
    {
        if( mpTabCtrlData->mpListBox->IsReallyVisible() )
            mpTabCtrlData->mpListBox->GrabFocus();
    }

    Control::GetFocus();
}

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

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

    if ( nItemId )
    {
        if ( rHEvt.GetMode() & HelpEventMode::BALLOON )
        {
            OUString aStr = GetHelpText( nItemId );
            if ( !aStr.isEmpty() )
            {
                tools::Rectangle aItemRect = ImplGetTabRect( GetPagePos( nItemId ) );
                Point aPt = OutputToScreenPixel( aItemRect.TopLeft() );
                aItemRect.SetLeft( aPt.X() );
                aItemRect.SetTop( aPt.Y() );
                aPt = OutputToScreenPixel( aItemRect.BottomRight() );
                aItemRect.SetRight( aPt.X() );
                aItemRect.SetBottom( aPt.Y() );
                Help::ShowBalloon( this, aItemRect.Center(), aItemRect, aStr );
                return;
            }
        }

        // for Quick or Ballon Help, we show the text, if it is cut
        if ( rHEvt.GetMode() & (HelpEventMode::QUICK | HelpEventMode::BALLOON) )
        {
            ImplTabItem* pItem = ImplGetItem( nItemId );
            const OUString& rStr = pItem->maText;
            if ( rStr != pItem->maFormatText )
            {
                tools::Rectangle aItemRect = ImplGetTabRect( GetPagePos( nItemId ) );
                Point aPt = OutputToScreenPixel( aItemRect.TopLeft() );
                aItemRect.SetLeft( aPt.X() );
                aItemRect.SetTop( aPt.Y() );
                aPt = OutputToScreenPixel( aItemRect.BottomRight() );
                aItemRect.SetRight( aPt.X() );
                aItemRect.SetBottom( aPt.Y() );
                if ( !rStr.isEmpty() )
                {
                    if ( rHEvt.GetMode() & HelpEventMode::BALLOON )
                        Help::ShowBalloon( this, aItemRect.Center(), aItemRect, rStr );
                    else
                        Help::ShowQuickHelp( this, aItemRect, rStr );
                    return;
                }
            }
        }

        if ( rHEvt.GetMode() & HelpEventMode::QUICK )
        {
            ImplTabItem* pItem = ImplGetItem( nItemId );
            const OUString& rHelpText = pItem->maHelpText;
            if (!rHelpText.isEmpty())
            {
                tools::Rectangle aItemRect = ImplGetTabRect( GetPagePos( nItemId ) );
                Point aPt = OutputToScreenPixel( aItemRect.TopLeft() );
                aItemRect.SetLeft( aPt.X() );
                aItemRect.SetTop( aPt.Y() );
                aPt = OutputToScreenPixel( aItemRect.BottomRight() );
                aItemRect.SetRight( aPt.X() );
                aItemRect.SetBottom( aPt.Y() );
                Help::ShowQuickHelp( this, aItemRect, rHelpText );
                return;
            }
        }
    }

    Control::RequestHelp( rHEvt );
}

void TabControl::Command( const CommandEvent& rCEvt )
{
    if( (mpTabCtrlData->mpListBox == nullptr) && (rCEvt.GetCommand() == CommandEventId::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 )
        {
            ScopedVclPtrInstance<PopupMenu> aMenu;
            for (auto const& item : mpTabCtrlData->maItemList)
            {
                aMenu->InsertItem(item.id(), item.maText, MenuItemBits::CHECKABLE | MenuItemBits::RADIOCHECK);
                if (item.id() == mnCurPageId)
                    aMenu->CheckItem(item.id());
                aMenu->SetHelpId(item.id(), {});
            }

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

    Control::Command( rCEvt );
}

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

    if ( nType == StateChangedType::InitShow )
    {
        ImplPosCurTabPage();
        if( mpTabCtrlData->mpListBox )
            Resize();
    }
    else if ( nType == StateChangedType::UpdateMode )
    {
        if ( IsUpdateMode() )
            Invalidate();
    }
    else if ( (nType == StateChangedType::Zoom)  ||
              (nType == StateChangedType::ControlFont) )
    {
        ImplInitSettings( false );
        Invalidate();
    }
    else if ( nType == StateChangedType::ControlForeground )
    {
        ImplInitSettings( false );
        Invalidate();
    }
    else if ( nType == StateChangedType::ControlBackground )
    {
        ImplInitSettings( true );
        Invalidate();
    }
}

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

    if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
         (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
         ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
          (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
    {
        ImplInitSettings( true );
        Invalidate();
    }
}

ImplTabItem* TabControl::ImplGetItem(const Point& rPt) const
{
    ImplTabItem* pFoundItem = nullptr;
    int nFound = 0;
    for (auto & item : mpTabCtrlData->maItemList)
    {
        if (item.m_bVisible && item.maRect.Contains(rPt))
        {
            nFound++;
            pFoundItem = &item;
        }
    }

    // assure that only one tab is highlighted at a time
    assert(nFound <= 1);
    return nFound == 1 ? pFoundItem : nullptr;
}

bool TabControl::PreNotify( NotifyEvent& rNEvt )
{
    if( rNEvt.GetType() == NotifyEventType::MOUSEMOVE )
    {
        const MouseEvent* pMouseEvt = rNEvt.GetMouseEvent();
        if( pMouseEvt && !pMouseEvt->GetButtons() && !pMouseEvt->IsSynthetic() && !pMouseEvt->IsModifierChanged() )
        {
            // trigger redraw if mouse over state has changed
            if( IsNativeControlSupported(ControlType::TabItem, ControlPart::Entire) )
            {
                ImplTabItem *pItem = ImplGetItem(GetPointerPosPixel());
                ImplTabItem *pLastItem = ImplGetItem(GetLastPointerPosPixel());
                if ((pItem != pLastItem) || pMouseEvt->IsLeaveWindow() || pMouseEvt->IsEnterWindow())
                {
                    vcl::Region aClipRgn;
                    if (pLastItem)
                    {
                        // allow for slightly bigger tabitems
                        // as used by gtk
                        // TODO: query for the correct sizes
                        tools::Rectangle aRect(pLastItem->maRect);
                        aRect.AdjustLeft( -2 );
                        aRect.AdjustRight(2 );
                        aRect.AdjustTop( -3 );
                        aClipRgn.Union( aRect );
                    }

                    if (pItem)
                    {
                        // allow for slightly bigger tabitems
                        // as used by gtk
                        // TODO: query for the correct sizes
                        tools::Rectangle aRect(pItem->maRect);
                        aRect.AdjustLeft( -2 );
                        aRect.AdjustRight(2 );
                        aRect.AdjustTop( -3 );
                        aClipRgn.Union( aRect );
                    }

                    if( !aClipRgn.IsEmpty() )
                        Invalidate( aClipRgn );
                }
            }
        }
    }

    return Control::PreNotify(rNEvt);
}

bool TabControl::EventNotify( NotifyEvent& rNEvt )
{
    bool bRet = false;

    if ( rNEvt.GetType() == NotifyEventType::KEYINPUT )
        bRet = ImplHandleKeyEvent( *rNEvt.GetKeyEvent() );

    return bRet || Control::EventNotify( rNEvt );
}

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

bool TabControl::DeactivatePage()
{
    return !maDeactivateHdl.IsSet() || maDeactivateHdl.Call( this );
}

void TabControl::SetTabPageSizePixel( const Size& rSize )
{
    Size aNewSize( rSize );
    aNewSize.AdjustWidth(TAB_OFFSET*2 );
    tools::Rectangle aRect = ImplGetTabRect( TAB_PAGERECT,
                                      aNewSize.Width(), aNewSize.Height() );
    aNewSize.AdjustHeight(aRect.Top()+TAB_OFFSET );
    Window::SetOutputSizePixel( aNewSize );
}

void TabControl::InsertPage( sal_uInt16 nPageId, const OUString& rText,
                             sal_uInt16 nPos )
{
    SAL_WARN_IF( !nPageId, "vcl""TabControl::InsertPage(): PageId == 0" );
    SAL_WARN_IF( GetPagePos( nPageId ) != TAB_PAGE_NOTFOUND, "vcl",
                "TabControl::InsertPage(): PageId already exists" );

    // insert new page item
    ImplTabItem* pItem = nullptr;
    if( nPos == TAB_APPEND || size_t(nPos) >= mpTabCtrlData->maItemList.size() )
    {
        mpTabCtrlData->maItemList.emplace_back(nPageId);
        pItem = &mpTabCtrlData->maItemList.back();
        if( mpTabCtrlData->mpListBox )
            mpTabCtrlData->mpListBox->InsertEntry( rText );
    }
    else
    {
        std::vector< ImplTabItem >::iterator new_it =
            mpTabCtrlData->maItemList.emplace(mpTabCtrlData->maItemList.begin() + nPos, nPageId);
        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->maText           = rText;
    pItem->mbFullVisible    = false;

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

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

    CallEventListeners( VclEventId::TabpageInserted, reinterpret_cast<void*>(nPageId) );
}

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

    // does the item exist ?
    if ( nPos == TAB_PAGE_NOTFOUND )
        return;

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

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

        if( ! mpTabCtrlData->maItemList.empty() )
        {
            // don't do this by simply setting mnCurPageId to pFirstItem->id()
            // this leaves a lot of stuff (such trivia 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].id());
        }
    }

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

    CallEventListeners( VclEventId::TabpageRemoved, reinterpret_cast<void*>(nPageId) );
}

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

    if (!pItem || pItem->m_bEnabled == i_bEnable)
        return;

    pItem->m_bEnabled = i_bEnable;
    if (!pItem->m_bVisible)
        return;

    mbFormat = true;
    if( mpTabCtrlData->mpListBox )
        mpTabCtrlData->mpListBox->SetEntryFlags( GetPagePos( i_nPageId ),
                                                 i_bEnable ? ListBoxEntryFlags::NONE : (ListBoxEntryFlags::DisableSelection | ListBoxEntryFlags::DrawDisabled) );

    // SetCurPageId will change to a valid page
    if (pItem->id() == mnCurPageId)
        SetCurPageId( mnCurPageId );
    else if ( IsUpdateMode() )
        Invalidate();
}

void TabControl::SetPageVisible( sal_uInt16 nPageId, bool bVisible )
{
    ImplTabItem* pItem = ImplGetItem( nPageId );
    if (!pItem || pItem->m_bVisible == bVisible)
        return;

    pItem->m_bVisible = bVisible;
    if (!bVisible)
    {
        if (pItem->mbFullVisible)
            mbSmallInvalidate = false;
        pItem->mbFullVisible = false;
        pItem->maRect.SetEmpty();
    }
    mbFormat = true;

    // SetCurPageId will change to a valid page
    if (pItem->id() == mnCurPageId)
        SetCurPageId(mnCurPageId);
    else if (IsUpdateMode())
        Invalidate();
}

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

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

sal_uInt16 TabControl::GetPagePos( sal_uInt16 nPageId ) const
{
    sal_uInt16 nPos = 0;
    for (auto const& item : mpTabCtrlData->maItemList)
    {
        if (item.id() == nPageId)
            return nPos;
        ++nPos;
    }

    return TAB_PAGE_NOTFOUND;
}

sal_uInt16 TabControl::GetPageId( const Point& rPos ) const
{
    Size winSize = Control::GetOutputSizePixel();
    const auto &rList = mpTabCtrlData->maItemList;
    const auto it = std::find_if(rList.begin(), rList.end(), [&rPos, &winSize, this](const auto &item)&nbsp;{
        return const_cast<TabControl*>(this)->ImplGetTabRect(&item, winSize.Width(), winSize.Height()).Contains(rPos); });
    return (it != rList.end()) ? it->id() : 0;
}

sal_uInt16 TabControl::GetPageId( const OUString& rName ) const
{
    const auto &rList = mpTabCtrlData->maItemList;
    const auto it = std::find_if(rList.begin(), rList.end(), [&rName](const auto &item) {
        return item.maTabName == rName; });
    return (it != rList.end()) ? it->id() : 0;
}

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

    if( nPos == TAB_PAGE_NOTFOUND )
        return;

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

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

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

void TabControl::SelectTabPage( sal_uInt16 nPageId )
{
    if ( !nPageId || (nPageId == mnCurPageId) )
        return;

    CallEventListeners( VclEventId::TabpageDeactivate, reinterpret_cast<void*>(mnCurPageId) );
    if ( DeactivatePage() )
    {
        mnActPageId = nPageId;
        ActivatePage();
        // Page could have been switched by the Activate handler
        nPageId = mnActPageId;
        mnActPageId = 0;
        SetCurPageId( nPageId );
        if( mpTabCtrlData->mpListBox )
            mpTabCtrlData->mpListBox->SelectEntryPos( GetPagePos( nPageId ) );
        CallEventListeners( VclEventId::TabpageActivate, reinterpret_cast<void*>(nPageId) );
    }
}

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

    if ( !pItem || (pItem->mpTabPage.get() == pTabPage) )
        return;

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

        // only set here, so that Resize does not reposition TabPage
        pItem->mpTabPage = pTabPage;
        queue_resize();

        if (pItem->id() == mnCurPageId)
            ImplChangeTabPage(pItem->id(), 0);
    }
    else
    {
        pItem->mpTabPage = nullptr;
        queue_resize();
    }
}

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

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

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

    if ( !pItem || pItem->maText == rText )
        return;

    pItem->maText = rText;
    mbFormat = true;
    if( mpTabCtrlData->mpListBox )
    {
        sal_uInt16 nPos = GetPagePos( nPageId );
        mpTabCtrlData->mpListBox->RemoveEntry( nPos );
        mpTabCtrlData->mpListBox->InsertEntry( rText, nPos );
    }
    if ( IsUpdateMode() )
        Invalidate();
    CallEventListeners( VclEventId::TabpagePageTextChanged, reinterpret_cast<void*>(nPageId) );
}

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

    assert( pItem );

    return pItem->maText;
}

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

    assert( pItem );

    pItem->maHelpText = rText;
}

const OUString& TabControl::GetHelpText( sal_uInt16 nPageId ) const
{
    ImplTabItem* pItem = ImplGetItem( nPageId );
    assert( pItem );
    return pItem->maHelpText;
}

void TabControl::SetAccessibleName(sal_uInt16 nPageId, const OUString& rName)
{
    ImplTabItem* pItem = ImplGetItem( nPageId );
    assert( pItem );
    pItem->maAccessibleName = rName;
}

OUString TabControl::GetAccessibleName( sal_uInt16 nPageId ) const
{
    ImplTabItem* pItem = ImplGetItem( nPageId );
    assert( pItem );
    if (!pItem->maAccessibleName.isEmpty())
        return pItem->maAccessibleName;
    return removeMnemonicFromString(pItem->maText);
}

void TabControl::SetAccessibleDescription(sal_uInt16 nPageId, const OUString& rDesc)
{
    ImplTabItem* pItem = ImplGetItem( nPageId );
    assert( pItem );
    pItem->maAccessibleDescription = rDesc;
}

OUString TabControl::GetAccessibleDescription( sal_uInt16 nPageId ) const
{
    ImplTabItem* pItem = ImplGetItem( nPageId );
    assert( pItem );
    if (!pItem->maAccessibleDescription.isEmpty())
        return pItem->maAccessibleDescription;
    return pItem->maHelpText;
}

void TabControl::SetPageName( sal_uInt16 nPageId, const OUString& rName ) const
{
    ImplTabItem* pItem = ImplGetItem( nPageId );

    if ( pItem )
        pItem->maTabName = rName;
}

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

    if (pItem)
        return pItem->maTabName;

    return {};
}

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

    if ( pItem )
    {
        pItem->maTabImage = i_rImage;
        mbFormat = true;
        if ( IsUpdateMode() )
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=92 H=93 G=92

¤ Dauer der Verarbeitung: 0.31 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.