mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-11-04 00:56:39 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			413 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			413 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
 | 
						|
#include "table.hpp"
 | 
						|
 | 
						|
#include <QHeaderView>
 | 
						|
 | 
						|
#include <QAction>
 | 
						|
#include <QMenu>
 | 
						|
#include <QContextMenuEvent>
 | 
						|
 | 
						|
#include "../../model/world/data.hpp"
 | 
						|
#include "../../model/world/commands.hpp"
 | 
						|
#include "../../model/world/idtableproxymodel.hpp"
 | 
						|
#include "../../model/world/idtable.hpp"
 | 
						|
#include "../../model/world/record.hpp"
 | 
						|
#include "../../model/world/columns.hpp"
 | 
						|
 | 
						|
#include "recordstatusdelegate.hpp"
 | 
						|
#include "util.hpp"
 | 
						|
 | 
						|
void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
 | 
						|
{
 | 
						|
    QModelIndexList selectedRows = selectionModel()->selectedRows();
 | 
						|
 | 
						|
    QMenu menu (this);
 | 
						|
 | 
						|
    ///  \todo add menu items for select all and clear selection
 | 
						|
 | 
						|
    if (!mEditLock)
 | 
						|
    {
 | 
						|
        if (selectedRows.size()==1)
 | 
						|
            menu.addAction (mEditAction);
 | 
						|
 | 
						|
        if (mCreateAction)
 | 
						|
            menu.addAction (mCreateAction);
 | 
						|
 | 
						|
        /// \todo Reverting temporarily disabled on tables that support reordering, because
 | 
						|
        /// revert logic currently can not handle reordering.
 | 
						|
        if (mModel->getReordering()==CSMWorld::IdTable::Reordering_None)
 | 
						|
            if (listRevertableSelectedIds().size()>0)
 | 
						|
                menu.addAction (mRevertAction);
 | 
						|
 | 
						|
        if (listDeletableSelectedIds().size()>0)
 | 
						|
            menu.addAction (mDeleteAction);
 | 
						|
 | 
						|
        if (mModel->getReordering()==CSMWorld::IdTable::Reordering_WithinTopic)
 | 
						|
        {
 | 
						|
            /// \todo allow reordering of multiple rows
 | 
						|
            if (selectedRows.size()==1)
 | 
						|
            {
 | 
						|
                int row =selectedRows.begin()->row();
 | 
						|
 | 
						|
                int column = mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_Topic);
 | 
						|
 | 
						|
                if (column==-1)
 | 
						|
                    column = mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_Journal);
 | 
						|
 | 
						|
                if (column!=-1)
 | 
						|
                {
 | 
						|
                    if (row>0 && mProxyModel->data (mProxyModel->index (row, column))==
 | 
						|
                        mProxyModel->data (mProxyModel->index (row-1, column)))
 | 
						|
                    {
 | 
						|
                        menu.addAction (mMoveUpAction);
 | 
						|
                    }
 | 
						|
 | 
						|
                    if (row<mProxyModel->rowCount()-1 && mProxyModel->data (mProxyModel->index (row, column))==
 | 
						|
                        mProxyModel->data (mProxyModel->index (row+1, column)))
 | 
						|
                    {
 | 
						|
                        menu.addAction (mMoveDownAction);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    menu.exec (event->globalPos());
 | 
						|
}
 | 
						|
 | 
						|
std::vector<std::string> CSVWorld::Table::listRevertableSelectedIds() const
 | 
						|
{
 | 
						|
    std::vector<std::string> revertableIds;
 | 
						|
 | 
						|
    if (mProxyModel->columnCount()>0)
 | 
						|
    {
 | 
						|
        QModelIndexList selectedRows = selectionModel()->selectedRows();
 | 
						|
 | 
						|
        for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end();
 | 
						|
             ++iter)
 | 
						|
        {
 | 
						|
            QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0));
 | 
						|
 | 
						|
            CSMWorld::RecordBase::State state =
 | 
						|
                static_cast<CSMWorld::RecordBase::State> (
 | 
						|
                mModel->data (mModel->index (index.row(), 1)).toInt());
 | 
						|
 | 
						|
            if (state!=CSMWorld::RecordBase::State_BaseOnly)
 | 
						|
            {
 | 
						|
                int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id);
 | 
						|
 | 
						|
                std::string id = mModel->data (mModel->index (index.row(), columnIndex)).
 | 
						|
                    toString().toUtf8().constData();
 | 
						|
 | 
						|
                revertableIds.push_back (id);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return revertableIds;
 | 
						|
}
 | 
						|
 | 
						|
std::vector<std::string> CSVWorld::Table::listDeletableSelectedIds() const
 | 
						|
{
 | 
						|
    std::vector<std::string> deletableIds;
 | 
						|
 | 
						|
    if (mProxyModel->columnCount()>0)
 | 
						|
    {
 | 
						|
        QModelIndexList selectedRows = selectionModel()->selectedRows();
 | 
						|
 | 
						|
        for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end();
 | 
						|
            ++iter)
 | 
						|
        {
 | 
						|
            QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0));
 | 
						|
 | 
						|
            // check record state
 | 
						|
            CSMWorld::RecordBase::State state =
 | 
						|
                static_cast<CSMWorld::RecordBase::State> (
 | 
						|
                mModel->data (mModel->index (index.row(), 1)).toInt());
 | 
						|
 | 
						|
            if (state==CSMWorld::RecordBase::State_Deleted)
 | 
						|
                continue;
 | 
						|
 | 
						|
            // check other columns (only relevant for a subset of the tables)
 | 
						|
            int dialogueTypeIndex =
 | 
						|
                mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_DialogueType);
 | 
						|
 | 
						|
            if (dialogueTypeIndex!=-1)
 | 
						|
            {
 | 
						|
                int type = mModel->data (mModel->index (index.row(), dialogueTypeIndex)).toInt();
 | 
						|
 | 
						|
                if (type!=ESM::Dialogue::Topic && type!=ESM::Dialogue::Journal)
 | 
						|
                    continue;
 | 
						|
            }
 | 
						|
 | 
						|
            // add the id to the collection
 | 
						|
            int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id);
 | 
						|
 | 
						|
            std::string id = mModel->data (mModel->index (index.row(), columnIndex)).
 | 
						|
                toString().toUtf8().constData();
 | 
						|
 | 
						|
            deletableIds.push_back (id);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return deletableIds;
 | 
						|
}
 | 
						|
 | 
						|
CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack,
 | 
						|
    bool createAndDelete, bool sorting)
 | 
						|
    : mUndoStack (undoStack), mCreateAction (0), mEditLock (false), mRecordStatusDisplay (0)
 | 
						|
{
 | 
						|
    mModel = &dynamic_cast<CSMWorld::IdTable&> (*data.getTableModel (id));
 | 
						|
 | 
						|
    mProxyModel = new CSMWorld::IdTableProxyModel (this);
 | 
						|
    mProxyModel->setSourceModel (mModel);
 | 
						|
 | 
						|
    setModel (mProxyModel);
 | 
						|
    horizontalHeader()->setResizeMode (QHeaderView::Interactive);
 | 
						|
    verticalHeader()->hide();
 | 
						|
    setSortingEnabled (sorting);
 | 
						|
    setSelectionBehavior (QAbstractItemView::SelectRows);
 | 
						|
    setSelectionMode (QAbstractItemView::ExtendedSelection);
 | 
						|
 | 
						|
    int columns = mModel->columnCount();
 | 
						|
 | 
						|
    for (int i=0; i<columns; ++i)
 | 
						|
    {
 | 
						|
        int flags = mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt();
 | 
						|
 | 
						|
        if (flags & CSMWorld::ColumnBase::Flag_Table)
 | 
						|
        {
 | 
						|
            CSMWorld::ColumnBase::Display display = static_cast<CSMWorld::ColumnBase::Display> (
 | 
						|
                mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt());
 | 
						|
 | 
						|
            CommandDelegate *delegate = CommandDelegateFactoryCollection::get().makeDelegate (display,
 | 
						|
                undoStack, this);
 | 
						|
 | 
						|
            mDelegates.push_back (delegate);
 | 
						|
            setItemDelegateForColumn (i, delegate);
 | 
						|
        }
 | 
						|
        else
 | 
						|
            hideColumn (i);
 | 
						|
    }
 | 
						|
 | 
						|
    mEditAction = new QAction (tr ("Edit Record"), this);
 | 
						|
    connect (mEditAction, SIGNAL (triggered()), this, SLOT (editRecord()));
 | 
						|
    addAction (mEditAction);
 | 
						|
 | 
						|
    if (createAndDelete)
 | 
						|
    {
 | 
						|
        mCreateAction = new QAction (tr ("Add Record"), this);
 | 
						|
        connect (mCreateAction, SIGNAL (triggered()), this, SIGNAL (createRequest()));
 | 
						|
        addAction (mCreateAction);
 | 
						|
    }
 | 
						|
 | 
						|
    mRevertAction = new QAction (tr ("Revert Record"), this);
 | 
						|
    connect (mRevertAction, SIGNAL (triggered()), this, SLOT (revertRecord()));
 | 
						|
    addAction (mRevertAction);
 | 
						|
 | 
						|
    mDeleteAction = new QAction (tr ("Delete Record"), this);
 | 
						|
    connect (mDeleteAction, SIGNAL (triggered()), this, SLOT (deleteRecord()));
 | 
						|
    addAction (mDeleteAction);
 | 
						|
 | 
						|
    mMoveUpAction = new QAction (tr ("Move Up"), this);
 | 
						|
    connect (mMoveUpAction, SIGNAL (triggered()), this, SLOT (moveUpRecord()));
 | 
						|
    addAction (mMoveUpAction);
 | 
						|
 | 
						|
    mMoveDownAction = new QAction (tr ("Move Down"), this);
 | 
						|
    connect (mMoveDownAction, SIGNAL (triggered()), this, SLOT (moveDownRecord()));
 | 
						|
    addAction (mMoveDownAction);
 | 
						|
 | 
						|
    connect (mProxyModel, SIGNAL (rowsInserted (const QModelIndex&, int, int)),
 | 
						|
        this, SLOT (tableSizeUpdate()));
 | 
						|
 | 
						|
    /// \note This signal could instead be connected to a slot that filters out changes not affecting
 | 
						|
    /// the records status column (for permanence reasons)
 | 
						|
    connect (mProxyModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
 | 
						|
        this, SLOT (tableSizeUpdate()));
 | 
						|
 | 
						|
    connect (selectionModel(), SIGNAL (selectionChanged (const QItemSelection&, const QItemSelection&)),
 | 
						|
        this, SLOT (selectionSizeUpdate ()));
 | 
						|
}
 | 
						|
 | 
						|
