/*
 * libkysdk-qtwidgets's Library
 *
 * Copyright (C) 2023, KylinSoft Co., Ltd.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This library 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 for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this library.  If not, see <https://www.gnu.org/licenses/>.
 *
 * Authors: Zhenyu Wang <wangzhenyu@kylinos.cn>
 *
 */

// #include "gui_g.h"

// #include <QtWidgets/private/qtwidgetsglobal_p.h>
#include "kcustomtabbar.h"
// #include "private/qwidget_p.h"

#include <QDebug>
#include <QIcon>
#include <QToolButton>
// #if QT_CONFIG(animation)
#include <QVariantAnimation>
// #endif

#include <QStyleOption>

// #include "private/qlayoutengine_p.h"
// #if QT_CONFIG(itemviews)
// #include "qabstractitemdelegate.h"
// #endif

#include <QAbstractButton>
#include <QApplication>
#include <QBitmap>
#include <QCursor>
#include <QEvent>
#include <QPainter>
#include <QStyle>
#include <QStylePainter>
// #include <QCustomLayoutStruct>
#include <QAbstractItemDelegate>

// #if QT_CONFIG(tabwidget)
#include <QTabWidget>
// #endif
#include <QToolTip>
// #if QT_CONFIG(whatsthis)
#include <QWhatsThis>
// #endif
// #ifndef QT_NO_ACCESSIBILITY
#include <QAccessible>
// #endif
// #ifdef Q_OS_OSX
// #include <qpa/qplatformnativeinterface.h>
// #endif
#include <QDebug>
// #include "qdebug.h"
// #include "private/KCustomTabBar_p.h"

// #if 0 // Used to be included in Qt4 for Q_WS_MAC
// #include <private/qt_mac_p.h>
// #include <private/qt_cocoa_helpers_mac_p.h>
// #endif

#define ANIMATION_DURATION 250
typedef qint64 Fixed64;
static inline Fixed64 toFixed(int i)
{
    return (Fixed64)i * 256;
}
static inline int fRound(Fixed64 i)
{
    return (i % 256 < 128) ? i / 256 : 1 + i / 256;
}
struct QCustomLayoutStruct
{
    inline void init(int stretchFactor = 0, int minSize = 0)
    {
        stretch = stretchFactor;
        minimumSize = sizeHint = minSize;
        maximumSize = QLAYOUTSIZE_MAX;
        expansive = false;
        empty = true;
        spacing = 0;
    }

    int smartSizeHint()
    {
        return (stretch > 0) ? minimumSize : sizeHint;
    }
    int effectiveSpacer(int uniformSpacer) const
    {
        Q_ASSERT(uniformSpacer >= 0 || spacing >= 0);
        return (uniformSpacer >= 0) ? uniformSpacer : spacing;
    }

    // parameters
    int stretch;
    int sizeHint;
    int maximumSize;
    int minimumSize;
    int spacing;
    bool expansive;
    bool empty;

    // temporary storage
    bool done;

    // result
    int pos;
    int size;
};

class Q_DECL_HIDDEN KCustomMovableTabWidget : public QWidget
{
public:
    explicit KCustomMovableTabWidget(QWidget *parent = nullptr);
    void setPixmap(const QPixmap &pixmap);

protected:
    void paintEvent(QPaintEvent *e) override;

private:
    QPixmap m_pixmap;
};

class Q_DECL_HIDDEN KCustomTabBarPrivate
{
    Q_DECLARE_PUBLIC(KCustomTabBar)
public:
    KCustomTabBarPrivate(KCustomTabBar *parent);
    Q_WIDGETS_EXPORT void qGeomCalc(QVector<QCustomLayoutStruct> &chain, int start, int count,
                                    int pos, int space, int spacer = -1);

    int currentIndex;
    int pressedIndex;
    KCustomTabBar::Shape shape;
    bool layoutDirty;
    bool drawBase;
    int scrollOffset;

    struct Tab
    {
        inline Tab(const QIcon &ico, const QString &txt)
            : enabled(true)
            , shortcutId(0)
            , text(txt)
            , icon(ico)
            , leftWidget(0)
            , rightWidget(0)
            , lastTab(-1)
            , dragOffset(0)
#if QT_CONFIG(animation)
            , animation(0)
#endif // animation
        {
        }
        bool operator==(const Tab &other) const
        {
            return &other == this;
        }
        bool enabled;
        int shortcutId;
        QString text;
#ifndef QT_NO_TOOLTIP
        QString toolTip;
#endif
#if QT_CONFIG(whatsthis)
        QString whatsThis;
#endif
        QIcon icon;
        QRect rect;
        QRect minRect;
        QRect maxRect;

        QColor textColor;
        QVariant data;
        QWidget *leftWidget;
        QWidget *rightWidget;
        int lastTab;
        int dragOffset;
#ifndef QT_NO_ACCESSIBILITY
        QString accessibleName;
#endif

#if QT_CONFIG(animation)
        ~Tab()
        {
            delete animation;
        }
        struct TabBarAnimation : public QVariantAnimation
        {
            TabBarAnimation(Tab *t, KCustomTabBarPrivate *_priv)
                : tab(t)
                , priv(_priv)
            {
                setEasingCurve(QEasingCurve::InOutQuad);
            }

            void updateCurrentValue(const QVariant &current) override;

            void updateState(State newState, State) override;

        private:
            // these are needed for the callbacks
            Tab *tab;
            KCustomTabBarPrivate *priv;
        } *animation;

        void startAnimation(KCustomTabBarPrivate *priv, int duration)
        {
            if (!priv->isAnimated()) {
                priv->moveTabFinished(priv->tabList.indexOf(*this));
                return;
            }
            if (!animation)
                animation = new TabBarAnimation(this, priv);
            animation->setStartValue(dragOffset);
            animation->setEndValue(0);
            animation->setDuration(duration);
            animation->start();
        }
#else
        void startAnimation(KCustomTabBarPrivate *priv, int duration)
        {
            Q_UNUSED(duration);
            priv->moveTabFinished(priv->tabList.indexOf(*this));
        }
#endif // animation
    };
    QList<Tab> tabList;
    mutable QHash<QString, QSize> textSizes;

    int calculateNewPosition(int from, int to, int index) const;
    void slide(int from, int to);
    void init();

    Tab *at(int index);
    const Tab *at(int index) const;

    int indexAtPos(const QPoint &p) const;

    inline bool isAnimated() const
    {
        Q_Q(const KCustomTabBar);
        return q->style()->styleHint(QStyle::SH_Widget_Animation_Duration, 0, q) > 0;
    }
    inline bool validIndex(int index) const
    {
        return index >= 0 && index < tabList.count();
    }
    void setCurrentNextEnabledIndex(int offset);

    QToolButton *rightB; // right or bottom
    QToolButton *leftB; // left or top

    void moveTab(int index, int offset);
    void moveTabFinished(int index);
    QRect hoverRect;
    int hoverIndex;

    void refresh();
    void layoutTabs();
    void layoutWidgets(int start = 0);
    void layoutTab(int index);
    void updateMacBorderMetrics();
    bool isTabInMacUnifiedToolbarArea() const;
    void setupMovableTab();
    void autoHideTabs();
    QRect normalizedScrollRect(int index = -1);
    int hoveredTabIndex() const;

    void initBasicStyleOption(QStyleOptionTab *option, int tabIndex) const;

    void makeVisible(int index);
    QSize iconSize;
    Qt::TextElideMode elideMode;
    bool elideModeSetByUser;
    bool useScrollButtons;
    bool useScrollButtonsSetByUser;

    bool expanding;
    bool closeButtonOnTabs;
    KCustomTabBar::SelectionBehavior selectionBehaviorOnRemove;

    QPoint dragStartPosition;
    bool paintWithOffsets;
    bool movable;
    bool dragInProgress;
    bool documentMode;
    bool autoHide;
    bool changeCurrentOnDrag;

    int switchTabCurrentIndex;
    int switchTabTimerId;

    KCustomMovableTabWidget *movingTab;

    // shared by tabwidget and KCustomTabBar
    static void initStyleBaseOption(QStyleOptionTabBarBase *optTabBase, KCustomTabBar *tabbar, QSize size)
    {
        QStyleOptionTab tabOverlap;
        tabOverlap.shape = static_cast<const QTabBar::Shape>(tabbar->shape());
        int overlap = tabbar->style()->pixelMetric(QStyle::PM_TabBarBaseOverlap, &tabOverlap, tabbar);
        QWidget *theParent = tabbar->parentWidget();
        optTabBase->init(tabbar);
        optTabBase->shape = static_cast<const QTabBar::Shape>(tabbar->shape());
        optTabBase->documentMode = tabbar->documentMode();
        if (theParent && overlap > 0) {
            QRect rect;
            switch (tabOverlap.shape) {
            case KCustomTabBar::RoundedNorth:
            case KCustomTabBar::TriangularNorth:
                rect.setRect(0, size.height() - overlap, size.width(), overlap);
                break;
            case KCustomTabBar::RoundedSouth:
            case KCustomTabBar::TriangularSouth:
                rect.setRect(0, 0, size.width(), overlap);
                break;
            case KCustomTabBar::RoundedEast:
            case KCustomTabBar::TriangularEast:
                rect.setRect(0, 0, overlap, size.height());
                break;
            case KCustomTabBar::RoundedWest:
            case KCustomTabBar::TriangularWest:
                rect.setRect(size.width() - overlap, 0, overlap, size.height());
                break;
            }
            optTabBase->rect = rect;
        }
    }

    void killSwitchTabTimer();

private:
    KCustomTabBar *const q_ptr;
};

namespace
{
class Q_DECL_HIDDEN CloseButton : public QAbstractButton
{
    Q_OBJECT

public:
    explicit CloseButton(QWidget *parent = 0);

    QSize sizeHint() const override;
    QSize minimumSizeHint() const override;
    void enterEvent(QEvent *event) override;
    void leaveEvent(QEvent *event) override;
    void paintEvent(QPaintEvent *event) override;
};
}

KCustomMovableTabWidget::KCustomMovableTabWidget(QWidget *parent)
    : QWidget(parent)
{
}

void KCustomMovableTabWidget::setPixmap(const QPixmap &pixmap)
{
    m_pixmap = pixmap;
    update();
}

void KCustomMovableTabWidget::paintEvent(QPaintEvent *e)
{
    Q_UNUSED(e);
    QPainter p(this);
    p.drawPixmap(0, 0, m_pixmap);
}

inline static bool verticalTabs(KCustomTabBar::Shape shape)
{
    return shape == KCustomTabBar::RoundedWest
        || shape == KCustomTabBar::RoundedEast
        || shape == KCustomTabBar::TriangularWest
        || shape == KCustomTabBar::TriangularEast;
}

void KCustomTabBarPrivate::updateMacBorderMetrics()
{
#if defined(Q_OS_OSX)
    Q_Q(KCustomTabBar);
    // Extend the unified title and toolbar area to cover the tab bar iff
    // 1) the tab bar is in document mode
    // 2) the tab bar is directly below an "unified" area.
    // The extending itself is done in the Cocoa platform plugin and Mac style,
    // this function registers geometry and visibility state for the tab bar.

    // Calculate geometry
    int upper, lower;
    if (documentMode) {
        QPoint windowPos = q->mapTo(q->window(), QPoint(0, 0));
        upper = windowPos.y();
        int tabStripHeight = q->tabSizeHint(0).height();
        int pixelTweak = -3;
        lower = upper + tabStripHeight + pixelTweak;
    } else {
        upper = 0;
        lower = 0;
    }

    QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface();
    quintptr identifier = reinterpret_cast<quintptr>(q);

    // Set geometry
    QPlatformNativeInterface::NativeResourceForIntegrationFunction function =
        nativeInterface->nativeResourceFunctionForIntegration("registerContentBorderArea");
    if (!function)
        return; // Not Cocoa platform plugin.
    typedef void (*RegisterContentBorderAreaFunction)(QWindow *window, quintptr identifier, int upper, int lower);
    (reinterpret_cast<RegisterContentBorderAreaFunction>(function))(q->window()->windowHandle(), identifier, upper, lower);

    // Set visibility state
    function = nativeInterface->nativeResourceFunctionForIntegration("setContentBorderAreaEnabled");
    if (!function)
        return;
    typedef void (*SetContentBorderAreaEnabledFunction)(QWindow *window, quintptr identifier, bool enable);
    (reinterpret_cast<SetContentBorderAreaEnabledFunction>(function))(q->window()->windowHandle(), identifier, q->isVisible());
#endif
}

/*!
    \internal
    This is basically KCustomTabBar::initStyleOption() but
    without the expensive QFontMetrics::elidedText() call.
*/

