tools/designer/src/components/taskmenu/treewidgeteditor.cpp
author Alex Gilkes <alex.gilkes@nokia.com>
Mon, 11 Jan 2010 14:00:40 +0000
changeset 0 1918ee327afb
child 4 3b1da2848fc7
permissions -rw-r--r--
Revision: 200952

/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Designer 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 "treewidgeteditor.h"
#include <formwindowbase_p.h>
#include <iconloader_p.h>
#include <qdesigner_command_p.h>
#include <qdesigner_utils_p.h>
#include <abstractformbuilder.h>
#include <designerpropertymanager.h>
#include <qttreepropertybrowser.h>

#include <QtDesigner/QDesignerFormWindowInterface>
#include <QtDesigner/QDesignerFormEditorInterface>
#include <QtDesigner/QDesignerIconCacheInterface>
#include <QtCore/QDir>
#include <QtCore/QQueue>
#include <QtGui/QHeaderView>
#include <QtGui/QTreeWidgetItemIterator>

QT_BEGIN_NAMESPACE

namespace qdesigner_internal {

TreeWidgetEditor::TreeWidgetEditor(QDesignerFormWindowInterface *form, QWidget *parent)
    : AbstractItemEditor(form, parent), m_updatingBrowser(false)
{
    m_columnEditor = new ItemListEditor(form, this);
    m_columnEditor->setObjectName(QLatin1String("columnEditor"));
    m_columnEditor->setNewItemText(tr("New Column"));
    ui.setupUi(this);

    injectPropertyBrowser(ui.itemsTab, ui.widget);
    connect(ui.showPropertiesButton, SIGNAL(clicked()),
            this, SLOT(togglePropertyBrowser()));
    togglePropertyBrowser();

    ui.tabWidget->insertTab(0, m_columnEditor, tr("&Columns"));
    ui.tabWidget->setCurrentIndex(0);
    setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);

    ui.newItemButton->setIcon(createIconSet(QString::fromUtf8("plus.png")));
    ui.newSubItemButton->setIcon(createIconSet(QString::fromUtf8("downplus.png")));
    ui.deleteItemButton->setIcon(createIconSet(QString::fromUtf8("minus.png")));
    ui.moveItemUpButton->setIcon(createIconSet(QString::fromUtf8("up.png")));
    ui.moveItemDownButton->setIcon(createIconSet(QString::fromUtf8("down.png")));
    ui.moveItemRightButton->setIcon(createIconSet(QString::fromUtf8("leveldown.png")));
    ui.moveItemLeftButton->setIcon(createIconSet(QString::fromUtf8("levelup.png")));

    ui.treeWidget->header()->setMovable(false);

    connect(iconCache(), SIGNAL(reloaded()), this, SLOT(cacheReloaded()));
}

static AbstractItemEditor::PropertyDefinition treeHeaderPropList[] = {
    { Qt::DisplayPropertyRole, 0, DesignerPropertyManager::designerStringTypeId, "text" },
    { Qt::DecorationPropertyRole, 0, DesignerPropertyManager::designerIconTypeId, "icon" },
    { Qt::ToolTipPropertyRole, 0, DesignerPropertyManager::designerStringTypeId, "toolTip" },
    { Qt::StatusTipPropertyRole, 0, DesignerPropertyManager::designerStringTypeId, "statusTip" },
    { Qt::WhatsThisPropertyRole, 0, DesignerPropertyManager::designerStringTypeId, "whatsThis" },
    { Qt::FontRole, QVariant::Font, 0, "font" },
    { Qt::TextAlignmentRole, 0, DesignerPropertyManager::designerAlignmentTypeId, "textAlignment" },
    { Qt::BackgroundRole, QVariant::Color, 0, "background" },
    { Qt::ForegroundRole, QVariant::Brush, 0, "foreground" },
    { 0, 0, 0, 0 }
};

