// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0

#include "qdesigner_command_p.h"
#include "qdesigner_propertycommand_p.h"
#include "qdesigner_utils_p.h"
#include "layout_p.h"
#include "qlayout_widget_p.h"
#include "qdesigner_widget_p.h"
#include "qdesigner_menu_p.h"
#include "shared_enums_p.h"
#include "metadatabase_p.h"
#include "formwindowbase_p.h"
#include <abstractformbuilder.h>

#include <QtDesigner/abstractformwindow.h>
#include <QtDesigner/abstractformeditor.h>
#include <QtDesigner/propertysheet.h>
#include <QtDesigner/abstractactioneditor.h>
#include <QtDesigner/abstractpropertyeditor.h>
#include <QtDesigner/qextensionmanager.h>
#include <QtDesigner/container.h>
#include <QtDesigner/layoutdecoration.h>
#include <QtDesigner/abstractwidgetfactory.h>
#include <QtDesigner/abstractobjectinspector.h>
#include <QtDesigner/abstractintegration.h>
#include <QtDesigner/abstractformwindowcursor.h>
#include <QtCore/qdebug.h>
#include <QtCore/qtextstream.h>
#include <QtCore/qqueue.h>

#include <QtWidgets/qmenubar.h>
#include <QtWidgets/qstatusbar.h>
#include <QtWidgets/qtoolbar.h>
#include <QtWidgets/qtoolbox.h>
#include <QtWidgets/qstackedwidget.h>
#include <QtWidgets/qtabwidget.h>
#include <QtWidgets/qtablewidget.h>
#include <QtWidgets/qtreewidget.h>
#include <QtWidgets/qlistwidget.h>
#include <QtWidgets/qcombobox.h>
#include <QtWidgets/qsplitter.h>
#include <QtWidgets/qdockwidget.h>
#include <QtWidgets/qmainwindow.h>
#include <QtWidgets/qwizard.h>
#include <QtWidgets/qapplication.h>
#include <QtWidgets/qformlayout.h>

#include <algorithm>

Q_DECLARE_METATYPE(QWidgetList)

QT_BEGIN_NAMESPACE

using namespace Qt::StringLiterals;

static inline void setPropertySheetWindowTitle(const QDesignerFormEditorInterface *core, QObject *o, const QString &t)
{
    if (QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core->extensionManager(), o)) {
        const int idx = sheet->indexOf(u"windowTitle"_s);
        if (idx != -1) {
            sheet->setProperty(idx, t);
            sheet->setChanged(idx, true);
        }
    }
}