void KCustomTabBarPrivate::initBasicStyleOption(QStyleOptionTab *option, int tabIndex) const
{
    Q_Q(const KCustomTabBar);
    const int totalTabs = tabList.size();

    if (!option || (tabIndex < 0 || tabIndex >= totalTabs))
        return;

    const KCustomTabBarPrivate::Tab &tab = tabList.at(tabIndex);
    option->initFrom(q);
    option->state &= ~(QStyle::State_HasFocus | QStyle::State_MouseOver);
    option->rect = q->tabRect(tabIndex);
    const bool isCurrent = tabIndex == currentIndex;
    option->row = 0;
    if (tabIndex == pressedIndex)
        option->state |= QStyle::State_Sunken;
    if (isCurrent)
        option->state |= QStyle::State_Selected;
    if (isCurrent && q->hasFocus())
        option->state |= QStyle::State_HasFocus;
    if (!tab.enabled)
        option->state &= ~QStyle::State_Enabled;
    if (q->isActiveWindow())
        option->state |= QStyle::State_Active;
    if (!dragInProgress && option->rect == hoverRect)
        option->state |= QStyle::State_MouseOver;
    option->shape = static_cast<const QTabBar::Shape>(shape);
    option->text = tab.text;

    if (tab.textColor.isValid())
        option->palette.setColor(q->foregroundRole(), tab.textColor);
    option->icon = tab.icon;
    option->iconSize = q->iconSize(); // Will get the default value then.

    option->leftButtonSize = tab.leftWidget ? tab.leftWidget->size() : QSize();
    option->rightButtonSize = tab.rightWidget ? tab.rightWidget->size() : QSize();
    option->documentMode = documentMode;

    if (tabIndex > 0 && tabIndex - 1 == currentIndex)
        option->selectedPosition = QStyleOptionTab::PreviousIsSelected;
    else if (tabIndex + 1 < totalTabs && tabIndex + 1 == currentIndex)
        option->selectedPosition = QStyleOptionTab::NextIsSelected;
    else
        option->selectedPosition = QStyleOptionTab::NotAdjacent;

    const bool paintBeginning = (tabIndex == 0) || (dragInProgress && tabIndex == pressedIndex + 1);
    const bool paintEnd = (tabIndex == totalTabs - 1) || (dragInProgress && tabIndex == pressedIndex - 1);
    if (paintBeginning) {
        if (paintEnd)
            option->position = QStyleOptionTab::OnlyOneTab;
        else
            option->position = QStyleOptionTab::Beginning;
    } else if (paintEnd) {
        option->position = QStyleOptionTab::End;
    } else {
        option->position = QStyleOptionTab::Middle;
    }

#if QT_CONFIG(tabwidget)
    if (const QTabWidget *tw = qobject_cast<const QTabWidget *>(q->parentWidget())) {
        option->features |= QStyleOptionTab::HasFrame;
        if (tw->cornerWidget(Qt::TopLeftCorner) || tw->cornerWidget(Qt::BottomLeftCorner))
            option->cornerWidgets |= QStyleOptionTab::LeftCornerWidget;
        if (tw->cornerWidget(Qt::TopRightCorner) || tw->cornerWidget(Qt::BottomRightCorner))
            option->cornerWidgets |= QStyleOptionTab::RightCornerWidget;
    }
#endif
}

/*!
    Initialize \a option with the values from the tab at \a tabIndex. This method
    is useful for subclasses when they need a QStyleOptionTab,
    but don't want to fill in all the information themselves.

    \sa QStyleOption::initFrom(), QTabWidget::initStyleOption()
*/
void KCustomTabBar::initStyleOption(QStyleOptionTab *option, int tabIndex) const
{
    Q_D(const KCustomTabBar);
    d->initBasicStyleOption(option, tabIndex);

    QRect textRect = style()->subElementRect(QStyle::SE_TabBarTabText, option, this);

    option->text = fontMetrics().elidedText(option->text, d->elideMode, textRect.width(),
                                            Qt::TextShowMnemonic);
}

void KCustomTabBarPrivate::init()
{
    Q_Q(KCustomTabBar);
    leftB = new QToolButton(q);
    leftB->setAutoRepeat(true);
    //    QObject::connect(leftB, SIGNAL(clicked()), q, SLOT(scrollTabs()));
    QObject::connect(leftB, &QToolButton::clicked, q, [=] {
        q->leftScrollTabs();
    });
    leftB->hide();
    rightB = new QToolButton(q);
    rightB->setAutoRepeat(true);
    //    QObject::connect(rightB, SIGNAL(clicked()), q, SLOT(scrollTabs()));
    QObject::connect(rightB, &QToolButton::clicked, q, [=] {
        q->rightScrollTabs();
    });
    rightB->hide();
#ifdef QT_KEYPAD_NAVIGATION
    if (QApplication::keypadNavigationEnabled()) {
        leftB->setFocusPolicy(Qt::NoFocus);
        rightB->setFocusPolicy(Qt::NoFocus);
        q->setFocusPolicy(Qt::NoFocus);
    } else
#endif
        q->setFocusPolicy(Qt::TabFocus);

#ifndef QT_NO_ACCESSIBILITY
    leftB->setAccessibleName(KCustomTabBar::tr("Scroll Left"));
    rightB->setAccessibleName(KCustomTabBar::tr("Scroll Right"));
#endif
    q->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
    elideMode = Qt::TextElideMode(q->style()->styleHint(QStyle::SH_TabBar_ElideMode, 0, q));
    useScrollButtons = !q->style()->styleHint(QStyle::SH_TabBar_PreferNoArrows, 0, q);
}

KCustomTabBarPrivate::Tab *KCustomTabBarPrivate::at(int index)
{
    return validIndex(index) ? &tabList[index] : 0;
}

const KCustomTabBarPrivate::Tab *KCustomTabBarPrivate::at(int index) const
{
    return validIndex(index) ? &tabList[index] : 0;
}

int KCustomTabBarPrivate::indexAtPos(const QPoint &p) const
{
    Q_Q(const KCustomTabBar);
    if (q->tabRect(currentIndex).contains(p))
        return currentIndex;
    for (int i = 0; i < tabList.count(); ++i)
        if (tabList.at(i).enabled && q->tabRect(i).contains(p))
            return i;
    return -1;
}

void KCustomTabBarPrivate::layoutTabs()
{
    Q_Q(KCustomTabBar);
    layoutDirty = false;
    QSize size = q->size();
    int last, available;
    int maxExtent;
    int i;
    bool vertTabs = verticalTabs(shape);
    int tabChainIndex = 0;

    Qt::Alignment tabAlignment = Qt::Alignment(q->style()->styleHint(QStyle::SH_TabBar_Alignment, 0, q));
    QVector<QCustomLayoutStruct> tabChain(tabList.count() + 2);

    // We put an empty item at the front and back and set its expansive attribute
    // depending on tabAlignment and expanding.
    tabChain[tabChainIndex].init();
    tabChain[tabChainIndex].expansive = (!expanding)
        && (tabAlignment != Qt::AlignLeft)
        && (tabAlignment != Qt::AlignJustify);
    tabChain[tabChainIndex].empty = true;
    ++tabChainIndex;

    // We now go through our list of tabs and set the minimum size and the size hint
    // This will allow us to elide text if necessary. Since we don't set
    // a maximum size, tabs will EXPAND to fill up the empty space.
    // Since tab widget is rather *ahem* strict about keeping the geometry of the
    // tab bar to its absolute minimum, this won't bleed through, but will show up
    // if you use tab bar on its own (a.k.a. not a bug, but a feature).
    // Update: if expanding is false, we DO set a maximum size to prevent the tabs
    // being wider than necessary.
    if (!vertTabs) {
        int minx = 0;
        int x = 0;
        int maxHeight = 0;
        for (i = 0; i < tabList.count(); ++i, ++tabChainIndex) {
            QSize sz = q->tabSizeHint(i);
            tabList[i].maxRect = QRect(x, 0, sz.width(), sz.height());
            x += sz.width();
            maxHeight = qMax(maxHeight, sz.height());
            sz = q->minimumTabSizeHint(i);
            tabList[i].minRect = QRect(minx, 0, sz.width(), sz.height());
            minx += sz.width();
            tabChain[tabChainIndex].init();
            tabChain[tabChainIndex].sizeHint = tabList.at(i).maxRect.width();
            tabChain[tabChainIndex].minimumSize = sz.width();
            tabChain[tabChainIndex].empty = false;
            tabChain[tabChainIndex].expansive = true;

            if (!expanding)
                tabChain[tabChainIndex].maximumSize = tabChain[tabChainIndex].sizeHint;
        }

        last = minx;
        available = size.width();
        maxExtent = maxHeight;
    } else {
        int miny = 0;
        int y = 0;
        int maxWidth = 0;
        for (i = 0; i < tabList.count(); ++i, ++tabChainIndex) {
            QSize sz = q->tabSizeHint(i);
            tabList[i].maxRect = QRect(0, y, sz.width(), sz.height());
            y += sz.height();
            maxWidth = qMax(maxWidth, sz.width());
            sz = q->minimumTabSizeHint(i);
            tabList[i].minRect = QRect(0, miny, sz.width(), sz.height());
            miny += sz.height();
            tabChain[tabChainIndex].init();
            tabChain[tabChainIndex].sizeHint = tabList.at(i).maxRect.height();
            tabChain[tabChainIndex].minimumSize = sz.height();
            tabChain[tabChainIndex].empty = false;
            tabChain[tabChainIndex].expansive = true;

            if (!expanding)
                tabChain[tabChainIndex].maximumSize = tabChain[tabChainIndex].sizeHint;
        }

        last = miny;
        available = size.height();
        maxExtent = maxWidth;
    }

    // Mirror our front item.
    tabChain[tabChainIndex].init();
    tabChain[tabChainIndex].expansive = (!expanding)
        && (tabAlignment != Qt::AlignRight)
        && (tabAlignment != Qt::AlignJustify);
    tabChain[tabChainIndex].empty = true;
    Q_ASSERT(tabChainIndex == tabChain.count() - 1); // add an assert just to make sure.

    // Do the calculation
    qGeomCalc(tabChain, 0, tabChain.count(), 0, qMax(available, last), 0);

    // Use the results
    for (i = 0; i < tabList.count(); ++i) {
        const QCustomLayoutStruct &lstruct = tabChain.at(i + 1);
        if (!vertTabs)
            tabList[i].rect.setRect(lstruct.pos, 0, lstruct.size, maxExtent);
        else
            tabList[i].rect.setRect(0, lstruct.pos, maxExtent, lstruct.size);
    }

    if (useScrollButtons && tabList.count() && last > available) {
        const QRect scrollRect = normalizedScrollRect(0);
        scrollOffset = -scrollRect.left();

        Q_Q(KCustomTabBar);
        QStyleOption opt;
        opt.init(q);
        QRect scrollButtonLeftRect = q->style()->subElementRect(QStyle::SE_TabBarScrollLeftButton, &opt, q);
        QRect scrollButtonRightRect = q->style()->subElementRect(QStyle::SE_TabBarScrollRightButton, &opt, q);
        int scrollButtonWidth = q->style()->pixelMetric(QStyle::PM_TabBarScrollButtonWidth, &opt, q);

        // Normally SE_TabBarScrollLeftButton should have the same width as PM_TabBarScrollButtonWidth.
        // But if that is not the case, we set the actual button width to PM_TabBarScrollButtonWidth, and
        // use the extra space from SE_TabBarScrollLeftButton as margins towards the tabs.
        if (vertTabs) {
            scrollButtonLeftRect.setHeight(scrollButtonWidth);
            scrollButtonRightRect.setY(scrollButtonRightRect.bottom() + 1 - scrollButtonWidth);
            scrollButtonRightRect.setHeight(scrollButtonWidth);
            leftB->setArrowType(Qt::UpArrow);
            rightB->setArrowType(Qt::DownArrow);
        } else if (q->layoutDirection() == Qt::RightToLeft) {
            scrollButtonRightRect.setWidth(scrollButtonWidth);
            scrollButtonLeftRect.setX(scrollButtonLeftRect.right() + 1 - scrollButtonWidth);
            scrollButtonLeftRect.setWidth(scrollButtonWidth);
            leftB->setArrowType(Qt::RightArrow);
            rightB->setArrowType(Qt::LeftArrow);
        } else {
            scrollButtonLeftRect.setWidth(scrollButtonWidth);
            scrollButtonRightRect.setX(scrollButtonRightRect.right() + 1 - scrollButtonWidth);
            scrollButtonRightRect.setWidth(scrollButtonWidth);
            leftB->setArrowType(Qt::LeftArrow);
            rightB->setArrowType(Qt::RightArrow);
        }

        leftB->setGeometry(scrollButtonLeftRect);
        leftB->setEnabled(false);
        leftB->show();

        rightB->setGeometry(scrollButtonRightRect);
        rightB->setEnabled(last - scrollOffset > scrollRect.x() + scrollRect.width());
        rightB->show();
    } else {
        scrollOffset = 0;
        rightB->hide();
        leftB->hide();
    }

    layoutWidgets();
    q->tabLayoutChange();
}