static AbstractItemEditor::PropertyDefinition treeItemColumnPropList[] = {
    { Qt::DisplayPropertyRole, 0, DesignerPropertyManager::designerStringTypeId, "text" },
    { Qt::DecorationPropertyRole, 0, DesignerPropertyManager::designerIconTypeId, "icon" },
    { Qt::ToolTipPropertyRole, 0, DesignerPropertyManager::designerStringTypeId, "toolTip" },
    { Qt::StatusTipPropertyRole, 0, DesignerPropertyManager::designerStringTypeId, "statusTip" },
    { Qt::WhatsThisPropertyRole, 0, DesignerPropertyManager::designerStringTypeId, "whatsThis" },
    { Qt::FontRole, QVariant::Font, 0, "font" },
    { Qt::TextAlignmentRole, 0, DesignerPropertyManager::designerAlignmentTypeId, "textAlignment" },
    { Qt::BackgroundRole, QVariant::Brush, 0, "background" },
    { Qt::ForegroundRole, QVariant::Brush, 0, "foreground" },
    { Qt::CheckStateRole, 0, QtVariantPropertyManager::enumTypeId, "checkState" },
    { 0, 0, 0, 0 }
};

static AbstractItemEditor::PropertyDefinition treeItemCommonPropList[] = {
    { ItemFlagsShadowRole, 0, QtVariantPropertyManager::flagTypeId, "flags" },
    { 0, 0, 0, 0 }
};

QtVariantProperty *TreeWidgetEditor::setupPropertyGroup(const QString &title, PropertyDefinition *propDefs)
{
    setupProperties(propDefs);
    QtVariantProperty *groupProp = m_propertyManager->addProperty(QtVariantPropertyManager::groupTypeId(), title);
    foreach (QtVariantProperty *prop, m_rootProperties)
        groupProp->addSubProperty(prop);
    m_rootProperties.clear();
    return groupProp;
}

TreeWidgetContents TreeWidgetEditor::fillContentsFromTreeWidget(QTreeWidget *treeWidget)
{
    TreeWidgetContents treeCont;
    treeCont.fromTreeWidget(treeWidget, false);
    treeCont.applyToTreeWidget(ui.treeWidget, iconCache(), true);

    treeCont.m_headerItem.applyToListWidget(m_columnEditor->listWidget(), iconCache(), true);
    m_columnEditor->setupEditor(treeWidget, treeHeaderPropList);

    QList<QtVariantProperty*> rootProperties;
    rootProperties.append(setupPropertyGroup(tr("Per column properties"), treeItemColumnPropList));
    rootProperties.append(setupPropertyGroup(tr("Common properties"), treeItemCommonPropList));
    m_rootProperties = rootProperties;
    m_propertyBrowser->setPropertiesWithoutValueMarked(true);
    m_propertyBrowser->setRootIsDecorated(false);
    setupObject(treeWidget);

    if (ui.treeWidget->topLevelItemCount() > 0)
        ui.treeWidget->setCurrentItem(ui.treeWidget->topLevelItem(0));

    updateEditor();

    return treeCont;
}

TreeWidgetContents TreeWidgetEditor::contents() const
{
    TreeWidgetContents retVal;
    retVal.fromTreeWidget(ui.treeWidget, true);
    return retVal;
}

void TreeWidgetEditor::setItemData(int role, const QVariant &v)
{
    const int col = (role == ItemFlagsShadowRole) ? 0 : ui.treeWidget->currentColumn();
    QVariant newValue = v;
    BoolBlocker block(m_updatingBrowser);
    if (role == Qt::FontRole && newValue.type() == QVariant::Font) {
        QFont oldFont = ui.treeWidget->font();
        QFont newFont = qVariantValue<QFont>(newValue).resolve(oldFont);
        newValue = qVariantFromValue(newFont);
        ui.treeWidget->currentItem()->setData(col, role, QVariant()); // force the right font with the current resolve mask is set (item view bug)
    }
    ui.treeWidget->currentItem()->setData(col, role, newValue);
}

QVariant TreeWidgetEditor::getItemData(int role) const
{
    const int col = (role == ItemFlagsShadowRole) ? 0 : ui.treeWidget->currentColumn();
    return ui.treeWidget->currentItem()->data(col, role);
}

