/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the QtGui module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qmainwindowlayout_p.h"
#include "qdockarealayout_p.h"

#ifndef QT_NO_MAINWINDOW
#include "qdockwidget.h"
#include "qdockwidget_p.h"
#include "qtoolbar_p.h"
#include "qmainwindow.h"
#include "qmainwindowlayout_p.h"
#include "qtoolbar.h"
#include "qtoolbarlayout_p.h"
#include "qwidgetanimator_p.h"
#include "qrubberband.h"
#include "qdockwidget_p.h"
#include "qtabbar_p.h"

#include <qapplication.h>
#include <qstatusbar.h>
#include <qstring.h>
#include <qstyle.h>
#include <qvarlengtharray.h>
#include <qstack.h>
#include <qmap.h>
#include <qtimer.h>

#include <qdebug.h>

#include <private/qapplication_p.h>
#include <private/qlayoutengine_p.h>
#ifdef Q_WS_MAC
#   include <private/qcore_mac_p.h>
#   include <private/qt_cocoa_helpers_mac_p.h>
#endif

#ifdef Q_DEBUG_MAINWINDOW_LAYOUT
#   include <QTextStream>
#endif

QT_BEGIN_NAMESPACE

/******************************************************************************
** debug
*/

#if defined(Q_DEBUG_MAINWINDOW_LAYOUT) && !defined(QT_NO_DOCKWIDGET)

static QTextStream qout(stderr, QIODevice::WriteOnly);

static void dumpLayout(QTextStream &qout, const QDockAreaLayoutInfo &layout, QString indent);

static void dumpLayout(QTextStream &qout, const QDockAreaLayoutItem &item, QString indent)
{
    qout << indent << "QDockAreaLayoutItem: "
            << "pos: " << item.pos << " size:" << item.size
            << " gap:" << (item.flags & QDockAreaLayoutItem::GapItem)
            << " keepSize:" << (item.flags & QDockAreaLayoutItem::KeepSize) << '\n';
    indent += QLatin1String("  ");
    if (item.widgetItem != 0) {
        qout << indent << "widget: "
            << item.widgetItem->widget()->metaObject()->className()
            << ' ' << item.widgetItem->widget()->windowTitle() << '\n';
    } else if (item.subinfo != 0) {
        qout << indent << "subinfo:\n";
        dumpLayout(qout, *item.subinfo, indent + QLatin1String("  "));
    } else if (item.placeHolderItem != 0) {
        QRect r = item.placeHolderItem->topLevelRect;
        qout << indent << "placeHolder: "
            << "pos: " << item.pos << " size:" << item.size
            << " gap:" << (item.flags & QDockAreaLayoutItem::GapItem)
            << " keepSize:" << (item.flags & QDockAreaLayoutItem::KeepSize)
            << " objectName:" << item.placeHolderItem->objectName
            << " hidden:" << item.placeHolderItem->hidden
            << " window:" << item.placeHolderItem->window
            << " rect:" << r.x() << ',' << r.y() << ' '
            << r.width() << 'x' << r.height() << '\n';
    }
    qout.flush();
}

static void dumpLayout(QTextStream &qout, const QDockAreaLayoutInfo &layout, QString indent)
{
    qout << indent << "QDockAreaLayoutInfo: "
            << layout.rect.left() << ','
            << layout.rect.top() << ' '
            << layout.rect.width() << 'x'
            << layout.rect.height()
            << " orient:" << layout.o
            << " tabbed:" << layout.tabbed
            << " tbshape:" << layout.tabBarShape << '\n';

    indent += QLatin1String("  ");

    for (int i = 0; i < layout.item_list.count(); ++i) {
        qout << indent << "Item: " << i << '\n';
        dumpLayout(qout, layout.item_list.at(i), indent + QLatin1String("  "));
    }
    qout.flush();
};

static void dumpLayout(QTextStream &qout, const QDockAreaLayout &layout, QString indent)
{
    qout << indent << "QDockAreaLayout: "
            << layout.rect.left() << ','
            << layout.rect.top() << ' '
            << layout.rect.width() << 'x'
            << layout.rect.height() << '\n';

    qout << indent << "TopDockArea:\n";
    dumpLayout(qout, layout.docks[QInternal::TopDock], indent + QLatin1String("  "));
    qout << indent << "LeftDockArea:\n";
    dumpLayout(qout, layout.docks[QInternal::LeftDock], indent + QLatin1String("  "));
    qout << indent << "RightDockArea:\n";
    dumpLayout(qout, layout.docks[QInternal::RightDock], indent + QLatin1String("  "));
    qout << indent << "BottomDockArea:\n";
    dumpLayout(qout, layout.docks[QInternal::BottomDock], indent + QLatin1String("  "));

    qout.flush();
};

void qt_dumpLayout(QTextStream &qout, QMainWindow *window)
{
    QMainWindowLayout *layout = qobject_cast<QMainWindowLayout*>(window->layout());
    dumpLayout(qout, layout->layoutState.dockAreaLayout, QString());
}

#endif // Q_DEBUG_MAINWINDOW_LAYOUT && !QT_NO_DOCKWIDGET

/******************************************************************************
** QMainWindowLayoutState
*/

// we deal with all the #ifndefferry here so QMainWindowLayout code is clean

QMainWindowLayoutState::QMainWindowLayoutState(QMainWindow *win)
    :
#ifndef QT_NO_TOOLBAR
    toolBarAreaLayout(win),
#endif
#ifndef QT_NO_DOCKWIDGET
    dockAreaLayout(win)
#else
    centralWidgetItem(0)
#endif

{
    mainWindow = win;
}

QSize QMainWindowLayoutState::sizeHint() const
{

    QSize result(0, 0);

#ifndef QT_NO_DOCKWIDGET
    result = dockAreaLayout.sizeHint();
#else
    if (centralWidgetItem != 0)
        result = centralWidgetItem->sizeHint();
#endif

#ifndef QT_NO_TOOLBAR
    result = toolBarAreaLayout.sizeHint(result);
#endif // QT_NO_TOOLBAR

    return result;
}

QSize QMainWindowLayoutState::minimumSize() const
{
    QSize result(0, 0);

#ifndef QT_NO_DOCKWIDGET
    result = dockAreaLayout.minimumSize();
#else
    if (centralWidgetItem != 0)
        result = centralWidgetItem->minimumSize();
#endif

#ifndef QT_NO_TOOLBAR
    result = toolBarAreaLayout.minimumSize(result);
#endif // QT_NO_TOOLBAR

    return result;
}

void QMainWindowLayoutState::apply(bool animated)
{
#ifndef QT_NO_TOOLBAR
    toolBarAreaLayout.apply(animated);
#endif

#ifndef QT_NO_DOCKWIDGET
//    dumpLayout(dockAreaLayout, QString());
    dockAreaLayout.apply(animated);
#else
    if (centralWidgetItem != 0) {
        QMainWindowLayout *layout = qobject_cast<QMainWindowLayout*>(mainWindow->layout());
        Q_ASSERT(layout != 0);
        layout->widgetAnimator.animate(centralWidgetItem->widget(), centralWidgetRect, animated);
    }
#endif
}

void QMainWindowLayoutState::fitLayout()
{
    QRect r;
#ifdef QT_NO_TOOLBAR
    r = rect;
#else
    toolBarAreaLayout.rect = rect;
    r = toolBarAreaLayout.fitLayout();
#endif // QT_NO_TOOLBAR

#ifndef QT_NO_DOCKWIDGET
    dockAreaLayout.rect = r;
    dockAreaLayout.fitLayout();
#else
    centralWidgetRect = r;
#endif
}

void QMainWindowLayoutState::deleteAllLayoutItems()
{
#ifndef QT_NO_TOOLBAR
    toolBarAreaLayout.deleteAllLayoutItems();
#endif

#ifndef QT_NO_DOCKWIDGET
    dockAreaLayout.deleteAllLayoutItems();
#endif
}