QRect KCustomTabBarPrivate::normalizedScrollRect(int index)
{
    // "Normalized scroll rect" means return the free space on the tab bar
    // that doesn't overlap with scroll buttons or tear indicators, and
    // always return the rect as horizontal Qt::LeftToRight, even if the
    // tab bar itself is in a different orientation.

    Q_Q(KCustomTabBar);
    QStyleOptionTab opt;
    q->initStyleOption(&opt, currentIndex);
    opt.rect = q->rect();

    QRect scrollButtonLeftRect = q->style()->subElementRect(QStyle::SE_TabBarScrollLeftButton, &opt, q);
    QRect scrollButtonRightRect = q->style()->subElementRect(QStyle::SE_TabBarScrollRightButton, &opt, q);
    QRect tearLeftRect = q->style()->subElementRect(QStyle::SE_TabBarTearIndicatorLeft, &opt, q);
    QRect tearRightRect = q->style()->subElementRect(QStyle::SE_TabBarTearIndicatorRight, &opt, q);

    if (verticalTabs(shape)) {
        int topEdge, bottomEdge;
        bool leftButtonIsOnTop = scrollButtonLeftRect.y() < q->height() / 2;
        bool rightButtonIsOnTop = scrollButtonRightRect.y() < q->height() / 2;

        if (leftButtonIsOnTop && rightButtonIsOnTop) {
            topEdge = scrollButtonRightRect.bottom() + 1;
            bottomEdge = q->height();
        } else if (!leftButtonIsOnTop && !rightButtonIsOnTop) {
            topEdge = 0;
            bottomEdge = scrollButtonLeftRect.top();
        } else {
            topEdge = scrollButtonLeftRect.bottom() + 1;
            bottomEdge = scrollButtonRightRect.top();
        }

        bool tearTopVisible = index != 0 && topEdge != -scrollOffset;
        bool tearBottomVisible = index != tabList.size() - 1 && bottomEdge != tabList.constLast().rect.bottom() + 1 - scrollOffset;
        if (tearTopVisible && !tearLeftRect.isNull())
            topEdge = tearLeftRect.bottom() + 1;
        if (tearBottomVisible && !tearRightRect.isNull())
            bottomEdge = tearRightRect.top();

        return QRect(topEdge, 0, bottomEdge - topEdge, q->height());
    } else {
        if (q->layoutDirection() == Qt::RightToLeft) {
            scrollButtonLeftRect = QStyle::visualRect(Qt::RightToLeft, q->rect(), scrollButtonLeftRect);
            scrollButtonRightRect = QStyle::visualRect(Qt::RightToLeft, q->rect(), scrollButtonRightRect);
            tearLeftRect = QStyle::visualRect(Qt::RightToLeft, q->rect(), tearLeftRect);
            tearRightRect = QStyle::visualRect(Qt::RightToLeft, q->rect(), tearRightRect);
        }

        int leftEdge, rightEdge;
        bool leftButtonIsOnLeftSide = scrollButtonLeftRect.x() < q->width() / 2;
        bool rightButtonIsOnLeftSide = scrollButtonRightRect.x() < q->width() / 2;

        if (leftButtonIsOnLeftSide && rightButtonIsOnLeftSide) {
            leftEdge = scrollButtonRightRect.right() + 1;
            rightEdge = q->width();
        } else if (!leftButtonIsOnLeftSide && !rightButtonIsOnLeftSide) {
            leftEdge = 0;
            rightEdge = scrollButtonLeftRect.left();
        } else {
            leftEdge = scrollButtonLeftRect.right() + 1;
            rightEdge = scrollButtonRightRect.left();
        }

        bool tearLeftVisible = index != 0 && leftEdge != -scrollOffset;
        bool tearRightVisible = index != tabList.size() - 1 && rightEdge != tabList.constLast().rect.right() + 1 - scrollOffset;
        if (tearLeftVisible && !tearLeftRect.isNull())
            leftEdge = tearLeftRect.right() + 1;
        if (tearRightVisible && !tearRightRect.isNull())
            rightEdge = tearRightRect.left();

        return QRect(leftEdge, 0, rightEdge - leftEdge, q->height());
    }
}

int KCustomTabBarPrivate::hoveredTabIndex() const
{
    if (dragInProgress)
        return currentIndex;
    if (hoverIndex >= 0)
        return hoverIndex;
    return -1;
}

void KCustomTabBarPrivate::makeVisible(int index)
{
    Q_Q(KCustomTabBar);
    if (!validIndex(index) || leftB->isHidden())
        return;

    const QRect tabRect = tabList.at(index).rect;
    const int oldScrollOffset = scrollOffset;
    const bool horiz = !verticalTabs(shape);
    const int tabStart = horiz ? tabRect.left() : tabRect.top();
    const int tabEnd = horiz ? tabRect.right() : tabRect.bottom();
    const int lastTabEnd = horiz ? tabList.constLast().rect.right() : tabList.constLast().rect.bottom();
    const QRect scrollRect = normalizedScrollRect(index);
    const int scrolledTabBarStart = qMax(1, scrollRect.left() + scrollOffset);
    const int scrolledTabBarEnd = qMin(lastTabEnd - 1, scrollRect.right() + scrollOffset);

    if (tabStart < scrolledTabBarStart) {
        // Tab is outside on the left, so scroll left.
        scrollOffset = tabStart - scrollRect.left();
    } else if (tabEnd > scrolledTabBarEnd) {
        // Tab is outside on the right, so scroll right.
        scrollOffset = tabEnd - scrollRect.right();
    }

    leftB->setEnabled(scrollOffset > -scrollRect.left());
    emit q->leftBStatus(scrollOffset > -scrollRect.left());
    rightB->setEnabled(scrollOffset < lastTabEnd - scrollRect.right());
    emit q->rightBStatus(scrollOffset < lastTabEnd - scrollRect.right());

    if (oldScrollOffset != scrollOffset) {
        q->update();
        layoutWidgets();
    }
}

void KCustomTabBarPrivate::killSwitchTabTimer()
{
    Q_Q(KCustomTabBar);
    if (switchTabTimerId) {
        q->killTimer(switchTabTimerId);
        switchTabTimerId = 0;
    }
    switchTabCurrentIndex = -1;
}

void KCustomTabBarPrivate::layoutTab(int index)
{
    Q_Q(KCustomTabBar);
    Q_ASSERT(index >= 0);

    Tab &tab = tabList[index];
    bool vertical = verticalTabs(shape);
    if (!(tab.leftWidget || tab.rightWidget))
        return;

    QStyleOptionTab opt;
    q->initStyleOption(&opt, index);
    if (tab.leftWidget) {
        QRect rect = q->style()->subElementRect(QStyle::SE_TabBarTabLeftButton, &opt, q);
        QPoint p = rect.topLeft();
        if ((index == pressedIndex) || paintWithOffsets) {
            if (vertical)
                p.setY(p.y() + tabList[index].dragOffset);
            else
                p.setX(p.x() + tabList[index].dragOffset);
        }
        tab.leftWidget->move(p);
    }
    if (tab.rightWidget) {
        QRect rect = q->style()->subElementRect(QStyle::SE_TabBarTabRightButton, &opt, q);
        QPoint p = rect.topLeft();
        if ((index == pressedIndex) || paintWithOffsets) {
            if (vertical)
                p.setY(p.y() + tab.dragOffset);
            else
                p.setX(p.x() + tab.dragOffset);
        }
        tab.rightWidget->move(p);
    }
}

void KCustomTabBarPrivate::layoutWidgets(int start)
{
    Q_Q(KCustomTabBar);
    for (int i = start; i < q->count(); ++i) {
        layoutTab(i);
    }
}

void KCustomTabBarPrivate::autoHideTabs()
{
    Q_Q(KCustomTabBar);

    if (autoHide)
        q->setVisible(q->count() > 1);
}

void KCustomTabBar::closeTab()
{
    Q_D(KCustomTabBar);
    QObject *object = sender();
    int tabToClose = -1;
    KCustomTabBar::ButtonPosition closeSide = (KCustomTabBar::ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, 0, this);
    for (int i = 0; i < d->tabList.count(); ++i) {
        if (closeSide == KCustomTabBar::LeftSide) {
            if (d->tabList.at(i).leftWidget == object) {
                tabToClose = i;
                break;
            }
        } else {
            if (d->tabList.at(i).rightWidget == object) {
                tabToClose = i;
                break;
            }
        }
    }
    if (tabToClose != -1)
        emit tabCloseRequested(tabToClose);
}

// void KCustomTabBar::scrollTabs()
//{

//    if (sender1 == d->leftB) {

//    } else if (sender1 == d->rightB) {

//    }
//}

void KCustomTabBar::leftScrollTabs()
{
    Q_D(KCustomTabBar);
    //    const QObject *sender1 = sender();

    const bool horizontal = !verticalTabs(d->shape);
    const QRect scrollRect = d->normalizedScrollRect();
    int i = -1;
    for (i = d->tabList.count() - 1; i >= 0; --i) {

        int start = horizontal ? d->tabList.at(i).rect.left() : d->tabList.at(i).rect.top();
        if (start < scrollRect.left() + d->scrollOffset) {
            d->makeVisible(i);
            return;
        }
    }
}

void KCustomTabBar::rightScrollTabs()
{
    Q_D(KCustomTabBar);

    //    const QObject *sender1 = sender();
    const bool horizontal = !verticalTabs(d->shape);
    const QRect scrollRect = d->normalizedScrollRect();
    int i = -1;
    for (i = 0; i < d->tabList.count(); ++i) {

        int end = horizontal ? d->tabList.at(i).rect.right() : d->tabList.at(i).rect.bottom();

        if (end > scrollRect.right() + d->scrollOffset) {
            d->makeVisible(i);
            return;
        }
    }
}

void KCustomTabBarPrivate::refresh()
{
    Q_Q(KCustomTabBar);

    // be safe in case a subclass is also handling move with the tabs
    if (pressedIndex != -1
        && movable
        && QApplication::mouseButtons() == Qt::NoButton) {
        moveTabFinished(pressedIndex);
        if (!validIndex(pressedIndex))
            pressedIndex = -1;
    }

    if (!q->isVisible()) {
        layoutDirty = true;
    } else {
        layoutTabs();
        makeVisible(currentIndex);
        q->update();
        q->updateGeometry();
    }
}

/*!
    Creates a new tab bar with the given \a parent.
*/
KCustomTabBar::KCustomTabBar(QWidget *parent)
    : QWidget(parent)
    , d_ptr(new KCustomTabBarPrivate(this))
{
    Q_D(KCustomTabBar);
    d->init();
}

/*!
    Destroys the tab bar.
*/
KCustomTabBar::~KCustomTabBar()
{
}

/*!
    \property KCustomTabBar::shape
    \brief the shape of the tabs in the tab bar

    Possible values for this property are described by the Shape enum.
*/

KCustomTabBar::Shape KCustomTabBar::shape() const
{
    Q_D(const KCustomTabBar);
    return d->shape;
}

void KCustomTabBar::setShape(Shape shape)
{
    Q_D(KCustomTabBar);
    if (d->shape == shape)
        return;
    d->shape = shape;
    d->refresh();
}

/*!
    \property KCustomTabBar::drawBase
    \brief defines whether or not tab bar should draw its base.

    If true then KCustomTabBar draws a base in relation to the styles overlab.
    Otherwise only the tabs are drawn.

    \sa QStyle::pixelMetric(), QStyle::PM_TabBarBaseOverlap, QStyleOptionTabBarBase
*/

void KCustomTabBar::setDrawBase(bool drawBase)
{
    Q_D(KCustomTabBar);
    if (d->drawBase == drawBase)
        return;
    d->drawBase = drawBase;
    update();
}

bool KCustomTabBar::drawBase() const
{
    Q_D(const KCustomTabBar);
    return d->drawBase;
}

/*!
    Adds a new tab with text \a text. Returns the new
    tab's index.
*/
int KCustomTabBar::addTab(const QString &text)
{
    return insertTab(-1, text);
}

/*!
    \overload

    Adds a new tab with icon \a icon and text \a
    text. Returns the new tab's index.
*/
int KCustomTabBar::addTab(const QIcon &icon, const QString &text)
{
    return insertTab(-1, icon, text);
}

/*!
    Inserts a new tab with text \a text at position \a index. If \a
    index is out of range, the new tab is appened. Returns the new
    tab's index.
*/
int KCustomTabBar::insertTab(int index, const QString &text)
{
    return insertTab(index, QIcon(), text);
}