void TreeWidgetEditor::on_newItemButton_clicked()
{
    QTreeWidgetItem *curItem = ui.treeWidget->currentItem();
    QTreeWidgetItem *newItem = 0;
    ui.treeWidget->blockSignals(true);
    if (curItem) {
        if (curItem->parent())
            newItem = new QTreeWidgetItem(curItem->parent(), curItem);
        else
            newItem = new QTreeWidgetItem(ui.treeWidget, curItem);
    } else
        newItem = new QTreeWidgetItem(ui.treeWidget);
    const QString newItemText = tr("New Item");
    newItem->setText(0, newItemText);
    newItem->setData(0, Qt::DisplayPropertyRole, qVariantFromValue(PropertySheetStringValue(newItemText)));
    newItem->setFlags(newItem->flags() | Qt::ItemIsEditable);
    ui.treeWidget->blockSignals(false);

    ui.treeWidget->setCurrentItem(newItem, qMax(ui.treeWidget->currentColumn(), 0));
    updateEditor();
    ui.treeWidget->editItem(newItem, ui.treeWidget->currentColumn());
}

void TreeWidgetEditor::on_newSubItemButton_clicked()
{
    QTreeWidgetItem *curItem = ui.treeWidget->currentItem();
    if (!curItem)
        return;

    ui.treeWidget->blockSignals(true);
    QTreeWidgetItem *newItem = new QTreeWidgetItem(curItem);
    const QString newItemText = tr("New Subitem");
    newItem->setText(0, newItemText);
    newItem->setData(0, Qt::DisplayPropertyRole, qVariantFromValue(PropertySheetStringValue(newItemText)));
    newItem->setFlags(newItem->flags() | Qt::ItemIsEditable);
    ui.treeWidget->blockSignals(false);

    ui.treeWidget->setCurrentItem(newItem, ui.treeWidget->currentColumn());
    updateEditor();
    ui.treeWidget->editItem(newItem, ui.treeWidget->currentColumn());
}

void TreeWidgetEditor::on_deleteItemButton_clicked()
{
    QTreeWidgetItem *curItem = ui.treeWidget->currentItem();
    if (!curItem)
        return;

    QTreeWidgetItem *nextCurrent = 0;
    if (curItem->parent()) {
        int idx = curItem->parent()->indexOfChild(curItem);
        if (idx == curItem->parent()->childCount() - 1)
            idx--;
        else
            idx++;
        if (idx < 0)
            nextCurrent = curItem->parent();
        else
            nextCurrent = curItem->parent()->child(idx);
    } else {
        int idx = ui.treeWidget->indexOfTopLevelItem(curItem);
        if (idx == ui.treeWidget->topLevelItemCount() - 1)
            idx--;
        else
            idx++;
        if (idx >= 0)
            nextCurrent = ui.treeWidget->topLevelItem(idx);
    }
    closeEditors();
    ui.treeWidget->blockSignals(true);
    delete curItem;
    ui.treeWidget->blockSignals(false);

    if (nextCurrent)
        ui.treeWidget->setCurrentItem(nextCurrent, ui.treeWidget->currentColumn());
    updateEditor();
}

void TreeWidgetEditor::on_moveItemUpButton_clicked()
{
    QTreeWidgetItem *curItem = ui.treeWidget->currentItem();
    if (!curItem)
        return;

    int idx;
    if (curItem->parent())
        idx = curItem->parent()->indexOfChild(curItem);
    else
        idx = ui.treeWidget->indexOfTopLevelItem(curItem);
    if (idx == 0)
        return;

    QTreeWidgetItem *takenItem;
    ui.treeWidget->blockSignals(true);
    if (curItem->parent()) {
        QTreeWidgetItem *parentItem = curItem->parent();
        takenItem = parentItem->takeChild(idx);
        parentItem->insertChild(idx - 1, takenItem);
    } else {
        takenItem = ui.treeWidget->takeTopLevelItem(idx);
        ui.treeWidget->insertTopLevelItem(idx - 1, takenItem);
    }
    ui.treeWidget->blockSignals(false);

    ui.treeWidget->setCurrentItem(takenItem, ui.treeWidget->currentColumn());
    updateEditor();
}