void QMainWindowLayoutState::deleteCentralWidgetItem()
{
#ifndef QT_NO_DOCKWIDGET
    delete dockAreaLayout.centralWidgetItem;
    dockAreaLayout.centralWidgetItem = 0;
#else
    delete centralWidgetItem;
    centralWidgetItem = 0;
#endif
}

QLayoutItem *QMainWindowLayoutState::itemAt(int index, int *x) const
{
#ifndef QT_NO_TOOLBAR
    if (QLayoutItem *ret = toolBarAreaLayout.itemAt(x, index))
        return ret;
#endif

#ifndef QT_NO_DOCKWIDGET
    if (QLayoutItem *ret = dockAreaLayout.itemAt(x, index))
        return ret;
#else
    if (centralWidgetItem != 0 && (*x)++ == index)
        return centralWidgetItem;
#endif

    return 0;
}

QLayoutItem *QMainWindowLayoutState::takeAt(int index, int *x)
{
#ifndef QT_NO_TOOLBAR
    if (QLayoutItem *ret = toolBarAreaLayout.takeAt(x, index))
        return ret;
#endif

#ifndef QT_NO_DOCKWIDGET
    if (QLayoutItem *ret = dockAreaLayout.takeAt(x, index))
        return ret;
#else
    if (centralWidgetItem != 0 && (*x)++ == index) {
        QLayoutItem *ret = centralWidgetItem;
        centralWidgetItem = 0;
        return ret;
    }
#endif

    return 0;
}

QList<int> QMainWindowLayoutState::indexOf(QWidget *widget) const
{
    QList<int> result;

#ifndef QT_NO_TOOLBAR
    // is it a toolbar?
    if (QToolBar *toolBar = qobject_cast<QToolBar*>(widget)) {
        result = toolBarAreaLayout.indexOf(toolBar);
        if (!result.isEmpty())
            result.prepend(0);
        return result;
    }
#endif

#ifndef QT_NO_DOCKWIDGET
    // is it a dock widget?
    if (QDockWidget *dockWidget = qobject_cast<QDockWidget *>(widget)) {
        result = dockAreaLayout.indexOf(dockWidget);
        if (!result.isEmpty())
            result.prepend(1);
        return result;
    }
#endif //QT_NO_DOCKWIDGET

    return result;
}

bool QMainWindowLayoutState::contains(QWidget *widget) const
{
#ifndef QT_NO_DOCKWIDGET
    if (dockAreaLayout.centralWidgetItem != 0 && dockAreaLayout.centralWidgetItem->widget() == widget)
        return true;
    if (!dockAreaLayout.indexOf(widget).isEmpty())
        return true;
#else
    if (centralWidgetItem != 0 && centralWidgetItem->widget() == widget)
        return true;
#endif

#ifndef QT_NO_TOOLBAR
    if (!toolBarAreaLayout.indexOf(widget).isEmpty())
        return true;
#endif
    return false;
}

void QMainWindowLayoutState::setCentralWidget(QWidget *widget)
{
    QLayoutItem *item = 0;
    //make sure we remove the widget
    deleteCentralWidgetItem();

    if (widget != 0)
        item = new QWidgetItemV2(widget);

#ifndef QT_NO_DOCKWIDGET
    dockAreaLayout.centralWidgetItem = item;
#else
    centralWidgetItem = item;
#endif
}

QWidget *QMainWindowLayoutState::centralWidget() const
{
    QLayoutItem *item = 0;

#ifndef QT_NO_DOCKWIDGET
    item = dockAreaLayout.centralWidgetItem;
#else
    item = centralWidgetItem;
#endif

    if (item != 0)
        return item->widget();
    return 0;
}

QList<int> QMainWindowLayoutState::gapIndex(QWidget *widget,
                                            const QPoint &pos) const
{
    QList<int> result;

#ifndef QT_NO_TOOLBAR
    // is it a toolbar?
    if (qobject_cast<QToolBar*>(widget) != 0) {
        result = toolBarAreaLayout.gapIndex(pos);
        if (!result.isEmpty())
            result.prepend(0);
        return result;
    }
#endif

#ifndef QT_NO_DOCKWIDGET
    // is it a dock widget?
    if (qobject_cast<QDockWidget *>(widget) != 0) {
        result = dockAreaLayout.gapIndex(pos);
        if (!result.isEmpty())
            result.prepend(1);
        return result;
    }
#endif //QT_NO_DOCKWIDGET

    return result;
}

bool QMainWindowLayoutState::insertGap(const QList<int> &path, QLayoutItem *item)
{
    if (path.isEmpty())
        return false;

    int i = path.first();

#ifndef QT_NO_TOOLBAR
    if (i == 0) {
        Q_ASSERT(qobject_cast<QToolBar*>(item->widget()) != 0);
        return toolBarAreaLayout.insertGap(path.mid(1), item);
    }
#endif

#ifndef QT_NO_DOCKWIDGET
    if (i == 1) {
        Q_ASSERT(qobject_cast<QDockWidget*>(item->widget()) != 0);
        return dockAreaLayout.insertGap(path.mid(1), item);
    }
#endif //QT_NO_DOCKWIDGET

    return false;
}

void QMainWindowLayoutState::remove(const QList<int> &path)
{
    int i = path.first();

#ifndef QT_NO_TOOLBAR
    if (i == 0)
        toolBarAreaLayout.remove(path.mid(1));
#endif

#ifndef QT_NO_DOCKWIDGET
    if (i == 1)
        dockAreaLayout.remove(path.mid(1));
#endif //QT_NO_DOCKWIDGET
}

void QMainWindowLayoutState::remove(QLayoutItem *item)
{
#ifndef QT_NO_TOOLBAR
    toolBarAreaLayout.remove(item);
#endif

#ifndef QT_NO_DOCKWIDGET
    // is it a dock widget?
    if (QDockWidget *dockWidget = qobject_cast<QDockWidget *>(item->widget())) {
        QList<int> path = dockAreaLayout.indexOf(dockWidget);
        if (!path.isEmpty())
            dockAreaLayout.remove(path);
    }
#endif //QT_NO_DOCKWIDGET
}

void QMainWindowLayoutState::clear()
{
#ifndef QT_NO_TOOLBAR
    toolBarAreaLayout.clear();
#endif

#ifndef QT_NO_DOCKWIDGET
    dockAreaLayout.clear();
#else
    centralWidgetRect = QRect();
#endif

    rect = QRect();
}

bool QMainWindowLayoutState::isValid() const
{
    return rect.isValid();
}

QLayoutItem *QMainWindowLayoutState::item(const QList<int> &path)
{
    int i = path.first();

#ifndef QT_NO_TOOLBAR
    if (i == 0)
        return toolBarAreaLayout.item(path.mid(1)).widgetItem;
#endif

#ifndef QT_NO_DOCKWIDGET
    if (i == 1)
        return dockAreaLayout.item(path.mid(1)).widgetItem;
#endif //QT_NO_DOCKWIDGET

    return 0;
}

QRect QMainWindowLayoutState::itemRect(const QList<int> &path) const
{
    int i = path.first();

#ifndef QT_NO_TOOLBAR
    if (i == 0)
        return toolBarAreaLayout.itemRect(path.mid(1));
#endif

#ifndef QT_NO_DOCKWIDGET
    if (i == 1)
        return dockAreaLayout.itemRect(path.mid(1));
#endif //QT_NO_DOCKWIDGET

    return QRect();
}

QRect QMainWindowLayoutState::gapRect(const QList<int> &path) const
{
    int i = path.first();

#ifndef QT_NO_TOOLBAR
    if (i == 0)
        return toolBarAreaLayout.itemRect(path.mid(1));
#endif

#ifndef QT_NO_DOCKWIDGET
    if (i == 1)
        return dockAreaLayout.gapRect(path.mid(1));
#endif //QT_NO_DOCKWIDGET

    return QRect();
}