/*!\overload

    Inserts a new tab with icon \a icon and text \a text at position
    \a index. If \a index is out of range, the new tab is
    appended. Returns the new tab's index.

    If the KCustomTabBar was empty before this function is called, the inserted tab
    becomes the current tab.

    Inserting a new tab at an index less than or equal to the current index
    will increment the current index, but keep the current tab.
*/
int KCustomTabBar::insertTab(int index, const QIcon &icon, const QString &text)
{
    Q_D(KCustomTabBar);
    if (!d->validIndex(index)) {
        index = d->tabList.count();
        d->tabList.append(KCustomTabBarPrivate::Tab(icon, text));
    } else {
        d->tabList.insert(index, KCustomTabBarPrivate::Tab(icon, text));
    }
#ifndef QT_NO_SHORTCUT
    d->tabList[index].shortcutId = grabShortcut(QKeySequence::mnemonic(text));
#endif
    d->refresh();
    if (d->tabList.count() == 1)
        setCurrentIndex(index);
    else if (index <= d->currentIndex)
        ++d->currentIndex;

    if (d->closeButtonOnTabs) {
        QStyleOptionTab opt;
        initStyleOption(&opt, index);
        ButtonPosition closeSide = (ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, 0, this);
        QAbstractButton *closeButton = new CloseButton(this);
        //        connect(closeButton, SIGNAL(clicked()), this, SLOT(_q_closeTab()));
        connect(closeButton, &QAbstractButton::clicked, this, [=] {
            closeTab();
        });
        setTabButton(index, closeSide, closeButton);
    }

    for (int i = 0; i < d->tabList.count(); ++i) {
        if (d->tabList[i].lastTab >= index)
            ++d->tabList[i].lastTab;
    }

    tabInserted(index);
    d->autoHideTabs();

    return index;
}

/*!
    Removes the tab at position \a index.

    \sa SelectionBehavior
 */
void KCustomTabBar::removeTab(int index)
{
    Q_D(KCustomTabBar);
    if (d->validIndex(index)) {
        if (d->dragInProgress)
            d->moveTabFinished(d->pressedIndex);

#ifndef QT_NO_SHORTCUT
        releaseShortcut(d->tabList.at(index).shortcutId);
#endif
        if (d->tabList[index].leftWidget) {
            d->tabList[index].leftWidget->hide();
            d->tabList[index].leftWidget->deleteLater();
            d->tabList[index].leftWidget = 0;
        }
        if (d->tabList[index].rightWidget) {
            d->tabList[index].rightWidget->hide();
            d->tabList[index].rightWidget->deleteLater();
            d->tabList[index].rightWidget = 0;
        }

        int newIndex = d->tabList[index].lastTab;
        d->tabList.removeAt(index);
        for (int i = 0; i < d->tabList.count(); ++i) {
            if (d->tabList[i].lastTab == index)
                d->tabList[i].lastTab = -1;
            if (d->tabList[i].lastTab > index)
                --d->tabList[i].lastTab;
        }
        if (index == d->currentIndex) {
            // The current tab is going away, in order to make sure
            // we emit that "current has changed", we need to reset this
            // around.
            d->currentIndex = -1;
            if (d->tabList.size() > 0) {
                switch (d->selectionBehaviorOnRemove) {
                case SelectPreviousTab:
                    if (newIndex > index)
                        newIndex--;
                    if (d->validIndex(newIndex))
                        break;
                    Q_FALLTHROUGH();
                case SelectRightTab:
                    newIndex = index;
                    if (newIndex >= d->tabList.size())
                        newIndex = d->tabList.size() - 1;
                    break;
                case SelectLeftTab:
                    newIndex = index - 1;
                    if (newIndex < 0)
                        newIndex = 0;
                    break;
                default:
                    break;
                }

                if (d->validIndex(newIndex)) {
                    // don't loose newIndex's old through setCurrentIndex
                    int bump = d->tabList[newIndex].lastTab;
                    setCurrentIndex(newIndex);
                    d->tabList[newIndex].lastTab = bump;
                }
            } else {
                emit currentChanged(-1);
            }
        } else if (index < d->currentIndex) {
            setCurrentIndex(d->currentIndex - 1);
        }
        d->refresh();
        d->autoHideTabs();
        if (!d->hoverRect.isEmpty()) {
            for (int i = 0; i < d->tabList.count(); ++i) {
                const QRect area = tabRect(i);
                if (area.contains(mapFromGlobal(QCursor::pos()))) {
                    d->hoverIndex = i;
                    d->hoverRect = area;
                    break;
                }
            }
            update(d->hoverRect);
        }
        tabRemoved(index);
    }
}

/*!
    Returns \c true if the tab at position \a index is enabled; otherwise
    returns \c false.
*/
bool KCustomTabBar::isTabEnabled(int index) const
{
    Q_D(const KCustomTabBar);
    if (const KCustomTabBarPrivate::Tab *tab = d->at(index))
        return tab->enabled;
    return false;
}

/*!
    If \a enabled is true then the tab at position \a index is
    enabled; otherwise the item at position \a index is disabled.
*/
void KCustomTabBar::setTabEnabled(int index, bool enabled)
{
    Q_D(KCustomTabBar);
    if (KCustomTabBarPrivate::Tab *tab = d->at(index)) {
        tab->enabled = enabled;
#ifndef QT_NO_SHORTCUT
        setShortcutEnabled(tab->shortcutId, enabled);
#endif
        update();
        if (!enabled && index == d->currentIndex)
            setCurrentIndex(d->validIndex(index + 1) ? index + 1 : 0);
        else if (enabled && !d->validIndex(d->currentIndex))
            setCurrentIndex(index);
    }
}

/*!
    Returns the text of the tab at position \a index, or an empty
    string if \a index is out of range.
*/
QString KCustomTabBar::tabText(int index) const
{
    Q_D(const KCustomTabBar);
    if (const KCustomTabBarPrivate::Tab *tab = d->at(index))
        return tab->text;
    return QString();
}

/*!
    Sets the text of the tab at position \a index to \a text.
*/
void KCustomTabBar::setTabText(int index, const QString &text)
{
    Q_D(KCustomTabBar);
    if (KCustomTabBarPrivate::Tab *tab = d->at(index)) {
        d->textSizes.remove(tab->text);
        tab->text = text;
#ifndef QT_NO_SHORTCUT
        releaseShortcut(tab->shortcutId);
        tab->shortcutId = grabShortcut(QKeySequence::mnemonic(text));
        setShortcutEnabled(tab->shortcutId, tab->enabled);
#endif
        d->refresh();
    }
}

void KCustomTabBar::setTabContent(int index, const QString &text)
{
    Q_D(KCustomTabBar);
    if (KCustomTabBarPrivate::Tab *tab = d->at(index)) {
        d->textSizes.remove(tab->text);
        tab->text = text;
#ifndef QT_NO_SHORTCUT
        releaseShortcut(tab->shortcutId);
        tab->shortcutId = grabShortcut(QKeySequence::mnemonic(text));
        setShortcutEnabled(tab->shortcutId, tab->enabled);
#endif
    }
}

/*!
    Returns the text color of the tab with the given \a index, or a invalid
    color if \a index is out of range.

    \sa setTabTextColor()
*/
QColor KCustomTabBar::tabTextColor(int index) const
{
    Q_D(const KCustomTabBar);
    if (const KCustomTabBarPrivate::Tab *tab = d->at(index))
        return tab->textColor;
    return QColor();
}

/*!
    Sets the color of the text in the tab with the given \a index to the specified \a color.

    If an invalid color is specified, the tab will use the KCustomTabBar foreground role instead.

    \sa tabTextColor()
*/
void KCustomTabBar::setTabTextColor(int index, const QColor &color)
{
    Q_D(KCustomTabBar);
    if (KCustomTabBarPrivate::Tab *tab = d->at(index)) {
        tab->textColor = color;
        update(tabRect(index));
    }
}

/*!
    Returns the icon of the tab at position \a index, or a null icon
    if \a index is out of range.
*/
QIcon KCustomTabBar::tabIcon(int index) const
{
    Q_D(const KCustomTabBar);
    if (const KCustomTabBarPrivate::Tab *tab = d->at(index))
        return tab->icon;
    return QIcon();
}

/*!
    Sets the icon of the tab at position \a index to \a icon.
*/
void KCustomTabBar::setTabIcon(int index, const QIcon &icon)
{
    Q_D(KCustomTabBar);
    if (KCustomTabBarPrivate::Tab *tab = d->at(index)) {
        bool simpleIconChange = (!icon.isNull() && !tab->icon.isNull());
        tab->icon = icon;
        if (simpleIconChange)
            update(tabRect(index));
        else
            d->refresh();
    }
}

#ifndef QT_NO_TOOLTIP
/*!
    Sets the tool tip of the tab at position \a index to \a tip.
*/
void KCustomTabBar::setTabToolTip(int index, const QString &tip)
{
    Q_D(KCustomTabBar);
    if (KCustomTabBarPrivate::Tab *tab = d->at(index))
        tab->toolTip = tip;
}

/*!
    Returns the tool tip of the tab at position \a index, or an empty
    string if \a index is out of range.
*/
QString KCustomTabBar::tabToolTip(int index) const
{
    Q_D(const KCustomTabBar);
    if (const KCustomTabBarPrivate::Tab *tab = d->at(index))
        return tab->toolTip;
    return QString();
}
#endif // QT_NO_TOOLTIP

#if QT_CONFIG(whatsthis)
/*!
    \since 4.1

    Sets the What's This help text of the tab at position \a index
    to \a text.
*/
void KCustomTabBar::setTabWhatsThis(int index, const QString &text)
{
    Q_D(KCustomTabBar);
    if (KCustomTabBarPrivate::Tab *tab = d->at(index))
        tab->whatsThis = text;
}

/*!
    \since 4.1

    Returns the What's This help text of the tab at position \a index,
    or an empty string if \a index is out of range.
*/
QString KCustomTabBar::tabWhatsThis(int index) const
{
    Q_D(const KCustomTabBar);
    if (const KCustomTabBarPrivate::Tab *tab = d->at(index))
        return tab->whatsThis;
    return QString();
}

#endif // QT_CONFIG(whatsthis)

/*!
    Sets the data of the tab at position \a index to \a data.
*/
void KCustomTabBar::setTabData(int index, const QVariant &data)
{
    Q_D(KCustomTabBar);
    if (KCustomTabBarPrivate::Tab *tab = d->at(index))
        tab->data = data;
}

/*!
    Returns the data of the tab at position \a index, or a null
    variant if \a index is out of range.
*/
QVariant KCustomTabBar::tabData(int index) const
{
    Q_D(const KCustomTabBar);
    if (const KCustomTabBarPrivate::Tab *tab = d->at(index))
        return tab->data;
    return QVariant();
}

/*!
    Returns the visual rectangle of the tab at position \a
    index, or a null rectangle if \a index is out of range.
*/
QRect KCustomTabBar::tabRect(int index) const
{
    Q_D(const KCustomTabBar);
    if (const KCustomTabBarPrivate::Tab *tab = d->at(index)) {
        if (d->layoutDirty)
            const_cast<KCustomTabBarPrivate *>(d)->layoutTabs();
        QRect r = tab->rect;
        if (verticalTabs(d->shape))
            r.translate(0, -d->scrollOffset);
        else
            r.translate(-d->scrollOffset, 0);
        if (!verticalTabs(d->shape))
            r = QStyle::visualRect(layoutDirection(), rect(), r);
        return r;
    }
    return QRect();
}

/*!
    \since 4.3
    Returns the index of the tab that covers \a position or -1 if no
    tab covers \a position;
*/

int KCustomTabBar::tabAt(const QPoint &position) const
{
    Q_D(const KCustomTabBar);
    if (d->validIndex(d->currentIndex)
        && tabRect(d->currentIndex).contains(position)) {
        return d->currentIndex;
    }
    const int max = d->tabList.size();
    for (int i = 0; i < max; ++i) {
        if (tabRect(i).contains(position)) {
            return i;
        }
    }
    return -1;
}

/*!
    \property KCustomTabBar::currentIndex
    \brief the index of the tab bar's visible tab

    The current index is -1 if there is no current tab.
*/

int KCustomTabBar::currentIndex() const
{
    Q_D(const KCustomTabBar);
    if (d->validIndex(d->currentIndex))
        return d->currentIndex;
    return -1;
}

void KCustomTabBar::setCurrentIndex(int index)
{
    Q_D(KCustomTabBar);
    if (d->dragInProgress && d->pressedIndex != -1)
        return;

    int oldIndex = d->currentIndex;
    if (d->validIndex(index) && d->currentIndex != index) {
        d->currentIndex = index;
        update();
        d->makeVisible(index);
        d->tabList[index].lastTab = oldIndex;
        if (oldIndex >= 0 && oldIndex < count())
            d->layoutTab(oldIndex);
        d->layoutTab(index);
#ifndef QT_NO_ACCESSIBILITY
        if (QAccessible::isActive()) {
            if (hasFocus()) {
                QAccessibleEvent focusEvent(this, QAccessible::Focus);
                focusEvent.setChild(index);
                QAccessible::updateAccessibility(&focusEvent);
            }
            QAccessibleEvent selectionEvent(this, QAccessible::Selection);
            selectionEvent.setChild(index);
            QAccessible::updateAccessibility(&selectionEvent);
        }
#endif
        emit currentChanged(index);
    }
}

/*!
    \property KCustomTabBar::iconSize
    \brief The size for icons in the tab bar
    \since 4.1

    The default value is style-dependent. \c iconSize is a maximum
    size; icons that are smaller are not scaled up.

    \sa QTabWidget::iconSize
*/
QSize KCustomTabBar::iconSize() const
{
    Q_D(const KCustomTabBar);
    if (d->iconSize.isValid())
        return d->iconSize;
    int iconExtent = style()->pixelMetric(QStyle::PM_TabBarIconSize, 0, this);
    return QSize(iconExtent, iconExtent);
}