void TreeWidgetEditor::on_moveItemDownButton_clicked()
{
    QTreeWidgetItem *curItem = ui.treeWidget->currentItem();
    if (!curItem)
        return;

    int idx, idxCount;
    if (curItem->parent()) {
        idx = curItem->parent()->indexOfChild(curItem);
        idxCount = curItem->parent()->childCount();
    } else {
        idx = ui.treeWidget->indexOfTopLevelItem(curItem);
        idxCount = ui.treeWidget->topLevelItemCount();
    }
    if (idx == idxCount - 1)
        return;

    QTreeWidgetItem *takenItem;
    ui.treeWidget->blockSignals(true);
    if (curItem->parent()) {
        QTreeWidgetItem *parentItem = curItem->parent();
        takenItem = parentItem->takeChild(idx);
        parentItem->insertChild(idx + 1, takenItem);
    } else {
        takenItem = ui.treeWidget->takeTopLevelItem(idx);
        ui.treeWidget->insertTopLevelItem(idx + 1, takenItem);
    }
    ui.treeWidget->blockSignals(false);

    ui.treeWidget->setCurrentItem(takenItem, ui.treeWidget->currentColumn());
    updateEditor();
}

void TreeWidgetEditor::on_moveItemLeftButton_clicked()
{
    QTreeWidgetItem *curItem = ui.treeWidget->currentItem();
    if (!curItem)
        return;

    QTreeWidgetItem *parentItem = curItem->parent();
    if (!parentItem)
        return;

    ui.treeWidget->blockSignals(true);
    QTreeWidgetItem *takenItem = parentItem->takeChild(parentItem->indexOfChild(curItem));
    if (parentItem->parent()) {
        int idx = parentItem->parent()->indexOfChild(parentItem);
        parentItem->parent()->insertChild(idx, takenItem);
    } else {
        int idx = ui.treeWidget->indexOfTopLevelItem(parentItem);
        ui.treeWidget->insertTopLevelItem(idx, takenItem);
    }
    ui.treeWidget->blockSignals(false);

    ui.treeWidget->setCurrentItem(takenItem, ui.treeWidget->currentColumn());
    updateEditor();
}

void TreeWidgetEditor::on_moveItemRightButton_clicked()
{
    QTreeWidgetItem *curItem = ui.treeWidget->currentItem();
    if (!curItem)
        return;

    int idx, idxCount;
    if (curItem->parent()) {
        idx = curItem->parent()->indexOfChild(curItem);
        idxCount = curItem->parent()->childCount();
    } else {
        idx = ui.treeWidget->indexOfTopLevelItem(curItem);
        idxCount = ui.treeWidget->topLevelItemCount();
    }
    if (idx == idxCount - 1)
        return;

    QTreeWidgetItem *takenItem;
    ui.treeWidget->blockSignals(true);
    if (curItem->parent()) {
        QTreeWidgetItem *parentItem = curItem->parent()->child(idx + 1);
        takenItem = curItem->parent()->takeChild(idx);
        parentItem->insertChild(0, takenItem);
    } else {
        QTreeWidgetItem *parentItem = ui.treeWidget->topLevelItem(idx + 1);
        takenItem = ui.treeWidget->takeTopLevelItem(idx);
        parentItem->insertChild(0, takenItem);
    }
    ui.treeWidget->blockSignals(false);

    ui.treeWidget->setCurrentItem(takenItem, ui.treeWidget->currentColumn());
    updateEditor();
}

void TreeWidgetEditor::togglePropertyBrowser()
{
    // Always hide in case parent widget is not visible -> on startup
    const bool isVisible =
            !this->isVisible() ? true : m_propertyBrowser->isVisible();
    if (isVisible)
        ui.showPropertiesButton->setText(tr("Properties &<<"));
    else
        ui.showPropertiesButton->setText(tr("Properties &>>"));

    m_propertyBrowser->setVisible(!isVisible);
}