QLayoutItem *QMainWindowLayoutState::plug(const QList<int> &path)
{
    int i = path.first();

#ifndef QT_NO_TOOLBAR
    if (i == 0)
        return toolBarAreaLayout.plug(path.mid(1));
#endif

#ifndef QT_NO_DOCKWIDGET
    if (i == 1)
        return dockAreaLayout.plug(path.mid(1));
#endif //QT_NO_DOCKWIDGET

    return 0;
}

QLayoutItem *QMainWindowLayoutState::unplug(const QList<int> &path, QMainWindowLayoutState *other)
{
    int i = path.first();

#ifdef QT_NO_TOOLBAR
    Q_UNUSED(other);
#else
    if (i == 0)
        return toolBarAreaLayout.unplug(path.mid(1), other ? &other->toolBarAreaLayout : 0);
#endif

#ifndef QT_NO_DOCKWIDGET
    if (i == 1)
        return dockAreaLayout.unplug(path.mid(1));
#endif //QT_NO_DOCKWIDGET

    return 0;
}

void QMainWindowLayoutState::saveState(QDataStream &stream) const
{
#ifndef QT_NO_DOCKWIDGET
    dockAreaLayout.saveState(stream);
#endif
#ifndef QT_NO_TOOLBAR
    toolBarAreaLayout.saveState(stream);
#endif
}

template <typename T>
static QList<T> findChildrenHelper(const QObject *o)
{
    const QObjectList &list = o->children();
    QList<T> result;

    for (int i=0; i < list.size(); ++i) {
        if (T t = qobject_cast<T>(list[i])) {
            result.append(t);
        }
    }

    return result;
}

//pre4.3 tests the format that was used before 4.3
bool QMainWindowLayoutState::checkFormat(QDataStream &stream, bool pre43)
{
#ifdef QT_NO_TOOLBAR
    Q_UNUSED(pre43);
#endif
    while (!stream.atEnd()) {
        uchar marker;
        stream >> marker;
        switch(marker)
        {
#ifndef QT_NO_TOOLBAR
            case QToolBarAreaLayout::ToolBarStateMarker:
            case QToolBarAreaLayout::ToolBarStateMarkerEx:
                {
                    QList<QToolBar *> toolBars = findChildrenHelper<QToolBar*>(mainWindow);
                    if (!toolBarAreaLayout.restoreState(stream, toolBars, marker,
                        pre43 /*testing 4.3 format*/, true /*testing*/)) {
                            return false;
                    }
                }
                break;
#endif // QT_NO_TOOLBAR

#ifndef QT_NO_DOCKWIDGET
            case QDockAreaLayout::DockWidgetStateMarker:
                {
                    QList<QDockWidget *> dockWidgets = findChildrenHelper<QDockWidget*>(mainWindow);
                    if (!dockAreaLayout.restoreState(stream, dockWidgets, true /*testing*/)) {
                        return false;
                    }
                }
                break;
#endif
            default:
                //there was an error during the parsing
                return false;
        }// switch
    } //while

    //everything went fine: it must be a pre-4.3 saved state
    return true;
}

bool QMainWindowLayoutState::restoreState(QDataStream &_stream,
                                        const QMainWindowLayoutState &oldState)
{
    //make a copy of the data so that we can read it more than once
    QByteArray copy;
    while(!_stream.atEnd()) {
        int length = 1024;
        QByteArray ba(length, '\0');
        length = _stream.readRawData(ba.data(), ba.size());
        ba.resize(length);
        copy += ba;
    }

    QDataStream ds(copy);
    const bool oldFormat = !checkFormat(ds, false);
    if (oldFormat) {
        //we should try with the old format
        QDataStream ds2(copy);
        if (!checkFormat(ds2, true)) {
            return false; //format unknown
        }
    }

    QDataStream stream(copy);

    while (!stream.atEnd()) {
        uchar marker;
        stream >> marker;
        switch(marker)
        {
#ifndef QT_NO_DOCKWIDGET
            case QDockAreaLayout::DockWidgetStateMarker:
                {
                    QList<QDockWidget *> dockWidgets = findChildrenHelper<QDockWidget*>(mainWindow);
                    if (!dockAreaLayout.restoreState(stream, dockWidgets))
                        return false;

                    for (int i = 0; i < dockWidgets.size(); ++i) {
                        QDockWidget *w = dockWidgets.at(i);
                        QList<int> path = dockAreaLayout.indexOf(w);
                        if (path.isEmpty()) {
                            QList<int> oldPath = oldState.dockAreaLayout.indexOf(w);
                            if (oldPath.isEmpty()) {
                                continue;
                            }
                            QDockAreaLayoutInfo *info = dockAreaLayout.info(oldPath);
                            if (info == 0) {
                                continue;
                            }
                            info->item_list.append(QDockAreaLayoutItem(new QDockWidgetItem(w)));
                        }
                    }
                }
                break;
#endif // QT_NO_DOCKWIDGET

#ifndef QT_NO_TOOLBAR
            case QToolBarAreaLayout::ToolBarStateMarker:
            case QToolBarAreaLayout::ToolBarStateMarkerEx:
                {
                    QList<QToolBar *> toolBars = findChildrenHelper<QToolBar*>(mainWindow);
                    if (!toolBarAreaLayout.restoreState(stream, toolBars, marker, oldFormat))
                        return false;

                    for (int i = 0; i < toolBars.size(); ++i) {
                        QToolBar *w = toolBars.at(i);
                        QList<int> path = toolBarAreaLayout.indexOf(w);
                        if (path.isEmpty()) {
                            QList<int> oldPath = oldState.toolBarAreaLayout.indexOf(w);
                            if (oldPath.isEmpty()) {
                                continue;
                            }
                            toolBarAreaLayout.docks[oldPath.at(0)].insertToolBar(0, w);
                        }
                    }
                }
                break;
#endif //QT_NO_TOOLBAR
            default:
                return false;
        }// switch
    } //while


    return true;
}

/******************************************************************************
** QMainWindowLayoutState - toolbars
*/

#ifndef QT_NO_TOOLBAR

static inline void validateToolBarArea(Qt::ToolBarArea &area)
{
    switch (area) {
    case Qt::LeftToolBarArea:
    case Qt::RightToolBarArea:
    case Qt::TopToolBarArea:
    case Qt::BottomToolBarArea:
        break;
    default:
        area = Qt::TopToolBarArea;
    }
}

static QInternal::DockPosition toDockPos(Qt::ToolBarArea area)
{
    switch (area) {
        case Qt::LeftToolBarArea: return QInternal::LeftDock;
        case Qt::RightToolBarArea: return QInternal::RightDock;
        case Qt::TopToolBarArea: return QInternal::TopDock;
        case Qt::BottomToolBarArea: return QInternal::BottomDock;
        default:
            break;
    }

    return QInternal::DockCount;
}

static Qt::ToolBarArea toToolBarArea(QInternal::DockPosition pos)
{
    switch (pos) {
        case QInternal::LeftDock:   return Qt::LeftToolBarArea;
        case QInternal::RightDock:  return Qt::RightToolBarArea;
        case QInternal::TopDock:    return Qt::TopToolBarArea;
        case QInternal::BottomDock: return Qt::BottomToolBarArea;
        default: break;
    }
    return Qt::NoToolBarArea;
}

static inline Qt::ToolBarArea toToolBarArea(int pos)
{
    return toToolBarArea(static_cast<QInternal::DockPosition>(pos));
}

void QMainWindowLayout::addToolBarBreak(Qt::ToolBarArea area)
{
    validateToolBarArea(area);

    layoutState.toolBarAreaLayout.addToolBarBreak(toDockPos(area));
    if (savedState.isValid())
        savedState.toolBarAreaLayout.addToolBarBreak(toDockPos(area));

    invalidate();
}

void QMainWindowLayout::insertToolBarBreak(QToolBar *before)
{
    layoutState.toolBarAreaLayout.insertToolBarBreak(before);
    if (savedState.isValid())
        savedState.toolBarAreaLayout.insertToolBarBreak(before);
    invalidate();
}