void KCustomTabBar::setIconSize(const QSize &size)
{
    Q_D(KCustomTabBar);
    d->iconSize = size;
    d->layoutDirty = true;
    update();
    updateGeometry();
}

/*!
    \property KCustomTabBar::count
    \brief the number of tabs in the tab bar
*/

int KCustomTabBar::count() const
{
    Q_D(const KCustomTabBar);
    return d->tabList.count();
}

/*!\reimp
 */
QSize KCustomTabBar::sizeHint() const
{
    Q_D(const KCustomTabBar);
    if (d->layoutDirty)
        const_cast<KCustomTabBarPrivate *>(d)->layoutTabs();
    QRect r;
    for (int i = 0; i < d->tabList.count(); ++i)
        r = r.united(d->tabList.at(i).maxRect);
    QSize sz = QApplication::globalStrut();
    return r.size().expandedTo(sz);
}

/*!\reimp
 */
QSize KCustomTabBar::minimumSizeHint() const
{
    Q_D(const KCustomTabBar);
    if (d->layoutDirty)
        const_cast<KCustomTabBarPrivate *>(d)->layoutTabs();
    if (!d->useScrollButtons) {
        QRect r;
        for (int i = 0; i < d->tabList.count(); ++i)
            r = r.united(d->tabList.at(i).minRect);
        return r.size().expandedTo(QApplication::globalStrut());
    }
    if (verticalTabs(d->shape))
        return QSize(sizeHint().width(), d->rightB->sizeHint().height() * 2 + 75);
    else
        return QSize(d->rightB->sizeHint().width() * 2 + 75, sizeHint().height());
}

// Compute the most-elided possible text, for minimumSizeHint
static QString computeElidedText(Qt::TextElideMode mode, const QString &text)
{
    if (text.length() <= 3)
        return text;

    static const QLatin1String Ellipses("...");
    QString ret;
    switch (mode) {
    case Qt::ElideRight:
        ret = text.leftRef(2) + Ellipses;
        break;
    case Qt::ElideMiddle:
        ret = text.leftRef(1) + Ellipses + text.rightRef(1);
        break;
    case Qt::ElideLeft:
        ret = Ellipses + text.rightRef(2);
        break;
    case Qt::ElideNone:
        ret = text;
        break;
    }
    return ret;
}

/*!
    Returns the minimum tab size hint for the tab at position \a index.
    \since 5.0
*/

QSize KCustomTabBar::minimumTabSizeHint(int index) const
{
    Q_D(const KCustomTabBar);
    KCustomTabBarPrivate::Tab &tab = const_cast<KCustomTabBarPrivate::Tab &>(d->tabList[index]);
    QString oldText = tab.text;
    tab.text = computeElidedText(d->elideMode, oldText);
    QSize size = tabSizeHint(index);
    tab.text = oldText;
    return size;
}

/*!
    Returns the size hint for the tab at position \a index.
*/
QSize KCustomTabBar::tabSizeHint(int index) const
{
    // Note: this must match with the computations in QCommonStylePrivate::tabLayout
    Q_D(const KCustomTabBar);
    if (const KCustomTabBarPrivate::Tab *tab = d->at(index)) {
        QStyleOptionTab opt;
        d->initBasicStyleOption(&opt, index);
        opt.text = d->tabList.at(index).text;
        QSize iconSize = tab->icon.isNull() ? QSize(0, 0) : opt.iconSize;
        int hframe = style()->pixelMetric(QStyle::PM_TabBarTabHSpace, &opt, this);
        int vframe = style()->pixelMetric(QStyle::PM_TabBarTabVSpace, &opt, this);
        const QFontMetrics fm = fontMetrics();

        int maxWidgetHeight = qMax(opt.leftButtonSize.height(), opt.rightButtonSize.height());
        int maxWidgetWidth = qMax(opt.leftButtonSize.width(), opt.rightButtonSize.width());

        int widgetWidth = 0;
        int widgetHeight = 0;
        int padding = 0;
        if (!opt.leftButtonSize.isEmpty()) {
            padding += 4;
            widgetWidth += opt.leftButtonSize.width();
            widgetHeight += opt.leftButtonSize.height();
        }
        if (!opt.rightButtonSize.isEmpty()) {
            padding += 4;
            widgetWidth += opt.rightButtonSize.width();
            widgetHeight += opt.rightButtonSize.height();
        }
        if (!opt.icon.isNull())
            padding += 4;

        QHash<QString, QSize>::iterator it = d->textSizes.find(tab->text);
        if (it == d->textSizes.end())
            it = d->textSizes.insert(tab->text, fm.size(Qt::TextShowMnemonic, tab->text));
        const int textWidth = it.value().width();
        QSize csz;
        if (verticalTabs(d->shape)) {
            csz = QSize(qMax(maxWidgetWidth, qMax(fm.height(), iconSize.height())) + vframe,
                        textWidth + iconSize.width() + hframe + widgetHeight + padding);
        } else {
            csz = QSize(textWidth + iconSize.width() + hframe + widgetWidth + padding,
                        qMax(maxWidgetHeight, qMax(fm.height(), iconSize.height())) + vframe);
        }

        QSize retSize = style()->sizeFromContents(QStyle::CT_TabBarTab, &opt, csz, this);
        return retSize;
    }
    return QSize();
}

/*!
  This virtual handler is called after a new tab was added or
  inserted at position \a index.

  \sa tabRemoved()
 */
void KCustomTabBar::tabInserted(int index)
{
    Q_UNUSED(index)
}

/*!
  This virtual handler is called after a tab was removed from
  position \a index.

  \sa tabInserted()
 */
void KCustomTabBar::tabRemoved(int index)
{
    Q_UNUSED(index)
}

/*!
  This virtual handler is called whenever the tab layout changes.

  \sa tabRect()
 */
void KCustomTabBar::tabLayoutChange()
{
}

/*!\reimp
 */
void KCustomTabBar::showEvent(QShowEvent *)
{
    Q_D(KCustomTabBar);
    if (d->layoutDirty)
        d->refresh();
    if (!d->validIndex(d->currentIndex))
        setCurrentIndex(0);
    d->updateMacBorderMetrics();
}

/*!\reimp
 */
void KCustomTabBar::hideEvent(QHideEvent *)
{
    Q_D(KCustomTabBar);
    d->updateMacBorderMetrics();
}

/*!\reimp
 */
bool KCustomTabBar::event(QEvent *event)
{
    Q_D(KCustomTabBar);
    if (event->type() == QEvent::HoverMove
        || event->type() == QEvent::HoverEnter) {
        QHoverEvent *he = static_cast<QHoverEvent *>(event);
        if (!d->hoverRect.contains(he->pos())) {
            QRect oldHoverRect = d->hoverRect;
            bool cursorOverTabs = false;
            for (int i = 0; i < d->tabList.count(); ++i) {
                QRect area = tabRect(i);
                if (area.contains(he->pos())) {
                    d->hoverIndex = i;
                    d->hoverRect = area;
                    cursorOverTabs = true;
                    break;
                }
            }
            if (!cursorOverTabs) {
                d->hoverIndex = -1;
                d->hoverRect = QRect();
            }
            if (he->oldPos() != QPoint(-1, -1))
                update(oldHoverRect);
            update(d->hoverRect);
        }
        return true;
    } else if (event->type() == QEvent::HoverLeave) {
        QRect oldHoverRect = d->hoverRect;
        d->hoverIndex = -1;
        d->hoverRect = QRect();
        update(oldHoverRect);
        return true;
#ifndef QT_NO_TOOLTIP
    } else if (event->type() == QEvent::ToolTip) {
        if (const KCustomTabBarPrivate::Tab *tab = d->at(tabAt(static_cast<QHelpEvent *>(event)->pos()))) {
            if (!tab->toolTip.isEmpty()) {
                QToolTip::showText(static_cast<QHelpEvent *>(event)->globalPos(), tab->toolTip, this);
                return true;
            }
        }
#endif // QT_NO_TOOLTIP
#if QT_CONFIG(whatsthis)
    } else if (event->type() == QEvent::QueryWhatsThis) {
        const KCustomTabBarPrivate::Tab *tab = d->at(d->indexAtPos(static_cast<QHelpEvent *>(event)->pos()));
        if (!tab || tab->whatsThis.isEmpty())
            event->ignore();
        return true;
    } else if (event->type() == QEvent::WhatsThis) {
        if (const KCustomTabBarPrivate::Tab *tab = d->at(d->indexAtPos(static_cast<QHelpEvent *>(event)->pos()))) {
            if (!tab->whatsThis.isEmpty()) {
                QWhatsThis::showText(static_cast<QHelpEvent *>(event)->globalPos(),
                                     tab->whatsThis, this);
                return true;
            }
        }
#endif // QT_CONFIG(whatsthis)
#ifndef QT_NO_SHORTCUT
    } else if (event->type() == QEvent::Shortcut) {
        QShortcutEvent *se = static_cast<QShortcutEvent *>(event);
        for (int i = 0; i < d->tabList.count(); ++i) {
            const KCustomTabBarPrivate::Tab *tab = &d->tabList.at(i);
            if (tab->shortcutId == se->shortcutId()) {
                setCurrentIndex(i);
                return true;
            }
        }
#endif
    } else if (event->type() == QEvent::MouseButtonDblClick) { // ### fixme Qt 6: move to mouseDoubleClickEvent(), here for BC reasons.
        const QPoint pos = static_cast<const QMouseEvent *>(event)->pos();
        const bool isEventInCornerButtons = (!d->leftB->isHidden() && d->leftB->geometry().contains(pos))
            || (!d->rightB->isHidden() && d->rightB->geometry().contains(pos));
        if (!isEventInCornerButtons)
            emit tabBarDoubleClicked(tabAt(pos));
    } else if (event->type() == QEvent::Move) {
        d->updateMacBorderMetrics();
        return QWidget::event(event);

#if QT_CONFIG(draganddrop)
    } else if (event->type() == QEvent::DragEnter) {
        if (d->changeCurrentOnDrag)
            event->accept();
    } else if (event->type() == QEvent::DragMove) {
        if (d->changeCurrentOnDrag) {
            const int tabIndex = tabAt(static_cast<QDragMoveEvent *>(event)->pos());
            if (isTabEnabled(tabIndex) && d->switchTabCurrentIndex != tabIndex) {
                d->switchTabCurrentIndex = tabIndex;
                if (d->switchTabTimerId)
                    killTimer(d->switchTabTimerId);
                d->switchTabTimerId = startTimer(style()->styleHint(QStyle::SH_TabBar_ChangeCurrentDelay));
            }
            event->ignore();
        }
    } else if (event->type() == QEvent::DragLeave || event->type() == QEvent::Drop) {
        d->killSwitchTabTimer();
        event->ignore();
#endif
    }
    return QWidget::event(event);
}

/*!\reimp
 */
void KCustomTabBar::resizeEvent(QResizeEvent *)
{
    Q_D(KCustomTabBar);
    if (d->layoutDirty)
        updateGeometry();
    d->layoutTabs();

    d->makeVisible(d->currentIndex);
}

/*!\reimp
 */
