diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index f78c411d2..ec4737d16 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -26,7 +26,7 @@ opencs_units_noqt (model/world universalid record commands columnbase columnimp 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 metadata defaultgmsts infoselectwrapper + idcompletionmanager metadata defaultgmsts infoselectwrapper commandmacro ) opencs_hdrs_noqt (model/world @@ -85,7 +85,7 @@ opencs_units (view/widget opencs_units (view/render scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget - previewwidget editmode instancemode instanceselectionmode + previewwidget editmode instancemode instanceselectionmode instancemovemode ) opencs_units_noqt (view/render diff --git a/apps/opencs/model/world/cellcoordinates.cpp b/apps/opencs/model/world/cellcoordinates.cpp index 3ef3e6c69..80ec9dc04 100644 --- a/apps/opencs/model/world/cellcoordinates.cpp +++ b/apps/opencs/model/world/cellcoordinates.cpp @@ -1,5 +1,7 @@ #include "cellcoordinates.hpp" +#include + #include #include @@ -7,6 +9,9 @@ CSMWorld::CellCoordinates::CellCoordinates() : mX (0), mY (0) {} CSMWorld::CellCoordinates::CellCoordinates (int x, int y) : mX (x), mY (y) {} +CSMWorld::CellCoordinates::CellCoordinates (const std::pair& coordinates) +: mX (coordinates.first), mY (coordinates.second) {} + int CSMWorld::CellCoordinates::getX() const { return mX; @@ -49,6 +54,13 @@ std::pair CSMWorld::CellCoordinates::fromId ( return std::make_pair (CellCoordinates(), false); } +std::pair CSMWorld::CellCoordinates::coordinatesToCellIndex (float x, float y) +{ + const int cellSize = 8192; + + return std::make_pair (std::floor (x/cellSize), std::floor (y/cellSize)); +} + bool CSMWorld::operator== (const CellCoordinates& left, const CellCoordinates& right) { return left.getX()==right.getX() && left.getY()==right.getY(); diff --git a/apps/opencs/model/world/cellcoordinates.hpp b/apps/opencs/model/world/cellcoordinates.hpp index 63db60c29..f8851a6d9 100644 --- a/apps/opencs/model/world/cellcoordinates.hpp +++ b/apps/opencs/model/world/cellcoordinates.hpp @@ -3,6 +3,7 @@ #include #include +#include #include @@ -19,6 +20,8 @@ namespace CSMWorld CellCoordinates (int x, int y); + CellCoordinates (const std::pair& coordinates); + int getX() const; int getY() const; @@ -34,6 +37,8 @@ namespace CSMWorld /// /// \note The worldspace part of \a id is ignored static std::pair fromId (const std::string& id); + + static std::pair coordinatesToCellIndex (float x, float y); }; bool operator== (const CellCoordinates& left, const CellCoordinates& right); diff --git a/apps/opencs/model/world/commanddispatcher.cpp b/apps/opencs/model/world/commanddispatcher.cpp index a1fc980eb..ffbaa3dec 100644 --- a/apps/opencs/model/world/commanddispatcher.cpp +++ b/apps/opencs/model/world/commanddispatcher.cpp @@ -11,6 +11,7 @@ #include "record.hpp" #include "commands.hpp" #include "idtableproxymodel.hpp" +#include "commandmacro.hpp" std::vector CSMWorld::CommandDispatcher::getDeletableRecords() const { @@ -171,10 +172,9 @@ void CSMWorld::CommandDispatcher::executeModify (QAbstractItemModel *model, cons if (modifyCell.get()) { - mDocument.getUndoStack().beginMacro (modifyData->text()); - mDocument.getUndoStack().push (modifyData.release()); - mDocument.getUndoStack().push (modifyCell.release()); - mDocument.getUndoStack().endMacro(); + CommandMacro macro (mDocument.getUndoStack()); + macro.push (modifyData.release()); + macro.push (modifyCell.release()); } else mDocument.getUndoStack().push (modifyData.release()); @@ -194,9 +194,7 @@ void CSMWorld::CommandDispatcher::executeDelete() int columnIndex = model.findColumnIndex (Columns::ColumnId_Id); - if (rows.size()>1) - mDocument.getUndoStack().beginMacro (tr ("Delete multiple records")); - + CommandMacro macro (mDocument.getUndoStack(), rows.size()>1 ? "Delete multiple records" : ""); for (std::vector::const_iterator iter (rows.begin()); iter!=rows.end(); ++iter) { std::string id = model.data (model.getModelIndex (*iter, columnIndex)). @@ -204,7 +202,7 @@ void CSMWorld::CommandDispatcher::executeDelete() if (mId.getType() == UniversalId::Type_Referenceables) { - mDocument.getUndoStack().push ( new CSMWorld::DeleteCommand (model, id, + macro.push (new CSMWorld::DeleteCommand (model, id, static_cast(model.data (model.index ( model.getModelIndex (id, columnIndex).row(), model.findColumnIndex (CSMWorld::Columns::ColumnId_RecordType))).toInt()))); @@ -212,9 +210,6 @@ void CSMWorld::CommandDispatcher::executeDelete() else mDocument.getUndoStack().push (new CSMWorld::DeleteCommand (model, id)); } - - if (rows.size()>1) - mDocument.getUndoStack().endMacro(); } void CSMWorld::CommandDispatcher::executeRevert() @@ -231,25 +226,19 @@ void CSMWorld::CommandDispatcher::executeRevert() int columnIndex = model.findColumnIndex (Columns::ColumnId_Id); - if (rows.size()>1) - mDocument.getUndoStack().beginMacro (tr ("Revert multiple records")); - + CommandMacro macro (mDocument.getUndoStack(), rows.size()>1 ? "Revert multiple records" : ""); for (std::vector::const_iterator iter (rows.begin()); iter!=rows.end(); ++iter) { std::string id = model.data (model.getModelIndex (*iter, columnIndex)). toString().toUtf8().constData(); - mDocument.getUndoStack().push (new CSMWorld::RevertCommand (model, id)); + macro.push (new CSMWorld::RevertCommand (model, id)); } - - if (rows.size()>1) - mDocument.getUndoStack().endMacro(); } void CSMWorld::CommandDispatcher::executeExtendedDelete() { - if (mExtendedTypes.size()>1) - mDocument.getUndoStack().beginMacro (tr ("Extended delete of multiple records")); + CommandMacro macro (mDocument.getUndoStack(), mExtendedTypes.size()>1 ? tr ("Extended delete of multiple records") : ""); for (std::vector::const_iterator iter (mExtendedTypes.begin()); iter!=mExtendedTypes.end(); ++iter) @@ -276,20 +265,15 @@ void CSMWorld::CommandDispatcher::executeExtendedDelete() Misc::StringUtils::lowerCase (record.get().mCell))) continue; - mDocument.getUndoStack().push ( - new CSMWorld::DeleteCommand (model, record.get().mId)); + macro.push (new CSMWorld::DeleteCommand (model, record.get().mId)); } } } - - if (mExtendedTypes.size()>1) - mDocument.getUndoStack().endMacro(); } void CSMWorld::CommandDispatcher::executeExtendedRevert() { - if (mExtendedTypes.size()>1) - mDocument.getUndoStack().beginMacro (tr ("Extended revert of multiple records")); + CommandMacro macro (mDocument.getUndoStack(), mExtendedTypes.size()>1 ? tr ("Extended revert of multiple records") : ""); for (std::vector::const_iterator iter (mExtendedTypes.begin()); iter!=mExtendedTypes.end(); ++iter) @@ -313,12 +297,8 @@ void CSMWorld::CommandDispatcher::executeExtendedRevert() Misc::StringUtils::lowerCase (record.get().mCell))) continue; - mDocument.getUndoStack().push ( - new CSMWorld::RevertCommand (model, record.get().mId)); + macro.push (new CSMWorld::RevertCommand (model, record.get().mId)); } } } - - if (mExtendedTypes.size()>1) - mDocument.getUndoStack().endMacro(); } diff --git a/apps/opencs/model/world/commandmacro.cpp b/apps/opencs/model/world/commandmacro.cpp new file mode 100644 index 000000000..0bd74cbe2 --- /dev/null +++ b/apps/opencs/model/world/commandmacro.cpp @@ -0,0 +1,26 @@ + +#include "commandmacro.hpp" + +#include +#include + +CSMWorld::CommandMacro::CommandMacro (QUndoStack& undoStack, const QString& description) +: mUndoStack (undoStack), mDescription (description), mStarted (false) +{} + +CSMWorld::CommandMacro::~CommandMacro() +{ + if (mStarted) + mUndoStack.endMacro(); +} + +void CSMWorld::CommandMacro::push (QUndoCommand *command) +{ + if (!mStarted) + { + mUndoStack.beginMacro (mDescription.isEmpty() ? command->text() : mDescription); + mStarted = true; + } + + mUndoStack.push (command); +} diff --git a/apps/opencs/model/world/commandmacro.hpp b/apps/opencs/model/world/commandmacro.hpp new file mode 100644 index 000000000..b1f6301d9 --- /dev/null +++ b/apps/opencs/model/world/commandmacro.hpp @@ -0,0 +1,34 @@ +#ifndef CSM_WOLRD_COMMANDMACRO_H +#define CSM_WOLRD_COMMANDMACRO_H + +class QUndoStack; +class QUndoCommand; + +#include + +namespace CSMWorld +{ + class CommandMacro + { + QUndoStack& mUndoStack; + QString mDescription; + bool mStarted; + + /// not implemented + CommandMacro (const CommandMacro&); + + /// not implemented + CommandMacro& operator= (const CommandMacro&); + + public: + + /// If \a description is empty, the description of the first command is used. + CommandMacro (QUndoStack& undoStack, const QString& description = ""); + + ~CommandMacro(); + + void push (QUndoCommand *command); + }; +} + +#endif diff --git a/apps/opencs/model/world/ref.cpp b/apps/opencs/model/world/ref.cpp index 638f7ec9c..e6345ce71 100644 --- a/apps/opencs/model/world/ref.cpp +++ b/apps/opencs/model/world/ref.cpp @@ -2,6 +2,10 @@ #include +#include + +#include "cellcoordinates.hpp" + CSMWorld::CellRef::CellRef() { mRefNum.mIndex = 0; @@ -10,8 +14,5 @@ CSMWorld::CellRef::CellRef() std::pair CSMWorld::CellRef::getCellIndex() const { - const int cellSize = 8192; - - return std::make_pair ( - std::floor (mPos.pos[0]/cellSize), std::floor (mPos.pos[1]/cellSize)); + return CellCoordinates::coordinatesToCellIndex (mPos.pos[0], mPos.pos[1]); } diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index 5348f4683..9a352ffad 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -51,7 +51,12 @@ bool CSVRender::Cell::addObjects (int start, int end) { std::string id = Misc::StringUtils::lowerCase (collection.getRecord (i).get().mId); - mObjects.insert (std::make_pair (id, new Object (mData, mCellNode, id, false))); + std::auto_ptr object (new Object (mData, mCellNode, id, false)); + + if (mSubModeElementMask & Mask_Reference) + object->setSubMode (mSubMode); + + mObjects.insert (std::make_pair (id, object.release())); modified = true; } } @@ -61,7 +66,8 @@ bool CSVRender::Cell::addObjects (int start, int end) CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::string& id, bool deleted) -: mData (data), mId (Misc::StringUtils::lowerCase (id)), mDeleted (deleted) +: mData (data), mId (Misc::StringUtils::lowerCase (id)), mDeleted (deleted), mSubMode (0), + mSubModeElementMask (0) { std::pair result = CSMWorld::CellCoordinates::fromId (id); @@ -349,3 +355,35 @@ std::vector > CSVRender::Cell::getSelection (un return result; } + +std::vector > CSVRender::Cell::getEdited (unsigned int elementMask) const +{ + std::vector > result; + + if (elementMask & Mask_Reference) + for (std::map::const_iterator iter (mObjects.begin()); + iter!=mObjects.end(); ++iter) + if (iter->second->isEdited()) + result.push_back (iter->second->getTag()); + + return result; +} + +void CSVRender::Cell::setSubMode (int subMode, unsigned int elementMask) +{ + mSubMode = subMode; + mSubModeElementMask = elementMask; + + if (elementMask & Mask_Reference) + for (std::map::const_iterator iter (mObjects.begin()); + iter!=mObjects.end(); ++iter) + iter->second->setSubMode (subMode); +} + +void CSVRender::Cell::reset (unsigned int elementMask) +{ + if (elementMask & Mask_Reference) + for (std::map::const_iterator iter (mObjects.begin()); + iter!=mObjects.end(); ++iter) + iter->second->reset(); +} diff --git a/apps/opencs/view/render/cell.hpp b/apps/opencs/view/render/cell.hpp index 54f5bea1e..631df24aa 100644 --- a/apps/opencs/view/render/cell.hpp +++ b/apps/opencs/view/render/cell.hpp @@ -47,6 +47,8 @@ namespace CSVRender std::auto_ptr mCellMarker; std::auto_ptr mCellBorder; bool mDeleted; + int mSubMode; + unsigned int mSubModeElementMask; /// Ignored if cell does not have an object with the given ID. /// @@ -118,6 +120,14 @@ namespace CSVRender bool isDeleted() const; std::vector > getSelection (unsigned int elementMask) const; + + std::vector > getEdited (unsigned int elementMask) const; + + void setSubMode (int subMode, unsigned int elementMask); + + /// Erase all overrides and restore the visual representation of the cell to its + /// true state. + void reset (unsigned int elementMask); }; } diff --git a/apps/opencs/view/render/editmode.cpp b/apps/opencs/view/render/editmode.cpp index b325e31fb..be264afc7 100644 --- a/apps/opencs/view/render/editmode.cpp +++ b/apps/opencs/view/render/editmode.cpp @@ -70,3 +70,8 @@ void CSVRender::EditMode::dragEnterEvent (QDragEnterEvent *event) {} void CSVRender::EditMode::dropEvent (QDropEvent* event) {} void CSVRender::EditMode::dragMoveEvent (QDragMoveEvent *event) {} + +int CSVRender::EditMode::getSubMode() const +{ + return -1; +} diff --git a/apps/opencs/view/render/editmode.hpp b/apps/opencs/view/render/editmode.hpp index 3ba97cf00..f5cef1be2 100644 --- a/apps/opencs/view/render/editmode.hpp +++ b/apps/opencs/view/render/editmode.hpp @@ -92,6 +92,9 @@ namespace CSVRender /// Default-implementation: ignored virtual void dragMoveEvent (QDragMoveEvent *event); + + /// Default: return -1 + virtual int getSubMode() const; }; } diff --git a/apps/opencs/view/render/instancemode.cpp b/apps/opencs/view/render/instancemode.cpp index 4b6b2e41f..32373c395 100644 --- a/apps/opencs/view/render/instancemode.cpp +++ b/apps/opencs/view/render/instancemode.cpp @@ -8,6 +8,7 @@ #include "../../model/world/idtable.hpp" #include "../../model/world/idtree.hpp" #include "../../model/world/commands.hpp" +#include "../../model/world/commandmacro.hpp" #include "../widget/scenetoolbar.hpp" #include "../widget/scenetoolmode.hpp" @@ -18,10 +19,17 @@ #include "worldspacewidget.hpp" #include "pagedworldspacewidget.hpp" #include "instanceselectionmode.hpp" +#include "instancemovemode.hpp" + +int CSVRender::InstanceMode::getSubModeFromId (const std::string& id) const +{ + return id=="move" ? 0 : (id=="rotate" ? 1 : 2); +} CSVRender::InstanceMode::InstanceMode (WorldspaceWidget *worldspaceWidget, QWidget *parent) : EditMode (worldspaceWidget, QIcon (":placeholder"), Mask_Reference, "Instance editing", - parent), mSubMode (0), mSelectionMode (0) + parent), mSubMode (0), mSubModeId ("move"), mSelectionMode (0), mDragMode (DragMode_None), + mDragAxis (-1), mLocked (false) { } @@ -30,12 +38,7 @@ void CSVRender::InstanceMode::activate (CSVWidget::SceneToolbar *toolbar) if (!mSubMode) { mSubMode = new CSVWidget::SceneToolMode (toolbar, "Edit Sub-Mode"); - mSubMode->addButton (":placeholder", "move", - "Move selected instances" - "
  • Use primary edit to move instances around freely
  • " - "
  • Use secondary edit to move instances around within the grid
  • " - "
" - "Not implemented yet"); + mSubMode->addButton (new InstanceMoveMode (this), "move"); mSubMode->addButton (":placeholder", "rotate", "Rotate selected instances" "
  • Use primary edit to rotate instances freely
  • " @@ -48,19 +51,33 @@ void CSVRender::InstanceMode::activate (CSVWidget::SceneToolbar *toolbar) "
  • Use secondary edit to scale instances along the grid
  • " "
" "Not implemented yet"); + + mSubMode->setButton (mSubModeId); + + connect (mSubMode, SIGNAL (modeChanged (const std::string&)), + this, SLOT (subModeChanged (const std::string&))); } if (!mSelectionMode) mSelectionMode = new InstanceSelectionMode (toolbar, getWorldspaceWidget()); + mDragMode = DragMode_None; + EditMode::activate (toolbar); toolbar->addTool (mSubMode); toolbar->addTool (mSelectionMode); + + std::string subMode = mSubMode->getCurrentId(); + + getWorldspaceWidget().setSubMode (getSubModeFromId (subMode), Mask_Reference); } void CSVRender::InstanceMode::deactivate (CSVWidget::SceneToolbar *toolbar) { + mDragMode = DragMode_None; + getWorldspaceWidget().reset (Mask_Reference); + if (mSelectionMode) { toolbar->removeTool (mSelectionMode); @@ -78,6 +95,14 @@ void CSVRender::InstanceMode::deactivate (CSVWidget::SceneToolbar *toolbar) EditMode::deactivate (toolbar); } +void CSVRender::InstanceMode::setEditLock (bool locked) +{ + mLocked = locked; + + if (mLocked) + getWorldspaceWidget().abortDrag(); +} + void CSVRender::InstanceMode::primaryEditPressed (osg::ref_ptr tag) { if (CSMPrefs::get()["3D Scene Input"]["context-select"].isTrue()) @@ -120,6 +145,173 @@ void CSVRender::InstanceMode::secondarySelectPressed (osg::ref_ptr tag) } } +bool CSVRender::InstanceMode::primaryEditStartDrag (osg::ref_ptr tag) +{ + if (mDragMode!=DragMode_None || mLocked) + return false; + + if (tag && CSMPrefs::get()["3D Scene Input"]["context-select"].isTrue()) + { + getWorldspaceWidget().clearSelection (Mask_Reference); + if (CSVRender::ObjectTag *objectTag = dynamic_cast (tag.get())) + { + CSVRender::Object* object = objectTag->mObject; + object->setSelected (true); + } + } + + std::vector > selection = + getWorldspaceWidget().getSelection (Mask_Reference); + + if (selection.empty()) + return false; + + for (std::vector >::iterator iter (selection.begin()); + iter!=selection.end(); ++iter) + { + if (CSVRender::ObjectTag *objectTag = dynamic_cast (iter->get())) + { + objectTag->mObject->setEdited (Object::Override_Position); + } + } + + // \todo check for sub-mode + + if (CSVRender::ObjectMarkerTag *objectTag = dynamic_cast (tag.get())) + { + mDragAxis = objectTag->mAxis; + } + else + mDragAxis = -1; + + mDragMode = DragMode_Move; + + return true; +} + +bool CSVRender::InstanceMode::secondaryEditStartDrag (osg::ref_ptr tag) +{ + if (mLocked) + return false; + + return false; +} + +void CSVRender::InstanceMode::drag (int diffX, int diffY, double speedFactor) +{ + osg::Vec3f eye; + osg::Vec3f centre; + osg::Vec3f up; + + getWorldspaceWidget().getCamera()->getViewMatrix().getLookAt (eye, centre, up); + + osg::Vec3f offset; + + if (diffY) + offset += up * diffY * speedFactor; + + if (diffX) + offset += ((centre-eye) ^ up) * diffX * speedFactor; + + switch (mDragMode) + { + case DragMode_Move: + { + if (mDragAxis!=-1) + for (int i=0; i<3; ++i) + if (i!=mDragAxis) + offset[i] = 0; + + std::vector > selection = + getWorldspaceWidget().getEdited (Mask_Reference); + + for (std::vector >::iterator iter (selection.begin()); + iter!=selection.end(); ++iter) + { + if (CSVRender::ObjectTag *objectTag = dynamic_cast (iter->get())) + { + ESM::Position position = objectTag->mObject->getPosition(); + for (int i=0; i<3; ++i) + position.pos[i] += offset[i]; + objectTag->mObject->setPosition (position.pos); + } + } + + break; + } + + case DragMode_None: break; + } +} + +void CSVRender::InstanceMode::dragCompleted() +{ + std::vector > selection = + getWorldspaceWidget().getEdited (Mask_Reference); + + QUndoStack& undoStack = getWorldspaceWidget().getDocument().getUndoStack(); + + QString description; + + switch (mDragMode) + { + case DragMode_Move: description = "Move Instances"; break; + + case DragMode_None: break; + } + + + CSMWorld::CommandMacro macro (undoStack, description); + + for (std::vector >::iterator iter (selection.begin()); + iter!=selection.end(); ++iter) + { + if (CSVRender::ObjectTag *objectTag = dynamic_cast (iter->get())) + { + objectTag->mObject->apply (macro); + } + } + + mDragMode = DragMode_None; +} + +void CSVRender::InstanceMode::dragAborted() +{ + getWorldspaceWidget().reset (Mask_Reference); + mDragMode = DragMode_None; +} + +void CSVRender::InstanceMode::dragWheel (int diff, double speedFactor) +{ + if (mDragMode==DragMode_Move) + { + osg::Vec3f eye; + osg::Vec3f centre; + osg::Vec3f up; + + getWorldspaceWidget().getCamera()->getViewMatrix().getLookAt (eye, centre, up); + + osg::Vec3f offset = centre - eye; + offset.normalize(); + offset *= diff * speedFactor; + + std::vector > selection = + getWorldspaceWidget().getEdited (Mask_Reference); + + for (std::vector >::iterator iter (selection.begin()); + iter!=selection.end(); ++iter) + { + if (CSVRender::ObjectTag *objectTag = dynamic_cast (iter->get())) + { + ESM::Position position = objectTag->mObject->getPosition(); + for (int i=0; i<3; ++i) + position.pos[i] += offset[i]; + objectTag->mObject->setPosition (position.pos); + } + } + } +} + void CSVRender::InstanceMode::dragEnterEvent (QDragEnterEvent *event) { if (const CSMWorld::TableMimeData* mime = dynamic_cast (event->mimeData())) @@ -243,11 +435,10 @@ void CSVRender::InstanceMode::dropEvent (QDropEvent* event) new CSMWorld::ModifyCommand (cellTable, countIndex, count+1)); } - document.getUndoStack().beginMacro (createCommand->text()); - document.getUndoStack().push (createCommand.release()); + CSMWorld::CommandMacro macro (document.getUndoStack()); + macro.push (createCommand.release()); if (incrementCommand.get()) - document.getUndoStack().push (incrementCommand.release()); - document.getUndoStack().endMacro(); + macro.push (incrementCommand.release()); dropped = true; } @@ -256,3 +447,15 @@ void CSVRender::InstanceMode::dropEvent (QDropEvent* event) event->accept(); } } + +int CSVRender::InstanceMode::getSubMode() const +{ + return mSubMode ? getSubModeFromId (mSubMode->getCurrentId()) : 0; +} + +void CSVRender::InstanceMode::subModeChanged (const std::string& id) +{ + mSubModeId = id; + getWorldspaceWidget().abortDrag(); + getWorldspaceWidget().setSubMode (getSubModeFromId (id), Mask_Reference); +} diff --git a/apps/opencs/view/render/instancemode.hpp b/apps/opencs/view/render/instancemode.hpp index 78836878a..db7fdaa96 100644 --- a/apps/opencs/view/render/instancemode.hpp +++ b/apps/opencs/view/render/instancemode.hpp @@ -15,8 +15,21 @@ namespace CSVRender class InstanceMode : public EditMode { Q_OBJECT + + enum DragMode + { + DragMode_None, + DragMode_Move + }; + CSVWidget::SceneToolMode *mSubMode; + std::string mSubModeId; InstanceSelectionMode *mSelectionMode; + DragMode mDragMode; + int mDragAxis; + bool mLocked; + + int getSubModeFromId (const std::string& id) const; public: @@ -26,6 +39,8 @@ namespace CSVRender virtual void deactivate (CSVWidget::SceneToolbar *toolbar); + virtual void setEditLock (bool locked); + virtual void primaryEditPressed (osg::ref_ptr tag); virtual void secondaryEditPressed (osg::ref_ptr tag); @@ -34,9 +49,29 @@ namespace CSVRender virtual void secondarySelectPressed (osg::ref_ptr tag); + virtual bool primaryEditStartDrag (osg::ref_ptr tag); + + virtual bool secondaryEditStartDrag (osg::ref_ptr tag); + + virtual void drag (int diffX, int diffY, double speedFactor); + + virtual void dragCompleted(); + + /// \note dragAborted will not be called, if the drag is aborted via changing + /// editing mode + virtual void dragAborted(); + + virtual void dragWheel (int diff, double speedFactor); + virtual void dragEnterEvent (QDragEnterEvent *event); virtual void dropEvent (QDropEvent* event); + + virtual int getSubMode() const; + + private slots: + + void subModeChanged (const std::string& id); }; } diff --git a/apps/opencs/view/render/instancemovemode.cpp b/apps/opencs/view/render/instancemovemode.cpp new file mode 100644 index 000000000..9929f5fcb --- /dev/null +++ b/apps/opencs/view/render/instancemovemode.cpp @@ -0,0 +1,12 @@ + +#include "instancemovemode.hpp" + +CSVRender::InstanceMoveMode::InstanceMoveMode (QWidget *parent) +: ModeButton (QIcon (QPixmap (":placeholder")), + "Move selected instances" + "
  • Use primary edit to move instances around freely
  • " + "
  • Use secondary edit to move instances around within the grid
  • " + "
" + "Grid move not implemented yet", + parent) +{} diff --git a/apps/opencs/view/render/instancemovemode.hpp b/apps/opencs/view/render/instancemovemode.hpp new file mode 100644 index 000000000..bd0e28dac --- /dev/null +++ b/apps/opencs/view/render/instancemovemode.hpp @@ -0,0 +1,18 @@ +#ifndef CSV_RENDER_INSTANCEMOVEMODE_H +#define CSV_RENDER_INSTANCEMOVEMODE_H + +#include "../widget/modebutton.hpp" + +namespace CSVRender +{ + class InstanceMoveMode : public CSVWidget::ModeButton + { + Q_OBJECT + + public: + + InstanceMoveMode (QWidget *parent = 0); + }; +} + +#endif diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp index c220321d5..cc151ead9 100644 --- a/apps/opencs/view/render/object.cpp +++ b/apps/opencs/view/render/object.cpp @@ -9,12 +9,18 @@ #include #include #include +#include +#include #include #include "../../model/world/data.hpp" #include "../../model/world/ref.hpp" #include "../../model/world/refidcollection.hpp" +#include "../../model/world/commands.hpp" +#include "../../model/world/universalid.hpp" +#include "../../model/world/commandmacro.hpp" +#include "../../model/world/cellcoordinates.hpp" #include #include @@ -50,6 +56,11 @@ QString CSVRender::ObjectTag::getToolTip (bool hideBasics) const } +CSVRender::ObjectMarkerTag::ObjectMarkerTag (Object* object, int axis) +: ObjectTag (object), mAxis (axis) +{} + + void CSVRender::Object::clear() { } @@ -130,18 +141,20 @@ void CSVRender::Object::adjustTransform() if (mReferenceId.empty()) return; - const CSMWorld::CellRef& reference = getReference(); + ESM::Position position = getPosition(); // position - mBaseNode->setPosition(mForceBaseToZero ? osg::Vec3() : osg::Vec3f(reference.mPos.pos[0], reference.mPos.pos[1], reference.mPos.pos[2])); + mRootNode->setPosition(mForceBaseToZero ? osg::Vec3() : osg::Vec3f(position.pos[0], position.pos[1], position.pos[2])); // orientation - osg::Quat xr (-reference.mPos.rot[0], osg::Vec3f(1,0,0)); - osg::Quat yr (-reference.mPos.rot[1], osg::Vec3f(0,1,0)); - osg::Quat zr (-reference.mPos.rot[2], osg::Vec3f(0,0,1)); + osg::Quat xr (-position.rot[0], osg::Vec3f(1,0,0)); + osg::Quat yr (-position.rot[1], osg::Vec3f(0,1,0)); + osg::Quat zr (-position.rot[2], osg::Vec3f(0,0,1)); mBaseNode->setAttitude(zr*yr*xr); - mBaseNode->setScale(osg::Vec3(reference.mScale, reference.mScale, reference.mScale)); + float scale = getScale(); + + mBaseNode->setScale(osg::Vec3(scale, scale, scale)); } const CSMWorld::CellRef& CSVRender::Object::getReference() const @@ -152,10 +165,147 @@ const CSMWorld::CellRef& CSVRender::Object::getReference() const return mData.getReferences().getRecord (mReferenceId).get(); } +void CSVRender::Object::updateMarker() +{ + for (int i=0; i<3; ++i) + { + if (mMarker[i]) + { + mRootNode->removeChild (mMarker[i]); + mMarker[i] = osg::ref_ptr(); + } + + if (mSelected) + { + if (mSubMode==0) + { + mMarker[i] = makeMarker (i); + mMarker[i]->setUserData(new ObjectMarkerTag (this, i)); + + mRootNode->addChild (mMarker[i]); + } + } + } +} + +osg::ref_ptr CSVRender::Object::makeMarker (int axis) +{ + osg::ref_ptr geometry (new osg::Geometry); + + const float shaftWidth = 10; + const float shaftBaseLength = 50; + const float headWidth = 30; + const float headLength = 30; + + float shaftLength = shaftBaseLength + mBaseNode->getBound().radius(); + + // shaft + osg::Vec3Array *vertices = new osg::Vec3Array; + + for (int i=0; i<2; ++i) + { + float length = i ? shaftLength : 0; + + vertices->push_back (getMarkerPosition (-shaftWidth/2, -shaftWidth/2, length, axis)); + vertices->push_back (getMarkerPosition (-shaftWidth/2, shaftWidth/2, length, axis)); + vertices->push_back (getMarkerPosition (shaftWidth/2, shaftWidth/2, length, axis)); + vertices->push_back (getMarkerPosition (shaftWidth/2, -shaftWidth/2, length, axis)); + } + + // head backside + vertices->push_back (getMarkerPosition (-headWidth/2, -headWidth/2, shaftLength, axis)); + vertices->push_back (getMarkerPosition (-headWidth/2, headWidth/2, shaftLength, axis)); + vertices->push_back (getMarkerPosition (headWidth/2, headWidth/2, shaftLength, axis)); + vertices->push_back (getMarkerPosition (headWidth/2, -headWidth/2, shaftLength, axis)); + + // head + vertices->push_back (getMarkerPosition (0, 0, shaftLength+headLength, axis)); + + geometry->setVertexArray (vertices); + + osg::DrawElementsUShort *primitives = new osg::DrawElementsUShort (osg::PrimitiveSet::TRIANGLES, 0); + + // shaft + for (int i=0; i<4; ++i) + { + int i2 = i==3 ? 0 : i+1; + primitives->push_back (i); + primitives->push_back (4+i); + primitives->push_back (i2); + + primitives->push_back (4+i); + primitives->push_back (4+i2); + primitives->push_back (i2); + } + + // cap + primitives->push_back (0); + primitives->push_back (1); + primitives->push_back (2); + + primitives->push_back (2); + primitives->push_back (3); + primitives->push_back (0); + + // head, backside + primitives->push_back (0+8); + primitives->push_back (1+8); + primitives->push_back (2+8); + + primitives->push_back (2+8); + primitives->push_back (3+8); + primitives->push_back (0+8); + + for (int i=0; i<4; ++i) + { + primitives->push_back (12); + primitives->push_back (8+(i==3 ? 0 : i+1)); + primitives->push_back (8+i); + } + + geometry->addPrimitiveSet (primitives); + + osg::Vec4Array *colours = new osg::Vec4Array; + + for (int i=0; i<8; ++i) + colours->push_back (osg::Vec4f (axis==0 ? 1.0f : 0.2f, axis==1 ? 1.0f : 0.2f, + axis==2 ? 1.0f : 0.2f, 1.0f)); + + for (int i=8; i<8+4+1; ++i) + colours->push_back (osg::Vec4f (axis==0 ? 1.0f : 0.0f, axis==1 ? 1.0f : 0.0f, + axis==2 ? 1.0f : 0.0f, 1.0f)); + + geometry->setColorArray (colours, osg::Array::BIND_PER_VERTEX); + + geometry->getOrCreateStateSet()->setMode (GL_LIGHTING, osg::StateAttribute::OFF); + + osg::ref_ptr geode (new osg::Geode); + geode->addDrawable (geometry); + + return geode; +} + +osg::Vec3f CSVRender::Object::getMarkerPosition (float x, float y, float z, int axis) +{ + switch (axis) + { + case 2: return osg::Vec3f (x, y, z); + case 0: return osg::Vec3f (z, x, y); + case 1: return osg::Vec3f (y, z, x); + + default: + + throw std::logic_error ("invalid axis for marker geometry"); + } +} + CSVRender::Object::Object (CSMWorld::Data& data, osg::Group* parentNode, const std::string& id, bool referenceable, bool forceBaseToZero) -: mData (data), mBaseNode(0), mSelected(false), mParentNode(parentNode), mResourceSystem(data.getResourceSystem().get()), mForceBaseToZero (forceBaseToZero) +: mData (data), mBaseNode(0), mSelected(false), mParentNode(parentNode), mResourceSystem(data.getResourceSystem().get()), mForceBaseToZero (forceBaseToZero), + mScaleOverride (1), mOverrideFlags (0), mSubMode (-1) { + mRootNode = new osg::PositionAttitudeTransform; + mBaseNode = new osg::PositionAttitudeTransform; mBaseNode->addCullCallback(new SceneUtil::LightListCallback); @@ -163,9 +313,11 @@ CSVRender::Object::Object (CSMWorld::Data& data, osg::Group* parentNode, mBaseNode->setUserData(new ObjectTag(this)); - parentNode->addChild(mBaseNode); + mRootNode->addChild (mBaseNode); - mBaseNode->setNodeMask(Mask_Reference); + parentNode->addChild (mRootNode); + + mRootNode->setNodeMask(Mask_Reference); if (referenceable) { @@ -179,14 +331,14 @@ CSVRender::Object::Object (CSMWorld::Data& data, osg::Group* parentNode, adjustTransform(); update(); + updateMarker(); } CSVRender::Object::~Object() { clear(); - mParentNode->removeChild(mBaseNode); - mParentNode->removeChild(mOutline); + mParentNode->removeChild (mRootNode); } void CSVRender::Object::setSelected(bool selected) @@ -194,15 +346,17 @@ void CSVRender::Object::setSelected(bool selected) mSelected = selected; mOutline->removeChild(mBaseNode); - mParentNode->removeChild(mOutline); - mParentNode->removeChild(mBaseNode); + mRootNode->removeChild(mOutline); + mRootNode->removeChild(mBaseNode); if (selected) { mOutline->addChild(mBaseNode); - mParentNode->addChild(mOutline); + mRootNode->addChild(mOutline); } else - mParentNode->addChild(mBaseNode); + mRootNode->addChild(mBaseNode); + + updateMarker(); } bool CSVRender::Object::getSelected() const @@ -221,6 +375,7 @@ bool CSVRender::Object::referenceableDataChanged (const QModelIndex& topLeft, { adjustTransform(); update(); + updateMarker(); return true; } @@ -271,6 +426,7 @@ bool CSVRender::Object::referenceDataChanged (const QModelIndex& topLeft, references.getData (index, columnIndex).toString().toUtf8().constData(); update(); + updateMarker(); } return true; @@ -293,3 +449,148 @@ osg::ref_ptr CSVRender::Object::getTag() const { return static_cast (mBaseNode->getUserData()); } + +bool CSVRender::Object::isEdited() const +{ + return mOverrideFlags; +} + +void CSVRender::Object::setEdited (int flags) +{ + bool discard = mOverrideFlags & ~flags; + int added = flags & ~mOverrideFlags; + + mOverrideFlags = flags; + + if (added & Override_Position) + for (int i=0; i<3; ++i) + mPositionOverride.pos[i] = getReference().mPos.pos[i]; + + if (added & Override_Rotation) + for (int i=0; i<3; ++i) + mPositionOverride.rot[i] = getReference().mPos.rot[i]; + + if (added & Override_Scale) + mScaleOverride = getReference().mScale; + + if (discard) + adjustTransform(); +} + +ESM::Position CSVRender::Object::getPosition() const +{ + ESM::Position position = getReference().mPos; + + if (mOverrideFlags & Override_Position) + for (int i=0; i<3; ++i) + position.pos[i] = mPositionOverride.pos[i]; + + if (mOverrideFlags & Override_Rotation) + for (int i=0; i<3; ++i) + position.rot[i] = mPositionOverride.rot[i]; + + return position; +} + +float CSVRender::Object::getScale() const +{ + return mOverrideFlags & Override_Scale ? mScaleOverride : getReference().mScale; +} + +void CSVRender::Object::setPosition (const float position[3]) +{ + mOverrideFlags |= Override_Position; + + for (int i=0; i<3; ++i) + mPositionOverride.pos[i] = position[i]; + + adjustTransform(); +} + +void CSVRender::Object::setRotation (const float rotation[3]) +{ + mOverrideFlags |= Override_Rotation; + + for (int i=0; i<3; ++i) + mPositionOverride.rot[i] = rotation[i]; + + adjustTransform(); +} + +void CSVRender::Object::setScale (float scale) +{ + mOverrideFlags |= Override_Scale; + + mScaleOverride = scale; + + adjustTransform(); +} + +void CSVRender::Object::apply (CSMWorld::CommandMacro& commands) +{ + const CSMWorld::RefCollection& collection = mData.getReferences(); + QAbstractItemModel *model = mData.getTableModel (CSMWorld::UniversalId::Type_References); + + int recordIndex = collection.getIndex (mReferenceId); + + if (mOverrideFlags & Override_Position) + { + for (int i=0; i<3; ++i) + { + int column = collection.findColumnIndex (static_cast ( + CSMWorld::Columns::ColumnId_PositionXPos+i)); + + commands.push (new CSMWorld::ModifyCommand (*model, + model->index (recordIndex, column), mPositionOverride.pos[i])); + } + + int column = collection.findColumnIndex (static_cast ( + CSMWorld::Columns::ColumnId_Cell)); + + std::pair cellIndex = collection.getRecord (recordIndex).get().getCellIndex(); + + /// \todo figure out worldspace (not important until multiple worldspaces are supported) + std::string cellId = CSMWorld::CellCoordinates (cellIndex).getId (""); + + commands.push (new CSMWorld::ModifyCommand (*model, + model->index (recordIndex, column), QString::fromUtf8 (cellId.c_str()))); + } + + if (mOverrideFlags & Override_Rotation) + { + for (int i=0; i<3; ++i) + { + int column = collection.findColumnIndex (static_cast ( + CSMWorld::Columns::ColumnId_PositionXRot+i)); + + commands.push (new CSMWorld::ModifyCommand (*model, + model->index (recordIndex, column), mPositionOverride.rot[i])); + } + } + + if (mOverrideFlags & Override_Scale) + { + int column = collection.findColumnIndex (CSMWorld::Columns::ColumnId_Scale); + + commands.push (new CSMWorld::ModifyCommand (*model, + model->index (recordIndex, column), mScaleOverride)); + } + + mOverrideFlags = 0; +} + +void CSVRender::Object::setSubMode (int subMode) +{ + if (subMode!=mSubMode) + { + mSubMode = subMode; + updateMarker(); + } +} + +void CSVRender::Object::reset() +{ + mOverrideFlags = 0; + adjustTransform(); + updateMarker(); +} diff --git a/apps/opencs/view/render/object.hpp b/apps/opencs/view/render/object.hpp index 4a89fe201..54a284fa3 100644 --- a/apps/opencs/view/render/object.hpp +++ b/apps/opencs/view/render/object.hpp @@ -8,14 +8,19 @@ #include #include +#include + #include "tagbase.hpp" class QModelIndex; +class QUndoStack; namespace osg { class PositionAttitudeTransform; class Group; + class Node; + class Geode; } namespace osgFX @@ -32,6 +37,7 @@ namespace CSMWorld { class Data; struct CellRef; + class CommandMacro; } namespace CSVRender @@ -50,18 +56,43 @@ namespace CSVRender virtual QString getToolTip (bool hideBasics) const; }; + class ObjectMarkerTag : public ObjectTag + { + public: + + ObjectMarkerTag (Object* object, int axis); + + int mAxis; + }; class Object { - const CSMWorld::Data& mData; + public: + + enum OverrideFlags + { + Override_Position = 1, + Override_Rotation = 2, + Override_Scale = 4 + }; + + private: + + CSMWorld::Data& mData; std::string mReferenceId; std::string mReferenceableId; + osg::ref_ptr mRootNode; osg::ref_ptr mBaseNode; osg::ref_ptr mOutline; bool mSelected; osg::Group* mParentNode; Resource::ResourceSystem* mResourceSystem; bool mForceBaseToZero; + ESM::Position mPositionOverride; + int mScaleOverride; + int mOverrideFlags; + osg::ref_ptr mMarker[3]; + int mSubMode; /// Not implemented Object (const Object&); @@ -82,6 +113,12 @@ namespace CSVRender /// Throws an exception if *this was constructed with referenceable const CSMWorld::CellRef& getReference() const; + void updateMarker(); + + osg::ref_ptr makeMarker (int axis); + + osg::Vec3f getMarkerPosition (float x, float y, float z, int axis); + public: Object (CSMWorld::Data& data, osg::Group *cellNode, @@ -116,6 +153,33 @@ namespace CSVRender std::string getReferenceableId() const; osg::ref_ptr getTag() const; + + /// Is there currently an editing operation running on this object? + bool isEdited() const; + + void setEdited (int flags); + + ESM::Position getPosition() const; + + float getScale() const; + + /// Set override position. + void setPosition (const float position[3]); + + /// Set override rotation + void setRotation (const float rotation[3]); + + /// Set override scale + void setScale (float scale); + + /// Apply override changes via command and end edit mode + void apply (CSMWorld::CommandMacro& commands); + + void setSubMode (int subMode); + + /// Erase all overrides and restore the visual representation of the object to its + /// true state. + void reset(); }; } diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index 60c5d1a23..d96f36692 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -309,10 +309,13 @@ void CSVRender::PagedWorldspaceWidget::addCellToScene ( bool deleted = index==-1 || cells.getRecord (index).mState==CSMWorld::RecordBase::State_Deleted; - Cell *cell = new Cell (mDocument.getData(), mRootNode, coordinates.getId (mWorldspace), - deleted); + std::auto_ptr cell ( + new Cell (mDocument.getData(), mRootNode, coordinates.getId (mWorldspace), + deleted)); + EditMode *editMode = getEditMode(); + cell->setSubMode (editMode->getSubMode(), editMode->getInteractionMask()); - mCells.insert (std::make_pair (coordinates, cell)); + mCells.insert (std::make_pair (coordinates, cell.release())); } void CSVRender::PagedWorldspaceWidget::removeCellFromScene ( @@ -555,6 +558,37 @@ std::vector > CSVRender::PagedWorldspaceWidget: return result; } +std::vector > CSVRender::PagedWorldspaceWidget::getEdited ( + unsigned int elementMask) const +{ + std::vector > result; + + for (std::map::const_iterator iter = mCells.begin(); + iter!=mCells.end(); ++iter) + { + std::vector > cellResult = + iter->second->getEdited (elementMask); + + result.insert (result.end(), cellResult.begin(), cellResult.end()); + } + + return result; +} + +void CSVRender::PagedWorldspaceWidget::setSubMode (int subMode, unsigned int elementMask) +{ + for (std::map::const_iterator iter = mCells.begin(); + iter!=mCells.end(); ++iter) + iter->second->setSubMode (subMode, elementMask); +} + +void CSVRender::PagedWorldspaceWidget::reset (unsigned int elementMask) +{ + for (std::map::const_iterator iter = mCells.begin(); + iter!=mCells.end(); ++iter) + iter->second->reset (elementMask); +} + CSVWidget::SceneToolToggle *CSVRender::PagedWorldspaceWidget::makeControlVisibilitySelector ( CSVWidget::SceneToolbar *parent) { diff --git a/apps/opencs/view/render/pagedworldspacewidget.hpp b/apps/opencs/view/render/pagedworldspacewidget.hpp index 419a2a46a..fe533be2d 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.hpp +++ b/apps/opencs/view/render/pagedworldspacewidget.hpp @@ -112,6 +112,14 @@ namespace CSVRender virtual std::vector > getSelection (unsigned int elementMask) const; + virtual std::vector > getEdited (unsigned int elementMask) + const; + + virtual void setSubMode (int subMode, unsigned int elementMask); + + /// Erase all overrides and restore the visual representation to its true state. + virtual void reset (unsigned int elementMask); + protected: virtual void addVisibilitySelectorButtons (CSVWidget::SceneToolToggle2 *tool); diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index ba31c9b8b..55d9c8195 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -120,6 +120,12 @@ bool RenderWidget::eventFilter(QObject* obj, QEvent* event) return QObject::eventFilter(obj, event); } +osg::Camera *RenderWidget::getCamera() +{ + return mView->getCamera(); +} + + // -------------------------------------------------- CompositeViewer::CompositeViewer() diff --git a/apps/opencs/view/render/scenewidget.hpp b/apps/opencs/view/render/scenewidget.hpp index 63da01a45..07a7d7600 100644 --- a/apps/opencs/view/render/scenewidget.hpp +++ b/apps/opencs/view/render/scenewidget.hpp @@ -21,6 +21,7 @@ namespace Resource namespace osg { class Group; + class Camera; } namespace CSVWidget @@ -47,6 +48,8 @@ namespace CSVRender bool eventFilter(QObject *, QEvent *); + osg::Camera *getCamera(); + protected: osg::ref_ptr mView; diff --git a/apps/opencs/view/render/unpagedworldspacewidget.cpp b/apps/opencs/view/render/unpagedworldspacewidget.cpp index 8d65c3694..16c28cf93 100644 --- a/apps/opencs/view/render/unpagedworldspacewidget.cpp +++ b/apps/opencs/view/render/unpagedworldspacewidget.cpp @@ -131,6 +131,22 @@ std::vector > CSVRender::UnpagedWorldspaceWidge return mCell->getSelection (elementMask); } +std::vector > CSVRender::UnpagedWorldspaceWidget::getEdited ( + unsigned int elementMask) const +{ + return mCell->getEdited (elementMask); +} + +void CSVRender::UnpagedWorldspaceWidget::setSubMode (int subMode, unsigned int elementMask) +{ + mCell->setSubMode (subMode, elementMask); +} + +void CSVRender::UnpagedWorldspaceWidget::reset (unsigned int elementMask) +{ + mCell->reset (elementMask); +} + void CSVRender::UnpagedWorldspaceWidget::referenceableDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { diff --git a/apps/opencs/view/render/unpagedworldspacewidget.hpp b/apps/opencs/view/render/unpagedworldspacewidget.hpp index a4c517948..c5bfd22e6 100644 --- a/apps/opencs/view/render/unpagedworldspacewidget.hpp +++ b/apps/opencs/view/render/unpagedworldspacewidget.hpp @@ -60,6 +60,14 @@ namespace CSVRender virtual std::vector > getSelection (unsigned int elementMask) const; + virtual std::vector > getEdited (unsigned int elementMask) + const; + + virtual void setSubMode (int subMode, unsigned int elementMask); + + /// Erase all overrides and restore the visual representation to its true state. + virtual void reset (unsigned int elementMask); + private: virtual void referenceableDataChanged (const QModelIndex& topLeft, diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp index 6ba6e9543..df5790473 100644 --- a/apps/opencs/view/render/worldspacewidget.cpp +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -364,6 +364,18 @@ osg::Vec3f CSVRender::WorldspaceWidget::getIntersectionPoint (const QPoint& loca return start + direction * CSMPrefs::get()["Scene Drops"]["distance"].toInt(); } +void CSVRender::WorldspaceWidget::abortDrag() +{ + if (mDragging) + { + EditMode& editMode = dynamic_cast (*mEditMode->getCurrent()); + + editMode.dragAborted(); + mDragging = false; + mDragMode.clear(); + } +} + void CSVRender::WorldspaceWidget::dragEnterEvent (QDragEnterEvent* event) { const CSMWorld::TableMimeData* mime = dynamic_cast (event->mimeData()); @@ -573,6 +585,7 @@ void CSVRender::WorldspaceWidget::editModeChanged (const std::string& id) { dynamic_cast (*mEditMode->getCurrent()).setEditLock (mLocked); mDragging = false; + mDragMode.clear(); } void CSVRender::WorldspaceWidget::showToolTip() @@ -736,13 +749,7 @@ void CSVRender::WorldspaceWidget::keyPressEvent (QKeyEvent *event) { if(event->key() == Qt::Key_Escape) { - if (mDragging) - { - EditMode& editMode = dynamic_cast (*mEditMode->getCurrent()); - - editMode.dragAborted(); - mDragging = false; - } + abortDrag(); } else RenderWidget::keyPressEvent(event); @@ -761,3 +768,8 @@ void CSVRender::WorldspaceWidget::handleMouseClick (osg::ref_ptr tag, c else if (button=="s-select") editMode.secondarySelectPressed (tag); } + +CSVRender::EditMode *CSVRender::WorldspaceWidget::getEditMode() +{ + return dynamic_cast (mEditMode->getCurrent()); +} diff --git a/apps/opencs/view/render/worldspacewidget.hpp b/apps/opencs/view/render/worldspacewidget.hpp index ac6426b42..4c9c0c31e 100644 --- a/apps/opencs/view/render/worldspacewidget.hpp +++ b/apps/opencs/view/render/worldspacewidget.hpp @@ -35,6 +35,7 @@ namespace CSVRender { class TagBase; class CellArrow; + class EditMode; class WorldspaceWidget : public SceneWidget { @@ -152,6 +153,20 @@ namespace CSVRender virtual std::vector > getSelection (unsigned int elementMask) const = 0; + virtual std::vector > getEdited (unsigned int elementMask) + const = 0; + + virtual void setSubMode (int subMode, unsigned int elementMask) = 0; + + /// Erase all overrides and restore the visual representation to its true state. + virtual void reset (unsigned int elementMask) = 0; + + /// \note Drags will be automatically aborted when the aborting is triggered + /// (either explicitly or implicitly) from within this class. This function only + /// needs to be called, when the drag abort is triggered externally (e.g. from + /// an edit mode). + void abortDrag(); + protected: /// Visual elements in a scene @@ -181,6 +196,8 @@ namespace CSVRender virtual void handleMouseClick (osg::ref_ptr tag, const std::string& button, bool shift); + EditMode *getEditMode(); + private: void dragEnterEvent(QDragEnterEvent *event); diff --git a/apps/opencs/view/widget/scenetoolmode.cpp b/apps/opencs/view/widget/scenetoolmode.cpp index 125f4ac79..c91890c69 100644 --- a/apps/opencs/view/widget/scenetoolmode.cpp +++ b/apps/opencs/view/widget/scenetoolmode.cpp @@ -38,6 +38,27 @@ void CSVWidget::SceneToolMode::adjustToolTip (const ModeButton *activeMode) setToolTip (toolTip); } +void CSVWidget::SceneToolMode::setButton (std::map::iterator iter) +{ + for (std::map::const_iterator iter2 = mButtons.begin(); + iter2!=mButtons.end(); ++iter2) + iter2->first->setChecked (iter2==iter); + + setIcon (iter->first->icon()); + adjustToolTip (iter->first); + + if (mCurrent!=iter->first) + { + if (mCurrent) + mCurrent->deactivate (mToolbar); + + mCurrent = iter->first; + mCurrent->activate (mToolbar); + } + + emit modeChanged (iter->second); +} + CSVWidget::SceneToolMode::SceneToolMode (SceneToolbar *parent, const QString& toolTip) : SceneTool (parent), mButtonSize (parent->getButtonSize()), mIconSize (parent->getIconSize()), mToolTip (toolTip), mFirst (0), mCurrent (0), mToolbar (parent) @@ -96,9 +117,25 @@ CSVWidget::ModeButton *CSVWidget::SceneToolMode::getCurrent() return mCurrent; } +std::string CSVWidget::SceneToolMode::getCurrentId() const +{ + return mButtons.find (mCurrent)->second; +} + +void CSVWidget::SceneToolMode::setButton (const std::string& id) +{ + for (std::map::iterator iter = mButtons.begin(); + iter!=mButtons.end(); ++iter) + if (iter->second==id) + { + setButton (iter); + break; + } +} + void CSVWidget::SceneToolMode::selected() { - std::map::const_iterator iter = + std::map::iterator iter = mButtons.find (dynamic_cast (sender())); if (iter!=mButtons.end()) @@ -106,22 +143,6 @@ void CSVWidget::SceneToolMode::selected() if (!iter->first->hasKeepOpen()) mPanel->hide(); - for (std::map::const_iterator iter2 = mButtons.begin(); - iter2!=mButtons.end(); ++iter2) - iter2->first->setChecked (iter2==iter); - - setIcon (iter->first->icon()); - adjustToolTip (iter->first); - - if (mCurrent!=iter->first) - { - if (mCurrent) - mCurrent->deactivate (mToolbar); - - mCurrent = iter->first; - mCurrent->activate (mToolbar); - } - - emit modeChanged (iter->second); + setButton (iter); } } diff --git a/apps/opencs/view/widget/scenetoolmode.hpp b/apps/opencs/view/widget/scenetoolmode.hpp index 3aa8e6799..192b3ee78 100644 --- a/apps/opencs/view/widget/scenetoolmode.hpp +++ b/apps/opencs/view/widget/scenetoolmode.hpp @@ -41,6 +41,8 @@ namespace CSVWidget /// items to be added, the function must return true anyway. virtual bool createContextMenu (QMenu *menu); + void setButton (std::map::iterator iter); + public: SceneToolMode (SceneToolbar *parent, const QString& toolTip); @@ -56,6 +58,12 @@ namespace CSVWidget /// Will return a 0-pointer only if the mode does not have any buttons yet. ModeButton *getCurrent(); + /// Must not be called if there aren't any buttons yet. + std::string getCurrentId() const; + + /// Manually change the current mode + void setButton (const std::string& id); + signals: void modeChanged (const std::string& id); diff --git a/apps/opencs/view/world/referencecreator.cpp b/apps/opencs/view/world/referencecreator.cpp index 73ca62e02..5428f4e95 100644 --- a/apps/opencs/view/world/referencecreator.cpp +++ b/apps/opencs/view/world/referencecreator.cpp @@ -9,6 +9,7 @@ #include "../../model/world/columns.hpp" #include "../../model/world/idtable.hpp" #include "../../model/world/idcompletionmanager.hpp" +#include "../../model/world/commandmacro.hpp" #include "../widget/droplineedit.hpp" @@ -53,10 +54,9 @@ void CSVWorld::ReferenceCreator::pushCommand (std::auto_ptr increment (new CSMWorld::ModifyCommand (cellTable, countIndex, count+1)); - getUndoStack().beginMacro (command->text()); + CSMWorld::CommandMacro macro (getUndoStack(), command->text()); GenericCreator::pushCommand (command, id); - getUndoStack().push (increment.release()); - getUndoStack().endMacro(); + macro.push (increment.release()); } int CSVWorld::ReferenceCreator::getRefNumCount() const @@ -147,11 +147,11 @@ void CSVWorld::ReferenceCreator::cloneMode(const std::string& originId, cellChanged(); //otherwise ok button will remain disabled } -CSVWorld::Creator *CSVWorld::ReferenceCreatorFactory::makeCreator (CSMDoc::Document& document, +CSVWorld::Creator *CSVWorld::ReferenceCreatorFactory::makeCreator (CSMDoc::Document& document, const CSMWorld::UniversalId& id) const { - return new ReferenceCreator(document.getData(), - document.getUndoStack(), + return new ReferenceCreator(document.getData(), + document.getUndoStack(), id, document.getIdCompletionManager()); } diff --git a/apps/opencs/view/world/regionmap.cpp b/apps/opencs/view/world/regionmap.cpp index 49764bd17..4de470dbc 100644 --- a/apps/opencs/view/world/regionmap.cpp +++ b/apps/opencs/view/world/regionmap.cpp @@ -17,6 +17,7 @@ #include "../../model/world/commands.hpp" #include "../../model/world/columns.hpp" #include "../../model/world/tablemimedata.hpp" +#include "../../model/world/commandmacro.hpp" void CSVWorld::RegionMap::contextMenuEvent (QContextMenuEvent *event) { @@ -159,8 +160,7 @@ void CSVWorld::RegionMap::setRegion (const std::string& regionId) QString regionId2 = QString::fromUtf8 (regionId.c_str()); - if (selected.size()>1) - mDocument.getUndoStack().beginMacro (tr ("Set Region")); + CSMWorld::CommandMacro macro (mDocument.getUndoStack(), selected.size()>1 ? tr ("Set Region") : ""); for (QModelIndexList::const_iterator iter (selected.begin()); iter!=selected.end(); ++iter) { @@ -170,12 +170,8 @@ void CSVWorld::RegionMap::setRegion (const std::string& regionId) QModelIndex index = cellsModel->getModelIndex (cellId, cellsModel->findColumnIndex (CSMWorld::Columns::ColumnId_Region)); - mDocument.getUndoStack().push ( - new CSMWorld::ModifyCommand (*cellsModel, index, regionId2)); + macro.push (new CSMWorld::ModifyCommand (*cellsModel, index, regionId2)); } - - if (selected.size()>1) - mDocument.getUndoStack().endMacro(); } CSVWorld::RegionMap::RegionMap (const CSMWorld::UniversalId& universalId, @@ -258,19 +254,15 @@ void CSVWorld::RegionMap::createCells() CSMWorld::IdTable *cellsModel = &dynamic_cast (* mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_Cells)); - if (selected.size()>1) - mDocument.getUndoStack().beginMacro (tr ("Create cells")); + CSMWorld::CommandMacro macro (mDocument.getUndoStack(), selected.size()>1 ? tr ("Create cells"): ""); for (QModelIndexList::const_iterator iter (selected.begin()); iter!=selected.end(); ++iter) { std::string cellId = model()->data (*iter, CSMWorld::RegionMap::Role_CellId). toString().toUtf8().constData(); - mDocument.getUndoStack().push (new CSMWorld::CreateCommand (*cellsModel, cellId)); + macro.push (new CSMWorld::CreateCommand (*cellsModel, cellId)); } - - if (selected.size()>1) - mDocument.getUndoStack().endMacro(); } void CSVWorld::RegionMap::setRegion() diff --git a/apps/opencs/view/world/vartypedelegate.cpp b/apps/opencs/view/world/vartypedelegate.cpp index a63e6028c..8aa43d15b 100644 --- a/apps/opencs/view/world/vartypedelegate.cpp +++ b/apps/opencs/view/world/vartypedelegate.cpp @@ -4,6 +4,7 @@ #include "../../model/world/commands.hpp" #include "../../model/world/columns.hpp" +#include "../../model/world/commandmacro.hpp" void CSVWorld::VarTypeDelegate::addCommands (QAbstractItemModel *model, const QModelIndex& index, int type) const @@ -36,13 +37,10 @@ void CSVWorld::VarTypeDelegate::addCommands (QAbstractItemModel *model, const QM default: break; // ignore the rest } - getUndoStack().beginMacro ( - "Modify " + model->headerData (index.column(), Qt::Horizontal, Qt::DisplayRole).toString()); + CSMWorld::CommandMacro macro (getUndoStack(), "Modify " + model->headerData (index.column(), Qt::Horizontal, Qt::DisplayRole).toString()); - getUndoStack().push (new CSMWorld::ModifyCommand (*model, index, type)); - getUndoStack().push (new CSMWorld::ModifyCommand (*model, next, value)); - - getUndoStack().endMacro(); + macro.push (new CSMWorld::ModifyCommand (*model, index, type)); + macro.push (new CSMWorld::ModifyCommand (*model, next, value)); } CSVWorld::VarTypeDelegate::VarTypeDelegate (const std::vector >& values,