namespace qdesigner_internal {

// Helpers for the dynamic properties that store Z/Widget order
static const char widgetOrderPropertyC[] = "_q_widgetOrder";
static const char zOrderPropertyC[] = "_q_zOrder";

static void addToWidgetListDynamicProperty(QWidget *parentWidget, QWidget *widget, const char *name, int index = -1)
{
    auto list = qvariant_cast<QWidgetList>(parentWidget->property(name));
    list.removeAll(widget);
    if (index >= 0 && index < list.size()) {
        list.insert(index, widget);
    } else {
        list.append(widget);
    }
    parentWidget->setProperty(name, QVariant::fromValue(list));
}

static int removeFromWidgetListDynamicProperty(QWidget *parentWidget, QWidget *widget, const char *name)
{
    auto list = qvariant_cast<QWidgetList>(parentWidget->property(name));
    const auto firstIndex = list.indexOf(widget);
    if (firstIndex != -1) {
        list.removeAll(widget);
        parentWidget->setProperty(name, QVariant::fromValue(list));
    }
    return firstIndex;
}

// ---- InsertWidgetCommand ----
InsertWidgetCommand::InsertWidgetCommand(QDesignerFormWindowInterface *formWindow)  :
    QDesignerFormWindowCommand(QString(), formWindow),
    m_insertMode(QDesignerLayoutDecorationExtension::InsertWidgetMode),
    m_layoutHelper(nullptr),
    m_widgetWasManaged(false)
{
}

InsertWidgetCommand::~InsertWidgetCommand()
{
    delete m_layoutHelper;
}

void InsertWidgetCommand::init(QWidget *widget, bool already_in_form, int layoutRow, int layoutColumn)
{
    m_widget = widget;

    setText(QApplication::translate("Command", "Insert '%1'").arg(widget->objectName()));

    QWidget *parentWidget = m_widget->parentWidget();
    QDesignerFormEditorInterface *core = formWindow()->core();
    QDesignerLayoutDecorationExtension *deco = qt_extension<QDesignerLayoutDecorationExtension*>(core->extensionManager(), parentWidget);

    m_insertMode = deco ? deco->currentInsertMode() : QDesignerLayoutDecorationExtension::InsertWidgetMode;
    if (layoutRow >= 0 && layoutColumn >= 0) {
        m_cell.first = layoutRow;
        m_cell.second = layoutColumn;
    } else {
        m_cell = deco ? deco->currentCell() : std::make_pair(0, 0);
    }
    m_widgetWasManaged = already_in_form;
}

static void recursiveUpdate(QWidget *w)
{
    w->update();

    for (auto *child : w->children()) {
        if (QWidget *w = qobject_cast<QWidget*>(child))
            recursiveUpdate(w);
    }
}

void InsertWidgetCommand::redo()
{
    QWidget *parentWidget = m_widget->parentWidget();
    Q_ASSERT(parentWidget);

    addToWidgetListDynamicProperty(parentWidget, m_widget, widgetOrderPropertyC);
    addToWidgetListDynamicProperty(parentWidget, m_widget, zOrderPropertyC);

    QDesignerFormEditorInterface *core = formWindow()->core();
    QDesignerLayoutDecorationExtension *deco = qt_extension<QDesignerLayoutDecorationExtension*>(core->extensionManager(), parentWidget);

    if (deco != nullptr) {
        const LayoutInfo::Type type = LayoutInfo::layoutType(core, LayoutInfo::managedLayout(core, parentWidget));
        m_layoutHelper = LayoutHelper::createLayoutHelper(type);
        m_layoutHelper->pushState(core, parentWidget);
        if (type == LayoutInfo::Grid) {
            switch (m_insertMode) {
                case QDesignerLayoutDecorationExtension::InsertRowMode: {
                    deco->insertRow(m_cell.first);
                } break;

                case QDesignerLayoutDecorationExtension::InsertColumnMode: {
                    deco->insertColumn(m_cell.second);
                } break;

                default: break;
            } // end switch
        }
        deco->insertWidget(m_widget, m_cell);
    }

    if (!m_widgetWasManaged)
        formWindow()->manageWidget(m_widget);
    m_widget->show();
    formWindow()->emitSelectionChanged();

    if (parentWidget && parentWidget->layout()) {
        recursiveUpdate(parentWidget);
        parentWidget->layout()->invalidate();
    }

    refreshBuddyLabels();
}

void InsertWidgetCommand::undo()
{
    QWidget *parentWidget = m_widget->parentWidget();

    QDesignerFormEditorInterface *core = formWindow()->core();
    QDesignerLayoutDecorationExtension *deco = qt_extension<QDesignerLayoutDecorationExtension*>(core->extensionManager(), parentWidget);

    if (deco) {
        deco->removeWidget(m_widget);
        m_layoutHelper->popState(core, parentWidget);
    }

    if (!m_widgetWasManaged) {
        formWindow()->unmanageWidget(m_widget);
        m_widget->hide();
    }

    removeFromWidgetListDynamicProperty(parentWidget, m_widget, widgetOrderPropertyC);
    removeFromWidgetListDynamicProperty(parentWidget, m_widget, zOrderPropertyC);

    formWindow()->emitSelectionChanged();

    refreshBuddyLabels();
}

void InsertWidgetCommand::refreshBuddyLabels()
{
    const auto label_list = formWindow()->findChildren<QLabel*>();
    if (label_list.isEmpty())
        return;

    const QString buddyProperty = u"buddy"_s;
    const QByteArray objectNameU8 = m_widget->objectName().toUtf8();
    // Re-set the buddy (The sheet locates the object by name and sets it)
    for (QLabel *label : label_list) {
        if (QDesignerPropertySheetExtension* sheet = propertySheet(label)) {
            const int idx = sheet->indexOf(buddyProperty);
            if (idx != -1) {
                const QVariant value = sheet->property(idx);
                if (value.toByteArray() == objectNameU8)
                    sheet->setProperty(idx, value);
            }
        }
    }
}

// ---- ChangeZOrderCommand ----
ChangeZOrderCommand::ChangeZOrderCommand(QDesignerFormWindowInterface *formWindow)
    : QDesignerFormWindowCommand(QString(), formWindow)
{
}

void ChangeZOrderCommand::init(QWidget *widget)
{
    Q_ASSERT(widget);

    m_widget = widget;

    setText(QApplication::translate("Command", "Change Z-order of '%1'").arg(widget->objectName()));

    m_oldParentZOrder = qvariant_cast<QWidgetList>(widget->parentWidget()->property("_q_zOrder"));
    const qsizetype index = m_oldParentZOrder.indexOf(m_widget);
    if (index != -1 && index + 1 < m_oldParentZOrder.size())
        m_oldPreceding = m_oldParentZOrder.at(index + 1);
}

void ChangeZOrderCommand::redo()
{
    m_widget->parentWidget()->setProperty("_q_zOrder", QVariant::fromValue(reorderWidget(m_oldParentZOrder, m_widget)));

    reorder(m_widget);
}

void ChangeZOrderCommand::undo()
{
    m_widget->parentWidget()->setProperty("_q_zOrder", QVariant::fromValue(m_oldParentZOrder));

    if (m_oldPreceding)
        m_widget->stackUnder(m_oldPreceding);
    else
        m_widget->raise();
}

// ---- RaiseWidgetCommand ----
RaiseWidgetCommand::RaiseWidgetCommand(QDesignerFormWindowInterface *formWindow)
    : ChangeZOrderCommand(formWindow)
{
}

void RaiseWidgetCommand::init(QWidget *widget)
{
    ChangeZOrderCommand::init(widget);
    setText(QApplication::translate("Command", "Raise '%1'").arg(widget->objectName()));
}

QWidgetList RaiseWidgetCommand::reorderWidget(const QWidgetList &list, QWidget *widget) const
{
    QWidgetList l = list;
    l.removeAll(widget);
    l.append(widget);
    return l;
}

void RaiseWidgetCommand::reorder(QWidget *widget) const
{
    widget->raise();
}

// ---- LowerWidgetCommand ----
LowerWidgetCommand::LowerWidgetCommand(QDesignerFormWindowInterface *formWindow)
    : ChangeZOrderCommand(formWindow)
{
}

QWidgetList LowerWidgetCommand::reorderWidget(const QWidgetList &list, QWidget *widget) const
{
    QWidgetList l = list;
    l.removeAll(widget);
    l.prepend(widget);
    return l;
}

void LowerWidgetCommand::init(QWidget *widget)
{
    ChangeZOrderCommand::init(widget);
    setText(QApplication::translate("Command", "Lower '%1'").arg(widget->objectName()));
}

void LowerWidgetCommand::reorder(QWidget *widget) const
{
    widget->lower();
}

// ---- ManageWidgetCommandHelper
ManageWidgetCommandHelper::ManageWidgetCommandHelper() = default;

void ManageWidgetCommandHelper::init(const QDesignerFormWindowInterface *fw, QWidget *widget)
{
    m_widget = widget;
    m_managedChildren.clear();

    const QWidgetList children = m_widget->findChildren<QWidget *>();
    m_managedChildren.reserve(children.size());
    for (auto *w : children) {
        if (fw->isManaged(w))
            m_managedChildren.push_back(w);
    }
}

void ManageWidgetCommandHelper::init(QWidget *widget, const QWidgetList &managedChildren)
{
    m_widget = widget;
    m_managedChildren = managedChildren;
}

void ManageWidgetCommandHelper::manage(QDesignerFormWindowInterface *fw)
{
    // Manage the managed children after parent
    fw->manageWidget(m_widget);
    for (auto *w : std::as_const(m_managedChildren))
        fw->manageWidget(w);
}

void ManageWidgetCommandHelper::unmanage(QDesignerFormWindowInterface *fw)
{
    // Unmanage the managed children first
    for (auto *w : std::as_const(m_managedChildren))
        fw->unmanageWidget(w);
    fw->unmanageWidget(m_widget);
}

// ---- DeleteWidgetCommand ----
DeleteWidgetCommand::DeleteWidgetCommand(QDesignerFormWindowInterface *formWindow) :
    QDesignerFormWindowCommand(QString(), formWindow),
    m_layoutType(LayoutInfo::NoLayout),
    m_layoutHelper(nullptr),
    m_flags(0),
    m_splitterIndex(-1),
    m_layoutSimplified(false),
    m_formItem(nullptr),
    m_tabOrderIndex(-1),
    m_widgetOrderIndex(-1),
    m_zOrderIndex(-1)
{
}

DeleteWidgetCommand::~DeleteWidgetCommand()
{
    delete  m_layoutHelper;
}

void DeleteWidgetCommand::init(QWidget *widget, unsigned flags)
{
    m_widget = widget;
    m_parentWidget = widget->parentWidget();
    m_geometry = widget->geometry();
    m_flags = flags;
    m_layoutType = LayoutInfo::NoLayout;
    m_splitterIndex = -1;
    bool isManaged = false; // Check for a managed layout
    QLayout *layout = nullptr;
    m_layoutType = LayoutInfo::laidoutWidgetType(formWindow()->core(), m_widget, &isManaged, &layout);
    if (!isManaged)
        m_layoutType = LayoutInfo::NoLayout;
    switch (m_layoutType) {
    case LayoutInfo::HSplitter:
    case LayoutInfo::VSplitter: {
        auto *splitter = qobject_cast<QSplitter *>(m_parentWidget);
        Q_ASSERT(splitter);
        m_splitterIndex = splitter->indexOf(widget);
    }
        break;
    case LayoutInfo::NoLayout:
        break;
    default:
        m_layoutHelper = LayoutHelper::createLayoutHelper(m_layoutType);
        m_layoutPosition = m_layoutHelper->itemInfo(layout, m_widget);
        break;
    }

    m_formItem = formWindow()->core()->metaDataBase()->item(formWindow());
    m_tabOrderIndex = m_formItem->tabOrder().indexOf(widget);

    // Build the list of managed children
    m_manageHelper.init(formWindow(), m_widget);

    setText(QApplication::translate("Command", "Delete '%1'").arg(widget->objectName()));
}

void DeleteWidgetCommand::redo()
{
    formWindow()->clearSelection();
    QDesignerFormEditorInterface *core = formWindow()->core();

    if (QDesignerContainerExtension *c = qt_extension<QDesignerContainerExtension*>(core->extensionManager(), m_parentWidget)) {
        const int count = c->count();
        for (int i=0; i<count; ++i) {
            if (c->widget(i) == m_widget) {
                c->remove(i);
                return;
            }
        }
    }

    m_widgetOrderIndex = removeFromWidgetListDynamicProperty(m_parentWidget, m_widget, widgetOrderPropertyC);
    m_zOrderIndex = removeFromWidgetListDynamicProperty(m_parentWidget, m_widget, zOrderPropertyC);

    if (QDesignerLayoutDecorationExtension *deco = qt_extension<QDesignerLayoutDecorationExtension*>(core->extensionManager(), m_parentWidget))
        deco->removeWidget(m_widget);

    if (m_layoutHelper)
        switch (m_layoutType) {
        case LayoutInfo::NoLayout:
        case LayoutInfo::HSplitter:
        case LayoutInfo::VSplitter:
            break;
        default:
            // Attempt to simplify grids if a row/column becomes empty
            m_layoutSimplified = (m_flags & DoNotSimplifyLayout) ? false : m_layoutHelper->canSimplify(core, m_parentWidget, m_layoutPosition);
            if (m_layoutSimplified) {
                m_layoutHelper->pushState(core, m_parentWidget);
                m_layoutHelper->simplify(core, m_parentWidget, m_layoutPosition);
            }
            break;
        }

    if (!(m_flags & DoNotUnmanage))
        m_manageHelper.unmanage(formWindow());

    m_widget->setParent(formWindow());
    m_widget->hide();

    if (m_tabOrderIndex != -1) {
        QWidgetList tab_order = m_formItem->tabOrder();
        tab_order.removeAt(m_tabOrderIndex);
        m_formItem->setTabOrder(tab_order);
    }
}

void DeleteWidgetCommand::undo()
{
    QDesignerFormEditorInterface *core = formWindow()->core();
    formWindow()->clearSelection();

    m_widget->setParent(m_parentWidget);

    if (QDesignerContainerExtension *c = qt_extension<QDesignerContainerExtension*>(core->extensionManager(), m_parentWidget)) {
        c->addWidget(m_widget);
        return;
    }

    addToWidgetListDynamicProperty(m_parentWidget, m_widget, widgetOrderPropertyC, m_widgetOrderIndex);
    addToWidgetListDynamicProperty(m_parentWidget, m_widget, zOrderPropertyC, m_zOrderIndex);

    m_widget->setGeometry(m_geometry);

    if (!(m_flags & DoNotUnmanage))
        m_manageHelper.manage(formWindow());
    // ### set up alignment
    switch (m_layoutType) {
    case LayoutInfo::NoLayout:
        break;
    case LayoutInfo::HSplitter:
    case LayoutInfo::VSplitter: {
        auto *splitter = qobject_cast<QSplitter *>(m_widget->parent());
        Q_ASSERT(splitter);
        splitter->insertWidget(m_splitterIndex, m_widget);
    } break;
    default: {
        Q_ASSERT(m_layoutHelper);
        if (m_layoutSimplified)
            m_layoutHelper->popState(core, m_parentWidget);
        QLayout *layout = LayoutInfo::managedLayout(core, m_parentWidget);
        Q_ASSERT(m_layoutType == LayoutInfo::layoutType(core, layout));
        m_layoutHelper->insertWidget(layout, m_layoutPosition, m_widget);
    }
        break;
    }

    m_widget->show();

    if (m_tabOrderIndex != -1) {
        QWidgetList tab_order = m_formItem->tabOrder();
        tab_order.insert(m_tabOrderIndex, m_widget);
        m_formItem->setTabOrder(tab_order);
    }
}

// ---- ReparentWidgetCommand ----
ReparentWidgetCommand::ReparentWidgetCommand(QDesignerFormWindowInterface *formWindow)
    : QDesignerFormWindowCommand(QString(), formWindow)
{
}

void ReparentWidgetCommand::init(QWidget *widget, QWidget *parentWidget)
{
    Q_ASSERT(widget);

    m_widget = widget;
    m_oldParentWidget = widget->parentWidget();
    m_newParentWidget = parentWidget;

    m_oldPos = m_widget->pos();
    m_newPos = m_newParentWidget->mapFromGlobal(m_oldParentWidget->mapToGlobal(m_oldPos));

    setText(QApplication::translate("Command", "Reparent '%1'").arg(widget->objectName()));

    m_oldParentList = qvariant_cast<QWidgetList>(m_oldParentWidget->property("_q_widgetOrder"));
    m_oldParentZOrder = qvariant_cast<QWidgetList>(m_oldParentWidget->property("_q_zOrder"));
}

void ReparentWidgetCommand::redo()
{
    m_widget->setParent(m_newParentWidget);
    m_widget->move(m_newPos);

    QWidgetList oldList = m_oldParentList;
    oldList.removeAll(m_widget);
    m_oldParentWidget->setProperty("_q_widgetOrder", QVariant::fromValue(oldList));

    auto newList = qvariant_cast<QWidgetList>(m_newParentWidget->property("_q_widgetOrder"));
    newList.append(m_widget);
    m_newParentWidget->setProperty("_q_widgetOrder", QVariant::fromValue(newList));

    QWidgetList oldZOrder = m_oldParentZOrder;
    oldZOrder.removeAll(m_widget);
    m_oldParentWidget->setProperty("_q_zOrder", QVariant::fromValue(oldZOrder));

    auto newZOrder = qvariant_cast<QWidgetList>(m_newParentWidget->property("_q_zOrder"));
    newZOrder.append(m_widget);
    m_newParentWidget->setProperty("_q_zOrder", QVariant::fromValue(newZOrder));

    m_widget->show();
    core()->objectInspector()->setFormWindow(formWindow());
}

void ReparentWidgetCommand::undo()
{
    m_widget->setParent(m_oldParentWidget);
    m_widget->move(m_oldPos);

    m_oldParentWidget->setProperty("_q_widgetOrder", QVariant::fromValue(m_oldParentList));

    auto newList = qvariant_cast<QWidgetList>(m_newParentWidget->property("_q_widgetOrder"));
    newList.removeAll(m_widget);
    m_newParentWidget->setProperty("_q_widgetOrder", QVariant::fromValue(newList));

    m_oldParentWidget->setProperty("_q_zOrder", QVariant::fromValue(m_oldParentZOrder));

    auto newZOrder = qvariant_cast<QWidgetList>(m_newParentWidget->property("_q_zOrder"));
    newZOrder.removeAll(m_widget);
    m_newParentWidget->setProperty("_q_zOrder", QVariant::fromValue(newZOrder));

    m_widget->show();
    core()->objectInspector()->setFormWindow(formWindow());
}

PromoteToCustomWidgetCommand::PromoteToCustomWidgetCommand
                                (QDesignerFormWindowInterface *formWindow)
    : QDesignerFormWindowCommand(QApplication::translate("Command", "Promote to custom widget"), formWindow)
{
}

void PromoteToCustomWidgetCommand::init(const WidgetPointerList &widgets,const QString &customClassName)
{
    m_widgets = widgets;
    m_customClassName = customClassName;
}

void PromoteToCustomWidgetCommand::redo()
{
    for (QWidget *w : std::as_const(m_widgets)) {
        if (w)
            promoteWidget(core(), w, m_customClassName);
    }
    updateSelection();
}

void PromoteToCustomWidgetCommand::updateSelection()
{
    // Update class names in ObjectInspector, PropertyEditor
    QDesignerFormWindowInterface *fw = formWindow();
    QDesignerFormEditorInterface *core = fw->core();
    core->objectInspector()->setFormWindow(fw);
    if (QObject *o = core->propertyEditor()->object())
        core->propertyEditor()->setObject(o);
}

void PromoteToCustomWidgetCommand::undo()
{
    for (QWidget *w : std::as_const(m_widgets)) {
        if (w)
            demoteWidget(core(), w);
    }
    updateSelection();
}

// ---- DemoteFromCustomWidgetCommand ----

DemoteFromCustomWidgetCommand::DemoteFromCustomWidgetCommand
                                    (QDesignerFormWindowInterface *formWindow) :
    QDesignerFormWindowCommand(QApplication::translate("Command", "Demote from custom widget"), formWindow),
    m_promote_cmd(formWindow)
{
}

void DemoteFromCustomWidgetCommand::init(const WidgetList &promoted)
{
    m_promote_cmd.init(promoted, promotedCustomClassName(core(), promoted.constFirst()));
}

void DemoteFromCustomWidgetCommand::redo()
{
    m_promote_cmd.undo();
}

void DemoteFromCustomWidgetCommand::undo()
{
    m_promote_cmd.redo();
}

// ----------  CursorSelectionState
CursorSelectionState::CursorSelectionState() = default;

void CursorSelectionState::save(const QDesignerFormWindowInterface *formWindow)
{
    const QDesignerFormWindowCursorInterface *cursor = formWindow->cursor();
    m_selection.clear();
    m_current = cursor->current();
    if (cursor->hasSelection()) {
        const int count = cursor->selectedWidgetCount();
        for(int i = 0; i < count; i++)
            m_selection.push_back(cursor->selectedWidget(i));
    }
}

void CursorSelectionState::restore(QDesignerFormWindowInterface *formWindow) const
{
    if (m_selection.isEmpty()) {
        formWindow->clearSelection(true);
    } else {
        // Select current as last
        formWindow->clearSelection(false);
        for (const auto &wp : m_selection) {
            if (!wp.isNull() && wp.data() != m_current)
                formWindow->selectWidget(wp.data(), true);
        }
        if (m_current)
            formWindow->selectWidget(m_current, true);
    }
}

// ---- LayoutCommand ----

LayoutCommand::LayoutCommand(QDesignerFormWindowInterface *formWindow) :
    QDesignerFormWindowCommand(QString(), formWindow),
    m_setup(false)
{
}

LayoutCommand::~LayoutCommand()
{
    delete m_layout;
}

void LayoutCommand::init(QWidget *parentWidget, const QWidgetList &widgets,
                         LayoutInfo::Type layoutType, QWidget *layoutBase,
                         bool reparentLayoutWidget)
{
    m_parentWidget = parentWidget;
    m_widgets = widgets;
    formWindow()->simplifySelection(&m_widgets);
    m_layout = Layout::createLayout(widgets, parentWidget, formWindow(), layoutBase, layoutType);
    m_layout->setReparentLayoutWidget(reparentLayoutWidget);

    switch (layoutType) {
    case LayoutInfo::Grid:
        setText(QApplication::translate("Command", "Lay out using grid"));
        break;
    case LayoutInfo::VBox:
        setText(QApplication::translate("Command", "Lay out vertically"));
        break;
    case LayoutInfo::HBox:
        setText(QApplication::translate("Command", "Lay out horizontally"));
        break;
    default:
        break;
    }
    // Delayed setup to avoid confusion in case we are chained
    // with a BreakLayout in a morph layout macro
    m_setup = false;
}

void LayoutCommand::redo()
{
    if (!m_setup) {
        m_layout->setup();
        m_cursorSelectionState.save(formWindow());
        m_setup = true;
    }
    m_layout->doLayout();
    core()->objectInspector()->setFormWindow(formWindow());
}

void LayoutCommand::undo()
{
    QDesignerFormEditorInterface *core = formWindow()->core();

    QWidget *lb = m_layout->layoutBaseWidget();
    auto *deco = qt_extension<QDesignerLayoutDecorationExtension*>(core->extensionManager(), lb);
    m_layout->undoLayout();
    delete deco; // release the extension

    // ### generalize (put in function)
    if (!m_layoutBase && lb != nullptr && !(qobject_cast<QLayoutWidget*>(lb) || qobject_cast<QSplitter*>(lb))) {
        core->metaDataBase()->add(lb);
        lb->show();
    }
    m_cursorSelectionState.restore(formWindow());
    core->objectInspector()->setFormWindow(formWindow());
}

// ---- BreakLayoutCommand ----
BreakLayoutCommand::BreakLayoutCommand(QDesignerFormWindowInterface *formWindow) :
    QDesignerFormWindowCommand(QApplication::translate("Command", "Break layout"), formWindow),
    m_layoutHelper(nullptr),
    m_properties(nullptr),
    m_propertyMask(0)
{
}

BreakLayoutCommand::~BreakLayoutCommand()
{
    delete m_layoutHelper;
    delete m_layout;
    delete m_properties;
}

const LayoutProperties *BreakLayoutCommand::layoutProperties() const
{
    return m_properties;
}

int BreakLayoutCommand::propertyMask() const
{
    return m_propertyMask;
}

void BreakLayoutCommand::init(const QWidgetList &widgets, QWidget *layoutBase, bool reparentLayoutWidget)
{
    enum Type { SplitterLayout, LayoutHasMarginSpacing, LayoutHasState };

    const QDesignerFormEditorInterface *core = formWindow()->core();
    m_widgets = widgets;
    m_layoutBase = core->widgetFactory()->containerOfWidget(layoutBase);
    QLayout *layoutToBeBroken = nullptr;
    const LayoutInfo::Type layoutType = LayoutInfo::managedLayoutType(core, m_layoutBase, &layoutToBeBroken);
    m_layout = Layout::createLayout(widgets, m_layoutBase, formWindow(), layoutBase, layoutType);
    m_layout->setReparentLayoutWidget(reparentLayoutWidget);

    Type type = LayoutHasState;
    switch (layoutType) {
    case LayoutInfo::NoLayout:
    case LayoutInfo::HSplitter:
    case LayoutInfo::VSplitter:
        type = SplitterLayout;
        break;
    case LayoutInfo::HBox:
    case LayoutInfo::VBox: // Margin/spacing need to be saved
        type = LayoutHasMarginSpacing;
        break;
    default: // Margin/spacing need to be saved + has a state (empty rows/columns of a grid)
        type = LayoutHasState;
        break;
    }
    Q_ASSERT(m_layout != nullptr);
    m_layout->sort();


    if (type >= LayoutHasMarginSpacing) {
        m_properties = new LayoutProperties;
        m_propertyMask = m_properties->fromPropertySheet(core, layoutToBeBroken, LayoutProperties::AllProperties);
    }
    if (type >= LayoutHasState)
         m_layoutHelper = LayoutHelper::createLayoutHelper(layoutType);
    m_cursorSelectionState.save(formWindow());
}

void BreakLayoutCommand::redo()
{
    if (!m_layout)
        return;

    QDesignerFormEditorInterface *core = formWindow()->core();
    QWidget *lb = m_layout->layoutBaseWidget();
    QDesignerLayoutDecorationExtension *deco = qt_extension<QDesignerLayoutDecorationExtension*>(core->extensionManager(), lb);
    formWindow()->clearSelection(false);
    if (m_layoutHelper)
        m_layoutHelper->pushState(core, m_layoutBase);
    m_layout->breakLayout();
    delete deco; // release the extension

    for (QWidget *widget : std::as_const(m_widgets)) {
        widget->resize(widget->size().expandedTo(QSize(16, 16)));
    }
    // Update unless we are in an intermediate state of morphing layout
    // in which a QLayoutWidget will have no layout at all.
    if (m_layout->reparentLayoutWidget())
        core->objectInspector()->setFormWindow(formWindow());
}

void BreakLayoutCommand::undo()
{
    if (!m_layout)
        return;

    formWindow()->clearSelection(false);
    m_layout->doLayout();
    if (m_layoutHelper)
        m_layoutHelper->popState(formWindow()->core(), m_layoutBase);

    QLayout *layoutToRestored = LayoutInfo::managedLayout(formWindow()->core(), m_layoutBase);
    if (m_properties && m_layoutBase && layoutToRestored)
        m_properties->toPropertySheet(formWindow()->core(), layoutToRestored, m_propertyMask);
     m_cursorSelectionState.restore(formWindow());
    core()->objectInspector()->setFormWindow(formWindow());
}
// ---- SimplifyLayoutCommand
SimplifyLayoutCommand::SimplifyLayoutCommand(QDesignerFormWindowInterface *formWindow) :
    QDesignerFormWindowCommand(QApplication::translate("Command", "Simplify Grid Layout"), formWindow),
    m_area(0, 0, 32767, 32767),
    m_layoutBase(nullptr),
    m_layoutHelper(nullptr),
    m_layoutSimplified(false)
{
}

SimplifyLayoutCommand::~SimplifyLayoutCommand()
{
    delete m_layoutHelper;
}

bool SimplifyLayoutCommand::canSimplify(QDesignerFormEditorInterface *core, const QWidget *w, int *layoutType)
{
    if (!w)
        return false;
    QLayout *layout = nullptr;
    const LayoutInfo::Type type = LayoutInfo::managedLayoutType(core, w, &layout);
    if (layoutType)
        *layoutType = type;
    if (!layout)
        return false;
    switch (type) { // Known negatives
    case LayoutInfo::NoLayout:
    case LayoutInfo::UnknownLayout:
    case LayoutInfo::HSplitter:
    case LayoutInfo::VSplitter:
    case LayoutInfo::HBox:
    case LayoutInfo::VBox:
        return false;
    default:
        break;
    }
    switch (type) {
    case LayoutInfo::Grid:
        return QLayoutSupport::canSimplifyQuickCheck(qobject_cast<QGridLayout*>(layout));
    case LayoutInfo::Form:
        return QLayoutSupport::canSimplifyQuickCheck(qobject_cast<const QFormLayout*>(layout));
    default:
        break;
    }
    return false;
}

bool SimplifyLayoutCommand::init(QWidget *layoutBase)
{
    QDesignerFormEditorInterface *core = formWindow()->core();
    m_layoutSimplified = false;
    int type = 0;
    if (canSimplify(core, layoutBase, &type)) {
        m_layoutBase = layoutBase;
        m_layoutHelper = LayoutHelper::createLayoutHelper(type);
        m_layoutSimplified = m_layoutHelper->canSimplify(core, layoutBase, m_area);
    }
    return m_layoutSimplified;
}

void SimplifyLayoutCommand::redo()
{
    const QDesignerFormEditorInterface *core = formWindow()->core();
    if (m_layoutSimplified) {
        m_layoutHelper->pushState(core, m_layoutBase);
        m_layoutHelper->simplify(core, m_layoutBase, m_area);
    }
}
void SimplifyLayoutCommand::undo()
{
    if (m_layoutSimplified)
         m_layoutHelper->popState(formWindow()->core(), m_layoutBase);
}

// ---- ToolBoxCommand ----
ToolBoxCommand::ToolBoxCommand(QDesignerFormWindowInterface *formWindow)  :
    QDesignerFormWindowCommand(QString(), formWindow),
    m_index(-1)
{
}

ToolBoxCommand::~ToolBoxCommand() = default;

void ToolBoxCommand::init(QToolBox *toolBox)
{
    m_toolBox = toolBox;
    m_index = m_toolBox->currentIndex();
    m_widget = m_toolBox->widget(m_index);
    m_itemText = m_toolBox->itemText(m_index);
    m_itemIcon = m_toolBox->itemIcon(m_index);
}

void ToolBoxCommand::removePage()
{
    m_toolBox->removeItem(m_index);

    m_widget->hide();
    m_widget->setParent(formWindow());
    formWindow()->clearSelection();
    formWindow()->selectWidget(m_toolBox, true);

}

void ToolBoxCommand::addPage()
{
    m_widget->setParent(m_toolBox);
    m_toolBox->insertItem(m_index, m_widget, m_itemIcon, m_itemText);
    m_toolBox->setCurrentIndex(m_index);

    QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(formWindow()->core()->extensionManager(), m_toolBox);
    if (sheet) {
        qdesigner_internal::PropertySheetStringValue itemText(m_itemText);
        sheet->setProperty(sheet->indexOf(u"currentItemText"_s), QVariant::fromValue(itemText));
    }

    m_widget->show();
    formWindow()->clearSelection();
    formWindow()->selectWidget(m_toolBox, true);
}

// ---- MoveToolBoxPageCommand ----
MoveToolBoxPageCommand::MoveToolBoxPageCommand(QDesignerFormWindowInterface *formWindow) :
    ToolBoxCommand(formWindow),
    m_newIndex(-1),
    m_oldIndex(-1)
{
}

MoveToolBoxPageCommand::~MoveToolBoxPageCommand() = default;

void MoveToolBoxPageCommand::init(QToolBox *toolBox, QWidget *page, int newIndex)
{
    ToolBoxCommand::init(toolBox);
    setText(QApplication::translate("Command", "Move Page"));

    m_widget = page;
    m_oldIndex = m_toolBox->indexOf(m_widget);
    m_itemText = m_toolBox->itemText(m_oldIndex);
    m_itemIcon = m_toolBox->itemIcon(m_oldIndex);
    m_newIndex = newIndex;
}

void MoveToolBoxPageCommand::redo()
{
    m_toolBox->removeItem(m_oldIndex);
    m_toolBox->insertItem(m_newIndex, m_widget, m_itemIcon, m_itemText);
}

void MoveToolBoxPageCommand::undo()
{
    m_toolBox->removeItem(m_newIndex);
    m_toolBox->insertItem(m_oldIndex, m_widget, m_itemIcon, m_itemText);
}

// ---- DeleteToolBoxPageCommand ----
DeleteToolBoxPageCommand::DeleteToolBoxPageCommand(QDesignerFormWindowInterface *formWindow)
    : ToolBoxCommand(formWindow)
{
}

DeleteToolBoxPageCommand::~DeleteToolBoxPageCommand() = default;

void DeleteToolBoxPageCommand::init(QToolBox *toolBox)
{
    ToolBoxCommand::init(toolBox);
    setText(QApplication::translate("Command", "Delete Page"));
}

void DeleteToolBoxPageCommand::redo()
{
    removePage();
    cheapUpdate();
}

void DeleteToolBoxPageCommand::undo()
{
    addPage();
    cheapUpdate();
}

// ---- AddToolBoxPageCommand ----
AddToolBoxPageCommand::AddToolBoxPageCommand(QDesignerFormWindowInterface *formWindow)
    : ToolBoxCommand(formWindow)
{
}

AddToolBoxPageCommand::~AddToolBoxPageCommand() = default;

void AddToolBoxPageCommand::init(QToolBox *toolBox)
{
    init(toolBox, InsertBefore);
}

void AddToolBoxPageCommand::init(QToolBox *toolBox, InsertionMode mode)
{
    m_toolBox = toolBox;

    m_index = m_toolBox->currentIndex();
    if (mode == InsertAfter)
        m_index++;
    m_widget = new QDesignerWidget(formWindow(), m_toolBox);
    m_itemText = QApplication::translate("Command", "Page");
    m_itemIcon = QIcon();
    m_widget->setObjectName(u"page"_s);
    formWindow()->ensureUniqueObjectName(m_widget);

    setText(QApplication::translate("Command", "Insert Page"));

    QDesignerFormEditorInterface *core = formWindow()->core();
    core->metaDataBase()->add(m_widget);
}

void AddToolBoxPageCommand::redo()
{
    addPage();
    cheapUpdate();
}

void AddToolBoxPageCommand::undo()
{
    removePage();
    cheapUpdate();
}

// ---- TabWidgetCommand ----
TabWidgetCommand::TabWidgetCommand(QDesignerFormWindowInterface *formWindow) :
    QDesignerFormWindowCommand(QString(), formWindow),
    m_index(-1)
{
}

TabWidgetCommand::~TabWidgetCommand() = default;

void TabWidgetCommand::init(QTabWidget *tabWidget)
{
    m_tabWidget = tabWidget;
    m_index = m_tabWidget->currentIndex();
    m_widget = m_tabWidget->widget(m_index);
    m_itemText = m_tabWidget->tabText(m_index);
    m_itemIcon = m_tabWidget->tabIcon(m_index);
}

void TabWidgetCommand::removePage()
{
    m_tabWidget->removeTab(m_index);

    m_widget->hide();
    m_widget->setParent(formWindow());
    m_tabWidget->setCurrentIndex(qMin(m_index, m_tabWidget->count()));

    formWindow()->clearSelection();
    formWindow()->selectWidget(m_tabWidget, true);
}

void TabWidgetCommand::addPage()
{
    m_widget->setParent(nullptr);
    m_tabWidget->insertTab(m_index, m_widget, m_itemIcon, m_itemText);
    m_widget->show();
    m_tabWidget->setCurrentIndex(m_index);

    QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(formWindow()->core()->extensionManager(), m_tabWidget);
    if (sheet) {
        qdesigner_internal::PropertySheetStringValue itemText(m_itemText);
        sheet->setProperty(sheet->indexOf(u"currentTabText"_s),
                           QVariant::fromValue(itemText));
    }

    formWindow()->clearSelection();
    formWindow()->selectWidget(m_tabWidget, true);
}

// ---- DeleteTabPageCommand ----
DeleteTabPageCommand::DeleteTabPageCommand(QDesignerFormWindowInterface *formWindow)
    : TabWidgetCommand(formWindow)
{
}

DeleteTabPageCommand::~DeleteTabPageCommand() = default;

void DeleteTabPageCommand::init(QTabWidget *tabWidget)
{
    TabWidgetCommand::init(tabWidget);
    setText(QApplication::translate("Command", "Delete Page"));
}

void DeleteTabPageCommand::redo()
{
    removePage();
    cheapUpdate();
}

void DeleteTabPageCommand::undo()
{
    addPage();
    cheapUpdate();
}

// ---- AddTabPageCommand ----
AddTabPageCommand::AddTabPageCommand(QDesignerFormWindowInterface *formWindow)
    : TabWidgetCommand(formWindow)
{
}

AddTabPageCommand::~AddTabPageCommand() = default;

void AddTabPageCommand::init(QTabWidget *tabWidget)
{
    init(tabWidget, InsertBefore);
}

void AddTabPageCommand::init(QTabWidget *tabWidget, InsertionMode mode)
{
    m_tabWidget = tabWidget;

    m_index = m_tabWidget->currentIndex();
    if (mode == InsertAfter)
        m_index++;
    m_widget = new QDesignerWidget(formWindow(), m_tabWidget);
    m_itemText = QApplication::translate("Command", "Page");
    m_itemIcon = QIcon();
    m_widget->setObjectName(u"tab"_s);
    formWindow()->ensureUniqueObjectName(m_widget);

    setText(QApplication::translate("Command", "Insert Page"));

    QDesignerFormEditorInterface *core = formWindow()->core();
    core->metaDataBase()->add(m_widget);
}

void AddTabPageCommand::redo()
{
    addPage();
    cheapUpdate();
}

void AddTabPageCommand::undo()
{
    removePage();
    cheapUpdate();
}

// ---- MoveTabPageCommand ----
MoveTabPageCommand::MoveTabPageCommand(QDesignerFormWindowInterface *formWindow) :
    TabWidgetCommand(formWindow),
    m_newIndex(-1),
    m_oldIndex(-1)
{
}

MoveTabPageCommand::~MoveTabPageCommand() = default;

void MoveTabPageCommand::init(QTabWidget *tabWidget, QWidget *page,
                      const QIcon &icon, const QString &label,
                      int index, int newIndex)
{
    TabWidgetCommand::init(tabWidget);
    setText(QApplication::translate("Command", "Move Page"));

    m_page = page;
    m_newIndex = newIndex;
    m_oldIndex = index;
    m_label = label;
    m_icon = icon;
}

void MoveTabPageCommand::redo()
{
    m_tabWidget->removeTab(m_oldIndex);
    m_tabWidget->insertTab(m_newIndex, m_page, m_icon, m_label);
    m_tabWidget->setCurrentIndex(m_newIndex);
}

void MoveTabPageCommand::undo()
{
    m_tabWidget->removeTab(m_newIndex);
    m_tabWidget->insertTab(m_oldIndex, m_page, m_icon, m_label);
    m_tabWidget->setCurrentIndex(m_oldIndex);
}

// ---- StackedWidgetCommand ----
StackedWidgetCommand::StackedWidgetCommand(QDesignerFormWindowInterface *formWindow) :
    QDesignerFormWindowCommand(QString(), formWindow),
    m_index(-1)
{
}

StackedWidgetCommand::~StackedWidgetCommand() = default;

void StackedWidgetCommand::init(QStackedWidget *stackedWidget)
{
    m_stackedWidget = stackedWidget;
    m_index = m_stackedWidget->currentIndex();
    m_widget = m_stackedWidget->widget(m_index);
}

void StackedWidgetCommand::removePage()
{
    m_stackedWidget->removeWidget(m_stackedWidget->widget(m_index));

    m_widget->hide();
    m_widget->setParent(formWindow());

    formWindow()->clearSelection();
    formWindow()->selectWidget(m_stackedWidget, true);
}

void StackedWidgetCommand::addPage()
{
    m_stackedWidget->insertWidget(m_index, m_widget);

    m_widget->show();
    m_stackedWidget->setCurrentIndex(m_index);

    formWindow()->clearSelection();
    formWindow()->selectWidget(m_stackedWidget, true);
}

// ---- MoveStackedWidgetCommand ----
MoveStackedWidgetCommand::MoveStackedWidgetCommand(QDesignerFormWindowInterface *formWindow) :
    StackedWidgetCommand(formWindow),
    m_newIndex(-1),
    m_oldIndex(-1)
{
}

MoveStackedWidgetCommand::~MoveStackedWidgetCommand() = default;

void MoveStackedWidgetCommand::init(QStackedWidget *stackedWidget, QWidget *page, int newIndex)
{
    StackedWidgetCommand::init(stackedWidget);
    setText(QApplication::translate("Command", "Move Page"));

    m_widget = page;
    m_newIndex = newIndex;
    m_oldIndex = m_stackedWidget->indexOf(m_widget);
}

void MoveStackedWidgetCommand::redo()
{
    m_stackedWidget->removeWidget(m_widget);
    m_stackedWidget->insertWidget(m_newIndex, m_widget);
}

void MoveStackedWidgetCommand::undo()
{
    m_stackedWidget->removeWidget(m_widget);
    m_stackedWidget->insertWidget(m_oldIndex, m_widget);
}

// ---- DeleteStackedWidgetPageCommand ----
DeleteStackedWidgetPageCommand::DeleteStackedWidgetPageCommand(QDesignerFormWindowInterface *formWindow)
    : StackedWidgetCommand(formWindow)
{
}

DeleteStackedWidgetPageCommand::~DeleteStackedWidgetPageCommand() = default;

void DeleteStackedWidgetPageCommand::init(QStackedWidget *stackedWidget)
{
    StackedWidgetCommand::init(stackedWidget);
    setText(QApplication::translate("Command", "Delete Page"));
}

void DeleteStackedWidgetPageCommand::redo()
{
    removePage();
    cheapUpdate();
}

void DeleteStackedWidgetPageCommand::undo()
{
    addPage();
    cheapUpdate();
}

// ---- AddStackedWidgetPageCommand ----
AddStackedWidgetPageCommand::AddStackedWidgetPageCommand(QDesignerFormWindowInterface *formWindow)
    : StackedWidgetCommand(formWindow)
{
}

AddStackedWidgetPageCommand::~AddStackedWidgetPageCommand() = default;

void AddStackedWidgetPageCommand::init(QStackedWidget *stackedWidget)
{
    init(stackedWidget, InsertBefore);
}

void AddStackedWidgetPageCommand::init(QStackedWidget *stackedWidget, InsertionMode mode)
{
    m_stackedWidget = stackedWidget;

    m_index = m_stackedWidget->currentIndex();
    if (mode == InsertAfter)
        m_index++;
    m_widget = new QDesignerWidget(formWindow(), m_stackedWidget);
    m_widget->setObjectName(u"page"_s);
    formWindow()->ensureUniqueObjectName(m_widget);

    setText(QApplication::translate("Command", "Insert Page"));

    QDesignerFormEditorInterface *core = formWindow()->core();
    core->metaDataBase()->add(m_widget);
}

void AddStackedWidgetPageCommand::redo()
{
    addPage();
    cheapUpdate();
}

void AddStackedWidgetPageCommand::undo()
{
    removePage();
    cheapUpdate();
}

// ---- TabOrderCommand ----
TabOrderCommand::TabOrderCommand(QDesignerFormWindowInterface *formWindow)
    : QDesignerFormWindowCommand(QApplication::translate("Command", "Change Tab order"), formWindow),
      m_widgetItem(nullptr)
{
}

void TabOrderCommand::init(const QWidgetList &newTabOrder)
{
    QDesignerFormEditorInterface *core = formWindow()->core();
    Q_ASSERT(core);

    m_widgetItem = core->metaDataBase()->item(formWindow());
    Q_ASSERT(m_widgetItem);
    m_oldTabOrder = m_widgetItem->tabOrder();
    m_newTabOrder = newTabOrder;
}

void TabOrderCommand::redo()
{
    m_widgetItem->setTabOrder(m_newTabOrder);
}

void TabOrderCommand::undo()
{
    m_widgetItem->setTabOrder(m_oldTabOrder);
}

// ---- CreateMenuBarCommand ----
CreateMenuBarCommand::CreateMenuBarCommand(QDesignerFormWindowInterface *formWindow)
    : QDesignerFormWindowCommand(QApplication::translate("Command", "Create Menu Bar"), formWindow)
{
}

void CreateMenuBarCommand::init(QMainWindow *mainWindow)
{
    m_mainWindow = mainWindow;
    QDesignerFormEditorInterface *core = formWindow()->core();
    m_menuBar = qobject_cast<QMenuBar*>(core->widgetFactory()->createWidget(u"QMenuBar"_s, m_mainWindow));
    core->widgetFactory()->initialize(m_menuBar);
}

void CreateMenuBarCommand::redo()
{
    QDesignerFormEditorInterface *core = formWindow()->core();
    auto *c = qt_extension<QDesignerContainerExtension*>(core->extensionManager(), m_mainWindow);
    c->addWidget(m_menuBar);

    m_menuBar->setObjectName(u"menuBar"_s);
    formWindow()->ensureUniqueObjectName(m_menuBar);
    core->metaDataBase()->add(m_menuBar);
    formWindow()->emitSelectionChanged();
    m_menuBar->setFocus();
}

void CreateMenuBarCommand::undo()
{
    QDesignerFormEditorInterface *core = formWindow()->core();
    auto *c = qt_extension<QDesignerContainerExtension*>(core->extensionManager(), m_mainWindow);
    for (int i = 0; i < c->count(); ++i) {
        if (c->widget(i) == m_menuBar) {
            c->remove(i);
            break;
        }
    }

    core->metaDataBase()->remove(m_menuBar);
    formWindow()->emitSelectionChanged();
}

// ---- DeleteMenuBarCommand ----
DeleteMenuBarCommand::DeleteMenuBarCommand(QDesignerFormWindowInterface *formWindow)
    : QDesignerFormWindowCommand(QApplication::translate("Command", "Delete Menu Bar"), formWindow)
{
}

void DeleteMenuBarCommand::init(QMenuBar *menuBar)
{
    m_menuBar = menuBar;
    m_mainWindow = qobject_cast<QMainWindow*>(menuBar->parentWidget());
}

void DeleteMenuBarCommand::redo()
{
    if (m_mainWindow) {
        auto *c = qt_extension<QDesignerContainerExtension*>(core()->extensionManager(), m_mainWindow);
        Q_ASSERT(c != nullptr);
        for (int i=0; i<c->count(); ++i) {
            if (c->widget(i) == m_menuBar) {
                c->remove(i);
                break;
            }
        }
    }

    core()->metaDataBase()->remove(m_menuBar);
    m_menuBar->hide();
    m_menuBar->setParent(formWindow());
    formWindow()->emitSelectionChanged();
}

void DeleteMenuBarCommand::undo()
{
    if (m_mainWindow) {
        m_menuBar->setParent(m_mainWindow);
        auto *c = qt_extension<QDesignerContainerExtension*>(core()->extensionManager(), m_mainWindow);

        c->addWidget(m_menuBar);

        core()->metaDataBase()->add(m_menuBar);
        m_menuBar->show();
        formWindow()->emitSelectionChanged();
    }
}

// ---- CreateStatusBarCommand ----
CreateStatusBarCommand::CreateStatusBarCommand(QDesignerFormWindowInterface *formWindow)
    : QDesignerFormWindowCommand(QApplication::translate("Command", "Create Status Bar"), formWindow)
{
}

void CreateStatusBarCommand::init(QMainWindow *mainWindow)
{
    m_mainWindow = mainWindow;
    QDesignerFormEditorInterface *core = formWindow()->core();
    m_statusBar = qobject_cast<QStatusBar*>(core->widgetFactory()->createWidget(u"QStatusBar"_s, m_mainWindow));
    core->widgetFactory()->initialize(m_statusBar);
}

void CreateStatusBarCommand::redo()
{
    QDesignerFormEditorInterface *core = formWindow()->core();
    auto *c = qt_extension<QDesignerContainerExtension*>(core->extensionManager(), m_mainWindow);
    c->addWidget(m_statusBar);

    m_statusBar->setObjectName(u"statusBar"_s);
    formWindow()->ensureUniqueObjectName(m_statusBar);
    core->metaDataBase()->add(m_statusBar);
    formWindow()->emitSelectionChanged();
}

void CreateStatusBarCommand::undo()
{
    QDesignerFormEditorInterface *core = formWindow()->core();
    QDesignerContainerExtension *c = qt_extension<QDesignerContainerExtension*>(core->extensionManager(), m_mainWindow);
    for (int i = 0; i < c->count(); ++i) {
        if (c->widget(i) == m_statusBar) {
            c->remove(i);
            break;
        }
    }

    core->metaDataBase()->remove(m_statusBar);
    formWindow()->emitSelectionChanged();
}

// ---- DeleteStatusBarCommand ----
DeleteStatusBarCommand::DeleteStatusBarCommand(QDesignerFormWindowInterface *formWindow)
    : QDesignerFormWindowCommand(QApplication::translate("Command", "Delete Status Bar"), formWindow)
{
}

void DeleteStatusBarCommand::init(QStatusBar *statusBar)
{
    m_statusBar = statusBar;
    m_mainWindow = qobject_cast<QMainWindow*>(statusBar->parentWidget());
}

void DeleteStatusBarCommand::redo()
{
    if (m_mainWindow) {
        QDesignerContainerExtension *c = qt_extension<QDesignerContainerExtension*>(core()->extensionManager(), m_mainWindow);
        Q_ASSERT(c != nullptr);
        for (int i=0; i<c->count(); ++i) {
            if (c->widget(i) == m_statusBar) {
                c->remove(i);
                break;
            }
        }
    }

    core()->metaDataBase()->remove(m_statusBar);
    m_statusBar->hide();
    m_statusBar->setParent(formWindow());
    formWindow()->emitSelectionChanged();
}

void DeleteStatusBarCommand::undo()
{
    if (m_mainWindow) {
        m_statusBar->setParent(m_mainWindow);
        QDesignerContainerExtension *c = qt_extension<QDesignerContainerExtension*>(core()->extensionManager(), m_mainWindow);

        c->addWidget(m_statusBar);

        core()->metaDataBase()->add(m_statusBar);
        m_statusBar->show();
        formWindow()->emitSelectionChanged();
    }
}

// ---- AddToolBarCommand ----
AddToolBarCommand::AddToolBarCommand(QDesignerFormWindowInterface *formWindow)
    : QDesignerFormWindowCommand(QApplication::translate("Command", "Add Tool Bar"), formWindow)
{
}

void AddToolBarCommand::init(QMainWindow *mainWindow, Qt::ToolBarArea area)
{
    m_mainWindow = mainWindow;
    QDesignerWidgetFactoryInterface * wf =  formWindow()->core()->widgetFactory();
    // Pass on 0 parent first to avoid reparenting flicker.
    m_toolBar = qobject_cast<QToolBar*>(wf->createWidget(u"QToolBar"_s, nullptr));
    m_toolBar->setProperty("_q_desiredArea", QVariant(area));
    wf->initialize(m_toolBar);
    m_toolBar->hide();
}

void AddToolBarCommand::redo()
{
    QDesignerFormEditorInterface *core = formWindow()->core();
    core->metaDataBase()->add(m_toolBar);

    QDesignerContainerExtension *c = qt_extension<QDesignerContainerExtension*>(core->extensionManager(), m_mainWindow);
    c->addWidget(m_toolBar);

    m_toolBar->setObjectName(u"toolBar"_s);
    formWindow()->ensureUniqueObjectName(m_toolBar);
    setPropertySheetWindowTitle(core, m_toolBar, m_toolBar->objectName());
    formWindow()->emitSelectionChanged();
}

void AddToolBarCommand::undo()
{
    QDesignerFormEditorInterface *core = formWindow()->core();
    core->metaDataBase()->remove(m_toolBar);
    QDesignerContainerExtension *c = qt_extension<QDesignerContainerExtension*>(core->extensionManager(), m_mainWindow);
    for (int i = 0; i < c->count(); ++i) {
        if (c->widget(i) == m_toolBar) {
            c->remove(i);
            break;
        }
    }
    formWindow()->emitSelectionChanged();
}

// ---- DockWidgetCommand:: ----
DockWidgetCommand::DockWidgetCommand(const QString &description, QDesignerFormWindowInterface *formWindow)
    : QDesignerFormWindowCommand(description, formWindow)
{
}

DockWidgetCommand::~DockWidgetCommand() = default;

void DockWidgetCommand::init(QDockWidget *dockWidget)
{
    m_dockWidget = dockWidget;
}

// ---- AddDockWidgetCommand ----
AddDockWidgetCommand::AddDockWidgetCommand(QDesignerFormWindowInterface *formWindow)
    : QDesignerFormWindowCommand(QApplication::translate("Command", "Add Dock Window"), formWindow)
{
}

void AddDockWidgetCommand::init(QMainWindow *mainWindow, QDockWidget *dockWidget)
{
    m_mainWindow = mainWindow;
    m_dockWidget = dockWidget;
}

void AddDockWidgetCommand::init(QMainWindow *mainWindow)
{
    m_mainWindow = mainWindow;
    QDesignerFormEditorInterface *core = formWindow()->core();
    m_dockWidget = qobject_cast<QDockWidget*>(core->widgetFactory()->createWidget(u"QDockWidget"_s, m_mainWindow));
}

void AddDockWidgetCommand::redo()
{
    QDesignerFormEditorInterface *core = formWindow()->core();
    QDesignerContainerExtension *c = qt_extension<QDesignerContainerExtension*>(core->extensionManager(), m_mainWindow);
    c->addWidget(m_dockWidget);

    m_dockWidget->setObjectName(u"dockWidget"_s);
    formWindow()->ensureUniqueObjectName(m_dockWidget);
    formWindow()->manageWidget(m_dockWidget);
    formWindow()->emitSelectionChanged();
}

void AddDockWidgetCommand::undo()
{
    QDesignerFormEditorInterface *core = formWindow()->core();
    QDesignerContainerExtension *c = qt_extension<QDesignerContainerExtension*>(core->extensionManager(), m_mainWindow);
    for (int i = 0; i < c->count(); ++i) {
        if (c->widget(i) == m_dockWidget) {
            c->remove(i);
            break;
        }
    }

    formWindow()->unmanageWidget(m_dockWidget);
    formWindow()->emitSelectionChanged();
}

// ---- AdjustWidgetSizeCommand ----
AdjustWidgetSizeCommand::AdjustWidgetSizeCommand(QDesignerFormWindowInterface *formWindow)
    : QDesignerFormWindowCommand(QString(), formWindow)
{
}

void AdjustWidgetSizeCommand::init(QWidget *widget)
{
    m_widget = widget;
    setText(QApplication::translate("Command", "Adjust Size of '%1'").arg(widget->objectName()));
}

QWidget *AdjustWidgetSizeCommand::widgetForAdjust() const
{
    QDesignerFormWindowInterface *fw = formWindow();
    // Return the outer, embedding widget if it is the main container
    if (Utils::isCentralWidget(fw, m_widget))
        return fw->core()->integration()->containerWindow(m_widget);
    return m_widget;
}

void AdjustWidgetSizeCommand::redo()
{
    QWidget *aw = widgetForAdjust();
    m_geometry = aw->geometry();
    QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
    aw->adjustSize();
    const bool isMainContainer = aw != m_widget;
    if (!isMainContainer) {
        /* When doing adjustsize on a selected non-laid out child that has been enlarged
         * and pushed partially over the top/left edge[s], it is possible that it "disappears"
         * when shrinking. In that case, move it back so that it remains visible. */
        if (aw->parentWidget()->layout() == nullptr) {
            const QRect contentsRect = aw->parentWidget()->contentsRect();
            const QRect newGeometry = aw->geometry();
            QPoint newPos = m_geometry.topLeft();
            if (newGeometry.bottom() <= contentsRect.y())
                newPos.setY(contentsRect.y());
            if (newGeometry.right() <= contentsRect.x())
                newPos.setX(contentsRect.x());
            if (newPos != m_geometry.topLeft())
                aw->move(newPos);
        }
    }
    updatePropertyEditor();
}

void AdjustWidgetSizeCommand::undo()
{
    QWidget *aw = widgetForAdjust();
    aw->resize(m_geometry.size());
    if (m_geometry.topLeft() != aw->geometry().topLeft())
        aw->move(m_geometry.topLeft());
    updatePropertyEditor();
}

void AdjustWidgetSizeCommand::updatePropertyEditor() const
{
    if (QDesignerPropertyEditorInterface *propertyEditor = formWindow()->core()->propertyEditor()) {
        if (propertyEditor->object() == m_widget)
            propertyEditor->setPropertyValue(u"geometry"_s, m_widget->geometry(), true);
    }
}
// ------------  ChangeFormLayoutItemRoleCommand

ChangeFormLayoutItemRoleCommand::ChangeFormLayoutItemRoleCommand(QDesignerFormWindowInterface *formWindow) :
    QDesignerFormWindowCommand(QApplication::translate("Command", "Change Form Layout Item Geometry"), formWindow),
    m_operation(SpanningToLabel)
{
}

void ChangeFormLayoutItemRoleCommand::init(QWidget *widget, Operation op)
{
    m_widget = widget;
    m_operation = op;
}

void ChangeFormLayoutItemRoleCommand::redo()
{
    doOperation(m_operation);
}

void ChangeFormLayoutItemRoleCommand::undo()
{
    doOperation(reverseOperation(m_operation));
}

ChangeFormLayoutItemRoleCommand::Operation ChangeFormLayoutItemRoleCommand::reverseOperation(Operation op)
{
    switch (op) {
    case SpanningToLabel:
        return LabelToSpanning;
    case SpanningToField:
        return FieldToSpanning;
    case LabelToSpanning:
        return SpanningToLabel;
    case FieldToSpanning:
        return SpanningToField;
    }
    return SpanningToField;
}

void ChangeFormLayoutItemRoleCommand::doOperation(Operation op)
{
    QFormLayout *fl = ChangeFormLayoutItemRoleCommand::managedFormLayoutOf(formWindow()->core(), m_widget);
    const int index = fl->indexOf(m_widget);
    Q_ASSERT(index != -1);
    int row = 0;
    QFormLayout::ItemRole role = QFormLayout::LabelRole;
    fl->getItemPosition (index, &row, &role);
    Q_ASSERT(index != -1);
    QLayoutItem *item = fl->takeAt(index);
    const QRect area = QRect(0, row, 2, 1);
    switch (op) {
    case SpanningToLabel:
        fl->setItem(row, QFormLayout::LabelRole, item);
        QLayoutSupport::createEmptyCells(fl);
        break;
    case SpanningToField:
        fl->setItem(row, QFormLayout::FieldRole, item);
        QLayoutSupport::createEmptyCells(fl);
        break;
    case LabelToSpanning:
    case FieldToSpanning:
        QLayoutSupport::removeEmptyCells(fl, area);
        fl->setItem(row, QFormLayout::SpanningRole, item);
        break;
    }
}

unsigned ChangeFormLayoutItemRoleCommand::possibleOperations(QDesignerFormEditorInterface *core, QWidget *w)
{
    QFormLayout *fl = managedFormLayoutOf(core, w);
    if (!fl)
        return 0;
    const int index = fl->indexOf(w);
    if (index == -1)
        return 0;
    int row = 0, col = 0, colspan = 0;
    getFormLayoutItemPosition(fl, index, &row, &col, nullptr, &colspan);
    // Spanning item?
    if (colspan > 1)
        return SpanningToLabel|SpanningToField;
    // Is the neighbouring column free, that is, can the current item be expanded?
    const QFormLayout::ItemRole neighbouringRole = col == 0 ? QFormLayout::FieldRole : QFormLayout::LabelRole;
    const bool empty = LayoutInfo::isEmptyItem(fl->itemAt(row, neighbouringRole));
    if (empty)
        return col == 0 ? LabelToSpanning : FieldToSpanning;
    return 0;
}

QFormLayout *ChangeFormLayoutItemRoleCommand::managedFormLayoutOf(QDesignerFormEditorInterface *core, QWidget *w)
{
    if (auto *layout = LayoutInfo::managedLayout(core, w->parentWidget())) {
        if (auto *fl = qobject_cast<QFormLayout *>(layout))
            return fl;
    }
    return nullptr;
}

// ---- ChangeLayoutItemGeometry ----
ChangeLayoutItemGeometry::ChangeLayoutItemGeometry(QDesignerFormWindowInterface *formWindow)
    : QDesignerFormWindowCommand(QApplication::translate("Command", "Change Layout Item Geometry"), formWindow)
{
}

void ChangeLayoutItemGeometry::init(QWidget *widget, int row, int column, int rowspan, int colspan)
{
    m_widget = widget;
    Q_ASSERT(m_widget->parentWidget() != nullptr);

    QLayout *layout = LayoutInfo::managedLayout(formWindow()->core(), m_widget->parentWidget());
    Q_ASSERT(layout != nullptr);

    auto *grid = qobject_cast<QGridLayout*>(layout);
    Q_ASSERT(grid != nullptr);

    const int itemIndex = grid->indexOf(m_widget);
    Q_ASSERT(itemIndex != -1);

    int current_row = 0, current_column = 0, current_rowspan = 0, current_colspan = 0;
    grid->getItemPosition(itemIndex, &current_row, &current_column, &current_rowspan, &current_colspan);

    m_oldInfo.setRect(current_column, current_row, current_colspan, current_rowspan);
    m_newInfo.setRect(column, row, colspan, rowspan);
}

void ChangeLayoutItemGeometry::changeItemPosition(const QRect &g)
{
    QLayout *layout = LayoutInfo::managedLayout(formWindow()->core(), m_widget->parentWidget());
    Q_ASSERT(layout != nullptr);

    auto *grid = qobject_cast<QGridLayout*>(layout);
    Q_ASSERT(grid != nullptr);

    const int itemIndex = grid->indexOf(m_widget);
    Q_ASSERT(itemIndex != -1);

    QLayoutItem *item = grid->takeAt(itemIndex);
    delete item;

    if (!QLayoutSupport::removeEmptyCells(grid, g))
        qWarning() << "ChangeLayoutItemGeometry::changeItemPosition: Nonempty cell at " <<  g << '.';

    grid->addWidget(m_widget, g.top(), g.left(), g.height(), g.width());

    grid->invalidate();
    grid->activate();

    QLayoutSupport::createEmptyCells(grid);

    formWindow()->clearSelection(false);
    formWindow()->selectWidget(m_widget, true);
}

void ChangeLayoutItemGeometry::redo()
{
    changeItemPosition(m_newInfo);
}

void ChangeLayoutItemGeometry::undo()
{
    changeItemPosition(m_oldInfo);
}

// ---- ContainerWidgetCommand ----
ContainerWidgetCommand::ContainerWidgetCommand(QDesignerFormWindowInterface *formWindow)  :
    QDesignerFormWindowCommand(QString(), formWindow),
    m_index(-1)
{
}

ContainerWidgetCommand::~ContainerWidgetCommand() = default;

QDesignerContainerExtension *ContainerWidgetCommand::containerExtension() const
{
    QExtensionManager *mgr = core()->extensionManager();
    return qt_extension<QDesignerContainerExtension*>(mgr, m_containerWidget);
}

void ContainerWidgetCommand::init(QWidget *containerWidget)
{
    m_containerWidget = containerWidget;

    if (QDesignerContainerExtension *c = containerExtension()) {
        m_index = c->currentIndex();
        m_widget = c->widget(m_index);
    }
}

void ContainerWidgetCommand::removePage()
{
    if (QDesignerContainerExtension *c = containerExtension()) {
        if (const int count = c->count()) {
            // Undo add after last?
            const int deleteIndex = m_index >= 0 ? m_index : count - 1;
            c->remove(deleteIndex);
            m_widget->hide();
            m_widget->setParent(formWindow());
        }
    }
}

void ContainerWidgetCommand::addPage()
{
    if (QDesignerContainerExtension *c = containerExtension()) {
        int newCurrentIndex = 0;
        if (m_index >= 0) {
            c->insertWidget(m_index, m_widget);
            newCurrentIndex = m_index;
        } else {
            c->addWidget(m_widget);
            newCurrentIndex = c->count() -1 ;
        }
        m_widget->show();
        c->setCurrentIndex(newCurrentIndex);
    }
}

// ---- DeleteContainerWidgetPageCommand ----
DeleteContainerWidgetPageCommand::DeleteContainerWidgetPageCommand(QDesignerFormWindowInterface *formWindow)
    : ContainerWidgetCommand(formWindow)
{
}

DeleteContainerWidgetPageCommand::~DeleteContainerWidgetPageCommand() = default;

void DeleteContainerWidgetPageCommand::init(QWidget *containerWidget, ContainerType ct)
{
    ContainerWidgetCommand::init(containerWidget);
    switch (ct) {
    case WizardContainer:
    case PageContainer:
        setText(QApplication::translate("Command", "Delete Page"));
        break;
    case MdiContainer:
        setText(QApplication::translate("Command", "Delete Subwindow"));
        break;
    }
}

void DeleteContainerWidgetPageCommand::redo()
{
    removePage();
    cheapUpdate();
}

void DeleteContainerWidgetPageCommand::undo()
{
    addPage();
    cheapUpdate();
}

// ---- AddContainerWidgetPageCommand ----
AddContainerWidgetPageCommand::AddContainerWidgetPageCommand(QDesignerFormWindowInterface *formWindow)
    : ContainerWidgetCommand(formWindow)
{
}

AddContainerWidgetPageCommand::~AddContainerWidgetPageCommand() = default;

void AddContainerWidgetPageCommand::init(QWidget *containerWidget, ContainerType ct, InsertionMode mode)
{
    m_containerWidget = containerWidget;

    if (QDesignerContainerExtension *c = containerExtension()) {
        m_index = c->currentIndex();
        if (m_index >= 0 && mode == InsertAfter)
            m_index++;
        m_widget = nullptr;
        const QDesignerFormEditorInterface *core = formWindow()->core();
        switch (ct) {
        case PageContainer:
            setText(QApplication::translate("Command", "Insert Page"));
            m_widget = new QDesignerWidget(formWindow(), m_containerWidget);
            m_widget->setObjectName(u"page"_s);
            break;
        case MdiContainer:
            setText(QApplication::translate("Command", "Insert Subwindow"));
            m_widget = new QDesignerWidget(formWindow(), m_containerWidget);
            m_widget->setObjectName(u"subwindow"_s);
            setPropertySheetWindowTitle(core, m_widget, QApplication::translate("Command", "Subwindow"));
            break;
        case WizardContainer: // Apply style, don't manage
            m_widget = core->widgetFactory()->createWidget(u"QWizardPage"_s, nullptr);
            break;
        }
        formWindow()->ensureUniqueObjectName(m_widget);
        core->metaDataBase()->add(m_widget);
    }
}

void AddContainerWidgetPageCommand::redo()
{
    addPage();
    cheapUpdate();
}

void AddContainerWidgetPageCommand::undo()
{
    removePage();
    cheapUpdate();
}

ChangeCurrentPageCommand::ChangeCurrentPageCommand(QDesignerFormWindowInterface *formWindow)
    :
    QDesignerFormWindowCommand(QString(), formWindow), m_oldIndex(0), m_newIndex(0)
{
}

ChangeCurrentPageCommand::~ChangeCurrentPageCommand() = default;

QDesignerContainerExtension *ChangeCurrentPageCommand::containerExtension() const
{
    QExtensionManager *mgr = core()->extensionManager();
    return qt_extension<QDesignerContainerExtension*>(mgr, m_containerWidget);
}

void ChangeCurrentPageCommand::init(QWidget *containerWidget, int newIndex)
{
    m_containerWidget = containerWidget;

    if (QDesignerContainerExtension *c = containerExtension()) {
        m_newIndex = newIndex;
        m_oldIndex = c->currentIndex();
        m_widget = c->widget(m_oldIndex);
    }
}

void ChangeCurrentPageCommand::redo()
{
    containerExtension()->setCurrentIndex(m_newIndex);
}

void ChangeCurrentPageCommand::undo()
{
    containerExtension()->setCurrentIndex(m_oldIndex);
}

static const int itemRoles[] = {
    Qt::DecorationPropertyRole,
    Qt::DisplayPropertyRole,
    Qt::ToolTipPropertyRole,
    Qt::StatusTipPropertyRole,
    Qt::WhatsThisPropertyRole,
    Qt::FontRole,
    Qt::TextAlignmentRole,
    Qt::BackgroundRole,
    Qt::ForegroundRole,
    Qt::CheckStateRole
};

template<class T>
static void copyRoleFromItem(ItemData *id, int role, const T *item)
{
    QVariant v = item->data(role);
    if (v.isValid())
        id->m_properties.insert(role, v);
}

template<class T>
static void copyRolesFromItem(ItemData *id, const T *item, bool editor)
{
    static const Qt::ItemFlags defaultFlags = T().flags();

    for (int i : itemRoles)
        copyRoleFromItem<T>(id, i, item);

    if (editor)
        copyRoleFromItem<T>(id, ItemFlagsShadowRole, item);
    else if (item->flags() != defaultFlags)
        id->m_properties.insert(ItemFlagsShadowRole, QVariant::fromValue(int(item->flags())));
}

template<class T>
static void copyRolesToItem(const ItemData *id, T *item, DesignerIconCache *iconCache, bool editor)
{
    for (auto it = id->m_properties.cbegin(), end = id->m_properties.cend(); it != end; ++it) {
        if (it.value().isValid()) {
            if (!editor && it.key() == ItemFlagsShadowRole) {
                item->setFlags((Qt::ItemFlags)it.value().toInt());
            } else {
                item->setData(it.key(), it.value());
                switch (it.key()) {
                case Qt::DecorationPropertyRole:
                    if (iconCache)
                        item->setIcon(iconCache->icon(qvariant_cast<PropertySheetIconValue>(it.value())));
                    break;
                case Qt::DisplayPropertyRole:
                    item->setText(qvariant_cast<PropertySheetStringValue>(it.value()).value());
                    break;
                case Qt::ToolTipPropertyRole:
                    item->setToolTip(qvariant_cast<PropertySheetStringValue>(it.value()).value());
                    break;
                case Qt::StatusTipPropertyRole:
                    item->setStatusTip(qvariant_cast<PropertySheetStringValue>(it.value()).value());
                    break;
                case Qt::WhatsThisPropertyRole:
                    item->setWhatsThis(qvariant_cast<PropertySheetStringValue>(it.value()).value());
                    break;
                }
            }
        }
    }

    if (editor)
        item->setFlags(item->flags() | Qt::ItemIsEditable);
}

ItemData::ItemData(const QListWidgetItem *item, bool editor)
{
    copyRolesFromItem<QListWidgetItem>(this, item, editor);
}

QListWidgetItem *ItemData::createListItem(DesignerIconCache *iconCache, bool editor) const
{
    auto *item = new QListWidgetItem();
    copyRolesToItem(this, item, iconCache, editor);
    return item;
}

ItemData::ItemData(const QTableWidgetItem *item, bool editor)
{
    copyRolesFromItem(this, item, editor);
}

QTableWidgetItem *ItemData::createTableItem(DesignerIconCache *iconCache, bool editor) const
{
    auto *item = new QTableWidgetItem;
    copyRolesToItem(this, item, iconCache, editor);
    return item;
}

static void copyRoleFromItem(ItemData *id, int role, const QTreeWidgetItem *item, int column)
{
    QVariant v = item->data(column, role);
    if (v.isValid())
        id->m_properties.insert(role, v);
}

ItemData::ItemData(const QTreeWidgetItem *item, int column)
{
    copyRoleFromItem(this, Qt::EditRole, item, column);
    PropertySheetStringValue str(item->text(column));
    m_properties.insert(Qt::DisplayPropertyRole, QVariant::fromValue(str));

    for (int i : itemRoles)
        copyRoleFromItem(this, i, item, column);
}

void ItemData::fillTreeItemColumn(QTreeWidgetItem *item, int column, DesignerIconCache *iconCache) const
{
    for (auto it = m_properties.cbegin(), end = m_properties.cend(); it != end; ++it) {
        if (it.value().isValid()) {
            item->setData(column, it.key(), it.value());
            switch (it.key()) {
            case Qt::DecorationPropertyRole:
                if (iconCache)
                    item->setIcon(column, iconCache->icon(qvariant_cast<PropertySheetIconValue>(it.value())));
                break;
            case Qt::DisplayPropertyRole:
                item->setText(column, qvariant_cast<PropertySheetStringValue>(it.value()).value());
                break;
            case Qt::ToolTipPropertyRole:
                item->setToolTip(column, qvariant_cast<PropertySheetStringValue>(it.value()).value());
                break;
            case Qt::StatusTipPropertyRole:
                item->setStatusTip(column, qvariant_cast<PropertySheetStringValue>(it.value()).value());
                break;
            case Qt::WhatsThisPropertyRole:
                item->setWhatsThis(column, qvariant_cast<PropertySheetStringValue>(it.value()).value());
                break;
            }
        }
    }
}

ListContents::ListContents(const QTreeWidgetItem *item)
{
    for (int i = 0; i < item->columnCount(); i++)
        m_items.append(ItemData(item, i));
}

QTreeWidgetItem *ListContents::createTreeItem(DesignerIconCache *iconCache) const
{
    auto *item = new QTreeWidgetItem;
    int i = 0;
    for (const ItemData &id : m_items)
        id.fillTreeItemColumn(item, i++, iconCache);
    return item;
}

void ListContents::createFromListWidget(const QListWidget *listWidget, bool editor)
{
    m_items.clear();

    for (int i = 0; i < listWidget->count(); i++)
        m_items.append(ItemData(listWidget->item(i), editor));
}

void ListContents::applyToListWidget(QListWidget *listWidget, DesignerIconCache *iconCache,
                                     bool editor, Qt::Alignment alignmentDefault) const
{
    listWidget->clear();

    int i = 0;
    for (const ItemData &entry : m_items) {
        auto *item = entry.isValid()
            ? entry.createListItem(iconCache, editor)
            : new QListWidgetItem(TableWidgetContents::defaultHeaderText(i));
        if (item->textAlignment() == 0)
            item->setTextAlignment(alignmentDefault);
        listWidget->addItem(item);
        i++;
    }
}

void ListContents::createFromComboBox(const QComboBox *comboBox)
{
    m_items.clear();

    const int count = comboBox->count();
    for (int i = 0; i < count; i++) {
        // We might encounter items added in a custom combo
        // constructor. Ignore those.
        const QVariant textValue = comboBox->itemData(i, Qt::DisplayPropertyRole);
        if (!textValue.isNull()) {
            ItemData entry;
            entry.m_properties.insert(Qt::DisplayPropertyRole, textValue);
            const QVariant iconValue =  comboBox->itemData(i, Qt::DecorationPropertyRole);
            if (!iconValue.isNull())
                entry.m_properties.insert(Qt::DecorationPropertyRole, iconValue);
            m_items.append(entry);
        }
    }
}

void ListContents::applyToComboBox(QComboBox *comboBox, DesignerIconCache *iconCache) const
{
    comboBox->clear();

    for (const ItemData &hash : m_items) {
        QIcon icon;
        if (iconCache)
            icon = iconCache->icon(qvariant_cast<PropertySheetIconValue>(
                    hash.m_properties[Qt::DecorationPropertyRole]));
        QVariant var = hash.m_properties[Qt::DisplayPropertyRole];
        auto str = qvariant_cast<PropertySheetStringValue>(var);
        comboBox->addItem(icon, str.value());
        comboBox->setItemData(comboBox->count() - 1,
                              var,
                              Qt::DisplayPropertyRole);
        comboBox->setItemData(comboBox->count() - 1,
                              hash.m_properties[Qt::DecorationPropertyRole],
                              Qt::DecorationPropertyRole);
    }
}

// --------- TableWidgetContents

TableWidgetContents::TableWidgetContents() = default;

void TableWidgetContents::clear()
{
    m_horizontalHeader.m_items.clear();
    m_verticalHeader.m_items.clear();
    m_items.clear();
    m_columnCount = 0;
    m_rowCount = 0;
}

QString TableWidgetContents::defaultHeaderText(int i)
{
    return QString::number(i + 1);
}

bool TableWidgetContents::nonEmpty(const QTableWidgetItem *item, int headerColumn)
{
    static const Qt::ItemFlags defaultFlags = QTableWidgetItem().flags();

    if (item->flags() != defaultFlags)
        return true;

    QString text = qvariant_cast<PropertySheetStringValue>(item->data(Qt::DisplayPropertyRole)).value();
    if (!text.isEmpty()) {
        if (headerColumn < 0 || text != defaultHeaderText(headerColumn))
            return true;
    } else {
        // FIXME: This doesn't seem to make sense
        return true;
    }

    auto pred = [item](int i) {
        return i != Qt::DisplayPropertyRole && item->data(i).isValid();
    };
    return std::any_of(std::begin(itemRoles), std::end(itemRoles), pred);
}

void TableWidgetContents::insertHeaderItem(const QTableWidgetItem *item, int i, ListContents *header, bool editor)
{
    if (nonEmpty(item, i))
        header->m_items.append(ItemData(item, editor));
    else
        header->m_items.append(ItemData());
}

void TableWidgetContents::fromTableWidget(const QTableWidget *tableWidget, bool editor)
{
    clear();
    m_columnCount = tableWidget->columnCount();
    m_rowCount = tableWidget->rowCount();
    // horiz header: Legacy behaviour: auto-generate number for empty items
    for (int col = 0; col <  m_columnCount; col++)
        if (const QTableWidgetItem *item = tableWidget->horizontalHeaderItem(col))
            insertHeaderItem(item, col, &m_horizontalHeader, editor);
    // vertical header: Legacy behaviour: auto-generate number for empty items
    for (int row = 0; row < m_rowCount; row++)
        if (const QTableWidgetItem *item = tableWidget->verticalHeaderItem(row))
            insertHeaderItem(item, row, &m_verticalHeader, editor);
    // cell data
    for (int col = 0; col < m_columnCount; col++)
        for (int row = 0; row < m_rowCount; row++)
            if (const QTableWidgetItem *item = tableWidget->item(row, col))
                if (nonEmpty(item, -1))
                    m_items.insert(CellRowColumnAddress(row, col), ItemData(item, editor));
}

void TableWidgetContents::applyToTableWidget(QTableWidget *tableWidget, DesignerIconCache *iconCache, bool editor) const
{
    tableWidget->clear();

    tableWidget->setColumnCount(m_columnCount);
    tableWidget->setRowCount(m_rowCount);

    // horiz header
    int col = 0;
    for (const ItemData &id : m_horizontalHeader.m_items) {
        if (id.isValid())
            tableWidget->setHorizontalHeaderItem(col, id.createTableItem(iconCache, editor));
        col++;
    }
    // vertical header
    int row = 0;
    for (const ItemData &id : m_verticalHeader.m_items) {
        if (id.isValid())
            tableWidget->setVerticalHeaderItem(row, id.createTableItem(iconCache, editor));
        row++;
    }
    // items
    for (auto it = m_items.cbegin(), icend = m_items.cend(); it != icend; ++ it) {
        tableWidget->setItem(it.key().first, it.key().second,
                             it.value().createTableItem(iconCache, editor));
    }
}

bool comparesEqual(const TableWidgetContents &lhs,
                   const TableWidgetContents &rhs) noexcept
{
    return lhs.m_columnCount == rhs.m_columnCount && lhs.m_rowCount == rhs.m_rowCount &&
           lhs.m_horizontalHeader.m_items == rhs.m_horizontalHeader.m_items &&
           lhs.m_verticalHeader.m_items == rhs.m_verticalHeader.m_items &&
           lhs.m_items == rhs.m_items;
}

// ---- ChangeTableContentsCommand ----
ChangeTableContentsCommand::ChangeTableContentsCommand(QDesignerFormWindowInterface *formWindow)  :
    QDesignerFormWindowCommand(QApplication::translate("Command", "Change Table Contents"),
    formWindow), m_iconCache(nullptr)
{
    if (auto *fwb = qobject_cast<FormWindowBase *>(formWindow))
        m_iconCache = fwb->iconCache();
}

void ChangeTableContentsCommand::init(QTableWidget *tableWidget,
        const TableWidgetContents &oldCont, const TableWidgetContents &newCont)
{
    m_tableWidget = tableWidget;
    m_oldContents = oldCont;
    m_newContents = newCont;
}

void ChangeTableContentsCommand::redo()
{
    m_newContents.applyToTableWidget(m_tableWidget, m_iconCache, false);
    QMetaObject::invokeMethod(m_tableWidget, "updateGeometries");
}

void ChangeTableContentsCommand::undo()
{
    m_oldContents.applyToTableWidget(m_tableWidget, m_iconCache, false);
    QMetaObject::invokeMethod(m_tableWidget, "updateGeometries");
}

// --------- TreeWidgetContents
TreeWidgetContents::ItemContents::ItemContents(const QTreeWidgetItem *item, bool editor) :
    ListContents(item)
{
    static const Qt::ItemFlags defaultFlags = QTreeWidgetItem().flags();

    if (editor) {
        QVariant v = item->data(0, ItemFlagsShadowRole);
        m_itemFlags = v.isValid() ? v.toInt() : -1;
    } else  {
        m_itemFlags = (item->flags() != defaultFlags) ? int(item->flags()) : -1;
    }

    for (int i = 0; i < item->childCount(); i++)
        m_children.append(ItemContents(item->child(i), editor));
}

QTreeWidgetItem *TreeWidgetContents::ItemContents::createTreeItem(DesignerIconCache *iconCache, bool editor) const
{
    QTreeWidgetItem *item = ListContents::createTreeItem(iconCache);

    if (editor)
        item->setFlags(item->flags() | Qt::ItemIsEditable);

    if (m_itemFlags != -1) {
        if (editor)
            item->setData(0, ItemFlagsShadowRole, QVariant::fromValue(m_itemFlags));
        else
            item->setFlags((Qt::ItemFlags)m_itemFlags);
    }

    for (const ItemContents &ic : m_children)
        item->addChild(ic.createTreeItem(iconCache, editor));

    return item;
}

bool comparesEqual(const TreeWidgetContents::ItemContents &lhs,
                   const TreeWidgetContents::ItemContents &rhs) noexcept
{
    return lhs.m_itemFlags == rhs.m_itemFlags && lhs.m_items == rhs.m_items
        && lhs.m_children == rhs.m_children;
}

void TreeWidgetContents::clear()
{
    m_headerItem.m_items.clear();
    m_rootItems.clear();
}

void TreeWidgetContents::fromTreeWidget(const QTreeWidget *treeWidget, bool editor)
{
    clear();
    m_headerItem = ListContents(treeWidget->headerItem());
    for (int col = 0; col < treeWidget->topLevelItemCount(); col++)
        m_rootItems.append(ItemContents(treeWidget->topLevelItem(col), editor));
}

void TreeWidgetContents::applyToTreeWidget(QTreeWidget *treeWidget, DesignerIconCache *iconCache, bool editor) const
{
    treeWidget->clear();

    treeWidget->setColumnCount(m_headerItem.m_items.size());
    treeWidget->setHeaderItem(m_headerItem.createTreeItem(iconCache));
    for (const ItemContents &ic : m_rootItems)
        treeWidget->addTopLevelItem(ic.createTreeItem(iconCache, editor));
    treeWidget->expandAll();
}

// ---- ChangeTreeContentsCommand ----
ChangeTreeContentsCommand::ChangeTreeContentsCommand(QDesignerFormWindowInterface *formWindow)
    : QDesignerFormWindowCommand(QApplication::translate("Command", "Change Tree Contents"), formWindow),
        m_iconCache(nullptr)
{
    if (auto *fwb = qobject_cast<FormWindowBase *>(formWindow))
        m_iconCache = fwb->iconCache();
}

void ChangeTreeContentsCommand::init(QTreeWidget *treeWidget,
        const TreeWidgetContents &oldState, const TreeWidgetContents &newState)
{
    m_treeWidget = treeWidget;
    m_oldState = oldState;
    m_newState = newState;
}

void ChangeTreeContentsCommand::redo()
{
    m_newState.applyToTreeWidget(m_treeWidget, m_iconCache, false);
}

void ChangeTreeContentsCommand::undo()
{
    m_oldState.applyToTreeWidget(m_treeWidget, m_iconCache, false);
}

// ---- ChangeListContentsCommand ----
ChangeListContentsCommand::ChangeListContentsCommand(QDesignerFormWindowInterface *formWindow)
    : QDesignerFormWindowCommand(QString(), formWindow), m_iconCache(nullptr)
{
    if (auto *fwb = qobject_cast<FormWindowBase *>(formWindow))
        m_iconCache = fwb->iconCache();
}

void ChangeListContentsCommand::init(QListWidget *listWidget,
        const ListContents &oldItems, const ListContents &items)
{
    m_listWidget = listWidget;
    m_comboBox = nullptr;

    m_newItemsState = items;
    m_oldItemsState = oldItems;
}

void ChangeListContentsCommand::init(QComboBox *comboBox,
        const ListContents &oldItems, const ListContents &items)
{
    m_listWidget = nullptr;
    m_comboBox = comboBox;

    m_newItemsState = items;
    m_oldItemsState = oldItems;
}

void ChangeListContentsCommand::redo()
{
    if (m_listWidget)
        m_newItemsState.applyToListWidget(m_listWidget, m_iconCache, false);
    else if (m_comboBox)
        m_newItemsState.applyToComboBox(m_comboBox, m_iconCache);
}

void ChangeListContentsCommand::undo()
{
    if (m_listWidget)
        m_oldItemsState.applyToListWidget(m_listWidget, m_iconCache, false);
    else if (m_comboBox)
        m_oldItemsState.applyToComboBox(m_comboBox, m_iconCache);
}

// ---- AddActionCommand ----

AddActionCommand::AddActionCommand(QDesignerFormWindowInterface *formWindow) :
    QDesignerFormWindowCommand(QApplication::translate("Command", "Add action"), formWindow)
{
}

void AddActionCommand::init(QAction *action)
{
    Q_ASSERT(m_action == nullptr);
    m_action = action;
}

void AddActionCommand::redo()
{
    core()->actionEditor()->setFormWindow(formWindow());
    core()->actionEditor()->manageAction(m_action);
}

void AddActionCommand::undo()
{
    core()->actionEditor()->setFormWindow(formWindow());
    core()->actionEditor()->unmanageAction(m_action);
}

// ---- RemoveActionCommand ----

RemoveActionCommand::RemoveActionCommand(QDesignerFormWindowInterface *formWindow) :
    QDesignerFormWindowCommand(QApplication::translate("Command", "Remove action"), formWindow)
{
}

static RemoveActionCommand::ActionData findActionIn(QAction *action)
{
    RemoveActionCommand::ActionData result;
    // We only want menus and toolbars, no toolbuttons.
    const QObjectList associatedObjects = action->associatedObjects();
    for (QObject *obj : associatedObjects) {
        if (!qobject_cast<const QMenu *>(obj) && !qobject_cast<const QToolBar *>(obj))
            continue;
        auto *widget = static_cast<QWidget *>(obj);
        const auto actionList = widget->actions();
        for (qsizetype i = 0, size = actionList.size(); i < size; ++i) {
            if (actionList.at(i) == action) {
                QAction *before = nullptr;
                if (i + 1 < size)
                    before = actionList.at(i + 1);
                result.append(RemoveActionCommand::ActionDataItem(before, widget));
                break;
            }
        }
    }
    return result;
}

void RemoveActionCommand::init(QAction *action)
{
    Q_ASSERT(m_action == nullptr);
    m_action = action;

    m_actionData = findActionIn(action);
}

void RemoveActionCommand::redo()
{
    QDesignerFormWindowInterface *fw = formWindow();
    for (const ActionDataItem &item : std::as_const(m_actionData)) {
        item.widget->removeAction(m_action);
    }
    // Notify components (for example, signal slot editor)
    if (auto *fwb = qobject_cast<qdesigner_internal::FormWindowBase *>(fw))
        fwb->emitObjectRemoved(m_action);

    core()->actionEditor()->setFormWindow(fw);
    core()->actionEditor()->unmanageAction(m_action);
    if (!m_actionData.isEmpty())
        core()->objectInspector()->setFormWindow(fw);
}

void RemoveActionCommand::undo()
{
    core()->actionEditor()->setFormWindow(formWindow());
    core()->actionEditor()->manageAction(m_action);
    for (const ActionDataItem &item : std::as_const(m_actionData))
        item.widget->insertAction(item.before, m_action);
    if (!m_actionData.isEmpty())
        core()->objectInspector()->setFormWindow(formWindow());
}

// ---- ActionInsertionCommand ----

ActionInsertionCommand::ActionInsertionCommand(const QString &text, QDesignerFormWindowInterface *formWindow) :
    QDesignerFormWindowCommand(text, formWindow),
    m_parentWidget(nullptr),
    m_action(nullptr),
    m_beforeAction(nullptr),
    m_update(false)
{
}

void ActionInsertionCommand::init(QWidget *parentWidget, QAction *action, QAction *beforeAction, bool update)
{
    Q_ASSERT(m_parentWidget == nullptr);
    Q_ASSERT(m_action == nullptr);

    m_parentWidget = parentWidget;
    m_action = action;
    m_beforeAction = beforeAction;
    m_update = update;
}

void ActionInsertionCommand::insertAction()
{
    Q_ASSERT(m_action != nullptr);
    Q_ASSERT(m_parentWidget != nullptr);

    if (m_beforeAction)
        m_parentWidget->insertAction(m_beforeAction, m_action);
    else
        m_parentWidget->addAction(m_action);

    if (m_update) {
        cheapUpdate();
        if (QMenu *menu = m_action->menu())
            selectUnmanagedObject(menu);
        else
            selectUnmanagedObject(m_action);
        PropertyHelper::triggerActionChanged(m_action); // Update Used column in action editor.
    }
}
void ActionInsertionCommand::removeAction()
{
    Q_ASSERT(m_action != nullptr);
    Q_ASSERT(m_parentWidget != nullptr);

    if (auto *menu = qobject_cast<QDesignerMenu*>(m_parentWidget))
        menu->hideSubMenu();

    m_parentWidget->removeAction(m_action);

    if (m_update) {
        cheapUpdate();
        selectUnmanagedObject(m_parentWidget);
        PropertyHelper::triggerActionChanged(m_action); // Update Used column in action editor.
    }
}

InsertActionIntoCommand::InsertActionIntoCommand(QDesignerFormWindowInterface *formWindow) :
    ActionInsertionCommand(QApplication::translate("Command", "Add action"), formWindow)
{
}
// ---- RemoveActionFromCommand ----

RemoveActionFromCommand::RemoveActionFromCommand(QDesignerFormWindowInterface *formWindow) :
    ActionInsertionCommand(QApplication::translate("Command", "Remove action"), formWindow)
{
}

// ---- AddMenuActionCommand ----

MenuActionCommand::MenuActionCommand(const QString &text, QDesignerFormWindowInterface *formWindow) :
    QDesignerFormWindowCommand(text, formWindow),
    m_action(nullptr),
    m_actionBefore(nullptr),
    m_menuParent(nullptr),
    m_associatedWidget(nullptr),
    m_objectToSelect(nullptr)
{
}

void MenuActionCommand::init(QAction *action, QAction *actionBefore,
                             QWidget *associatedWidget, QWidget *objectToSelect)
{
    QMenu *menu = action->menu();
    Q_ASSERT(menu);
    m_menuParent = menu->parentWidget();
    m_action = action;
    m_actionBefore = actionBefore;
    m_associatedWidget = associatedWidget;
    m_objectToSelect = objectToSelect;
}

void MenuActionCommand::insertMenu()
{
    core()->metaDataBase()->add(m_action);
    QMenu *menu = m_action->menu();
    if (m_menuParent && menu->parentWidget() != m_menuParent)
        menu->setParent(m_menuParent);
    core()->metaDataBase()->add(menu);
    m_associatedWidget->insertAction(m_actionBefore, m_action);
    cheapUpdate();
    selectUnmanagedObject(menu);
}

void MenuActionCommand::removeMenu()
{
    m_action->menu()->setParent(nullptr);
    QMenu *menu = m_action->menu();
    core()->metaDataBase()->remove(menu);
    menu->setParent(nullptr);
    core()->metaDataBase()->remove(m_action);
    m_associatedWidget->removeAction(m_action);
    cheapUpdate();
    selectUnmanagedObject(m_objectToSelect);
}

AddMenuActionCommand::AddMenuActionCommand(QDesignerFormWindowInterface *formWindow)  :
    MenuActionCommand(QApplication::translate("Command", "Add menu"), formWindow)
{
}

// ---- RemoveMenuActionCommand ----
RemoveMenuActionCommand::RemoveMenuActionCommand(QDesignerFormWindowInterface *formWindow) :
    MenuActionCommand(QApplication::translate("Command", "Remove menu"), formWindow)
{
}

// ---- CreateSubmenuCommand ----
CreateSubmenuCommand::CreateSubmenuCommand(QDesignerFormWindowInterface *formWindow) :
    QDesignerFormWindowCommand(QApplication::translate("Command", "Create submenu"), formWindow),
    m_action(nullptr),
    m_menu(nullptr),
    m_objectToSelect(nullptr)
{
}

void CreateSubmenuCommand::init(QDesignerMenu *menu, QAction *action, QObject *objectToSelect)
{
    m_menu = menu;
    m_action = action;
    m_objectToSelect = objectToSelect;
}

void CreateSubmenuCommand::redo()
{
    m_menu->createRealMenuAction(m_action);
    cheapUpdate();
    if (m_objectToSelect)
        selectUnmanagedObject(m_objectToSelect);
}

void CreateSubmenuCommand::undo()
{
    m_menu->removeRealMenu(m_action);
    cheapUpdate();
    selectUnmanagedObject(m_menu);
}

// ---- DeleteToolBarCommand ----
DeleteToolBarCommand::DeleteToolBarCommand(QDesignerFormWindowInterface *formWindow)
    : QDesignerFormWindowCommand(QApplication::translate("Command", "Delete Tool Bar"), formWindow)
{
}

void DeleteToolBarCommand::init(QToolBar *toolBar)
{
    m_toolBar = toolBar;
    m_mainWindow = qobject_cast<QMainWindow*>(toolBar->parentWidget());
}

void DeleteToolBarCommand::redo()
{
    if (m_mainWindow) {
        QDesignerContainerExtension *c = qt_extension<QDesignerContainerExtension*>(core()->extensionManager(), m_mainWindow);
        Q_ASSERT(c != nullptr);
        for (int i=0; i<c->count(); ++i) {
            if (c->widget(i) == m_toolBar) {
                c->remove(i);
                break;
            }
        }
    }

    core()->metaDataBase()->remove(m_toolBar);
    m_toolBar->hide();
    m_toolBar->setParent(formWindow());
    formWindow()->emitSelectionChanged();
}

void DeleteToolBarCommand::undo()
{
    if (m_mainWindow) {
        m_toolBar->setParent(m_mainWindow);
        QDesignerContainerExtension *c = qt_extension<QDesignerContainerExtension*>(core()->extensionManager(), m_mainWindow);

        c->addWidget(m_toolBar);

        core()->metaDataBase()->add(m_toolBar);
        m_toolBar->show();
        formWindow()->emitSelectionChanged();
    }
}

} // namespace qdesigner_internal

QT_END_NAMESPACE