void KCustomTabBar::paintEvent(QPaintEvent *)
{
    Q_D(KCustomTabBar);
    QStyleOptionTabBarBase optTabBase;
    KCustomTabBarPrivate::initStyleBaseOption(&optTabBase, this, size());

    QStylePainter p(this);
    int selected = -1;
    int cutLeft = -1;
    int cutRight = -1;
    bool vertical = verticalTabs(d->shape);
    QStyleOptionTab cutTabLeft;
    QStyleOptionTab cutTabRight;
    selected = d->currentIndex;
    if (d->dragInProgress)
        selected = d->pressedIndex;
    const QRect scrollRect = d->normalizedScrollRect();

    for (int i = 0; i < d->tabList.count(); ++i)
        optTabBase.tabBarRect |= tabRect(i);

    optTabBase.selectedTabRect = tabRect(selected);

    if (d->drawBase)
        p.drawPrimitive(QStyle::PE_FrameTabBarBase, optTabBase);

    for (int i = 0; i < d->tabList.count(); ++i) {
        QStyleOptionTab tab;
        initStyleOption(&tab, i);
        if (d->paintWithOffsets && d->tabList[i].dragOffset != 0) {
            if (vertical) {
                tab.rect.moveTop(tab.rect.y() + d->tabList[i].dragOffset);
            } else {
                tab.rect.moveLeft(tab.rect.x() + d->tabList[i].dragOffset);
            }
        }
        if (!(tab.state & QStyle::State_Enabled)) {
            tab.palette.setCurrentColorGroup(QPalette::Disabled);
        }

        // If this tab is partially obscured, make a note of it so that we can pass the information
        // along when we draw the tear.
        QRect tabRect = d->tabList[i].rect;
        int tabStart = vertical ? tabRect.top() : tabRect.left();
        int tabEnd = vertical ? tabRect.bottom() : tabRect.right();
        if (tabStart < scrollRect.left() + d->scrollOffset) {
            cutLeft = i;
            cutTabLeft = tab;
        } else if (tabEnd > scrollRect.right() + d->scrollOffset) {
            cutRight = i;
            cutTabRight = tab;
        }

        // Don't bother drawing a tab if the entire tab is outside of the visible tab bar.
        if ((!vertical && (tab.rect.right() < 0 || tab.rect.left() > width()))
            || (vertical && (tab.rect.bottom() < 0 || tab.rect.top() > height())))
            continue;

        optTabBase.tabBarRect |= tab.rect;
        if (i == selected)
            continue;

        p.drawControl(QStyle::CE_TabBarTab, tab);
    }

    // Draw the selected tab last to get it "on top"
    if (selected >= 0) {
        QStyleOptionTab tab;
        initStyleOption(&tab, selected);
        if (d->paintWithOffsets && d->tabList[selected].dragOffset != 0) {
            if (vertical)
                tab.rect.moveTop(tab.rect.y() + d->tabList[selected].dragOffset);
            else
                tab.rect.moveLeft(tab.rect.x() + d->tabList[selected].dragOffset);
        }
        if (!d->dragInProgress)
            p.drawControl(QStyle::CE_TabBarTab, tab);
        else {
            int taboverlap = style()->pixelMetric(QStyle::PM_TabBarTabOverlap, 0, this);
            if (verticalTabs(d->shape))
                d->movingTab->setGeometry(tab.rect.adjusted(0, -taboverlap, 0, taboverlap));
            else
                d->movingTab->setGeometry(tab.rect.adjusted(-taboverlap, 0, taboverlap, 0));
        }
    }

    // Only draw the tear indicator if necessary. Most of the time we don't need too.
    if (d->leftB->isVisible() && cutLeft >= 0) {
        cutTabLeft.rect = rect();
        cutTabLeft.rect = style()->subElementRect(QStyle::SE_TabBarTearIndicatorLeft, &cutTabLeft, this);
        p.drawPrimitive(QStyle::PE_IndicatorTabTearLeft, cutTabLeft);
    }

    if (d->rightB->isVisible() && cutRight >= 0) {
        cutTabRight.rect = rect();
        cutTabRight.rect = style()->subElementRect(QStyle::SE_TabBarTearIndicatorRight, &cutTabRight, this);
        p.drawPrimitive(QStyle::PE_IndicatorTabTearRight, cutTabRight);
    }
}

/*
    Given that index at position from moved to position to where return where index goes.
 */
KCustomTabBarPrivate::KCustomTabBarPrivate(KCustomTabBar *parent)
    : q_ptr(parent)
    , currentIndex(-1)
    , pressedIndex(-1)
    , shape(KCustomTabBar::RoundedNorth)
    , layoutDirty(false)
    , drawBase(true)
    , scrollOffset(0)
    , hoverIndex(-1)
    , elideModeSetByUser(false)
    , useScrollButtonsSetByUser(false)
    , expanding(true)
    , closeButtonOnTabs(false)
    , selectionBehaviorOnRemove(KCustomTabBar::SelectRightTab)
    , paintWithOffsets(true)
    , movable(false)
    , dragInProgress(false)
    , documentMode(false)
    , autoHide(false)
    , changeCurrentOnDrag(false)
    , switchTabCurrentIndex(-1)
    , switchTabTimerId(0)
    , movingTab(0)
{
}

void KCustomTabBarPrivate::qGeomCalc(QVector<QCustomLayoutStruct> &chain, int start, int count, int pos, int space, int spacer)
{
    int cHint = 0;
    int cMin = 0;
    int cMax = 0;
    int sumStretch = 0;
    int sumSpacing = 0;
    int expandingCount = 0;

    bool allEmptyNonstretch = true;
    int pendingSpacing = -1;
    int spacerCount = 0;
    int i;

    for (i = start; i < start + count; i++) {
        QCustomLayoutStruct *data = &chain[i];

        data->done = false;
        cHint += data->smartSizeHint();
        cMin += data->minimumSize;
        cMax += data->maximumSize;
        sumStretch += data->stretch;
        if (!data->empty) {
            /*
                Using pendingSpacing, we ensure that the spacing for the last
                (non-empty) item is ignored.
            */
            if (pendingSpacing >= 0) {
                sumSpacing += pendingSpacing;
                ++spacerCount;
            }
            pendingSpacing = data->effectiveSpacer(spacer);
        }
        if (data->expansive)
            expandingCount++;
        allEmptyNonstretch = allEmptyNonstretch && data->empty && !data->expansive && data->stretch <= 0;
    }

    int extraspace = 0;

    if (space < cMin + sumSpacing) {
        /*
          Less space than minimumSize; take from the biggest first
        */

        int minSize = cMin + sumSpacing;

        // shrink the spacers proportionally
        if (spacer >= 0) {
            spacer = minSize > 0 ? spacer * space / minSize : 0;
            sumSpacing = spacer * spacerCount;
        }

        QVarLengthArray<int, 32> minimumSizes;
        minimumSizes.reserve(count);

        for (i = start; i < start + count; i++)
            minimumSizes << chain.at(i).minimumSize;

        std::sort(minimumSizes.begin(), minimumSizes.end());

        int space_left = space - sumSpacing;

        int sum = 0;
        int idx = 0;
        int space_used = 0;
        int current = 0;
        while (idx < count && space_used < space_left) {
            current = minimumSizes.at(idx);
            space_used = sum + current * (count - idx);
            sum += current;
            ++idx;
        }
        --idx;
        int deficit = space_used - space_left;

        int items = count - idx;
        /*
         * If we truncate all items to "current", we would get "deficit" too many pixels. Therefore, we have to remove
         * deficit/items from each item bigger than maxval. The actual value to remove is deficitPerItem + remainder/items
         * "rest" is the accumulated error from using integer arithmetic.
         */
        int deficitPerItem = deficit / items;
        int remainder = deficit % items;
        int maxval = current - deficitPerItem;

        int rest = 0;
        for (i = start; i < start + count; i++) {
            int maxv = maxval;
            rest += remainder;
            if (rest >= items) {
                maxv--;
                rest -= items;
            }
            QCustomLayoutStruct *data = &chain[i];
            data->size = qMin(data->minimumSize, maxv);
            data->done = true;
        }
    } else if (space < cHint + sumSpacing) {
        /*
          Less space than smartSizeHint(), but more than minimumSize.
          Currently take space equally from each, as in Qt 2.x.
          Commented-out lines will give more space to stretchier
          items.
        */
        int n = count;
        int space_left = space - sumSpacing;
        int overdraft = cHint - space_left;

        // first give to the fixed ones:
        for (i = start; i < start + count; i++) {
            QCustomLayoutStruct *data = &chain[i];
            if (!data->done
                && data->minimumSize >= data->smartSizeHint()) {
                data->size = data->smartSizeHint();
                data->done = true;
                space_left -= data->smartSizeHint();
                // sumStretch -= data->stretch;
                n--;
            }
        }
        bool finished = n == 0;
        while (!finished) {
            finished = true;
            Fixed64 fp_over = toFixed(overdraft);
            Fixed64 fp_w = 0;

            for (i = start; i < start + count; i++) {
                QCustomLayoutStruct *data = &chain[i];
                if (data->done)
                    continue;
                // if (sumStretch <= 0)
                fp_w += fp_over / n;
                // else
                //    fp_w += (fp_over * data->stretch) / sumStretch;
                int w = fRound(fp_w);
                data->size = data->smartSizeHint() - w;
                fp_w -= toFixed(w); // give the difference to the next
                if (data->size < data->minimumSize) {
                    data->done = true;
                    data->size = data->minimumSize;
                    finished = false;
                    overdraft -= data->smartSizeHint() - data->minimumSize;
                    // sumStretch -= data->stretch;
                    n--;
                    break;
                }
            }
        }
    } else { // extra space
        int n = count;
        int space_left = space - sumSpacing;
        // first give to the fixed ones, and handle non-expansiveness
        for (i = start; i < start + count; i++) {
            QCustomLayoutStruct *data = &chain[i];
            if (!data->done
                && (data->maximumSize <= data->smartSizeHint()
                    || (!allEmptyNonstretch && data->empty && !data->expansive && data->stretch == 0))) {
                data->size = data->smartSizeHint();
                data->done = true;
                space_left -= data->size;
                sumStretch -= data->stretch;
                if (data->expansive)
                    expandingCount--;
                n--;
            }
        }
        extraspace = space_left;

        /*
          Do a trial distribution and calculate how much it is off.
          If there are more deficit pixels than surplus pixels, give
          the minimum size items what they need, and repeat.
          Otherwise give to the maximum size items, and repeat.

          Paul Olav Tvete has a wonderful mathematical proof of the
          correctness of this principle, but unfortunately this
          comment is too small to contain it.
        */
        int surplus, deficit;
        do {
            surplus = deficit = 0;
            Fixed64 fp_space = toFixed(space_left);
            Fixed64 fp_w = 0;
            for (i = start; i < start + count; i++) {
                QCustomLayoutStruct *data = &chain[i];
                if (data->done)
                    continue;
                extraspace = 0;
                if (sumStretch > 0) {
                    fp_w += (fp_space * data->stretch) / sumStretch;
                } else if (expandingCount > 0) {
                    fp_w += (fp_space * (data->expansive ? 1 : 0)) / expandingCount;
                } else {
                    fp_w += fp_space * 1 / n;
                }
                int w = fRound(fp_w);
                data->size = w;
                fp_w -= toFixed(w); // give the difference to the next
                if (w < data->smartSizeHint()) {
                    deficit += data->smartSizeHint() - w;
                } else if (w > data->maximumSize) {
                    surplus += w - data->maximumSize;
                }
            }
            if (deficit > 0 && surplus <= deficit) {
                // give to the ones that have too little
                for (i = start; i < start + count; i++) {
                    QCustomLayoutStruct *data = &chain[i];
                    if (!data->done && data->size < data->smartSizeHint()) {
                        data->size = data->smartSizeHint();
                        data->done = true;
                        space_left -= data->smartSizeHint();
                        sumStretch -= data->stretch;
                        if (data->expansive)
                            expandingCount--;
                        n--;
                    }
                }
            }
            if (surplus > 0 && surplus >= deficit) {
                // take from the ones that have too much
                for (i = start; i < start + count; i++) {
                    QCustomLayoutStruct *data = &chain[i];
                    if (!data->done && data->size > data->maximumSize) {
                        data->size = data->maximumSize;
                        data->done = true;
                        space_left -= data->maximumSize;
                        sumStretch -= data->stretch;
                        if (data->expansive)
                            expandingCount--;
                        n--;
                    }
                }
            }
        } while (n > 0 && surplus != deficit);
        if (n == 0)
            extraspace = space_left;
    }

    /*
      As a last resort, we distribute the unwanted space equally
      among the spacers (counting the start and end of the chain). We
      could, but don't, attempt a sub-pixel allocation of the extra
      space.
    */
    int extra = extraspace / (spacerCount + 2);
    int p = pos + extra;
    for (i = start; i < start + count; i++) {
        QCustomLayoutStruct *data = &chain[i];
        data->pos = p;
        p += data->size;
        if (!data->empty)
            p += data->effectiveSpacer(spacer) + extra;
    }

#ifdef QLAYOUT_EXTRA_DEBUG
    qDebug() << "qGeomCalc"
             << "start" << start << "count" << count << "pos" << pos
             << "space" << space << "spacer" << spacer;
    for (i = start; i < start + count; ++i) {
        qDebug() << i << ':' << chain[i].minimumSize << chain[i].smartSizeHint()
                 << chain[i].maximumSize << "stretch" << chain[i].stretch
                 << "empty" << chain[i].empty << "expansive" << chain[i].expansive
                 << "spacing" << chain[i].spacing;
        qDebug() << "result pos" << chain[i].pos << "size" << chain[i].size;
    }
#endif
}

int KCustomTabBarPrivate::calculateNewPosition(int from, int to, int index) const
{
    if (index == from)
        return to;

    int start = qMin(from, to);
    int end = qMax(from, to);
    if (index >= start && index <= end)
        index += (from < to) ? -1 : 1;
    return index;
}

/*!
    Moves the item at index position \a from to index position \a to.
    \since 4.5

    \sa tabMoved(), tabLayoutChange()
 */