void QMainWindowLayout::removeToolBarBreak(QToolBar *before)
{
    layoutState.toolBarAreaLayout.removeToolBarBreak(before);
    if (savedState.isValid())
        savedState.toolBarAreaLayout.removeToolBarBreak(before);
    invalidate();
}

void QMainWindowLayout::moveToolBar(QToolBar *toolbar, int pos)
{
    layoutState.toolBarAreaLayout.moveToolBar(toolbar, pos);
    if (savedState.isValid())
        savedState.toolBarAreaLayout.moveToolBar(toolbar, pos);
    invalidate();
}

/* Removes the toolbar from the mainwindow so that it can be added again. Does not
   explicitly hide the toolbar. */
void QMainWindowLayout::removeToolBar(QToolBar *toolbar)
{
    if (toolbar) {
        QObject::disconnect(parentWidget(), SIGNAL(iconSizeChanged(QSize)),
                   toolbar, SLOT(_q_updateIconSize(QSize)));
        QObject::disconnect(parentWidget(), SIGNAL(toolButtonStyleChanged(Qt::ToolButtonStyle)),
                   toolbar, SLOT(_q_updateToolButtonStyle(Qt::ToolButtonStyle)));

#ifdef Q_WS_MAC
        if (usesHIToolBar(toolbar)) {
            removeFromMacToolbar(toolbar);
        } else
#endif // Q_WS_MAC
        {
            removeWidget(toolbar);
        }
    }
}

/*!
    Adds \a toolbar to \a area, continuing the current line.
*/
void QMainWindowLayout::addToolBar(Qt::ToolBarArea area,
                                   QToolBar *toolbar,
                                   bool)
{
    validateToolBarArea(area);
#ifdef Q_WS_MAC
    if ((area == Qt::TopToolBarArea)
            && layoutState.mainWindow->unifiedTitleAndToolBarOnMac()) {
        insertIntoMacToolbar(0, toolbar);
    } else
#endif
    {
        //let's add the toolbar to the layout
        addChildWidget(toolbar);
        QLayoutItem * item = layoutState.toolBarAreaLayout.addToolBar(toDockPos(area), toolbar);
        if (savedState.isValid() && item) {
            // copy the toolbar also in the saved state
            savedState.toolBarAreaLayout.insertItem(toDockPos(area), item);
        }
        invalidate();

        //this ensures that the toolbar has the right window flags (not floating any more)
        toolbar->d_func()->updateWindowFlags(false /*floating*/);
    }
}

/*!
    Adds \a toolbar before \a before
*/
void QMainWindowLayout::insertToolBar(QToolBar *before, QToolBar *toolbar)
{
#ifdef Q_WS_MAC
    if (usesHIToolBar(before)) {
        insertIntoMacToolbar(before, toolbar);
    } else
#endif // Q_WS_MAC
    {
        addChildWidget(toolbar);
        QLayoutItem * item = layoutState.toolBarAreaLayout.insertToolBar(before, toolbar);
        if (savedState.isValid() && item) {
            // copy the toolbar also in the saved state
            savedState.toolBarAreaLayout.insertItem(before, item);
        }
        if (!currentGapPos.isEmpty() && currentGapPos.first() == 0) {
            currentGapPos = layoutState.toolBarAreaLayout.currentGapIndex();
            if (!currentGapPos.isEmpty()) {
                currentGapPos.prepend(0);
                currentGapRect = layoutState.itemRect(currentGapPos);
            }
        }
        invalidate();
    }
}

Qt::ToolBarArea QMainWindowLayout::toolBarArea(QToolBar *toolbar) const
{
    QInternal::DockPosition pos = layoutState.toolBarAreaLayout.findToolBar(toolbar);
    switch (pos) {
        case QInternal::LeftDock:   return Qt::LeftToolBarArea;
        case QInternal::RightDock:  return Qt::RightToolBarArea;
        case QInternal::TopDock:    return Qt::TopToolBarArea;
        case QInternal::BottomDock: return Qt::BottomToolBarArea;
        default: break;
    }
#ifdef Q_WS_MAC
    if (pos == QInternal::DockCount) {
        if (qtoolbarsInUnifiedToolbarList.contains(toolbar))
            return Qt::TopToolBarArea;
    }
#endif
    return Qt::NoToolBarArea;
}

bool QMainWindowLayout::toolBarBreak(QToolBar *toolBar) const
{
    return layoutState.toolBarAreaLayout.toolBarBreak(toolBar);
}

void QMainWindowLayout::getStyleOptionInfo(QStyleOptionToolBar *option, QToolBar *toolBar) const
{
    option->toolBarArea = toolBarArea(toolBar);
    layoutState.toolBarAreaLayout.getStyleOptionInfo(option, toolBar);
}

void QMainWindowLayout::toggleToolBarsVisible()
{
    bool updateNonUnifiedParts = true;
#ifdef Q_WS_MAC
    if (layoutState.mainWindow->unifiedTitleAndToolBarOnMac()) {
        // If we hit this case, someone has pressed the "toolbar button" which will
        // toggle the unified toolbar visibility, because that's what the user wants.
        // We might be in a situation where someone has hidden all the toolbars
        // beforehand (maybe in construction), but now they've hit this button and
        // and are expecting the items to show. What do we do?
        // 1) Check the visibility of all the toolbars, if one is visible, do nothing, this
        //    preserves what people would expect (these toolbars were visible when I clicked last time).
        // 2) If NONE are visible, then show them all. Again, this preserves the user expectation
        //    of, "I want to see the toolbars." The user may get more toolbars than expected, but this
        //    is better seeing nothing.
        // Don't worry about any of this if we are going invisible. This does mean we may get
        // into issues when switching into and out of fullscreen mode, but this is probably minor.
        // If we ever need to do hiding, that would have to be taken care of after the unified toolbar
        // has finished hiding.
        // People can of course handle the QEvent::ToolBarChange event themselves and do
        // WHATEVER they want if they don't like what we are doing (though the unified toolbar
        // will fire regardless).

        // Check if we REALLY need to update the geometry below. If we only have items in the
        // unified toolbar, all the docks will be empty, so there's very little point
        // in doing the geometry as Apple will do it (we also avoid flicker in Cocoa as well).
        // FWIW, layoutState.toolBarAreaLayout.visible and the state of the unified toolbar
        // visibility can get out of sync. I really don't think it's a big issue. It is kept
        // to a minimum because we only change the visibility if we absolutely must.
        // update the "non unified parts."
        updateNonUnifiedParts = !layoutState.toolBarAreaLayout.isEmpty();

        // We get this function before the unified toolbar does its thing.
        // So, the value will be opposite of what we expect.
        bool goingVisible = !macWindowToolbarIsVisible(qt_mac_window_for(layoutState.mainWindow));
        if (goingVisible) {
            const int ToolBarCount = qtoolbarsInUnifiedToolbarList.size();
            bool needAllVisible = true;
            for (int i = 0; i < ToolBarCount; ++i) {
                if (!qtoolbarsInUnifiedToolbarList.at(i)->isHidden()) {
                    needAllVisible = false;
                    break;
                }
            }
            if (needAllVisible) {
                QBoolBlocker blocker(blockVisiblityCheck);  // Disable the visibilty check because
                                                            // the toggle has already happened.
                for (int i = 0; i < ToolBarCount; ++i)
                    qtoolbarsInUnifiedToolbarList.at(i)->setVisible(true);
            }
        }
        if (!updateNonUnifiedParts)
            layoutState.toolBarAreaLayout.visible = goingVisible;
    }
#endif
    if (updateNonUnifiedParts) {
        layoutState.toolBarAreaLayout.visible = !layoutState.toolBarAreaLayout.visible;
        if (!layoutState.mainWindow->isMaximized()) {
            QPoint topLeft = parentWidget()->geometry().topLeft();
            QRect r = parentWidget()->geometry();
            r = layoutState.toolBarAreaLayout.rectHint(r);
            r.moveTo(topLeft);
            parentWidget()->setGeometry(r);
        } else {
            update();
        }
    }
}