void CSVWorld::Table::setEditLock (bool locked)
 | 
						|
{
 | 
						|
    for (std::vector<CommandDelegate *>::iterator iter (mDelegates.begin()); iter!=mDelegates.end(); ++iter)
 | 
						|
        (*iter)->setEditLock (locked);
 | 
						|
 | 
						|
    mEditLock = locked;
 | 
						|
}
 | 
						|
 | 
						|
CSMWorld::UniversalId CSVWorld::Table::getUniversalId (int row) const
 | 
						|
{
 | 
						|
    return CSMWorld::UniversalId (
 | 
						|
        static_cast<CSMWorld::UniversalId::Type> (mProxyModel->data (mProxyModel->index (row, 2)).toInt()),
 | 
						|
        mProxyModel->data (mProxyModel->index (row, 0)).toString().toStdString());
 | 
						|
}
 | 
						|
 | 
						|
void CSVWorld::Table::revertRecord()
 | 
						|
{
 | 
						|
    if (!mEditLock)
 | 
						|
    {
 | 
						|
        std::vector<std::string> revertableIds = listRevertableSelectedIds();
 | 
						|
 | 
						|
        if (revertableIds.size()>0)
 | 
						|
        {
 | 
						|
            if (revertableIds.size()>1)
 | 
						|
                mUndoStack.beginMacro (tr ("Revert multiple records"));
 | 
						|
 | 
						|
            for (std::vector<std::string>::const_iterator iter (revertableIds.begin()); iter!=revertableIds.end(); ++iter)
 | 
						|
                mUndoStack.push (new CSMWorld::RevertCommand (*mModel, *iter));
 | 
						|
 | 
						|
            if (revertableIds.size()>1)
 | 
						|
                mUndoStack.endMacro();
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void CSVWorld::Table::deleteRecord()
 | 
						|
{
 | 
						|
    if (!mEditLock)
 | 
						|
    {
 | 
						|
        std::vector<std::string> deletableIds = listDeletableSelectedIds();
 | 
						|
 | 
						|
        if (deletableIds.size()>0)
 | 
						|
        {
 | 
						|
            if (deletableIds.size()>1)
 | 
						|
                mUndoStack.beginMacro (tr ("Delete multiple records"));
 | 
						|
 | 
						|
            for (std::vector<std::string>::const_iterator iter (deletableIds.begin()); iter!=deletableIds.end(); ++iter)
 | 
						|
                mUndoStack.push (new CSMWorld::DeleteCommand (*mModel, *iter));
 | 
						|
 | 
						|
            if (deletableIds.size()>1)
 | 
						|
                mUndoStack.endMacro();
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void CSVWorld::Table::editRecord()
 | 
						|
{
 | 
						|
    if (!mEditLock)
 | 
						|
    {
 | 
						|
        QModelIndexList selectedRows = selectionModel()->selectedRows();
 | 
						|
 | 
						|
        if (selectedRows.size()==1)
 | 
						|
            emit editRequest (selectedRows.begin()->row());
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void CSVWorld::Table::moveUpRecord()
 | 
						|
{
 | 
						|
    QModelIndexList selectedRows = selectionModel()->selectedRows();
 | 
						|
 | 
						|
    if (selectedRows.size()==1)
 | 
						|
    {
 | 
						|
        int row2 =selectedRows.begin()->row();
 | 
						|
 | 
						|
        if (row2>0)
 | 
						|
        {
 | 
						|
            int row = row2-1;
 | 
						|
 | 
						|
            row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row();
 | 
						|
            row2 = mProxyModel->mapToSource (mProxyModel->index (row2, 0)).row();
 | 
						|
 | 
						|
            if (row2<=row)
 | 
						|
                throw std::runtime_error ("Inconsistent row order");
 | 
						|
 | 
						|
            std::vector<int> newOrder (row2-row+1);
 | 
						|
            newOrder[0] = row2-row;
 | 
						|
            newOrder[row2-row] = 0;
 | 
						|
            for (int i=1; i<row2-row; ++i)
 | 
						|
                newOrder[i] = i;
 | 
						|
 | 
						|
            mUndoStack.push (new CSMWorld::ReorderRowsCommand (*mModel, row, newOrder));
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void CSVWorld::Table::moveDownRecord()
 | 
						|
{
 | 
						|
    QModelIndexList selectedRows = selectionModel()->selectedRows();
 | 
						|
 | 
						|
    if (selectedRows.size()==1)
 | 
						|
    {
 | 
						|
        int row =selectedRows.begin()->row();
 | 
						|
 | 
						|
        if (row<mProxyModel->rowCount()-1)
 | 
						|
        {
 | 
						|
            int row2 = row+1;
 | 
						|
 | 
						|
            row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row();
 | 
						|
            row2 = mProxyModel->mapToSource (mProxyModel->index (row2, 0)).row();
 | 
						|
 | 
						|
            if (row2<=row)
 | 
						|
                throw std::runtime_error ("Inconsistent row order");
 | 
						|
 | 
						|
            std::vector<int> newOrder (row2-row+1);
 | 
						|
            newOrder[0] = row2-row;
 | 
						|
            newOrder[row2-row] = 0;
 | 
						|
            for (int i=1; i<row2-row; ++i)
 | 
						|
                newOrder[i] = i;
 | 
						|
 | 
						|
            mUndoStack.push (new CSMWorld::ReorderRowsCommand (*mModel, row, newOrder));
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void CSVWorld::Table::updateEditorSetting (const QString &settingName, const QString &settingValue)
 | 
						|
{
 | 
						|
    int columns = mModel->columnCount();
 | 
						|
 | 
						|
    for (int i=0; i<columns; ++i)
 | 
						|
        if (QAbstractItemDelegate *delegate = itemDelegateForColumn (i))
 | 
						|
            if (dynamic_cast<CommandDelegate&> (*delegate).
 | 
						|
                updateEditorSetting (settingName, settingValue))
 | 
						|
                emit dataChanged (mModel->index (0, i), mModel->index (mModel->rowCount()-1, i));
 | 
						|
}
 | 
						|
 | 
						|
void CSVWorld::Table::tableSizeUpdate()
 | 
						|
{
 | 
						|
    int size = 0;
 | 
						|
    int deleted = 0;
 | 
						|
    int modified = 0;
 | 
						|
 | 
						|
    if (mProxyModel->columnCount()>0)
 | 
						|
    {
 | 
						|
        int rows = mProxyModel->rowCount();
 | 
						|
 | 
						|
        for (int i=0; i<rows; ++i)
 | 
						|
        {
 | 
						|
            QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (i, 0));
 | 
						|
 | 
						|
            int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Modification);
 | 
						|
            int state = mModel->data (mModel->index (index.row(), columnIndex)).toInt();
 | 
						|
 | 
						|
            switch (state)
 | 
						|
            {
 | 
						|
                case CSMWorld::RecordBase::State_BaseOnly: ++size; break;
 | 
						|
                case CSMWorld::RecordBase::State_Modified: ++size; ++modified; break;
 | 
						|
                case CSMWorld::RecordBase::State_ModifiedOnly: ++size; ++modified; break;
 | 
						|
                case CSMWorld::RecordBase:: State_Deleted: ++deleted; ++modified; break;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    tableSizeChanged (size, deleted, modified);
 | 
						|
}
 | 
						|
 | 
						|
void CSVWorld::Table::selectionSizeUpdate()
 | 
						|
{
 | 
						|
    selectionSizeChanged (selectionModel()->selectedRows().size());
 | 
						|
}
 | 
						|
 | 
						|
void CSVWorld::Table::requestFocus (const std::string& id)
 | 
						|
{
 | 
						|
    QModelIndex index = mProxyModel->getModelIndex (id, 0);
 | 
						|
 | 
						|
    if (index.isValid())
 | 
						|
        scrollTo (index, QAbstractItemView::PositionAtTop);
 | 
						|
}
 | 
						|
 | 
						|
void CSVWorld::Table::recordFilterChanged (boost::shared_ptr<CSMFilter::Node> filter)
 | 
						|
{
 | 
						|
    mProxyModel->setFilter (filter);
 | 
						|
}
 |