Implemented functionality for editing, creating, and deleting script object links

This commit is contained in:
parax0 2016-03-06 17:44:51 -07:00
parent 984d9cf3f3
commit 8f82dcbdd9
33 changed files with 805 additions and 219 deletions

View File

@ -186,7 +186,7 @@ HEADERS += \
Resource/Factory/CSectionMgrIn.h \ Resource/Factory/CSectionMgrIn.h \
Resource/Cooker/CScriptCooker.h \ Resource/Cooker/CScriptCooker.h \
ScriptExtra/CSplinePathExtra.h \ ScriptExtra/CSplinePathExtra.h \
Resource/Script/SLink.h Resource/Script/CLink.h
# Source Files # Source Files
SOURCES += \ SOURCES += \

View File

@ -1,4 +1,5 @@
#include "CScriptCooker.h" #include "CScriptCooker.h"
#include "Core/Resource/Script/CLink.h"
void CScriptCooker::WriteProperty(IProperty *pProp, bool InSingleStruct) void CScriptCooker::WriteProperty(IProperty *pProp, bool InSingleStruct)
{ {
@ -211,14 +212,14 @@ void CScriptCooker::WriteInstanceMP1(CScriptObject *pInstance)
u32 InstanceStart = mpSCLY->Tell(); u32 InstanceStart = mpSCLY->Tell();
mpSCLY->WriteLong(pInstance->InstanceID()); mpSCLY->WriteLong(pInstance->InstanceID());
mpSCLY->WriteLong(pInstance->NumOutLinks()); mpSCLY->WriteLong(pInstance->NumLinks(eOutgoing));
for (u32 iLink = 0; iLink < pInstance->NumOutLinks(); iLink++) for (u32 iLink = 0; iLink < pInstance->NumLinks(eOutgoing); iLink++)
{ {
const SLink& rkLink = pInstance->OutLink(iLink); CLink *pLink = pInstance->Link(eOutgoing, iLink);
mpSCLY->WriteLong(rkLink.State); mpSCLY->WriteLong(pLink->State());
mpSCLY->WriteLong(rkLink.Message); mpSCLY->WriteLong(pLink->Message());
mpSCLY->WriteLong(rkLink.ObjectID); mpSCLY->WriteLong(pLink->ReceiverID());
} }
WriteProperty(pInstance->Properties(), false); WriteProperty(pInstance->Properties(), false);
@ -261,14 +262,14 @@ void CScriptCooker::WriteInstanceMP2(CScriptObject *pInstance)
u32 InstanceStart = mpSCLY->Tell(); u32 InstanceStart = mpSCLY->Tell();
mpSCLY->WriteLong(pInstance->InstanceID()); mpSCLY->WriteLong(pInstance->InstanceID());
mpSCLY->WriteShort((u16) pInstance->NumOutLinks()); mpSCLY->WriteShort((u16) pInstance->NumLinks(eOutgoing));
for (u32 iLink = 0; iLink < pInstance->NumOutLinks(); iLink++) for (u32 iLink = 0; iLink < pInstance->NumLinks(eOutgoing); iLink++)
{ {
const SLink& rkLink = pInstance->OutLink(iLink); CLink *pLink = pInstance->Link(eOutgoing, iLink);
mpSCLY->WriteLong(rkLink.State); mpSCLY->WriteLong(pLink->State());
mpSCLY->WriteLong(rkLink.Message); mpSCLY->WriteLong(pLink->Message());
mpSCLY->WriteLong(rkLink.ObjectID); mpSCLY->WriteLong(pLink->ReceiverID());
} }
WriteProperty(pInstance->Properties(), false); WriteProperty(pInstance->Properties(), false);

View File

@ -627,15 +627,10 @@ void CAreaLoader::SetUpObjects()
mpArea->mObjectMap[pObj->InstanceID()] = pObj; mpArea->mObjectMap[pObj->InstanceID()] = pObj;
// Store outgoing connections // Store outgoing connections
for (u32 iCon = 0; iCon < pObj->NumOutLinks(); iCon++) for (u32 iCon = 0; iCon < pObj->NumLinks(eOutgoing); iCon++)
{ {
SLink Connection = pObj->OutLink(iCon); CLink *pLink = pObj->Link(eOutgoing, iCon);
mConnectionMap[pLink->ReceiverID()].push_back(pLink);
SLink NewConnection;
NewConnection.State = Connection.State;
NewConnection.Message = Connection.Message;
NewConnection.ObjectID = pObj->InstanceID();
mConnectionMap[Connection.ObjectID].push_back(NewConnection);
} }
} }
} }
@ -649,7 +644,7 @@ void CAreaLoader::SetUpObjects()
if (iConMap != mConnectionMap.end()) if (iConMap != mConnectionMap.end())
{ {
CScriptObject *pObj = mpArea->GetInstanceByID(InstanceID); CScriptObject *pObj = mpArea->GetInstanceByID(InstanceID);
pObj->mInConnections = iConMap->second; pObj->mInLinks = iConMap->second;
} }
} }
} }

View File

@ -2,7 +2,7 @@
#define CAREALOADER_H #define CAREALOADER_H
#include "CSectionMgrIn.h" #include "CSectionMgrIn.h"
#include "Core/Resource/Script/SLink.h" #include "Core/Resource/Script/CLink.h"
#include "Core/Resource/CGameArea.h" #include "Core/Resource/CGameArea.h"
#include "Core/Resource/EGame.h" #include "Core/Resource/EGame.h"
#include "Core/Resource/CResCache.h" #include "Core/Resource/CResCache.h"
@ -22,7 +22,7 @@ class CAreaLoader
u32 mNumLayers; u32 mNumLayers;
// Object connections // Object connections
std::unordered_map<u32, std::vector<SLink>> mConnectionMap; std::unordered_map<u32, std::vector<CLink*>> mConnectionMap;
// Compression // Compression
u8 *mDecmpBuffer; u8 *mDecmpBuffer;

View File

@ -219,15 +219,16 @@ CScriptObject* CScriptLoader::LoadObjectMP1(IInputStream& SCLY)
// Load connections // Load connections
u32 NumLinks = SCLY.ReadLong(); u32 NumLinks = SCLY.ReadLong();
mpObj->mOutConnections.reserve(NumLinks); mpObj->mOutLinks.reserve(NumLinks);
for (u32 iLink = 0; iLink < NumLinks; iLink++) for (u32 iLink = 0; iLink < NumLinks; iLink++)
{ {
SLink Link; u32 State = SCLY.ReadLong();
Link.State = SCLY.ReadLong(); u32 Message = SCLY.ReadLong();
Link.Message = SCLY.ReadLong(); u32 ReceiverID = SCLY.ReadLong();
Link.ObjectID = SCLY.ReadLong();
mpObj->mOutConnections.push_back(Link); CLink *pLink = new CLink(mpArea, State, Message, mpObj->mInstanceID, ReceiverID);
mpObj->mOutLinks.push_back(pLink);
} }
// Load object... // Load object...
@ -331,15 +332,16 @@ CScriptObject* CScriptLoader::LoadObjectMP2(IInputStream& SCLY)
// Load connections // Load connections
u32 NumConnections = SCLY.ReadShort(); u32 NumConnections = SCLY.ReadShort();
mpObj->mOutConnections.reserve(NumConnections); mpObj->mOutLinks.reserve(NumConnections);
for (u32 iCon = 0; iCon < NumConnections; iCon++) for (u32 iCon = 0; iCon < NumConnections; iCon++)
{ {
SLink Link; u32 State = SCLY.ReadLong();
Link.State = SCLY.ReadLong(); u32 Message = SCLY.ReadLong();
Link.Message = SCLY.ReadLong(); u32 ReceiverID = SCLY.ReadLong();
Link.ObjectID = SCLY.ReadLong();
mpObj->mOutConnections.push_back(Link); CLink *pLink = new CLink(mpArea, State, Message, mpObj->mInstanceID, ReceiverID);
mpObj->mOutLinks.push_back(pLink);
} }
// Load object // Load object

View File