#endif // QT_NO_TOOLBAR

/******************************************************************************
** QMainWindowLayoutState - dock areas
*/

#ifndef QT_NO_DOCKWIDGET

static inline void validateDockWidgetArea(Qt::DockWidgetArea &area)
{
    switch (area) {
    case Qt::LeftDockWidgetArea:
    case Qt::RightDockWidgetArea:
    case Qt::TopDockWidgetArea:
    case Qt::BottomDockWidgetArea:
        break;
    default:
        area = Qt::LeftDockWidgetArea;
    }
}

static QInternal::DockPosition toDockPos(Qt::DockWidgetArea area)
{
    switch (area) {
        case Qt::LeftDockWidgetArea: return QInternal::LeftDock;
        case Qt::RightDockWidgetArea: return QInternal::RightDock;
        case Qt::TopDockWidgetArea: return QInternal::TopDock;
        case Qt::BottomDockWidgetArea: return QInternal::BottomDock;
        default:
            break;
    }

    return QInternal::DockCount;
}

static Qt::DockWidgetArea toDockWidgetArea(QInternal::DockPosition pos)
{
    switch (pos) {
        case QInternal::LeftDock : return Qt::LeftDockWidgetArea;
        case QInternal::RightDock : return Qt::RightDockWidgetArea;
        case QInternal::TopDock : return Qt::TopDockWidgetArea;
        case QInternal::BottomDock : return Qt::BottomDockWidgetArea;
        default:
            break;
    }

    return Qt::NoDockWidgetArea;
}

inline static Qt::DockWidgetArea toDockWidgetArea(int pos)
{
    return toDockWidgetArea(static_cast<QInternal::DockPosition>(pos));
}

void QMainWindowLayout::setCorner(Qt::Corner corner, Qt::DockWidgetArea area)
{
    if (layoutState.dockAreaLayout.corners[corner] == area)
        return;
    layoutState.dockAreaLayout.corners[corner] = area;
    if (savedState.isValid())
        savedState.dockAreaLayout.corners[corner] = area;
    invalidate();
}

Qt::DockWidgetArea QMainWindowLayout::corner(Qt::Corner corner) const
{
    return layoutState.dockAreaLayout.corners[corner];
}

void QMainWindowLayout::addDockWidget(Qt::DockWidgetArea area,
                                             QDockWidget *dockwidget,
                                             Qt::Orientation orientation)
{
    addChildWidget(dockwidget);

    // If we are currently moving a separator, then we need to abort the move, since each
    // time we move the mouse layoutState is replaced by savedState modified by the move.
    if (!movingSeparator.isEmpty())
        endSeparatorMove(movingSeparatorPos);

    layoutState.dockAreaLayout.addDockWidget(toDockPos(area), dockwidget, orientation);
    emit dockwidget->dockLocationChanged(area);
    invalidate();
}

void QMainWindowLayout::tabifyDockWidget(QDockWidget *first, QDockWidget *second)
{
    addChildWidget(second);
    layoutState.dockAreaLayout.tabifyDockWidget(first, second);
    emit second->dockLocationChanged(dockWidgetArea(first));
    invalidate();
}

bool QMainWindowLayout::restoreDockWidget(QDockWidget *dockwidget)
{
    addChildWidget(dockwidget);
    if (!layoutState.dockAreaLayout.restoreDockWidget(dockwidget))
        return false;
    emit dockwidget->dockLocationChanged(dockWidgetArea(dockwidget));
    invalidate();
    return true;
}

#ifndef QT_NO_TABBAR
bool QMainWindowLayout::documentMode() const
{
    return _documentMode;
}

void QMainWindowLayout::setDocumentMode(bool enabled)
{
    if (_documentMode == enabled)
        return;

    _documentMode = enabled;

    // Update the document mode for all tab bars
    foreach (QTabBar *bar, usedTabBars)
        bar->setDocumentMode(_documentMode);
    foreach (QTabBar *bar, unusedTabBars)
        bar->setDocumentMode(_documentMode);
}
#endif // QT_NO_TABBAR

void QMainWindowLayout::setVerticalTabsEnabled(bool enabled)
{
#ifdef QT_NO_TABBAR
    Q_UNUSED(enabled);
#else
    if (verticalTabsEnabled == enabled)
        return;

    verticalTabsEnabled = enabled;

    updateTabBarShapes();
#endif // QT_NO_TABBAR
}

#ifndef QT_NO_TABWIDGET
QTabWidget::TabShape QMainWindowLayout::tabShape() const
{
    return _tabShape;
}

void QMainWindowLayout::setTabShape(QTabWidget::TabShape tabShape)
{
    if (_tabShape == tabShape)
        return;

    _tabShape = tabShape;

    updateTabBarShapes();
}

QTabWidget::TabPosition QMainWindowLayout::tabPosition(Qt::DockWidgetArea area) const
{
    return tabPositions[toDockPos(area)];
}

void QMainWindowLayout::setTabPosition(Qt::DockWidgetAreas areas, QTabWidget::TabPosition tabPosition)
{
    const Qt::DockWidgetArea dockWidgetAreas[] = {
        Qt::TopDockWidgetArea,
        Qt::LeftDockWidgetArea,
        Qt::BottomDockWidgetArea,
        Qt::RightDockWidgetArea
    };
    const QInternal::DockPosition dockPositions[] = {
        QInternal::TopDock,
        QInternal::LeftDock,
        QInternal::BottomDock,
        QInternal::RightDock
    };

    for (int i = 0; i < QInternal::DockCount; ++i)
        if (areas & dockWidgetAreas[i])
            tabPositions[dockPositions[i]] = tabPosition;

    updateTabBarShapes();
}

static inline QTabBar::Shape tabBarShapeFrom(QTabWidget::TabShape shape, QTabWidget::TabPosition position)
{
    const bool rounded = (shape == QTabWidget::Rounded);
    if (position == QTabWidget::North)
        return rounded ? QTabBar::RoundedNorth : QTabBar::TriangularNorth;
    if (position == QTabWidget::South)
        return rounded ? QTabBar::RoundedSouth : QTabBar::TriangularSouth;
    if (position == QTabWidget::East)
        return rounded ? QTabBar::RoundedEast : QTabBar::TriangularEast;
    if (position == QTabWidget::West)
        return rounded ? QTabBar::RoundedWest : QTabBar::TriangularWest;
    return QTabBar::RoundedNorth;
}
#endif // QT_NO_TABWIDGET

#ifndef QT_NO_TABBAR
void QMainWindowLayout::updateTabBarShapes()
{
#ifndef QT_NO_TABWIDGET
    const QTabWidget::TabPosition vertical[] = {
        QTabWidget::West,
        QTabWidget::East,
        QTabWidget::North,
        QTabWidget::South
    };
#else
    const QTabBar::Shape vertical[] = {
        QTabBar::RoundedWest,
        QTabBar::RoundedEast,
        QTabBar::RoundedNorth,
        QTabBar::RoundedSouth
    };
#endif

    QDockAreaLayout &layout = layoutState.dockAreaLayout;

    for (int i = 0; i < QInternal::DockCount; ++i) {
#ifndef QT_NO_TABWIDGET
        QTabWidget::TabPosition pos = verticalTabsEnabled ? vertical[i] : tabPositions[i];
        QTabBar::Shape shape = tabBarShapeFrom(_tabShape, pos);
#else
        QTabBar::Shape shape = verticalTabsEnabled ? vertical[i] : QTabBar::RoundedSouth;
#endif
        layout.docks[i].setTabBarShape(shape);
    }
}
#endif // QT_NO_TABBAR

void QMainWindowLayout::splitDockWidget(QDockWidget *after,
                                        QDockWidget *dockwidget,
                                        Qt::Orientation orientation)
{
    addChildWidget(dockwidget);
    layoutState.dockAreaLayout.splitDockWidget(after, dockwidget, orientation);
    emit dockwidget->dockLocationChanged(dockWidgetArea(after));
    invalidate();
}

Qt::DockWidgetArea QMainWindowLayout::dockWidgetArea(QDockWidget *widget) const
{
    QList<int> pathToWidget = layoutState.dockAreaLayout.indexOf(widget);
    if (pathToWidget.isEmpty())
        return Qt::NoDockWidgetArea;
    return toDockWidgetArea(pathToWidget.first());
}

void QMainWindowLayout::keepSize(QDockWidget *w)
{
    layoutState.dockAreaLayout.keepSize(w);
}

#ifndef QT_NO_TABBAR

class QMainWindowTabBar : public QTabBar
{
public:
    QMainWindowTabBar(QWidget *parent);
protected:
    bool event(QEvent *e);
};

QMainWindowTabBar::QMainWindowTabBar(QWidget *parent)
    : QTabBar(parent)
{
    setExpanding(false);
}

bool QMainWindowTabBar::event(QEvent *e)
{
    // show the tooltip if tab is too small to fit label

    if (e->type() != QEvent::ToolTip)
        return QTabBar::event(e);
    QSize size = this->size();
    QSize hint = sizeHint();
    if (shape() == QTabBar::RoundedWest || shape() == QTabBar::RoundedEast) {
        size.transpose();
        hint.transpose();
    }
    if (size.width() < hint.width())
        return QTabBar::event(e);
    e->accept();
    return true;
}

QTabBar *QMainWindowLayout::getTabBar()
{
    QTabBar *result = 0;
    if (!unusedTabBars.isEmpty()) {
        result = unusedTabBars.takeLast();
    } else {
        result = new QMainWindowTabBar(parentWidget());
        result->setDrawBase(true);
        result->setElideMode(Qt::ElideRight);
        result->setDocumentMode(_documentMode);
        connect(result, SIGNAL(currentChanged(int)), this, SLOT(tabChanged()));
    }

    usedTabBars.insert(result);
    return result;
}

// Allocates a new separator widget if needed
QWidget *QMainWindowLayout::getSeparatorWidget()
{
    QWidget *result = 0;
    if (!unusedSeparatorWidgets.isEmpty()) {
        result = unusedSeparatorWidgets.takeLast();
    } else {
        result = new QWidget(parentWidget());
        result->setAttribute(Qt::WA_MouseNoMask, true);
        result->setAutoFillBackground(false);
        result->setObjectName(QLatin1String("qt_qmainwindow_extended_splitter"));
    }
    usedSeparatorWidgets.insert(result);
    return result;
}

void QMainWindowLayout::tabChanged()
{
    QTabBar *tb = qobject_cast<QTabBar*>(sender());
    if (tb == 0)
        return;
    QDockAreaLayoutInfo *info = layoutState.dockAreaLayout.info(tb);
    if (info == 0)
        return;
    info->apply(false);

    if (QWidget *w = centralWidget())
        w->raise();
}
#endif // QT_NO_TABBAR

bool QMainWindowLayout::startSeparatorMove(const QPoint &pos)
{
    movingSeparator = layoutState.dockAreaLayout.findSeparator(pos);

    if (movingSeparator.isEmpty())
        return false;

    savedState = layoutState;
    movingSeparatorPos = movingSeparatorOrigin = pos;

    return true;
}

bool QMainWindowLayout::separatorMove(const QPoint &pos)
{
    if (movingSeparator.isEmpty())
        return false;
    movingSeparatorPos = pos;
    separatorMoveTimer.start(0, this);
    return true;
}

bool QMainWindowLayout::endSeparatorMove(const QPoint&)
{
    bool result = !movingSeparator.isEmpty();
    movingSeparator.clear();
    savedState.clear();
    return result;
}

void QMainWindowLayout::raise(QDockWidget *widget)
{
    QDockAreaLayoutInfo *info = layoutState.dockAreaLayout.info(widget);
    if (info == 0)
        return;
#ifndef QT_NO_TABBAR
    if (!info->tabbed)
        return;
    info->setCurrentTab(widget);
#endif
}

#endif // QT_NO_DOCKWIDGET


/******************************************************************************
** QMainWindowLayoutState - layout interface
*/

int QMainWindowLayout::count() const
{
    qWarning("QMainWindowLayout::count: ?");
    return 0; //#################################################
}

QLayoutItem *QMainWindowLayout::itemAt(int index) const
{
    int x = 0;

    if (QLayoutItem *ret = layoutState.itemAt(index, &x))
        return ret;

    if (statusbar && x++ == index)
        return statusbar;

    return 0;
}

QLayoutItem *QMainWindowLayout::takeAt(int index)
{
    int x = 0;

    if (QLayoutItem *ret = layoutState.takeAt(index, &x)) {
        // the widget might in fact have been destroyed by now
        if (QWidget *w = ret->widget()) {
            widgetAnimator.abort(w);
            if (w == pluggingWidget)
                pluggingWidget = 0;
        }

        if (savedState.isValid() ) {
            //we need to remove the item also from the saved state to prevent crash
            savedState.remove(ret);
            //Also, the item may be contained several times as a gap item.
            layoutState.remove(ret);
        }

#ifndef QT_NO_TOOLBAR
        if (!currentGapPos.isEmpty() && currentGapPos.first() == 0) {
            currentGapPos = layoutState.toolBarAreaLayout.currentGapIndex();
            if (!currentGapPos.isEmpty()) {
                currentGapPos.prepend(0);
                currentGapRect = layoutState.itemRect(currentGapPos);
            }
        }
#endif

        return ret;
    }

    if (statusbar && x++ == index) {
        QLayoutItem *ret = statusbar;
        statusbar = 0;
        return ret;
    }

    return 0;
}

void QMainWindowLayout::setGeometry(const QRect &_r)
{
    if (savedState.isValid())
        return;

    QRect r = _r;

    QLayout::setGeometry(r);

    if (statusbar) {
        QRect sbr(QPoint(0, 0),
                  QSize(r.width(), statusbar->heightForWidth(r.width()))
                  .expandedTo(statusbar->minimumSize()));
        sbr.moveBottom(r.bottom());
        QRect vr = QStyle::visualRect(parentWidget()->layoutDirection(), _r, sbr);
        statusbar->setGeometry(vr);
        r.setBottom(sbr.top() - 1);
    }

    layoutState.rect = r;
    layoutState.fitLayout();
    applyState(layoutState, false);
}

void QMainWindowLayout::addItem(QLayoutItem *)
{ qWarning("QMainWindowLayout::addItem: Please use the public QMainWindow API instead"); }

QSize QMainWindowLayout::sizeHint() const
{
    if (!szHint.isValid()) {
        szHint = layoutState.sizeHint();
        const QSize sbHint = statusbar ? statusbar->sizeHint() : QSize(0, 0);
        szHint = QSize(qMax(sbHint.width(), szHint.width()),
                        sbHint.height() + szHint.height());
    }
    return szHint;
}

QSize QMainWindowLayout::minimumSize() const
{
    if (!minSize.isValid()) {
        minSize = layoutState.minimumSize();
        const QSize sbMin = statusbar ? statusbar->minimumSize() : QSize(0, 0);
        minSize = QSize(qMax(sbMin.width(), minSize.width()),
                        sbMin.height() + minSize.height());
#ifdef Q_WS_MAC
        const QSize storedSize = minSize;
        int minWidth = 0;
        foreach (QToolBar *toolbar, qtoolbarsInUnifiedToolbarList) {
            minWidth += toolbar->sizeHint().width() + 20;
        }
        minSize = QSize(qMax(minWidth, storedSize.width()), storedSize.height());
#endif
    }
    return minSize;
}

void QMainWindowLayout::invalidate()
{
    QLayout::invalidate();
    minSize = szHint = QSize();
}

/******************************************************************************
** QMainWindowLayout - remaining stuff
*/

