diff --git a/src/Core/Core.pro b/src/Core/Core.pro index d932a8b6..4e0f1dea 100644 --- a/src/Core/Core.pro +++ b/src/Core/Core.pro @@ -115,7 +115,6 @@ HEADERS += \ Resource/Script/CScriptTemplate.h \ Resource/Script/EPropertyType.h \ Resource/Script/EVolumeShape.h \ - Resource/Script/SConnection.h \ Resource/CAnimationParameters.h \ Resource/CAnimSet.h \ Resource/CCollisionMesh.h \ @@ -186,7 +185,8 @@ HEADERS += \ Resource/Cooker/CPoiToWorldCooker.h \ Resource/Factory/CSectionMgrIn.h \ Resource/Cooker/CScriptCooker.h \ - ScriptExtra/CSplinePathExtra.h + ScriptExtra/CSplinePathExtra.h \ + Resource/Script/SLink.h # Source Files SOURCES += \ diff --git a/src/Core/Resource/Cooker/CTemplateWriter.cpp b/src/Core/Resource/Cooker/CTemplateWriter.cpp index 056f227a..8cb593df 100644 --- a/src/Core/Resource/Cooker/CTemplateWriter.cpp +++ b/src/Core/Resource/Cooker/CTemplateWriter.cpp @@ -150,23 +150,39 @@ void CTemplateWriter::SaveGameTemplates(CMasterTemplate *pMaster) } // Write script states/messages - std::map *pMaps[2] = { &pMaster->mStates, &pMaster->mMessages }; - TString Types[2] = { "state", "message" }; - - for (u32 iScr = 0; iScr < 2; iScr++) + for (u32 iType = 0; iType < 2; iType++) { - XMLElement *pElem = Master.NewElement(*(Types[iScr] + "s")); + TString Type = (iType == 0 ? "state" : "message"); + XMLElement *pElem = Master.NewElement(*(Type + "s")); pBase->LinkEndChild(pElem); - for (auto it = pMaps[iScr]->begin(); it != pMaps[iScr]->end(); it++) - { - TString ID; - if (it->first <= 0xFF) ID = TString::HexString(it->first, true, true, 2); - else ID = CFourCC(it->first).ToString(); + u32 Num = (iType == 0 ? pMaster->NumStates() : pMaster->NumMessages()); - XMLElement *pSubElem = Master.NewElement(*Types[iScr]); - pSubElem->SetAttribute("ID", *ID); - pSubElem->SetAttribute("name", *(it->second)); + for (u32 iScr = 0; iScr < Num; iScr++) + { + u32 ID; + TString Name; + + if (iType == 0) + { + SState State = pMaster->StateByIndex(iScr); + ID = State.ID; + Name = State.Name; + } + else + { + SMessage Message = pMaster->MessageByIndex(iScr); + ID = Message.ID; + Name = Message.Name; + } + + TString StrID; + if (ID <= 0xFF) StrID = TString::HexString(ID, true, true, 2); + else StrID = CFourCC(ID).ToString(); + + XMLElement *pSubElem = Master.NewElement(*Type); + pSubElem->SetAttribute("ID", *StrID); + pSubElem->SetAttribute("name", *Name); pElem->LinkEndChild(pSubElem); } } diff --git a/src/Core/Resource/Factory/CAreaLoader.h b/src/Core/Resource/Factory/CAreaLoader.h index 15f69a28..ab3db78e 100644 --- a/src/Core/Resource/Factory/CAreaLoader.h +++ b/src/Core/Resource/Factory/CAreaLoader.h @@ -2,7 +2,7 @@ #define CAREALOADER_H #include "CSectionMgrIn.h" -#include "Core/Resource/Script/SConnection.h" +#include "Core/Resource/Script/SLink.h" #include "Core/Resource/CGameArea.h" #include "Core/Resource/EGame.h" #include "Core/Resource/CResCache.h" diff --git a/src/Core/Resource/Factory/CTemplateLoader.cpp b/src/Core/Resource/Factory/CTemplateLoader.cpp index 7f2698fe..a04106d9 100644 --- a/src/Core/Resource/Factory/CTemplateLoader.cpp +++ b/src/Core/Resource/Factory/CTemplateLoader.cpp @@ -732,7 +732,7 @@ void CTemplateLoader::LoadMasterTemplate(XMLDocument *pDoc, CMasterTemplate *pMa StateID = CFourCC(StrID).ToLong(); TString StateName = pState->Attribute("name"); - mpMaster->mStates[StateID] = StateName; + mpMaster->mStates[StateID] = SState(StateID, StateName); pState = pState->NextSiblingElement("state"); } } @@ -753,7 +753,7 @@ void CTemplateLoader::LoadMasterTemplate(XMLDocument *pDoc, CMasterTemplate *pMa MessageID = CFourCC(StrID).ToLong(); TString MessageName = pMessage->Attribute("name"); - mpMaster->mMessages[MessageID] = MessageName; + mpMaster->mMessages[MessageID] = SMessage(MessageID, MessageName); pMessage = pMessage->NextSiblingElement("message"); } } diff --git a/src/Core/Resource/Script/CMasterTemplate.cpp b/src/Core/Resource/Script/CMasterTemplate.cpp index b6013f90..9129a011 100644 --- a/src/Core/Resource/Script/CMasterTemplate.cpp +++ b/src/Core/Resource/Script/CMasterTemplate.cpp @@ -57,43 +57,43 @@ CScriptTemplate* CMasterTemplate::TemplateByIndex(u32 Index) return (std::next(it, Index))->second; } -TString CMasterTemplate::StateByID(u32 StateID) +SState CMasterTemplate::StateByID(u32 StateID) { auto it = mStates.find(StateID); if (it != mStates.end()) return it->second; else - return "Invalid"; + return SState(-1, "Invalid"); } -TString CMasterTemplate::StateByID(const CFourCC& State) +SState CMasterTemplate::StateByID(const CFourCC& State) { return StateByID(State.ToLong()); } -TString CMasterTemplate::StateByIndex(u32 Index) +SState CMasterTemplate::StateByIndex(u32 Index) { auto it = mStates.begin(); return (std::next(it, Index))->second; } -TString CMasterTemplate::MessageByID(u32 MessageID) +SMessage CMasterTemplate::MessageByID(u32 MessageID) { auto it = mMessages.find(MessageID); if (it != mMessages.end()) return it->second; else - return "Invalid"; + return SMessage(-1, "Invalid"); } -TString CMasterTemplate::MessageByID(const CFourCC& MessageID) +SMessage CMasterTemplate::MessageByID(const CFourCC& MessageID) { return MessageByID(MessageID.ToLong()); } -TString CMasterTemplate::MessageByIndex(u32 Index) +SMessage CMasterTemplate::MessageByIndex(u32 Index) { auto it = mMessages.begin(); return (std::next(it, Index))->second; diff --git a/src/Core/Resource/Script/CMasterTemplate.h b/src/Core/Resource/Script/CMasterTemplate.h index 36932e42..d361ac48 100644 --- a/src/Core/Resource/Script/CMasterTemplate.h +++ b/src/Core/Resource/Script/CMasterTemplate.h @@ -2,6 +2,7 @@ #define CMASTERTEMPLATE_H #include "CScriptTemplate.h" +#include "SLink.h" #include "Core/Resource/EGame.h" #include #include @@ -21,8 +22,8 @@ class CMasterTemplate std::map mStructTemplates; std::map mTemplates; - std::map mStates; - std::map mMessages; + std::map mStates; + std::map mMessages; struct SPropIDInfo { @@ -46,12 +47,12 @@ public: CScriptTemplate* TemplateByID(u32 ObjectID); CScriptTemplate* TemplateByID(const CFourCC& ObjectID); CScriptTemplate* TemplateByIndex(u32 Index); - TString StateByID(u32 StateID); - TString StateByID(const CFourCC& StateID); - TString StateByIndex(u32 Index); - TString MessageByID(u32 MessageID); - TString MessageByID(const CFourCC& MessageID); - TString MessageByIndex(u32 Index); + SState StateByID(u32 StateID); + SState StateByID(const CFourCC& StateID); + SState StateByIndex(u32 Index); + SMessage MessageByID(u32 MessageID); + SMessage MessageByID(const CFourCC& MessageID); + SMessage MessageByIndex(u32 Index); TString GetDirectory() const; CStructTemplate* GetStructAtSource(const TString& rkSource); bool IsLoadedSuccessfully(); diff --git a/src/Core/Resource/Script/CScriptObject.h b/src/Core/Resource/Script/CScriptObject.h index 04a933eb..58a5a1e9 100644 --- a/src/Core/Resource/Script/CScriptObject.h +++ b/src/Core/Resource/Script/CScriptObject.h @@ -1,7 +1,7 @@ #ifndef CSCRIPTOBJECT_H #define CSCRIPTOBJECT_H -#include "SConnection.h" +#include "SLink.h" #include "IProperty.h" #include "IPropertyTemplate.h" #include "CScriptTemplate.h" diff --git a/src/Core/Resource/Script/SConnection.h b/src/Core/Resource/Script/SConnection.h deleted file mode 100644 index 7f3e4676..00000000 --- a/src/Core/Resource/Script/SConnection.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef SCONNECTION_H -#define SCONNECTION_H - -#include - -struct SLink -{ - u32 State; - u32 Message; - u32 ObjectID; // not a pointer because it can refer to objects outside the current area -}; - -#endif // SCONNECTION_H diff --git a/src/Core/Resource/Script/SLink.h b/src/Core/Resource/Script/SLink.h new file mode 100644 index 00000000..f4943062 --- /dev/null +++ b/src/Core/Resource/Script/SLink.h @@ -0,0 +1,32 @@ +#ifndef SLINK_H +#define SLINK_H + +#include +#include + +struct SState +{ + u32 ID; + TString Name; + + SState() {} + SState(u32 _ID, const TString& rkName) : ID(_ID), Name(rkName) {} +}; + +struct SMessage +{ + u32 ID; + TString Name; + + SMessage() {} + SMessage(u32 _ID, const TString& rkName) : ID(_ID), Name(rkName) {} +}; + +struct SLink +{ + u32 State; + u32 Message; + u32 ObjectID; // not a pointer because it can refer to objects outside the current area +}; + +#endif // SLINK_H diff --git a/src/Editor/Editor.pro b/src/Editor/Editor.pro index 6db3606c..d63995b2 100644 --- a/src/Editor/Editor.pro +++ b/src/Editor/Editor.pro @@ -142,7 +142,10 @@ HEADERS += \ Undo/IUndoCommand.h \ WorldEditor/WEditorProperties.h \ Undo/CChangeLayerCommand.h \ - WorldEditor/CTemplateEditDialog.h + WorldEditor/CTemplateEditDialog.h \ + WorldEditor/CLinkDialog.h \ + WorldEditor/CStateMessageModel.h \ + WorldEditor/CSelectInstanceDialog.h # Source Files SOURCES += \ @@ -199,7 +202,9 @@ SOURCES += \ Undo/CBasicPropertyCommand.cpp \ WorldEditor/WEditorProperties.cpp \ Undo/CChangeLayerCommand.cpp \ - WorldEditor/CTemplateEditDialog.cpp + WorldEditor/CTemplateEditDialog.cpp \ + WorldEditor/CLinkDialog.cpp \ + WorldEditor/CSelectInstanceDialog.cpp # UI Files FORMS += \ @@ -216,4 +221,6 @@ FORMS += \ WorldEditor/WModifyTab.ui \ CErrorLogDialog.ui \ WorldEditor/CPoiMapEditDialog.ui \ - WorldEditor/CTemplateEditDialog.ui + WorldEditor/CTemplateEditDialog.ui \ + WorldEditor/CLinkDialog.ui \ + WorldEditor/CSelectInstanceDialog.ui diff --git a/src/Editor/Icons.qrc b/src/Editor/Icons.qrc index ff679534..31d98119 100644 --- a/src/Editor/Icons.qrc +++ b/src/Editor/Icons.qrc @@ -36,5 +36,9 @@ icons/Redo.png icons/Save.png icons/Undo.png + icons/Instances_16px.png + icons/SelectMode_16px.png + icons/Swap_16px.png + icons/Swap_24px.png diff --git a/src/Editor/WorldEditor/CInstancesModel.cpp b/src/Editor/WorldEditor/CInstancesModel.cpp index ab5265e2..127ac200 100644 --- a/src/Editor/WorldEditor/CInstancesModel.cpp +++ b/src/Editor/WorldEditor/CInstancesModel.cpp @@ -38,6 +38,7 @@ CInstancesModel::CInstancesModel(QObject *pParent) : QAbstractItemModel(pParent) mpArea = nullptr; mpCurrentMaster = nullptr; mModelType = eLayers; + mShowColumnEnabled = true; mBaseItems << "Script"; } @@ -199,7 +200,7 @@ int CInstancesModel::rowCount(const QModelIndex &parent) const int CInstancesModel::columnCount(const QModelIndex& /*parent*/) const { - return 3; + return (mShowColumnEnabled ? 3 : 2); } QVariant CInstancesModel::data(const QModelIndex &index, int role) const @@ -340,6 +341,12 @@ void CInstancesModel::SetModelType(EInstanceModelType type) mModelType = type; } +void CInstancesModel::SetShowColumnEnabled(bool Enabled) +{ + mShowColumnEnabled = Enabled; + emit layoutChanged(); +} + void CInstancesModel::NodeCreated(CSceneNode *pNode) { if (mModelType == eTypes) diff --git a/src/Editor/WorldEditor/CInstancesModel.h b/src/Editor/WorldEditor/CInstancesModel.h index c8e68be2..5b810787 100644 --- a/src/Editor/WorldEditor/CInstancesModel.h +++ b/src/Editor/WorldEditor/CInstancesModel.h @@ -36,6 +36,7 @@ private: EInstanceModelType mModelType; QList mTemplateList; QStringList mBaseItems; + bool mShowColumnEnabled; public: explicit CInstancesModel(QObject *pParent = 0); @@ -46,10 +47,12 @@ public: int rowCount(const QModelIndex &parent) const; int columnCount(const QModelIndex &parent) const; QVariant data(const QModelIndex &index, int role) const; + void SetEditor(CWorldEditor *pEditor); void SetMaster(CMasterTemplate *pMaster); void SetArea(CGameArea *pArea); void SetModelType(EInstanceModelType type); + void SetShowColumnEnabled(bool Enabled); void NodeCreated(CSceneNode *pNode); void NodeDeleted(CSceneNode *pNode); CScriptLayer* IndexLayer(const QModelIndex& index) const; diff --git a/src/Editor/WorldEditor/CLinkDialog.cpp b/src/Editor/WorldEditor/CLinkDialog.cpp new file mode 100644 index 00000000..9bf7acee --- /dev/null +++ b/src/Editor/WorldEditor/CLinkDialog.cpp @@ -0,0 +1,189 @@ +#include "CLinkDialog.h" +#include "ui_CLinkDialog.h" +#include "CSelectInstanceDialog.h" +#include "CStateMessageModel.h" +#include + +CLinkDialog::CLinkDialog(CWorldEditor *pEditor, QWidget *pParent /*= 0*/) + : QDialog(pParent) + , ui(new Ui::CLinkDialog) + , mpEditor(pEditor) + , mpMaster(nullptr) + , mpSender(nullptr) + , mpReceiver(nullptr) + , mSenderStateModel(CStateMessageModel::eStates, this) + , mReceiverMessageModel(CStateMessageModel::eMessages, this) +{ + ui->setupUi(this); + ui->SenderStateComboBox->setModel(&mSenderStateModel); + ui->ReceiverMessageComboBox->setModel(&mReceiverMessageModel); + + connect(ui->SwapButton, SIGNAL(clicked()), this, SLOT(OnSwapClicked())); + connect(ui->SenderPickFromViewport, SIGNAL(clicked()), this, SLOT(OnPickFromViewportClicked())); + connect(ui->SenderPickFromList, SIGNAL(clicked()), this, SLOT(OnPickFromListClicked())); + connect(ui->ReceiverPickFromViewport, SIGNAL(clicked()), this, SLOT(OnPickFromViewportClicked())); + connect(ui->ReceiverPickFromList, SIGNAL(clicked()), this, SLOT(OnPickFromListClicked())); +} + +CLinkDialog::~CLinkDialog() +{ + delete ui; +} + +void CLinkDialog::resizeEvent(QResizeEvent *) +{ + SetSenderNameLabel(); + SetReceiverNameLabel(); +} + +void CLinkDialog::showEvent(QShowEvent *) +{ + // This is needed to get the labels to elide correctly when the window is first shown. It shouldn't be + // needed because showing the window generates a resize event, but for some reason it is, so whatever. + SetSenderNameLabel(); + SetReceiverNameLabel(); +} + +void CLinkDialog::SetMaster(CMasterTemplate *pMaster) +{ + if (mpMaster != pMaster) + { + mpMaster = pMaster; + mSenderStateModel.SetMasterTemplate(pMaster); + mReceiverMessageModel.SetMasterTemplate(pMaster); + } +} + +void CLinkDialog::SetSender(CScriptObject *pSender) +{ + bool HadSender = mpSender != nullptr; + mpSender = pSender; + mSenderStateModel.SetScriptTemplate(pSender ? pSender->Template() : nullptr); + SetSenderNameLabel(); + + if (pSender) + { + if (!HadSender) ui->SenderStateComboBox->setCurrentIndex(0); + ui->SenderStateComboBox->setEnabled(true); + } + else + { + ui->SenderStateComboBox->setCurrentIndex(-1); + ui->SenderStateComboBox->setEnabled(false); + } +} + +void CLinkDialog::SetReceiver(CScriptObject *pReceiver) +{ + bool HadReceiver = mpReceiver != nullptr; + mpReceiver = pReceiver; + mReceiverMessageModel.SetScriptTemplate(pReceiver ? pReceiver->Template() : nullptr); + SetReceiverNameLabel(); + + if (pReceiver) + { + if (!HadReceiver) ui->ReceiverMessageComboBox->setCurrentIndex(0); + ui->ReceiverMessageComboBox->setEnabled(true); + } + else + { + ui->ReceiverMessageComboBox->setCurrentIndex(-1); + ui->ReceiverMessageComboBox->setEnabled(false); + } +} + +u32 CLinkDialog::State() const +{ + return mSenderStateModel.State(ui->SenderStateComboBox->currentIndex()); +} + +u32 CLinkDialog::Message() const +{ + return mReceiverMessageModel.State(ui->ReceiverMessageComboBox->currentIndex()); +} + +void CLinkDialog::SetSenderNameLabel() +{ + QString Text = (mpSender ? TO_QSTRING(mpSender->InstanceName()) : "No sender"); + ui->SenderNameLabel->setToolTip(Text); + + QFontMetrics Metrics(ui->SenderNameLabel->font()); + QString Elided = Metrics.elidedText(Text, Qt::ElideRight, ui->SenderNameLabel->width() - (ui->SenderNameLabel->frameWidth() * 2)); + ui->SenderNameLabel->setText(Elided); + + ui->SenderGroupBox->setTitle(mpSender ? "Sender - " + TO_QSTRING(mpSender->Template()->Name()) : "Sender"); +} + +void CLinkDialog::SetReceiverNameLabel() +{ + QString Text = (mpReceiver ? TO_QSTRING(mpReceiver->InstanceName()) : "No receiver"); + ui->ReceiverNameLabel->setToolTip(Text); + + QFontMetrics Metrics(ui->ReceiverNameLabel->font()); + QString Elided = Metrics.elidedText(Text, Qt::ElideRight, ui->ReceiverNameLabel->width() - (ui->ReceiverNameLabel->frameWidth() * 2)); + ui->ReceiverNameLabel->setText(Elided); + + ui->ReceiverGroupBox->setTitle(mpReceiver ? "Receiver - " + TO_QSTRING(mpReceiver->Template()->Name()) : "Receiver"); +} + +// ************ PUBLIC SLOTS ************ +void CLinkDialog::OnSwapClicked() +{ + CScriptObject *pSender = mpReceiver; + CScriptObject *pReceiver = mpSender; + SetSender(pSender); + SetReceiver(pReceiver); +} + +void CLinkDialog::OnPickFromViewportClicked() +{ + QPushButton *pButton = qobject_cast(sender()); + + if (pButton && pButton->isChecked()) + { + mpEditor->EnterPickMode(eScriptNode, true, false, false); + connect(mpEditor, SIGNAL(PickModeClick(SRayIntersection,QMouseEvent*)), this, SLOT(OnPickModeClick(SRayIntersection,QMouseEvent*))); + connect(mpEditor, SIGNAL(PickModeExited()), this, SLOT(OnPickModeExit())); + + QPushButton *pOtherButton = (pButton == ui->SenderPickFromViewport ? ui->ReceiverPickFromViewport : ui->SenderPickFromViewport); + pOtherButton->setChecked(false); + } + + else + mpEditor->ExitPickMode(); +} + +void CLinkDialog::OnPickModeClick(const SRayIntersection& rkHit, QMouseEvent* /*pEvent*/) +{ + CScriptNode *pScript = static_cast(rkHit.pNode); + + if (ui->SenderPickFromViewport->isChecked()) + SetSender(pScript->Object()); + else + SetReceiver(pScript->Object()); + + mpEditor->ExitPickMode(); +} + +void CLinkDialog::OnPickModeExit() +{ + ui->SenderPickFromViewport->setChecked(false); + ui->ReceiverPickFromViewport->setChecked(false); + disconnect(mpEditor, SIGNAL(PickModeClick(SRayIntersection,QMouseEvent*)), this, 0); + disconnect(mpEditor, SIGNAL(PickModeExited()), this, 0); +} + +void CLinkDialog::OnPickFromListClicked() +{ + CSelectInstanceDialog Dialog(mpEditor, this); + Dialog.exec(); + CScriptObject *pResult = Dialog.SelectedInstance(); + + if (pResult) + { + if (sender() == ui->SenderPickFromList) + SetSender(pResult); + else + SetReceiver(pResult); + } +} diff --git a/src/Editor/WorldEditor/CLinkDialog.h b/src/Editor/WorldEditor/CLinkDialog.h new file mode 100644 index 00000000..14d7d299 --- /dev/null +++ b/src/Editor/WorldEditor/CLinkDialog.h @@ -0,0 +1,52 @@ +#ifndef CLINKDIALOG_H +#define CLINKDIALOG_H + +#include "CStateMessageModel.h" +#include "CWorldEditor.h" +#include + +namespace Ui { +class CLinkDialog; +} + +class CLinkDialog : public QDialog +{ + Q_OBJECT + + CWorldEditor *mpEditor; + CMasterTemplate *mpMaster; + CScriptObject *mpSender; + CScriptObject *mpReceiver; + + CStateMessageModel mSenderStateModel; + CStateMessageModel mReceiverMessageModel; + + Ui::CLinkDialog *ui; + +public: + explicit CLinkDialog(CWorldEditor *pEditor, QWidget *parent = 0); + ~CLinkDialog(); + void resizeEvent(QResizeEvent *); + void showEvent(QShowEvent *); + + void SetMaster(CMasterTemplate *pMaster); + void SetSender(CScriptObject *pSender); + void SetReceiver(CScriptObject *pReceiver); + u32 State() const; + u32 Message() const; + + void SetSenderNameLabel(); + void SetReceiverNameLabel(); + + inline CScriptObject* Sender() const { return mpSender; } + inline CScriptObject* Receiver() const { return mpReceiver; } + +public slots: + void OnSwapClicked(); + void OnPickFromViewportClicked(); + void OnPickModeClick(const SRayIntersection& rkHit, QMouseEvent *pEvent); + void OnPickModeExit(); + void OnPickFromListClicked(); +}; + +#endif // CLINKDIALOG_H diff --git a/src/Editor/WorldEditor/CLinkDialog.ui b/src/Editor/WorldEditor/CLinkDialog.ui new file mode 100644 index 00000000..8d8cd58f --- /dev/null +++ b/src/Editor/WorldEditor/CLinkDialog.ui @@ -0,0 +1,277 @@ + + + CLinkDialog + + + + 0 + 0 + 622 + 134 + + + + + 0 + 0 + + + + Create Link + + + + QLayout::SetDefaultConstraint + + + + + + + Sender + + + + + + + + + 1 + 0 + + + + QFrame::Panel + + + QFrame::Sunken + + + SENDER + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + + + + :/icons/Instances_16px.png:/icons/Instances_16px.png + + + + 16 + 16 + + + + + + + + + + + + :/icons/SelectMode_16px.png:/icons/SelectMode_16px.png + + + + 16 + 16 + + + + true + + + + + + + + + + + + + + + + 0 + 0 + + + + + 24 + 24 + + + + + 24 + 24 + + + + + + + + :/icons/Swap_16px.png:/icons/Swap_16px.png + + + + 16 + 16 + + + + + + + + Receiver + + + + + + + + + 1 + 0 + + + + QFrame::Panel + + + QFrame::Sunken + + + RECEIVER + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + + + + :/icons/Instances_16px.png:/icons/Instances_16px.png + + + + 16 + 16 + + + + + + + + + + + + :/icons/SelectMode_16px.png:/icons/SelectMode_16px.png + + + + 16 + 16 + + + + true + + + + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + + ButtonBox + accepted() + CLinkDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + ButtonBox + rejected() + CLinkDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/Editor/WorldEditor/CLinkModel.cpp b/src/Editor/WorldEditor/CLinkModel.cpp index 77145c1c..d80a38c2 100644 --- a/src/Editor/WorldEditor/CLinkModel.cpp +++ b/src/Editor/WorldEditor/CLinkModel.cpp @@ -67,13 +67,13 @@ QVariant CLinkModel::data(const QModelIndex &index, int role) const case 1: // Column 1 - State { - TString StateName = mpObject->MasterTemplate()->StateByID(link.State); + TString StateName = mpObject->MasterTemplate()->StateByID(link.State).Name; return UICommon::ToQString(StateName); } case 2: // Column 2 - Message { - TString MessageName = mpObject->MasterTemplate()->MessageByID(link.Message); + TString MessageName = mpObject->MasterTemplate()->MessageByID(link.Message).Name; return UICommon::ToQString(MessageName); } diff --git a/src/Editor/WorldEditor/CPoiMapEditDialog.ui b/src/Editor/WorldEditor/CPoiMapEditDialog.ui index e37706ca..4e7c66e1 100644 --- a/src/Editor/WorldEditor/CPoiMapEditDialog.ui +++ b/src/Editor/WorldEditor/CPoiMapEditDialog.ui @@ -79,7 +79,7 @@ - :/icons/SelectMode.png:/icons/SelectMode.png + :/icons/SelectMode_16px.png:/icons/SelectMode_16px.png true @@ -96,7 +96,7 @@ - :/icons/Instances.png:/icons/Instances.png + :/icons/Instances_16px.png:/icons/Instances_16px.png diff --git a/src/Editor/WorldEditor/CSelectInstanceDialog.cpp b/src/Editor/WorldEditor/CSelectInstanceDialog.cpp new file mode 100644 index 00000000..e72012ec --- /dev/null +++ b/src/Editor/WorldEditor/CSelectInstanceDialog.cpp @@ -0,0 +1,98 @@ +#include "CSelectInstanceDialog.h" +#include "ui_CSelectInstanceDialog.h" + +CSelectInstanceDialog::CSelectInstanceDialog(CWorldEditor *pEditor, QWidget *pParent) + : QDialog(pParent) + , ui(new Ui::CSelectInstanceDialog) + , mpEditor(pEditor) + , mValidSelection(false) + , mpLayersInst(nullptr) + , mpTypesInst(nullptr) +{ + ui->setupUi(this); + + mLayersModel.SetModelType(CInstancesModel::eLayers); + mLayersModel.SetEditor(pEditor); + mLayersModel.SetArea(pEditor->ActiveArea()); + mLayersModel.SetShowColumnEnabled(false); + + mTypesModel.SetModelType(CInstancesModel::eTypes); + mTypesModel.SetEditor(pEditor); + mTypesModel.SetArea(pEditor->ActiveArea()); + mTypesModel.SetMaster(CMasterTemplate::GetMasterForGame(pEditor->CurrentGame())); + mTypesModel.SetShowColumnEnabled(false); + + int Col0Width = ui->LayersTreeView->width() * 0.9; + int Col1Width = ui->LayersTreeView->width() * 0.1; + mLayersProxyModel.setSourceModel(&mLayersModel); + ui->LayersTreeView->setModel(&mLayersProxyModel); + ui->LayersTreeView->header()->setSortIndicator(0, Qt::AscendingOrder); + ui->LayersTreeView->header()->resizeSection(0, Col0Width); + ui->LayersTreeView->header()->resizeSection(1, Col1Width); + + mTypesProxyModel.setSourceModel(&mTypesModel); + ui->TypesTreeView->setModel(&mTypesProxyModel); + ui->TypesTreeView->header()->setSortIndicator(0, Qt::AscendingOrder); + ui->TypesTreeView->header()->resizeSection(0, Col0Width); + ui->TypesTreeView->header()->resizeSection(1, Col1Width); + + ui->LayersTreeView->expand(mLayersProxyModel.index(0, 0)); + ui->TypesTreeView->expand(mTypesProxyModel.index(0, 0)); + + ui->ButtonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + + connect(ui->TabWidget, SIGNAL(currentChanged(int)), this, SLOT(OnTabChanged(int))); + connect(ui->LayersTreeView, SIGNAL(clicked(QModelIndex)), this, SLOT(OnTreeClicked(QModelIndex))); + connect(ui->LayersTreeView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(OnTreeDoubleClicked(QModelIndex))); + connect(ui->TypesTreeView, SIGNAL(clicked(QModelIndex)), this, SLOT(OnTreeClicked(QModelIndex))); + connect(ui->TypesTreeView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(OnTreeDoubleClicked(QModelIndex))); +} + +CSelectInstanceDialog::~CSelectInstanceDialog() +{ + delete ui; +} + +CScriptObject* CSelectInstanceDialog::SelectedInstance() const +{ + return (ui->TabWidget->currentIndex() == 0 ? mpLayersInst : mpTypesInst); +} + +// ************ PUBLIC SLOTS ************ +void CSelectInstanceDialog::OnTabChanged(int NewTabIndex) +{ + if (NewTabIndex == 0) + mValidSelection = (mpLayersInst != nullptr); + else + mValidSelection = (mpTypesInst != nullptr); + + ui->ButtonBox->button(QDialogButtonBox::Ok)->setEnabled(mValidSelection); +} + +void CSelectInstanceDialog::OnTreeClicked(QModelIndex Index) +{ + int TabIndex = ui->TabWidget->currentIndex(); + + if (TabIndex == 0) + { + QModelIndex SourceIndex = mLayersProxyModel.mapToSource(Index); + mpLayersInst = mLayersModel.IndexObject(SourceIndex); + mValidSelection = (mpLayersInst != nullptr); + } + + else + { + QModelIndex SourceIndex = mTypesProxyModel.mapToSource(Index); + mpTypesInst = mTypesModel.IndexObject(SourceIndex); + mValidSelection = (mpTypesInst != nullptr); + } + + ui->ButtonBox->button(QDialogButtonBox::Ok)->setEnabled(mValidSelection); +} + +void CSelectInstanceDialog::OnTreeDoubleClicked(QModelIndex /*Index*/) +{ + // Instance selection was handled in OnTreeClicked on the first click. + if (mValidSelection) + close(); +} diff --git a/src/Editor/WorldEditor/CSelectInstanceDialog.h b/src/Editor/WorldEditor/CSelectInstanceDialog.h new file mode 100644 index 00000000..aa20c870 --- /dev/null +++ b/src/Editor/WorldEditor/CSelectInstanceDialog.h @@ -0,0 +1,41 @@ +#ifndef CSELECTINSTANCEDIALOG_H +#define CSELECTINSTANCEDIALOG_H + +#include "CInstancesModel.h" +#include "CInstancesProxyModel.h" +#include +#include + +namespace Ui { +class CSelectInstanceDialog; +} + +class CSelectInstanceDialog : public QDialog +{ + Q_OBJECT + + CWorldEditor *mpEditor; + CInstancesModel mLayersModel; + CInstancesModel mTypesModel; + CInstancesProxyModel mLayersProxyModel; + CInstancesProxyModel mTypesProxyModel; + + bool mValidSelection; + CScriptObject *mpLayersInst; + CScriptObject *mpTypesInst; + + Ui::CSelectInstanceDialog *ui; + +public: + explicit CSelectInstanceDialog(CWorldEditor *pEditor, QWidget *pParent = 0); + ~CSelectInstanceDialog(); + + CScriptObject* SelectedInstance() const; + +public slots: + void OnTabChanged(int NewTabIndex); + void OnTreeClicked(QModelIndex Index); + void OnTreeDoubleClicked(QModelIndex Index); +}; + +#endif // CSELECTINSTANCEDIALOG_H diff --git a/src/Editor/WorldEditor/CSelectInstanceDialog.ui b/src/Editor/WorldEditor/CSelectInstanceDialog.ui new file mode 100644 index 00000000..972e51ae --- /dev/null +++ b/src/Editor/WorldEditor/CSelectInstanceDialog.ui @@ -0,0 +1,147 @@ + + + CSelectInstanceDialog + + + + 0 + 0 + 816 + 534 + + + + Select Instance + + + + + + 1 + + + + Layers + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::NoFrame + + + QAbstractItemView::NoEditTriggers + + + true + + + QAbstractItemView::ScrollPerPixel + + + true + + + + + + + + Types + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::NoFrame + + + QAbstractItemView::NoEditTriggers + + + true + + + QAbstractItemView::ScrollPerPixel + + + true + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + ButtonBox + accepted() + CSelectInstanceDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + ButtonBox + rejected() + CSelectInstanceDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/Editor/WorldEditor/CStateMessageModel.h b/src/Editor/WorldEditor/CStateMessageModel.h new file mode 100644 index 00000000..87cadd8f --- /dev/null +++ b/src/Editor/WorldEditor/CStateMessageModel.h @@ -0,0 +1,110 @@ +#ifndef CSTATEMESSAGEMODEL_H +#define CSTATEMESSAGEMODEL_H + +#include "Editor/UICommon.h" +#include +#include +#include + +// todo: support pulling states/messages from script templates instead of master +class CStateMessageModel : public QAbstractListModel +{ + Q_OBJECT + +public: + enum EType + { + eStates, + eMessages + }; + +private: + struct SEntry + { + u32 ID; + QString Name; + + SEntry() {} + SEntry(u32 _ID, const QString& rkName) + : ID(_ID), Name(rkName) {} + + bool operator<(const SEntry& rkOther) const + { + return Name < rkOther.Name; + } + }; + QList mEntries; + + CMasterTemplate *mpMaster; + CScriptTemplate *mpScript; + EType mType; + +public: + explicit CStateMessageModel(EType Type, QObject *pParent = 0) + : QAbstractListModel(pParent) + , mType(Type) + , mpMaster(nullptr) + , mpScript(nullptr) + {} + + int rowCount(const QModelIndex& /*rkParent*/) const + { + return mEntries.size(); + } + + QVariant data(const QModelIndex& rkIndex, int Role) const + { + if (Role == Qt::DisplayRole) + { + return mEntries[rkIndex.row()].Name; + } + + else return QVariant::Invalid; + } + + void SetMasterTemplate(CMasterTemplate *pMaster) + { + beginResetModel(); + + mpMaster = pMaster; + mEntries.clear(); + + if (mType == eStates) + { + for (u32 iState = 0; iState < pMaster->NumStates(); iState++) + { + SState State = pMaster->StateByIndex(iState); + mEntries << SEntry(State.ID, TO_QSTRING(State.Name)); + } + } + + else + { + for (u32 iMsg = 0; iMsg < pMaster->NumMessages(); iMsg++) + { + SMessage Message = pMaster->MessageByIndex(iMsg); + mEntries << SEntry(Message.ID, TO_QSTRING(Message.Name)); + } + } + + qSort(mEntries); + endResetModel(); + } + + void SetScriptTemplate(CScriptTemplate *pScript) + { + mpScript = pScript; + } + + inline u32 State(u32 Index) const + { + return (mType == eStates ? mEntries[Index].ID : 0); + } + + inline u32 Message(u32 Index) const + { + return (mType == eMessages ? mEntries[Index].ID : 0); + } +}; + +#endif // CSTATEMESSAGEMODEL_H diff --git a/src/Editor/WorldEditor/CWorldEditor.cpp b/src/Editor/WorldEditor/CWorldEditor.cpp index 40488bfa..8f080493 100644 --- a/src/Editor/WorldEditor/CWorldEditor.cpp +++ b/src/Editor/WorldEditor/CWorldEditor.cpp @@ -111,6 +111,8 @@ void CWorldEditor::closeEvent(QCloseEvent *pEvent) if (mpPoiDialog) mpPoiDialog->close(); + + emit Closed(); } else { diff --git a/src/Editor/WorldEditor/CWorldEditor.h b/src/Editor/WorldEditor/CWorldEditor.h index 954b3fbb..8809b70c 100644 --- a/src/Editor/WorldEditor/CWorldEditor.h +++ b/src/Editor/WorldEditor/CWorldEditor.h @@ -97,6 +97,7 @@ private slots: void on_ActionEditPoiToWorldMap_triggered(); signals: + void Closed(); void LayersModified(); void InstancesLayerAboutToChange(); void InstancesLayerChanged(const QList& rkInstanceList); diff --git a/src/Editor/WorldEditor/WInstancesTab.ui b/src/Editor/WorldEditor/WInstancesTab.ui index a5500048..4767e1e9 100644 --- a/src/Editor/WorldEditor/WInstancesTab.ui +++ b/src/Editor/WorldEditor/WInstancesTab.ui @@ -56,6 +56,9 @@ QFrame::NoFrame + + true + QAbstractItemView::ScrollPerPixel @@ -98,7 +101,7 @@ QFrame::NoFrame - false + true QAbstractItemView::ScrollPerPixel diff --git a/src/Editor/WorldEditor/WModifyTab.cpp b/src/Editor/WorldEditor/WModifyTab.cpp index adddc265..c03156f1 100644 --- a/src/Editor/WorldEditor/WModifyTab.cpp +++ b/src/Editor/WorldEditor/WModifyTab.cpp @@ -1,14 +1,16 @@ #include "WModifyTab.h" #include "ui_WModifyTab.h" + +#include "CLinkDialog.h" #include "CWorldEditor.h" #include #include #include -WModifyTab::WModifyTab(QWidget *pParent) : - QWidget(pParent), - ui(new Ui::WModifyTab) +WModifyTab::WModifyTab(QWidget *pParent) + : QWidget(pParent) + , ui(new Ui::WModifyTab) { ui->setupUi(this); @@ -21,6 +23,7 @@ WModifyTab::WModifyTab(QWidget *pParent) : mpInLinkModel->SetConnectionType(CLinkModel::eIncoming); mpOutLinkModel = new CLinkModel(this); mpOutLinkModel->SetConnectionType(CLinkModel::eOutgoing); + mpLinkDialog = nullptr; ui->InLinksTableView->setModel(mpInLinkModel); ui->OutLinksTableView->setModel(mpOutLinkModel); @@ -28,6 +31,10 @@ WModifyTab::WModifyTab(QWidget *pParent) : ui->OutLinksTableView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); connect(ui->InLinksTableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(OnLinkTableDoubleClick(QModelIndex))); connect(ui->OutLinksTableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(OnLinkTableDoubleClick(QModelIndex))); + connect(ui->AddOutgoingConnectionButton, SIGNAL(clicked()), this, SLOT(OnAddOutgoingLinkClicked())); + connect(ui->AddIncomingConnectionButton, SIGNAL(clicked()), this, SLOT(OnAddIncomingLinkClicked())); + connect(ui->DeleteOutgoingConnectionButton, SIGNAL(clicked()), this, SLOT(OnDeleteOutgoingLinkClicked())); + connect(ui->DeleteIncomingConnectionButton, SIGNAL(clicked()), this, SLOT(OnDeleteIncomingLinkClicked())); ClearUI(); } @@ -41,6 +48,7 @@ void WModifyTab::SetEditor(CWorldEditor *pEditor) { mpWorldEditor = pEditor; ui->PropertyView->SetEditor(mpWorldEditor); + connect(mpWorldEditor, SIGNAL(Closed()), this, SLOT(OnWorldEditorClosed())); connect(mpWorldEditor, SIGNAL(SelectionTransformed()), this, SLOT(OnWorldSelectionTransformed())); } @@ -80,11 +88,95 @@ void WModifyTab::ClearUI() mpSelectedNode = nullptr; } +void WModifyTab::CreateLinkDialog() +{ + if (!mpLinkDialog) + { + mpLinkDialog = new CLinkDialog(mpWorldEditor, this); + + if (mpSelectedNode && mpSelectedNode->NodeType() == eScriptNode) + { + CScriptNode *pScript = static_cast(mpSelectedNode); + mpLinkDialog->SetMaster(pScript->Object()->MasterTemplate()); + } + + connect(mpLinkDialog, SIGNAL(accepted()), this, SLOT(OnLinkDialogAccept())); + connect(mpLinkDialog, SIGNAL(rejected()), this, SLOT(OnLinkDialogReject())); + } +} + +void WModifyTab::DeleteLinkDialog() +{ + if (mpLinkDialog) + { + delete mpLinkDialog; + mpLinkDialog = nullptr; + } +} + +// ************ PUBLIC SLOTS ************ +void WModifyTab::OnWorldEditorClosed() +{ + DeleteLinkDialog(); +} + void WModifyTab::OnWorldSelectionTransformed() { ui->PropertyView->UpdateEditorProperties(QModelIndex()); } +void WModifyTab::OnAddOutgoingLinkClicked() +{ + if (mpSelectedNode && mpSelectedNode->NodeType() == eScriptNode) + { + CScriptObject *pInst = static_cast(mpSelectedNode)->Object(); + CreateLinkDialog(); + + if (mpLinkDialog->Sender() != pInst) + { + mpLinkDialog->SetSender(pInst); + mpLinkDialog->SetReceiver(nullptr); + } + + mpLinkDialog->show(); + } +} + +void WModifyTab::OnAddIncomingLinkClicked() +{ + if (mpSelectedNode && mpSelectedNode->NodeType() == eScriptNode) + { + CScriptObject *pInst = static_cast(mpSelectedNode)->Object(); + CreateLinkDialog(); + + if (mpLinkDialog->Receiver() != pInst) + { + mpLinkDialog->SetSender(nullptr); + mpLinkDialog->SetReceiver(pInst); + } + + mpLinkDialog->show(); + } +} + +void WModifyTab::OnDeleteOutgoingLinkClicked() +{ +} + +void WModifyTab::OnDeleteIncomingLinkClicked() +{ +} + +void WModifyTab::OnLinkDialogAccept() +{ + DeleteLinkDialog(); +} + +void WModifyTab::OnLinkDialogReject() +{ + DeleteLinkDialog(); +} + // ************ PRIVATE SLOTS ************ void WModifyTab::OnLinkTableDoubleClick(QModelIndex Index) { diff --git a/src/Editor/WorldEditor/WModifyTab.h b/src/Editor/WorldEditor/WModifyTab.h index dd9384e6..f83eb6e7 100644 --- a/src/Editor/WorldEditor/WModifyTab.h +++ b/src/Editor/WorldEditor/WModifyTab.h @@ -1,6 +1,7 @@ #ifndef WMODIFYTAB_H #define WMODIFYTAB_H +#include "CLinkDialog.h" #include "CLinkModel.h" #include #include @@ -27,16 +28,28 @@ class WModifyTab : public QWidget CLinkModel *mpInLinkModel; CLinkModel *mpOutLinkModel; + CLinkDialog *mpLinkDialog; + public: explicit WModifyTab(QWidget *pParent = 0); ~WModifyTab(); void SetEditor(CWorldEditor *pEditor); void GenerateUI(QList& Selection); void ClearUI(); + void CreateLinkDialog(); + void DeleteLinkDialog(); public slots: + void OnWorldEditorClosed(); void OnWorldSelectionTransformed(); + void OnAddOutgoingLinkClicked(); + void OnAddIncomingLinkClicked(); + void OnDeleteOutgoingLinkClicked(); + void OnDeleteIncomingLinkClicked(); + void OnLinkDialogAccept(); + void OnLinkDialogReject(); + private: Ui::WModifyTab *ui; diff --git a/src/Editor/WorldEditor/WModifyTab.ui b/src/Editor/WorldEditor/WModifyTab.ui index 907a1e7e..cd39aece 100644 --- a/src/Editor/WorldEditor/WModifyTab.ui +++ b/src/Editor/WorldEditor/WModifyTab.ui @@ -59,7 +59,7 @@ - alternate-background-color: rgb(35,35,35); + QAbstractItemView::AllEditTriggers @@ -149,6 +149,45 @@ + + + + + + + + + + :/icons/Plus.png:/icons/Plus.png + + + + + + + + + + + :/icons/Minus v2.png:/icons/Minus v2.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + @@ -174,6 +213,45 @@ + + + + + + + + + + :/icons/Plus.png:/icons/Plus.png + + + + + + + + + + + :/icons/Minus v2.png:/icons/Minus v2.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + @@ -201,6 +279,8 @@
Editor/PropertyEdit/CPropertyView.h
- + + + diff --git a/src/Editor/icons/Instances_16px.png b/src/Editor/icons/Instances_16px.png new file mode 100644 index 00000000..fa7d302f Binary files /dev/null and b/src/Editor/icons/Instances_16px.png differ diff --git a/src/Editor/icons/SelectMode_16px.png b/src/Editor/icons/SelectMode_16px.png new file mode 100644 index 00000000..95f29a8a Binary files /dev/null and b/src/Editor/icons/SelectMode_16px.png differ diff --git a/src/Editor/icons/Swap_16px.png b/src/Editor/icons/Swap_16px.png new file mode 100644 index 00000000..74de1f99 Binary files /dev/null and b/src/Editor/icons/Swap_16px.png differ diff --git a/src/Editor/icons/Swap_24px.png b/src/Editor/icons/Swap_24px.png new file mode 100644 index 00000000..7ea2ee12 Binary files /dev/null and b/src/Editor/icons/Swap_24px.png differ diff --git a/src/Editor/main.cpp b/src/Editor/main.cpp index 9d790eaf..9403f98f 100644 --- a/src/Editor/main.cpp +++ b/src/Editor/main.cpp @@ -45,7 +45,7 @@ int main(int argc, char *argv[]) darkPalette.setColor(QPalette::Window, QColor(53,53,53)); darkPalette.setColor(QPalette::WindowText, Qt::white); darkPalette.setColor(QPalette::Base, QColor(25,25,25)); - darkPalette.setColor(QPalette::AlternateBase, QColor(53,53,53)); + darkPalette.setColor(QPalette::AlternateBase, QColor(35,35,35)); darkPalette.setColor(QPalette::ToolTipBase, Qt::white); darkPalette.setColor(QPalette::ToolTipText, Qt::white); darkPalette.setColor(QPalette::Text, Qt::white);