void TreeWidgetEditor::on_treeWidget_currentItemChanged()
{
    m_columnEditor->setCurrentIndex(ui.treeWidget->currentColumn());
    updateEditor();
}

void TreeWidgetEditor::on_treeWidget_itemChanged(QTreeWidgetItem *item, int column)
{
    if (m_updatingBrowser)
        return;

    PropertySheetStringValue val = qVariantValue<PropertySheetStringValue>(item->data(column, Qt::DisplayPropertyRole));
    val.setValue(item->text(column));
    BoolBlocker block(m_updatingBrowser);
    item->setData(column, Qt::DisplayPropertyRole, qVariantFromValue(val));

    updateBrowser();
}

void TreeWidgetEditor::on_columnEditor_indexChanged(int idx)
{
    if (QTreeWidgetItem *item = ui.treeWidget->currentItem())
        ui.treeWidget->setCurrentItem(item, idx);
}

void TreeWidgetEditor::on_columnEditor_itemChanged(int idx, int role, const QVariant &v)
{
    if (role == Qt::DisplayPropertyRole)
        ui.treeWidget->headerItem()->setData(idx, Qt::EditRole, qVariantValue<PropertySheetStringValue>(v).value());
    ui.treeWidget->headerItem()->setData(idx, role, v);
}

void TreeWidgetEditor::updateEditor()
{
    QTreeWidgetItem *current = ui.treeWidget->currentItem();

    bool itemsEnabled = false;
    bool currentItemEnabled = false;
    bool moveItemUpEnabled = false;
    bool moveItemDownEnabled = false;
    bool moveItemRightEnabled = false;
    bool moveItemLeftEnabled = false;

    if (ui.treeWidget->columnCount() > 0) {
        itemsEnabled = true;
        if (current) {
            int idx;
            int idxCount;
            currentItemEnabled = true;
            if (current->parent()) {
                moveItemLeftEnabled = true;
                idx = current->parent()->indexOfChild(current);
                idxCount = current->parent()->childCount();
            } else {
                idx = ui.treeWidget->indexOfTopLevelItem(current);
                idxCount = ui.treeWidget->topLevelItemCount();
            }
            if (idx > 0)
                moveItemUpEnabled = true;
            if (idx < idxCount - 1) {
                moveItemDownEnabled = true;
                moveItemRightEnabled = true;
            }
        }
    }
    ui.tabWidget->setTabEnabled(1, itemsEnabled);
    ui.newSubItemButton->setEnabled(currentItemEnabled);
    ui.deleteItemButton->setEnabled(currentItemEnabled);

    ui.moveItemUpButton->setEnabled(moveItemUpEnabled);
    ui.moveItemDownButton->setEnabled(moveItemDownEnabled);
    ui.moveItemRightButton->setEnabled(moveItemRightEnabled);
    ui.moveItemLeftButton->setEnabled(moveItemLeftEnabled);

    if (current)
        updateBrowser();
    else
        m_propertyBrowser->clear();
}

void TreeWidgetEditor::moveColumnItems(const PropertyDefinition *propList,
        QTreeWidgetItem *item, int fromColumn, int toColumn, int step)
{
    BoolBlocker block(m_updatingBrowser);

    QList<QVariant> saveCol;
    for (int j = 0; propList[j].name; j++)
        saveCol.append(item->data(toColumn, propList[j].role));
    QVariant editVariant = item->data(toColumn, Qt::EditRole);
    QVariant toolTipVariant = item->data(toColumn, Qt::ToolTipRole);
    QVariant statusTipVariant = item->data(toColumn, Qt::StatusTipRole);
    QVariant whatsThisVariant = item->data(toColumn, Qt::WhatsThisRole);
    QVariant decorationVariant = item->data(toColumn, Qt::DecorationRole);
    for (int i = toColumn; i != fromColumn; i += step) {
        for (int j = 0; propList[j].name; j++)
            item->setData(i, propList[j].role, item->data(i + step, propList[j].role));
        item->setData(i, Qt::EditRole, item->data(i + step, Qt::EditRole));
        item->setData(i, Qt::ToolTipRole, item->data(i + step, Qt::ToolTipRole));
        item->setData(i, Qt::StatusTipRole, item->data(i + step, Qt::StatusTipRole));
        item->setData(i, Qt::WhatsThisRole, item->data(i + step, Qt::WhatsThisRole));
        item->setData(i, Qt::DecorationRole, item->data(i + step, Qt::DecorationRole));
    }
    for (int j = 0; propList[j].name; j++)
        item->setData(fromColumn, propList[j].role, saveCol[j]);
    item->setData(fromColumn, Qt::EditRole, editVariant);
    item->setData(fromColumn, Qt::ToolTipRole, toolTipVariant);
    item->setData(fromColumn, Qt::StatusTipRole, statusTipVariant);
    item->setData(fromColumn, Qt::WhatsThisRole, whatsThisVariant);
    item->setData(fromColumn, Qt::DecorationRole, decorationVariant);
}

