diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 66c446bae..6da863b97 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -57,13 +57,14 @@ opencs_hdrs_noqt (view/doc opencs_units (view/world - table tablesubview scriptsubview util regionmapsubview + table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator + cellcreator referenceablecreator referencecreator ) opencs_units_noqt (view/world dialoguesubview subviews enumdelegate vartypedelegate recordstatusdelegate refidtypedelegate datadisplaydelegate - scripthighlighter + scripthighlighter idvalidator ) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index eaded5b70..43ecaca63 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -3,7 +3,7 @@ #include -#include "idtableproxymodel.hpp" +#include "idtable.hpp" #include "idtable.hpp" CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index, @@ -25,15 +25,28 @@ void CSMWorld::ModifyCommand::undo() mModel.setData (mIndex, mOld); } -CSMWorld::CreateCommand::CreateCommand (IdTableProxyModel& model, const std::string& id, QUndoCommand *parent) -: QUndoCommand (parent), mModel (model), mId (id) +CSMWorld::CreateCommand::CreateCommand (IdTable& model, const std::string& id, QUndoCommand *parent) +: QUndoCommand (parent), mModel (model), mId (id), mType (UniversalId::Type_None) { setText (("Create record " + id).c_str()); } +void CSMWorld::CreateCommand::addValue (int column, const QVariant& value) +{ + mValues[column] = value; +} + +void CSMWorld::CreateCommand::setType (UniversalId::Type type) +{ + mType = type; +} + void CSMWorld::CreateCommand::redo() { - mModel.addRecord (mId); + mModel.addRecord (mId, mType); + + for (std::map::const_iterator iter (mValues.begin()); iter!=mValues.end(); ++iter) + mModel.setData (mModel.getModelIndex (mId, iter->first), iter->second); } void CSMWorld::CreateCommand::undo() diff --git a/apps/opencs/model/world/commands.hpp b/apps/opencs/model/world/commands.hpp index af419215d..3fc2522ea 100644 --- a/apps/opencs/model/world/commands.hpp +++ b/apps/opencs/model/world/commands.hpp @@ -4,17 +4,20 @@ #include "record.hpp" #include +#include #include #include #include +#include "universalid.hpp" + class QModelIndex; class QAbstractItemModel; namespace CSMWorld { - class IdTableProxyModel; + class IdTable; class IdTable; class RecordBase; @@ -37,12 +40,18 @@ namespace CSMWorld class CreateCommand : public QUndoCommand { - IdTableProxyModel& mModel; + IdTable& mModel; std::string mId; + UniversalId::Type mType; + std::map mValues; public: - CreateCommand (IdTableProxyModel& model, const std::string& id, QUndoCommand *parent = 0); + CreateCommand (IdTable& model, const std::string& id, QUndoCommand *parent = 0); + + void setType (UniversalId::Type type); + + void addValue (int column, const QVariant& value); virtual void redo(); diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index d54b3ac16..594bc19c2 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -300,6 +300,16 @@ CSMWorld::RefIdCollection& CSMWorld::Data::getReferenceables() return mReferenceables; } +const CSMWorld::RefCollection& CSMWorld::Data::getReferences() const +{ + return mRefs; +} + +CSMWorld::RefCollection& CSMWorld::Data::getReferences() +{ + return mRefs; +} + QAbstractItemModel *CSMWorld::Data::getTableModel (const UniversalId& id) { std::map::iterator iter = mModelIndex.find (id.getType()); @@ -394,4 +404,22 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base) reader.skipRecord(); } } -} \ No newline at end of file +} + +bool CSMWorld::Data::hasId (const std::string& id) const +{ + return + getGlobals().searchId (id)!=-1 || + getGmsts().searchId (id)!=-1 || + getSkills().searchId (id)!=-1 || + getClasses().searchId (id)!=-1 || + getFactions().searchId (id)!=-1 || + getRaces().searchId (id)!=-1 || + getSounds().searchId (id)!=-1 || + getScripts().searchId (id)!=-1 || + getRegions().searchId (id)!=-1 || + getBirthsigns().searchId (id)!=-1 || + getSpells().searchId (id)!=-1 || + getCells().searchId (id)!=-1 || + getReferenceables().searchId (id)!=-1; +} diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index ca030d9b2..8b2403db3 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -112,6 +112,10 @@ namespace CSMWorld RefIdCollection& getReferenceables(); + const RefCollection& getReferences() const; + + RefCollection& getReferences(); + QAbstractItemModel *getTableModel (const UniversalId& id); ///< If no table model is available for \a id, an exception is thrown. /// @@ -123,6 +127,8 @@ namespace CSMWorld void loadFile (const boost::filesystem::path& path, bool base); ///< Merging content of a file into base or modified. + + bool hasId (const std::string& id) const; }; } diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index 25258398b..3d36b5503 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -116,13 +116,13 @@ QModelIndex CSMWorld::IdTable::parent (const QModelIndex& index) const return QModelIndex(); } -void CSMWorld::IdTable::addRecord (const std::string& id) +void CSMWorld::IdTable::addRecord (const std::string& id, UniversalId::Type type) { int index = mIdCollection->getAppendIndex(); beginInsertRows (QModelIndex(), index, index); - mIdCollection->appendBlankRecord (id); + mIdCollection->appendBlankRecord (id, type); endInsertRows(); } diff --git a/apps/opencs/model/world/idtable.hpp b/apps/opencs/model/world/idtable.hpp index 98bf6ad6d..556d7f0ba 100644 --- a/apps/opencs/model/world/idtable.hpp +++ b/apps/opencs/model/world/idtable.hpp @@ -3,6 +3,8 @@ #include +#include "universalid.hpp" + namespace CSMWorld { class CollectionBase; @@ -44,7 +46,8 @@ namespace CSMWorld virtual QModelIndex parent (const QModelIndex& index) const; - void addRecord (const std::string& id); + void addRecord (const std::string& id, UniversalId::Type type = UniversalId::Type_None); + ///< \param type Will be ignored, unless the collection supports multiple record types QModelIndex getModelIndex (const std::string& id, int column) const; diff --git a/apps/opencs/model/world/idtableproxymodel.cpp b/apps/opencs/model/world/idtableproxymodel.cpp index 78995f60b..e99e1575c 100644 --- a/apps/opencs/model/world/idtableproxymodel.cpp +++ b/apps/opencs/model/world/idtableproxymodel.cpp @@ -7,11 +7,6 @@ CSMWorld::IdTableProxyModel::IdTableProxyModel (QObject *parent) : QSortFilterProxyModel (parent) {} -void CSMWorld::IdTableProxyModel::addRecord (const std::string& id) -{ - dynamic_cast (*sourceModel()).addRecord (id); -} - QModelIndex CSMWorld::IdTableProxyModel::getModelIndex (const std::string& id, int column) const { return mapFromSource (dynamic_cast (*sourceModel()).getModelIndex (id, column)); diff --git a/apps/opencs/model/world/idtableproxymodel.hpp b/apps/opencs/model/world/idtableproxymodel.hpp index 3f1537cce..200b99fe2 100644 --- a/apps/opencs/model/world/idtableproxymodel.hpp +++ b/apps/opencs/model/world/idtableproxymodel.hpp @@ -15,8 +15,6 @@ namespace CSMWorld IdTableProxyModel (QObject *parent = 0); - virtual void addRecord (const std::string& id); - virtual QModelIndex getModelIndex (const std::string& id, int column) const; }; } diff --git a/apps/opencs/model/world/refcollection.cpp b/apps/opencs/model/world/refcollection.cpp index 6a1e8045b..085817753 100644 --- a/apps/opencs/model/world/refcollection.cpp +++ b/apps/opencs/model/world/refcollection.cpp @@ -21,10 +21,7 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool while (cell2.getNextRef (reader, ref)) { /// \todo handle deleted and moved references - std::ostringstream stream; - stream << "ref#" << mNextId++; - - ref.load (reader, cell2, stream.str()); + ref.load (reader, cell2, getNewId()); Record record2; record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; @@ -34,4 +31,11 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool } mCells.setRecord (cellIndex, cell); +} + +std::string CSMWorld::RefCollection::getNewId() +{ + std::ostringstream stream; + stream << "ref#" << mNextId++; + return stream.str(); } \ No newline at end of file diff --git a/apps/opencs/model/world/refcollection.hpp b/apps/opencs/model/world/refcollection.hpp index a2063590b..895315a17 100644 --- a/apps/opencs/model/world/refcollection.hpp +++ b/apps/opencs/model/world/refcollection.hpp @@ -21,6 +21,8 @@ namespace CSMWorld void load (ESM::ESMReader& reader, int cellIndex, bool base); ///< Load a sequence of references. + + std::string getNewId(); }; } diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp index c66101950..4674f23f1 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -12,81 +12,82 @@ namespace CSMWorld::UniversalId::Class mClass; CSMWorld::UniversalId::Type mType; const char *mName; + const char *mIcon; }; 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_RecordList, CSMWorld::UniversalId::Type_Gmsts, "Game Settings" }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Skills, "Skills" }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Classes, "Classes" }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Factions, "Factions" }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Races, "Races" }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Sounds, "Sounds" }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Scripts, "Scripts" }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Regions, "Regions" }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Birthsigns, "Birthsigns" }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Spells, "Spells" }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Cells, "Cells" }, + { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, "empty", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Globals, "Global Variables", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Gmsts, "Game Settings", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Skills, "Skills", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Classes, "Classes", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Factions, "Factions", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Races, "Races", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Sounds, "Sounds", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Scripts, "Scripts", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Regions, "Regions", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Birthsigns, "Birthsigns", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Spells, "Spells", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Cells, "Cells", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables, - "Referenceables" }, + "Referenceables", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_References, - "References" }, + "References", 0 }, { CSMWorld::UniversalId::Class_NonRecord, CSMWorld::UniversalId::Type_RegionMap, - "Region Map" }, + "Region Map", 0 }, - { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0 } // end marker + { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker }; static const TypeData sIdArg[] = { - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Global, "Global Variable" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Gmst, "Game Setting" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Skill, "Skill" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Class, "Class" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Faction, "Faction" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Race, "Race" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Sound, "Sound" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Script, "Script" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Region, "Region" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Birthsign, "Birthsign" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Spell, "Spell" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, "Cell" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Referenceables" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Activator, "Activator" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Potion, "Potion" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Apparatus, "Apparatus" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Armor, "Armor" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Book, "Book" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Clothing, "Clothing" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Container, "Container" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Creature, "Creature" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Door, "Door" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Ingredient, "Ingredient" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_CreatureLevelledList, - "Creature Levelled List" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_ItemLevelledList, - "Item Levelled List" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Light, "Light" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Lockpick, "Lockpick" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Miscellaneous, - "Miscellaneous" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Npc, "NPC" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Probe, "Probe" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Repair, "Repair" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Static, "Static" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Weapon, "Weapon" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Reference, "Reference" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Global, "Global Variable", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Gmst, "Game Setting", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Skill, "Skill", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Class, "Class", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Faction, "Faction", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Race, "Race", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Sound, "Sound", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Script, "Script", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Region, "Region", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Birthsign, "Birthsign", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Spell, "Spell", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, "Cell", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Referenceables", 0 }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Activator, "Activator", ":./activator.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Potion, "Potion", ":./potion.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Apparatus, "Apparatus", ":./apparatus.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Armor, "Armor", ":./armor.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Book, "Book", ":./book.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Clothing, "Clothing", ":./clothing.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Container, "Container", ":./container.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Creature, "Creature", ":./creature.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Door, "Door", ":./door.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Ingredient, "Ingredient", ":./ingredient.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_CreatureLevelledList, + "Creature Levelled List", ":./creature.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_ItemLevelledList, + "Item Levelled List", ":./leveled-item.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Light, "Light", ":./light.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Lockpick, "Lockpick", ":./lockpick.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Miscellaneous, + "Miscellaneous", ":./miscellaneous.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Npc, "NPC", ":./npc.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Probe, "Probe", ":./probe.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Repair, "Repair", ":./repair.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Static, "Static", ":./static.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Weapon, "Weapon", ":./weapon.png" }, + { CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_Reference, "Reference", 0 }, - { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0 } // end marker + { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker }; static const TypeData sIndexArg[] = { - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_VerificationResults, "Verification Results" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_VerificationResults, "Verification Results", 0 }, - { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0 } // end marker + { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker }; static const unsigned int IDARG_SIZE = sizeof (sIdArg) / sizeof (TypeData); @@ -268,6 +269,28 @@ std::string CSMWorld::UniversalId::toString() const return stream.str(); } +std::string CSMWorld::UniversalId::getIcon() const +{ + const TypeData *typeData = mArgumentType==ArgumentType_None ? sNoArg : + (mArgumentType==ArgumentType_Id ? sIdArg : sIndexArg); + + for (int i=0; typeData[i].mName; ++i) + if (typeData[i].mType==mType) + return typeData[i].mIcon ? typeData[i].mIcon : ""; + + throw std::logic_error ("failed to retrieve UniversalId type icon"); +} + +std::vector CSMWorld::UniversalId::listReferenceableTypes() +{ + std::vector list; + + for (int i=0; sIdArg[i].mName; ++i) + if (sIdArg[i].mClass==Class_RefRecord) + list.push_back (sIdArg[i].mType); + + return list; +} std::pair CSMWorld::UniversalId::getIdArgPair (unsigned int index) { diff --git a/apps/opencs/model/world/universalid.hpp b/apps/opencs/model/world/universalid.hpp index 453da3ef3..7dc4c851a 100644 --- a/apps/opencs/model/world/universalid.hpp +++ b/apps/opencs/model/world/universalid.hpp @@ -3,6 +3,7 @@ #include #include +#include #include @@ -14,13 +15,14 @@ namespace CSMWorld enum Class { - Class_None = 0, - Class_Record, - Class_SubRecord, - Class_RecordList, - Class_Collection, // multiple types of records combined - Class_Transient, // not part of the world data or the project data - Class_NonRecord // record like data that is not part of the world + Class_None = 0, + Class_Record, + Class_RefRecord, // referenceable record + Class_SubRecord, + Class_RecordList, + Class_Collection, // multiple types of records combined + Class_Transient, // not part of the world data or the project data + Class_NonRecord // record like data that is not part of the world }; enum ArgumentType @@ -126,6 +128,11 @@ namespace CSMWorld std::string toString() const; + std::string getIcon() const; + ///< Will return an empty string, if no icon is available. + + static std::vector listReferenceableTypes(); + static std::pair getIdArgPair (unsigned int index); static unsigned int getIdArgSize (); }; diff --git a/apps/opencs/view/doc/subview.cpp b/apps/opencs/view/doc/subview.cpp index 731adabb3..09361a1c0 100644 --- a/apps/opencs/view/doc/subview.cpp +++ b/apps/opencs/view/doc/subview.cpp @@ -1,15 +1,10 @@ #include "subview.hpp" - CSVDoc::SubView::SubView (const CSMWorld::UniversalId& id) : mUniversalId (id) { /// \todo add a button to the title bar that clones this sub view setWindowTitle (mUniversalId.toString().c_str()); - - /// \todo remove (for testing only) - setMinimumWidth (100); - setMinimumHeight (60); } CSMWorld::UniversalId CSVDoc::SubView::getUniversalId() const @@ -20,3 +15,5 @@ CSMWorld::UniversalId CSVDoc::SubView::getUniversalId() const void CSVDoc::SubView::updateEditorSetting (const QString &settingName, const QString &settingValue) { } + +void CSVDoc::SubView::setStatusBar (bool show) {} \ No newline at end of file diff --git a/apps/opencs/view/doc/subview.hpp b/apps/opencs/view/doc/subview.hpp index 280a2a487..aa073f81d 100644 --- a/apps/opencs/view/doc/subview.hpp +++ b/apps/opencs/view/doc/subview.hpp @@ -37,6 +37,9 @@ namespace CSVDoc virtual void setEditLock (bool locked) = 0; virtual void updateEditorSetting (const QString &, const QString &); + virtual void setStatusBar (bool show); + ///< Default implementation: ignored + signals: void focusId (const CSMWorld::UniversalId& universalId); diff --git a/apps/opencs/view/doc/subviewfactoryimp.hpp b/apps/opencs/view/doc/subviewfactoryimp.hpp index d16e0b2b7..2c4158f85 100644 --- a/apps/opencs/view/doc/subviewfactoryimp.hpp +++ b/apps/opencs/view/doc/subviewfactoryimp.hpp @@ -22,28 +22,20 @@ namespace CSVDoc return new SubViewT (id, document); } - template - class SubViewFactoryWithCreateFlag : public SubViewFactoryBase + + template + class SubViewFactoryWithCreator : public SubViewFactoryBase { - bool mCreateAndDelete; - public: - SubViewFactoryWithCreateFlag (bool createAndDelete); - virtual CSVDoc::SubView *makeSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document); }; - template - SubViewFactoryWithCreateFlag::SubViewFactoryWithCreateFlag (bool createAndDelete) - : mCreateAndDelete (createAndDelete) - {} - - template - CSVDoc::SubView *SubViewFactoryWithCreateFlag::makeSubView (const CSMWorld::UniversalId& id, - CSMDoc::Document& document) + template + CSVDoc::SubView *SubViewFactoryWithCreator::makeSubView ( + const CSMWorld::UniversalId& id, CSMDoc::Document& document) { - return new SubViewT (id, document, mCreateAndDelete); + return new SubViewT (id, document, CreatorFactoryT()); } } diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 63fab56db..b3374c4a5 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -78,6 +78,11 @@ void CSVDoc::View::setupViewMenu() QAction *newWindow = new QAction (tr ("&New View"), this); connect (newWindow, SIGNAL (triggered()), this, SLOT (newView())); view->addAction (newWindow); + + mShowStatusBar = new QAction (tr ("Show Status Bar"), this); + mShowStatusBar->setCheckable (true); + connect (mShowStatusBar, SIGNAL (toggled (bool)), this, SLOT (toggleShowStatusBar (bool))); + view->addAction (mShowStatusBar); } void CSVDoc::View::setupWorldMenu() @@ -282,6 +287,9 @@ 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) SubView *view = mSubViewFactory.makeSubView (id, *mDocument); + + view->setStatusBar (mShowStatusBar->isChecked()); + mSubViewWindow.addDockWidget (Qt::TopDockWidgetArea, view); connect (view, SIGNAL (focusId (const CSMWorld::UniversalId&)), this, @@ -436,3 +444,12 @@ void CSVDoc::View::updateEditorSetting (const QString &settingName, const QStrin else if (settingName == "Height") resizeViewHeight (settingValue.toInt()); } + +void CSVDoc::View::toggleShowStatusBar (bool show) +{ + foreach (QObject *view, mSubViewWindow.children()) + { + if (CSVDoc::SubView *subView = dynamic_cast (view)) + subView->setStatusBar (show); + } +} diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index 4e6b216a1..c0163ce61 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -38,6 +38,7 @@ namespace CSVDoc QAction *mRedo; QAction *mSave; QAction *mVerify; + QAction *mShowStatusBar; std::vector mEditingActions; Operations *mOperations; SubViewFactoryManager mSubViewFactory; @@ -158,6 +159,8 @@ namespace CSVDoc void addRegionMapSubView(); void showUserSettings(); + + void toggleShowStatusBar (bool show); }; } diff --git a/apps/opencs/view/world/cellcreator.cpp b/apps/opencs/view/world/cellcreator.cpp new file mode 100644 index 000000000..74fb06870 --- /dev/null +++ b/apps/opencs/view/world/cellcreator.cpp @@ -0,0 +1,81 @@ + +#include "cellcreator.hpp" + +#include +#include + +#include +#include +#include + +std::string CSVWorld::CellCreator::getId() const +{ + if (mType->currentIndex()==0) + return GenericCreator::getId(); + + std::ostringstream stream; + + stream << "#" << mX->value() << " " << mY->value(); + + return stream.str(); +} + +CSVWorld::CellCreator::CellCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id) +: GenericCreator (data, undoStack, id) +{ + mY = new QSpinBox (this); + mY->setVisible (false); + mY->setMinimum (std::numeric_limits::min()); + mY->setMaximum (std::numeric_limits::max()); + connect (mY, SIGNAL (valueChanged (int)), this, SLOT (valueChanged (int))); + insertAtBeginning (mY, true); + + mYLabel = new QLabel ("Y", this); + mYLabel->setVisible (false); + insertAtBeginning (mYLabel, false); + + mX = new QSpinBox (this); + mX->setVisible (false); + mX->setMinimum (std::numeric_limits::min()); + mX->setMaximum (std::numeric_limits::max()); + connect (mX, SIGNAL (valueChanged (int)), this, SLOT (valueChanged (int))); + insertAtBeginning (mX, true); + + mXLabel = new QLabel ("X", this); + mXLabel->setVisible (false); + insertAtBeginning (mXLabel, false); + + mType = new QComboBox (this); + + mType->addItem ("Interior Cell"); + mType->addItem ("Exterior Cell"); + + connect (mType, SIGNAL (currentIndexChanged (int)), this, SLOT (setType (int))); + + insertAtBeginning (mType, false); +} + +void CSVWorld::CellCreator::reset() +{ + mX->setValue (0); + mY->setValue (0); + mType->setCurrentIndex (0); + GenericCreator::reset(); +} + +void CSVWorld::CellCreator::setType (int index) +{ + setManualEditing (index==0); + mXLabel->setVisible (index==1); + mX->setVisible (index==1); + mYLabel->setVisible (index==1); + mY->setVisible (index==1); + + update(); +} + +void CSVWorld::CellCreator::valueChanged (int index) +{ + update(); +} \ No newline at end of file diff --git a/apps/opencs/view/world/cellcreator.hpp b/apps/opencs/view/world/cellcreator.hpp new file mode 100644 index 000000000..a5473e2c9 --- /dev/null +++ b/apps/opencs/view/world/cellcreator.hpp @@ -0,0 +1,40 @@ +#ifndef CSV_WORLD_CELLCREATOR_H +#define CSV_WORLD_CELLCREATOR_H + +class QLabel; +class QSpinBox; +class QComboBox; + +#include "genericcreator.hpp" + +namespace CSVWorld +{ + class CellCreator : public GenericCreator + { + Q_OBJECT + + QComboBox *mType; + QLabel *mXLabel; + QSpinBox *mX; + QLabel *mYLabel; + QSpinBox *mY; + + protected: + + virtual std::string getId() const; + + public: + + CellCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id); + + virtual void reset(); + + private slots: + + void setType (int index); + + void valueChanged (int index); + }; +} + +#endif diff --git a/apps/opencs/view/world/creator.cpp b/apps/opencs/view/world/creator.cpp new file mode 100644 index 000000000..d753a2b47 --- /dev/null +++ b/apps/opencs/view/world/creator.cpp @@ -0,0 +1,13 @@ + +#include "creator.hpp" + +CSVWorld::Creator:: ~Creator() {} + +CSVWorld::CreatorFactoryBase::~CreatorFactoryBase() {} + + +CSVWorld::Creator *CSVWorld::NullCreatorFactory::makeCreator (CSMWorld::Data& data, + QUndoStack& undoStack, const CSMWorld::UniversalId& id) const +{ + return 0; +} \ No newline at end of file diff --git a/apps/opencs/view/world/creator.hpp b/apps/opencs/view/world/creator.hpp new file mode 100644 index 000000000..df9b116ee --- /dev/null +++ b/apps/opencs/view/world/creator.hpp @@ -0,0 +1,86 @@ +#ifndef CSV_WORLD_CREATOR_H +#define CSV_WORLD_CREATOR_H + +#include + +class QUndoStack; + +namespace CSMWorld +{ + class Data; + class UniversalId; +} + +namespace CSVWorld +{ + /// \brief Record creator UI base class + class Creator : public QWidget + { + Q_OBJECT + + public: + + virtual ~Creator(); + + virtual void reset() = 0; + + virtual void setEditLock (bool locked) = 0; + + signals: + + void done(); + + void requestFocus (const std::string& id); + ///< Request owner of this creator to focus the just created \a id. The owner may + /// ignore this request. + }; + + /// \brief Base class for Creator factory + class CreatorFactoryBase + { + public: + + virtual ~CreatorFactoryBase(); + + virtual Creator *makeCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id) const = 0; + ///< The ownership of the returned Creator is transferred to the caller. + /// + /// \note The function can return a 0-pointer, which means no UI for creating/deleting + /// records should be provided. + }; + + /// \brief Creator factory that does not produces any creator + class NullCreatorFactory : public CreatorFactoryBase + { + public: + + virtual Creator *makeCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id) const; + ///< The ownership of the returned Creator is transferred to the caller. + /// + /// \note The function always returns 0. + }; + + template + class CreatorFactory : public CreatorFactoryBase + { + public: + + virtual Creator *makeCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id) const; + ///< The ownership of the returned Creator is transferred to the caller. + /// + /// \note The function can return a 0-pointer, which means no UI for creating/deleting + /// records should be provided. + }; + + template + Creator *CreatorFactory::makeCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id) const + { + return new CreatorT (data, undoStack, id); + } +} + +#endif \ No newline at end of file diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp new file mode 100644 index 000000000..7502b6555 --- /dev/null +++ b/apps/opencs/view/world/genericcreator.cpp @@ -0,0 +1,135 @@ + +#include "genericcreator.hpp" + +#include + +#include +#include +#include +#include + +#include "../../model/world/commands.hpp" +#include "../../model/world/data.hpp" +#include "../../model/world/idtable.hpp" + +#include "idvalidator.hpp" + +void CSVWorld::GenericCreator::update() +{ + mErrors = getErrors(); + + mCreate->setToolTip (QString::fromUtf8 (mErrors.c_str())); + mId->setToolTip (QString::fromUtf8 (mErrors.c_str())); + + mCreate->setEnabled (mErrors.empty() && !mLocked); +} + +void CSVWorld::GenericCreator::setManualEditing (bool enabled) +{ + mId->setVisible (enabled); +} + +void CSVWorld::GenericCreator::insertAtBeginning (QWidget *widget, bool stretched) +{ + mLayout->insertWidget (0, widget, stretched ? 1 : 0); +} + +void CSVWorld::GenericCreator::insertBeforeButtons (QWidget *widget, bool stretched) +{ + mLayout->insertWidget (mLayout->count()-2, widget, stretched ? 1 : 0); +} + +std::string CSVWorld::GenericCreator::getId() const +{ + return mId->text().toUtf8().constData(); +} + +void CSVWorld::GenericCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const {} + +const CSMWorld::Data& CSVWorld::GenericCreator::getData() const +{ + return mData; +} + +CSMWorld::Data& CSVWorld::GenericCreator::getData() +{ + return mData; +} + +CSVWorld::GenericCreator::GenericCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id) +: mData (data), mUndoStack (undoStack), mListId (id), mLocked (false) +{ + mLayout = new QHBoxLayout; + mLayout->setContentsMargins (0, 0, 0, 0); + + mId = new QLineEdit; + mId->setValidator (new IdValidator (this)); + mLayout->addWidget (mId, 1); + + mCreate = new QPushButton ("Create"); + mLayout->addWidget (mCreate); + + QPushButton *cancelButton = new QPushButton ("Cancel"); + mLayout->addWidget (cancelButton); + + setLayout (mLayout); + + connect (cancelButton, SIGNAL (clicked (bool)), this, SIGNAL (done())); + connect (mCreate, SIGNAL (clicked (bool)), this, SLOT (create())); + + connect (mId, SIGNAL (textChanged (const QString&)), this, SLOT (textChanged (const QString&))); +} + +void CSVWorld::GenericCreator::setEditLock (bool locked) +{ + mLocked = locked; + update(); +} + +void CSVWorld::GenericCreator::reset() +{ + mId->setText (""); + update(); +} + +std::string CSVWorld::GenericCreator::getErrors() const +{ + std::string errors; + + std::string id = getId(); + + if (id.empty()) + { + errors = "Missing ID"; + } + else if (mData.hasId (id)) + { + errors = "ID is already in use"; + } + + return errors; +} + +void CSVWorld::GenericCreator::textChanged (const QString& text) +{ + update(); +} + +void CSVWorld::GenericCreator::create() +{ + if (!mLocked) + { + std::string id = getId(); + + std::auto_ptr command (new CSMWorld::CreateCommand ( + dynamic_cast (*mData.getTableModel (mListId)), id)); + + configureCreateCommand (*command); + + mUndoStack.push (command.release()); + + emit done(); + emit requestFocus (id); + } +} \ No newline at end of file diff --git a/apps/opencs/view/world/genericcreator.hpp b/apps/opencs/view/world/genericcreator.hpp new file mode 100644 index 000000000..5409f0839 --- /dev/null +++ b/apps/opencs/view/world/genericcreator.hpp @@ -0,0 +1,73 @@ +#ifndef CSV_WORLD_GENERICCREATOR_H +#define CSV_WORLD_GENERICCREATOR_H + +class QPushButton; +class QLineEdit; +class QHBoxLayout; + +#include "creator.hpp" + +#include "../../model/world/universalid.hpp" + +namespace CSMWorld +{ + class CreateCommand; +} + +namespace CSVWorld +{ + class GenericCreator : public Creator + { + Q_OBJECT + + CSMWorld::Data& mData; + QUndoStack& mUndoStack; + CSMWorld::UniversalId mListId; + QPushButton *mCreate; + QLineEdit *mId; + std::string mErrors; + QHBoxLayout *mLayout; + bool mLocked; + + protected: + + void update(); + + virtual void setManualEditing (bool enabled); + ///< Enable/disable manual ID editing (enabled by default). + + void insertAtBeginning (QWidget *widget, bool stretched); + + void insertBeforeButtons (QWidget *widget, bool stretched); + + virtual std::string getId() const; + + virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const; + + const CSMWorld::Data& getData() const; + + CSMWorld::Data& getData(); + + public: + + GenericCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id); + + virtual void setEditLock (bool locked); + + virtual void reset(); + + virtual std::string getErrors() const; + ///< Return formatted error descriptions for the current state of the creator. if an empty + /// string is returned, there is no error. + + + private slots: + + void textChanged (const QString& text); + + void create(); + }; +} + +#endif \ No newline at end of file diff --git a/apps/opencs/view/world/idvalidator.cpp b/apps/opencs/view/world/idvalidator.cpp new file mode 100644 index 000000000..cf6e5d77b --- /dev/null +++ b/apps/opencs/view/world/idvalidator.cpp @@ -0,0 +1,26 @@ + +#include "idvalidator.hpp" + +bool CSVWorld::IdValidator::isValid (const QChar& c, bool first) const +{ + if (c.isLetter() || c=='_') + return true; + + if (!first && (c.isDigit() || c.isSpace())) + return true; + + return false; +} + +CSVWorld::IdValidator::IdValidator (QObject *parent) : QValidator (parent) {} + +QValidator::State CSVWorld::IdValidator::validate (QString& input, int& pos) const +{ + bool first = true; + + for (QString::const_iterator iter (input.begin()); iter!=input.end(); ++iter, first = false) + if (!isValid (*iter, first)) + return QValidator::Invalid; + + return QValidator::Acceptable; +} \ No newline at end of file diff --git a/apps/opencs/view/world/idvalidator.hpp b/apps/opencs/view/world/idvalidator.hpp new file mode 100644 index 000000000..db0ecb27a --- /dev/null +++ b/apps/opencs/view/world/idvalidator.hpp @@ -0,0 +1,23 @@ +#ifndef CSV_WORLD_IDVALIDATOR_H +#define CSV_WORLD_IDVALIDATOR_H + +#include + +namespace CSVWorld +{ + class IdValidator : public QValidator + { + private: + + bool isValid (const QChar& c, bool first) const; + + public: + + IdValidator (QObject *parent = 0); + + virtual State validate (QString& input, int& pos) const; + + }; +} + +#endif diff --git a/apps/opencs/view/world/referenceablecreator.cpp b/apps/opencs/view/world/referenceablecreator.cpp new file mode 100644 index 000000000..718fe9ca7 --- /dev/null +++ b/apps/opencs/view/world/referenceablecreator.cpp @@ -0,0 +1,43 @@ + +#include "referenceablecreator.hpp" + +#include +#include + +#include "../../model/world/universalid.hpp" +#include "../../model/world/commands.hpp" + +void CSVWorld::ReferenceableCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const +{ + command.setType ( + static_cast (mType->itemData (mType->currentIndex()).toInt())); +} + +CSVWorld::ReferenceableCreator::ReferenceableCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id) +: GenericCreator (data, undoStack, id) +{ + QLabel *label = new QLabel ("Type", this); + insertBeforeButtons (label, false); + + std::vector types = CSMWorld::UniversalId::listReferenceableTypes(); + + mType = new QComboBox (this); + + for (std::vector::const_iterator iter (types.begin()); + iter!=types.end(); ++iter) + { + CSMWorld::UniversalId id (*iter, ""); + + mType->addItem (QIcon (id.getIcon().c_str()), id.getTypeName().c_str(), + static_cast (id.getType())); + } + + insertBeforeButtons (mType, false); +} + +void CSVWorld::ReferenceableCreator::reset() +{ + mType->setCurrentIndex (0); + GenericCreator::reset(); +} \ No newline at end of file diff --git a/apps/opencs/view/world/referenceablecreator.hpp b/apps/opencs/view/world/referenceablecreator.hpp new file mode 100644 index 000000000..06e0e582b --- /dev/null +++ b/apps/opencs/view/world/referenceablecreator.hpp @@ -0,0 +1,30 @@ +#ifndef CSV_WORLD_REFERENCEABLECREATOR_H +#define CSV_WORLD_REFERENCEABLECREATOR_H + +class QComboBox; + +#include "genericcreator.hpp" + +namespace CSVWorld +{ + class ReferenceableCreator : public GenericCreator + { + Q_OBJECT + + QComboBox *mType; + + private: + + virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const; + + public: + + ReferenceableCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id); + + virtual void reset(); + + }; +} + +#endif diff --git a/apps/opencs/view/world/referencecreator.cpp b/apps/opencs/view/world/referencecreator.cpp new file mode 100644 index 000000000..48a168a35 --- /dev/null +++ b/apps/opencs/view/world/referencecreator.cpp @@ -0,0 +1,70 @@ + +#include "referencecreator.hpp" + +#include +#include + +#include "../../model/world/data.hpp" +#include "../../model/world/commands.hpp" + +std::string CSVWorld::ReferenceCreator::getId() const +{ + return mId; +} + +void CSVWorld::ReferenceCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const +{ + /// \todo avoid using hard-coded column numbers + command.addValue (2, mCell->text()); +} + +CSVWorld::ReferenceCreator::ReferenceCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id) +: GenericCreator (data, undoStack, id) +{ + QLabel *label = new QLabel ("Cell", this); + insertBeforeButtons (label, false); + + mCell = new QLineEdit (this); + insertBeforeButtons (mCell, true); + + setManualEditing (false); + + connect (mCell, SIGNAL (textChanged (const QString&)), this, SLOT (cellChanged())); +} + +void CSVWorld::ReferenceCreator::reset() +{ + mCell->setText (""); + mId = getData().getReferences().getNewId(); + GenericCreator::reset(); +} + +std::string CSVWorld::ReferenceCreator::getErrors() const +{ + std::string errors = GenericCreator::getErrors(); + + std::string cell = mCell->text().toUtf8().constData(); + + if (cell.empty()) + { + if (!errors.empty()) + errors += "
"; + + errors += "Missing Cell ID"; + } + else if (getData().getCells().searchId (cell)==-1) + { + if (!errors.empty()) + errors += "
"; + + errors += "Invalid Cell ID"; + } + + return errors; +} + +void CSVWorld::ReferenceCreator::cellChanged() +{ + update(); +} \ No newline at end of file diff --git a/apps/opencs/view/world/referencecreator.hpp b/apps/opencs/view/world/referencecreator.hpp new file mode 100644 index 000000000..27f81564f --- /dev/null +++ b/apps/opencs/view/world/referencecreator.hpp @@ -0,0 +1,40 @@ +#ifndef CSV_WORLD_REFERENCECREATOR_H +#define CSV_WORLD_REFERENCECREATOR_H + +#include "genericcreator.hpp" + +class QLineEdit; + +namespace CSVWorld +{ + class ReferenceCreator : public GenericCreator + { + Q_OBJECT + + QLineEdit *mCell; + std::string mId; + + private: + + virtual std::string getId() const; + + virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const; + + public: + + ReferenceCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id); + + virtual void reset(); + + virtual std::string getErrors() const; + ///< Return formatted error descriptions for the current state of the creator. if an empty + /// string is returned, there is no error. + + private slots: + + void cellChanged(); + }; +} + +#endif diff --git a/apps/opencs/view/world/refidtypedelegate.cpp b/apps/opencs/view/world/refidtypedelegate.cpp index 40bf88584..bf3acbb20 100755 --- a/apps/opencs/view/world/refidtypedelegate.cpp +++ b/apps/opencs/view/world/refidtypedelegate.cpp @@ -27,26 +27,15 @@ CSVWorld::RefIdTypeDelegateFactory::UidTypeList CSVWorld::RefIdTypeDelegateFacto { UidTypeList list; - list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Activator, ":./activator.png")); - list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Potion, ":./potion.png")); - list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Apparatus, ":./apparatus.png")); - list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Armor, ":./armor.png")); - list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Book, ":./book.png")); - list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Clothing, ":./clothing.png")); - list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Container, ":./container.png")); - list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Creature, ":./creature.png")); - list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Door, ":./door.png")); - list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Ingredient, ":./ingredient.png")); - list.push_back (std::make_pair (CSMWorld::UniversalId::Type_CreatureLevelledList, ":./creature.png")); - list.push_back (std::make_pair (CSMWorld::UniversalId::Type_ItemLevelledList, ":./item.png")); - list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Light, ":./light.png")); - list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Lockpick, ":./lockpick.png")); - list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Miscellaneous, ":./misc.png")); - list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Npc, ":./npc.png")); - list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Probe, ":./probe.png")); - list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Repair, ":./repair.png")); - list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Static, ":./static.png")); - list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Weapon, ":./weapon.png")); + std::vector types = CSMWorld::UniversalId::listReferenceableTypes(); + + for (std::vector::const_iterator iter (types.begin()); + iter!=types.end(); ++iter) + { + CSMWorld::UniversalId id (*iter, ""); + + list.push_back (std::make_pair (id.getType(), id.getIcon().c_str())); + } return list; } diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp index 1c06ea2f6..8990e2c72 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -7,14 +7,20 @@ #include "dialoguesubview.hpp" #include "scriptsubview.hpp" #include "regionmapsubview.hpp" +#include "genericcreator.hpp" +#include "cellcreator.hpp" +#include "referenceablecreator.hpp" +#include "referencecreator.hpp" void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) { + // Regular record tables (including references which are actually sub-records, but are promoted + // to top-level records within the editor) manager.add (CSMWorld::UniversalId::Type_Gmsts, - new CSVDoc::SubViewFactoryWithCreateFlag (false)); + new CSVDoc::SubViewFactoryWithCreator); manager.add (CSMWorld::UniversalId::Type_Skills, - new CSVDoc::SubViewFactoryWithCreateFlag (false)); + new CSVDoc::SubViewFactoryWithCreator); static const CSMWorld::UniversalId::Type sTableTypes[] = { @@ -27,20 +33,26 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) CSMWorld::UniversalId::Type_Regions, CSMWorld::UniversalId::Type_Birthsigns, CSMWorld::UniversalId::Type_Spells, - CSMWorld::UniversalId::Type_Cells, - CSMWorld::UniversalId::Type_Referenceables, - CSMWorld::UniversalId::Type_References, CSMWorld::UniversalId::Type_None // end marker }; for (int i=0; sTableTypes[i]!=CSMWorld::UniversalId::Type_None; ++i) - manager.add (sTableTypes[i], new CSVDoc::SubViewFactoryWithCreateFlag (true)); + manager.add (sTableTypes[i], + new CSVDoc::SubViewFactoryWithCreator >); + manager.add (CSMWorld::UniversalId::Type_Cells, + new CSVDoc::SubViewFactoryWithCreator >); + + manager.add (CSMWorld::UniversalId::Type_Referenceables, + new CSVDoc::SubViewFactoryWithCreator >); + + manager.add (CSMWorld::UniversalId::Type_References, + new CSVDoc::SubViewFactoryWithCreator >); + + // Subviews for editing/viewing individual records manager.add (CSMWorld::UniversalId::Type_Script, new CSVDoc::SubViewFactory); + // Other stuff (combined record tables) manager.add (CSMWorld::UniversalId::Type_RegionMap, new CSVDoc::SubViewFactory); - -// manager.add (CSMWorld::UniversalId::Type_Global, -// new CSVDoc::SubViewFactoryWithCreateFlag (true)); } \ No newline at end of file diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 90ce42b34..fdbf67e42 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -44,19 +44,30 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) std::vector CSVWorld::Table::listRevertableSelectedIds() const { - QModelIndexList selectedRows = selectionModel()->selectedRows(); - + /// \todo Do not use hardcoded column numbers std::vector revertableIds; - for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); ++iter) + if (mProxyModel->columnCount()>0) { - std::string id = mProxyModel->data (*iter).toString().toStdString(); + QModelIndexList selectedRows = selectionModel()->selectedRows(); - CSMWorld::RecordBase::State state = - static_cast (mModel->data (mModel->getModelIndex (id, 1)).toInt()); + for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); + ++iter) + { + QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)); - if (state!=CSMWorld::RecordBase::State_BaseOnly) - revertableIds.push_back (id); + CSMWorld::RecordBase::State state = + static_cast ( + mModel->data (mModel->index (index.row(), 1)).toInt()); + + if (state!=CSMWorld::RecordBase::State_BaseOnly) + { + std::string id = mModel->data (mModel->index (index.row(), 0)). + toString().toUtf8().constData(); + + revertableIds.push_back (id); + } + } } return revertableIds; @@ -64,19 +75,30 @@ std::vector CSVWorld::Table::listRevertableSelectedIds() const std::vector CSVWorld::Table::listDeletableSelectedIds() const { - QModelIndexList selectedRows = selectionModel()->selectedRows(); - + /// \todo Do not use hardcoded column numbers std::vector deletableIds; - for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); ++iter) + if (mProxyModel->columnCount()>0) { - std::string id = mProxyModel->data (*iter).toString().toStdString(); + QModelIndexList selectedRows = selectionModel()->selectedRows(); - CSMWorld::RecordBase::State state = - static_cast (mModel->data (mModel->getModelIndex (id, 1)).toInt()); + for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); + ++iter) + { + QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)); - if (state!=CSMWorld::RecordBase::State_Deleted) - deletableIds.push_back (id); + CSMWorld::RecordBase::State state = + static_cast ( + mModel->data (mModel->index (index.row(), 1)).toInt()); + + if (state!=CSMWorld::RecordBase::State_Deleted) + { + std::string id = mModel->data (mModel->index (index.row(), 0)). + toString().toUtf8().constData(); + + deletableIds.push_back (id); + } + } } return deletableIds; @@ -126,7 +148,7 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q if (createAndDelete) { mCreateAction = new QAction (tr ("Add Record"), this); - connect (mCreateAction, SIGNAL (triggered()), this, SLOT (createRecord())); + connect (mCreateAction, SIGNAL (triggered()), this, SIGNAL (createRequest())); addAction (mCreateAction); } @@ -137,6 +159,17 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q mDeleteAction = new QAction (tr ("Delete Record"), this); connect (mDeleteAction, SIGNAL (triggered()), this, SLOT (deleteRecord())); addAction (mDeleteAction); + + 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) @@ -154,22 +187,6 @@ CSMWorld::UniversalId CSVWorld::Table::getUniversalId (int row) const mProxyModel->data (mProxyModel->index (row, 0)).toString().toStdString()); } -#include /// \todo remove -void CSVWorld::Table::createRecord() -{ - if (!mEditLock) - { - /// \todo ask the user for an ID instead. - static int index = 0; - - std::ostringstream stream; - stream << "id" << index++; - - mUndoStack.push (new CSMWorld::CreateCommand (*mProxyModel, stream.str())); - } - -} - void CSVWorld::Table::revertRecord() { if (!mEditLock) @@ -231,3 +248,46 @@ void CSVWorld::Table::updateEditorSetting (const QString &settingName, const QSt 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 (mModel->columnCount()>0) + { + int rows = mModel->rowCount(); + + for (int i=0; imapToSource (mProxyModel->index (i, 0)); + + /// \todo Do not use hardcoded column numbers + int state = mModel->data (mModel->index (index.row(), 1)).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); +} \ No newline at end of file diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index eedfeb8a1..0c24e7b54 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -60,15 +60,31 @@ namespace CSVWorld void editRequest (int row); - private slots: + void selectionSizeChanged (int size); - void createRecord(); + void tableSizeChanged (int size, int deleted, int modified); + ///< \param size Number of not deleted records + /// \param deleted Number of deleted records + /// \param modified Number of added and modified records + + void createRequest(); + + private slots: void revertRecord(); void deleteRecord(); void editRecord(); + + public slots: + + void tableSizeUpdate(); + + void selectionSizeUpdate(); + + void requestFocus (const std::string& id); + }; } diff --git a/apps/opencs/view/world/tablebottombox.cpp b/apps/opencs/view/world/tablebottombox.cpp new file mode 100644 index 000000000..6cf21a132 --- /dev/null +++ b/apps/opencs/view/world/tablebottombox.cpp @@ -0,0 +1,156 @@ + +#include "tablebottombox.hpp" + +#include + +#include +#include +#include + +#include "creator.hpp" + +void CSVWorld::TableBottomBox::updateStatus() +{ + if (mShowStatusBar) + { + static const char *sLabels[4] = { "record", "deleted", "touched", "selected" }; + static const char *sLabelsPlural[4] = { "records", "deleted", "touched", "selected" }; + + std::ostringstream stream; + + bool first = true; + + for (int i=0; i<4; ++i) + { + if (mStatusCount[i]>0) + { + if (first) + first = false; + else + stream << ", "; + + stream + << mStatusCount[i] << ' ' + << (mStatusCount[i]==1 ? sLabels[i] : sLabelsPlural[i]); + } + } + + mStatus->setText (QString::fromUtf8 (stream.str().c_str())); + } +} + +CSVWorld::TableBottomBox::TableBottomBox (const CreatorFactoryBase& creatorFactory, + CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id, QWidget *parent) +: QWidget (parent), mShowStatusBar (false), mCreating (false) +{ + for (int i=0; i<4; ++i) + mStatusCount[i] = 0; + + setVisible (false); + + mLayout = new QStackedLayout; + mLayout->setContentsMargins (0, 0, 0, 0); + + mStatus = new QLabel; + + mStatusBar = new QStatusBar; + + mStatusBar->addWidget (mStatus); + + mLayout->addWidget (mStatusBar); + + setLayout (mLayout); + + mCreator = creatorFactory.makeCreator (data, undoStack, id); + + mLayout->addWidget (mCreator); + + connect (mCreator, SIGNAL (done()), this, SLOT (createRequestDone())); + + connect (mCreator, SIGNAL (requestFocus (const std::string&)), + this, SIGNAL (requestFocus (const std::string&))); +} + +void CSVWorld::TableBottomBox::setEditLock (bool locked) +{ + if (mCreator) + mCreator->setEditLock (locked); +} + +CSVWorld::TableBottomBox::~TableBottomBox() +{ + delete mCreator; +} + +void CSVWorld::TableBottomBox::setStatusBar (bool show) +{ + if (show!=mShowStatusBar) + { + setVisible (show || mCreating); + + mShowStatusBar = show; + + if (show) + updateStatus(); + } +} + +bool CSVWorld::TableBottomBox::canCreateAndDelete() const +{ + return mCreator; +} + +void CSVWorld::TableBottomBox::createRequestDone() +{ + if (!mShowStatusBar) + setVisible (false); + else + updateStatus(); + + mLayout->setCurrentWidget (mStatusBar); + + mCreating = false; +} + +void CSVWorld::TableBottomBox::selectionSizeChanged (int size) +{ + if (mStatusCount[3]!=size) + { + mStatusCount[3] = size; + updateStatus(); + } +} + +void CSVWorld::TableBottomBox::tableSizeChanged (int size, int deleted, int modified) +{ + bool changed = false; + + if (mStatusCount[0]!=size) + { + mStatusCount[0] = size; + changed = true; + } + + if (mStatusCount[1]!=deleted) + { + mStatusCount[1] = deleted; + changed = true; + } + + if (mStatusCount[2]!=modified) + { + mStatusCount[2] = modified; + changed = true; + } + + if (changed) + updateStatus(); +} + +void CSVWorld::TableBottomBox::createRequest() +{ + mCreator->reset(); + mLayout->setCurrentWidget (mCreator); + setVisible (true); + mCreating = true; +} \ No newline at end of file diff --git a/apps/opencs/view/world/tablebottombox.hpp b/apps/opencs/view/world/tablebottombox.hpp new file mode 100644 index 000000000..a5ae5e0bd --- /dev/null +++ b/apps/opencs/view/world/tablebottombox.hpp @@ -0,0 +1,82 @@ +#ifndef CSV_WORLD_BOTTOMBOX_H +#define CSV_WORLD_BOTTOMBOX_H + +#include + +class QLabel; +class QStackedLayout; +class QStatusBar; +class QUndoStack; + +namespace CSMWorld +{ + class Data; + class UniversalId; +} + +namespace CSVWorld +{ + class CreatorFactoryBase; + class Creator; + + class TableBottomBox : public QWidget + { + Q_OBJECT + + bool mShowStatusBar; + QLabel *mStatus; + QStatusBar *mStatusBar; + int mStatusCount[4]; + Creator *mCreator; + bool mCreating; + QStackedLayout *mLayout; + + private: + + // not implemented + TableBottomBox (const TableBottomBox&); + TableBottomBox& operator= (const TableBottomBox&); + + void updateStatus(); + + public: + + TableBottomBox (const CreatorFactoryBase& creatorFactory, CSMWorld::Data& data, + QUndoStack& undoStack, const CSMWorld::UniversalId& id, QWidget *parent = 0); + + virtual ~TableBottomBox(); + + void setEditLock (bool locked); + + void setStatusBar (bool show); + + bool canCreateAndDelete() const; + ///< Is record creation and deletion supported? + /// + /// \note The BotomBox does not partake in the deletion of records. + + signals: + + void requestFocus (const std::string& id); + ///< Request owner of this box to focus the just created \a id. The owner may + /// ignore this request. + + private slots: + + void createRequestDone(); + ///< \note This slot being called does not imply success. + + public slots: + + void selectionSizeChanged (int size); + + void tableSizeChanged (int size, int deleted, int modified); + ///< \param size Number of not deleted records + /// \param deleted Number of deleted records + /// \param modified Number of added and modified records + + void createRequest(); + }; +} + +#endif diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 65cba4b02..af3d186e8 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -1,22 +1,55 @@ #include "tablesubview.hpp" +#include + #include "../../model/doc/document.hpp" #include "table.hpp" +#include "tablebottombox.hpp" +#include "creator.hpp" CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, - bool createAndDelete) + const CreatorFactoryBase& creatorFactory) : SubView (id) { - setWidget (mTable = new Table (id, document.getData(), document.getUndoStack(), createAndDelete)); + QVBoxLayout *layout = new QVBoxLayout; + + layout->setContentsMargins (QMargins (0, 0, 0, 0)); + + layout->addWidget (mBottom = + new TableBottomBox (creatorFactory, document.getData(), document.getUndoStack(), id, this), 0); + + layout->insertWidget (0, mTable = + new Table (id, document.getData(), document.getUndoStack(), mBottom->canCreateAndDelete()), 2); + + QWidget *widget = new QWidget; + + widget->setLayout (layout); + + setWidget (widget); connect (mTable, SIGNAL (editRequest (int)), this, SLOT (editRequest (int))); + + connect (mTable, SIGNAL (selectionSizeChanged (int)), + mBottom, SLOT (selectionSizeChanged (int))); + connect (mTable, SIGNAL (tableSizeChanged (int, int, int)), + mBottom, SLOT (tableSizeChanged (int, int, int))); + + mTable->tableSizeUpdate(); + mTable->selectionSizeUpdate(); + + if (mBottom->canCreateAndDelete()) + connect (mTable, SIGNAL (createRequest()), mBottom, SLOT (createRequest())); + + connect (mBottom, SIGNAL (requestFocus (const std::string&)), + mTable, SLOT (requestFocus (const std::string&))); } void CSVWorld::TableSubView::setEditLock (bool locked) { mTable->setEditLock (locked); + mBottom->setEditLock (locked); } void CSVWorld::TableSubView::editRequest (int row) @@ -28,3 +61,8 @@ void CSVWorld::TableSubView::updateEditorSetting(const QString &settingName, con { mTable->updateEditorSetting(settingName, settingValue); } + +void CSVWorld::TableSubView::setStatusBar (bool show) +{ + mBottom->setStatusBar (show); +} \ No newline at end of file diff --git a/apps/opencs/view/world/tablesubview.hpp b/apps/opencs/view/world/tablesubview.hpp index ccee50dbc..d61c78935 100644 --- a/apps/opencs/view/world/tablesubview.hpp +++ b/apps/opencs/view/world/tablesubview.hpp @@ -13,18 +13,26 @@ namespace CSMDoc namespace CSVWorld { class Table; + class TableBottomBox; + class CreatorFactoryBase; class TableSubView : public CSVDoc::SubView { Q_OBJECT Table *mTable; + TableBottomBox *mBottom; public: - TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, bool createAndDelete); + TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, + const CreatorFactoryBase& creatorFactory); + virtual void setEditLock (bool locked); - void updateEditorSetting (const QString &, const QString &); + + virtual void updateEditorSetting (const QString& key, const QString& value); + + virtual void setStatusBar (bool show); private slots: