diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index a56e77d089..92fd859bed 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -4,11 +4,11 @@ set (OPENCS_SRC model/doc/documentmanager.cpp model/doc/document.cpp - model/world/universalid.cpp + model/world/universalid.cpp model/world/idcollection.cpp model/world/data.cpp model/world/idtable.cpp view/doc/viewmanager.cpp view/doc/view.cpp view/doc/operations.cpp view/doc/operation.cpp - view/world/subview.cpp + view/world/subview.cpp view/world/globals.cpp ) set (OPENCS_HDR @@ -16,11 +16,12 @@ set (OPENCS_HDR model/doc/documentmanager.hpp model/doc/document.hpp - model/world/universalid.hpp + model/world/universalid.hpp model/world/record.hpp model/world/idcollection.hpp model/world/data.hpp + model/world/idtable.hpp model/world/columns.hpp view/doc/viewmanager.hpp view/doc/view.hpp view/doc/operations.hpp view/doc/operation.hpp - view/world/subview.hpp + view/world/subview.hpp view/world/globals.hpp ) set (OPENCS_US diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index c03a1dc5dc..6977a22f02 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -5,6 +5,9 @@ #include +#include "model/doc/document.hpp" +#include "model/world/data.hpp" + CS::Editor::Editor() : mViewManager (mDocumentManager), mNewDocumentIndex (0) { connect (&mViewManager, SIGNAL (newDocumentRequest ()), this, SLOT (createDocument ())); @@ -17,6 +20,21 @@ void CS::Editor::createDocument() stream << "NewDocument" << (++mNewDocumentIndex); CSMDoc::Document *document = mDocumentManager.addDocument (stream.str()); + + static const char *sGlobals[] = + { + "Day", "DaysPassed", "GameHour", "Month", "PCRace", "PCVampire", "PCWerewolf", "PCYear", 0 + }; + + for (int i=0; sGlobals[i]; ++i) + { + ESM::Global record; + record.mId = sGlobals[i]; + record.mValue = i==0 ? 1 : 0; + record.mType = ESM::VT_Float; + document->getData().getGlobals().add (record); + } + mViewManager.addView (document); } diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index cd65e83aef..e8091b3e00 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -104,4 +104,14 @@ void CSMDoc::Document::verifying() mVerifyTimer.stop(); emit stateChanged (getState(), this); } +} + +const CSMWorld::Data& CSMDoc::Document::getData() const +{ + return mData; +} + +CSMWorld::Data& CSMDoc::Document::getData() +{ + return mData; } \ No newline at end of file diff --git a/apps/opencs/model/doc/document.hpp b/apps/opencs/model/doc/document.hpp index 1f73b54371..b414bf04d4 100644 --- a/apps/opencs/model/doc/document.hpp +++ b/apps/opencs/model/doc/document.hpp @@ -7,6 +7,8 @@ #include #include +#include "../world/data.hpp" + namespace CSMDoc { class Document : public QObject @@ -25,8 +27,11 @@ namespace CSMDoc State_Searching = 32 // not implemented yet }; + private: + std::string mName; ///< \todo replace name with ESX list QUndoStack mUndoStack; + CSMWorld::Data mData; int mSaveCount; ///< dummy implementation -> remove when proper save is implemented. QTimer mSaveTimer; ///< dummy implementation -> remove when proper save is implemented. @@ -56,6 +61,10 @@ namespace CSMDoc void abortOperation (int type); + const CSMWorld::Data& getData() const; + + CSMWorld::Data& getData(); + signals: void stateChanged (int state, CSMDoc::Document *document); diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp new file mode 100644 index 0000000000..56362e4cdb --- /dev/null +++ b/apps/opencs/model/world/columns.hpp @@ -0,0 +1,21 @@ +#ifndef CSM_WOLRD_COLUMNS_H +#define CSM_WOLRD_COLUMNS_H + +#include "idcollection.hpp" + +namespace CSMWorld +{ + template + struct FloatValueColumn : public Column + { + FloatValueColumn() : Column ("Value") {} + + virtual QVariant get (const Record& record) const + { + return record.get().mValue; + } + }; + +} + +#endif \ No newline at end of file diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp new file mode 100644 index 0000000000..7fdb149b31 --- /dev/null +++ b/apps/opencs/model/world/data.cpp @@ -0,0 +1,47 @@ + +#include "data.hpp" + +#include + +#include + +#include + +#include "idtable.hpp" +#include "columns.hpp" + +CSMWorld::Data::Data() +{ + mGlobals.addColumn (new FloatValueColumn); + + mModels.insert (std::make_pair ( + UniversalId (UniversalId::Type_Globals), + new IdTable (&mGlobals) + )); +} + +CSMWorld::Data::~Data() +{ + for (std::map::iterator iter (mModels.begin()); iter!=mModels.end(); ++iter) + delete iter->second; +} + +const CSMWorld::IdCollection& CSMWorld::Data::getGlobals() const +{ + return mGlobals; +} + +CSMWorld::IdCollection& CSMWorld::Data::getGlobals() +{ + return mGlobals; +} + +QAbstractTableModel *CSMWorld::Data::getTableModel (const UniversalId& id) +{ + std::map::iterator iter = mModels.find (id); + + if (iter==mModels.end()) + throw std::logic_error ("No table model available for " + id.toString()); + + return iter->second; +} \ No newline at end of file diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp new file mode 100644 index 0000000000..11073c5e37 --- /dev/null +++ b/apps/opencs/model/world/data.hpp @@ -0,0 +1,39 @@ +#ifndef CSM_WOLRD_IDLIST_H +#define CSM_WOLRD_IDLIST_H + +#include + +#include + +#include "idcollection.hpp" +#include "universalid.hpp" + +class QAbstractTableModel; + +namespace CSMWorld +{ + class Data + { + IdCollection mGlobals; + std::map mModels; + + // not implemented + Data (const Data&); + Data& operator= (const Data&); + + public: + + Data(); + + ~Data(); + + const IdCollection& getGlobals() const; + + IdCollection& getGlobals(); + + QAbstractTableModel *getTableModel (const UniversalId& id); + ///< If no table model is available for \æ id, an exception is thrown. + }; +} + +#endif \ No newline at end of file diff --git a/apps/opencs/model/world/idcollection.cpp b/apps/opencs/model/world/idcollection.cpp new file mode 100644 index 0000000000..fc4bb1ef65 --- /dev/null +++ b/apps/opencs/model/world/idcollection.cpp @@ -0,0 +1,6 @@ + +#include "idcollection.hpp" + +CSMWorld::IdCollectionBase::IdCollectionBase() {} + +CSMWorld::IdCollectionBase::~IdCollectionBase() {} \ No newline at end of file diff --git a/apps/opencs/model/world/idcollection.hpp b/apps/opencs/model/world/idcollection.hpp new file mode 100644 index 0000000000..0bddda1ac9 --- /dev/null +++ b/apps/opencs/model/world/idcollection.hpp @@ -0,0 +1,158 @@ +#ifndef CSM_WOLRD_IDCOLLECTION_H +#define CSM_WOLRD_IDCOLLECTION_H + +#include +#include +#include +#include +#include + +#include + +#include "record.hpp" + +namespace CSMWorld +{ + template + struct Column + { + std::string mTitle; + + Column (const std::string& title) : mTitle (title) {} + + virtual ~Column() {} + + virtual QVariant get (const Record& record) const = 0; + }; + + class IdCollectionBase + { + // not implemented + IdCollectionBase (const IdCollectionBase&); + IdCollectionBase& operator= (const IdCollectionBase&); + + public: + + IdCollectionBase(); + + virtual ~IdCollectionBase(); + + virtual int getSize() const = 0; + + virtual std::string getId (int index) const = 0; + + virtual int getColumns() const = 0; + + virtual std::string getTitle (int column) const = 0; + + virtual QVariant getData (int index, int column) const = 0; + }; + + ///< \brief Collection of ID-based records + template + class IdCollection : public IdCollectionBase + { + std::vector > mRecords; + std::map mIndex; + std::vector *> mColumns; + + // not implemented + IdCollection (const IdCollection&); + IdCollection& operator= (const IdCollection&); + + public: + + IdCollection(); + + virtual ~IdCollection(); + + void add (const ESXRecordT& record); + ///< Add a new record (modified) + + virtual int getSize() const; + + virtual std::string getId (int index) const; + + virtual int getColumns() const; + + virtual QVariant getData (int index, int column) const; + + virtual std::string getTitle (int column) const; + + virtual void addColumn (Column *column); + }; + + template + IdCollection::IdCollection() + {} + + template + IdCollection::~IdCollection() + { + for (typename std::vector *>::iterator iter (mColumns.begin()); iter!=mColumns.end(); ++iter) + delete *iter; + } + + template + void IdCollection::add (const ESXRecordT& record) + { + std::string id; + + std::transform (record.mId.begin(), record.mId.end(), std::back_inserter (id), + (int(*)(int)) std::tolower); + + std::map::iterator iter = mIndex.find (id); + + if (iter==mIndex.end()) + { + Record record2; + record2.mState = Record::State_ModifiedOnly; + record2.mModified = record; + + mRecords.push_back (record2); + mIndex.insert (std::make_pair (id, mRecords.size()-1)); + } + else + { + mRecords[iter->second].setModified (record); + } + } + + template + int IdCollection::getSize() const + { + return mRecords.size(); + } + + template + std::string IdCollection::getId (int index) const + { + return mRecords.at (index).get().mId; + } + + template + int IdCollection::getColumns() const + { + return mColumns.size(); + } + + template + QVariant IdCollection::getData (int index, int column) const + { + return mColumns.at (column)->get (mRecords.at (index)); + } + + template + std::string IdCollection::getTitle (int column) const + { + return mColumns.at (column)->mTitle; + } + + template + void IdCollection::addColumn (Column *column) + { + mColumns.push_back (column); + } +} + +#endif diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp new file mode 100644 index 0000000000..f8acd4f9fe --- /dev/null +++ b/apps/opencs/model/world/idtable.cpp @@ -0,0 +1,55 @@ + +#include "idtable.hpp" + +#include "idcollection.hpp" + +CSMWorld::IdTable::IdTable (IdCollectionBase *idCollection) : mIdCollection (idCollection) +{ + +} + +CSMWorld::IdTable::~IdTable() +{ + +} + +int CSMWorld::IdTable::rowCount (const QModelIndex & parent) const +{ + if (parent.isValid()) + return 0; + + return mIdCollection->getSize(); +} + +int CSMWorld::IdTable::columnCount (const QModelIndex & parent) const +{ + if (parent.isValid()) + return 0; + + return 1+mIdCollection->getColumns(); +} + +QVariant CSMWorld::IdTable::data (const QModelIndex & index, int role) const +{ + if (role!=Qt::DisplayRole) + return QVariant(); + + if (index.column()==0) + return QVariant (tr (mIdCollection->getId (index.row()).c_str())); + + return mIdCollection->getData (index.row(), index.column()-1); +} + +QVariant CSMWorld::IdTable::headerData (int section, Qt::Orientation orientation, int role) const +{ + if (role!=Qt::DisplayRole) + return QVariant(); + + if (orientation==Qt::Vertical) + return QVariant(); + + if (section==0) + return QVariant (tr ("ID")); + + return tr (mIdCollection->getTitle (section-1).c_str()); +} \ No newline at end of file diff --git a/apps/opencs/model/world/idtable.hpp b/apps/opencs/model/world/idtable.hpp new file mode 100644 index 0000000000..4fb939fa31 --- /dev/null +++ b/apps/opencs/model/world/idtable.hpp @@ -0,0 +1,37 @@ +#ifndef CSM_WOLRD_IDTABLE_H +#define CSM_WOLRD_IDTABLE_H + +#include + +namespace CSMWorld +{ + class IdCollectionBase; + + class IdTable : public QAbstractTableModel + { + Q_OBJECT + + IdCollectionBase *mIdCollection; + + // not implemented + IdTable (const IdTable&); + IdTable& operator= (const IdTable&); + + public: + + IdTable (IdCollectionBase *idCollection); + ///< The ownership of \a idCollection is not transferred. + + virtual ~IdTable(); + + int rowCount (const QModelIndex & parent = QModelIndex()) const; + + int columnCount (const QModelIndex & parent = QModelIndex()) const; + + QVariant data (const QModelIndex & index, int role = Qt::DisplayRole) const; + + QVariant headerData (int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + }; +} + +#endif diff --git a/apps/opencs/model/world/record.hpp b/apps/opencs/model/world/record.hpp new file mode 100644 index 0000000000..230d39eb06 --- /dev/null +++ b/apps/opencs/model/world/record.hpp @@ -0,0 +1,76 @@ +#ifndef CSM_WOLRD_RECORD_H +#define CSM_WOLRD_RECORD_H + +#include + +namespace CSMWorld +{ + template + struct Record + { + enum state + { + State_BaseOnly, // defined in base only + State_Modified, // exists in base, but has been modified + State_ModifiedOnly, // newly created in modified + State_Deleted // exists in base, but has been deleted + }; + + ESXRecordT mBase; + ESXRecordT mModified; + state mState; + + bool isDeleted() const; + + bool isModified() const; + + const ESXRecordT& get() const; + ///< Throws an exception, if the record is deleted. + + ESXRecordT& get(); + ///< Throws an exception, if the record is deleted. + + void setModified (const ESXRecordT& modified); + }; + + template + bool Record::isDeleted() const + { + return mState==State_Deleted; + } + + template + bool Record::isModified() const + { + return mState==State_Modified || mState==State_ModifiedOnly; + } + + template + const ESXRecordT& Record::get() const + { + if (isDeleted()) + throw std::logic_error ("attempt to access a deleted record"); + + return mState==State_BaseOnly ? mBase : mModified; + } + + template + ESXRecordT& Record::get() + { + if (isDeleted()) + throw std::logic_error ("attempt to access a deleted record"); + + return mState==State_BaseOnly ? mBase : mModified; + } + + template + void Record::setModified (const ESXRecordT& modified) + { + mModified = modified; + + if (mState!=State_ModifiedOnly) + mState = State_Modified; + } +} + +#endif diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp index b56a336a6e..e2d8d1ffb1 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -17,6 +17,7 @@ namespace static const TypeData sNoArg[] = { { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, "empty" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Globals, "Global Variables" }, { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0 } // end marker }; @@ -117,6 +118,23 @@ bool CSMWorld::UniversalId::isEqual (const UniversalId& universalId) const } } +bool CSMWorld::UniversalId::isLess (const UniversalId& universalId) const +{ + if (mTypeuniversalId.mType) + return false; + + switch (mArgumentType) + { + case ArgumentType_Id: return mId +#include #include #include @@ -10,6 +11,7 @@ #include "../../model/doc/document.hpp" #include "../world/subview.hpp" +#include "../world/globals.hpp" #include "viewmanager.hpp" #include "operations.hpp" @@ -59,16 +61,16 @@ void CSVDoc::View::setupViewMenu() QAction *newWindow = new QAction (tr ("&New View"), this); connect (newWindow, SIGNAL (triggered()), this, SLOT (newView())); view->addAction (newWindow); - - QAction *test = new QAction (tr ("Add test Subview"), this); - connect (test, SIGNAL (triggered()), this, SLOT (addTestSubView())); - view->addAction (test); } void CSVDoc::View::setupWorldMenu() { QMenu *world = menuBar()->addMenu (tr ("&World")); + QAction *globals = new QAction (tr ("Globals"), this); + connect (globals, SIGNAL (triggered()), this, SLOT (addGlobalsSubView())); + world->addAction (globals); + mVerify = new QAction (tr ("&Verify"), this); connect (mVerify, SIGNAL (triggered()), this, SLOT (verify())); world->addAction (mVerify); @@ -124,6 +126,16 @@ CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int to updateTitle(); setupUi(); + + mSubViewFactories.insert (std::make_pair (CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Globals), + new CSVWorld::SubViewFactory())); +} + +CSVDoc::View::~View() +{ + for (std::map::iterator iter (mSubViewFactories.begin()); + iter!=mSubViewFactories.end(); ++iter) + delete iter->second; } const CSMDoc::Document *CSVDoc::View::getDocument() const @@ -176,8 +188,13 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id) /// \todo add an user setting to reuse sub views (on a per document basis or on a per top level view basis) - CSVWorld::SubView *view = new CSVWorld::SubView (id); - addDockWidget (Qt::RightDockWidgetArea, view); + std::map::iterator iter = mSubViewFactories.find (id); + + if (iter==mSubViewFactories.end()) + throw std::logic_error ("can't create subview for " + id.toString()); + + CSVWorld::SubView *view = iter->second->makeSubView (id, mDocument->getData()); + addDockWidget (Qt::TopDockWidgetArea, view); view->show(); } @@ -201,7 +218,7 @@ void CSVDoc::View::verify() mDocument->verify(); } -void CSVDoc::View::addTestSubView() +void CSVDoc::View::addGlobalsSubView() { - addSubView (CSMWorld::UniversalId::Type_None); + addSubView (CSMWorld::UniversalId::Type_Globals); } \ No newline at end of file diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index 7a9164e12f..8e2b272930 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -2,6 +2,7 @@ #define CSV_DOC_VIEW_H #include +#include #include @@ -17,6 +18,11 @@ namespace CSMWorld class UniversalId; } +namespace CSVWorld +{ + struct SubViewFactoryBase; +} + namespace CSVDoc { class ViewManager; @@ -36,6 +42,7 @@ namespace CSVDoc QAction *mVerify; std::vector mEditingActions; Operations *mOperations; + std::map mSubViewFactories; // not implemented View (const View&); @@ -64,6 +71,8 @@ namespace CSVDoc View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews); ///< The ownership of \a document is not transferred to *this. + virtual ~View(); + const CSMDoc::Document *getDocument() const; CSMDoc::Document *getDocument(); @@ -90,7 +99,7 @@ namespace CSVDoc void verify(); - void addTestSubView(); ///< \todo remove + void addGlobalsSubView(); }; } diff --git a/apps/opencs/view/world/globals.cpp b/apps/opencs/view/world/globals.cpp new file mode 100644 index 0000000000..1c0faa029e --- /dev/null +++ b/apps/opencs/view/world/globals.cpp @@ -0,0 +1,26 @@ + +#include "globals.hpp" + +#include +#include +#include + +#include "../../model/world/data.hpp" + +CSVWorld::Globals::Globals (const CSMWorld::UniversalId& id, CSMWorld::Data& data) +: SubView (id) +{ + QTableView *table = new QTableView(); + + setWidget (table); + + QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel (this); + proxyModel->setSourceModel (data.getTableModel (id)); + + table->setModel (proxyModel); + table->horizontalHeader()->setResizeMode (QHeaderView::Interactive); + table->verticalHeader()->hide(); + table->setSortingEnabled (true); + + /// \todo make initial layout fill the whole width of the table +} \ No newline at end of file diff --git a/apps/opencs/view/world/globals.hpp b/apps/opencs/view/world/globals.hpp new file mode 100644 index 0000000000..c54716255a --- /dev/null +++ b/apps/opencs/view/world/globals.hpp @@ -0,0 +1,17 @@ +#ifndef CSV_WORLD_GLOBALS_H +#define CSV_WORLD_GLOBALS_H + +#include "subview.hpp" + +namespace CSVWorld +{ + class Globals : public SubView + { + + public: + + Globals (const CSMWorld::UniversalId& id, CSMWorld::Data& data); + }; +} + +#endif \ No newline at end of file diff --git a/apps/opencs/view/world/subview.hpp b/apps/opencs/view/world/subview.hpp index 8488690c88..95cbe4243f 100644 --- a/apps/opencs/view/world/subview.hpp +++ b/apps/opencs/view/world/subview.hpp @@ -5,6 +5,11 @@ #include +namespace CSMWorld +{ + class Data; +} + namespace CSVWorld { class SubView : public QDockWidget @@ -23,6 +28,23 @@ namespace CSVWorld CSMWorld::UniversalId getUniversalId() const; }; + + struct SubViewFactoryBase + { + virtual SubView *makeSubView (const CSMWorld::UniversalId& id, CSMWorld::Data& data) = 0; + }; + + template + struct SubViewFactory : public SubViewFactoryBase + { + virtual SubView *makeSubView (const CSMWorld::UniversalId& id, CSMWorld::Data& data); + }; + + template + SubView *SubViewFactory::makeSubView (const CSMWorld::UniversalId& id, CSMWorld::Data& data) + { + return new SubViewT (id, data); + } } #endif \ No newline at end of file diff --git a/components/esm/loadglob.hpp b/components/esm/loadglob.hpp index 302729d2e6..5cfb3c87b7 100644 --- a/components/esm/loadglob.hpp +++ b/components/esm/loadglob.hpp @@ -18,7 +18,7 @@ class ESMWriter; struct Global { std::string mId; - unsigned mValue; + float mValue; VarType mType; void load(ESMReader &esm);