static void fixToolBarOrientation(QLayoutItem *item, int dockPos)
{
#ifndef QT_NO_TOOLBAR
    QToolBar *toolBar = qobject_cast<QToolBar*>(item->widget());
    if (toolBar == 0)
        return;

    QRect oldGeo = toolBar->geometry();

    QInternal::DockPosition pos
        = static_cast<QInternal::DockPosition>(dockPos);
    Qt::Orientation o = pos == QInternal::TopDock || pos == QInternal::BottomDock
                        ? Qt::Horizontal : Qt::Vertical;
    if (o != toolBar->orientation())
        toolBar->setOrientation(o);

    QSize hint = toolBar->sizeHint().boundedTo(toolBar->maximumSize())
                    .expandedTo(toolBar->minimumSize());

    if (toolBar->size() != hint) {
        QRect newGeo(oldGeo.topLeft(), hint);
        if (toolBar->layoutDirection() == Qt::RightToLeft)
            newGeo.moveRight(oldGeo.right());
        toolBar->setGeometry(newGeo);
    }

#else
    Q_UNUSED(item);
    Q_UNUSED(dockPos);
#endif
}

void QMainWindowLayout::revert(QLayoutItem *widgetItem)
{
    if (!savedState.isValid())
        return;

    QWidget *widget = widgetItem->widget();
    layoutState = savedState;
    currentGapPos = layoutState.indexOf(widget);
    fixToolBarOrientation(widgetItem, currentGapPos.at(1));
    layoutState.unplug(currentGapPos);
    layoutState.fitLayout();
    currentGapRect = layoutState.itemRect(currentGapPos);

    plug(widgetItem);
}

bool QMainWindowLayout::plug(QLayoutItem *widgetItem)
{
    if (!parentWidget()->isVisible() || parentWidget()->isMinimized() || currentGapPos.isEmpty())
        return false;

    fixToolBarOrientation(widgetItem, currentGapPos.at(1));

    QWidget *widget = widgetItem->widget();

    QList<int> previousPath = layoutState.indexOf(widget);

    QLayoutItem *it = layoutState.plug(currentGapPos);
    Q_ASSERT(it == widgetItem);
    Q_UNUSED(it);
    if (!previousPath.isEmpty())
        layoutState.remove(previousPath);

    pluggingWidget = widget;
    QRect globalRect = currentGapRect;
    globalRect.moveTopLeft(parentWidget()->mapToGlobal(globalRect.topLeft()));
#ifndef QT_NO_DOCKWIDGET
    if (qobject_cast<QDockWidget*>(widget) != 0) {
        QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(widget->layout());
        if (layout->nativeWindowDeco()) {
            globalRect.adjust(0, layout->titleHeight(), 0, 0);
        } else {
            int fw = widget->style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth, 0, widget);
            globalRect.adjust(-fw, -fw, fw, fw);
        }
    }
#endif
    widgetAnimator.animate(widget, globalRect, dockOptions & QMainWindow::AnimatedDocks);

    return true;
}

void QMainWindowLayout::animationFinished(QWidget *widget)
{
    //this function is called from within the Widget Animator whenever an animation is finished
    //on a certain widget
#ifndef QT_NO_TOOLBAR
    if (QToolBar *tb = qobject_cast<QToolBar*>(widget)) {
        QToolBarLayout *tbl = qobject_cast<QToolBarLayout*>(tb->layout());
        if (tbl->animating) {
            tbl->animating = false;
            if (tbl->expanded)
                tbl->layoutActions(tb->size());
            tb->update();
        }
    }
#endif

    if (widget == pluggingWidget) {

#ifndef QT_NO_DOCKWIDGET
        if (QDockWidget *dw = qobject_cast<QDockWidget*>(widget))
            dw->d_func()->plug(currentGapRect);
#endif
#ifndef QT_NO_TOOLBAR
        if (QToolBar *tb = qobject_cast<QToolBar*>(widget))
            tb->d_func()->plug(currentGapRect);
#endif

        savedState.clear();
        currentGapPos.clear();
        pluggingWidget = 0;
        //applying the state will make sure that the currentGap is updated correctly
        //and all the geometries (especially the one from the central widget) is correct
        layoutState.apply(false);

#ifndef QT_NO_DOCKWIDGET
#ifndef QT_NO_TABBAR
        if (qobject_cast<QDockWidget*>(widget) != 0) {
            // info() might return null if the widget is destroyed while
            // animating but before the animationFinished signal is received.
            if (QDockAreaLayoutInfo *info = layoutState.dockAreaLayout.info(widget))
                info->setCurrentTab(widget);
        }
#endif
#endif
    }

    if (!widgetAnimator.animating()) {
        //all animations are finished
#ifndef QT_NO_DOCKWIDGET
        parentWidget()->update(layoutState.dockAreaLayout.separatorRegion());
#ifndef QT_NO_TABBAR
        foreach (QTabBar *tab_bar, usedTabBars)
            tab_bar->show();
#endif // QT_NO_TABBAR
#endif // QT_NO_DOCKWIDGET
    }

    updateGapIndicator();
}

void QMainWindowLayout::restore(bool keepSavedState)
{
    if (!savedState.isValid())
        return;

    layoutState = savedState;
    applyState(layoutState);
    if (!keepSavedState)
        savedState.clear();
    currentGapPos.clear();
    pluggingWidget = 0;
    updateGapIndicator();
}

QMainWindowLayout::QMainWindowLayout(QMainWindow *mainwindow)
    : QLayout(mainwindow)
    , layoutState(mainwindow)
    , savedState(mainwindow)
    , dockOptions(QMainWindow::AnimatedDocks | QMainWindow::AllowTabbedDocks)
    , statusbar(0)
#ifndef QT_NO_DOCKWIDGET
#ifndef QT_NO_TABBAR
    , _documentMode(false)
    , verticalTabsEnabled(false)
#ifndef QT_NO_TABWIDGET
    , _tabShape(QTabWidget::Rounded)
#endif
#endif
#endif // QT_NO_DOCKWIDGET
    , widgetAnimator(this)
    , pluggingWidget(0)
#ifndef QT_NO_RUBBERBAND
    , gapIndicator(new QRubberBand(QRubberBand::Rectangle, mainwindow))
#endif //QT_NO_RUBBERBAND
#ifdef Q_WS_MAC
    , blockVisiblityCheck(false)
#endif
{
#ifndef QT_NO_DOCKWIDGET
#ifndef QT_NO_TABBAR
    sep = mainwindow->style()->pixelMetric(QStyle::PM_DockWidgetSeparatorExtent, 0, mainwindow);
#endif

#ifndef QT_NO_TABWIDGET
    for (int i = 0; i < QInternal::DockCount; ++i)
        tabPositions[i] = QTabWidget::South;
#endif
#endif // QT_NO_DOCKWIDGET

#ifndef QT_NO_RUBBERBAND
    // For accessibility to identify this special widget.
    gapIndicator->setObjectName(QLatin1String("qt_rubberband"));
    gapIndicator->hide();
#endif
    pluggingWidget = 0;

    setObjectName(mainwindow->objectName() + QLatin1String("_layout"));
}

QMainWindowLayout::~QMainWindowLayout()
{
    layoutState.deleteAllLayoutItems();
    layoutState.deleteCentralWidgetItem();

#ifdef Q_WS_MAC
    cleanUpMacToolbarItems();
#endif

    delete statusbar;
}

void QMainWindowLayout::setDockOptions(QMainWindow::DockOptions opts)
{
    if (opts == dockOptions)
        return;

    dockOptions = opts;

#ifndef QT_NO_DOCKWIDGET
    setVerticalTabsEnabled(opts & QMainWindow::VerticalTabs);
#endif

    invalidate();
}

#ifndef QT_NO_STATUSBAR
QStatusBar *QMainWindowLayout::statusBar() const
{ return statusbar ? qobject_cast<QStatusBar *>(statusbar->widget()) : 0; }

