diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 5386da707f..3399b9dddb 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -26,7 +26,7 @@ opencs_units_noqt (model/world universalid record commands columnbase scriptcontext cell refidcollection refidadapter refiddata refidadapterimp ref collectionbase refcollection columns infocollection tablemimedata cellcoordinates cellselection resources resourcesmanager scope pathgrid landtexture land nestedtablewrapper nestedcollection nestedcoladapterimp nestedinfocollection - idcompletionmanager + idcompletionmanager metadata ) opencs_hdrs_noqt (model/world diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index cb349d8be3..ce74fbeb96 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -2282,9 +2282,6 @@ CSMDoc::Document::Document (const Files::ConfigurationManager& configuration, if (mNew) { - mData.setDescription (""); - mData.setAuthor (""); - if (mContentFiles.size()==1) createBase(); } diff --git a/apps/opencs/model/doc/savingstages.cpp b/apps/opencs/model/doc/savingstages.cpp index d6258da6ae..f78c57ecd6 100644 --- a/apps/opencs/model/doc/savingstages.cpp +++ b/apps/opencs/model/doc/savingstages.cpp @@ -53,18 +53,16 @@ void CSMDoc::WriteHeaderStage::perform (int stage, Messages& messages) mState.getWriter().clearMaster(); - mState.getWriter().setFormat (0); - if (mSimple) { mState.getWriter().setAuthor (""); mState.getWriter().setDescription (""); mState.getWriter().setRecordCount (0); + mState.getWriter().setFormat (ESM::Header::CurrentFormat); } else { - mState.getWriter().setAuthor (mDocument.getData().getAuthor()); - mState.getWriter().setDescription (mDocument.getData().getDescription()); + mDocument.getData().getMetaData().save (mState.getWriter()); mState.getWriter().setRecordCount ( mDocument.getData().count (CSMWorld::RecordBase::State_Modified) + mDocument.getData().count (CSMWorld::RecordBase::State_ModifiedOnly) + diff --git a/apps/opencs/model/world/columnbase.cpp b/apps/opencs/model/world/columnbase.cpp index 9385038757..f209e48c66 100644 --- a/apps/opencs/model/world/columnbase.cpp +++ b/apps/opencs/model/world/columnbase.cpp @@ -100,7 +100,8 @@ bool CSMWorld::ColumnBase::isId (Display display) bool CSMWorld::ColumnBase::isText (Display display) { - return display==Display_String || display==Display_LongString; + return display==Display_String || display==Display_LongString || + display==Display_String32 || display==Display_LongString256; } bool CSMWorld::ColumnBase::isScript (Display display) diff --git a/apps/opencs/model/world/columnbase.hpp b/apps/opencs/model/world/columnbase.hpp index 789823d5cd..59f2836c21 100644 --- a/apps/opencs/model/world/columnbase.hpp +++ b/apps/opencs/model/world/columnbase.hpp @@ -123,6 +123,8 @@ namespace CSMWorld Display_InfoCondVar, Display_InfoCondComp, Display_RaceSkill, + Display_String32, + Display_LongString256, //top level columns that nest other columns Display_NestedHeader diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index a8ae5dfa12..15dd2c15b0 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -2308,6 +2308,78 @@ namespace CSMWorld return true; } }; + + template + struct FormatColumn : public Column + { + FormatColumn() + : Column (Columns::ColumnId_FileFormat, ColumnBase::Display_Integer) + {} + + virtual QVariant get (const Record& record) const + { + return record.get().mFormat; + } + + virtual bool isEditable() const + { + return false; + } + }; + + template + struct AuthorColumn : public Column + { + AuthorColumn() + : Column (Columns::ColumnId_Author, ColumnBase::Display_String32) + {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mAuthor.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mAuthor = data.toString().toUtf8().constData(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct FileDescriptionColumn : public Column + { + FileDescriptionColumn() + : Column (Columns::ColumnId_FileDescription, ColumnBase::Display_LongString256) + {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mDescription.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mDescription = data.toString().toUtf8().constData(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; } #endif diff --git a/apps/opencs/model/world/columns.cpp b/apps/opencs/model/world/columns.cpp index 9491c32469..d6e27caeb1 100644 --- a/apps/opencs/model/world/columns.cpp +++ b/apps/opencs/model/world/columns.cpp @@ -311,6 +311,10 @@ namespace CSMWorld { ColumnId_WaterLevel, "Water Level" }, { ColumnId_MapColor, "Map Color" }, + { ColumnId_FileFormat, "File Format" }, + { ColumnId_FileDescription, "File Description" }, + { ColumnId_Author, "Author" }, + { ColumnId_UseValue1, "Use value 1" }, { ColumnId_UseValue2, "Use value 2" }, { ColumnId_UseValue3, "Use value 3" }, diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index 191bbdea8c..d699c67b7e 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -302,6 +302,10 @@ namespace CSMWorld ColumnId_WaterLevel = 273, ColumnId_MapColor = 274, + ColumnId_FileFormat = 275, + ColumnId_FileDescription = 276, + ColumnId_Author = 277, + // Allocated to a separate value range, so we don't get a collision should we ever need // to extend the number of use values. ColumnId_UseValue1 = 0x10000, diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 5acd80339e..b38607748f 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -475,6 +475,14 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc mDebugProfiles.addColumn (new ScriptColumn ( ScriptColumn::Type_Lines)); + mMetaData.appendBlankRecord ("sys::meta"); + + mMetaData.addColumn (new StringIdColumn (true)); + mMetaData.addColumn (new RecordStateColumn); + mMetaData.addColumn (new FormatColumn); + mMetaData.addColumn (new AuthorColumn); + mMetaData.addColumn (new FileDescriptionColumn); + addModel (new IdTable (&mGlobals), UniversalId::Type_Global); addModel (new IdTable (&mGmsts), UniversalId::Type_Gmst); addModel (new IdTable (&mSkills), UniversalId::Type_Skill); @@ -515,6 +523,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc UniversalId::Type_Texture); addModel (new ResourceTable (&mResourcesManager.get (UniversalId::Type_Videos)), UniversalId::Type_Video); + addModel (new IdTable (&mMetaData), UniversalId::Type_MetaData); mRefLoadCache.clear(); // clear here rather than startLoading() and continueLoading() for multiple content files } @@ -803,6 +812,11 @@ const CSMWorld::Resources& CSMWorld::Data::getResources (const UniversalId& id) return mResourcesManager.get (id.getType()); } +const CSMWorld::MetaData& CSMWorld::Data::getMetaData() const +{ + return mMetaData.getRecord (0).get(); +} + QAbstractItemModel *CSMWorld::Data::getTableModel (const CSMWorld::UniversalId& id) { std::map::iterator iter = mModelIndex.find (id.getType()); @@ -847,9 +861,15 @@ int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base mBase = base; mProject = project; - mAuthor = mReader->getAuthor(); - mDescription = mReader->getDesc(); + if (!mProject && !mBase) + { + MetaData metaData; + metaData.mId = "sys::meta"; + metaData.load (*mReader); + mMetaData.setRecord (0, Record (RecordBase::State_ModifiedOnly, 0, &metaData)); + } + return mReader->getRecordCount(); } @@ -1103,26 +1123,6 @@ int CSMWorld::Data::count (RecordBase::State state) const count (state, mPathgrids); } -void CSMWorld::Data::setDescription (const std::string& description) -{ - mDescription = description; -} - -std::string CSMWorld::Data::getDescription() const -{ - return mDescription; -} - -void CSMWorld::Data::setAuthor (const std::string& author) -{ - mAuthor = author; -} - -std::string CSMWorld::Data::getAuthor() const -{ - return mAuthor; -} - std::vector CSMWorld::Data::getIds (bool listDeleted) const { std::vector ids; diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index 060e47bd95..15e39b9bab 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -44,6 +44,7 @@ #include "infocollection.hpp" #include "nestedinfocollection.hpp" #include "pathgrid.hpp" +#include "metadata.hpp" #ifndef Q_MOC_RUN #include "subcellcollection.hpp" #endif @@ -94,11 +95,10 @@ namespace CSMWorld RefIdCollection mReferenceables; RefCollection mRefs; IdCollection mFilters; + Collection mMetaData; const ResourcesManager& mResourcesManager; std::vector mModels; std::map mModelIndex; - std::string mAuthor; - std::string mDescription; ESM::ESMReader *mReader; const ESM::Dialogue *mDialogue; // last loaded dialogue bool mBase; @@ -238,6 +238,8 @@ namespace CSMWorld /// Throws an exception, if \a id does not match a resources list. const Resources& getResources (const UniversalId& id) const; + const MetaData& getMetaData() const; + QAbstractItemModel *getTableModel (const UniversalId& id); ///< If no table model is available for \a id, an exception is thrown. /// @@ -267,14 +269,6 @@ namespace CSMWorld int count (RecordBase::State state) const; ///< Return number of top-level records with the given \a state. - void setDescription (const std::string& description); - - std::string getDescription() const; - - void setAuthor (const std::string& author); - - std::string getAuthor() const; - signals: void idListChanged(); diff --git a/apps/opencs/model/world/infocollection.cpp b/apps/opencs/model/world/infocollection.cpp index a508d28f3d..560be8131e 100644 --- a/apps/opencs/model/world/infocollection.cpp +++ b/apps/opencs/model/world/infocollection.cpp @@ -97,7 +97,8 @@ bool CSMWorld::InfoCollection::reorderRows (int baseIndex, const std::vector + #include "idtablebase.hpp" #include "columns.hpp" +namespace +{ + QString toLower(const QString &str) + { + return QString::fromUtf8(Misc::StringUtils::lowerCase(str.toStdString()).c_str()); + } +} + CSMWorld::InfoTableProxyModel::InfoTableProxyModel(CSMWorld::UniversalId::Type type, QObject *parent) : IdTableProxyModel(parent), mType(type), - mSourceModel(NULL) + mSourceModel(NULL), + mInfoColumnId(type == UniversalId::Type_TopicInfos ? Columns::ColumnId_Topic : + Columns::ColumnId_Journal) { Q_ASSERT(type == UniversalId::Type_TopicInfos || type == UniversalId::Type_JournalInfos); } @@ -15,44 +27,53 @@ void CSMWorld::InfoTableProxyModel::setSourceModel(QAbstractItemModel *sourceMod { IdTableProxyModel::setSourceModel(sourceModel); mSourceModel = dynamic_cast(sourceModel); - connect(mSourceModel, - SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), - this, - SLOT(modelDataChanged(const QModelIndex &, const QModelIndex &))); - mFirstRowCache.clear(); + if (mSourceModel != NULL) + { + connect(mSourceModel, + SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, + SLOT(modelRowsChanged(const QModelIndex &, int, int))); + connect(mSourceModel, + SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, + SLOT(modelRowsChanged(const QModelIndex &, int, int))); + 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()); + + // If both indexes are belonged to the same Topic/Journal, compare their original rows only + if (first.row() == second.row()) + { + return sortOrder() == Qt::AscendingOrder ? left.row() < right.row() : right.row() < left.row(); + } 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(); + int row = currentRow; + int column = mSourceModel->findColumnIndex(mInfoColumnId); + QString info = toLower(mSourceModel->data(mSourceModel->index(row, column)).toString()); if (mFirstRowCache.contains(info)) { return mFirstRowCache[info]; } - while (--currentRow >= 0 && - mSourceModel->data(mSourceModel->index(currentRow, column)) == info); + while (--row >= 0 && + toLower(mSourceModel->data(mSourceModel->index(row, column)).toString()) == info); + ++row; - mFirstRowCache[info] = currentRow + 1; - return currentRow + 1; + mFirstRowCache[info] = row; + return row; } -void CSMWorld::InfoTableProxyModel::modelDataChanged(const QModelIndex &/*topLeft*/, const QModelIndex &/*bottomRight*/) +void CSMWorld::InfoTableProxyModel::modelRowsChanged(const QModelIndex &/*parent*/, int /*start*/, int /*end*/) { mFirstRowCache.clear(); } diff --git a/apps/opencs/model/world/infotableproxymodel.hpp b/apps/opencs/model/world/infotableproxymodel.hpp index 3228316264..28d6017b31 100644 --- a/apps/opencs/model/world/infotableproxymodel.hpp +++ b/apps/opencs/model/world/infotableproxymodel.hpp @@ -4,6 +4,7 @@ #include #include "idtableproxymodel.hpp" +#include "columns.hpp" #include "universalid.hpp" namespace CSMWorld @@ -16,6 +17,8 @@ namespace CSMWorld UniversalId::Type mType; IdTableBase *mSourceModel; + Columns::ColumnId mInfoColumnId; + ///< Contains ID for Topic or Journal ID mutable QHash mFirstRowCache; @@ -31,7 +34,7 @@ namespace CSMWorld virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const; private slots: - void modelDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); + void modelRowsChanged(const QModelIndex &parent, int start, int end); }; } diff --git a/apps/opencs/model/world/metadata.cpp b/apps/opencs/model/world/metadata.cpp new file mode 100644 index 0000000000..40b8e95197 --- /dev/null +++ b/apps/opencs/model/world/metadata.cpp @@ -0,0 +1,27 @@ + +#include "metadata.hpp" + +#include +#include +#include + +void CSMWorld::MetaData::blank() +{ + mFormat = ESM::Header::CurrentFormat; + mAuthor.clear(); + mDescription.clear(); +} + +void CSMWorld::MetaData::load (ESM::ESMReader& esm) +{ + mFormat = esm.getHeader().mFormat; + mAuthor = esm.getHeader().mData.author.toString(); + mDescription = esm.getHeader().mData.desc.toString(); +} + +void CSMWorld::MetaData::save (ESM::ESMWriter& esm) const +{ + esm.setFormat (mFormat); + esm.setAuthor (mAuthor); + esm.setDescription (mDescription); +} diff --git a/apps/opencs/model/world/metadata.hpp b/apps/opencs/model/world/metadata.hpp new file mode 100644 index 0000000000..f8df2690ec --- /dev/null +++ b/apps/opencs/model/world/metadata.hpp @@ -0,0 +1,29 @@ +#ifndef CSM_WOLRD_METADATA_H +#define CSM_WOLRD_METADATA_H + +#include + +namespace ESM +{ + class ESMReader; + class ESMWriter; +} + +namespace CSMWorld +{ + struct MetaData + { + std::string mId; + + int mFormat; + std::string mAuthor; + std::string mDescription; + + void blank(); + + void load (ESM::ESMReader& esm); + void save (ESM::ESMWriter& esm) const; + }; +} + +#endif diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp index e496fe79bb..73d893a260 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -56,6 +56,7 @@ namespace { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MagicEffects, "Magic Effects", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Pathgrids, "Pathgrids", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_StartScripts, "Start Scripts", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MetaDatas, "Meta Data Table", 0 }, { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker }; @@ -120,6 +121,7 @@ namespace { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MagicEffect, "Magic Effect", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Pathgrid, "Pathgrid", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_StartScript, "Start Script", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MetaData, "Meta Data", 0 }, { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker }; diff --git a/apps/opencs/model/world/universalid.hpp b/apps/opencs/model/world/universalid.hpp index 0a9fa38473..e9104fc226 100644 --- a/apps/opencs/model/world/universalid.hpp +++ b/apps/opencs/model/world/universalid.hpp @@ -131,6 +131,8 @@ namespace CSMWorld Type_StartScripts, Type_StartScript, Type_Search, + Type_MetaDatas, + Type_MetaData, Type_RunLog }; diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 64b066eb1e..c4abb26225 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -70,6 +70,10 @@ void CSVDoc::View::setupFileMenu() connect (loadErrors, SIGNAL (triggered()), this, SLOT (loadErrorLog())); file->addAction (loadErrors); + QAction *meta = new QAction (tr ("Meta Data"), this); + connect (meta, SIGNAL (triggered()), this, SLOT (addMetaDataSubView())); + file->addAction (meta); + QAction *close = new QAction (tr ("&Close"), this); connect (close, SIGNAL (triggered()), this, SLOT (close())); file->addAction(close); @@ -813,6 +817,11 @@ void CSVDoc::View::addSearchSubView() addSubView (mDocument->newSearch()); } +void CSVDoc::View::addMetaDataSubView() +{ + addSubView (CSMWorld::UniversalId (CSMWorld::UniversalId::Type_MetaData, "sys::meta")); +} + void CSVDoc::View::abortOperation (int type) { mDocument->abortOperation (type); diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index 814dabc6b5..7f4255f8fc 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -224,6 +224,8 @@ namespace CSVDoc void addSearchSubView(); + void addMetaDataSubView(); + void toggleShowStatusBar (bool show); void loadErrorLog(); diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 284c5928ba..99650834ef 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -64,16 +64,24 @@ void CSVWorld::NotEditableSubDelegate::setEditorData (QWidget* editor, const QMo } } + CSMWorld::Columns::ColumnId columnId = static_cast ( + mTable->getColumnId (index.column())); + if (QVariant::String == v.type()) { label->setText(v.toString()); } - else //else we are facing enums + else if (CSMWorld::Columns::hasEnums (columnId)) { int data = v.toInt(); - std::vector enumNames (CSMWorld::Columns::getEnums (static_cast (mTable->getColumnId (index.column())))); + std::vector enumNames (CSMWorld::Columns::getEnums (columnId)); + label->setText(QString::fromUtf8(enumNames.at(data).c_str())); } + else + { + label->setText (v.toString()); + } } void CSVWorld::NotEditableSubDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const @@ -548,12 +556,38 @@ void CSVWorld::EditWidget::remake(int row) this->setWidgetResizable(true); } -/* -==============================DialogueSubView========================================== -*/ -CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, - const CreatorFactoryBase& creatorFactory, bool sorting) : +QVBoxLayout& CSVWorld::SimpleDialogueSubView::getMainLayout() +{ + return *mMainLayout; +} + +CSMWorld::IdTable& CSVWorld::SimpleDialogueSubView::getTable() +{ + return *mTable; +} + +CSMWorld::CommandDispatcher& CSVWorld::SimpleDialogueSubView::getCommandDispatcher() +{ + return mCommandDispatcher; +} + +std::string CSVWorld::SimpleDialogueSubView::getCurrentId() const +{ + return mCurrentId; +} + +CSVWorld::EditWidget& CSVWorld::SimpleDialogueSubView::getEditWidget() +{ + return *mEditWidget; +} + +bool CSVWorld::SimpleDialogueSubView::isLocked() const +{ + return mLocked; +} + +CSVWorld::SimpleDialogueSubView::SimpleDialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) : SubView (id), mEditWidget(0), mMainLayout(NULL), @@ -570,60 +604,8 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM QWidget *mainWidget = new QWidget(this); - QHBoxLayout *buttonsLayout = new QHBoxLayout; - QToolButton* prevButton = new QToolButton(mainWidget); - prevButton->setIcon(QIcon(":/go-previous.png")); - prevButton->setToolTip ("Switch to previous record"); - QToolButton* nextButton = new QToolButton(mainWidget); - nextButton->setIcon(QIcon(":/go-next.png")); - nextButton->setToolTip ("Switch to next record"); - buttonsLayout->addWidget(prevButton, 0); - buttonsLayout->addWidget(nextButton, 1); - buttonsLayout->addStretch(2); - - QToolButton* cloneButton = new QToolButton(mainWidget); - cloneButton->setIcon(QIcon(":/edit-clone.png")); - cloneButton->setToolTip ("Clone record"); - QToolButton* addButton = new QToolButton(mainWidget); - addButton->setIcon(QIcon(":/add.png")); - addButton->setToolTip ("Add new record"); - QToolButton* deleteButton = new QToolButton(mainWidget); - deleteButton->setIcon(QIcon(":/edit-delete.png")); - deleteButton->setToolTip ("Delete record"); - QToolButton* revertButton = new QToolButton(mainWidget); - revertButton->setIcon(QIcon(":/edit-undo.png")); - revertButton->setToolTip ("Revert record"); - - if (mTable->getFeatures() & CSMWorld::IdTable::Feature_Preview) - { - QToolButton* previewButton = new QToolButton(mainWidget); - previewButton->setIcon(QIcon(":/edit-preview.png")); - previewButton->setToolTip ("Open a preview of this record"); - buttonsLayout->addWidget(previewButton); - connect(previewButton, SIGNAL(clicked()), this, SLOT(showPreview())); - } - - if (mTable->getFeatures() & CSMWorld::IdTable::Feature_View) - { - QToolButton* viewButton = new QToolButton(mainWidget); - viewButton->setIcon(QIcon(":/cell.png")); - viewButton->setToolTip ("Open a scene view of the cell this record is located in"); - buttonsLayout->addWidget(viewButton); - connect(viewButton, SIGNAL(clicked()), this, SLOT(viewRecord())); - } - - buttonsLayout->addWidget(cloneButton); - buttonsLayout->addWidget(addButton); - buttonsLayout->addWidget(deleteButton); - buttonsLayout->addWidget(revertButton); - - connect(nextButton, SIGNAL(clicked()), this, SLOT(nextId())); - connect(prevButton, SIGNAL(clicked()), this, SLOT(prevId())); - connect(cloneButton, SIGNAL(clicked()), this, SLOT(cloneRequest())); - connect(revertButton, SIGNAL(clicked()), &mCommandDispatcher, SLOT(executeRevert())); - connect(deleteButton, SIGNAL(clicked()), &mCommandDispatcher, SLOT(executeDelete())); - mMainLayout = new QVBoxLayout(mainWidget); + setWidget (mainWidget); mEditWidget = new EditWidget(mainWidget, mTable->getModelIndex(mCurrentId, 0).row(), mTable, mCommandDispatcher, document, false); @@ -631,98 +613,10 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM mMainLayout->addWidget(mEditWidget); mEditWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); - mMainLayout->addWidget (mBottom = new TableBottomBox (creatorFactory, document, id, this)); - - mBottom->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); - - connect(mBottom, SIGNAL(requestFocus(const std::string&)), this, SLOT(requestFocus(const std::string&))); - - connect(addButton, SIGNAL(clicked()), mBottom, SLOT(createRequest())); - - if(!mBottom->canCreateAndDelete()) - { - cloneButton->setDisabled (true); - addButton->setDisabled (true); - deleteButton->setDisabled (true); - } - dataChanged(mTable->getModelIndex (mCurrentId, 0)); - mMainLayout->addLayout (buttonsLayout); - setWidget (mainWidget); } -void CSVWorld::DialogueSubView::prevId () -{ - int newRow = mTable->getModelIndex(mCurrentId, 0).row() - 1; - - if (newRow < 0) - { - return; - } - while (newRow >= 0) - { - QModelIndex newIndex(mTable->index(newRow, 0)); - - if (!newIndex.isValid()) - { - return; - } - - CSMWorld::RecordBase::State state = static_cast(mTable->data (mTable->index (newRow, 1)).toInt()); - if (!(state == CSMWorld::RecordBase::State_Deleted || state == CSMWorld::RecordBase::State_Erased)) - { - mEditWidget->remake(newRow); - - setUniversalId(CSMWorld::UniversalId (static_cast (mTable->data (mTable->index (newRow, 2)).toInt()), - mTable->data (mTable->index (newRow, 0)).toString().toUtf8().constData())); - - changeCurrentId(std::string(mTable->data (mTable->index (newRow, 0)).toString().toUtf8().constData())); - - mEditWidget->setDisabled(mLocked); - - return; - } - --newRow; - } -} - -void CSVWorld::DialogueSubView::nextId () -{ - int newRow = mTable->getModelIndex(mCurrentId, 0).row() + 1; - - if (newRow >= mTable->rowCount()) - { - return; - } - - while (newRow < mTable->rowCount()) - { - QModelIndex newIndex(mTable->index(newRow, 0)); - - if (!newIndex.isValid()) - { - return; - } - - CSMWorld::RecordBase::State state = static_cast(mTable->data (mTable->index (newRow, 1)).toInt()); - if (!(state == CSMWorld::RecordBase::State_Deleted)) - { - mEditWidget->remake(newRow); - - setUniversalId(CSMWorld::UniversalId (static_cast (mTable->data (mTable->index (newRow, 2)).toInt()), - mTable->data (mTable->index (newRow, 0)).toString().toUtf8().constData())); - - changeCurrentId(std::string(mTable->data (mTable->index (newRow, 0)).toString().toUtf8().constData())); - - mEditWidget->setDisabled(mLocked); - - return; - } - ++newRow; - } -} - -void CSVWorld::DialogueSubView::setEditLock (bool locked) +void CSVWorld::SimpleDialogueSubView::setEditLock (bool locked) { if (!mEditWidget) // hack to indicate that mCurrentId is no longer valid return; @@ -741,7 +635,7 @@ void CSVWorld::DialogueSubView::setEditLock (bool locked) } -void CSVWorld::DialogueSubView::dataChanged (const QModelIndex & index) +void CSVWorld::SimpleDialogueSubView::dataChanged (const QModelIndex & index) { QModelIndex currentIndex(mTable->getModelIndex(mCurrentId, 0)); @@ -774,7 +668,7 @@ void CSVWorld::DialogueSubView::dataChanged (const QModelIndex & index) } } -void CSVWorld::DialogueSubView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) +void CSVWorld::SimpleDialogueSubView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) { QModelIndex currentIndex(mTable->getModelIndex(mCurrentId, 0)); @@ -789,45 +683,14 @@ void CSVWorld::DialogueSubView::rowsAboutToBeRemoved(const QModelIndex &parent, } } -void CSVWorld::DialogueSubView::requestFocus (const std::string& id) +void CSVWorld::SimpleDialogueSubView::requestFocus (const std::string& id) { changeCurrentId(id); mEditWidget->remake(mTable->getModelIndex (id, 0).row()); } -void CSVWorld::DialogueSubView::cloneRequest () -{ - mBottom->cloneRequest(mCurrentId, static_cast(mTable->data(mTable->getModelIndex(mCurrentId, 2)).toInt())); -} - -void CSVWorld::DialogueSubView::showPreview () -{ - QModelIndex currentIndex(mTable->getModelIndex(mCurrentId, 0)); - - if (currentIndex.isValid() && - mTable->getFeatures() & CSMWorld::IdTable::Feature_Preview && - currentIndex.row() < mTable->rowCount()) - { - emit focusId(CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Preview, mCurrentId), ""); - } -} - -void CSVWorld::DialogueSubView::viewRecord () -{ - QModelIndex currentIndex(mTable->getModelIndex (mCurrentId, 0)); - - if (currentIndex.isValid() && - currentIndex.row() < mTable->rowCount()) - { - std::pair params = mTable->view (currentIndex.row()); - - if (params.first.getType()!=CSMWorld::UniversalId::Type_None) - emit focusId (params.first, params.second); - } -} - -void CSVWorld::DialogueSubView::changeCurrentId (const std::string& newId) +void CSVWorld::SimpleDialogueSubView::changeCurrentId (const std::string& newId) { std::vector selection; mCurrentId = std::string(newId); @@ -835,3 +698,186 @@ void CSVWorld::DialogueSubView::changeCurrentId (const std::string& newId) selection.push_back(mCurrentId); mCommandDispatcher.setSelection(selection); } + + +CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, + CSMDoc::Document& document, const CreatorFactoryBase& creatorFactory, bool sorting) +: SimpleDialogueSubView (id, document) +{ + // bottom box + getMainLayout().addWidget (mBottom = new TableBottomBox (creatorFactory, document, id, this)); + + mBottom->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); + + connect(mBottom, SIGNAL(requestFocus(const std::string&)), this, SLOT(requestFocus(const std::string&))); + + // buttons + QHBoxLayout *buttonsLayout = new QHBoxLayout; + QToolButton* prevButton = new QToolButton (this); + prevButton->setIcon(QIcon(":/go-previous.png")); + prevButton->setToolTip ("Switch to previous record"); + QToolButton* nextButton = new QToolButton (this); + nextButton->setIcon(QIcon(":/go-next.png")); + nextButton->setToolTip ("Switch to next record"); + buttonsLayout->addWidget(prevButton, 0); + buttonsLayout->addWidget(nextButton, 1); + buttonsLayout->addStretch(2); + + QToolButton* cloneButton = new QToolButton (this); + cloneButton->setIcon(QIcon(":/edit-clone.png")); + cloneButton->setToolTip ("Clone record"); + QToolButton* addButton = new QToolButton (this); + addButton->setIcon(QIcon(":/add.png")); + addButton->setToolTip ("Add new record"); + QToolButton* deleteButton = new QToolButton (this); + deleteButton->setIcon(QIcon(":/edit-delete.png")); + deleteButton->setToolTip ("Delete record"); + QToolButton* revertButton = new QToolButton (this); + revertButton->setIcon(QIcon(":/edit-undo.png")); + revertButton->setToolTip ("Revert record"); + + if (getTable().getFeatures() & CSMWorld::IdTable::Feature_Preview) + { + QToolButton* previewButton = new QToolButton (this); + previewButton->setIcon(QIcon(":/edit-preview.png")); + previewButton->setToolTip ("Open a preview of this record"); + buttonsLayout->addWidget(previewButton); + connect(previewButton, SIGNAL(clicked()), this, SLOT(showPreview())); + } + + if (getTable().getFeatures() & CSMWorld::IdTable::Feature_View) + { + QToolButton* viewButton = new QToolButton (this); + viewButton->setIcon(QIcon(":/cell.png")); + viewButton->setToolTip ("Open a scene view of the cell this record is located in"); + buttonsLayout->addWidget(viewButton); + connect(viewButton, SIGNAL(clicked()), this, SLOT(viewRecord())); + } + + buttonsLayout->addWidget(cloneButton); + buttonsLayout->addWidget(addButton); + buttonsLayout->addWidget(deleteButton); + buttonsLayout->addWidget(revertButton); + + connect(nextButton, SIGNAL(clicked()), this, SLOT(nextId())); + connect(prevButton, SIGNAL(clicked()), this, SLOT(prevId())); + connect(cloneButton, SIGNAL(clicked()), this, SLOT(cloneRequest())); + connect(revertButton, SIGNAL(clicked()), &getCommandDispatcher(), SLOT(executeRevert())); + connect(deleteButton, SIGNAL(clicked()), &getCommandDispatcher(), SLOT(executeDelete())); + + connect(addButton, SIGNAL(clicked()), mBottom, SLOT(createRequest())); + + if(!mBottom->canCreateAndDelete()) + { + cloneButton->setDisabled (true); + addButton->setDisabled (true); + deleteButton->setDisabled (true); + } + + getMainLayout().addLayout (buttonsLayout); +} + +void CSVWorld::DialogueSubView::cloneRequest() +{ + mBottom->cloneRequest (getCurrentId(), + static_cast (getTable(). + data (getTable().getModelIndex(getCurrentId(), 2)).toInt())); +} + +void CSVWorld::DialogueSubView::prevId() +{ + int newRow = getTable().getModelIndex (getCurrentId(), 0).row() - 1; + + if (newRow < 0) + { + return; + } + while (newRow >= 0) + { + QModelIndex newIndex (getTable().index(newRow, 0)); + + if (!newIndex.isValid()) + { + return; + } + + CSMWorld::RecordBase::State state = static_cast (getTable().data (getTable().index (newRow, 1)).toInt()); + if (!(state == CSMWorld::RecordBase::State_Deleted || state == CSMWorld::RecordBase::State_Erased)) + { + getEditWidget().remake (newRow); + + setUniversalId(CSMWorld::UniversalId (static_cast (getTable().data (getTable().index (newRow, 2)).toInt()), + getTable().data (getTable().index (newRow, 0)).toString().toUtf8().constData())); + + changeCurrentId(std::string (getTable().data (getTable().index (newRow, 0)).toString().toUtf8().constData())); + + getEditWidget().setDisabled (isLocked()); + + return; + } + --newRow; + } +} + +void CSVWorld::DialogueSubView::nextId () +{ + int newRow = getTable().getModelIndex (getCurrentId(), 0).row() + 1; + + if (newRow >= getTable().rowCount()) + { + return; + } + + while (newRow < getTable().rowCount()) + { + QModelIndex newIndex (getTable().index(newRow, 0)); + + if (!newIndex.isValid()) + { + return; + } + + CSMWorld::RecordBase::State state = static_cast (getTable().data (getTable().index (newRow, 1)).toInt()); + if (!(state == CSMWorld::RecordBase::State_Deleted)) + { + getEditWidget().remake(newRow); + + setUniversalId(CSMWorld::UniversalId (static_cast (getTable().data (getTable().index (newRow, 2)).toInt()), + getTable().data (getTable().index (newRow, 0)).toString().toUtf8().constData())); + + changeCurrentId(std::string (getTable().data (getTable().index (newRow, 0)).toString().toUtf8().constData())); + + getEditWidget().setDisabled (isLocked()); + + return; + } + ++newRow; + } +} + + +void CSVWorld::DialogueSubView::showPreview () +{ + QModelIndex currentIndex (getTable().getModelIndex (getCurrentId(), 0)); + + if (currentIndex.isValid() && + getTable().getFeatures() & CSMWorld::IdTable::Feature_Preview && + currentIndex.row() < getTable().rowCount()) + { + emit focusId(CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Preview, getCurrentId()), ""); + } +} + +void CSVWorld::DialogueSubView::viewRecord () +{ + QModelIndex currentIndex (getTable().getModelIndex (getCurrentId(), 0)); + + if (currentIndex.isValid() && + currentIndex.row() < getTable().rowCount()) + { + std::pair params = getTable().view (currentIndex.row()); + + if (params.first.getType()!=CSMWorld::UniversalId::Type_None) + emit focusId (params.first, params.second); + } +} diff --git a/apps/opencs/view/world/dialoguesubview.hpp b/apps/opencs/view/world/dialoguesubview.hpp index 69e0dc8648..5d6915d42d 100644 --- a/apps/opencs/view/world/dialoguesubview.hpp +++ b/apps/opencs/view/world/dialoguesubview.hpp @@ -173,7 +173,7 @@ namespace CSVWorld void remake(int row); }; - class DialogueSubView : public CSVDoc::SubView + class SimpleDialogueSubView : public CSVDoc::SubView { Q_OBJECT @@ -184,33 +184,32 @@ namespace CSVWorld std::string mCurrentId; bool mLocked; const CSMDoc::Document& mDocument; - TableBottomBox* mBottom; CSMWorld::CommandDispatcher mCommandDispatcher; + protected: + + QVBoxLayout& getMainLayout(); + + CSMWorld::IdTable& getTable(); + + CSMWorld::CommandDispatcher& getCommandDispatcher(); + + std::string getCurrentId() const; + + EditWidget& getEditWidget(); + + void changeCurrentId(const std::string& newCurrent); + + bool isLocked() const; + public: - DialogueSubView (const CSMWorld::UniversalId& id, - CSMDoc::Document& document, - const CreatorFactoryBase& creatorFactory, - bool sorting = false); + SimpleDialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document); virtual void setEditLock (bool locked); - private: - void changeCurrentId(const std::string& newCurrent); - private slots: - void nextId(); - - void prevId(); - - void showPreview(); - - void viewRecord(); - - void cloneRequest(); - void dataChanged(const QModelIndex & index); ///\brief we need to care for deleting currently edited record @@ -218,6 +217,30 @@ namespace CSVWorld void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end); }; + + class DialogueSubView : public SimpleDialogueSubView + { + Q_OBJECT + + TableBottomBox* mBottom; + + public: + + DialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, + const CreatorFactoryBase& creatorFactory, bool sorting = false); + + private slots: + + void cloneRequest(); + + void nextId(); + + void prevId(); + + void showPreview(); + + void viewRecord(); + }; } #endif diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp index ba3ab1358c..b8a6ba4298 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -170,6 +170,9 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) manager.add (CSMWorld::UniversalId::Type_Filter, new CSVDoc::SubViewFactoryWithCreator > (false)); + manager.add (CSMWorld::UniversalId::Type_MetaData, + new CSVDoc::SubViewFactory); + //preview manager.add (CSMWorld::UniversalId::Type_Preview, new CSVDoc::SubViewFactory); } diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 0ec701a65a..d757d5bbed 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -9,6 +9,8 @@ #include #include +#include + #include "../../model/doc/document.hpp" #include "../../model/world/data.hpp" @@ -128,17 +130,24 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) { int row = mProxyModel->mapToSource ( mProxyModel->index (selectedRows.begin()->row(), 0)).row(); + QString curData = mModel->data(mModel->index(row, column)).toString(); - if (row>0 && mModel->data (mModel->index (row, column))== - mModel->data (mModel->index (row-1, column))) + if (row > 0) { - menu.addAction (mMoveUpAction); + QString prevData = mModel->data(mModel->index(row - 1, column)).toString(); + if (Misc::StringUtils::ciEqual(curData.toStdString(), prevData.toStdString())) + { + menu.addAction(mMoveUpAction); + } } - if (rowrowCount()-1 && mModel->data (mModel->index (row, column))== - mModel->data (mModel->index (row+1, column))) + if (row < mModel->rowCount() - 1) { - menu.addAction (mMoveDownAction); + QString nextData = mModel->data(mModel->index(row + 1, column)).toString(); + if (Misc::StringUtils::ciEqual(curData.toStdString(), nextData.toStdString())) + { + menu.addAction(mMoveDownAction); + } } } } diff --git a/apps/opencs/view/world/util.cpp b/apps/opencs/view/world/util.cpp index 62cde4608a..f216585811 100644 --- a/apps/opencs/view/world/util.cpp +++ b/apps/opencs/view/world/util.cpp @@ -232,6 +232,14 @@ QWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleO return edit; } + case CSMWorld::ColumnBase::Display_LongString256: + { + /// \todo implement size limit. QPlainTextEdit does not support a size limit. + QPlainTextEdit *edit = new QPlainTextEdit(parent); + edit->setUndoRedoEnabled (false); + return edit; + } + case CSMWorld::ColumnBase::Display_Boolean: return new QCheckBox(parent); @@ -245,6 +253,14 @@ QWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleO return new CSVWidget::DropLineEdit(display, parent); + case CSMWorld::ColumnBase::Display_String32: + { + // For other Display types (that represent record IDs) with drop support IdCompletionDelegate is used + CSVWidget::DropLineEdit *widget = new CSVWidget::DropLineEdit(display, parent); + widget->setMaxLength (32); + return widget; + } + default: return QStyledItemDelegate::createEditor (parent, option, index);