void KCustomTabBar::moveTab(int from, int to)
{
    Q_D(KCustomTabBar);
    if (from == to
        || !d->validIndex(from)
        || !d->validIndex(to))
        return;

    bool vertical = verticalTabs(d->shape);
    int oldPressedPosition = 0;
    if (d->pressedIndex != -1) {
        // Record the position of the pressed tab before reordering the tabs.
        oldPressedPosition = vertical ? d->tabList[d->pressedIndex].rect.y()
                                      : d->tabList[d->pressedIndex].rect.x();
    }

    // Update the locations of the tabs first
    int start = qMin(from, to);
    int end = qMax(from, to);
    int width = vertical ? d->tabList[from].rect.height() : d->tabList[from].rect.width();
    if (from < to)
        width *= -1;
    bool rtl = isRightToLeft();
    for (int i = start; i <= end; ++i) {
        if (i == from)
            continue;
        if (vertical)
            d->tabList[i].rect.moveTop(d->tabList[i].rect.y() + width);
        else
            d->tabList[i].rect.moveLeft(d->tabList[i].rect.x() + width);
        int direction = -1;
        if (rtl && !vertical)
            direction *= -1;
        if (d->tabList[i].dragOffset != 0)
            d->tabList[i].dragOffset += (direction * width);
    }

    if (vertical) {
        if (from < to)
            d->tabList[from].rect.moveTop(d->tabList[to].rect.bottom() + 1);
        else
            d->tabList[from].rect.moveTop(d->tabList[to].rect.top() - width);
    } else {
        if (from < to)
            d->tabList[from].rect.moveLeft(d->tabList[to].rect.right() + 1);
        else
            d->tabList[from].rect.moveLeft(d->tabList[to].rect.left() - width);
    }

    // Move the actual data structures
    d->tabList.move(from, to);

    // update lastTab locations
    for (int i = 0; i < d->tabList.count(); ++i)
        d->tabList[i].lastTab = d->calculateNewPosition(from, to, d->tabList[i].lastTab);

    // update external variables
    int previousIndex = d->currentIndex;
    d->currentIndex = d->calculateNewPosition(from, to, d->currentIndex);

    // If we are in the middle of a drag update the dragStartPosition
    if (d->pressedIndex != -1) {
        d->pressedIndex = d->calculateNewPosition(from, to, d->pressedIndex);
        int newPressedPosition = vertical ? d->tabList[d->pressedIndex].rect.top() : d->tabList[d->pressedIndex].rect.left();
        int diff = oldPressedPosition - newPressedPosition;
        if (isRightToLeft() && !vertical)
            diff *= -1;
        if (vertical)
            d->dragStartPosition.setY(d->dragStartPosition.y() - diff);
        else
            d->dragStartPosition.setX(d->dragStartPosition.x() - diff);
    }

    d->layoutWidgets(start);
    update();
    emit tabMoved(from, to);
    if (previousIndex != d->currentIndex)
        emit currentChanged(d->currentIndex);
    emit tabLayoutChange();
}

void KCustomTabBarPrivate::slide(int from, int to)
{
    Q_Q(KCustomTabBar);
    if (from == to
        || !validIndex(from)
        || !validIndex(to))
        return;
    bool vertical = verticalTabs(shape);
    int preLocation = vertical ? q->tabRect(from).y() : q->tabRect(from).x();
    q->setUpdatesEnabled(false);
    q->moveTab(from, to);
    q->setUpdatesEnabled(true);
    int postLocation = vertical ? q->tabRect(to).y() : q->tabRect(to).x();
    int length = postLocation - preLocation;
    tabList[to].dragOffset -= length;
    tabList[to].startAnimation(this, ANIMATION_DURATION);
}

void KCustomTabBarPrivate::moveTab(int index, int offset)
{
    if (!validIndex(index))
        return;
    tabList[index].dragOffset = offset;
    layoutTab(index); // Make buttons follow tab
    q_func()->update();
}

/*!\reimp
 */
void KCustomTabBar::mousePressEvent(QMouseEvent *event)
{
    Q_D(KCustomTabBar);

    const QPoint pos = event->pos();
    const bool isEventInCornerButtons = (!d->leftB->isHidden() && d->leftB->geometry().contains(pos))
        || (!d->rightB->isHidden() && d->rightB->geometry().contains(pos));
    if (!isEventInCornerButtons) {
        const int index = d->indexAtPos(pos);
        emit tabBarClicked(index);
    }

    if (event->button() != Qt::LeftButton) {
        event->ignore();
        return;
    }
    // Be safe!
    if (d->pressedIndex != -1 && d->movable)
        d->moveTabFinished(d->pressedIndex);

    d->pressedIndex = d->indexAtPos(event->pos());

    if (d->validIndex(d->pressedIndex)) {
        QStyleOptionTabBarBase optTabBase;
        optTabBase.init(this);
        optTabBase.documentMode = d->documentMode;
        if (event->type() == style()->styleHint(QStyle::SH_TabBar_SelectMouseType, &optTabBase, this))
            setCurrentIndex(d->pressedIndex);
        else
            repaint(tabRect(d->pressedIndex));
        if (d->movable) {
            d->dragStartPosition = event->pos();
        }
    }
}

/*!\reimp
 */
void KCustomTabBar::mouseMoveEvent(QMouseEvent *event)
{
    Q_D(KCustomTabBar);
    if (d->movable) {
        // Be safe!
        if (d->pressedIndex != -1
            && event->buttons() == Qt::NoButton)
            d->moveTabFinished(d->pressedIndex);

        // Start drag
        if (!d->dragInProgress && d->pressedIndex != -1) {
            if ((event->pos() - d->dragStartPosition).manhattanLength() > QApplication::startDragDistance()) {
                d->dragInProgress = true;
                d->setupMovableTab();
            }
        }

        if (event->buttons() == Qt::LeftButton
            && d->dragInProgress
            && d->validIndex(d->pressedIndex)) {
            bool vertical = verticalTabs(d->shape);
            int dragDistance;
            if (vertical) {
                dragDistance = (event->pos().y() - d->dragStartPosition.y());
            } else {
                dragDistance = (event->pos().x() - d->dragStartPosition.x());
            }
            d->tabList[d->pressedIndex].dragOffset = dragDistance;

            QRect startingRect = tabRect(d->pressedIndex);
            if (vertical)
                startingRect.moveTop(startingRect.y() + dragDistance);
            else
                startingRect.moveLeft(startingRect.x() + dragDistance);

            int overIndex;
            if (dragDistance < 0)
                overIndex = tabAt(startingRect.topLeft());
            else
                overIndex = tabAt(startingRect.topRight());

            if (overIndex != d->pressedIndex && overIndex != -1) {
                int offset = 1;
                if (isRightToLeft() && !vertical)
                    offset *= -1;
                if (dragDistance < 0) {
                    dragDistance *= -1;
                    offset *= -1;
                }
                for (int i = d->pressedIndex;
                     offset > 0 ? i < overIndex : i > overIndex;
                     i += offset) {
                    QRect overIndexRect = tabRect(overIndex);
                    int needsToBeOver = (vertical ? overIndexRect.height() : overIndexRect.width()) / 2;
                    if (dragDistance > needsToBeOver)
                        d->slide(i + offset, d->pressedIndex);
                }
            }
            // Buttons needs to follow the dragged tab
            d->layoutTab(d->pressedIndex);

            update();
        }
    }

    if (event->buttons() != Qt::LeftButton) {
        event->ignore();
        return;
    }
}

void KCustomTabBarPrivate::setupMovableTab()
{
    Q_Q(KCustomTabBar);
    if (!movingTab)
        movingTab = new KCustomMovableTabWidget(q);

    int taboverlap = q->style()->pixelMetric(QStyle::PM_TabBarTabOverlap, 0, q);
    QRect grabRect = q->tabRect(pressedIndex);
    if (verticalTabs(shape))
        grabRect.adjust(0, -taboverlap, 0, taboverlap);
    else
        grabRect.adjust(-taboverlap, 0, taboverlap, 0);

    QPixmap grabImage(grabRect.size() * q->devicePixelRatioF());
    grabImage.setDevicePixelRatio(q->devicePixelRatioF());
    grabImage.fill(Qt::transparent);
    QStylePainter p(&grabImage, q);
    p.initFrom(q);

    QStyleOptionTab tab;
    q->initStyleOption(&tab, pressedIndex);
    tab.position = QStyleOptionTab::OnlyOneTab;
    if (verticalTabs(shape))
        tab.rect.moveTopLeft(QPoint(0, taboverlap));
    else
        tab.rect.moveTopLeft(QPoint(taboverlap, 0));
    p.drawControl(QStyle::CE_TabBarTab, tab);
    p.end();

    movingTab->setPixmap(grabImage);
    movingTab->setGeometry(grabRect);
    movingTab->raise();

    // Re-arrange widget order to avoid overlaps
    if (tabList[pressedIndex].leftWidget)
        tabList[pressedIndex].leftWidget->raise();
    if (tabList[pressedIndex].rightWidget)
        tabList[pressedIndex].rightWidget->raise();
    if (leftB)
        leftB->raise();
    if (rightB)
        rightB->raise();
    movingTab->setVisible(true);
}

void KCustomTabBarPrivate::moveTabFinished(int index)
{
    Q_Q(KCustomTabBar);
    bool cleanup = (pressedIndex == index) || (pressedIndex == -1) || !validIndex(index);
    bool allAnimationsFinished = true;
#if QT_CONFIG(animation)
    for (int i = 0; allAnimationsFinished && i < tabList.count(); ++i) {
        const Tab &t = tabList.at(i);
        if (t.animation && t.animation->state() == QAbstractAnimation::Running)
            allAnimationsFinished = false;
    }
#endif // animation
    if (allAnimationsFinished && cleanup) {
        if (movingTab)
            movingTab->setVisible(false); // We might not get a mouse release
        for (int i = 0; i < tabList.count(); ++i) {
            tabList[i].dragOffset = 0;
        }
        if (pressedIndex != -1 && movable) {
            pressedIndex = -1;
            dragInProgress = false;
            dragStartPosition = QPoint();
        }
        layoutWidgets();
    } else {
        if (!validIndex(index))
            return;
        tabList[index].dragOffset = 0;
    }
    q->update();
}

/*!\reimp
 */
void KCustomTabBar::mouseReleaseEvent(QMouseEvent *event)
{
    Q_D(KCustomTabBar);
    if (event->button() != Qt::LeftButton) {
        event->ignore();
        return;
    }
    if (d->movable && d->dragInProgress && d->validIndex(d->pressedIndex)) {
        int length = d->tabList[d->pressedIndex].dragOffset;
        int width = verticalTabs(d->shape)
            ? tabRect(d->pressedIndex).height()
            : tabRect(d->pressedIndex).width();
        int duration = qMin(ANIMATION_DURATION,
                            (qAbs(length) * ANIMATION_DURATION) / width);
        d->tabList[d->pressedIndex].startAnimation(d, duration);
        d->dragInProgress = false;
        d->movingTab->setVisible(false);
        d->dragStartPosition = QPoint();
    }

    int i = d->indexAtPos(event->pos()) == d->pressedIndex ? d->pressedIndex : -1;
    d->pressedIndex = -1;
    QStyleOptionTabBarBase optTabBase;
    optTabBase.initFrom(this);
    optTabBase.documentMode = d->documentMode;
    const bool selectOnRelease =
        (style()->styleHint(QStyle::SH_TabBar_SelectMouseType, &optTabBase, this) == QEvent::MouseButtonRelease);
    if (selectOnRelease)
        setCurrentIndex(i);
    if (!selectOnRelease || !d->validIndex(i) || d->currentIndex == i)
        repaint(tabRect(i));
}

/*!\reimp
 */
void KCustomTabBar::keyPressEvent(QKeyEvent *event)
{
    Q_D(KCustomTabBar);
    if (event->key() != Qt::Key_Left && event->key() != Qt::Key_Right) {
        event->ignore();
        return;
    }
    int offset = event->key() == (isRightToLeft() ? Qt::Key_Right : Qt::Key_Left) ? -1 : 1;
    d->setCurrentNextEnabledIndex(offset);
}

/*!\reimp
 */
#if QT_CONFIG(wheelevent)
void KCustomTabBar::wheelEvent(QWheelEvent *event)
{
#ifndef Q_OS_MAC
    Q_D(KCustomTabBar);
    int offset = event->delta() > 0 ? -1 : 1;
    d->setCurrentNextEnabledIndex(offset);
    QWidget::wheelEvent(event);
#else
    Q_UNUSED(event)
#endif
}
#endif // QT_CONFIG(wheelevent)

void KCustomTabBarPrivate::setCurrentNextEnabledIndex(int offset)
{
    Q_Q(KCustomTabBar);
    for (int index = currentIndex + offset; validIndex(index); index += offset) {
        if (tabList.at(index).enabled) {
            q->setCurrentIndex(index);
            break;
        }
    }
}

/*!\reimp
 */
void KCustomTabBar::changeEvent(QEvent *event)
{
    Q_D(KCustomTabBar);
    switch (event->type()) {
    case QEvent::StyleChange:
        if (!d->elideModeSetByUser)
            d->elideMode = Qt::TextElideMode(style()->styleHint(QStyle::SH_TabBar_ElideMode, 0, this));
        if (!d->useScrollButtonsSetByUser)
            d->useScrollButtons = !style()->styleHint(QStyle::SH_TabBar_PreferNoArrows, 0, this);
        Q_FALLTHROUGH();
    case QEvent::FontChange:
        d->textSizes.clear();
        d->refresh();
        break;
    default:
        break;
    }

    QWidget::changeEvent(event);
}