@ -0,0 +1,128 @@
#ifndef CLINK_H
#define CLINK_H
#include "CScriptObject.h"
#include "Core/Resource/CGameArea.h"
#include <Common/TString.h>
#include <Common/types.h>
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) {}
};
class CLink
{
CGameArea *mpArea;
u32 mStateID;
u32 mMessageID;
u32 mSenderID;
u32 mReceiverID;
public:
CLink(CGameArea *pArea)
: mpArea(pArea)
, mStateID(-1)
, mMessageID(-1)
, mSenderID(-1)
, mReceiverID(-1)
{}
CLink(CGameArea *pArea, u32 StateID, u32 MessageID, u32 SenderID, u32 ReceiverID)
: mpArea(pArea)
, mStateID(StateID)
, mMessageID(MessageID)
, mSenderID(SenderID)
, mReceiverID(ReceiverID)
{}
void SetSender(u32 NewSenderID, u32 Index = -1)
{
u32 OldSenderID = mSenderID;
CScriptObject *pOldSender = mpArea->GetInstanceByID(OldSenderID);
CScriptObject *pNewSender = mpArea->GetInstanceByID(NewSenderID);
mSenderID = NewSenderID;
pOldSender->RemoveLink(eOutgoing, this);
pNewSender->AddLink(eOutgoing, this, Index);
}
void SetReceiver(u32 NewReceiverID, u32 Index = -1)
{
u32 OldReceiverID = mSenderID;
CScriptObject *pOldReceiver = mpArea->GetInstanceByID(OldReceiverID);
CScriptObject *pNewReceiver = mpArea->GetInstanceByID(NewReceiverID);
mReceiverID = NewReceiverID;
pOldReceiver->RemoveLink(eIncoming, this);
pNewReceiver->AddLink(eIncoming, this, Index);
}
u32 SenderIndex() const
{
CScriptObject *pSender = mpArea->GetInstanceByID(mSenderID);
for (u32 iLink = 0; iLink < pSender->NumLinks(eOutgoing); iLink++)
{
if (pSender->Link(eOutgoing, iLink) == this)
return iLink;
}
return -1;
}
u32 ReceiverIndex() const
{
CScriptObject *pReceiver = mpArea->GetInstanceByID(mReceiverID);
for (u32 iLink = 0; iLink < pReceiver->NumLinks(eIncoming); iLink++)
{
if (pReceiver->Link(eIncoming, iLink) == this)
return iLink;
}
return -1;
}
// Operators
bool operator==(const CLink& rkOther)
{
return ( (mpArea == rkOther.mpArea) &&
(mStateID == rkOther.mStateID) &&
(mMessageID == rkOther.mMessageID) &&
(mSenderID == rkOther.mSenderID) &&
(mReceiverID == rkOther.mReceiverID) );
}
bool operator!=(const CLink& rkOther)
{
return (!(*this == rkOther));
}
// Accessors
u32 State() const { return mStateID; }
u32 Message() const { return mMessageID; }
u32 SenderID() const { return mSenderID; }
u32 ReceiverID() const { return mReceiverID; }
CScriptObject* Sender() const { return mpArea->GetInstanceByID(mSenderID); }
CScriptObject* Receiver() const { return mpArea->GetInstanceByID(mReceiverID); }
void SetState(u32 StateID) { mStateID = StateID; }
void SetMessage(u32 MessageID) { mMessageID = MessageID; }
};
#endif // CLINK_H

View File

@ -2,7 +2,7 @@
#define CMASTERTEMPLATE_H #define CMASTERTEMPLATE_H
#include "CScriptTemplate.h" #include "CScriptTemplate.h"
#include "SLink.h" #include "CLink.h"
#include "Core/Resource/EGame.h" #include "Core/Resource/EGame.h"
#include <Common/types.h> #include <Common/types.h>
#include <map> #include <map>

View File

