diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index b8abbc1e6..308c42914 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -18,7 +18,7 @@ opencs_hdrs_noqt (model/doc opencs_units (model/world - idtable idtableproxymodel regionmap data commanddispatcher idtablebase resourcetable nestedtableproxymodel idtree + idtable idtableproxymodel regionmap data commanddispatcher idtablebase resourcetable nestedtableproxymodel idtree infotableproxymodel ) diff --git a/apps/opencs/model/world/infotableproxymodel.cpp b/apps/opencs/model/world/infotableproxymodel.cpp new file mode 100644 index 000000000..7522e492b --- /dev/null +++ b/apps/opencs/model/world/infotableproxymodel.cpp @@ -0,0 +1,58 @@ +#include "infotableproxymodel.hpp" + +#include "idtablebase.hpp" +#include "columns.hpp" + +CSMWorld::InfoTableProxyModel::InfoTableProxyModel(CSMWorld::UniversalId::Type type, QObject *parent) + : IdTableProxyModel(parent), + mType(type), + mSourceModel(NULL) +{ + Q_ASSERT(type == UniversalId::Type_TopicInfos || type == UniversalId::Type_JournalInfos); +} + +void CSMWorld::InfoTableProxyModel::setSourceModel(QAbstractItemModel *sourceModel) +{ + IdTableProxyModel::setSourceModel(sourceModel); + mSourceModel = dynamic_cast(sourceModel); + connect(mSourceModel, + SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), + this, + SLOT(modelDataChanged(const QModelIndex &, const QModelIndex &))); + mFirstRowCache.clear(); +} + +bool CSMWorld::InfoTableProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const +{ + QModelIndex first = mSourceModel->index(getFirstInfoRow(left.row()), left.column()); + QModelIndex second = mSourceModel->index(getFirstInfoRow(right.row()), right.column()); + return IdTableProxyModel::lessThan(first, second); +} + +int CSMWorld::InfoTableProxyModel::getFirstInfoRow(int currentRow) const +{ + Columns::ColumnId columnId = Columns::ColumnId_Topic; + if (mType == UniversalId::Type_JournalInfos) + { + columnId = Columns::ColumnId_Journal; + } + + int column = mSourceModel->findColumnIndex(columnId); + QString info = mSourceModel->data(mSourceModel->index(currentRow, column)).toString(); + + if (mFirstRowCache.contains(info)) + { + return mFirstRowCache[info]; + } + + while (--currentRow >= 0 && + mSourceModel->data(mSourceModel->index(currentRow, column)) == info); + + mFirstRowCache[info] = currentRow + 1; + return currentRow + 1; +} + +void CSMWorld::InfoTableProxyModel::modelDataChanged(const QModelIndex &/*topLeft*/, const QModelIndex &/*bottomRight*/) +{ + mFirstRowCache.clear(); +} diff --git a/apps/opencs/model/world/infotableproxymodel.hpp b/apps/opencs/model/world/infotableproxymodel.hpp new file mode 100644 index 000000000..322831626 --- /dev/null +++ b/apps/opencs/model/world/infotableproxymodel.hpp @@ -0,0 +1,38 @@ +#ifndef CSM_WORLD_INFOTABLEPROXYMODEL_HPP +#define CSM_WORLD_INFOTABLEPROXYMODEL_HPP + +#include + +#include "idtableproxymodel.hpp" +#include "universalid.hpp" + +namespace CSMWorld +{ + class IdTableBase; + + class InfoTableProxyModel : public IdTableProxyModel + { + Q_OBJECT + + UniversalId::Type mType; + IdTableBase *mSourceModel; + + mutable QHash mFirstRowCache; + + int getFirstInfoRow(int currentRow) const; + ///< Finds the first row with the same topic (journal entry) as in \a currentRow + + public: + InfoTableProxyModel(UniversalId::Type type, QObject *parent = 0); + + void setSourceModel(QAbstractItemModel *sourceModel); + + protected: + virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const; + + private slots: + void modelDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); + }; +} + +#endif diff --git a/apps/opencs/view/world/datadisplaydelegate.cpp b/apps/opencs/view/world/datadisplaydelegate.cpp index b9df52bf7..72f45a18c 100644 --- a/apps/opencs/view/world/datadisplaydelegate.cpp +++ b/apps/opencs/view/world/datadisplaydelegate.cpp @@ -12,11 +12,10 @@ CSVWorld::DataDisplayDelegate::DataDisplayDelegate(const ValueList &values, const QString &settingName, QObject *parent) : EnumDelegate (values, dispatcher, document, parent), mDisplayMode (Mode_TextOnly), - mIcons (icons), mIconSize (QSize(16, 16)), mIconLeftOffset(3), + mIcons (icons), mIconSize (QSize(16, 16)), + mHorizontalMargin(QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1), mTextLeftOffset(8), mSettingKey (pageName + '/' + settingName) { - mTextAlignment.setAlignment (Qt::AlignLeft | Qt::AlignVCenter ); - buildPixmaps(); QString value = @@ -45,16 +44,35 @@ void CSVWorld::DataDisplayDelegate::setIconSize(const QSize& size) buildPixmaps(); } -void CSVWorld::DataDisplayDelegate::setIconLeftOffset(int offset) -{ - mIconLeftOffset = offset; -} - void CSVWorld::DataDisplayDelegate::setTextLeftOffset(int offset) { mTextLeftOffset = offset; } +QSize CSVWorld::DataDisplayDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QSize size = EnumDelegate::sizeHint(option, index); + + int valueIndex = getValueIndex(index); + if (valueIndex != -1) + { + if (mDisplayMode == Mode_IconOnly) + { + size.setWidth(mIconSize.width() + 2 * mHorizontalMargin); + } + else if (mDisplayMode == Mode_IconAndText) + { + size.setWidth(size.width() + mIconSize.width() + mTextLeftOffset); + } + + if (mDisplayMode != Mode_TextOnly) + { + size.setHeight(qMax(size.height(), mIconSize.height())); + } + } + return size; +} + void CSVWorld::DataDisplayDelegate::paint (QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { painter->save(); @@ -64,16 +82,11 @@ void CSVWorld::DataDisplayDelegate::paint (QPainter *painter, const QStyleOption EnumDelegate::paint(painter, option, index); else { - unsigned int i = 0; - - for (; i < mValues.size(); ++i) + int valueIndex = getValueIndex(index); + if (valueIndex != -1) { - if (mValues.at(i).first == index.data().toInt()) - break; + paintIcon(painter, option, valueIndex); } - - if (i < mValues.size() ) - paintIcon (painter, option, i); } painter->restore(); @@ -81,24 +94,28 @@ void CSVWorld::DataDisplayDelegate::paint (QPainter *painter, const QStyleOption void CSVWorld::DataDisplayDelegate::paintIcon (QPainter *painter, const QStyleOptionViewItem &option, int index) const { - //function-level statics QRect iconRect = option.rect; QRect textRect = iconRect; - const QString &text = mValues.at(index).second; - - iconRect.setSize (mIconSize); - iconRect.translate(mIconLeftOffset, (option.rect.height() - iconRect.height())/2); - - if (mDisplayMode == Mode_IconAndText ) + iconRect.setLeft(iconRect.left() + mHorizontalMargin); + iconRect.setRight(option.rect.right() - mHorizontalMargin); + if (mDisplayMode == Mode_IconAndText) { - textRect.translate (iconRect.width() + mTextLeftOffset, 0 ); - painter->drawText (textRect, text, mTextAlignment); - } - else - iconRect.translate( (option.rect.width() - iconRect.width()) / 2, 0); + iconRect.setWidth(mIconSize.width()); + textRect.setLeft(iconRect.right() + mTextLeftOffset); + textRect.setRight(option.rect.right() - mHorizontalMargin); - painter->drawPixmap (iconRect, mPixmaps.at(index).second); + QString text = option.fontMetrics.elidedText(mValues.at(index).second, + option.textElideMode, + textRect.width()); + QApplication::style()->drawItemText(painter, + textRect, + Qt::AlignLeft | Qt::AlignVCenter, + option.palette, + true, + text); + } + QApplication::style()->drawItemPixmap(painter, iconRect, Qt::AlignCenter, mPixmaps.at(index).second); } void CSVWorld::DataDisplayDelegate::updateUserSetting (const QString &name, diff --git a/apps/opencs/view/world/datadisplaydelegate.hpp b/apps/opencs/view/world/datadisplaydelegate.hpp index f6e4c2688..e565a3469 100755 --- a/apps/opencs/view/world/datadisplaydelegate.hpp +++ b/apps/opencs/view/world/datadisplaydelegate.hpp @@ -30,9 +30,8 @@ namespace CSVWorld private: std::vector > mPixmaps; - QTextOption mTextAlignment; QSize mIconSize; - int mIconLeftOffset; + int mHorizontalMargin; int mTextLeftOffset; QString mSettingKey; @@ -46,12 +45,11 @@ namespace CSVWorld virtual void paint (QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; + virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; + /// pass a QSize defining height / width of icon. Default is QSize (16,16). void setIconSize (const QSize& icon); - /// offset the horizontal position of the icon from the left edge of the cell. Default is 3 pixels. - void setIconLeftOffset (int offset); - /// offset the horizontal position of the text from the right edge of the icon. Default is 8 pixels. void setTextLeftOffset (int offset); diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 66e8fcb7a..fa3a3e27a 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -469,8 +469,7 @@ void CSVWorld::EditWidget::remake(int row) mTable->data (mTable->index (row, idColumn)).toString().toUtf8().constData()); NestedTable* table = new NestedTable(mDocument, id, mNestedModels.back(), this); - // FIXME: does not work well when enum delegates are used - //table->resizeColumnsToContents(); + table->resizeColumnsToContents(); if(mTable->index(row, i).data().type() == QVariant::UserType) { diff --git a/apps/opencs/view/world/enumdelegate.cpp b/apps/opencs/view/world/enumdelegate.cpp index 4b76bf9d6..2190a62c6 100644 --- a/apps/opencs/view/world/enumdelegate.cpp +++ b/apps/opencs/view/world/enumdelegate.cpp @@ -10,6 +10,24 @@ #include "../../model/world/commands.hpp" +int CSVWorld::EnumDelegate::getValueIndex(const QModelIndex &index, int role) const +{ + if (index.isValid() && index.data(role).isValid()) + { + int value = index.data(role).toInt(); + + int size = static_cast(mValues.size()); + for (int i = 0; i < size; ++i) + { + if (value == mValues.at(i).first) + { + return i; + } + } + } + return -1; +} + void CSVWorld::EnumDelegate::setModelDataImp (QWidget *editor, QAbstractItemModel *model, const QModelIndex& index) const { @@ -67,54 +85,59 @@ QWidget *CSVWorld::EnumDelegate::createEditor(QWidget *parent, const QStyleOptio void CSVWorld::EnumDelegate::setEditorData (QWidget *editor, const QModelIndex& index, bool tryDisplay) const { - if (QComboBox *comboBox = dynamic_cast (editor)) + if (QComboBox *comboBox = dynamic_cast(editor)) { - QVariant data = index.data (Qt::EditRole); - - if (tryDisplay && !data.isValid()) + int role = Qt::EditRole; + if (tryDisplay && !index.data(role).isValid()) { - data = index.data (Qt::DisplayRole); - if (!data.isValid()) + role = Qt::DisplayRole; + if (!index.data(role).isValid()) { return; } } - int value = data.toInt(); - - std::size_t size = mValues.size(); - - for (std::size_t i=0; isetCurrentIndex (i); - break; - } + int valueIndex = getValueIndex(index, role); + if (valueIndex != -1) + { + comboBox->setCurrentIndex(valueIndex); + } } } void CSVWorld::EnumDelegate::paint (QPainter *painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { - if (index.data().isValid()) + int valueIndex = getValueIndex(index); + if (valueIndex != -1) { - QStyleOptionViewItemV4 option2 (option); - - int value = index.data().toInt(); - - for (std::vector >::const_iterator iter (mValues.begin()); - iter!=mValues.end(); ++iter) - if (iter->first==value) - { - option2.text = iter->second; - - QApplication::style()->drawControl (QStyle::CE_ItemViewItem, &option2, painter); - - break; - } + QStyleOptionViewItemV4 itemOption(option); + itemOption.text = mValues.at(valueIndex).second; + QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &itemOption, painter); } } +QSize CSVWorld::EnumDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + int valueIndex = getValueIndex(index); + if (valueIndex != -1) + { + // Calculate the size hint as for a combobox. + // So, the whole text is visible (isn't elided) when the editor is created + QStyleOptionComboBox itemOption; + itemOption.fontMetrics = option.fontMetrics; + itemOption.palette = option.palette; + itemOption.rect = option.rect; + itemOption.state = option.state; + + const QString &valueText = mValues.at(valueIndex).second; + QSize valueSize = QSize(itemOption.fontMetrics.width(valueText), itemOption.fontMetrics.height()); + + itemOption.currentText = valueText; + return QApplication::style()->sizeFromContents(QStyle::CT_ComboBox, &itemOption, valueSize); + } + return option.rect.size(); +} CSVWorld::EnumDelegateFactory::EnumDelegateFactory() {} diff --git a/apps/opencs/view/world/enumdelegate.hpp b/apps/opencs/view/world/enumdelegate.hpp index 82890c791..a31945427 100644 --- a/apps/opencs/view/world/enumdelegate.hpp +++ b/apps/opencs/view/world/enumdelegate.hpp @@ -19,6 +19,8 @@ namespace CSVWorld std::vector > mValues; + int getValueIndex(const QModelIndex &index, int role = Qt::DisplayRole) const; + private: virtual void setModelDataImp (QWidget *editor, QAbstractItemModel *model, @@ -46,6 +48,8 @@ namespace CSVWorld virtual void paint (QPainter *painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; + virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; + }; class EnumDelegateFactory : public CommandDelegateFactory diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp index cd9b37a64..e81da23e1 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -68,10 +68,10 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) new CSVDoc::SubViewFactoryWithCreator); manager.add (CSMWorld::UniversalId::Type_TopicInfos, - new CSVDoc::SubViewFactoryWithCreator > (false)); + new CSVDoc::SubViewFactoryWithCreator >); manager.add (CSMWorld::UniversalId::Type_JournalInfos, - new CSVDoc::SubViewFactoryWithCreator > (false)); + new CSVDoc::SubViewFactoryWithCreator >); // Subviews for resources tables manager.add (CSMWorld::UniversalId::Type_Meshes, diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index b4efbd3c8..072b645c4 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -13,6 +13,7 @@ #include "../../model/world/data.hpp" #include "../../model/world/commands.hpp" +#include "../../model/world/infotableproxymodel.hpp" #include "../../model/world/idtableproxymodel.hpp" #include "../../model/world/idtablebase.hpp" #include "../../model/world/idtable.hpp" @@ -276,7 +277,16 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, mModel = &dynamic_cast (*mDocument.getData().getTableModel (id)); - mProxyModel = new CSMWorld::IdTableProxyModel (this); + bool isInfoTable = id.getType() == CSMWorld::UniversalId::Type_TopicInfos || + id.getType() == CSMWorld::UniversalId::Type_JournalInfos; + if (isInfoTable) + { + mProxyModel = new CSMWorld::InfoTableProxyModel(id.getType(), this); + } + else + { + mProxyModel = new CSMWorld::IdTableProxyModel (this); + } mProxyModel->setSourceModel (mModel); mDispatcher = new CSMWorld::CommandDispatcher (document, id, this); diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index 0370072a4..78a2bfd9f 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -4,8 +4,6 @@ #include "pathfinding.hpp" #include -#include "../mwworld/cellstore.hpp" - #include "obstacle.hpp" #include "aistate.hpp" @@ -16,6 +14,7 @@ namespace MWWorld namespace ESM { + struct Cell; namespace AiSequence { struct AiSequence; diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 2c569a18a..3c1a40fe0 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -111,8 +111,7 @@ namespace MWMechanics } PathFinder::PathFinder() - : mIsPathConstructed(false), - mPathgrid(NULL), + : mPathgrid(NULL), mCell(NULL) { } @@ -121,7 +120,6 @@ namespace MWMechanics { if(!mPath.empty()) mPath.clear(); - mIsPathConstructed = false; } /* @@ -177,7 +175,6 @@ namespace MWMechanics static_cast(endPoint.mX), static_cast(endPoint.mY), static_cast(endPoint.mZ))) { mPath.push_back(endPoint); - mIsPathConstructed = true; return; } } @@ -194,7 +191,6 @@ namespace MWMechanics if(!mPathgrid || mPathgrid->mPoints.empty()) { mPath.push_back(endPoint); - mIsPathConstructed = true; return; } @@ -232,7 +228,6 @@ namespace MWMechanics if(startNode == endNode.first) { mPath.push_back(endPoint); - mIsPathConstructed = true; return; } @@ -240,7 +235,6 @@ namespace MWMechanics if(!mPath.empty()) { - mIsPathConstructed = true; // Add the destination (which may be different to the closest // pathgrid point). However only add if endNode was the closest // point to endPoint. @@ -253,14 +247,8 @@ namespace MWMechanics if(endNode.second) mPath.push_back(endPoint); } - else - mIsPathConstructed = false; } - else - mIsPathConstructed = false; } - else - mIsPathConstructed = false; return; } @@ -268,7 +256,7 @@ namespace MWMechanics float PathFinder::getZAngleToNext(float x, float y) const { // This should never happen (programmers should have an if statement checking - // mIsPathConstructed that prevents this call if otherwise). + // isPathConstructed that prevents this call if otherwise). if(mPath.empty()) return 0.; @@ -290,7 +278,6 @@ namespace MWMechanics mPath.pop_front(); if(mPath.empty()) { - mIsPathConstructed = false; return true; } } diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index da668e27b..6de4db1d0 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -46,7 +46,7 @@ namespace MWMechanics bool isPathConstructed() const { - return mIsPathConstructed; + return !mPath.empty(); } int getPathSize() const @@ -93,8 +93,6 @@ namespace MWMechanics private: - bool mIsPathConstructed; - std::list mPath; const ESM::Pathgrid *mPathgrid;