/*!
    \reimp
*/
void KCustomTabBar::timerEvent(QTimerEvent *event)
{
    Q_D(KCustomTabBar);
    if (event->timerId() == d->switchTabTimerId) {
        killTimer(d->switchTabTimerId);
        d->switchTabTimerId = 0;
        setCurrentIndex(d->switchTabCurrentIndex);
        d->switchTabCurrentIndex = -1;
    }
    QWidget::timerEvent(event);
}

/*!
    \property KCustomTabBar::elideMode
    \brief how to elide text in the tab bar
    \since 4.2

    This property controls how items are elided when there is not
    enough space to show them for a given tab bar size.

    By default the value is style dependent.

    \sa QTabWidget::elideMode, usesScrollButtons, QStyle::SH_TabBar_ElideMode
*/

Qt::TextElideMode KCustomTabBar::elideMode() const
{
    Q_D(const KCustomTabBar);
    return d->elideMode;
}

void KCustomTabBar::setElideMode(Qt::TextElideMode mode)
{
    Q_D(KCustomTabBar);
    d->elideMode = mode;
    d->elideModeSetByUser = true;
    d->textSizes.clear();
    d->refresh();
}

/*!
    \property KCustomTabBar::usesScrollButtons
    \brief Whether or not a tab bar should use buttons to scroll tabs when it
    has many tabs.
    \since 4.2

    When there are too many tabs in a tab bar for its size, the tab bar can either choose
    to expand its size or to add buttons that allow you to scroll through the tabs.

    By default the value is style dependant.

    \sa elideMode, QTabWidget::usesScrollButtons, QStyle::SH_TabBar_PreferNoArrows
*/
bool KCustomTabBar::usesScrollButtons() const
{
    return d_func()->useScrollButtons;
}

void KCustomTabBar::setUsesScrollButtons(bool useButtons)
{
    Q_D(KCustomTabBar);
    d->useScrollButtonsSetByUser = true;
    if (d->useScrollButtons == useButtons)
        return;
    d->useScrollButtons = useButtons;
    d->refresh();
}

/*!
    \property KCustomTabBar::tabsClosable
    \brief Whether or not a tab bar should place close buttons on each tab
    \since 4.5

    When tabsClosable is set to true a close button will appear on the tab on
    either the left or right hand side depending upon the style.  When the button
    is clicked the tab the signal tabCloseRequested will be emitted.

    By default the value is false.

    \sa setTabButton(), tabRemoved()
*/

bool KCustomTabBar::tabsClosable() const
{
    Q_D(const KCustomTabBar);
    return d->closeButtonOnTabs;
}

void KCustomTabBar::setTabsClosable(bool closable)
{
    Q_D(KCustomTabBar);
    if (d->closeButtonOnTabs == closable)
        return;
    d->closeButtonOnTabs = closable;
    ButtonPosition closeSide = (ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, 0, this);
    if (!closable) {
        for (int i = 0; i < d->tabList.count(); ++i) {
            if (closeSide == LeftSide && d->tabList[i].leftWidget) {
                d->tabList[i].leftWidget->deleteLater();
                d->tabList[i].leftWidget = 0;
            }
            if (closeSide == RightSide && d->tabList[i].rightWidget) {
                d->tabList[i].rightWidget->deleteLater();
                d->tabList[i].rightWidget = 0;
            }
        }
    } else {
        bool newButtons = false;
        for (int i = 0; i < d->tabList.count(); ++i) {
            if (tabButton(i, closeSide))
                continue;
            newButtons = true;
            QAbstractButton *closeButton = new CloseButton(this);
            //            connect(closeButton, SIGNAL(clicked()), this, SLOT(_q_closeTab()));
            connect(closeButton, &QAbstractButton::clicked, this, [=] {
                closeTab();
            });
            setTabButton(i, closeSide, closeButton);
        }
        if (newButtons)
            d->layoutTabs();
    }
    update();
}

/*!
    \enum KCustomTabBar::ButtonPosition
    \since 4.5

    This enum type lists the location of the widget on a tab.

    \value LeftSide Left side of the tab.

    \value RightSide Right side of the tab.

*/

/*!
    \enum KCustomTabBar::SelectionBehavior
    \since 4.5

    This enum type lists the behavior of KCustomTabBar when a tab is removed
    and the tab being removed is also the current tab.

    \value SelectLeftTab  Select the tab to the left of the one being removed.

    \value SelectRightTab  Select the tab to the right of the one being removed.

    \value SelectPreviousTab  Select the previously selected tab.

*/

/*!
    \property KCustomTabBar::selectionBehaviorOnRemove
    \brief What tab should be set as current when removeTab is called if
    the removed tab is also the current tab.
    \since 4.5

    By default the value is SelectRightTab.

    \sa removeTab()
*/

KCustomTabBar::SelectionBehavior KCustomTabBar::selectionBehaviorOnRemove() const
{
    Q_D(const KCustomTabBar);
    return d->selectionBehaviorOnRemove;
}

void KCustomTabBar::setSelectionBehaviorOnRemove(KCustomTabBar::SelectionBehavior behavior)
{
    Q_D(KCustomTabBar);
    d->selectionBehaviorOnRemove = behavior;
}

/*!
    \property KCustomTabBar::expanding
    \brief When expanding is true KCustomTabBar will expand the tabs to use the empty space.
    \since 4.5

    By default the value is true.

    \sa QTabWidget::documentMode
*/

bool KCustomTabBar::expanding() const
{
    Q_D(const KCustomTabBar);
    return d->expanding;
}

void KCustomTabBar::setExpanding(bool enabled)
{
    Q_D(KCustomTabBar);
    if (d->expanding == enabled)
        return;
    d->expanding = enabled;
    d->layoutTabs();
}

/*!
    \property KCustomTabBar::movable
    \brief This property holds whether the user can move the tabs
    within the tabbar area.

    \since 4.5

    By default, this property is \c false;
*/

bool KCustomTabBar::isMovable() const
{
    Q_D(const KCustomTabBar);
    return d->movable;
}

void KCustomTabBar::setMovable(bool movable)
{
    Q_D(KCustomTabBar);
    d->movable = movable;
}

/*!
    \property KCustomTabBar::documentMode
    \brief Whether or not the tab bar is rendered in a mode suitable for the main window.
    \since 4.5

    This property is used as a hint for styles to draw the tabs in a different
    way then they would normally look in a tab widget.  On \macos this will
    look similar to the tabs in Safari or Sierra's Terminal.app.

    \sa QTabWidget::documentMode
*/
bool KCustomTabBar::documentMode() const
{
    return d_func()->documentMode;
}

void KCustomTabBar::setDocumentMode(bool enabled)
{
    Q_D(KCustomTabBar);

    d->documentMode = enabled;
    d->updateMacBorderMetrics();
}

/*!
    \property KCustomTabBar::autoHide
    \brief If true, the tab bar is automatically hidden when it contains less
    than 2 tabs.
    \since 5.4

    By default, this property is false.

    \sa QWidget::visible
*/

bool KCustomTabBar::autoHide() const
{
    Q_D(const KCustomTabBar);
    return d->autoHide;
}

void KCustomTabBar::setAutoHide(bool hide)
{
    Q_D(KCustomTabBar);
    if (d->autoHide == hide)
        return;

    d->autoHide = hide;
    if (hide)
        d->autoHideTabs();
    else
        setVisible(true);
}

/*!
    \property KCustomTabBar::changeCurrentOnDrag
    \brief If true, then the current tab is automatically changed when dragging
    over the tabbar.
    \since 5.4

    \note You should also set acceptDrops property to true to make this feature
    work.

    By default, this property is false.
*/

bool KCustomTabBar::changeCurrentOnDrag() const
{
    Q_D(const KCustomTabBar);
    return d->changeCurrentOnDrag;
}

void KCustomTabBar::setChangeCurrentOnDrag(bool change)
{
    Q_D(KCustomTabBar);
    d->changeCurrentOnDrag = change;
    if (!change)
        d->killSwitchTabTimer();
}

/*!
    Sets \a widget on the tab \a index.  The widget is placed
    on the left or right hand side depending upon the \a position.
    \since 4.5

    Any previously set widget in \a position is hidden.

    The tab bar will take ownership of the widget and so all widgets set here
    will be deleted by the tab bar when it is destroyed unless you separately
    reparent the widget after setting some other widget (or 0).

    \sa tabsClosable()
  */
void KCustomTabBar::setTabButton(int index, ButtonPosition position, QWidget *widget)
{
    Q_D(KCustomTabBar);
    if (index < 0 || index >= d->tabList.count())
        return;
    if (widget) {
        widget->setParent(this);
        // make sure our left and right widgets stay on top
        widget->lower();
        widget->show();
    }
    if (position == LeftSide) {
        if (d->tabList[index].leftWidget)
            d->tabList[index].leftWidget->hide();
        d->tabList[index].leftWidget = widget;
    } else {
        if (d->tabList[index].rightWidget)
            d->tabList[index].rightWidget->hide();
        d->tabList[index].rightWidget = widget;
    }
    d->layoutTabs();
    d->refresh();
    update();
}

/*!
    Returns the widget set a tab \a index and \a position or 0 if
    one is not set.
  */
QWidget *KCustomTabBar::tabButton(int index, ButtonPosition position) const
{
    Q_D(const KCustomTabBar);
    if (index < 0 || index >= d->tabList.count())
        return 0;
    if (position == LeftSide)
        return d->tabList.at(index).leftWidget;
    else
        return d->tabList.at(index).rightWidget;
}

#ifndef QT_NO_ACCESSIBILITY
/*!
    Sets the accessibleName of the tab at position \a index to \a name.
*/
void KCustomTabBar::setAccessibleTabName(int index, const QString &name)
{
    Q_D(KCustomTabBar);
    if (KCustomTabBarPrivate::Tab *tab = d->at(index)) {
        tab->accessibleName = name;
        QAccessibleEvent event(this, QAccessible::NameChanged);
        event.setChild(index);
        QAccessible::updateAccessibility(&event);
    }
}

/*!
    Returns the accessibleName of the tab at position \a index, or an empty
    string if \a index is out of range.
*/
QString KCustomTabBar::accessibleTabName(int index) const
{
    Q_D(const KCustomTabBar);
    if (const KCustomTabBarPrivate::Tab *tab = d->at(index))
        return tab->accessibleName;
    return QString();
}
#endif // QT_NO_ACCESSIBILITY

CloseButton::CloseButton(QWidget *parent)
    : QAbstractButton(parent)
{
    setFocusPolicy(Qt::NoFocus);
#ifndef QT_NO_CURSOR
    setCursor(Qt::ArrowCursor);
#endif
#ifndef QT_NO_TOOLTIP
    setToolTip(tr("Close Tab"));
#endif
    resize(sizeHint());
}

QSize CloseButton::sizeHint() const
{
    ensurePolished();
    int width = style()->pixelMetric(QStyle::PM_TabCloseIndicatorWidth, 0, this);
    int height = style()->pixelMetric(QStyle::PM_TabCloseIndicatorHeight, 0, this);
    return QSize(width, height);
}

QSize CloseButton::minimumSizeHint() const
{
    return sizeHint();
}

void CloseButton::enterEvent(QEvent *event)
{
    if (isEnabled())
        update();
    QAbstractButton::enterEvent(event);
}

void CloseButton::leaveEvent(QEvent *event)
{
    if (isEnabled())
        update();
    QAbstractButton::leaveEvent(event);
}

void CloseButton::paintEvent(QPaintEvent *)
{
    QPainter p(this);
    QStyleOption opt;
    opt.init(this);
    opt.state |= QStyle::State_AutoRaise;
    if (isEnabled() && underMouse() && !isChecked() && !isDown())
        opt.state |= QStyle::State_Raised;
    if (isChecked())
        opt.state |= QStyle::State_On;
    if (isDown())
        opt.state |= QStyle::State_Sunken;

    if (const KCustomTabBar *tb = qobject_cast<const KCustomTabBar *>(parent())) {
        int index = tb->currentIndex();
        KCustomTabBar::ButtonPosition position = (KCustomTabBar::ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, 0, tb);
        if (tb->tabButton(index, position) == this)
            opt.state |= QStyle::State_Selected;
    }

    style()->drawPrimitive(QStyle::PE_IndicatorTabClose, &opt, &p, this);
}

#if QT_CONFIG(animation)
void KCustomTabBarPrivate::Tab::TabBarAnimation::updateCurrentValue(const QVariant &current)
{
    priv->moveTab(priv->tabList.indexOf(*tab), current.toInt());
}

void KCustomTabBarPrivate::Tab::TabBarAnimation::updateState(QAbstractAnimation::State newState, QAbstractAnimation::State)
{
    if (newState == Stopped)
        priv->moveTabFinished(priv->tabList.indexOf(*tab));
}
#endif

QT_END_NAMESPACE

#include "kcustomtabbar.moc"
#include "moc_kcustomtabbar.cpp"
