// 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& 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 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& InFilter) { Filters->Add(InFilter); OnFilterChanged(); } void FLevelCollectionModel::RemoveFilter(const TSharedRef& 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>& SelectedLevelObjects = CurrentWorld->GetSelectedLevels(); FLevelModelList LevelsToSelect; for (ULevel* LevelObject : SelectedLevelObjects) { TSharedPtr LevelModel = FindLevelModel(LevelObject); if (LevelModel.IsValid()) { LevelsToSelect.Add(LevelModel); } } SetSelectedLevels(LevelsToSelect); } TSharedPtr FLevelCollectionModel::FindLevelModel(ULevel* InLevel) const { if (InLevel) { for (auto It = AllLevelsList.CreateConstIterator(); It; ++It) { if ((*It)->GetLevelObject() == InLevel) { return (*It); } } } // not found return TSharedPtr(); } TSharedPtr FLevelCollectionModel::FindLevelModel(const FName& PackageName) const { const TSharedPtr* LevelModel = AllLevelsMap.Find(PackageName); if (LevelModel != NULL) { return *LevelModel; } // not found return TSharedPtr(); } 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 LevelModels; TArray 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 LevelModels; TArray 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 LevelModels; TArray 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 LevelModels; TArray 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 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 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 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(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 InParent) { // Attach levels to the new parent for (auto It = InLevels.CreateConstIterator(); It; ++It) { (*It)->AttachTo(InParent); } OnLevelsHierarchyChanged(); } void FLevelCollectionModel::AddExistingLevelsFromAssetData(const TArray& WorldList) { } TSharedPtr FLevelCollectionModel::CreateDragDropOp() const { return MakeShareable( new WorldHierarchy::FWorldBrowserDragDropOp ); } TSharedPtr FLevelCollectionModel::CreateDragDropOp(const FLevelModelList& InLevels) const { return TSharedPtr(); } 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(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 InA, TSharedPtr InB) const { return false; } void FLevelCollectionModel::RegisterDetailsCustomization(FPropertyEditorModule& InPropertyModule, TSharedPtr InDetailsView) { } void FLevelCollectionModel::UnregisterDetailsCustomization(FPropertyEditorModule& InPropertyModule, TSharedPtr 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& 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 FilenamesToCheckOut = GetFilenamesList(InList); // Update the source control status of all potentially relevant packages ISourceControlModule::Get().GetProvider().Execute( ISourceControlOperation::Create(), FilenamesToCheckOut ); // Now check them out FEditorFileUtils::CheckoutPackages(FilenamesToCheckOut); } void FLevelCollectionModel::SCCCheckIn(const FLevelModelList& InList) { TArray PackagesToCheckIn = GetPackagesList(InList); TArray 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 FilenamesList = GetFilenamesList(InList); TArray FilenamesToAdd; TArray 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(), FilenamesToAdd); } } void FLevelCollectionModel::SCCHistory(const FLevelModelList& InList) { // This is odd, why SCC needs package names, instead of filenames? TArray 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("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(); 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()->TextDiffToolPath.FilePath; AssetToolsModule.Get().CreateDiffProcess(DiffCommand, OldTextFilename, NewTextFilename); AssetToolsModule.Get().DiffAssets(OldPackage, OriginalPackage, OldRevision, NewRevision); } } } } } } } TArray FLevelCollectionModel::GetPackageNamesList(const FLevelModelList& InList) { TArray ResultList; for (auto It = InList.CreateConstIterator(); It; ++It) { if ((*It)->HasValidPackage()) { ResultList.Add((*It)->GetLongPackageName()); } } return ResultList; } TArray FLevelCollectionModel::GetFilenamesList(const FLevelModelList& InList) { TArray ResultList; for (auto It = InList.CreateConstIterator(); It; ++It) { if ((*It)->HasValidPackage()) { ResultList.Add((*It)->GetPackageFileName()); } } return ResultList; } TArray FLevelCollectionModel::GetPackagesList(const FLevelModelList& InList) { TArray ResultList; for (auto It = InList.CreateConstIterator(); It; ++It) { ULevel* Level = (*It)->GetLevelObject(); if (Level) { ResultList.Add(Level->GetOutermost()); } } return ResultList; } TArray FLevelCollectionModel::GetLevelObjectList(const FLevelModelList& InList) { TArray 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 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 PackageNames = GetPackageNamesList(GetSelectedLevels()); FAssetToolsModule& AssetToolsModule = FModuleManager::Get().LoadModuleChecked("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 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(*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("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& 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 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 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 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 LevelModel = FindLevelModel(InLevel->GetOutermost()->GetFName()); if (LevelModel.IsValid()) { LevelModel->OnLevelAddedToWorld(InLevel); } } } void FLevelCollectionModel::OnLevelRemovedFromWorld(ULevel* InLevel, UWorld* InWorld) { if (InWorld == GetWorld()) { TSharedPtr 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(*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("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