Files
UnrealEngine/Engine/Source/Programs/Enterprise/Datasmith/DatasmithARCHICADExporter/Private/ConnectionWindow.cpp
2025-05-18 13:04:45 +08:00

483 lines
16 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "ConnectionWindow.h"
#include "ResourcesIDs.h"
#include "Synchronizer.h"
#include "Commander.h"
#include "Menus.h"
#include "Utils/ShellOpenDocument.h"
#include "Utils/TaskCalledFromEventLoop.h"
#undef TicksPerSecond
#include "DirectLinkEndpoint.h"
#include "DatasmithDirectLink.h"
BEGIN_NAMESPACE_UE_AC
static class FConnectionDialog* ConnectionDialog = nullptr;
template < class Key, class Element >
bool Equals(const TMap< Key, Element >& InMap1, const TMap< Key, Element >& InMap2);
template < class Element > bool Equals(const TArray< Element >& InArray1, const TArray< Element >& InArray2);
inline bool operator==(const DirectLink::FRawInfo::FDataPointId& InDataPointId1,
const DirectLink::FRawInfo::FDataPointId& InDataPointId2)
{
return InDataPointId1.Name == InDataPointId2.Name && InDataPointId1.Id == InDataPointId2.Id &&
InDataPointId1.bIsPublic == InDataPointId2.bIsPublic;
}
inline bool operator==(const DirectLink::FRawInfo::FEndpointInfo& InEndpointInfo1,
const DirectLink::FRawInfo::FEndpointInfo& InEndpointInfo2)
{
return InEndpointInfo1.Name == InEndpointInfo2.Name &&
InEndpointInfo1.Version.ExactMatch(InEndpointInfo2.Version) &&
Equals(InEndpointInfo1.Destinations, InEndpointInfo2.Destinations) &&
Equals(InEndpointInfo1.Sources, InEndpointInfo2.Sources) &&
InEndpointInfo1.UserName == InEndpointInfo2.UserName &&
InEndpointInfo1.ExecutableName == InEndpointInfo2.ExecutableName &&
InEndpointInfo1.ComputerName == InEndpointInfo2.ComputerName &&
InEndpointInfo1.bIsLocal == InEndpointInfo2.bIsLocal &&
InEndpointInfo1.ProcessId == InEndpointInfo2.ProcessId;
}
inline bool operator==(const DirectLink::FRawInfo::FDataPointInfo& InDataPointInfo1,
const DirectLink::FRawInfo::FDataPointInfo& InDataPointInfo2)
{
return InDataPointInfo1.EndpointAddress == InDataPointInfo2.EndpointAddress &&
InDataPointInfo1.Name == InDataPointInfo2.Name && InDataPointInfo1.bIsSource == InDataPointInfo2.bIsSource &&
InDataPointInfo1.bIsPublic == InDataPointInfo2.bIsPublic;
}
inline bool operator==(const DirectLink::FCommunicationStatus& InCommunicationStatus1,
const DirectLink::FCommunicationStatus& InCommunicationStatus2)
{
return InCommunicationStatus1.bIsSending == InCommunicationStatus2.bIsSending &&
InCommunicationStatus1.bIsReceiving == InCommunicationStatus2.bIsReceiving &&
InCommunicationStatus1.TaskTotal == InCommunicationStatus2.TaskTotal &&
InCommunicationStatus1.TaskCompleted == InCommunicationStatus2.TaskCompleted;
}
inline bool operator==(const DirectLink::FRawInfo::FStreamInfo& InStreamInfo1,
const DirectLink::FRawInfo::FStreamInfo& InStreamInfo2)
{
return InStreamInfo1.StreamId == InStreamInfo2.StreamId && InStreamInfo1.Source == InStreamInfo2.Source &&
InStreamInfo1.Destination == InStreamInfo2.Destination &&
InStreamInfo1.ConnectionState == InStreamInfo2.ConnectionState &&
InStreamInfo1.CommunicationStatus == InStreamInfo2.CommunicationStatus;
}
inline bool operator==(const DirectLink::FRawInfo& InState1, const DirectLink::FRawInfo& InState2)
{
return InState1.ThisEndpointAddress == InState2.ThisEndpointAddress &&
Equals(InState1.EndpointsInfo, InState2.EndpointsInfo) &&
Equals(InState1.DataPointsInfo, InState2.DataPointsInfo) &&
Equals(InState1.StreamsInfo, InState2.StreamsInfo);
}
template < class Key, class Element >
bool Equals(const TMap< Key, Element >& InMap1, const TMap< Key, Element >& InMap2)
{
int32 NbElements = InMap1.Num();
if (NbElements != InMap2.Num())
{
return false;
}
for (const auto& Pair : InMap1)
{
const Element* Value = InMap2.Find(Pair.Key);
if (Value == nullptr || !(*Value == Pair.Value))
{
return false;
}
}
return true;
}
template < class Element > bool Equals(const TArray< Element >& InArray1, const TArray< Element >& InArray2)
{
int32 NbElements = InArray1.Num();
if (NbElements != InArray2.Num())
{
return false;
}
for (int32 Index = 0; Index < NbElements; ++Index)
{
if (!(InArray1[Index] == InArray2[Index]))
{
return false;
}
}
return true;
}
// Class that observe endpoint state change
class FEndpointObserver : public DirectLink::IEndpointObserver
{
public:
FEndpointObserver()
: CV(AccessControl)
, Endpoint(FDatasmithDirectLink::GetEnpoint())
{
Endpoint->AddEndpointObserver(this);
}
~FEndpointObserver()
{
Endpoint->RemoveEndpointObserver(this);
bChanged = false;
}
bool GetStatus(DirectLink::FRawInfo* OutStatus)
{
GS::Guard< GS::Lock > lck(AccessControl);
if (!bChanged)
{
return false;
}
*OutStatus = LastRawInfo;
bChanged = false;
return true;
}
private:
virtual void OnStateChanged(const DirectLink::FRawInfo& InRawInfo) override
{
GS::Guard< GS::Lock > lck(AccessControl);
if (!(InRawInfo == LastRawInfo))
{
LastRawInfo = InRawInfo;
bChanged = true;
}
}
TSharedRef< DirectLink::FEndpoint, ESPMode::ThreadSafe > Endpoint;
DirectLink::FRawInfo LastRawInfo;
bool bChanged = false;
// Control access on this object (for queue operations)
GS::Lock AccessControl;
// Condition variable
GS::Condition CV;
};
class FToolTipText : public GS::Object
{
public:
FToolTipText(const GS::UniString& InString)
: ToolTipText(InString)
{}
GS::UniString ToolTipText;
};
class FConnectionDialog : public DG::Palette,
public DG::PanelObserver,
public DG::ListBoxObserver,
public DG::ButtonItemObserver,
public DG::CompoundItemObserver
{
enum
{
kConnectionsSingleSelListId = 1,
kNoConnectionTextId,
kCacheFolderTextId,
kGotoCacheFolderButtonId,
kChooseCacheFolderButtonId
};
enum
{
kSourceColumn = 1,
kDestinationColumn,
kNbColumns = kDestinationColumn
};
DG::SingleSelListBox ConnectionsListBox;
DG::LeftText NoConnectionText;
DG::LeftText CacheFolderText;
DG::IconButton GotoCacheFolderButton;
DG::IconButton ChooseCacheFolderButton;
FEndpointObserver EndpointObserver;
DirectLink::FRawInfo CurrentStatus;
static GS::Guid PaletteGuid;
public:
FConnectionDialog();
~FConnectionDialog();
virtual void PanelOpened(const DG::PanelOpenEvent& /*ev*/) override
{
// SetClientSize(GetOriginalClientWidth(), GetOriginalClientHeight());
}
virtual void PanelClosed(const DG::PanelCloseEvent& /* ev */) override {}
virtual void PanelCloseRequested(const DG::PanelCloseRequestEvent& /* ev */, bool* bAccepted) override
{
*bAccepted = true;
Hide();
FTaskCalledFromEventLoop::CallFunctorFromEventLoop([]() { FConnectionWindow::DeleteWindow(); });
}
virtual void PanelIdle(const DG::PanelIdleEvent& /* ev */) override
{
if (EndpointObserver.GetStatus(&CurrentStatus))
{
short ItemsCount = ConnectionsListBox.GetItemCount();
while (ItemsCount > 0)
{
ConnectionsListBox.DeleteItem(ItemsCount--);
}
// Grab the sources
TMap< FGuid, const DirectLink::FRawInfo::FDataPointId* > SourcesMap;
const DirectLink::FRawInfo::FEndpointInfo* SourceEndpointInfo =
CurrentStatus.EndpointsInfo.Find(CurrentStatus.ThisEndpointAddress);
if (SourceEndpointInfo != nullptr)
{
// Grab all the sources
SourcesMap.Reserve(SourceEndpointInfo->Sources.Num());
for (const DirectLink::FRawInfo::FDataPointId& NamedId : SourceEndpointInfo->Sources)
{
SourcesMap.Add(NamedId.Id, &NamedId);
}
}
// Grab all the potential destinations
class FDestination
{
public:
const DirectLink::FRawInfo::FEndpointInfo* EndpointInfo;
const DirectLink::FRawInfo::FDataPointId* DataPointId;
};
TMap< FGuid, FDestination > DestinationsMap;
for (const TPair< FMessageAddress, DirectLink::FRawInfo::FEndpointInfo >& EnpointsInfoPair :
CurrentStatus.EndpointsInfo)
{
const DirectLink::FRawInfo::FEndpointInfo& EndpointInfo = EnpointsInfoPair.Value;
DestinationsMap.Reserve(EndpointInfo.Destinations.Num());
for (const DirectLink::FRawInfo::FDataPointId& NamedId : EndpointInfo.Destinations)
{
DestinationsMap.Add(NamedId.Id, {&EndpointInfo, &NamedId});
}
}
// Collect all active connections between our EndPoint and destinations
FString ThisText;
for (const DirectLink::FRawInfo::FStreamInfo& StreamInfo : CurrentStatus.StreamsInfo)
{
if (StreamInfo.ConnectionState == DirectLink::EStreamConnectionState::Active)
{
const DirectLink::FRawInfo::FDataPointId** SourceDataPointId = SourcesMap.Find(StreamInfo.Source);
const FDestination* DestinationDataPointId = DestinationsMap.Find(StreamInfo.Destination);
if (SourceDataPointId != nullptr && DestinationDataPointId != nullptr)
{
ConnectionsListBox.InsertItem(++ItemsCount);
ConnectionsListBox.SetTabItemText(ItemsCount, kSourceColumn,
UEToGSString(*(*SourceDataPointId)->Name));
FString DestinationName(DestinationDataPointId->EndpointInfo->Name + TEXT(" : ") +
DestinationDataPointId->DataPointId->Name);
ConnectionsListBox.SetTabItemText(ItemsCount, kDestinationColumn,
UEToGSString(*DestinationName));
/* kName_FmtTooltip "User : %T\nComputer Name : %T\nProgram Name: %T\nEndpoint Name : %T" */
GS::UniString ToolTipString(GS::UniString::Printf(GetGSName(kName_FmtTooltip),
UEToGSString(*DestinationDataPointId->EndpointInfo->UserName).ToPrintf(),
UEToGSString(*DestinationDataPointId->EndpointInfo->ComputerName).ToPrintf(),
UEToGSString(*DestinationDataPointId->EndpointInfo->ExecutableName).ToPrintf(),
UEToGSString(*DestinationDataPointId->EndpointInfo->Name).ToPrintf()));
ConnectionsListBox.SetItemObjectData(ItemsCount, new FToolTipText(ToolTipString));
}
}
}
if (ItemsCount > 0)
{
ConnectionsListBox.Show();
NoConnectionText.Hide();
}
else
{
ConnectionsListBox.Hide();
NoConnectionText.Show();
}
UE_AC_TraceF("Connection status changed\n");
}
}
virtual void PanelResized(const DG::PanelResizeEvent& ev) override
{
if (ev.GetSource() == this)
{
ConnectionsListBox.MoveAndResize(0, 0, ev.GetHorizontalChange(), ev.GetVerticalChange());
const short ListBoxSize = ConnectionsListBox.GetItemWidth();
const short SourceColumnSize = ListBoxSize / 3;
const short DestinationColumnSize = ListBoxSize - SourceColumnSize;
ConnectionsListBox.SetHeaderItemSize(kSourceColumn, SourceColumnSize);
ConnectionsListBox.SetHeaderItemSize(kDestinationColumn, DestinationColumnSize);
ConnectionsListBox.SetTabFieldProperties(kSourceColumn, 0, SourceColumnSize, DG::ListBox::Left,
DG::ListBox::MiddleTruncate, false);
ConnectionsListBox.SetTabFieldProperties(kDestinationColumn, SourceColumnSize,
SourceColumnSize + DestinationColumnSize, DG::ListBox::Left,
DG::ListBox::MiddleTruncate, false);
NoConnectionText.MoveAndResize(0, 0, ev.GetHorizontalChange(), ev.GetVerticalChange());
CacheFolderText.MoveAndResize(0, ev.GetVerticalChange(), ev.GetHorizontalChange(), 0);
GotoCacheFolderButton.MoveAndResize(ev.GetHorizontalChange(), ev.GetVerticalChange(), 0, 0);
ChooseCacheFolderButton.MoveAndResize(ev.GetHorizontalChange(), ev.GetVerticalChange(), 0, 0);
}
}
virtual void ButtonClicked(const DG::ButtonClickEvent& ev) override
{
if (ev.GetSource() == &ChooseCacheFolderButton)
{
GS::UniString CachePath(FSyncDatabase::GetCachePath());
IO::Location NewFolderLocation;
IO::Location CurrentFolderLocation(CachePath);
if (DGBrowseForFolder(&NewFolderLocation, &CurrentFolderLocation, GS::UniString("Choose a cache folder")))
{
GSErrCode GSErr = NewFolderLocation.ToPath(&CachePath);
if (GSErr == NoError)
{
UE_AC_TraceF("FConnectionDialog::ButtonClicked - New cache folder is \"%s\"\n", CachePath.ToUtf8());
FSyncDatabase::SetCachePath(CachePath);
UpdateCacheDirectoryText();
}
else
{
UE_AC_DebugF("FConnectionDialog::ButtonClicked - NewFolderLocation.ToPath return error %s\n",
GetErrorName(GSErr));
}
}
}
else if (ev.GetSource() == &GotoCacheFolderButton)
{
UE_AC::ShellOpenDocument(FSyncDatabase::GetCachePath().ToUtf8());
}
}
void UpdateCacheDirectoryText()
{
CacheFolderText.SetText(GetGSName(kName_CacheDirectory) + FSyncDatabase::GetCachePath());
}
#if PLATFORM_MAC & AC_VERSION > 25
virtual void ItemMouseExited(const DG::ItemMouseMoveEvent& /*ev*/) override {}
virtual void ItemMouseEntered(const DG::ItemMouseMoveEvent& /*ev*/) override {}
virtual short SpecMouseExited(const DG::ItemMouseMoveEvent& /*ev*/) override { return 0; }
virtual short SpecMouseEntered(const DG::ItemMouseMoveEvent& /*ev*/) override { return 0; }
#endif
};
FConnectionDialog::FConnectionDialog()
: DG::Palette(ACAPI_GetOwnResModule(), LocalizeResId(kDlgConnections), ACAPI_GetOwnResModule(), PaletteGuid)
, ConnectionsListBox(GetReference(), kConnectionsSingleSelListId)
, NoConnectionText(GetReference(), kNoConnectionTextId)
, CacheFolderText(GetReference(), kCacheFolderTextId)
, GotoCacheFolderButton(GetReference(), kGotoCacheFolderButtonId)
, ChooseCacheFolderButton(GetReference(), kChooseCacheFolderButtonId)
{
// initialize the listbox
ConnectionsListBox.SetTabFieldCount(kNbColumns);
ConnectionsListBox.SetHeaderSynchronState(false);
const short ListBoxSize = ConnectionsListBox.GetItemWidth();
const short SourceColumnSize = ListBoxSize / 3;
const short DestinationColumnSize = ListBoxSize - SourceColumnSize;
ConnectionsListBox.SetHeaderItemSize(kSourceColumn, SourceColumnSize);
ConnectionsListBox.SetHeaderItemSize(kDestinationColumn, DestinationColumnSize);
ConnectionsListBox.SetTabFieldProperties(kSourceColumn, 0, SourceColumnSize, DG::ListBox::Left,
DG::ListBox::MiddleTruncate, false);
ConnectionsListBox.SetTabFieldProperties(kDestinationColumn, SourceColumnSize,
SourceColumnSize + DestinationColumnSize, DG::ListBox::Left,
DG::ListBox::MiddleTruncate, false);
ConnectionsListBox.SetHeaderItemText(kSourceColumn, GS::UniString("Source"));
ConnectionsListBox.SetHeaderItemText(kDestinationColumn, GS::UniString("Destination"));
ConnectionsListBox.SetHelpStyle(ConnectionsListBox.HSForItem);
ConnectionsListBox.Hide();
#if AC_VERSION >= 24
ConnectionsListBox.onToolTipRequested += [this](const DG::Item& inItem, DG::HelpEventArg& EventArgs) -> short
{
if (&inItem != &ConnectionsListBox)
{
UE_AC_DebugF("ConnectionsListBox.onToolTipRequested - Item isn't ConnectionsListBox\n");
return 0;
}
if (EventArgs.subMessage != DG_HSM_TOOLTIP || EventArgs.listItem == 0)
{
return 0;
}
const GS::Object* Object = ConnectionsListBox.GetItemObjectData(EventArgs.listItem);
if (Object == nullptr)
{
UE_AC_DebugF("ConnectionsListBox.onToolTipRequested - Object is null\n");
return 0;
}
EventArgs.toolTipText += static_cast<const FToolTipText&>(*Object).ToolTipText;
return 1;
};
#endif
Attach(*this);
AttachToAllItems(*this);
UpdateCacheDirectoryText();
bool SendForInactiveApp = false;
EnableIdleEvent(SendForInactiveApp);
ConnectionDialog = this;
BeginEventProcessing();
}
FConnectionDialog::~FConnectionDialog()
{
EndEventProcessing();
DetachFromAllItems(*this);
Detach(*this);
if (ConnectionDialog == this)
{
ConnectionDialog = nullptr;
}
}
GS::Guid FConnectionDialog::PaletteGuid("5E23E6F0-7F6A-4BA4-91EC-2F7BA22D4CB3");
void FConnectionWindow::ShowWindow()
{
if (ConnectionDialog == nullptr)
{
new FConnectionDialog();
}
if (ConnectionDialog != nullptr)
{
ConnectionDialog->Show();
ConnectionDialog->BringToFront();
}
}
void FConnectionWindow::DeleteWindow()
{
if (ConnectionDialog != nullptr)
{
delete ConnectionDialog;
ConnectionDialog = nullptr;
}
}
END_NAMESPACE_UE_AC