Files
UnrealEngine/Engine/Source/Editor/WorldBrowser/Private/LevelCollectionModel.cpp
2025-05-18 13:04:45 +08:00

2279 lines
64 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "LevelCollectionModel.h"
#include "Algo/AnyOf.h"
#include "Misc/PackageName.h"
#include "AssetRegistry/AssetData.h"
#include "Misc/MessageDialog.h"
#include "Misc/ConfigCacheIni.h"
#include "Misc/FeedbackContext.h"
#include "Modules/ModuleManager.h"
#include "Framework/MultiBox/MultiBoxBuilder.h"
#include "ISourceControlOperation.h"
#include "SourceControlOperations.h"
#include "ISourceControlModule.h"
#include "SourceControlHelpers.h"
#include "Settings/EditorLoadingSavingSettings.h"
#include "Engine/Selection.h"
#include "EditorModeManager.h"
#include "EditorModes.h"
#include "FileHelpers.h"
#include "ScopedTransaction.h"
#include "EditorLevelUtils.h"
#include "LevelCollectionCommands.h"
#include "SourceControlWindows.h"
#include "IAssetTools.h"
#include "IAssetTypeActions.h"
#include "AssetToolsModule.h"
#include "DiffUtils.h"
#include "EditorSupportDelegates.h"
#include "GameFramework/WorldSettings.h"
#include "ShaderCompiler.h"
#include "FoliageEditModule.h"
#include "InstancedFoliageActor.h"
#include "FoliageEditUtility.h"
#include "Engine/WorldComposition.h"
#include "Misc/ScopeExit.h"
#include "LevelUtils.h"
#include "WorldTreeItemTypes.h"
#define LOCTEXT_NAMESPACE "WorldBrowser"
FLevelCollectionModel::FLevelCollectionModel()
: bRequestedUpdateAllLevels(false)
, bRequestedRedrawAllLevels(false)
, bRequestedUpdateActorsCount(false)
, CommandList(MakeShareable(new FUICommandList))
, Filters(MakeShareable(new LevelFilterCollection))
, WorldSize(FIntPoint::ZeroValue)
, bDisplayPaths(false)
, bCanExecuteSCCCheckOut(false)
, bCanExecuteSCCOpenForAdd(false)
, bCanExecuteSCCCheckIn(false)
, bCanExecuteSCC(false)
, bSelectionHasChanged(true)
, bUpdatingLevelsSelection(false)
{
FEditorDelegates::RefreshLevelBrowser.AddRaw(this, &FLevelCollectionModel::PopulateLevelsList);
}
FLevelCollectionModel::~FLevelCollectionModel()
{
SaveSettings();
Filters->OnChanged().RemoveAll(this);
FWorldDelegates::LevelAddedToWorld.RemoveAll(this);
FWorldDelegates::LevelRemovedFromWorld.RemoveAll(this);
FEditorSupportDelegates::RedrawAllViewports.RemoveAll(this);
GEditor->OnLevelActorAdded().RemoveAll(this);
GEditor->OnLevelActorDeleted().RemoveAll(this);
if (CurrentWorld.IsValid())
{
CurrentWorld->OnSelectedLevelsChanged().RemoveAll(this);
}
FEditorDelegates::RefreshLevelBrowser.RemoveAll(this);
}
void FLevelCollectionModel::Initialize(UWorld* InWorld)
{
LoadSettings();
CurrentWorld = InWorld;
Filters->OnChanged().AddSP(this, &FLevelCollectionModel::OnFilterChanged);
FWorldDelegates::LevelAddedToWorld.AddSP(this, &FLevelCollectionModel::OnLevelAddedToWorld);
FWorldDelegates::LevelRemovedFromWorld.AddSP(this, &FLevelCollectionModel::OnLevelRemovedFromWorld);
FEditorSupportDelegates::RedrawAllViewports.AddSP(this, &FLevelCollectionModel::OnRedrawAllViewports);
GEditor->OnLevelActorAdded().AddSP( this, &FLevelCollectionModel::OnLevelActorAdded);
GEditor->OnLevelActorDeleted().AddSP( this, &FLevelCollectionModel::OnLevelActorDeleted);
USelection::SelectionChangedEvent.AddSP(this, &FLevelCollectionModel::OnActorSelectionChanged);
SelectionChanged.AddSP(this, &FLevelCollectionModel::OnActorOrLevelSelectionChanged);
CurrentWorld->OnSelectedLevelsChanged().AddSP(this, &FLevelCollectionModel::OnLevelsSelectionChangedOutside);
PopulateLevelsList();
}
void FLevelCollectionModel::BindCommands()
{
const FLevelCollectionCommands& Commands = FLevelCollectionCommands::Get();
FUICommandList& ActionList = *CommandList;
ActionList.MapAction(Commands.RefreshBrowser,
FExecuteAction::CreateSP(this, &FLevelCollectionModel::RefreshBrowser_Executed));
ActionList.MapAction(Commands.ExpandSelectedItems,
FExecuteAction::CreateSP(this, &FLevelCollectionModel::ExpandSelectedItems_Executed),
FCanExecuteAction::CreateSP(this, &FLevelCollectionModel::AreAnyLevelsSelected));
ActionList.MapAction(Commands.World_MakeLevelCurrent,
FExecuteAction::CreateSP(this, &FLevelCollectionModel::MakeLevelCurrent_Executed),
FCanExecuteAction::CreateSP(this, &FLevelCollectionModel::IsOneLevelSelected));
ActionList.MapAction(Commands.World_FindInContentBrowser,
FExecuteAction::CreateSP(this, &FLevelCollectionModel::FindInContentBrowser_Executed),
FCanExecuteAction::CreateSP(this, &FLevelCollectionModel::IsValidFindInContentBrowser));
ActionList.MapAction(Commands.MoveActorsToSelected,
FExecuteAction::CreateSP(this, &FLevelCollectionModel::MoveActorsToSelected_Executed),
FCanExecuteAction::CreateSP(this, &FLevelCollectionModel::IsValidMoveActorsToLevel));
ActionList.MapAction(Commands.MoveFoliageToSelected,
FExecuteAction::CreateSP(this, &FLevelCollectionModel::MoveFoliageToSelected_Executed),
FCanExecuteAction::CreateSP(this, &FLevelCollectionModel::IsValidMoveFoliageToLevel));
ActionList.MapAction(Commands.World_SaveSelectedLevels,
FExecuteAction::CreateSP(this, &FLevelCollectionModel::SaveSelectedLevels_Executed),
FCanExecuteAction::CreateSP(this, &FLevelCollectionModel::AreAnySelectedLevelsDirty));
ActionList.MapAction(Commands.World_SaveSelectedLevelAs,
FExecuteAction::CreateSP(this, &FLevelCollectionModel::SaveSelectedLevelAs_Executed),
FCanExecuteAction::CreateSP(this, &FLevelCollectionModel::IsSelectedLevelEditable));
ActionList.MapAction(Commands.World_LoadLevel,
FExecuteAction::CreateSP(this, &FLevelCollectionModel::LoadSelectedLevels_Executed),
FCanExecuteAction::CreateSP(this, &FLevelCollectionModel::AreAnySelectedLevelsUnloaded));
ActionList.MapAction(Commands.World_UnloadLevel,
FExecuteAction::CreateSP(this, &FLevelCollectionModel::UnloadSelectedLevels_Executed),
FCanExecuteAction::CreateSP(this, &FLevelCollectionModel::AreAllSelectedLevelsUserManaged));
ActionList.MapAction( Commands.World_MigrateSelectedLevels,
FExecuteAction::CreateSP( this, &FLevelCollectionModel::MigrateSelectedLevels_Executed),
FCanExecuteAction::CreateSP( this, &FLevelCollectionModel::AreAllSelectedLevelsEditable));
//actors
ActionList.MapAction(Commands.AddsActors,
FExecuteAction::CreateSP(this, &FLevelCollectionModel::SelectActors_Executed),
FCanExecuteAction::CreateSP(this, &FLevelCollectionModel::AreAnySelectedLevelsEditable));
ActionList.MapAction(Commands.RemovesActors,
FExecuteAction::CreateSP(this, &FLevelCollectionModel::DeselectActors_Executed),
FCanExecuteAction::CreateSP(this, &FLevelCollectionModel::AreAnySelectedLevelsEditable));
ActionList.MapAction(Commands.ConvertLevelToExternalActors,
FExecuteAction::CreateSP(this, &FLevelCollectionModel::ConvertLevelToExternalActors_Executed, true),
FCanExecuteAction::CreateSP(this, &FLevelCollectionModel::CanConvertAnyLevelToExternalActors, true));
ActionList.MapAction(Commands.ConvertLevelToInternalActors,
FExecuteAction::CreateSP(this, &FLevelCollectionModel::ConvertLevelToExternalActors_Executed, false),
FCanExecuteAction::CreateSP(this, &FLevelCollectionModel::CanConvertAnyLevelToExternalActors, false));
//editor visibility
ActionList.MapAction( Commands.World_ShowInEditorSelectedLevels,
FExecuteAction::CreateSP( this, &FLevelCollectionModel::ShowInEditorSelectedLevels_Executed ),
FCanExecuteAction::CreateSP( this, &FLevelCollectionModel::AreAnySelectedLevelsLoaded ) );
ActionList.MapAction( Commands.World_HideInEditorSelectedLevels,
FExecuteAction::CreateSP( this, &FLevelCollectionModel::HideInEditorSelectedLevels_Executed ),
FCanExecuteAction::CreateSP( this, &FLevelCollectionModel::AreAnySelectedLevelsLoaded ) );
ActionList.MapAction( Commands.World_ShowInEditorOnlySelectedLevels,
FExecuteAction::CreateSP( this, &FLevelCollectionModel::ShowInEditorOnlySelectedLevels_Executed ),
FCanExecuteAction::CreateSP( this, &FLevelCollectionModel::AreAnySelectedLevelsLoaded ) );
ActionList.MapAction(Commands.World_ShowInEditorAllButSelectedLevels,
FExecuteAction::CreateSP(this, &FLevelCollectionModel::ShowInEditorAllButSelectedLevels_Executed),
FCanExecuteAction::CreateSP(this, &FLevelCollectionModel::AreAnySelectedLevelsLoaded));
ActionList.MapAction(Commands.World_ShowInEditorAllLevels,
FExecuteAction::CreateSP(this, &FLevelCollectionModel::ShowInEditorAllLevels_Executed));
ActionList.MapAction(Commands.World_HideInEditorAllLevels,
FExecuteAction::CreateSP(this, &FLevelCollectionModel::HideInEditorAllLevels_Executed));
//game visibility
ActionList.MapAction( Commands.World_ShowInGameSelectedLevels,
FExecuteAction::CreateSP( this, &FLevelCollectionModel::ShowInGameSelectedLevels_Executed ),
FCanExecuteAction::CreateSP( this, &FLevelCollectionModel::CanExecuteGameVisibilityCommandsForSelectedLevels ) );
ActionList.MapAction( Commands.World_HideInGameSelectedLevels,
FExecuteAction::CreateSP( this, &FLevelCollectionModel::HideInGameSelectedLevels_Executed ),
FCanExecuteAction::CreateSP( this, &FLevelCollectionModel::CanExecuteGameVisibilityCommandsForSelectedLevels ) );
ActionList.MapAction( Commands.World_ShowInGameOnlySelectedLevels,
FExecuteAction::CreateSP( this, &FLevelCollectionModel::ShowInGameOnlySelectedLevels_Executed ),
FCanExecuteAction::CreateSP( this, &FLevelCollectionModel::CanExecuteGameVisibilityCommandsForSelectedLevels ) );
ActionList.MapAction(Commands.World_ShowInGameAllButSelectedLevels,
FExecuteAction::CreateSP(this, &FLevelCollectionModel::ShowInGameAllButSelectedLevels_Executed),
FCanExecuteAction::CreateSP(this, &FLevelCollectionModel::CanExecuteGameVisibilityCommandsForSelectedLevels));
ActionList.MapAction(Commands.World_ShowInGameAllLevels,
FExecuteAction::CreateSP(this, &FLevelCollectionModel::ShowInGameAllLevels_Executed),
FCanExecuteAction::CreateSP(this, &FLevelCollectionModel::CanExecuteGameVisibilityCommands));
ActionList.MapAction(Commands.World_HideInGameAllLevels,
FExecuteAction::CreateSP(this, &FLevelCollectionModel::HideInGameAllLevels_Executed),
FCanExecuteAction::CreateSP(this, &FLevelCollectionModel::CanExecuteGameVisibilityCommands));
//lock
ActionList.MapAction( Commands.World_LockSelectedLevels,
FExecuteAction::CreateSP( this, &FLevelCollectionModel::LockSelectedLevels_Executed ) );
ActionList.MapAction( Commands.World_UnlockSelectedLevels,
FExecuteAction::CreateSP( this, &FLevelCollectionModel::UnlockSelectedLevels_Executed ) );
ActionList.MapAction(Commands.World_LockOnlySelectedLevels,
FExecuteAction::CreateSP(this, &FLevelCollectionModel::LockOnlySelectedLevels_Executed));
ActionList.MapAction(Commands.World_LockAllButSelectedLevels,
FExecuteAction::CreateSP(this, &FLevelCollectionModel::LockAllButSelectedLevels_Executed));
ActionList.MapAction( Commands.World_LockAllLevels,
FExecuteAction::CreateSP( this, &FLevelCollectionModel::LockAllLevels_Executed ) );
ActionList.MapAction( Commands.World_UnlockAllLevels,
FExecuteAction::CreateSP( this, &FLevelCollectionModel::UnlockAllLevels_Executed ) );
ActionList.MapAction( Commands.World_LockReadOnlyLevels,
FExecuteAction::CreateSP( this, &FLevelCollectionModel::ToggleReadOnlyLevels_Executed ) );
ActionList.MapAction( Commands.World_UnlockReadOnlyLevels,
FExecuteAction::CreateSP( this, &FLevelCollectionModel::ToggleReadOnlyLevels_Executed ) );
//level selection
ActionList.MapAction( Commands.SelectAllLevels,
FExecuteAction::CreateSP( this, &FLevelCollectionModel::SelectAllLevels_Executed ) );
ActionList.MapAction( Commands.DeselectAllLevels,
FExecuteAction::CreateSP( this, &FLevelCollectionModel::DeselectAllLevels_Executed ) );
ActionList.MapAction( Commands.InvertLevelSelection,
FExecuteAction::CreateSP( this, &FLevelCollectionModel::InvertSelection_Executed ) );
//source control
ActionList.MapAction( Commands.SCCCheckOut,
FExecuteAction::CreateSP( this, &FLevelCollectionModel::OnSCCCheckOut ) );
ActionList.MapAction( Commands.SCCCheckIn,
FExecuteAction::CreateSP( this, &FLevelCollectionModel::OnSCCCheckIn ) );
ActionList.MapAction( Commands.SCCOpenForAdd,
FExecuteAction::CreateSP( this, &FLevelCollectionModel::OnSCCOpenForAdd ) );
ActionList.MapAction( Commands.SCCHistory,
FExecuteAction::CreateSP( this, &FLevelCollectionModel::OnSCCHistory ) );
ActionList.MapAction( Commands.SCCRefresh,
FExecuteAction::CreateSP( this, &FLevelCollectionModel::OnSCCRefresh ) );
ActionList.MapAction( Commands.SCCDiffAgainstDepot,
FExecuteAction::CreateSP( this, &FLevelCollectionModel::OnSCCDiffAgainstDepot ) );
ActionList.MapAction( Commands.SCCConnect,
FExecuteAction::CreateSP( this, &FLevelCollectionModel::OnSCCConnect ) );
}
void FLevelCollectionModel::PopulateLevelsList()
{
RootLevelsList.Empty();
AllLevelsList.Empty();
FilteredLevelsList.Empty();
SelectedLevelsList.Empty();
AllLevelsMap.Empty();
OnLevelsCollectionChanged();
}
void FLevelCollectionModel::PopulateFilteredLevelsList()
{
FilteredLevelsList.Empty();
// Filter out our flat list
for (auto& LevelModel : AllLevelsList)
{
LevelModel->SetLevelFilteredOutFlag(true);
if (LevelModel->IsPersistent() || PassesAllFilters(*LevelModel))
{
FilteredLevelsList.Add(LevelModel);
LevelModel->SetLevelFilteredOutFlag(false);
}
}
// Walk through hierarchy and filter it out
for (auto It = RootLevelsList.CreateIterator(); It; ++It)
{
(*It)->OnFilterChanged();
}
}
void FLevelCollectionModel::Tick( float DeltaTime )
{
if (GEditor == nullptr)
{
// Could it be during hot-reloading?
return;
}
if (bRequestedUpdateAllLevels)
{
UpdateAllLevels();
}
if (bRequestedRedrawAllLevels)
{
RedrawAllLevels();
}
if (bRequestedUpdateActorsCount)
{
UpdateLevelActorsCount();
}
if (IsSimulating())
{
// Reset simulation status for all levels
for (TSharedPtr<FLevelModel>& LevelModel : AllLevelsList)
{
LevelModel->UpdateSimulationStatus(nullptr);
}
// Traverse streaming levels and update simulation status for corresponding level models
for (ULevelStreaming* StreamingLevel : GetSimulationWorld()->GetStreamingLevels())
{
// Rebuild the original NonPrefixedPackageName so we can find it
const FString PrefixedPackageName = StreamingLevel->GetWorldAssetPackageName();
const FString NonPrefixedPackageName = FPackageName::GetLongPackagePath(PrefixedPackageName) + "/"
+ FPackageName::GetLongPackageAssetName(PrefixedPackageName).RightChop(GetSimulationWorld()->StreamingLevelsPrefix.Len());
TSharedPtr<FLevelModel> LevelModel = FindLevelModel(FName(*NonPrefixedPackageName));
if (LevelModel.IsValid())
{
LevelModel->UpdateSimulationStatus(StreamingLevel);
}
}
}
}
TStatId FLevelCollectionModel::GetStatId() const
{
RETURN_QUICK_DECLARE_CYCLE_STAT(FLevelCollectionModel, STATGROUP_Tickables);
}
bool FLevelCollectionModel::IsReadOnly() const
{
// read only in PIE/SIE
return IsSimulating();
}
bool FLevelCollectionModel::IsSimulating() const
{
return (GEditor->bIsSimulatingInEditor || GEditor->PlayWorld != NULL);
}
UWorld* FLevelCollectionModel::GetSimulationWorld() const
{
return GEditor->PlayWorld;
}
bool FLevelCollectionModel::IsOriginRebasingEnabled() const
{
UWorld* ThisWorld = GetWorld();
return ThisWorld && ThisWorld->GetWorldSettings()->bEnableWorldOriginRebasing;
}
FLevelModelList& FLevelCollectionModel::GetRootLevelList()
{
return RootLevelsList;
}
const FLevelModelList& FLevelCollectionModel::GetAllLevels() const
{
return AllLevelsList;
}
const FLevelModelList& FLevelCollectionModel::GetFilteredLevels() const
{
return FilteredLevelsList;
}
const FLevelModelList& FLevelCollectionModel::GetSelectedLevels() const
{
return SelectedLevelsList;
}
void FLevelCollectionModel::AddFilter(const TSharedRef<LevelFilter>& InFilter)
{
Filters->Add(InFilter);
OnFilterChanged();
}
void FLevelCollectionModel::RemoveFilter(const TSharedRef<LevelFilter>& InFilter)
{
Filters->Remove(InFilter);
OnFilterChanged();
}
bool FLevelCollectionModel::IsFilterActive() const
{
return (AllLevelsList.Num() != FilteredLevelsList.Num());
}
void FLevelCollectionModel::SetSelectedLevels(const FLevelModelList& InList)
{
// Clear selection flag from currently selected levels
for (auto LevelModel : SelectedLevelsList)
{
LevelModel->SetLevelSelectionFlag(false);
}
SelectedLevelsList.Empty();
// Set selection flag to selected levels
for (auto& LevelModel : InList)
{
if (LevelModel.IsValid() && PassesAllFilters(*LevelModel))
{
LevelModel->SetLevelSelectionFlag(true);
SelectedLevelsList.Add(LevelModel);
}
}
OnLevelsSelectionChanged();
}
void FLevelCollectionModel::SetSelectedLevelsFromWorld()
{
TArray<TObjectPtr<ULevel>>& SelectedLevelObjects = CurrentWorld->GetSelectedLevels();
FLevelModelList LevelsToSelect;
for (ULevel* LevelObject : SelectedLevelObjects)
{
TSharedPtr<FLevelModel> LevelModel = FindLevelModel(LevelObject);
if (LevelModel.IsValid())
{
LevelsToSelect.Add(LevelModel);
}
}
SetSelectedLevels(LevelsToSelect);
}
TSharedPtr<FLevelModel> FLevelCollectionModel::FindLevelModel(ULevel* InLevel) const
{
if (InLevel)
{
for (auto It = AllLevelsList.CreateConstIterator(); It; ++It)
{
if ((*It)->GetLevelObject() == InLevel)
{
return (*It);
}
}
}
// not found
return TSharedPtr<FLevelModel>();
}
TSharedPtr<FLevelModel> FLevelCollectionModel::FindLevelModel(const FName& PackageName) const
{
const TSharedPtr<FLevelModel>* LevelModel = AllLevelsMap.Find(PackageName);
if (LevelModel != NULL)
{
return *LevelModel;
}
// not found
return TSharedPtr<FLevelModel>();
}
void FLevelCollectionModel::IterateHierarchy(FLevelModelVisitor& Visitor)
{
for (auto It = RootLevelsList.CreateIterator(); It; ++It)
{
(*It)->Accept(Visitor);
}
}
void FLevelCollectionModel::HideLevelsInEditor(const FLevelModelList& InLevelList)
{
if (IsReadOnly())
{
return;
}
// Disable Origin Tracking
UWorldComposition* WorldComposition = GetWorld()->WorldComposition;
if (WorldComposition)
{
WorldComposition->bTemporarilyDisableOriginTracking = true;
}
ON_SCOPE_EXIT
{
// Reenable Origin Tracking
if (WorldComposition)
{
WorldComposition->bTemporarilyDisableOriginTracking = false;
}
};
// For efficiency, set visibility of all levels at once
TArray<FLevelModel*> LevelModels;
TArray<bool> bVisible;
for (auto It = InLevelList.CreateConstIterator(); It; ++It)
{
LevelModels.Add(It->Get());
bVisible.Add(false);
}
FLevelModel::SetVisibleInEditor(LevelModels, bVisible);
RequestUpdateAllLevels();
}
void FLevelCollectionModel::ShowLevelsInEditor(const FLevelModelList& InLevelList)
{
if (IsReadOnly())
{
return;
}
// Disable Origin Tracking
UWorldComposition* WorldComposition = GetWorld()->WorldComposition;
if (WorldComposition)
{
WorldComposition->bTemporarilyDisableOriginTracking = true;
}
ON_SCOPE_EXIT
{
// Reenable Origin Tracking
if (WorldComposition)
{
WorldComposition->bTemporarilyDisableOriginTracking = false;
}
};
OnPreShowLevels(InLevelList);
// For efficiency, set visibility of all levels at once
TArray<FLevelModel*> LevelModels;
TArray<bool> bVisible;
for (auto It = InLevelList.CreateConstIterator(); It; ++It)
{
LevelModels.Add(It->Get());
bVisible.Add(true);
}
FLevelModel::SetVisibleInEditor(LevelModels, bVisible);
RequestUpdateAllLevels();
}
void FLevelCollectionModel::ShowInEditorOnlySelectedLevels()
{
ShowInEditorOnlySelectedLevels_Executed();
}
void FLevelCollectionModel::ShowInEditorAllButSelectedLevels()
{
ShowInEditorAllButSelectedLevels_Executed();
}
void FLevelCollectionModel::HideLevelsInGame(const FLevelModelList& InLevelList)
{
// For efficiency, set visibility of all levels at once
TArray<FLevelModel*> LevelModels;
TArray<bool> bVisible;
for (auto It = InLevelList.CreateConstIterator(); It; ++It)
{
LevelModels.Add(It->Get());
bVisible.Add(false);
}
FLevelModel::SetVisibleInGame(LevelModels, bVisible);
}
void FLevelCollectionModel::ShowLevelsInGame(const FLevelModelList& InLevelList)
{
// For efficiency, set visibility of all levels at once
TArray<FLevelModel*> LevelModels;
TArray<bool> bVisible;
for (auto It = InLevelList.CreateConstIterator(); It; ++It)
{
LevelModels.Add(It->Get());
bVisible.Add(true);
}
FLevelModel::SetVisibleInGame(LevelModels, bVisible);
}
void FLevelCollectionModel::ShowInGameOnlySelectedLevels()
{
ShowInGameOnlySelectedLevels_Executed();
}
void FLevelCollectionModel::ShowInGameAllButSelectedLevels()
{
ShowInGameAllButSelectedLevels_Executed();
}
void FLevelCollectionModel::UnlockLevels(const FLevelModelList& InLevelList)
{
if (!IsReadOnly())
{
const FText UndoTransactionText = (InLevelList.Num() == 1) ?
LOCTEXT("UnlockLevel", "Unlock Level") :
LOCTEXT("UnlockMultipleLevels", "Unlock Multiple Levels");
const FScopedTransaction Transaction(UndoTransactionText);
for (auto It = InLevelList.CreateConstIterator(); It; ++It)
{
(*It)->SetLocked(false);
}
}
}
void FLevelCollectionModel::LockLevels(const FLevelModelList& InLevelList)
{
if (!IsReadOnly())
{
const FText UndoTransactionText = (InLevelList.Num() == 1) ?
LOCTEXT("LockLevel", "Lock Level") :
LOCTEXT("LockMultipleLevels", "Lock Multiple Levels");
const FScopedTransaction Transaction(UndoTransactionText);
for (auto It = InLevelList.CreateConstIterator(); It; ++It)
{
(*It)->SetLocked(true);
}
}
}
void FLevelCollectionModel::LockOnlySelectedLevels()
{
LockOnlySelectedLevels_Executed();
}
void FLevelCollectionModel::LockAllButSelectedLevels()
{
LockAllButSelectedLevels_Executed();
}
void FLevelCollectionModel::SaveLevels(const FLevelModelList& InLevelList)
{
if (IsReadOnly())
{
return;
}
FLevelModelList LevelModelsToSave;
TArray<ULevel*> LevelsToSave;
for (auto It = InLevelList.CreateConstIterator(); It; ++It)
{
if ((*It)->GetLevelObject())
{
if (!(*It)->IsVisibleInEditor())
{
FMessageDialog::Open( EAppMsgType::Ok, NSLOCTEXT("UnrealEd", "UnableToSaveInvisibleLevels", "Save aborted. Levels must be made visible before they can be saved.") );
return;
}
else if ((*It)->IsLocked())
{
FMessageDialog::Open( EAppMsgType::Ok, NSLOCTEXT("UnrealEd", "UnableToSaveLockedLevels", "Save aborted. Level must be unlocked before it can be saved.") );
return;
}
LevelModelsToSave.Add(*It);
LevelsToSave.Add((*It)->GetLevelObject());
}
}
TArray< UPackage* > PackagesNotNeedingCheckout;
// Check dirtiness in case of level using external actors to avoid taking in checkout all actors
bool bCheckDirty = Algo::AnyOf(LevelsToSave, [](const ULevel* InLevel) -> bool { return (InLevel != nullptr) && InLevel->IsUsingExternalActors(); });
// Prompt the user to check out the levels from source control before saving
if (FEditorFileUtils::PromptToCheckoutLevels(bCheckDirty, LevelsToSave, &PackagesNotNeedingCheckout))
{
for (auto It = LevelsToSave.CreateIterator(); It; ++It)
{
FEditorFileUtils::SaveLevel(*It);
}
// Add all files that needs to be marked for add in one command, if any
if (GEditor)
{
GEditor->RunDeferredMarkForAddFiles();
}
}
else if (PackagesNotNeedingCheckout.Num() > 0)
{
// The user canceled the checkout dialog but some packages didn't need to be checked out in order to save
// For each selected level if the package its in didn't need to be saved, save the level!
for (int32 LevelIdx = 0; LevelIdx < LevelsToSave.Num(); ++LevelIdx)
{
ULevel* Level = LevelsToSave[LevelIdx];
if (PackagesNotNeedingCheckout.Contains(Level->GetOutermost()))
{
FEditorFileUtils::SaveLevel(Level);
}
else
{
//remove it from the list, so that only successfully saved levels are highlighted when saving is complete
LevelModelsToSave.RemoveAt(LevelIdx);
LevelsToSave.RemoveAt(LevelIdx);
}
}
}
// Select tiles that were saved successfully
SetSelectedLevels(LevelModelsToSave);
}
void FLevelCollectionModel::LoadLevels(const FLevelModelList& InLevelList)
{
if (IsReadOnly())
{
return;
}
GWarn->BeginSlowTask(LOCTEXT("LoadWorldTiles", "Loading levels"), true);
OnPreLoadLevels(InLevelList);
int32 LevelIdx = 0;
for (TSharedPtr<FLevelModel> LevelModel : InLevelList)
{
GWarn->StatusUpdate(LevelIdx++, InLevelList.Num(),
FText::Format(LOCTEXT("LoadingWorldTiles", "Loading: {0}..." ), FText::FromString(LevelModel->GetLongPackageName().ToString()))
);
LevelModel->LoadLevel();
}
if (InLevelList.Num() > 0)
{
GEditor->ResetTransaction(LOCTEXT("LoadingWorldTilesTransReset", "Loading Levels"));
}
GWarn->EndSlowTask();
}
void FLevelCollectionModel::UnloadLevels(const FLevelModelList& InLevelList)
{
if (InLevelList.Num() == 0)
{
return;
}
UWorld* ThisWorld = GetWorld();
check(ThisWorld != nullptr);
if(GLevelEditorModeTools().IsModeActive(FBuiltinEditorModes::EM_Landscape))
{
GLevelEditorModeTools().ActivateDefaultMode();
}
BroadcastPreLevelsUnloaded();
// Take a copy of the list rather than using a reference to the selected levels list, as this will be modified in the loop below
const FLevelModelList LevelListCopy = InLevelList;
for (auto It = LevelListCopy.CreateConstIterator(); It; ++It)
{
TSharedPtr<FLevelModel> LevelModel = (*It);
ULevel* Level = LevelModel->GetLevelObject();
if (Level != nullptr && !LevelModel->IsPersistent() && LevelModel->IsUserManaged())
{
// Unselect all actors before removing the level
// This avoids crashing in areas that rely on getting a selected actors level. The level will be invalid after its removed.
for (auto ActorIt = Level->Actors.CreateIterator(); ActorIt; ++ActorIt)
{
GEditor->SelectActor((*ActorIt), /*bInSelected=*/ false, /*bSelectEvenIfHidden=*/ false);
}
// In case we have created temporary streaming level object for this sub-level - remove it before unloading sub-level
{
FName LevelPackageName = LevelModel->GetLongPackageName();
auto Predicate = [&](ULevelStreaming* StreamingLevel)
{
return (StreamingLevel && StreamingLevel->GetWorldAssetPackageFName() == LevelPackageName && StreamingLevel->HasAnyFlags(RF_Transient));
};
if (ULevelStreaming*const* StreamingLevel = ThisWorld->GetStreamingLevels().FindByPredicate(Predicate))
{
(*StreamingLevel)->MarkAsGarbage();
ThisWorld->RemoveStreamingLevel(*StreamingLevel);
}
}
EditorLevelUtils::RemoveLevelFromWorld(Level);
}
else if (ULevelStreaming* StreamingLevel = Cast<ULevelStreaming>(LevelModel->GetNodeObject()))
{
StreamingLevel->MarkAsGarbage();
ThisWorld->RemoveStreamingLevel(StreamingLevel);
}
}
BroadcastPostLevelsUnloaded();
GEditor->ResetTransaction( LOCTEXT("RemoveLevelTransReset", "Removing Levels from World") );
// Collect garbage to clear out the destroyed level
CollectGarbage( GARBAGE_COLLECTION_KEEPFLAGS );
PopulateLevelsList();
}
void FLevelCollectionModel::TranslateLevels(const FLevelModelList& InLevels, FVector2D InDelta, bool bSnapDelta)
{
}
FVector2D FLevelCollectionModel::SnapTranslationDelta(const FLevelModelList& InLevelList, FVector2D InTranslationDelta, bool bBoundsSnapping, FVector2D::FReal InSnappingValue)
{
return InTranslationDelta;
}
void FLevelCollectionModel::UpdateTranslationDelta(const FLevelModelList& InLevelList, FVector2D InTranslationDelta, bool bBoundsSnapping, FVector2D::FReal InSnappingValue)
{
FLevelModelList EditableLevels;
// Only editable levels could be moved
for (auto It = InLevelList.CreateConstIterator(); It; ++It)
{
if ((*It)->IsEditable())
{
EditableLevels.Add(*It);
}
}
// Snap translation delta
if (InTranslationDelta != FVector2D::ZeroVector)
{
InTranslationDelta = SnapTranslationDelta(EditableLevels, InTranslationDelta, bBoundsSnapping, InSnappingValue);
}
for (auto It = EditableLevels.CreateIterator(); It; ++It)
{
(*It)->SetLevelTranslationDelta(InTranslationDelta);
}
}
void FLevelCollectionModel::AssignParent(const FLevelModelList& InLevels, TSharedPtr<FLevelModel> InParent)
{
// Attach levels to the new parent
for (auto It = InLevels.CreateConstIterator(); It; ++It)
{
(*It)->AttachTo(InParent);
}
OnLevelsHierarchyChanged();
}
void FLevelCollectionModel::AddExistingLevelsFromAssetData(const TArray<FAssetData>& WorldList)
{
}
TSharedPtr<WorldHierarchy::FWorldBrowserDragDropOp> FLevelCollectionModel::CreateDragDropOp() const
{
return MakeShareable( new WorldHierarchy::FWorldBrowserDragDropOp );
}
TSharedPtr<WorldHierarchy::FWorldBrowserDragDropOp> FLevelCollectionModel::CreateDragDropOp(const FLevelModelList& InLevels) const
{
return TSharedPtr<WorldHierarchy::FWorldBrowserDragDropOp>();
}
bool FLevelCollectionModel::PassesAllFilters(const FLevelModel& Item) const
{
if (Item.IsPersistent() || Filters->PassesAllFilters(&Item))
{
return true;
}
return false;
}
void FLevelCollectionModel::BuildHierarchyMenu(FMenuBuilder& InMenuBuilder, EBuildHierarchyMenuFlags Flags) const
{
}
void FLevelCollectionModel::CustomizeFileMainMenu(FMenuBuilder& InMenuBuilder) const
{
const FLevelCollectionCommands& Commands = FLevelCollectionCommands::Get();
// Cache SCC state
CacheCanExecuteSourceControlVars();
InMenuBuilder.AddSubMenu(
LOCTEXT("SourceControl", "Revision Control"),
LOCTEXT("SourceControl_ToolTip", "Revision Control Options"),
FNewMenuDelegate::CreateSP(const_cast<FLevelCollectionModel*>(this), &FLevelCollectionModel::FillSourceControlSubMenu));
if (AreAnyLevelsSelected())
{
InMenuBuilder.AddMenuEntry( Commands.World_SaveSelectedLevels );
InMenuBuilder.AddMenuEntry( Commands.World_SaveSelectedLevelAs );
InMenuBuilder.AddMenuEntry( Commands.World_MigrateSelectedLevels );
}
}
bool FLevelCollectionModel::GetPlayerView(FVector& Location, FRotator& Rotation) const
{
return false;
}
bool FLevelCollectionModel::GetObserverView(FVector& Location, FRotator& Rotation) const
{
return false;
}
bool FLevelCollectionModel::CompareLevelsZOrder(TSharedPtr<FLevelModel> InA, TSharedPtr<FLevelModel> InB) const
{
return false;
}
void FLevelCollectionModel::RegisterDetailsCustomization(FPropertyEditorModule& InPropertyModule,
TSharedPtr<IDetailsView> InDetailsView)
{
}
void FLevelCollectionModel::UnregisterDetailsCustomization(FPropertyEditorModule& InPropertyModule,
TSharedPtr<IDetailsView> InDetailsView)
{
}
void FLevelCollectionModel::RequestUpdateAllLevels()
{
bRequestedUpdateAllLevels = true;
}
void FLevelCollectionModel::RequestRedrawAllLevels()
{
bRequestedRedrawAllLevels = true;
}
void FLevelCollectionModel::UpdateAllLevels()
{
bRequestedUpdateAllLevels = false;
for (auto It = AllLevelsList.CreateConstIterator(); It; ++It)
{
(*It)->Update();
}
// Update world size
FBox WorldBounds = GetLevelsBoundingBox(AllLevelsList, false);
WorldSize.X = FMath::RoundToInt(WorldBounds.GetSize().X);
WorldSize.Y = FMath::RoundToInt(WorldBounds.GetSize().Y);
}
void FLevelCollectionModel::RedrawAllLevels()
{
bRequestedRedrawAllLevels = false;
for (auto It = AllLevelsList.CreateConstIterator(); It; ++It)
{
(*It)->UpdateVisuals();
}
}
void FLevelCollectionModel::UpdateLevelActorsCount()
{
for( auto It = AllLevelsList.CreateIterator(); It; ++It )
{
(*It)->UpdateLevelActorsCount();
}
bRequestedUpdateActorsCount = false;
}
bool FLevelCollectionModel::IsOneLevelSelected() const
{
return SelectedLevelsList.Num() == 1;
}
bool FLevelCollectionModel::AreAnyLevelsSelected() const
{
return SelectedLevelsList.Num() > 0;
}
bool FLevelCollectionModel::AreAllSelectedLevelsUserManaged() const
{
for (int32 LevelIdx = 0; LevelIdx < SelectedLevelsList.Num(); LevelIdx++)
{
if (!SelectedLevelsList[LevelIdx]->IsUserManaged())
{
return false;
}
}
return AreAnyLevelsSelected();
}
bool FLevelCollectionModel::AreAllSelectedLevelsLoaded() const
{
for (int32 LevelIdx = 0; LevelIdx < SelectedLevelsList.Num(); LevelIdx++)
{
if (SelectedLevelsList[LevelIdx]->IsLoaded() == false)
{
return false;
}
}
return AreAnyLevelsSelected();
}
bool FLevelCollectionModel::AreAnySelectedLevelsLoaded() const
{
return !AreAllSelectedLevelsUnloaded();
}
bool FLevelCollectionModel::AreAllSelectedLevelsUnloaded() const
{
for (int32 LevelIdx = 0; LevelIdx < SelectedLevelsList.Num(); LevelIdx++)
{
if (SelectedLevelsList[LevelIdx]->IsLoaded() == true)
{
return false;
}
}
return true;
}
bool FLevelCollectionModel::AreAnySelectedLevelsUnloaded() const
{
return !AreAllSelectedLevelsLoaded();
}
bool FLevelCollectionModel::AreAllSelectedLevelsEditable() const
{
for (auto It = SelectedLevelsList.CreateConstIterator(); It; ++It)
{
if ((*It)->IsEditable() == false)
{
return false;
}
}
return AreAnyLevelsSelected();
}
bool FLevelCollectionModel::AreAllSelectedLevelsEditableAndNotPersistent() const
{
for (auto It = SelectedLevelsList.CreateConstIterator(); It; ++It)
{
if ((*It)->IsEditable() == false || (*It)->IsPersistent() || !(*It)->IsUserManaged())
{
return false;
}
}
return AreAnyLevelsSelected();
}
bool FLevelCollectionModel::AreAllSelectedLevelsEditableAndVisible() const
{
for (auto It = SelectedLevelsList.CreateConstIterator(); It; ++It)
{
if ((*It)->IsEditable() == false ||
(*It)->IsVisibleInEditor() == false)
{
return false;
}
}
return AreAnyLevelsSelected();
}
bool FLevelCollectionModel::AreAnySelectedLevelsEditable() const
{
for (auto It = SelectedLevelsList.CreateConstIterator(); It; ++It)
{
if ((*It)->IsEditable() == true)
{
return true;
}
}
return false;
}
bool FLevelCollectionModel::AreAnySelectedLevelsEditableAndVisible() const
{
for (auto It = SelectedLevelsList.CreateConstIterator(); It; ++It)
{
if ((*It)->IsEditable() == true &&
(*It)->IsVisibleInEditor() == true)
{
return true;
}
}
return false;
}
bool FLevelCollectionModel::CanExecuteGameVisibilityCommandsForSelectedLevels() const
{
return AreAnySelectedLevelsLoaded() && CanExecuteGameVisibilityCommands();
}
bool FLevelCollectionModel::CanExecuteGameVisibilityCommands() const
{
return !WorldHierarchy::IsInPie();
}
bool FLevelCollectionModel::IsSelectedLevelEditable() const
{
if (SelectedLevelsList.Num() == 1)
{
return SelectedLevelsList[0]->IsEditable();
}
return false;
}
bool FLevelCollectionModel::IsNewLightingScenarioState(bool bExistingState) const
{
if (SelectedLevelsList.Num() == 1)
{
return SelectedLevelsList[0]->IsLightingScenario() != bExistingState;
}
return false;
}
void FLevelCollectionModel::SetIsLightingScenario(bool bNewLightingScenario)
{
if (SelectedLevelsList.Num() == 1)
{
SelectedLevelsList[0]->SetIsLightingScenario(bNewLightingScenario);
}
}
bool FLevelCollectionModel::AreAnySelectedLevelsDirty() const
{
for (auto It = SelectedLevelsList.CreateConstIterator(); It; ++It)
{
if ((*It)->IsLoaded() == true && (*It)->IsDirty() == true)
{
return true;
}
}
return false;
}
bool FLevelCollectionModel::AreActorsSelected() const
{
return GEditor->GetSelectedActorCount() > 0;
}
bool FLevelCollectionModel::CanConvertAnyLevelToExternalActors(bool bExternal) const
{
if (SelectedLevelsList.Num())
{
for (const TSharedPtr<FLevelModel>& LevelModel : SelectedLevelsList)
{
if (!LevelModel->CanConvertLevelToExternalActors(bExternal))
{
return false;
}
}
return true;
}
return false;
}
bool FLevelCollectionModel::GetDisplayPathsState() const
{
return bDisplayPaths;
}
void FLevelCollectionModel::SetDisplayPathsState(bool InDisplayPaths)
{
bDisplayPaths = InDisplayPaths;
for (auto It = AllLevelsList.CreateIterator(); It; ++It)
{
(*It)->UpdateDisplayName();
}
}
bool FLevelCollectionModel::GetDisplayActorsCountState() const
{
return bDisplayActorsCount;
}
void FLevelCollectionModel::SetDisplayActorsCountState(bool InDisplayActorsCount)
{
bDisplayActorsCount = InDisplayActorsCount;
for (auto It = AllLevelsList.CreateIterator(); It; ++It)
{
(*It)->UpdateDisplayName();
}
}
void FLevelCollectionModel::BroadcastSelectionChanged()
{
SelectionChanged.Broadcast();
}
void FLevelCollectionModel::BroadcastCollectionChanged()
{
CollectionChanged.Broadcast();
}
void FLevelCollectionModel::BroadcastHierarchyChanged()
{
HierarchyChanged.Broadcast();
}
void FLevelCollectionModel::BroadcastPreLevelsUnloaded()
{
PreLevelsUnloaded.Broadcast();
}
void FLevelCollectionModel::BroadcastPostLevelsUnloaded()
{
PostLevelsUnloaded.Broadcast();
}
double FLevelCollectionModel::EditableAxisLength()
{
return HALF_WORLD_MAX;
};
FBox FLevelCollectionModel::EditableWorldArea()
{
FVector::FReal AxisLength = EditableAxisLength();
return FBox(
FVector(-AxisLength, -AxisLength, -AxisLength),
FVector(+AxisLength, +AxisLength, +AxisLength)
);
}
void FLevelCollectionModel::SCCCheckOut(const FLevelModelList& InList)
{
TArray<FString> FilenamesToCheckOut = GetFilenamesList(InList);
// Update the source control status of all potentially relevant packages
ISourceControlModule::Get().GetProvider().Execute(
ISourceControlOperation::Create<FUpdateStatus>(), FilenamesToCheckOut
);
// Now check them out
FEditorFileUtils::CheckoutPackages(FilenamesToCheckOut);
}
void FLevelCollectionModel::SCCCheckIn(const FLevelModelList& InList)
{
TArray<UPackage*> PackagesToCheckIn = GetPackagesList(InList);
TArray<FString> FilenamesToCheckIn = GetFilenamesList(InList);
// Prompt the user to ask if they would like to first save any dirty packages they are trying to check-in
const auto UserResponse = FEditorFileUtils::PromptForCheckoutAndSave(PackagesToCheckIn, true, true);
// If the user elected to save dirty packages, but one or more of the packages failed to save properly OR if the user
// canceled out of the prompt, don't follow through on the check-in process
const bool bShouldProceed = UserResponse == FEditorFileUtils::EPromptReturnCode::PR_Success ||
UserResponse == FEditorFileUtils::EPromptReturnCode::PR_Declined;
if (bShouldProceed)
{
const bool bUseSourceControlStateCache = false;
FSourceControlWindows::PromptForCheckin(bUseSourceControlStateCache, FilenamesToCheckIn);
}
else
{
// If a failure occurred, alert the user that the check-in was aborted. This warning shouldn't be necessary if the user cancelled
// from the dialog, because they obviously intended to cancel the whole operation.
if (UserResponse == FEditorFileUtils::EPromptReturnCode::PR_Failure)
{
FMessageDialog::Open(EAppMsgType::Ok,
NSLOCTEXT("UnrealEd", "SCC_Checkin_Aborted", "Check-in aborted as a result of save failure.")
);
}
}
}
void FLevelCollectionModel::SCCOpenForAdd(const FLevelModelList& InList)
{
ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider();
TArray<FString> FilenamesList = GetFilenamesList(InList);
TArray<FString> FilenamesToAdd;
TArray<UPackage*> PackagesToSave;
for (auto It = FilenamesList.CreateConstIterator(); It; ++It)
{
const FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(*It, EStateCacheUsage::Use);
if (SourceControlState.IsValid() && !SourceControlState->IsSourceControlled())
{
FilenamesToAdd.Add(*It);
// Make sure the file actually exists on disk before adding it
FString LongPackageName = FPackageName::FilenameToLongPackageName(*It);
if (!FPackageName::DoesPackageExist(LongPackageName))
{
UPackage* Package = FindPackage(NULL, *LongPackageName);
if (Package)
{
PackagesToSave.Add(Package);
}
}
}
}
if (FilenamesToAdd.Num() > 0)
{
// If any of the packages are new, save them now
if (PackagesToSave.Num() > 0)
{
const bool bCheckDirty = false;
const bool bPromptToSave = false;
const auto Return = FEditorFileUtils::PromptForCheckoutAndSave(PackagesToSave, bCheckDirty, bPromptToSave);
}
SourceControlProvider.Execute(ISourceControlOperation::Create<FMarkForAdd>(), FilenamesToAdd);
}
}
void FLevelCollectionModel::SCCHistory(const FLevelModelList& InList)
{
// This is odd, why SCC needs package names, instead of filenames?
TArray<FString> PackageNames;
for (auto It = InList.CreateConstIterator(); It; ++It)
{
if ((*It)->HasValidPackage())
{
PackageNames.Add((*It)->GetLongPackageName().ToString());
}
}
FSourceControlWindows::DisplayRevisionHistory(PackageNames);
}
void FLevelCollectionModel::SCCRefresh(const FLevelModelList& InList)
{
if(ISourceControlModule::Get().IsEnabled())
{
ISourceControlModule::Get().QueueStatusUpdate(GetFilenamesList(InList));
}
}
void FLevelCollectionModel::SCCDiffAgainstDepot(const FLevelModelList& InList, UEditorEngine* InEditor)
{
// Load the asset registry module
FAssetToolsModule& AssetToolsModule = FModuleManager::GetModuleChecked<FAssetToolsModule>("AssetTools");
ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider();
// Iterate over each selected asset
for (auto It = InList.CreateConstIterator(); It; ++It)
{
ULevel* Level = (*It)->GetLevelObject();
if (Level == NULL)
{
return;
}
UPackage* OriginalPackage = Level->GetOutermost();
FString PackageName = OriginalPackage->GetName();
// Make sure our history is up to date
auto UpdateStatusOperation = ISourceControlOperation::Create<FUpdateStatus>();
UpdateStatusOperation->SetUpdateHistory(true);
SourceControlProvider.Execute(UpdateStatusOperation, OriginalPackage);
// Get the SCC state
FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(
OriginalPackage, EStateCacheUsage::Use
);
// If the level is in SCC.
if (SourceControlState.IsValid() && SourceControlState->IsSourceControlled())
{
// Get the file name of package
FString RelativeFileName;
if(FPackageName::DoesPackageExist(PackageName, &RelativeFileName))
{
if (SourceControlState->GetHistorySize() > 0)
{
auto Revision = SourceControlState->GetHistoryItem(0);
check(Revision.IsValid());
// Get the head revision of this package from source control
FString AbsoluteFileName = FPaths::ConvertRelativePathToFull(RelativeFileName);
FString TempFileName;
if (UPackage* OldPackage = DiffUtils::LoadPackageForDiff(Revision))
{
// Try and load that package
FText NotMapReason;
if(InEditor->PackageIsAMapFile(*TempFileName, NotMapReason))
{
/* Set the revision information*/
UPackage* Package = OriginalPackage;
FRevisionInfo OldRevision;
OldRevision.Changelist = Revision->GetCheckInIdentifier();
OldRevision.Date = Revision->GetDate();
OldRevision.Revision = Revision->GetRevision();
FRevisionInfo NewRevision;
NewRevision.Revision = TEXT("");
// Dump assets to temp text files
FString OldTextFilename = AssetToolsModule.Get().DumpAssetToTempFile(OldPackage);
FString NewTextFilename = AssetToolsModule.Get().DumpAssetToTempFile(OriginalPackage);
FString DiffCommand = GetDefault<UEditorLoadingSavingSettings>()->TextDiffToolPath.FilePath;
AssetToolsModule.Get().CreateDiffProcess(DiffCommand, OldTextFilename, NewTextFilename);
AssetToolsModule.Get().DiffAssets(OldPackage, OriginalPackage, OldRevision, NewRevision);
}
}
}
}
}
}
}
TArray<FName> FLevelCollectionModel::GetPackageNamesList(const FLevelModelList& InList)
{
TArray<FName> ResultList;
for (auto It = InList.CreateConstIterator(); It; ++It)
{
if ((*It)->HasValidPackage())
{
ResultList.Add((*It)->GetLongPackageName());
}
}
return ResultList;
}
TArray<FString> FLevelCollectionModel::GetFilenamesList(const FLevelModelList& InList)
{
TArray<FString> ResultList;
for (auto It = InList.CreateConstIterator(); It; ++It)
{
if ((*It)->HasValidPackage())
{
ResultList.Add((*It)->GetPackageFileName());
}
}
return ResultList;
}
TArray<UPackage*> FLevelCollectionModel::GetPackagesList(const FLevelModelList& InList)
{
TArray<UPackage*> ResultList;
for (auto It = InList.CreateConstIterator(); It; ++It)
{
ULevel* Level = (*It)->GetLevelObject();
if (Level)
{
ResultList.Add(Level->GetOutermost());
}
}
return ResultList;
}
TArray<ULevel*> FLevelCollectionModel::GetLevelObjectList(const FLevelModelList& InList)
{
TArray<ULevel*> ResultList;
for (auto It = InList.CreateConstIterator(); It; ++It)
{
ULevel* Level = (*It)->GetLevelObject();
if (Level)
{
ResultList.Add(Level);
}
}
return ResultList;
}
FLevelModelList FLevelCollectionModel::GetLoadedLevels(const FLevelModelList& InList)
{
FLevelModelList ResultList;
for (auto It = InList.CreateConstIterator(); It; ++It)
{
if ((*It)->IsLoaded())
{
ResultList.Add(*It);
}
}
return ResultList;
}
FLevelModelList FLevelCollectionModel::GetLevelsHierarchy(const FLevelModelList& InList)
{
struct FHierarchyCollector : public FLevelModelVisitor
{
virtual void Visit(FLevelModel& Item) override
{
ResultList.AddUnique(Item.AsShared());
}
FLevelModelList ResultList;
};
FHierarchyCollector HierarchyCollector;
for (auto It = InList.CreateConstIterator(); It; ++It)
{
(*It)->Accept(HierarchyCollector);
}
return HierarchyCollector.ResultList;
}
FBox FLevelCollectionModel::GetLevelsBoundingBox(const FLevelModelList& InList, bool bIncludeChildren)
{
FBox TotalBounds(ForceInit);
for (auto It = InList.CreateConstIterator(); It; ++It)
{
if (bIncludeChildren)
{
TotalBounds+= GetVisibleLevelsBoundingBox((*It)->GetChildren(), bIncludeChildren);
}
TotalBounds+= (*It)->GetLevelBounds();
}
return TotalBounds;
}
FBox FLevelCollectionModel::GetVisibleLevelsBoundingBox(const FLevelModelList& InList, bool bIncludeChildren)
{
FBox TotalBounds(ForceInit);
for (auto It = InList.CreateConstIterator(); It; ++It)
{
if (bIncludeChildren)
{
TotalBounds+= GetVisibleLevelsBoundingBox((*It)->GetChildren(), bIncludeChildren);
}
if ((*It)->IsVisibleInEditor())
{
TotalBounds+= (*It)->GetLevelBounds();
}
}
return TotalBounds;
}
const TSharedRef<FUICommandList> FLevelCollectionModel::GetCommandList() const
{
return CommandList;
}
const FString ConfigIniSection = TEXT("WorldBrowser");
void FLevelCollectionModel::LoadSettings()
{
// Display paths
bool bDisplayPathsSetting = false;
GConfig->GetBool(*ConfigIniSection, TEXT("DisplayPaths"), bDisplayPathsSetting, GEditorPerProjectIni);
SetDisplayPathsState(bDisplayPathsSetting);
// Display actors count
bool bDisplayActorsCountSetting = false;
GConfig->GetBool(*ConfigIniSection, TEXT("DisplayActorsCount"), bDisplayActorsCountSetting, GEditorPerProjectIni);
SetDisplayActorsCountState(bDisplayActorsCountSetting);
}
void FLevelCollectionModel::SaveSettings()
{
// Display paths
GConfig->SetBool(*ConfigIniSection, TEXT("DisplayPaths"), GetDisplayPathsState(), GEditorPerProjectIni);
// Display actors count
GConfig->SetBool(*ConfigIniSection, TEXT("DisplayActorsCount"), GetDisplayActorsCountState(), GEditorPerProjectIni);
}
void FLevelCollectionModel::RefreshBrowser_Executed()
{
PopulateLevelsList();
}
void FLevelCollectionModel::LoadSelectedLevels_Executed()
{
LoadLevels(GetSelectedLevels());
}
void FLevelCollectionModel::UnloadSelectedLevels_Executed()
{
UnloadLevels(GetSelectedLevels());
}
void FLevelCollectionModel::OnSCCCheckOut()
{
SCCCheckOut(GetSelectedLevels());
}
void FLevelCollectionModel::OnSCCCheckIn()
{
SCCCheckIn(GetSelectedLevels());
}
void FLevelCollectionModel::OnSCCOpenForAdd()
{
SCCOpenForAdd(GetSelectedLevels());
}
void FLevelCollectionModel::OnSCCHistory()
{
SCCHistory(GetSelectedLevels());
}
void FLevelCollectionModel::OnSCCRefresh()
{
SCCRefresh(GetSelectedLevels());
}
void FLevelCollectionModel::OnSCCDiffAgainstDepot()
{
SCCDiffAgainstDepot(GetSelectedLevels(), GEditor);
}
void FLevelCollectionModel::OnSCCConnect() const
{
ISourceControlModule::Get().ShowLoginDialog(FSourceControlLoginClosed(), ELoginWindowMode::Modeless);
}
void FLevelCollectionModel::SaveSelectedLevels_Executed()
{
SaveLevels(GetSelectedLevels());
}
void FLevelCollectionModel::SaveSelectedLevelAs_Executed()
{
if (SelectedLevelsList.Num() > 0)
{
ULevel* Level = SelectedLevelsList[0]->GetLevelObject();
if (Level)
{
FEditorFileUtils::SaveLevelAs(Level);
}
}
}
void FLevelCollectionModel::MigrateSelectedLevels_Executed()
{
// Gather the package names for the levels
TArray<FName> PackageNames = GetPackageNamesList(GetSelectedLevels());
FAssetToolsModule& AssetToolsModule = FModuleManager::Get().LoadModuleChecked<FAssetToolsModule>("AssetTools");
AssetToolsModule.Get().MigratePackages(PackageNames);
}
void FLevelCollectionModel::SelectAllLevels_Executed()
{
SetSelectedLevels(FilteredLevelsList);
}
void FLevelCollectionModel::DeselectAllLevels_Executed()
{
FLevelModelList NoLevels;
SetSelectedLevels(NoLevels);
}
void FLevelCollectionModel::InvertSelection_Executed()
{
FLevelModelList InvertedLevels;
for (auto It = FilteredLevelsList.CreateIterator(); It; ++It)
{
if (!SelectedLevelsList.Contains(*It))
{
InvertedLevels.Add(*It);
}
}
SetSelectedLevels(InvertedLevels);
}
void FLevelCollectionModel::ShowInEditorSelectedLevels_Executed()
{
ShowLevelsInEditor(GetSelectedLevels());
}
void FLevelCollectionModel::HideInEditorSelectedLevels_Executed()
{
HideLevelsInEditor(GetSelectedLevels());
}
void FLevelCollectionModel::ShowInEditorOnlySelectedLevels_Executed()
{
//stash off a copy of the original array, as setting visibility can destroy the selection
FLevelModelList SelectedLevelsCopy = GetSelectedLevels();
InvertSelection_Executed();
HideInEditorSelectedLevels_Executed();
SetSelectedLevels(SelectedLevelsCopy);
ShowInEditorSelectedLevels_Executed();
}
void FLevelCollectionModel::ShowInEditorAllButSelectedLevels_Executed()
{
//stash off a copy of the original array, as setting visibility can destroy the selection
FLevelModelList SelectedLevelsCopy = GetSelectedLevels();
InvertSelection_Executed();
ShowInEditorSelectedLevels_Executed();
SetSelectedLevels(SelectedLevelsCopy);
HideInEditorSelectedLevels_Executed();
}
void FLevelCollectionModel::ShowInEditorAllLevels_Executed()
{
ShowLevelsInEditor(GetFilteredLevels());
}
void FLevelCollectionModel::HideInEditorAllLevels_Executed()
{
HideLevelsInEditor(GetFilteredLevels());
}
void FLevelCollectionModel::ShowInGameSelectedLevels_Executed()
{
ShowLevelsInGame(GetSelectedLevels());
}
void FLevelCollectionModel::HideInGameSelectedLevels_Executed()
{
HideLevelsInGame(GetSelectedLevels());
}
void FLevelCollectionModel::ShowInGameOnlySelectedLevels_Executed()
{
//stash off a copy of the original array, as setting visibility can destroy the selection
FLevelModelList SelectedLevelsCopy = GetSelectedLevels();
InvertSelection_Executed();
HideInGameSelectedLevels_Executed();
SetSelectedLevels(SelectedLevelsCopy);
ShowInGameSelectedLevels_Executed();
}
void FLevelCollectionModel::ShowInGameAllButSelectedLevels_Executed()
{
//stash off a copy of the original array, as setting visibility can destroy the selection
FLevelModelList SelectedLevelsCopy = GetSelectedLevels();
InvertSelection_Executed();
ShowInGameSelectedLevels_Executed();
SetSelectedLevels(SelectedLevelsCopy);
HideInGameSelectedLevels_Executed();
}
void FLevelCollectionModel::ShowInGameAllLevels_Executed()
{
ShowLevelsInGame(GetFilteredLevels());
}
void FLevelCollectionModel::HideInGameAllLevels_Executed()
{
HideLevelsInGame(GetFilteredLevels());
}
void FLevelCollectionModel::LockSelectedLevels_Executed()
{
LockLevels(GetSelectedLevels());
}
void FLevelCollectionModel::UnlockSelectedLevels_Executed()
{
UnlockLevels(GetSelectedLevels());
}
void FLevelCollectionModel::LockOnlySelectedLevels_Executed()
{
//stash off a copy of the original array, as setting visibility can destroy the selection
FLevelModelList SelectedLevelsCopy = GetSelectedLevels();
InvertSelection_Executed();
UnlockSelectedLevels_Executed();
SetSelectedLevels(SelectedLevelsCopy);
LockSelectedLevels_Executed();
}
void FLevelCollectionModel::LockAllButSelectedLevels_Executed()
{
//stash off a copy of the original array, as setting visibility can destroy the selection
FLevelModelList SelectedLevelsCopy = GetSelectedLevels();
InvertSelection_Executed();
LockSelectedLevels_Executed();
SetSelectedLevels(SelectedLevelsCopy);
UnlockSelectedLevels_Executed();
}
void FLevelCollectionModel::LockAllLevels_Executed()
{
if (!IsReadOnly())
{
const FScopedTransaction Transaction(LOCTEXT("LockAllLevels", "Lock All Levels"));
LockLevels(GetFilteredLevels());
}
}
void FLevelCollectionModel::UnlockAllLevels_Executed()
{
if (!IsReadOnly())
{
const FScopedTransaction Transaction(LOCTEXT("UnlockAllLevels", "Unlock All Levels"));
UnlockLevels(GetFilteredLevels());
}
}
void FLevelCollectionModel::ToggleReadOnlyLevels_Executed()
{
//We are about to lock some Levels, deselect all actor and surfaces from the read only levels
if (!GEditor->bLockReadOnlyLevels)
{
DeselectActorsInAllReadOnlyLevel(GetAllLevels());
DeselectSurfaceInAllReadOnlyLevel(GetAllLevels());
// Tell the editor selection status was changed.
GEditor->NoteSelectionChange();
}
GEditor->bLockReadOnlyLevels = !GEditor->bLockReadOnlyLevels;
}
void FLevelCollectionModel::MakeLevelCurrent_Executed()
{
check( SelectedLevelsList.Num() == 1 );
SelectedLevelsList[0]->MakeLevelCurrent();
}
void FLevelCollectionModel::FindInContentBrowser_Executed()
{
TArray<UObject*> Objects;
for (auto It = SelectedLevelsList.CreateConstIterator(); It; ++It)
{
ULevel* Level = (*It)->GetLevelObject();
if (Level)
{
UObject* LevelOuter = Level->GetOuter();
if (LevelOuter)
{
// Search for the level's outer (the UWorld) as this is the actual asset shown by the content browser
Objects.AddUnique(LevelOuter);
}
}
}
GEditor->SyncBrowserToObjects(Objects);
}
bool FLevelCollectionModel::IsValidFindInContentBrowser()
{
return true;
}
void FLevelCollectionModel::MoveActorsToSelected_Executed()
{
MakeLevelCurrent_Executed();
const FScopedTransaction Transaction(LOCTEXT("MoveSelectedActorsToSelectedLevel", "Move Selected Actors to Level"));
// Redirect selected foliage actor to use the MoveActorFoliageInstancesToLevel functionality as we can't move the foliage actor only instances
USelection* SelectedActors = GEditor->GetSelectedActors();
for (FSelectionIterator Iter(*SelectedActors); Iter; ++Iter)
{
AInstancedFoliageActor* Actor = Cast<AInstancedFoliageActor>(*Iter);
if (Actor != nullptr)
{
FFoliageEditUtility::MoveActorFoliageInstancesToLevel(GetWorld()->GetCurrentLevel(), Actor);
}
}
UEditorLevelUtils::MoveSelectedActorsToLevel(GetWorld()->GetCurrentLevel());
RequestUpdateAllLevels();
}
void FLevelCollectionModel::MoveFoliageToSelected_Executed()
{
if (GetSelectedLevels().Num() == 1)
{
ULevel* TargetLevel = GetSelectedLevels()[0]->GetLevelObject();
// Need to only permit this action when the foliage mode is open as the selection is being done there
if (GLevelEditorModeTools().IsModeActive(FBuiltinEditorModes::EM_Foliage))
{
IFoliageEditModule& FoliageModule = FModuleManager::GetModuleChecked<IFoliageEditModule>("FoliageEdit");
FoliageModule.MoveSelectedFoliageToLevel(TargetLevel);
}
}
}
void FLevelCollectionModel::SelectActors_Executed()
{
//first clear any existing actor selection
const FScopedTransaction Transaction( LOCTEXT("SelectActors", "Select Actors in Level") );
GEditor->GetSelectedActors()->Modify();
GEditor->SelectNone( false, true );
for(auto It = SelectedLevelsList.CreateConstIterator(); It; ++It)
{
(*It)->SelectActors(/*bSelect*/ true, /*bNotify*/ true, /*bSelectEvenIfHidden*/ true);
}
}
void FLevelCollectionModel::DeselectActors_Executed()
{
const FScopedTransaction Transaction( LOCTEXT("DeselectActors", "Deselect Actors in Level") );
for(auto It = SelectedLevelsList.CreateConstIterator(); It; ++It)
{
(*It)->SelectActors(/*bSelect*/ false, /*bNotify*/ true, /*bSelectEvenIfHidden*/ true);
}
}
void FLevelCollectionModel::ConvertLevelToExternalActors_Executed(bool bExternal)
{
FScopedTransaction Transaction(LOCTEXT("WorldUseExternalActors", "Change World Use External Actors"));
for (const TSharedPtr<FLevelModel>& LevelModel : SelectedLevelsList)
{
LevelModel->ConvertLevelToExternalActors(bExternal);
}
}
void FLevelCollectionModel::ExpandSelectedItems_Executed()
{
struct FExpandLevelVisitor : public FLevelModelVisitor
{
virtual void Visit(FLevelModel& Item) override { Item.SetLevelExpansionFlag(true); }
} Expander;
for (TSharedPtr<FLevelModel> LevelModel: SelectedLevelsList)
{
LevelModel->Accept(Expander);
}
BroadcastHierarchyChanged();
}
void FLevelCollectionModel::FillLockSubMenu(FMenuBuilder& InMenuBuilder)
{
const FLevelCollectionCommands& Commands = FLevelCollectionCommands::Get();
InMenuBuilder.AddMenuEntry( Commands.World_LockSelectedLevels );
InMenuBuilder.AddMenuEntry( Commands.World_UnlockSelectedLevels );
InMenuBuilder.AddMenuEntry( Commands.World_LockOnlySelectedLevels );
InMenuBuilder.AddMenuEntry( Commands.World_LockAllButSelectedLevels );
InMenuBuilder.AddMenuEntry( Commands.World_LockAllLevels );
InMenuBuilder.AddMenuEntry( Commands.World_UnlockAllLevels );
if (GEditor->bLockReadOnlyLevels)
{
InMenuBuilder.AddMenuEntry( Commands.World_UnlockReadOnlyLevels );
}
else
{
InMenuBuilder.AddMenuEntry( Commands.World_LockReadOnlyLevels );
}
}
void FLevelCollectionModel::FillEditorVisibilitySubMenu(FMenuBuilder& InMenuBuilder)
{
const FLevelCollectionCommands& Commands = FLevelCollectionCommands::Get();
InMenuBuilder.AddMenuEntry( Commands.World_ShowInEditorSelectedLevels );
InMenuBuilder.AddMenuEntry( Commands.World_HideInEditorSelectedLevels );
InMenuBuilder.AddMenuEntry( Commands.World_ShowInEditorOnlySelectedLevels );
InMenuBuilder.AddMenuEntry( Commands.World_ShowInEditorAllButSelectedLevels );
InMenuBuilder.AddMenuEntry( Commands.World_ShowInEditorAllLevels );
InMenuBuilder.AddMenuEntry( Commands.World_HideInEditorAllLevels );
}
void FLevelCollectionModel::FillGameVisibilitySubMenu(FMenuBuilder& InMenuBuilder)
{
const FLevelCollectionCommands& Commands = FLevelCollectionCommands::Get();
InMenuBuilder.AddMenuEntry( Commands.World_ShowInGameSelectedLevels );
InMenuBuilder.AddMenuEntry( Commands.World_HideInGameSelectedLevels );
InMenuBuilder.AddMenuEntry( Commands.World_ShowInGameOnlySelectedLevels );
InMenuBuilder.AddMenuEntry( Commands.World_ShowInGameAllButSelectedLevels );
InMenuBuilder.AddMenuEntry( Commands.World_ShowInGameAllLevels );
InMenuBuilder.AddMenuEntry( Commands.World_HideInGameAllLevels );
}
void FLevelCollectionModel::FillSourceControlSubMenu(FMenuBuilder& InMenuBuilder)
{
const FLevelCollectionCommands& Commands = FLevelCollectionCommands::Get();
if (CanExecuteSCC())
{
if (CanExecuteSCCCheckOut())
{
InMenuBuilder.AddMenuEntry( Commands.SCCCheckOut );
}
if (CanExecuteSCCOpenForAdd())
{
InMenuBuilder.AddMenuEntry( Commands.SCCOpenForAdd );
}
if (CanExecuteSCCCheckIn())
{
InMenuBuilder.AddMenuEntry( Commands.SCCCheckIn );
}
InMenuBuilder.AddMenuEntry( Commands.SCCRefresh );
InMenuBuilder.AddMenuEntry( Commands.SCCHistory );
InMenuBuilder.AddMenuEntry( Commands.SCCDiffAgainstDepot );
}
else
{
InMenuBuilder.AddMenuEntry( Commands.SCCConnect );
}
}
void FLevelCollectionModel::DeselectActorsInAllReadOnlyLevel(const FLevelModelList& InLevelList)
{
const FScopedTransaction Transaction(LOCTEXT("DeselectActorsInReadOnlyLevel", "Deselect Actors in all read only Level"));
for (auto It = InLevelList.CreateConstIterator(); It; ++It)
{
if ((*It)->IsFileReadOnly())
{
(*It)->SelectActors(/*bSelect*/ false, /*bNotify*/ true, /*bSelectEvenIfHidden*/ true);
}
}
}
void FLevelCollectionModel::DeselectSurfaceInAllReadOnlyLevel(const FLevelModelList& InLevelList)
{
const FScopedTransaction Transaction(LOCTEXT("DeselectSurfacesInReadOnlyLevel", "Deselect Surfaces in all read only Level"));
for (auto It = InLevelList.CreateConstIterator(); It; ++It)
{
if ((*It)->IsFileReadOnly())
{
(*It)->DeselectAllSurfaces();
}
}
}
void FLevelCollectionModel::OnLevelsCollectionChanged()
{
UpdateAllLevels();
PopulateFilteredLevelsList();
BroadcastCollectionChanged();
}
void FLevelCollectionModel::OnLevelsSelectionChanged()
{
if (bUpdatingLevelsSelection)
{
return;
}
TGuardValue<bool> UpdateGuard(bUpdatingLevelsSelection, true);
// Pass the list we just created to the world to set the selection
CurrentWorld->SetSelectedLevels(
GetLevelObjectList(SelectedLevelsList)
);
// Request SC status update for selected levels
ISourceControlModule::Get().QueueStatusUpdate(
GetFilenamesList(SelectedLevelsList)
);
// Expand hierarchy to selected levels
for (auto It = SelectedLevelsList.CreateIterator(); It; ++It)
{
TSharedPtr<FLevelModel> ParentLevelModel = (*It)->GetParent();
while (ParentLevelModel.IsValid())
{
ParentLevelModel->SetLevelExpansionFlag(true);
ParentLevelModel = ParentLevelModel->GetParent();
}
}
BroadcastSelectionChanged();
}
void FLevelCollectionModel::OnLevelsSelectionChangedOutside()
{
if (!bUpdatingLevelsSelection)
{
SetSelectedLevelsFromWorld();
}
}
void FLevelCollectionModel::OnLevelsHierarchyChanged()
{
BroadcastHierarchyChanged();
}
void FLevelCollectionModel::OnLevelAddedToWorld(ULevel* InLevel, UWorld* InWorld)
{
if (InWorld == GetWorld())
{
TSharedPtr<FLevelModel> LevelModel = FindLevelModel(InLevel->GetOutermost()->GetFName());
if (LevelModel.IsValid())
{
LevelModel->OnLevelAddedToWorld(InLevel);
}
}
}
void FLevelCollectionModel::OnLevelRemovedFromWorld(ULevel* InLevel, UWorld* InWorld)
{
if (InWorld == GetWorld())
{
TSharedPtr<FLevelModel> LevelModel = FindLevelModel(InLevel->GetOutermost()->GetFName());
if (LevelModel.IsValid())
{
LevelModel->OnLevelRemovedFromWorld();
}
}
}
void FLevelCollectionModel::OnRedrawAllViewports()
{
if (GShaderCompilingManager && GShaderCompilingManager->IsCompiling())
{
// Editor seems like still compiling shaders, do not request tiles redraw until all shaders complation is finished
// Basically redraw only on last event
return;
}
RequestRedrawAllLevels();
}
void FLevelCollectionModel::OnLevelActorAdded(AActor* InActor)
{
if (InActor &&
InActor->GetWorld() == CurrentWorld.Get()) // we care about our world only
{
bRequestedUpdateActorsCount = true;
}
}
void FLevelCollectionModel::OnLevelActorDeleted(AActor* InActor)
{
bRequestedUpdateActorsCount = true;
}
void FLevelCollectionModel::OnFilterChanged()
{
PopulateFilteredLevelsList();
BroadcastCollectionChanged();
}
void FLevelCollectionModel::CacheCanExecuteSourceControlVars() const
{
bCanExecuteSCCCheckOut = false;
bCanExecuteSCCOpenForAdd = false;
bCanExecuteSCCCheckIn = false;
bCanExecuteSCC = false;
ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider();
for (auto It = SelectedLevelsList.CreateConstIterator(); It; ++It)
{
if (ISourceControlModule::Get().IsEnabled() && SourceControlProvider.IsAvailable())
{
bCanExecuteSCC = true;
ULevel* Level = (*It)->GetLevelObject();
if (Level)
{
// Check the SCC state for each package in the selected paths
FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(Level->GetOutermost(), EStateCacheUsage::Use);
if (SourceControlState.IsValid())
{
if (SourceControlState->CanCheckout())
{
bCanExecuteSCCCheckOut = true;
}
else if (!SourceControlState->IsSourceControlled())
{
bCanExecuteSCCOpenForAdd = true;
}
else if (SourceControlState->IsCheckedOut() || SourceControlState->IsAdded())
{
bCanExecuteSCCCheckIn = true;
}
}
}
}
if (bCanExecuteSCCCheckOut &&
bCanExecuteSCCOpenForAdd &&
bCanExecuteSCCCheckIn)
{
// All options are available, no need to keep iterating
break;
}
}
}
bool FLevelCollectionModel::IsValidMoveActorsToLevel() const
{
static bool bCachedIsValidActorMoveResult = false;
if (bSelectionHasChanged)
{
bSelectionHasChanged = false;
bCachedIsValidActorMoveResult = false;
// We can only operate on a single selected level
if ( SelectedLevelsList.Num() == 1 )
{
ULevel* Level = SelectedLevelsList[0]->GetLevelObject();
if (Level)
{
// Allow the move if at least one actor is in another level
USelection* SelectedActors = GEditor->GetSelectedActors();
for (FSelectionIterator Iter(*SelectedActors); Iter; ++Iter)
{
AActor* Actor = CastChecked<AActor>(*Iter);
if (Actor != nullptr)
{
if (Actor->GetLevel() != Level)
{
bCachedIsValidActorMoveResult = true;
break;
}
}
}
}
}
}
// if non of the selected actors are in the level, just check the level is unlocked
return bCachedIsValidActorMoveResult && AreAllSelectedLevelsEditableAndVisible();
}
bool FLevelCollectionModel::IsValidMoveFoliageToLevel() const
{
if (IsOneLevelSelected() &&
AreAllSelectedLevelsEditableAndVisible() &&
GLevelEditorModeTools().IsModeActive(FBuiltinEditorModes::EM_Foliage))
{
IFoliageEditModule& FoliageModule = FModuleManager::GetModuleChecked<IFoliageEditModule>("FoliageEdit");
ULevel* TargetLevel = GetSelectedLevels()[0]->GetLevelObject();
return FoliageModule.CanMoveSelectedFoliageToLevel(TargetLevel);
}
return false;
}
void FLevelCollectionModel::OnActorSelectionChanged(UObject* obj)
{
OnActorOrLevelSelectionChanged();
}
void FLevelCollectionModel::OnActorOrLevelSelectionChanged()
{
bSelectionHasChanged = true;
}
#undef LOCTEXT_NAMESPACE