void TreeWidgetEditor::moveColumns(int fromColumn, int toColumn, int step)
{
    ui.treeWidget->blockSignals(true);

    moveColumnItems(treeHeaderPropList, ui.treeWidget->headerItem(), fromColumn, toColumn, step);

    QQueue<QTreeWidgetItem *> pendingQueue;
    for (int i = 0; i < ui.treeWidget->topLevelItemCount(); i++)
        pendingQueue.enqueue(ui.treeWidget->topLevelItem(i));

    while (!pendingQueue.isEmpty()) {
        QTreeWidgetItem *item = pendingQueue.dequeue();
        for (int i = 0; i < item->childCount(); i++)
            pendingQueue.enqueue(item->child(i));

        moveColumnItems(treeItemColumnPropList, item, fromColumn, toColumn, step);
    }

    ui.treeWidget->blockSignals(false);
}

void TreeWidgetEditor::moveColumnsLeft(int fromColumn, int toColumn)
{
    if (fromColumn >= toColumn)
        return;

    moveColumns(fromColumn, toColumn, -1);
}

void TreeWidgetEditor::moveColumnsRight(int fromColumn, int toColumn)
{
    if (fromColumn >= toColumn)
        return;

    moveColumns(toColumn, fromColumn, 1);
}

void TreeWidgetEditor::on_columnEditor_itemInserted(int idx)
{
    int columnCount = ui.treeWidget->columnCount();
    ui.treeWidget->setColumnCount(columnCount + 1);
    ui.treeWidget->headerItem()->setText(columnCount, m_columnEditor->newItemText());
    moveColumnsLeft(idx, columnCount);

    updateEditor();
}

void TreeWidgetEditor::on_columnEditor_itemDeleted(int idx)
{
    closeEditors();

    int columnCount = ui.treeWidget->columnCount() - 1;
    if (!columnCount)
        ui.treeWidget->clear();
    else
        moveColumnsRight(idx, columnCount);
    ui.treeWidget->setColumnCount(columnCount);

    updateEditor();
}

void TreeWidgetEditor::on_columnEditor_itemMovedUp(int idx)
{
    moveColumnsRight(idx - 1, idx);

    ui.treeWidget->setCurrentItem(ui.treeWidget->currentItem(), idx - 1);
    updateEditor();
}

void TreeWidgetEditor::on_columnEditor_itemMovedDown(int idx)
{
    moveColumnsLeft(idx, idx + 1);

    ui.treeWidget->setCurrentItem(ui.treeWidget->currentItem(), idx + 1);
    updateEditor();
}

void TreeWidgetEditor::closeEditors()
{
    if (QTreeWidgetItem *cur = ui.treeWidget->currentItem() ) {
        const int numCols = cur->columnCount ();
        for (int i = 0; i < numCols; i++)
            ui.treeWidget->closePersistentEditor (cur, i);
    }
}

void TreeWidgetEditor::cacheReloaded()
{
    reloadIconResources(iconCache(), ui.treeWidget);
}

}
QT_END_NAMESPACE