void QMainWindowLayout::setStatusBar(QStatusBar *sb)
{
    if (sb)
        addChildWidget(sb);
    delete statusbar;
    statusbar = sb ? new QWidgetItemV2(sb) : 0;
    invalidate();
}
#endif // QT_NO_STATUSBAR

QWidget *QMainWindowLayout::centralWidget() const
{
    return layoutState.centralWidget();
}

void QMainWindowLayout::setCentralWidget(QWidget *widget)
{
    if (widget != 0)
        addChildWidget(widget);
    layoutState.setCentralWidget(widget);
    if (savedState.isValid()) {
#ifndef QT_NO_DOCKWIDGET
        savedState.dockAreaLayout.centralWidgetItem = layoutState.dockAreaLayout.centralWidgetItem;
        savedState.dockAreaLayout.fallbackToSizeHints = true;
#else
        savedState.centralWidgetItem = layoutState.centralWidgetItem;
#endif
    }
    invalidate();
}

QLayoutItem *QMainWindowLayout::unplug(QWidget *widget)
{
    QList<int> path = layoutState.indexOf(widget);
    if (path.isEmpty())
        return 0;

    QLayoutItem *item = layoutState.item(path);
    if (widget->isWindow())
        return item;

    QRect r = layoutState.itemRect(path);
    savedState = layoutState;

#ifndef QT_NO_DOCKWIDGET
    if (QDockWidget *dw = qobject_cast<QDockWidget*>(widget)) {
        dw->d_func()->unplug(r);
    }
#endif
#ifndef QT_NO_TOOLBAR
    if (QToolBar *tb = qobject_cast<QToolBar*>(widget)) {
        tb->d_func()->unplug(r);
    }
#endif


    layoutState.unplug(path ,&savedState);
    savedState.fitLayout();
    currentGapPos = path;
    currentGapRect = r;
    updateGapIndicator();

    fixToolBarOrientation(item, currentGapPos.at(1));

    return item;
}

void QMainWindowLayout::updateGapIndicator()
{
#ifndef QT_NO_RUBBERBAND
    gapIndicator->setVisible(!widgetAnimator.animating() && !currentGapPos.isEmpty());
    gapIndicator->setGeometry(currentGapRect);
#endif
}

QList<int> QMainWindowLayout::hover(QLayoutItem *widgetItem, const QPoint &mousePos)
{
    if (!parentWidget()->isVisible() || parentWidget()->isMinimized()
        || pluggingWidget != 0 || widgetItem == 0)
        return QList<int>();

    QWidget *widget = widgetItem->widget();
    QPoint pos = parentWidget()->mapFromGlobal(mousePos);

    if (!savedState.isValid())
        savedState = layoutState;

    QList<int> path = savedState.gapIndex(widget, pos);

    if (!path.isEmpty()) {
        bool allowed = false;

#ifndef QT_NO_DOCKWIDGET
        if (QDockWidget *dw = qobject_cast<QDockWidget*>(widget))
            allowed = dw->isAreaAllowed(toDockWidgetArea(path.at(1)));
#endif
#ifndef QT_NO_TOOLBAR
        if (QToolBar *tb = qobject_cast<QToolBar*>(widget))
            allowed = tb->isAreaAllowed(toToolBarArea(path.at(1)));
#endif

        if (!allowed)
            path.clear();
    }

    if (path == currentGapPos)
        return currentGapPos; // the gap is already there

    currentGapPos = path;
    if (path.isEmpty()) {
        fixToolBarOrientation(widgetItem, 2); // 2 = top dock, ie. horizontal
        restore(true);
        return QList<int>();
    }

    fixToolBarOrientation(widgetItem, currentGapPos.at(1));

    QMainWindowLayoutState newState = savedState;

    if (!newState.insertGap(path, widgetItem)) {
        restore(true); // not enough space
        return QList<int>();
    }

    QSize min = newState.minimumSize();
    QSize size = newState.rect.size();

    if (min.width() > size.width() || min.height() > size.height()) {
        restore(true);
        return QList<int>();
    }

    newState.fitLayout();

    currentGapRect = newState.gapRect(currentGapPos);

#ifndef QT_NO_DOCKWIDGET
    parentWidget()->update(layoutState.dockAreaLayout.separatorRegion());
#endif
    layoutState = newState;
    applyState(layoutState);

    updateGapIndicator();

    return path;
}

void QMainWindowLayout::applyState(QMainWindowLayoutState &newState, bool animate)
{
#ifndef QT_NO_DOCKWIDGET
#ifndef QT_NO_TABBAR
    QSet<QTabBar*> used = newState.dockAreaLayout.usedTabBars();
    QSet<QTabBar*> retired = usedTabBars - used;
    usedTabBars = used;
    foreach (QTabBar *tab_bar, retired) {
        tab_bar->hide();
        while (tab_bar->count() > 0)
            tab_bar->removeTab(0);
        unusedTabBars.append(tab_bar);
    }

    if (sep == 1) {
        QSet<QWidget*> usedSeps = newState.dockAreaLayout.usedSeparatorWidgets();
        QSet<QWidget*> retiredSeps = usedSeparatorWidgets - usedSeps;
        usedSeparatorWidgets = usedSeps;
        foreach (QWidget *sepWidget, retiredSeps) {
            unusedSeparatorWidgets.append(sepWidget);
        }
    }


#endif // QT_NO_TABBAR
#endif // QT_NO_DOCKWIDGET
    newState.apply(dockOptions & QMainWindow::AnimatedDocks && animate);
}

void QMainWindowLayout::saveState(QDataStream &stream) const
{
    layoutState.saveState(stream);
}

bool QMainWindowLayout::restoreState(QDataStream &stream)
{
    savedState = layoutState;
    layoutState.clear();
    layoutState.rect = savedState.rect;

    if (!layoutState.restoreState(stream, savedState)) {
        layoutState.deleteAllLayoutItems();
        layoutState = savedState;
        if (parentWidget()->isVisible())
            applyState(layoutState, false); // hides tabBars allocated by newState
        return false;
    }

    if (parentWidget()->isVisible()) {
        layoutState.fitLayout();
        applyState(layoutState, false);
    }

    savedState.deleteAllLayoutItems();
    savedState.clear();

#ifndef QT_NO_DOCKWIDGET
    if (parentWidget()->isVisible()) {
#ifndef QT_NO_TABBAR
        foreach (QTabBar *tab_bar, usedTabBars)
            tab_bar->show();

#endif
    }
#endif // QT_NO_DOCKWIDGET

    return true;
}


// Returns if this toolbar *should* be using HIToolbar. Won't work for all in between cases
// for example, you have a toolbar in the top area and then you suddenly turn on
// HIToolbar.
bool QMainWindowLayout::usesHIToolBar(QToolBar *toolbar) const
{
#ifndef Q_WS_MAC
    Q_UNUSED(toolbar);
    return false;
#else
    return qtoolbarsInUnifiedToolbarList.contains(toolbar)
           || ((toolBarArea(toolbar) == Qt::TopToolBarArea)
                && layoutState.mainWindow->unifiedTitleAndToolBarOnMac());
#endif
}

void QMainWindowLayout::timerEvent(QTimerEvent *e)
{
#ifndef QT_NO_DOCKWIDGET
    if (e->timerId() == separatorMoveTimer.timerId()) {
        //let's move the separators
        separatorMoveTimer.stop();
        if (movingSeparator.isEmpty())
            return;
        if (movingSeparatorOrigin == movingSeparatorPos)
            return;

        //when moving the separator, we need to update the previous position
        parentWidget()->update(layoutState.dockAreaLayout.separatorRegion());

        layoutState = savedState;
        layoutState.dockAreaLayout.separatorMove(movingSeparator, movingSeparatorOrigin,
                                                    movingSeparatorPos);
        movingSeparatorPos = movingSeparatorOrigin;
    }
#endif
    QLayout::timerEvent(e);
}


QT_END_NAMESPACE

#endif // QT_NO_MAINWINDOW