@ -21,6 +21,10 @@ CScriptObject::~CScriptObject()
{ {
if (mpProperties) delete mpProperties; if (mpProperties) delete mpProperties;
mpTemplate->RemoveObject(this); mpTemplate->RemoveObject(this);
// Note: Incoming links will be deleted by the sender.
for (u32 iLink = 0; iLink < mOutLinks.size(); iLink++)
delete mOutLinks[iLink];
} }
// ************ DATA MANIPULATION ************ // ************ DATA MANIPULATION ************
@ -94,17 +98,17 @@ bool CScriptObject::HasNearVisibleActivation() const
if (mIsCheckingNearVisibleActivation) return false; if (mIsCheckingNearVisibleActivation) return false;
mIsCheckingNearVisibleActivation = true; mIsCheckingNearVisibleActivation = true;
for (u32 iLink = 0; iLink < mInConnections.size(); iLink++) for (u32 iLink = 0; iLink < mInLinks.size(); iLink++)
{ {
const SLink& rkLink = mInConnections[iLink]; CLink *pLink = mInLinks[iLink];
// Check for trigger activation // Check for trigger activation
if (rkLink.State == 0x49533034 || rkLink.State == 0x49533035 || rkLink.State == 0x49533036) // "IS04", "IS05", or "IS06" if (pLink->State() == 0x49533034 || pLink->State() == 0x49533035 || pLink->State() == 0x49533036) // "IS04", "IS05", or "IS06"
{ {
if ( (!IsRelay && rkLink.Message == 0x41435456) || // "ACTV" if ( (!IsRelay && pLink->Message() == 0x41435456) || // "ACTV"
(IsRelay && rkLink.Message == 0x4143544E) ) // "ACTN" (IsRelay && pLink->Message() == 0x4143544E) ) // "ACTN"
{ {
CScriptObject *pObj = mpArea->GetInstanceByID(rkLink.ObjectID); CScriptObject *pObj = pLink->Sender();
if (pObj->ObjectTypeID() == 0x54524752) // "TRGR" if (pObj->ObjectTypeID() == 0x54524752) // "TRGR"
{ {
@ -115,12 +119,12 @@ bool CScriptObject::HasNearVisibleActivation() const
} }
// Check for relay activation // Check for relay activation
else if (rkLink.State == 0x524C4159) // "RLAY" else if (pLink->State() == 0x524C4159) // "RLAY"
{ {
if ( (!IsRelay && rkLink.Message == 0x41435456) || // "ACTV" if ( (!IsRelay && pLink->Message() == 0x41435456) || // "ACTV"
(IsRelay && rkLink.Message == 0x4143544E) ) // "ACTN" (IsRelay && pLink->Message() == 0x4143544E) ) // "ACTN"
{ {
CScriptObject *pObj = mpArea->GetInstanceByID(rkLink.ObjectID); CScriptObject *pObj = pLink->Sender();
if (pObj->ObjectTypeID() == 0x53524C59) // "SRLY" if (pObj->ObjectTypeID() == 0x53524C59) // "SRLY"
Relays.push_back(pObj); Relays.push_back(pObj);
@ -198,24 +202,42 @@ u32 CScriptObject::InstanceID() const
return mInstanceID; return mInstanceID;
} }
u32 CScriptObject::NumInLinks() const u32 CScriptObject::NumLinks(ELinkType Type) const
{ {
return mInConnections.size(); return (Type == eIncoming ? mInLinks.size() : mOutLinks.size());
} }
u32 CScriptObject::NumOutLinks() const CLink* CScriptObject::Link(ELinkType Type, u32 Index) const
{ {
return mOutConnections.size(); return (Type == eIncoming ? mInLinks[Index] : mOutLinks[Index]);
} }
const SLink& CScriptObject::InLink(u32 index) const void CScriptObject::AddLink(ELinkType Type, CLink *pLink, u32 Index /*= -1*/)
{ {
return mInConnections[index]; std::vector<CLink*> *pLinkVec = (Type == eIncoming ? &mInLinks : &mOutLinks);
if (Index == -1 || Index == pLinkVec->size())
pLinkVec->push_back(pLink);
else
{
auto it = pLinkVec->begin();
std::advance(it, Index);
pLinkVec->insert(it, pLink);
}
} }
const SLink& CScriptObject::OutLink(u32 index) const void CScriptObject::RemoveLink(ELinkType Type, CLink *pLink)
{ {
return mOutConnections[index]; std::vector<CLink*> *pLinkVec = (Type == eIncoming ? &mInLinks : &mOutLinks);
for (auto it = pLinkVec->begin(); it != pLinkVec->end(); it++)
{
if (*it == pLink)
{
pLinkVec->erase(it);
break;
}
}
} }
TString CScriptObject::InstanceName() const TString CScriptObject::InstanceName() const

View File

@ -1,7 +1,6 @@
#ifndef CSCRIPTOBJECT_H #ifndef CSCRIPTOBJECT_H
#define CSCRIPTOBJECT_H #define CSCRIPTOBJECT_H
#include "SLink.h"
#include "IProperty.h" #include "IProperty.h"
#include "IPropertyTemplate.h" #include "IPropertyTemplate.h"
#include "CScriptTemplate.h" #include "CScriptTemplate.h"
@ -10,6 +9,13 @@
#include "Core/Resource/CGameArea.h" #include "Core/Resource/CGameArea.h"
class CScriptLayer; class CScriptLayer;
class CLink;
enum ELinkType
{
eIncoming,
eOutgoing
};
class CScriptObject class CScriptObject
{ {
@ -22,8 +28,8 @@ class CScriptObject
u32 mVersion; u32 mVersion;
u32 mInstanceID; u32 mInstanceID;
std::vector<SLink> mOutConnections; std::vector<CLink*> mOutLinks;
std::vector<SLink> mInConnections; std::vector<CLink*> mInLinks;
CPropertyStruct *mpProperties; CPropertyStruct *mpProperties;
TStringProperty *mpInstanceName; TStringProperty *mpInstanceName;
@ -67,10 +73,11 @@ public:
IProperty* PropertyByIDString(const TIDString& str) const; IProperty* PropertyByIDString(const TIDString& str) const;
u32 ObjectTypeID() const; u32 ObjectTypeID() const;
u32 InstanceID() const; u32 InstanceID() const;
u32 NumInLinks() const;
u32 NumOutLinks() const; u32 NumLinks(ELinkType Type) const;
const SLink& InLink(u32 index) const; CLink* Link(ELinkType Type, u32 Index) const;
const SLink& OutLink(u32 index) const; void AddLink(ELinkType Type, CLink *pLink, u32 Index = -1);
void RemoveLink(ELinkType Type, CLink *pLink);
CVector3f Position() const; CVector3f Position() const;
CVector3f Rotation() const; CVector3f Rotation() const;

View File

@ -1,32 +0,0 @@
#ifndef SLINK_H
#define SLINK_H
#include <Common/TString.h>
#include <Common/types.h>
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

View File

@ -255,18 +255,18 @@ void CScriptNode::DrawSelection()
CGraphics::sMVPBlock.ModelMatrix = CMatrix4f::skIdentity; CGraphics::sMVPBlock.ModelMatrix = CMatrix4f::skIdentity;
CGraphics::UpdateMVPBlock(); CGraphics::UpdateMVPBlock();
for (u32 iIn = 0; iIn < mpInstance->NumInLinks(); iIn++) for (u32 iIn = 0; iIn < mpInstance->NumLinks(eIncoming); iIn++)
{ {
// Don't draw in links if the other object is selected. // Don't draw in links if the other object is selected.
const SLink& con = mpInstance->InLink(iIn); CLink *pLink = mpInstance->Link(eIncoming, iIn);
CScriptNode *pLinkNode = mpScene->ScriptNodeByID(con.ObjectID); CScriptNode *pLinkNode = mpScene->ScriptNodeByID(pLink->SenderID());
if (pLinkNode && !pLinkNode->IsSelected()) CDrawUtil::DrawLine(CenterPoint(), pLinkNode->CenterPoint(), CColor::skTransparentRed); if (pLinkNode && !pLinkNode->IsSelected()) CDrawUtil::DrawLine(CenterPoint(), pLinkNode->CenterPoint(), CColor::skTransparentRed);
} }
for (u32 iOut = 0; iOut < mpInstance->NumOutLinks(); iOut++) for (u32 iOut = 0; iOut < mpInstance->NumLinks(eOutgoing); iOut++)
{ {
const SLink& con = mpInstance->OutLink(iOut); CLink *pLink = mpInstance->Link(eOutgoing, iOut);
CScriptNode *pLinkNode = mpScene->ScriptNodeByID(con.ObjectID); CScriptNode *pLinkNode = mpScene->ScriptNodeByID(pLink->ReceiverID());
if (pLinkNode) CDrawUtil::DrawLine(CenterPoint(), pLinkNode->CenterPoint(), CColor::skTransparentGreen); if (pLinkNode) CDrawUtil::DrawLine(CenterPoint(), pLinkNode->CenterPoint(), CColor::skTransparentGreen);
} }
} }
@ -429,6 +429,11 @@ CColor CScriptNode::TintColor(const SViewInfo &ViewInfo) const
return BaseColor; return BaseColor;
} }
void CScriptNode::LinksModified()
{
if (mpExtra) mpExtra->LinksModified();
}
void CScriptNode::PropertyModified(IProperty *pProp) void CScriptNode::PropertyModified(IProperty *pProp)
{ {
// Update volume // Update volume
@ -534,13 +539,13 @@ void CScriptNode::GeneratePosition()
// Ideal way to generate the position is to find a spot close to where it's being used. // Ideal way to generate the position is to find a spot close to where it's being used.
// To do this I check the location of the objects that this one is linked to. // To do this I check the location of the objects that this one is linked to.
u32 NumLinks = mpInstance->NumInLinks() + mpInstance->NumOutLinks(); u32 NumLinks = mpInstance->NumLinks(eIncoming) + mpInstance->NumLinks(eOutgoing);
// In the case of one link, apply an offset so the new position isn't the same place as the object it's linked to // In the case of one link, apply an offset so the new position isn't the same place as the object it's linked to
if (NumLinks == 1) if (NumLinks == 1)
{ {
const SLink& link = (mpInstance->NumInLinks() > 0 ? mpInstance->InLink(0) : mpInstance->OutLink(0)); u32 LinkedID = (mpInstance->NumLinks(eIncoming) > 0 ? mpInstance->Link(eIncoming, 0)->SenderID() : mpInstance->Link(eOutgoing, 0)->ReceiverID());
CScriptNode *pNode = mpScene->ScriptNodeByID(link.ObjectID); CScriptNode *pNode = mpScene->ScriptNodeByID(LinkedID);
pNode->GeneratePosition(); pNode->GeneratePosition();
mPosition = pNode->AbsolutePosition(); mPosition = pNode->AbsolutePosition();
mPosition.z += (pNode->AABox().Size().z / 2.f); mPosition.z += (pNode->AABox().Size().z / 2.f);
@ -553,9 +558,9 @@ void CScriptNode::GeneratePosition()
{ {
CVector3f NewPos = CVector3f::skZero; CVector3f NewPos = CVector3f::skZero;
for (u32 iIn = 0; iIn < mpInstance->NumInLinks(); iIn++) for (u32 iIn = 0; iIn < mpInstance->NumLinks(eIncoming); iIn++)
{ {
CScriptNode *pNode = mpScene->ScriptNodeByID(mpInstance->InLink(iIn).ObjectID); CScriptNode *pNode = mpScene->ScriptNodeByID(mpInstance->Link(eIncoming, iIn)->SenderID());
if (pNode) if (pNode)
{ {
@ -564,9 +569,9 @@ void CScriptNode::GeneratePosition()
} }
} }
for (u32 iOut = 0; iOut < mpInstance->NumOutLinks(); iOut++) for (u32 iOut = 0; iOut < mpInstance->NumLinks(eOutgoing); iOut++)
{ {
CScriptNode *pNode = mpScene->ScriptNodeByID(mpInstance->OutLink(iOut).ObjectID); CScriptNode *pNode = mpScene->ScriptNodeByID(mpInstance->Link(eOutgoing, iOut)->ReceiverID());
if (pNode) if (pNode)
{ {

View File

@ -45,6 +45,7 @@ public:
CColor TintColor(const SViewInfo &ViewInfo) const; CColor TintColor(const SViewInfo &ViewInfo) const;
CColor WireframeColor() const; CColor WireframeColor() const;
void LinksModified();
void PropertyModified(IProperty *pProp); void PropertyModified(IProperty *pProp);
void UpdatePreviewVolume(); void UpdatePreviewVolume();
void GeneratePosition(); void GeneratePosition();

View File

@ -1,5 +1,6 @@
#include "CSplinePathExtra.h" #include "CSplinePathExtra.h"
#include "CWaypointExtra.h" #include "CWaypointExtra.h"
#include "Core/Resource/Script/CLink.h"
#include "Core/Scene/CScene.h" #include "Core/Scene/CScene.h"
CSplinePathExtra::CSplinePathExtra(CScriptObject *pInstance, CScene *pScene, CSceneNode *pParent) CSplinePathExtra::CSplinePathExtra(CScriptObject *pInstance, CScene *pScene, CSceneNode *pParent)
@ -45,14 +46,14 @@ void CSplinePathExtra::AddWaypoints()
std::set<CWaypointExtra*> CheckedWaypoints; std::set<CWaypointExtra*> CheckedWaypoints;
for (u32 iLink = 0; iLink < mpInstance->NumOutLinks(); iLink++) for (u32 iLink = 0; iLink < mpInstance->NumLinks(eOutgoing); iLink++)
{ {
const SLink& rkLink = mpInstance->OutLink(iLink); CLink *pLink = mpInstance->Link(eOutgoing, iLink);
if ( (rkLink.State == 0x49533030 && rkLink.Message == 0x41544348) || // InternalState00/Attach if ( (pLink->State() == 0x49533030 && pLink->Message() == 0x41544348) || // InternalState00/Attach
(rkLink.State == 0x4D4F5450 && rkLink.Message == 0x41544348) ) // MotionPath/Attach (pLink->State() == 0x4D4F5450 && pLink->Message() == 0x41544348) ) // MotionPath/Attach
{ {
CScriptNode *pNode = mpScene->ScriptNodeByID(rkLink.ObjectID); CScriptNode *pNode = mpScene->ScriptNodeByID(pLink->ReceiverID());
if (pNode && pNode->Object()->ObjectTypeID() == 0x57415950) // Waypoint if (pNode && pNode->Object()->ObjectTypeID() == 0x57415950) // Waypoint
{ {

View File

@ -1,4 +1,5 @@
#include "CWaypointExtra.h" #include "CWaypointExtra.h"
#include "Core/Resource/Script/CLink.h"
#include "Core/Render/CDrawUtil.h" #include "Core/Render/CDrawUtil.h"
#include "Core/Render/CRenderer.h" #include "Core/Render/CRenderer.h"
#include "Core/Scene/CScene.h" #include "Core/Scene/CScene.h"
@ -78,13 +79,13 @@ void CWaypointExtra::BuildLinks()
{ {
mLinks.clear(); mLinks.clear();
for (u32 iLink = 0; iLink < mpInstance->NumOutLinks(); iLink++) for (u32 iLink = 0; iLink < mpInstance->NumLinks(eOutgoing); iLink++)
{ {
const SLink& rkLink = mpInstance->OutLink(iLink); CLink *pLink = mpInstance->Link(eOutgoing, iLink);
if (IsPathLink(rkLink)) if (IsPathLink(pLink))
{ {
CScriptNode *pNode = mpScene->ScriptNodeByID(rkLink.ObjectID); CScriptNode *pNode = mpScene->ScriptNodeByID(pLink->ReceiverID());
SWaypointLink Link; SWaypointLink Link;
Link.pWaypoint = pNode; Link.pWaypoint = pNode;
@ -97,26 +98,26 @@ void CWaypointExtra::BuildLinks()
mLinksBuilt = true; mLinksBuilt = true;
} }
bool CWaypointExtra::IsPathLink(const SLink& rkLink) bool CWaypointExtra::IsPathLink(CLink *pLink)
{ {
bool Valid = false; bool Valid = false;
if (rkLink.State < 0xFF) if (pLink->State() < 0xFF)
{ {
if (rkLink.State == 0x1 && rkLink.Message == 0x8) Valid = true; // Arrived / Next (MP1) if (pLink->State() == 0x1 && pLink->Message() == 0x8) Valid = true; // Arrived / Next (MP1)
} }
else else
{ {
CFourCC State(rkLink.State); CFourCC State(pLink->State());
CFourCC Message(rkLink.Message); CFourCC Message(pLink->Message());
if (State == "ARRV" && Message == "NEXT") Valid = true; // Arrived / Next (MP2) if (State == "ARRV" && Message == "NEXT") Valid = true; // Arrived / Next (MP2)
if (State == "NEXT" && Message == "ATCH") Valid = true; // Next / Attach (MP3/DKCR) if (State == "NEXT" && Message == "ATCH") Valid = true; // Next / Attach (MP3/DKCR)
} }
if (Valid) if (Valid)
{ {
CScriptNode *pNode = mpScene->ScriptNodeByID(rkLink.ObjectID); CScriptNode *pNode = mpScene->ScriptNodeByID(pLink->ReceiverID());
if (pNode) if (pNode)
return pNode->Object()->ObjectTypeID() == mpInstance->ObjectTypeID(); return pNode->Object()->ObjectTypeID() == mpInstance->ObjectTypeID();

View File

@ -26,7 +26,7 @@ public:
void AddToSplinePath(CSplinePathExtra *pPath); void AddToSplinePath(CSplinePathExtra *pPath);
void RemoveFromSplinePath(CSplinePathExtra *pPath); void RemoveFromSplinePath(CSplinePathExtra *pPath);
void BuildLinks(); void BuildLinks();
bool IsPathLink(const SLink& rkLink); bool IsPathLink(CLink *pLink);
void GetLinkedWaypoints(std::list<CWaypointExtra*>& rOut); void GetLinkedWaypoints(std::list<CWaypointExtra*>& rOut);
void LinksModified(); void LinksModified();

View File

@ -145,7 +145,10 @@ HEADERS += \
WorldEditor/CTemplateEditDialog.h \ WorldEditor/CTemplateEditDialog.h \
WorldEditor/CLinkDialog.h \ WorldEditor/CLinkDialog.h \
WorldEditor/CStateMessageModel.h \ WorldEditor/CStateMessageModel.h \
WorldEditor/CSelectInstanceDialog.h WorldEditor/CSelectInstanceDialog.h \
Undo/CAddLinkCommand.h \
Undo/CDeleteLinksCommand.h \
Undo/CEditLinkCommand.h
# Source Files # Source Files
SOURCES += \ SOURCES += \
@ -204,7 +207,10 @@ SOURCES += \
Undo/CChangeLayerCommand.cpp \ Undo/CChangeLayerCommand.cpp \
WorldEditor/CTemplateEditDialog.cpp \ WorldEditor/CTemplateEditDialog.cpp \
WorldEditor/CLinkDialog.cpp \ WorldEditor/CLinkDialog.cpp \
WorldEditor/CSelectInstanceDialog.cpp WorldEditor/CSelectInstanceDialog.cpp \
Undo/CAddLinkCommand.cpp \
Undo/CDeleteLinksCommand.cpp \
Undo/CEditLinkCommand.cpp
# UI Files # UI Files
FORMS += \ FORMS += \

View File

@ -0,0 +1,35 @@
#include "CAddLinkCommand.h"
#include <Core/Resource/Script/CLink.h>
CAddLinkCommand::CAddLinkCommand(CWorldEditor *pEditor, CLink Link)
: IUndoCommand("Add Link")
, mpEditor(pEditor)
, mLink(Link)
{
mAffectedInstances << mLink.Sender();
if (mLink.Sender() != mLink.Receiver())
mAffectedInstances << mLink.Receiver();
}
void CAddLinkCommand::undo()
{
CScriptObject *pSender = mLink.Sender();
CScriptObject *pReceiver = mLink.Receiver();
u32 SenderIndex = pSender->NumLinks(eOutgoing) - 1;
CLink *pLink = pSender->Link(eOutgoing, SenderIndex);
pSender->RemoveLink(eOutgoing, pLink);
pReceiver->RemoveLink(eIncoming, pLink);
delete pLink;
mpEditor->InstanceLinksModified(mAffectedInstances);
}
void CAddLinkCommand::redo()
{
CLink *pLink = new CLink(mLink);
pLink->Sender()->AddLink(eOutgoing, pLink, -1);
pLink->Receiver()->AddLink(eIncoming, pLink, -1);
mpEditor->InstanceLinksModified(mAffectedInstances);
}

View File

@ -0,0 +1,21 @@
#ifndef CADDLINKCOMMAND_H
#define CADDLINKCOMMAND_H
#include "IUndoCommand.h"
#include "Editor/WorldEditor/CWorldEditor.h"
#include <Core/Resource/Script/CLink.h>
class CAddLinkCommand : public IUndoCommand
{
CWorldEditor *mpEditor;
CLink mLink;
QList<CScriptObject*> mAffectedInstances;
public:
CAddLinkCommand(CWorldEditor *pEditor, CLink Link);
void undo();
void redo();
bool AffectsCleanState() const { return true; }
};
#endif // CADDLINKCOMMAND_H

View File

@ -0,0 +1,97 @@
#include "CDeleteLinksCommand.h"
#include <Core/Resource/Script/CLink.h>
CDeleteLinksCommand::CDeleteLinksCommand(CWorldEditor *pEditor, CScriptObject *pObject, ELinkType Type, const QVector<u32>& rkIndices)
: IUndoCommand("Delete Links")
, mpEditor(pEditor)
{
mAffectedInstances << pObject;
for (int iIdx = 0; iIdx < rkIndices.size(); iIdx++)
{
CLink *pLink = pObject->Link(Type, rkIndices[iIdx]);
SDeletedLink DelLink;
DelLink.State = pLink->State();
DelLink.Message = pLink->Message();
DelLink.pSender = pLink->Sender();
DelLink.pReceiver = pLink->Receiver();
DelLink.SenderIndex = pLink->SenderIndex();
DelLink.ReceiverIndex = pLink->ReceiverIndex();
mLinks << DelLink;
if (Type == eOutgoing)
{
if (!mAffectedInstances.contains(DelLink.pReceiver))
mAffectedInstances << DelLink.pReceiver;
}
else
{
if (!mAffectedInstances.contains(DelLink.pSender))
mAffectedInstances << DelLink.pSender;
}
}
}
void CDeleteLinksCommand::undo()
{
struct SNewLink
{
SDeletedLink *pDelLink;
CLink *pLink;
};
QVector<SNewLink> NewLinks;
for (int iLink = 0; iLink < mLinks.size(); iLink++)
{
SDeletedLink& rDelLink = mLinks[iLink];
SNewLink Link;
Link.pDelLink = &mLinks[iLink];
Link.pLink = new CLink(mpEditor->ActiveArea(), rDelLink.State, rDelLink.Message, rDelLink.pSender->InstanceID(), rDelLink.pReceiver->InstanceID());
NewLinks << Link;
}
// Add to senders
qSort(NewLinks.begin(), NewLinks.end(), [](SNewLink& rLinkA, SNewLink& rLinkB) { return rLinkA.pDelLink->SenderIndex < rLinkB.pDelLink->SenderIndex; });
for (int iLink = 0; iLink < NewLinks.size(); iLink++)
{
SNewLink& rNew = NewLinks[iLink];
rNew.pDelLink->pSender->AddLink(eOutgoing, rNew.pLink, rNew.pDelLink->SenderIndex);
}
// Add to receivers
qSort(NewLinks.begin(), NewLinks.end(), [](SNewLink& rLinkA, SNewLink& rLinkB) { return rLinkA.pDelLink->ReceiverIndex < rLinkB.pDelLink->ReceiverIndex; });
for (int iLink = 0; iLink < NewLinks.size(); iLink++)
{
SNewLink& rNew = NewLinks[iLink];
rNew.pDelLink->pReceiver->AddLink(eIncoming, rNew.pLink, rNew.pDelLink->ReceiverIndex);
}
// Notify world editor
mpEditor->OnLinksModified(mAffectedInstances);
}
void CDeleteLinksCommand::redo()
{
QVector<CLink*> Links;
for (int iLink = 0; iLink < mLinks.size(); iLink++)
{
SDeletedLink& rLink = mLinks[iLink];
Links << rLink.pSender->Link(eOutgoing, rLink.SenderIndex);
}
for (int iLink = 0; iLink < Links.size(); iLink++)
{
CLink *pLink = Links[iLink];
pLink->Sender()->RemoveLink(eOutgoing, pLink);
pLink->Receiver()->RemoveLink(eIncoming, pLink);
delete pLink;
}
// Notify world editor
mpEditor->OnLinksModified(mAffectedInstances);
}

View File

@ -0,0 +1,30 @@
#ifndef CDELETELINKSCOMMAND_H
#define CDELETELINKSCOMMAND_H
#include "IUndoCommand.h"
#include "Editor/WorldEditor/CWorldEditor.h"
class CDeleteLinksCommand : public IUndoCommand
{
CWorldEditor *mpEditor;
QList<CScriptObject*> mAffectedInstances;
struct SDeletedLink
{
u32 State;
u32 Message;
CScriptObject *pSender;
CScriptObject *pReceiver;
u32 SenderIndex;
u32 ReceiverIndex;
};
QVector<SDeletedLink> mLinks;
public:
CDeleteLinksCommand(CWorldEditor *pEditor, CScriptObject *pObject, ELinkType Type, const QVector<u32>& rkIndices);
void undo();
void redo();
bool AffectsCleanState() const { return true; }
};
#endif // CDELETELINKSCOMMAND_H

View File

@ -0,0 +1,50 @@
#include "CEditLinkCommand.h"
CEditLinkCommand::CEditLinkCommand(CWorldEditor *pEditor, CLink *pLink, CLink NewLink)
: IUndoCommand("Edit Link")
, mpEditor(pEditor)
, mpEditLink(pLink)
, mOldLink(*pLink)
, mNewLink(NewLink)
{
mOldSenderIndex = pLink->SenderIndex();
mOldReceiverIndex = pLink->ReceiverIndex();
mAffectedInstances << pLink->Sender();
if (pLink->Receiver() != pLink->Sender()) mAffectedInstances << pLink->Receiver();
if (NewLink.Sender() != pLink->Sender()) mAffectedInstances << NewLink.Sender();
if (NewLink.Receiver() != pLink->Receiver()) mAffectedInstances << NewLink.Receiver();
}
void CEditLinkCommand::undo()
{
if (mOldLink.Sender() != mNewLink.Sender())
{
mNewLink.Sender()->RemoveLink(eOutgoing, mpEditLink);
mOldLink.Sender()->AddLink(eOutgoing, mpEditLink, mOldSenderIndex);
}
if (mOldLink.Receiver() != mNewLink.Receiver())
{
mNewLink.Receiver()->RemoveLink(eIncoming, mpEditLink);
mOldLink.Receiver()->AddLink(eIncoming, mpEditLink, mOldReceiverIndex);
}
*mpEditLink = mOldLink;
mpEditor->OnLinksModified(mAffectedInstances);
}
void CEditLinkCommand::redo()
{
if (mOldLink.Sender() != mNewLink.Sender())
{
mOldLink.Sender()->RemoveLink(eOutgoing, mpEditLink);
mNewLink.Sender()->AddLink(eOutgoing, mpEditLink);
}
if (mOldLink.Receiver() != mNewLink.Receiver())
{
mOldLink.Receiver()->RemoveLink(eIncoming, mpEditLink);
mNewLink.Receiver()->AddLink(eIncoming, mpEditLink);
}
*mpEditLink = mNewLink;
mpEditor->OnLinksModified(mAffectedInstances);
}

View File

@ -0,0 +1,27 @@
#ifndef CEDITLINKCOMMAND_H
#define CEDITLINKCOMMAND_H
#include "IUndoCommand.h"
#include "Editor/WorldEditor/CWorldEditor.h"
#include <Core/Resource/Script/CLink.h>
class CEditLinkCommand : public IUndoCommand
{
CWorldEditor *mpEditor;
CLink *mpEditLink;
CLink mOldLink;
CLink mNewLink;
u32 mOldSenderIndex;
u32 mOldReceiverIndex;
QList<CScriptObject*> mAffectedInstances;
public:
CEditLinkCommand(CWorldEditor *pEditor, CLink *pLink, CLink NewLink);
void undo();
void redo();
bool AffectsCleanState() const { return true; }
};
#endif // CEDITLINKCOMMAND_H

View File

@ -15,6 +15,10 @@
#include "CResizeScriptArrayCommand.h" #include "CResizeScriptArrayCommand.h"
#include "CChangeLayerCommand.h" #include "CChangeLayerCommand.h"
#include "CAddLinkCommand.h"
#include "CDeleteLinksCommand.h"
#include "CEditLinkCommand.h"
#include "EUndoCommand.h" #include "EUndoCommand.h"
#endif // UNDOCOMMANDS #endif // UNDOCOMMANDS

View File

@ -2,6 +2,9 @@
#include "ui_CLinkDialog.h" #include "ui_CLinkDialog.h"
#include "CSelectInstanceDialog.h" #include "CSelectInstanceDialog.h"
#include "CStateMessageModel.h" #include "CStateMessageModel.h"
#include "CWorldEditor.h"
#include "Editor/Undo/CAddLinkCommand.h"
#include "Editor/Undo/CEditLinkCommand.h"
#include <Core/Resource/Script/CScriptObject.h> #include <Core/Resource/Script/CScriptObject.h>
CLinkDialog::CLinkDialog(CWorldEditor *pEditor, QWidget *pParent /*= 0*/) CLinkDialog::CLinkDialog(CWorldEditor *pEditor, QWidget *pParent /*= 0*/)
@ -13,6 +16,7 @@ CLinkDialog::CLinkDialog(CWorldEditor *pEditor, QWidget *pParent /*= 0*/)
, mpReceiver(nullptr) , mpReceiver(nullptr)
, mSenderStateModel(CStateMessageModel::eStates, this) , mSenderStateModel(CStateMessageModel::eStates, this)
, mReceiverMessageModel(CStateMessageModel::eMessages, this) , mReceiverMessageModel(CStateMessageModel::eMessages, this)
, mpEditLink(nullptr)
{ {
ui->setupUi(this); ui->setupUi(this);
ui->SenderStateComboBox->setModel(&mSenderStateModel); ui->SenderStateComboBox->setModel(&mSenderStateModel);
@ -44,6 +48,29 @@ void CLinkDialog::showEvent(QShowEvent *)
SetReceiverNameLabel(); SetReceiverNameLabel();
} }
void CLinkDialog::NewLink(CScriptObject *pSender, CScriptObject *pReceiver)
{
mpEditLink = nullptr;
SetSender(pSender);
SetReceiver(pReceiver);
if (pSender) ui->SenderStateComboBox->setCurrentIndex(0);
if (pReceiver) ui->ReceiverMessageComboBox->setCurrentIndex(0);
}
void CLinkDialog::EditLink(CLink *pLink)
{
mpEditLink = pLink;
CScriptObject *pSender = pLink->Sender();
CScriptObject *pReceiver = pLink->Receiver();
SetSender(pSender);
SetReceiver(pReceiver);
if (pSender)
ui->SenderStateComboBox->setCurrentIndex(mSenderStateModel.StateIndex(pLink->State()));
if (pReceiver)
ui->ReceiverMessageComboBox->setCurrentIndex(mReceiverMessageModel.MessageIndex(pLink->Message()));
}
void CLinkDialog::SetMaster(CMasterTemplate *pMaster) void CLinkDialog::SetMaster(CMasterTemplate *pMaster)
{ {
if (mpMaster != pMaster) if (mpMaster != pMaster)
@ -99,7 +126,7 @@ u32 CLinkDialog::State() const
u32 CLinkDialog::Message() const u32 CLinkDialog::Message() const
{ {
return mReceiverMessageModel.State(ui->ReceiverMessageComboBox->currentIndex()); return mReceiverMessageModel.Message(ui->ReceiverMessageComboBox->currentIndex());
} }
void CLinkDialog::SetSenderNameLabel() void CLinkDialog::SetSenderNameLabel()
@ -127,6 +154,25 @@ void CLinkDialog::SetReceiverNameLabel()
} }
// ************ PUBLIC SLOTS ************ // ************ PUBLIC SLOTS ************
void CLinkDialog::accept()
{
CLink Link(mpEditor->ActiveArea(), State(), Message(), Sender()->InstanceID(), Receiver()->InstanceID());
if (!mpEditLink)
{
CAddLinkCommand *pCmd = new CAddLinkCommand(mpEditor, Link);
mpEditor->UndoStack()->push(pCmd);
}
else if (Link != *mpEditLink)
{
CEditLinkCommand *pCmd = new CEditLinkCommand(mpEditor, mpEditLink, Link);
mpEditor->UndoStack()->push(pCmd);
}
QDialog::accept();
}
void CLinkDialog::OnSwapClicked() void CLinkDialog::OnSwapClicked()
{ {
CScriptObject *pSender = mpReceiver; CScriptObject *pSender = mpReceiver;

View File

@ -2,13 +2,14 @@
#define CLINKDIALOG_H #define CLINKDIALOG_H
#include "CStateMessageModel.h" #include "CStateMessageModel.h"
#include "CWorldEditor.h"
#include <QDialog> #include <QDialog>
namespace Ui { namespace Ui {
class CLinkDialog; class CLinkDialog;
} }
class CWorldEditor;
class CLinkDialog : public QDialog class CLinkDialog : public QDialog
{ {
Q_OBJECT Q_OBJECT
@ -17,6 +18,7 @@ class CLinkDialog : public QDialog
CMasterTemplate *mpMaster; CMasterTemplate *mpMaster;
CScriptObject *mpSender; CScriptObject *mpSender;
CScriptObject *mpReceiver; CScriptObject *mpReceiver;
CLink *mpEditLink;
CStateMessageModel mSenderStateModel; CStateMessageModel mSenderStateModel;
CStateMessageModel mReceiverMessageModel; CStateMessageModel mReceiverMessageModel;
@ -29,6 +31,9 @@ public:
void resizeEvent(QResizeEvent *); void resizeEvent(QResizeEvent *);
void showEvent(QShowEvent *); void showEvent(QShowEvent *);
void NewLink(CScriptObject *pSender, CScriptObject *pReceiver);
void EditLink(CLink *pLink);
void SetMaster(CMasterTemplate *pMaster); void SetMaster(CMasterTemplate *pMaster);
void SetSender(CScriptObject *pSender); void SetSender(CScriptObject *pSender);
void SetReceiver(CScriptObject *pReceiver); void SetReceiver(CScriptObject *pReceiver);
@ -42,6 +47,7 @@ public:
inline CScriptObject* Receiver() const { return mpReceiver; } inline CScriptObject* Receiver() const { return mpReceiver; }
public slots: public slots:
void accept();
void OnSwapClicked(); void OnSwapClicked();
void OnPickFromViewportClicked(); void OnPickFromViewportClicked();
void OnPickModeClick(const SRayIntersection& rkHit, QMouseEvent *pEvent); void OnPickModeClick(const SRayIntersection& rkHit, QMouseEvent *pEvent);

View File

@ -15,7 +15,7 @@ void CLinkModel::SetObject(CScriptObject *pObj)
emit layoutChanged(); emit layoutChanged();
} }
void CLinkModel::SetConnectionType(EConnectionType type) void CLinkModel::SetConnectionType(ELinkType type)
{ {
mType = type; mType = type;
emit layoutChanged(); emit layoutChanged();
@ -24,12 +24,7 @@ void CLinkModel::SetConnectionType(EConnectionType type)
int CLinkModel::rowCount(const QModelIndex&) const int CLinkModel::rowCount(const QModelIndex&) const
{ {
if (mpObject) if (mpObject)
{ return mpObject->NumLinks(mType);
if (mType == eIncoming)
return mpObject->NumInLinks();
else
return mpObject->NumOutLinks();
}
else return 0; else return 0;
} }
@ -45,21 +40,22 @@ QVariant CLinkModel::data(const QModelIndex &index, int role) const
else if ((role == Qt::DisplayRole) || (role == Qt::ToolTipRole)) else if ((role == Qt::DisplayRole) || (role == Qt::ToolTipRole))
{ {
SLink link = (mType == eIncoming ? mpObject->InLink(index.row()) : mpObject->OutLink(index.row())); CLink *pLink = mpObject->Link(mType, index.row());
switch (index.column()) switch (index.column())
{ {
case 0: // Column 0 - Target Object case 0: // Column 0 - Target Object
{ {
CScriptObject *pTargetObj = mpObject->Area()->GetInstanceByID(link.ObjectID); u32 TargetID = (mType == eIncoming ? pLink->SenderID() : pLink->ReceiverID());
CScriptObject *pTarget = mpObject->Area()->GetInstanceByID(TargetID);
if (pTargetObj) { if (pTarget) {
QString ObjType = QString("[%1] ").arg(UICommon::ToQString(pTargetObj->Template()->Name())); QString ObjType = QString("[%1] ").arg(UICommon::ToQString(pTarget->Template()->Name()));
return ObjType + UICommon::ToQString(pTargetObj->InstanceName()); return ObjType + UICommon::ToQString(pTarget->InstanceName());
} }
else { else {
QString strID = QString::number(link.ObjectID, 16); QString strID = QString::number(TargetID, 16);
while (strID.length() < 8) strID = "0" + strID; while (strID.length() < 8) strID = "0" + strID;
return QString("External: 0x") + strID; return QString("External: 0x") + strID;
} }
@ -67,13 +63,13 @@ QVariant CLinkModel::data(const QModelIndex &index, int role) const
case 1: // Column 1 - State case 1: // Column 1 - State
{ {
TString StateName = mpObject->MasterTemplate()->StateByID(link.State).Name; TString StateName = mpObject->MasterTemplate()->StateByID(pLink->State()).Name;
return UICommon::ToQString(StateName); return UICommon::ToQString(StateName);
} }
case 2: // Column 2 - Message case 2: // Column 2 - Message
{ {
TString MessageName = mpObject->MasterTemplate()->MessageByID(link.Message).Name; TString MessageName = mpObject->MasterTemplate()->MessageByID(pLink->Message()).Name;
return UICommon::ToQString(MessageName); return UICommon::ToQString(MessageName);
} }

View File

@ -8,19 +8,13 @@ class CLinkModel : public QAbstractTableModel
{ {
Q_OBJECT Q_OBJECT
public:
enum EConnectionType {
eIncoming, eOutgoing
};
private:
CScriptObject *mpObject; CScriptObject *mpObject;
EConnectionType mType; ELinkType mType;
public: public:
explicit CLinkModel(QObject *pParent = 0); explicit CLinkModel(QObject *pParent = 0);
void SetObject(CScriptObject *pObj); void SetObject(CScriptObject *pObj);
void SetConnectionType(EConnectionType type); void SetConnectionType(ELinkType type);
int rowCount(const QModelIndex& parent = QModelIndex()) const; int rowCount(const QModelIndex& parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent) const; int columnCount(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const; QVariant data(const QModelIndex &index, int role) const;

View File

@ -91,7 +91,33 @@ public:
endResetModel(); endResetModel();
} }
void SetScriptTemplate(CScriptTemplate *pScript) u32 StateIndex(u32 StateID) const
{
if (mType == eMessages) return -1;
for (int iState = 0; iState < mEntries.size(); iState++)
{
if (mEntries[iState].ID == StateID)
return iState;
}
return -1;
}
u32 MessageIndex(u32 MessageID) const
{
if (mType == eStates) return -1;
for (int iMsg = 0; iMsg < mEntries.size(); iMsg++)
{
if (mEntries[iMsg].ID == MessageID)
return iMsg;
}
return -1;
}
inline void SetScriptTemplate(CScriptTemplate *pScript)
{ {
mpScript = pScript; mpScript = pScript;
} }

View File

@ -31,6 +31,7 @@ CWorldEditor::CWorldEditor(QWidget *parent) :
mpArea = nullptr; mpArea = nullptr;
mpWorld = nullptr; mpWorld = nullptr;
mpLinkDialog = new CLinkDialog(this, this);
mpPoiDialog = nullptr; mpPoiDialog = nullptr;
mGizmoHovering = false; mGizmoHovering = false;
mGizmoTransforming = false; mGizmoTransforming = false;
@ -108,6 +109,7 @@ void CWorldEditor::closeEvent(QCloseEvent *pEvent)
if (ShouldClose) if (ShouldClose)
{ {
mUndoStack.clear(); mUndoStack.clear();
mpLinkDialog->close();
if (mpPoiDialog) if (mpPoiDialog)
mpPoiDialog->close(); mpPoiDialog->close();
@ -176,6 +178,9 @@ void CWorldEditor::SetArea(CWorld *pWorld, CGameArea *pArea, u32 AreaIndex)
CMasterTemplate *pMaster = CMasterTemplate::GetMasterForGame(mpArea->Version()); CMasterTemplate *pMaster = CMasterTemplate::GetMasterForGame(mpArea->Version());
ui->InstancesTabContents->SetMaster(pMaster); ui->InstancesTabContents->SetMaster(pMaster);
// Set up dialogs
mpLinkDialog->SetMaster(pMaster);
// Set window title // Set window title
CStringTable *pWorldNameTable = mpWorld->GetWorldName(); CStringTable *pWorldNameTable = mpWorld->GetWorldName();
TWideString WorldName = pWorldNameTable ? pWorldNameTable->GetString("ENGL", 0) : "[Untitled World]"; TWideString WorldName = pWorldNameTable ? pWorldNameTable->GetString("ENGL", 0) : "[Untitled World]";
@ -248,6 +253,17 @@ bool CWorldEditor::Save()
} }
} }
void CWorldEditor::OnLinksModified(const QList<CScriptObject*>& rkInstances)
{
foreach (CScriptObject *pInstance, rkInstances)
{
CScriptNode *pNode = mScene.NodeForObject(pInstance);
pNode->LinksModified();
}
emit InstanceLinksModified(rkInstances);
}
void CWorldEditor::OnPropertyModified(IProperty *pProp) void CWorldEditor::OnPropertyModified(IProperty *pProp)
{ {
bool EditorProperty = false; bool EditorProperty = false;

View File

@ -1,6 +1,7 @@
#ifndef CWORLDEDITOR_H #ifndef CWORLDEDITOR_H
#define CWORLDEDITOR_H #define CWORLDEDITOR_H
#include "CLinkDialog.h"
#include "CPoiMapEditDialog.h" #include "CPoiMapEditDialog.h"
#include "Editor/INodeEditor.h" #include "Editor/INodeEditor.h"
#include "Editor/CGizmo.h" #include "Editor/CGizmo.h"
@ -35,6 +36,7 @@ class CWorldEditor : public INodeEditor
TResPtr<CGameArea> mpArea; TResPtr<CGameArea> mpArea;
QTimer mRefreshTimer; QTimer mRefreshTimer;
CLinkDialog *mpLinkDialog;
CPoiMapEditDialog *mpPoiDialog; CPoiMapEditDialog *mpPoiDialog;
public: public:
@ -47,9 +49,11 @@ public:
inline CGameArea* ActiveArea() const { return mpArea; } inline CGameArea* ActiveArea() const { return mpArea; }
inline EGame CurrentGame() const { return mpArea ? mpArea->Version() : eUnknownVersion; } inline EGame CurrentGame() const { return mpArea ? mpArea->Version() : eUnknownVersion; }
inline CLinkDialog* LinkDialog() const { return mpLinkDialog; }
public slots: public slots:
bool Save(); bool Save();
void OnLinksModified(const QList<CScriptObject*>& rkInstances);
void OnPropertyModified(IProperty *pProp); void OnPropertyModified(IProperty *pProp);
void SetSelectionActive(bool Active); void SetSelectionActive(bool Active);
void SetSelectionInstanceNames(const QString& rkNewName, bool IsDone); void SetSelectionInstanceNames(const QString& rkNewName, bool IsDone);
@ -101,6 +105,7 @@ signals:
void LayersModified(); void LayersModified();
void InstancesLayerAboutToChange(); void InstancesLayerAboutToChange();
void InstancesLayerChanged(const QList<CScriptNode*>& rkInstanceList); void InstancesLayerChanged(const QList<CScriptNode*>& rkInstanceList);
void InstanceLinksModified(const QList<CScriptObject*>& rkInstances);
void PropertyModified(IProperty *pProp, bool IsEditorProperty); void PropertyModified(IProperty *pProp, bool IsEditorProperty);
}; };

View File

@ -3,6 +3,7 @@
#include "CLinkDialog.h" #include "CLinkDialog.h"
#include "CWorldEditor.h" #include "CWorldEditor.h"
#include "Editor/Undo/UndoCommands.h"
#include <Core/Scene/CScriptNode.h> #include <Core/Scene/CScriptNode.h>
#include <QScrollArea> #include <QScrollArea>
@ -20,10 +21,9 @@ WModifyTab::WModifyTab(QWidget *pParent)
ui->PropertyView->header()->setSectionResizeMode(1, QHeaderView::Fixed); ui->PropertyView->header()->setSectionResizeMode(1, QHeaderView::Fixed);
mpInLinkModel = new CLinkModel(this); mpInLinkModel = new CLinkModel(this);
mpInLinkModel->SetConnectionType(CLinkModel::eIncoming); mpInLinkModel->SetConnectionType(eIncoming);
mpOutLinkModel = new CLinkModel(this); mpOutLinkModel = new CLinkModel(this);
mpOutLinkModel->SetConnectionType(CLinkModel::eOutgoing); mpOutLinkModel->SetConnectionType(eOutgoing);
mpLinkDialog = nullptr;
ui->InLinksTableView->setModel(mpInLinkModel); ui->InLinksTableView->setModel(mpInLinkModel);
ui->OutLinksTableView->setModel(mpOutLinkModel); ui->OutLinksTableView->setModel(mpOutLinkModel);
@ -31,10 +31,14 @@ WModifyTab::WModifyTab(QWidget *pParent)
ui->OutLinksTableView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); ui->OutLinksTableView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
connect(ui->InLinksTableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(OnLinkTableDoubleClick(QModelIndex))); connect(ui->InLinksTableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(OnLinkTableDoubleClick(QModelIndex)));
connect(ui->OutLinksTableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(OnLinkTableDoubleClick(QModelIndex))); connect(ui->OutLinksTableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(OnLinkTableDoubleClick(QModelIndex)));
connect(ui->InLinksTableView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(OnIncomingLinksSelectionModified()));
connect(ui->OutLinksTableView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(OnOutgoingLinksSelectionModified()));
connect(ui->AddOutgoingConnectionButton, SIGNAL(clicked()), this, SLOT(OnAddOutgoingLinkClicked())); connect(ui->AddOutgoingConnectionButton, SIGNAL(clicked()), this, SLOT(OnAddOutgoingLinkClicked()));
connect(ui->AddIncomingConnectionButton, SIGNAL(clicked()), this, SLOT(OnAddIncomingLinkClicked())); connect(ui->AddIncomingConnectionButton, SIGNAL(clicked()), this, SLOT(OnAddIncomingLinkClicked()));
connect(ui->DeleteOutgoingConnectionButton, SIGNAL(clicked()), this, SLOT(OnDeleteOutgoingLinkClicked())); connect(ui->DeleteOutgoingConnectionButton, SIGNAL(clicked()), this, SLOT(OnDeleteOutgoingLinkClicked()));
connect(ui->DeleteIncomingConnectionButton, SIGNAL(clicked()), this, SLOT(OnDeleteIncomingLinkClicked())); connect(ui->DeleteIncomingConnectionButton, SIGNAL(clicked()), this, SLOT(OnDeleteIncomingLinkClicked()));
connect(ui->EditOutgoingConnectionButton, SIGNAL(clicked()), this, SLOT(OnEditOutgoingLinkClicked()));
connect(ui->EditIncomingConnectionButton, SIGNAL(clicked()), this, SLOT(OnEditIncomingLinkClicked()));
ClearUI(); ClearUI();
} }
@ -48,8 +52,8 @@ void WModifyTab::SetEditor(CWorldEditor *pEditor)
{ {
mpWorldEditor = pEditor; mpWorldEditor = pEditor;
ui->PropertyView->SetEditor(mpWorldEditor); ui->PropertyView->SetEditor(mpWorldEditor);
connect(mpWorldEditor, SIGNAL(Closed()), this, SLOT(OnWorldEditorClosed()));
connect(mpWorldEditor, SIGNAL(SelectionTransformed()), this, SLOT(OnWorldSelectionTransformed())); connect(mpWorldEditor, SIGNAL(SelectionTransformed()), this, SLOT(OnWorldSelectionTransformed()));
connect(mpWorldEditor, SIGNAL(InstanceLinksModified(const QList<CScriptObject*>&)), this, SLOT(OnInstanceLinksModified(const QList<CScriptObject*>&)));
} }
void WModifyTab::GenerateUI(QList<CSceneNode*>& Selection) void WModifyTab::GenerateUI(QList<CSceneNode*>& Selection)
@ -69,9 +73,12 @@ void WModifyTab::GenerateUI(QList<CSceneNode*>& Selection)
// Set up UI // Set up UI
ui->PropertyView->SetInstance(pObj); ui->PropertyView->SetInstance(pObj);
ui->LightGroupBox->hide();
ui->InLinksTableView->clearSelection();
ui->OutLinksTableView->clearSelection();
mpInLinkModel->SetObject(pObj); mpInLinkModel->SetObject(pObj);
mpOutLinkModel->SetObject(pObj); mpOutLinkModel->SetObject(pObj);
ui->LightGroupBox->hide();
} }
} }
} }
@ -88,36 +95,21 @@ void WModifyTab::ClearUI()
mpSelectedNode = nullptr; mpSelectedNode = nullptr;
} }
void WModifyTab::CreateLinkDialog() // ************ PUBLIC SLOTS ************
void WModifyTab::OnInstanceLinksModified(const QList<CScriptObject*>& rkInstances)
{ {
if (!mpLinkDialog)
{
mpLinkDialog = new CLinkDialog(mpWorldEditor, this);
if (mpSelectedNode && mpSelectedNode->NodeType() == eScriptNode) if (mpSelectedNode && mpSelectedNode->NodeType() == eScriptNode)
{ {
CScriptNode *pScript = static_cast<CScriptNode*>(mpSelectedNode); CScriptObject *pInstance = static_cast<CScriptNode*>(mpSelectedNode)->Object();
mpLinkDialog->SetMaster(pScript->Object()->MasterTemplate());
}
connect(mpLinkDialog, SIGNAL(accepted()), this, SLOT(OnLinkDialogAccept())); if (pInstance && rkInstances.contains(pInstance))
connect(mpLinkDialog, SIGNAL(rejected()), this, SLOT(OnLinkDialogReject()));
}
}
void WModifyTab::DeleteLinkDialog()
{ {
if (mpLinkDialog) mpInLinkModel->layoutChanged();
{ mpOutLinkModel->layoutChanged();
delete mpLinkDialog; ui->InLinksTableView->clearSelection();
mpLinkDialog = nullptr; ui->OutLinksTableView->clearSelection();
} }
} }
// ************ PUBLIC SLOTS ************
void WModifyTab::OnWorldEditorClosed()
{
DeleteLinkDialog();
} }
void WModifyTab::OnWorldSelectionTransformed() void WModifyTab::OnWorldSelectionTransformed()
@ -125,20 +117,26 @@ void WModifyTab::OnWorldSelectionTransformed()
ui->PropertyView->UpdateEditorProperties(QModelIndex()); ui->PropertyView->UpdateEditorProperties(QModelIndex());
} }
void WModifyTab::OnOutgoingLinksSelectionModified()
{
u32 NumSelectedRows = ui->OutLinksTableView->selectionModel()->selectedRows().size();
ui->EditOutgoingConnectionButton->setEnabled(NumSelectedRows == 1);
}
void WModifyTab::OnIncomingLinksSelectionModified()
{
u32 NumSelectedRows = ui->InLinksTableView->selectionModel()->selectedRows().size();
ui->EditIncomingConnectionButton->setEnabled(NumSelectedRows == 1);
}
void WModifyTab::OnAddOutgoingLinkClicked() void WModifyTab::OnAddOutgoingLinkClicked()
{ {
if (mpSelectedNode && mpSelectedNode->NodeType() == eScriptNode) if (mpSelectedNode && mpSelectedNode->NodeType() == eScriptNode)
{ {
CScriptObject *pInst = static_cast<CScriptNode*>(mpSelectedNode)->Object(); CScriptObject *pInst = static_cast<CScriptNode*>(mpSelectedNode)->Object();
CreateLinkDialog(); CLinkDialog *pDialog = mpWorldEditor->LinkDialog();
pDialog->NewLink(pInst, nullptr);
if (mpLinkDialog->Sender() != pInst) pDialog->show();
{
mpLinkDialog->SetSender(pInst);
mpLinkDialog->SetReceiver(nullptr);
}
mpLinkDialog->show();
} }
} }
@ -147,34 +145,82 @@ void WModifyTab::OnAddIncomingLinkClicked()
if (mpSelectedNode && mpSelectedNode->NodeType() == eScriptNode) if (mpSelectedNode && mpSelectedNode->NodeType() == eScriptNode)
{ {
CScriptObject *pInst = static_cast<CScriptNode*>(mpSelectedNode)->Object(); CScriptObject *pInst = static_cast<CScriptNode*>(mpSelectedNode)->Object();
CreateLinkDialog(); CLinkDialog *pDialog = mpWorldEditor->LinkDialog();
pDialog->NewLink(nullptr, pInst);
if (mpLinkDialog->Receiver() != pInst) pDialog->show();
{
mpLinkDialog->SetSender(nullptr);
mpLinkDialog->SetReceiver(pInst);
}
mpLinkDialog->show();
} }
} }
void WModifyTab::OnDeleteOutgoingLinkClicked() void WModifyTab::OnDeleteOutgoingLinkClicked()
{ {
if (mpSelectedNode && mpSelectedNode->NodeType() == eScriptNode)
{
QModelIndexList SelectedIndices = ui->OutLinksTableView->selectionModel()->selectedRows();
if (!SelectedIndices.isEmpty())
{
QVector<u32> Indices;
for (int iIdx = 0; iIdx < SelectedIndices.size(); iIdx++)
Indices << SelectedIndices[iIdx].row();
CScriptObject *pInst = static_cast<CScriptNode*>(mpSelectedNode)->Object();
CDeleteLinksCommand *pCmd = new CDeleteLinksCommand(mpWorldEditor, pInst, eOutgoing, Indices);
mpWorldEditor->UndoStack()->push(pCmd);
}
}
} }
void WModifyTab::OnDeleteIncomingLinkClicked() void WModifyTab::OnDeleteIncomingLinkClicked()
{ {
if (mpSelectedNode && mpSelectedNode->NodeType() == eScriptNode)
{
QModelIndexList SelectedIndices = ui->InLinksTableView->selectionModel()->selectedRows();
if (!SelectedIndices.isEmpty())
{
QVector<u32> Indices;
for (int iIdx = 0; iIdx < SelectedIndices.size(); iIdx++)
Indices << SelectedIndices[iIdx].row();
CScriptObject *pInst = static_cast<CScriptNode*>(mpSelectedNode)->Object();
CDeleteLinksCommand *pCmd = new CDeleteLinksCommand(mpWorldEditor, pInst, eIncoming, Indices);
mpWorldEditor->UndoStack()->push(pCmd);
}
}
} }
void WModifyTab::OnLinkDialogAccept() void WModifyTab::OnEditOutgoingLinkClicked()
{ {
DeleteLinkDialog(); if (mpSelectedNode && mpSelectedNode->NodeType() == eScriptNode)
{
QModelIndexList SelectedIndices = ui->OutLinksTableView->selectionModel()->selectedRows();
if (SelectedIndices.size() == 1)
{
CScriptObject *pInst = static_cast<CScriptNode*>(mpSelectedNode)->Object();
CLinkDialog *pDialog = mpWorldEditor->LinkDialog();
pDialog->EditLink(pInst->Link(eOutgoing, SelectedIndices.front().row()));
pDialog->show();
}
}
} }
void WModifyTab::OnLinkDialogReject() void WModifyTab::OnEditIncomingLinkClicked()
{ {
DeleteLinkDialog(); if (mpSelectedNode && mpSelectedNode->NodeType() == eScriptNode)
{
QModelIndexList SelectedIndices = ui->InLinksTableView->selectionModel()->selectedRows();
if (SelectedIndices.size() == 1)
{
CScriptObject *pInst = static_cast<CScriptNode*>(mpSelectedNode)->Object();
CLinkDialog *pDialog = mpWorldEditor->LinkDialog();
pDialog->EditLink(pInst->Link(eIncoming, SelectedIndices.front().row()));
pDialog->show();
}
}
} }
// ************ PRIVATE SLOTS ************ // ************ PRIVATE SLOTS ************
@ -184,14 +230,14 @@ void WModifyTab::OnLinkTableDoubleClick(QModelIndex Index)
{ {
// The link table will only be visible if the selected node is a script node // The link table will only be visible if the selected node is a script node
CScriptNode *pNode = static_cast<CScriptNode*>(mpSelectedNode); CScriptNode *pNode = static_cast<CScriptNode*>(mpSelectedNode);
SLink Link; u32 InstanceID;
if (sender() == ui->InLinksTableView) if (sender() == ui->InLinksTableView)
Link = pNode->Object()->InLink(Index.row()); InstanceID = pNode->Object()->Link(eIncoming, Index.row())->SenderID();
else if (sender() == ui->OutLinksTableView) else if (sender() == ui->OutLinksTableView)
Link = pNode->Object()->OutLink(Index.row()); InstanceID = pNode->Object()->Link(eOutgoing, Index.row())->ReceiverID();
CScriptNode *pLinkedNode = pNode->Scene()->ScriptNodeByID(Link.ObjectID); CScriptNode *pLinkedNode = pNode->Scene()->ScriptNodeByID(InstanceID);
if (pLinkedNode) if (pLinkedNode)
{ {

View File

@ -28,28 +28,25 @@ class WModifyTab : public QWidget
CLinkModel *mpInLinkModel; CLinkModel *mpInLinkModel;
CLinkModel *mpOutLinkModel; CLinkModel *mpOutLinkModel;
CLinkDialog *mpLinkDialog;
public: public:
explicit WModifyTab(QWidget *pParent = 0); explicit WModifyTab(QWidget *pParent = 0);
~WModifyTab(); ~WModifyTab();
void SetEditor(CWorldEditor *pEditor); void SetEditor(CWorldEditor *pEditor);
void GenerateUI(QList<CSceneNode*>& Selection); void GenerateUI(QList<CSceneNode*>& Selection);
void ClearUI(); void ClearUI();
void CreateLinkDialog();
void DeleteLinkDialog();
public slots: public slots:
void OnWorldEditorClosed(); void OnInstanceLinksModified(const QList<CScriptObject*>& rkInstances);
void OnWorldSelectionTransformed(); void OnWorldSelectionTransformed();
void OnOutgoingLinksSelectionModified();
void OnIncomingLinksSelectionModified();
void OnAddOutgoingLinkClicked(); void OnAddOutgoingLinkClicked();
void OnAddIncomingLinkClicked(); void OnAddIncomingLinkClicked();
void OnDeleteOutgoingLinkClicked(); void OnDeleteOutgoingLinkClicked();
void OnDeleteIncomingLinkClicked(); void OnDeleteIncomingLinkClicked();
void OnLinkDialogAccept(); void OnEditOutgoingLinkClicked();
void OnLinkDialogReject(); void OnEditIncomingLinkClicked();
private: private:
Ui::WModifyTab *ui; Ui::WModifyTab *ui;

View File

@ -127,11 +127,14 @@
<item> <item>
<widget class="QTableView" name="OutLinksTableView"> <widget class="QTableView" name="OutLinksTableView">
<property name="sizeAdjustPolicy"> <property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum> <enum>QAbstractScrollArea::AdjustIgnored</enum>
</property> </property>
<property name="editTriggers"> <property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set> <set>QAbstractItemView::NoEditTriggers</set>
</property> </property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="verticalScrollMode"> <property name="verticalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum> <enum>QAbstractItemView::ScrollPerPixel</enum>
</property> </property>
@ -173,6 +176,28 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QPushButton" name="EditOutgoingConnectionButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>30</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Edit</string>
</property>
</widget>
</item>
<item> <item>
<spacer name="OutgoingButtonsSpacer"> <spacer name="OutgoingButtonsSpacer">
<property name="orientation"> <property name="orientation">
@ -205,6 +230,18 @@
<layout class="QVBoxLayout" name="verticalLayout_2"> <layout class="QVBoxLayout" name="verticalLayout_2">
<item> <item>
<widget class="QTableView" name="InLinksTableView"> <widget class="QTableView" name="InLinksTableView">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="verticalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="horizontalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<attribute name="horizontalHeaderDefaultSectionSize"> <attribute name="horizontalHeaderDefaultSectionSize">
<number>75</number> <number>75</number>
</attribute> </attribute>
@ -237,6 +274,22 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QPushButton" name="EditIncomingConnectionButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="maximumSize">
<size>
<width>39</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Edit</string>
</property>
</widget>
</item>
<item> <item>
<spacer name="IncomingButtonsSpacer"> <spacer name="IncomingButtonsSpacer">
<property name="orientation"> <property name="orientation">