You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#UE4 If you have actors from multiple levels selected, you can now move them to the selected level via the level browser. It will only move actors that are not already in the destination level. -------- Integrated using branch Ue4-To-UE4-Fortnite-Simple (reversed) of change#2270697 by Bob.Tellez on 2014/08/25 12:47:35. [CL 2270698 by Bob Tellez in Main branch]
2163 lines
67 KiB
C++
2163 lines
67 KiB
C++
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
|
|
#include "LevelsPrivatePCH.h"
|
|
|
|
#include "Matinee/MatineeActor.h"
|
|
|
|
#include "EditorLevelUtils.h"
|
|
#include "LevelUtils.h"
|
|
#include "Editor/PropertyEditor/Public/PropertyEditorModule.h"
|
|
#include "Editor/MainFrame/Public/MainFrame.h"
|
|
#include "DesktopPlatformModule.h"
|
|
#include "LevelEditor.h"
|
|
#include "AssetToolsModule.h"
|
|
|
|
#include "ISourceControlModule.h"
|
|
#include "ISourceControlRevision.h"
|
|
#include "SourceControlWindows.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "LevelsView"
|
|
|
|
FLevelCollectionViewModel::FLevelCollectionViewModel( const TWeakObjectPtr< UEditorEngine >& InEditor )
|
|
: bIsRefreshing( false )
|
|
, bPendingUpdateActorsCount( false )
|
|
, Filters( MakeShareable( new LevelFilterCollection ) )
|
|
, CommandList( MakeShareable( new FUICommandList ) )
|
|
, Editor( InEditor )
|
|
, CurrentWorld( NULL )
|
|
, AddedLevelStreamingClass( ULevelStreamingKismet::StaticClass() )
|
|
, bSelectionHasChanged( true )
|
|
{
|
|
OnResetLevels();
|
|
}
|
|
|
|
FLevelCollectionViewModel::~FLevelCollectionViewModel()
|
|
{
|
|
Filters->OnChanged().RemoveAll( this );
|
|
Editor->UnregisterForUndo( this );
|
|
if( CurrentWorld.IsValid() )
|
|
{
|
|
RemoveLevelChangeHandlers( CurrentWorld.Get() );
|
|
}
|
|
if( GEngine )
|
|
{
|
|
GEngine->OnWorldAdded().RemoveAll( this );
|
|
GEngine->OnWorldDestroyed().RemoveAll( this );
|
|
GEngine->OnLevelActorAdded().RemoveAll( this );
|
|
GEngine->OnLevelActorDeleted().RemoveAll( this );
|
|
}
|
|
}
|
|
|
|
void FLevelCollectionViewModel::Initialize()
|
|
{
|
|
BindCommands();
|
|
|
|
Filters->OnChanged().AddSP( this, &FLevelCollectionViewModel::OnFilterChanged );
|
|
Editor->RegisterForUndo( this );
|
|
|
|
// Add world added/destroyed handlers
|
|
if( GEngine )
|
|
{
|
|
GEngine->OnWorldAdded().AddSP( this, &FLevelCollectionViewModel::WorldAdded );
|
|
GEngine->OnWorldDestroyed().AddSP( this, &FLevelCollectionViewModel::WorldDestroyed );
|
|
GEngine->OnLevelActorAdded().AddSP( this, &FLevelCollectionViewModel::OnLevelActorAdded );
|
|
GEngine->OnLevelActorDeleted().AddSP( this, &FLevelCollectionViewModel::OnLevelActorDeleted );
|
|
|
|
|
|
// We need a world to browse levels. Get the first one in the list
|
|
if( GEditor )
|
|
{
|
|
SetCurrentWorld( GEditor->GetEditorWorldContext().World() );
|
|
}
|
|
}
|
|
|
|
USelection::SelectionChangedEvent.AddSP(this, &FLevelCollectionViewModel::OnActorSelectionChanged);
|
|
SelectionChanged.AddSP(this, &FLevelCollectionViewModel::OnActorOrLevelSelectionChanged);
|
|
}
|
|
|
|
void FLevelCollectionViewModel::Tick( float DeltaTime )
|
|
{
|
|
if (bPendingUpdateActorsCount)
|
|
{
|
|
UpdateLevelActorsCount();
|
|
}
|
|
}
|
|
|
|
TStatId FLevelCollectionViewModel::GetStatId() const
|
|
{
|
|
RETURN_QUICK_DECLARE_CYCLE_STAT(FLevelCollectionViewModel, STATGROUP_Tickables);
|
|
}
|
|
|
|
void FLevelCollectionViewModel::BindCommands()
|
|
{
|
|
const FLevelsViewCommands& Commands = FLevelsViewCommands::Get();
|
|
FUICommandList& ActionList = *CommandList;
|
|
|
|
//selected level
|
|
ActionList.MapAction( Commands.MakeLevelCurrent,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::MakeLevelCurrent_Executed ),
|
|
FCanExecuteAction::CreateSP( this, &FLevelCollectionViewModel::IsSelectedLevelUnlocked ) );
|
|
|
|
ActionList.MapAction( Commands.MoveActorsToSelected,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::MoveActorsToSelected_Executed ),
|
|
FCanExecuteAction::CreateSP(this, &FLevelCollectionViewModel::IsValidMoveActorsToLevel));
|
|
|
|
//invalid selected levels
|
|
ActionList.MapAction( Commands.FixUpInvalidReference,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::FixupInvalidReference_Executed ) );
|
|
|
|
ActionList.MapAction( Commands.RemoveInvalidReference,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::RemoveInvalidSelectedLevels_Executed ));
|
|
|
|
//levels
|
|
ActionList.MapAction( Commands.EditProperties,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::EditProperties_Executed ),
|
|
FCanExecuteAction::CreateSP( this, &FLevelCollectionViewModel::CanEditProperties ) );
|
|
|
|
ActionList.MapAction( Commands.SaveSelectedLevels,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::SaveSelectedLevels_Executed ),
|
|
FCanExecuteAction::CreateSP( this, &FLevelCollectionViewModel::AreSelectedLevelsUnlocked ) );
|
|
|
|
ActionList.MapAction( Commands.SCCCheckOut,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::OnSCCCheckOut ) );
|
|
|
|
ActionList.MapAction( Commands.SCCCheckIn,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::OnSCCCheckIn ) );
|
|
|
|
ActionList.MapAction( Commands.SCCOpenForAdd,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::OnSCCOpenForAdd ) );
|
|
|
|
ActionList.MapAction( Commands.SCCHistory,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::OnSCCHistory ) );
|
|
|
|
ActionList.MapAction( Commands.SCCRefresh,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::OnSCCRefresh ) );
|
|
|
|
ActionList.MapAction( Commands.SCCDiffAgainstDepot,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::OnSCCDiffAgainstDepot ) );
|
|
|
|
ActionList.MapAction( Commands.SCCConnect,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::OnSCCConnect ) );
|
|
|
|
ActionList.MapAction( Commands.MigrateSelectedLevels,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::MigrateSelectedLevels_Executed ),
|
|
FCanExecuteAction::CreateSP( this, &FLevelCollectionViewModel::AreSelectedLevelsUnlocked ) );
|
|
|
|
ActionList.MapAction( Commands.DisplayActorCount,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::OnToggleDisplayActorCount ),
|
|
FCanExecuteAction(),
|
|
FCanExecuteAction::CreateSP( this, &FLevelCollectionViewModel::GetDisplayActorCountState ) );
|
|
|
|
ActionList.MapAction( Commands.DisplayLightmassSize,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::OnToggleLightmassSize ),
|
|
FCanExecuteAction(),
|
|
FCanExecuteAction::CreateSP( this, &FLevelCollectionViewModel::GetDisplayLightmassSizeState ) );
|
|
|
|
ActionList.MapAction( Commands.DisplayFileSize,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::OnToggleFileSize ),
|
|
FCanExecuteAction(),
|
|
FCanExecuteAction::CreateSP( this, &FLevelCollectionViewModel::GetDisplayFileSizeState ) );
|
|
|
|
ActionList.MapAction( Commands.DisplayPaths,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::OnToggleDisplayPaths ),
|
|
FCanExecuteAction(),
|
|
FCanExecuteAction::CreateSP( this, &FLevelCollectionViewModel::GetDisplayPathsState ) );
|
|
|
|
ActionList.MapAction( Commands.DisplayEditorOffset,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::OnToggleEditorOffset ),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked::CreateSP( this, &FLevelCollectionViewModel::GetDisplayEditorOffsetState ) );
|
|
|
|
ActionList.MapAction( Commands.CreateEmptyLevel,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::CreateEmptyLevel_Executed ) );
|
|
|
|
ActionList.MapAction( Commands.AddExistingLevel,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::AddExistingLevel_Executed ) );
|
|
|
|
ActionList.MapAction( Commands.AddSelectedActorsToNewLevel,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::AddSelectedActorsToNewLevel_Executed ),
|
|
FCanExecuteAction::CreateSP( this, &FLevelCollectionViewModel::AreActorsSelected ) );
|
|
|
|
ActionList.MapAction( Commands.RemoveSelectedLevels,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::RemoveSelectedLevels_Executed ),
|
|
FCanExecuteAction::CreateSP( this, &FLevelCollectionViewModel::AreSelectedLevelsUnlockedAndNotPersistent ) );
|
|
|
|
ActionList.MapAction( Commands.MergeSelectedLevels,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::MergeSelectedLevels_Executed ),
|
|
FCanExecuteAction::CreateSP( this, &FLevelCollectionViewModel::AreSelectedLevelsUnlocked ) );
|
|
|
|
// new level streaming method
|
|
ActionList.MapAction( Commands.SetAddStreamingMethod_Blueprint,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::SetAddedLevelStreamingClass_Executed, ULevelStreamingKismet::StaticClass() ),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked::CreateSP( this, &FLevelCollectionViewModel::IsNewStreamingMethodChecked, ULevelStreamingKismet::StaticClass()));
|
|
|
|
ActionList.MapAction( Commands.SetAddStreamingMethod_AlwaysLoaded,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::SetAddedLevelStreamingClass_Executed, ULevelStreamingAlwaysLoaded::StaticClass() ),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked::CreateSP( this, &FLevelCollectionViewModel::IsNewStreamingMethodChecked, ULevelStreamingAlwaysLoaded::StaticClass()));
|
|
|
|
// change streaming method
|
|
ActionList.MapAction( Commands.SetStreamingMethod_Blueprint,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::SetStreamingLevelsClass_Executed, ULevelStreamingKismet::StaticClass() ),
|
|
FCanExecuteAction::CreateSP( this, &FLevelCollectionViewModel::AreSelectedLevelsUnlockedAndNotPersistent ),
|
|
FIsActionChecked::CreateSP( this, &FLevelCollectionViewModel::IsStreamingMethodChecked, ULevelStreamingKismet::StaticClass()));
|
|
|
|
ActionList.MapAction( Commands.SetStreamingMethod_AlwaysLoaded,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::SetStreamingLevelsClass_Executed, ULevelStreamingAlwaysLoaded::StaticClass() ),
|
|
FCanExecuteAction::CreateSP( this, &FLevelCollectionViewModel::AreSelectedLevelsUnlockedAndNotPersistent ),
|
|
FIsActionChecked::CreateSP( this, &FLevelCollectionViewModel::IsStreamingMethodChecked, ULevelStreamingAlwaysLoaded::StaticClass()));
|
|
|
|
//level selection
|
|
ActionList.MapAction( Commands.SelectAllLevels,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::SelectAllLevels_Executed ) );
|
|
|
|
ActionList.MapAction( Commands.DeselectAllLevels,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::DeselectAllLevels_Executed ) );
|
|
|
|
ActionList.MapAction( Commands.InvertLevelSelection,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::InvertSelection_Executed ) );
|
|
|
|
|
|
//actors
|
|
ActionList.MapAction( Commands.SelectLevelActors,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::SelectActors_Executed ),
|
|
FCanExecuteAction::CreateSP( this, &FLevelCollectionViewModel::AreSelectedLevelsUnlocked ) );
|
|
|
|
ActionList.MapAction( Commands.DeselectLevelActors,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::DeselectActors_Executed ),
|
|
FCanExecuteAction::CreateSP( this, &FLevelCollectionViewModel::AreSelectedLevelsUnlocked ) );
|
|
|
|
|
|
//streaming volumes
|
|
ActionList.MapAction( Commands.AddStreamingLevelVolumes,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::AddStreamingLevelVolumes_Executed ),
|
|
FCanExecuteAction::CreateSP( this, &FLevelCollectionViewModel::CanAddOrSelectStreamingVolumes ) );
|
|
|
|
ActionList.MapAction( Commands.SetStreamingLevelVolumes,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::SetStreamingLevelVolumes_Executed ),
|
|
FCanExecuteAction::CreateSP( this, &FLevelCollectionViewModel::CanAddOrSelectStreamingVolumes ) );
|
|
|
|
ActionList.MapAction( Commands.SelectStreamingVolumes,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::SelectStreamingVolumes_Executed ),
|
|
FCanExecuteAction::CreateSP( this, &FLevelCollectionViewModel::AreSelectedLevelsUnlocked ) );
|
|
|
|
ActionList.MapAction( Commands.ClearStreamingVolumes,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::ClearStreamingVolumes_Executed ),
|
|
FCanExecuteAction::CreateSP( this, &FLevelCollectionViewModel::AreSelectedLevelsUnlocked ) );
|
|
|
|
|
|
//visibility
|
|
ActionList.MapAction( Commands.ShowSelectedLevels,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::ShowSelectedLevels_Executed ),
|
|
FCanExecuteAction::CreateSP( this, &FLevelCollectionViewModel::AreLevelsSelected ) );
|
|
|
|
ActionList.MapAction( Commands.HideSelectedLevels,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::HideSelectedLevels_Executed ),
|
|
FCanExecuteAction::CreateSP( this, &FLevelCollectionViewModel::AreLevelsSelected ) );
|
|
|
|
ActionList.MapAction( Commands.ShowOnlySelectedLevels,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::ShowOnlySelectedLevels_Executed ),
|
|
FCanExecuteAction::CreateSP( this, &FLevelCollectionViewModel::AreLevelsSelected ) );
|
|
|
|
ActionList.MapAction( Commands.ShowAllLevels,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::ShowAllLevels_Executed ) );
|
|
|
|
ActionList.MapAction( Commands.HideAllLevels,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::HideAllLevels_Executed ) );
|
|
|
|
|
|
//lock
|
|
ActionList.MapAction( Commands.LockSelectedLevels,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::LockSelectedLevels_Executed ),
|
|
FCanExecuteAction::CreateSP( this, &FLevelCollectionViewModel::AreSelectedLevelsNotPersistent ) );
|
|
|
|
ActionList.MapAction( Commands.UnockSelectedLevels,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::UnockSelectedLevels_Executed ),
|
|
FCanExecuteAction::CreateSP( this, &FLevelCollectionViewModel::AreSelectedLevelsNotPersistent ) );
|
|
|
|
ActionList.MapAction( Commands.LockAllLevels,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::LockAllLevels_Executed ) );
|
|
|
|
ActionList.MapAction( Commands.UnockAllLevels,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::UnockAllLevels_Executed ) );
|
|
|
|
ActionList.MapAction( Commands.LockReadOnlyLevels,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::ToggleReadOnlyLevels_Executed ) );
|
|
|
|
ActionList.MapAction( Commands.UnlockReadOnlyLevels,
|
|
FExecuteAction::CreateSP( this, &FLevelCollectionViewModel::ToggleReadOnlyLevels_Executed ) );
|
|
}
|
|
|
|
void FLevelCollectionViewModel::ToggleReadOnlyLevels_Executed()
|
|
{
|
|
GEngine->bLockReadOnlyLevels = !GEngine->bLockReadOnlyLevels;
|
|
}
|
|
|
|
void FLevelCollectionViewModel::OnToggleDisplayActorCount()
|
|
{
|
|
ULevelBrowserSettings* Settings = GetMutableDefault<ULevelBrowserSettings>();
|
|
Settings->bDisplayActorCount = !Settings->bDisplayActorCount;
|
|
Settings->PostEditChange();
|
|
|
|
DisplayActorCountChanged.Broadcast(Settings->bDisplayActorCount);
|
|
}
|
|
|
|
bool FLevelCollectionViewModel::GetDisplayActorCountState() const
|
|
{
|
|
return (GetDefault<ULevelBrowserSettings>()->bDisplayActorCount);
|
|
}
|
|
|
|
void FLevelCollectionViewModel::OnToggleLightmassSize()
|
|
{
|
|
ULevelBrowserSettings* Settings = GetMutableDefault<ULevelBrowserSettings>();
|
|
Settings->bDisplayLightmassSize = !GetDisplayLightmassSizeState();
|
|
Settings->PostEditChange();
|
|
|
|
DisplayLightmassSizeChanged.Broadcast(Settings->bDisplayLightmassSize);
|
|
}
|
|
|
|
bool FLevelCollectionViewModel::GetDisplayLightmassSizeState() const
|
|
{
|
|
return GetDefault<ULevelBrowserSettings>()->bDisplayLightmassSize;
|
|
}
|
|
|
|
void FLevelCollectionViewModel::OnToggleFileSize()
|
|
{
|
|
ULevelBrowserSettings* Settings = GetMutableDefault<ULevelBrowserSettings>();
|
|
Settings->bDisplayFileSize = !GetDisplayFileSizeState();
|
|
Settings->PostEditChange();
|
|
|
|
DisplayFileSizeChanged.Broadcast(Settings->bDisplayFileSize);
|
|
}
|
|
|
|
bool FLevelCollectionViewModel::GetDisplayFileSizeState() const
|
|
{
|
|
return GetDefault<ULevelBrowserSettings>()->bDisplayFileSize;
|
|
}
|
|
|
|
void FLevelCollectionViewModel::OnToggleEditorOffset()
|
|
{
|
|
ULevelBrowserSettings* Settings = GetMutableDefault<ULevelBrowserSettings>();
|
|
Settings->bDisplayEditorOffset = !Settings->bDisplayEditorOffset;
|
|
Settings->PostEditChange();
|
|
|
|
DisplayEditorOffsetChanged.Broadcast(Settings->bDisplayEditorOffset);
|
|
}
|
|
|
|
bool FLevelCollectionViewModel::GetDisplayEditorOffsetState() const
|
|
{
|
|
return GetDefault<ULevelBrowserSettings>()->bDisplayEditorOffset;
|
|
}
|
|
|
|
void FLevelCollectionViewModel::OnToggleDisplayPaths()
|
|
{
|
|
ULevelBrowserSettings* Settings = GetMutableDefault<ULevelBrowserSettings>();
|
|
Settings->bDisplayPaths = !GetDisplayPathsState();
|
|
Settings->PostEditChange();
|
|
}
|
|
|
|
bool FLevelCollectionViewModel::GetDisplayPathsState() const
|
|
{
|
|
return (GetDefault<ULevelBrowserSettings>()->bDisplayPaths);
|
|
}
|
|
|
|
bool FLevelCollectionViewModel::CanShiftSelection()
|
|
{
|
|
if ( !IsOneLevelSelected() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for ( int32 i = 0; i < SelectedLevels.Num(); ++i )
|
|
{
|
|
if ( !SelectedLevels[i].IsValid() || SelectedLevels[i]->IsLocked() ||
|
|
SelectedLevels[i]->IsPersistent() || !SelectedLevels[i]->IsLevel() )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void FLevelCollectionViewModel::ShiftSelection( bool bUp )
|
|
{
|
|
if ( !CanShiftSelection() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( !IsSelectedLevelUnlocked() )
|
|
{
|
|
FMessageDialog::Open( EAppMsgType::Ok, LOCTEXT("ShiftLevelLocked", "Shift Level: The requested operation could not be completed because the level is locked.") );
|
|
return;
|
|
}
|
|
|
|
const ULevel* InLevel = SelectedLevels[0]->GetLevel().Get();
|
|
UWorld *OwningWorld = InLevel->OwningWorld;
|
|
|
|
check( OwningWorld );
|
|
|
|
int32 PrevFoundLevelIndex = -1;
|
|
int32 FoundLevelIndex = -1;
|
|
int32 PostFoundLevelIndex = -1;
|
|
for( int32 LevelIndex = 0 ; LevelIndex < OwningWorld->StreamingLevels.Num() ; ++LevelIndex )
|
|
{
|
|
ULevelStreaming* StreamingLevel = OwningWorld->StreamingLevels[LevelIndex];
|
|
if( StreamingLevel != NULL )
|
|
{
|
|
ULevel* Level = StreamingLevel->GetLoadedLevel();
|
|
if( Level != NULL && Level->OwningWorld != NULL )
|
|
{
|
|
if ( FoundLevelIndex > -1 )
|
|
{
|
|
// Mark the first valid index after the found level and stop searching.
|
|
PostFoundLevelIndex = LevelIndex;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if ( Level == InLevel )
|
|
{
|
|
// We've found the level.
|
|
FoundLevelIndex = LevelIndex;
|
|
}
|
|
else
|
|
{
|
|
// Mark this level as being the index before the found level.
|
|
PrevFoundLevelIndex = LevelIndex;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we found the level . . .
|
|
if ( FoundLevelIndex > -1 )
|
|
{
|
|
// Check if we found a destination index to swap it to.
|
|
const int32 DestIndex = bUp ? PrevFoundLevelIndex : PostFoundLevelIndex;
|
|
const bool bFoundPrePost = DestIndex > -1;
|
|
if ( bFoundPrePost )
|
|
{
|
|
// Swap the level into position.
|
|
const FScopedTransaction Transaction( NSLOCTEXT("UnrealEd", "ShiftLevelInLevelBrowser", "Shift level in Level Browser") );
|
|
OwningWorld->Modify();
|
|
OwningWorld->StreamingLevels.Swap( FoundLevelIndex, DestIndex );
|
|
OwningWorld->MarkPackageDirty();
|
|
}
|
|
}
|
|
|
|
RefreshSortIndexes();
|
|
}
|
|
|
|
void FLevelCollectionViewModel::AddFilter( const TSharedRef< LevelFilter >& InFilter )
|
|
{
|
|
Filters->Add( InFilter );
|
|
OnFilterChanged();
|
|
}
|
|
|
|
void FLevelCollectionViewModel::RemoveFilter( const TSharedRef< LevelFilter >& InFilter )
|
|
{
|
|
Filters->Remove( InFilter );
|
|
OnFilterChanged();
|
|
}
|
|
|
|
TArray< TSharedPtr< FLevelViewModel > >& FLevelCollectionViewModel::GetLevels()
|
|
{
|
|
return FilteredLevelViewModels;
|
|
}
|
|
|
|
const TArray< TSharedPtr< FLevelViewModel > >& FLevelCollectionViewModel::GetSelectedLevels() const
|
|
{
|
|
return SelectedLevels;
|
|
}
|
|
|
|
const TArray< TSharedPtr< FLevelViewModel > >& FLevelCollectionViewModel::GetInvalidSelectedLevels() const
|
|
{
|
|
return InvalidSelectedLevels;
|
|
}
|
|
|
|
void FLevelCollectionViewModel::GetSelectedLevelNames( OUT TArray< FName >& OutSelectedLevelNames ) const
|
|
{
|
|
AppendSelectLevelNames( OutSelectedLevelNames );
|
|
}
|
|
|
|
void FLevelCollectionViewModel::SetSelectedLevels( const TArray< TSharedPtr< FLevelViewModel > >& InSelectedLevels )
|
|
{
|
|
InvalidSelectedLevels.Empty();
|
|
|
|
TArray<class ULevel*> Levels;
|
|
TArray<UPackage*> PackagesToUpdate;
|
|
for( auto LevelIter = InSelectedLevels.CreateConstIterator(); LevelIter; ++LevelIter )
|
|
{
|
|
const auto LevelViewModel = *LevelIter;
|
|
|
|
// Only add the level if its valid.
|
|
if( LevelViewModel->IsLevel() )
|
|
{
|
|
ULevel* Level = LevelViewModel->GetLevel().Get();
|
|
if(Level != NULL)
|
|
{
|
|
Levels.Add( Level );
|
|
if(Level->GetOutermost() != NULL)
|
|
{
|
|
PackagesToUpdate.Add(Level->GetOutermost());
|
|
}
|
|
}
|
|
}
|
|
else if ( LevelViewModel->IsLevelStreaming() )
|
|
{
|
|
InvalidSelectedLevels.AddUnique(LevelViewModel);
|
|
}
|
|
}
|
|
|
|
ISourceControlModule::Get().QueueStatusUpdate(PackagesToUpdate);
|
|
|
|
// Pass this list to our own function
|
|
SetSelectedLevelsInWorld( Levels );
|
|
}
|
|
|
|
void FLevelCollectionViewModel::SetSelectedLevels( const TArray< FName >& LevelNames )
|
|
{
|
|
TArray<class ULevel*> Levels;
|
|
TArray<UPackage*> PackagesToUpdate;
|
|
for( auto LevelIter = FilteredLevelViewModels.CreateConstIterator(); LevelIter; ++LevelIter )
|
|
{
|
|
const auto LevelViewModel = *LevelIter;
|
|
|
|
if( LevelNames.Contains( LevelViewModel->GetFName() ) )
|
|
{
|
|
// Only add the level if its valid and not a volume level.
|
|
if( LevelViewModel->IsLevel() )
|
|
{
|
|
ULevel* Level = LevelViewModel->GetLevel().Get();
|
|
if(Level != NULL)
|
|
{
|
|
Levels.Add( Level );
|
|
if(Level->GetOutermost() != NULL)
|
|
{
|
|
PackagesToUpdate.Add(Level->GetOutermost());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ISourceControlModule::Get().QueueStatusUpdate(PackagesToUpdate);
|
|
|
|
// Pass this list to our own function
|
|
SetSelectedLevelsInWorld( Levels );
|
|
}
|
|
|
|
void FLevelCollectionViewModel::SetSelectedLevelsInWorld( const TArray< ULevel* >& Levels )
|
|
{
|
|
// Pass the list we just created to the world to set the selection
|
|
if( CurrentWorld.IsValid() )
|
|
{
|
|
CurrentWorld->SetSelectedLevels( Levels );
|
|
}
|
|
}
|
|
|
|
void FLevelCollectionViewModel::SetSelectedLevel( const FName& LevelName )
|
|
{
|
|
ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider();
|
|
|
|
TArray<class ULevel*> Levels;
|
|
for( auto LevelIter = FilteredLevelViewModels.CreateConstIterator(); LevelIter; ++LevelIter )
|
|
{
|
|
const auto LevelViewModel = *LevelIter;
|
|
|
|
if( LevelName == LevelViewModel->GetFName() )
|
|
{
|
|
// Only add the level if its valid and not a volume level.
|
|
if( LevelViewModel->IsLevel() )
|
|
{
|
|
ULevel* Level = LevelViewModel->GetLevel().Get();
|
|
if(Level != NULL)
|
|
{
|
|
Levels.AddUnique( Level );
|
|
if(Level->GetOutermost() != NULL)
|
|
{
|
|
ISourceControlModule::Get().QueueStatusUpdate(Level->GetOutermost());
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
// Pass this list to our own function
|
|
SetSelectedLevelsInWorld( Levels );
|
|
}
|
|
|
|
const TSharedRef< FUICommandList > FLevelCollectionViewModel::GetCommandList() const
|
|
{
|
|
return CommandList;
|
|
}
|
|
|
|
void FLevelCollectionViewModel::BroadcastUpdateIfNotInRefresh()
|
|
{
|
|
if( bIsRefreshing == false )
|
|
{
|
|
LevelsChanged.Broadcast( ELevelsAction::Reset, NULL, NAME_None );
|
|
}
|
|
}
|
|
|
|
void FLevelCollectionViewModel::OnFilterChanged()
|
|
{
|
|
RefreshFilteredLevels();
|
|
BroadcastUpdateIfNotInRefresh();
|
|
}
|
|
|
|
void FLevelCollectionViewModel::Refresh()
|
|
{
|
|
OnLevelsChanged( ELevelsAction::Reset, NULL, NAME_None );
|
|
}
|
|
|
|
void FLevelCollectionViewModel::RefreshSelected()
|
|
{
|
|
check( !bIsRefreshing );
|
|
bIsRefreshing = true;
|
|
SelectedLevels.Reset() ;
|
|
if( CurrentWorld.IsValid() )
|
|
{
|
|
for( auto LevelIter = FilteredLevelViewModels.CreateConstIterator(); LevelIter; ++LevelIter )
|
|
{
|
|
const auto LevelViewModel = *LevelIter;
|
|
// Only add the level if its valid.
|
|
if( LevelViewModel->IsLevel() )
|
|
{
|
|
if( CurrentWorld->IsLevelSelected( LevelViewModel->GetLevel().Get() ) == true )
|
|
{
|
|
SelectedLevels.Add( LevelViewModel );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Broadcast that we have changed our selection
|
|
SelectionChanged.Broadcast();
|
|
BroadcastUpdateIfNotInRefresh();
|
|
bIsRefreshing = false;
|
|
}
|
|
|
|
void FLevelCollectionViewModel::OnLevelsChanged( const ELevelsAction::Type Action, const TWeakObjectPtr< ULevel >& ChangedLevel, const FName& ChangedProperty )
|
|
{
|
|
check( !bIsRefreshing );
|
|
bIsRefreshing = true;
|
|
|
|
switch ( Action )
|
|
{
|
|
case ELevelsAction::Add:
|
|
OnLevelAdded( ChangedLevel );
|
|
break;
|
|
|
|
case ELevelsAction::Rename:
|
|
//We purposely ignore re-filtering in this case
|
|
SortFilteredLevels();
|
|
break;
|
|
|
|
case ELevelsAction::Modify:
|
|
RefreshFilteredLevels();
|
|
break;
|
|
|
|
case ELevelsAction::Delete:
|
|
OnLevelDelete();
|
|
break;
|
|
|
|
case ELevelsAction::Reset:
|
|
default:
|
|
OnResetLevels();
|
|
break;
|
|
}
|
|
|
|
LevelsChanged.Broadcast( Action, ChangedLevel, ChangedProperty );
|
|
bIsRefreshing = false;
|
|
}
|
|
|
|
void FLevelCollectionViewModel::CreateLocalArrays()
|
|
{
|
|
ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider();
|
|
|
|
if ( CurrentWorld.IsValid() )
|
|
{
|
|
TArray< ULevel* > Levels;
|
|
for (int32 iLevel = 0; iLevel < CurrentWorld->GetNumLevels() ; iLevel++)
|
|
{
|
|
ULevel* Level = CurrentWorld->GetLevel( iLevel );
|
|
if(Level != NULL)
|
|
{
|
|
Levels.AddUnique( Level );
|
|
if(Level->GetOutermost() != NULL)
|
|
{
|
|
ISourceControlModule::Get().QueueStatusUpdate(Level->GetOutermost());
|
|
}
|
|
}
|
|
}
|
|
|
|
TArray< ULevelStreaming* > LevelsStreaming;
|
|
for (int32 iLevel = 0; iLevel < CurrentWorld->StreamingLevels.Num() ; iLevel++)
|
|
{
|
|
ULevelStreaming* StreamingLevel = CurrentWorld->StreamingLevels[iLevel];
|
|
if ( StreamingLevel != NULL )
|
|
{
|
|
ULevel* Level = StreamingLevel->GetLoadedLevel();
|
|
if ( !Levels.Contains(Level) )
|
|
{
|
|
LevelsStreaming.AddUnique(StreamingLevel);
|
|
}
|
|
}
|
|
}
|
|
|
|
CreateViewModels( Levels, LevelsStreaming );
|
|
}
|
|
}
|
|
|
|
void FLevelCollectionViewModel::PopulateLevelsList()
|
|
{
|
|
DestructivelyPurgeInvalidViewModels();
|
|
CreateLocalArrays();
|
|
RefreshSortIndexes();
|
|
SortFilteredLevels();
|
|
UpdateLevelActorsCount();
|
|
}
|
|
|
|
void FLevelCollectionViewModel::RefreshSortIndexes()
|
|
{
|
|
for( auto LevelIt = AllLevelViewModels.CreateIterator(); LevelIt; ++LevelIt )
|
|
{
|
|
// Only refresh the level if its valid.
|
|
if ( (*LevelIt)->IsValid() )
|
|
{
|
|
(*LevelIt)->RefreshStreamingLevelIndex();
|
|
}
|
|
}
|
|
|
|
OnFilterChanged();
|
|
}
|
|
|
|
void FLevelCollectionViewModel::OnLevelAdded( const TWeakObjectPtr< ULevel >& AddedLevel )
|
|
{
|
|
// Force a rebuild of the cached data
|
|
PopulateLevelsList();
|
|
// Tell anyone that wants to know of changes
|
|
BroadcastUpdateIfNotInRefresh();
|
|
}
|
|
|
|
void FLevelCollectionViewModel::OnLevelDelete()
|
|
{
|
|
// Force a rebuild of the cached data
|
|
PopulateLevelsList();
|
|
// Tell anyone that wants to know of changes
|
|
BroadcastUpdateIfNotInRefresh();
|
|
}
|
|
|
|
void FLevelCollectionViewModel::OnResetLevels()
|
|
{
|
|
// Force a rebuild of the cached data
|
|
PopulateLevelsList();
|
|
// Tell anyone that wants to know of changes
|
|
BroadcastUpdateIfNotInRefresh();
|
|
}
|
|
|
|
void FLevelCollectionViewModel::DestructivelyPurgeInvalidViewModels()
|
|
{
|
|
for( int LevelIndex = AllLevelViewModels.Num() - 1; LevelIndex >= 0; --LevelIndex )
|
|
{
|
|
const auto LevelViewModel = AllLevelViewModels[ LevelIndex ];
|
|
AllLevelViewModels.RemoveAt( LevelIndex );
|
|
FilteredLevelViewModels.Remove( LevelViewModel );
|
|
SelectedLevels.Remove( LevelViewModel );
|
|
}
|
|
}
|
|
|
|
void FLevelCollectionViewModel::CreateViewModels( const TArray< ULevel* >& InLevels, const TArray< ULevelStreaming* >& InLevelStreaming)
|
|
{
|
|
//InLevels
|
|
for( auto LevelIt = InLevels.CreateConstIterator(); LevelIt; ++LevelIt )
|
|
{
|
|
if( *LevelIt )
|
|
{
|
|
const TSharedRef< FLevelViewModel > NewLevelViewModel = FLevelViewModel::Create( *LevelIt, NULL, Editor );
|
|
AllLevelViewModels.Add( NewLevelViewModel );
|
|
|
|
if( Filters->PassesAllFilters( NewLevelViewModel ) )
|
|
{
|
|
FilteredLevelViewModels.Add( NewLevelViewModel );
|
|
}
|
|
}
|
|
}
|
|
|
|
//InStreamedLevels
|
|
for( auto LevelStreamingIt = InLevelStreaming.CreateConstIterator(); LevelStreamingIt; ++LevelStreamingIt )
|
|
{
|
|
ULevelStreaming* LevelStreaming = *LevelStreamingIt;
|
|
if( LevelStreaming != NULL )
|
|
{
|
|
const TSharedRef< FLevelViewModel > NewLevelViewModel = FLevelViewModel::Create(LevelStreaming->GetLoadedLevel(), LevelStreaming, Editor );
|
|
AllLevelViewModels.Add( NewLevelViewModel );
|
|
|
|
if( Filters->PassesAllFilters( NewLevelViewModel ) )
|
|
{
|
|
FilteredLevelViewModels.Add( NewLevelViewModel );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FLevelCollectionViewModel::ClearStreamingLevelVolumes()
|
|
{
|
|
for( auto LevelIt = SelectedLevels.CreateIterator(); LevelIt; ++LevelIt )
|
|
{
|
|
// Only process the level if its valid.
|
|
if ( (*LevelIt)->IsValid() )
|
|
{
|
|
ULevelStreaming* StreamingLevel = FLevelUtils::FindStreamingLevel( (*LevelIt)->GetLevel().Get() );
|
|
if ( StreamingLevel )
|
|
{
|
|
StreamingLevel->Modify();
|
|
|
|
// Disassociate the level from the volume.
|
|
for ( int32 i = 0 ; i < StreamingLevel->EditorStreamingVolumes.Num() ; ++i )
|
|
{
|
|
ALevelStreamingVolume* LevelStreamingVolume = StreamingLevel->EditorStreamingVolumes[i];
|
|
if ( LevelStreamingVolume )
|
|
{
|
|
LevelStreamingVolume->Modify();
|
|
LevelStreamingVolume->StreamingLevelNames.Remove( StreamingLevel->GetWorldAssetPackageFName() );
|
|
}
|
|
}
|
|
|
|
// Disassociate the volumes from the level.
|
|
StreamingLevel->EditorStreamingVolumes.Empty();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FLevelCollectionViewModel::RefreshFilteredLevels()
|
|
{
|
|
FilteredLevelViewModels.Empty();
|
|
|
|
for( auto LevelIt = AllLevelViewModels.CreateIterator(); LevelIt; ++LevelIt )
|
|
{
|
|
const auto LevelViewModel = *LevelIt;
|
|
// Only add valid levels.
|
|
if( LevelViewModel->IsValid() )
|
|
{
|
|
if( Filters->PassesAllFilters( LevelViewModel ) )
|
|
{
|
|
FilteredLevelViewModels.Add( LevelViewModel );
|
|
}
|
|
}
|
|
}
|
|
|
|
SortFilteredLevels();
|
|
}
|
|
|
|
void FLevelCollectionViewModel::SortFilteredLevels()
|
|
{
|
|
struct FCompareLevels
|
|
{
|
|
FORCEINLINE bool operator()( const TSharedPtr< FLevelViewModel >& Lhs, const TSharedPtr< FLevelViewModel >& Rhs ) const
|
|
{
|
|
//First, sort the ViewModels into tiers:
|
|
//Persistent Level
|
|
//Streaming Levels
|
|
|
|
int LeftTier = 0;
|
|
int RightTier = 0;
|
|
|
|
if ( Lhs->IsLevel() )
|
|
{
|
|
LeftTier = ( Lhs->IsPersistent() )? 1 : 2;
|
|
}
|
|
if ( Rhs->IsLevel() )
|
|
{
|
|
RightTier = ( Rhs->IsPersistent() )? 1 : 2;
|
|
}
|
|
|
|
if ( LeftTier != RightTier )
|
|
{
|
|
//Sort the tiers
|
|
return (LeftTier < RightTier);
|
|
}
|
|
else
|
|
{
|
|
//Within the Streaming Levels tier, sort by a user-defined order.
|
|
return (Lhs->GetStreamingLevelIndex() < Rhs->GetStreamingLevelIndex() );
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
|
|
FilteredLevelViewModels.Sort( FCompareLevels() );
|
|
}
|
|
|
|
void FLevelCollectionViewModel::AppendSelectLevelNames( TArray< FName >& OutLevelNames ) const
|
|
{
|
|
for(auto LevelsIt = SelectedLevels.CreateConstIterator(); LevelsIt; ++LevelsIt)
|
|
{
|
|
const TSharedPtr< FLevelViewModel >& Level = *LevelsIt;
|
|
OutLevelNames.Add( Level->GetFName() );
|
|
}
|
|
}
|
|
|
|
void FLevelCollectionViewModel::GetSelectedLevelPackages( TArray<UPackage*>* OutSelectedPackages, TArray<FString>* OutSelectedPackagesNames )
|
|
{
|
|
for( auto SelectedLevelIt = SelectedLevels.CreateIterator(); SelectedLevelIt; ++SelectedLevelIt )
|
|
{
|
|
if ( (*SelectedLevelIt)->GetLevel().IsValid() )
|
|
{
|
|
ULevel* CurrentLevel = (*SelectedLevelIt)->GetLevel().Get();
|
|
if(OutSelectedPackages)
|
|
{
|
|
(*OutSelectedPackages).Add( CurrentLevel->GetOutermost() );
|
|
}
|
|
|
|
if(OutSelectedPackagesNames)
|
|
{
|
|
(*OutSelectedPackagesNames).Add( CurrentLevel->GetOutermost()->GetName() );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//delegates
|
|
bool FLevelCollectionViewModel::IsOneLevelSelected() const
|
|
{
|
|
return ( SelectedLevels.Num() == 1 );
|
|
}
|
|
|
|
bool FLevelCollectionViewModel::IsSelectedLevelUnlocked() const
|
|
{
|
|
if ( !IsOneLevelSelected() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return ( SelectedLevels[0].IsValid() && !SelectedLevels[0]->IsLocked() );
|
|
}
|
|
|
|
bool FLevelCollectionViewModel::AreLevelsSelected() const
|
|
{
|
|
return ( SelectedLevels.Num() > 0 );
|
|
}
|
|
|
|
bool FLevelCollectionViewModel::AreSelectedLevelsUnlocked() const
|
|
{
|
|
if ( !AreLevelsSelected() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for ( int32 i = 0; i < SelectedLevels.Num(); ++i )
|
|
{
|
|
if ( !SelectedLevels[i].IsValid() || SelectedLevels[i]->IsLocked() )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FLevelCollectionViewModel::AreSelectedLevelsNotPersistent() const
|
|
{
|
|
if ( !AreLevelsSelected() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for ( int32 i = 0; i < SelectedLevels.Num(); ++i )
|
|
{
|
|
if ( !SelectedLevels[i].IsValid() || SelectedLevels[i]->IsPersistent() )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FLevelCollectionViewModel::AreSelectedLevelsUnlockedAndNotPersistent() const
|
|
{
|
|
if ( !AreLevelsSelected() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for ( int32 i = 0; i < SelectedLevels.Num(); ++i )
|
|
{
|
|
if ( !SelectedLevels[i].IsValid() || SelectedLevels[i]->IsLocked() || SelectedLevels[i]->IsPersistent() )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FLevelCollectionViewModel::AreAllSelectedLevelsUnlockedAndVisible() const
|
|
{
|
|
for (const auto& Level : SelectedLevels)
|
|
{
|
|
if (Level->IsLocked() == true ||
|
|
Level->IsVisible() == false)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return SelectedLevels.Num() > 0;
|
|
}
|
|
|
|
bool FLevelCollectionViewModel::AreActorsSelected() const
|
|
{
|
|
return Editor->GetSelectedActorCount() > 0;
|
|
}
|
|
|
|
bool FLevelCollectionViewModel::CanEditProperties() const
|
|
{
|
|
if ( !AreLevelsSelected() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for ( int32 i = 0; i < SelectedLevels.Num(); ++i )
|
|
{
|
|
if ( !SelectedLevels[i].IsValid() || SelectedLevels[i]->IsLocked() || SelectedLevels[i]->IsPersistent() )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FLevelCollectionViewModel::CanAddOrSelectStreamingVolumes() const
|
|
{
|
|
bool bStreamingLevelVolumeSelected = false;
|
|
|
|
for( FSelectionIterator It( GEditor->GetSelectedActorIterator() ); It; ++It )
|
|
{
|
|
const ALevelStreamingVolume* StreamingVolume = Cast<ALevelStreamingVolume>( *It );
|
|
if ( StreamingVolume && StreamingVolume->GetLevel() == GWorld->PersistentLevel )
|
|
{
|
|
bStreamingLevelVolumeSelected = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return !bStreamingLevelVolumeSelected ? false : AreSelectedLevelsUnlocked();
|
|
}
|
|
|
|
//selected level
|
|
void FLevelCollectionViewModel::MakeLevelCurrent_Executed()
|
|
{
|
|
check( SelectedLevels.Num() == 1 );
|
|
SelectedLevels[0]->MakeLevelCurrent();
|
|
}
|
|
|
|
void FLevelCollectionViewModel::EditProperties_Executed()
|
|
{
|
|
//Remove any selected actor info from the Inspector and the Property views.
|
|
GEditor->SelectNone( true, true );
|
|
|
|
TArray<UObject*> SelectedObjects;
|
|
|
|
for( auto SelectedLevelIt = SelectedLevels.CreateIterator(); SelectedLevelIt; ++SelectedLevelIt )
|
|
{
|
|
if( (*SelectedLevelIt)->IsLevelStreaming() )
|
|
{
|
|
ULevelStreaming* LevelStreaming = (*SelectedLevelIt)->GetLevelStreaming().Get();
|
|
SelectedObjects.Add( LevelStreaming );
|
|
}
|
|
}
|
|
|
|
if ( SelectedObjects.Num() > 0 )
|
|
{
|
|
IMainFrameModule& MainFrameModule = FModuleManager::LoadModuleChecked<IMainFrameModule>(TEXT("MainFrame"));
|
|
FPropertyEditorModule& PropertyEditorModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>( TEXT("PropertyEditor") );
|
|
|
|
// If the slate main frame is shown, summon a new property viewer in the Level editor module
|
|
if(MainFrameModule.IsWindowInitialized())
|
|
{
|
|
FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked<FLevelEditorModule>( TEXT("LevelEditor") );
|
|
LevelEditorModule.SummonSelectionDetails();
|
|
}
|
|
else // create a floating property window if the slate main frame is not initialized
|
|
{
|
|
PropertyEditorModule.CreateFloatingDetailsView( SelectedObjects, true );
|
|
}
|
|
|
|
GUnrealEd->UpdateFloatingPropertyWindowsFromActorList( SelectedObjects );
|
|
}
|
|
}
|
|
|
|
void FLevelCollectionViewModel::MoveActorsToSelected_Executed()
|
|
{
|
|
// We shouldn't be able to get here if we have more than 1 level selected. But just in case lets check.
|
|
if (SelectedLevels.Num() == 1)
|
|
{
|
|
// If matinee is open, and if an actor being moved belongs to it, message the user
|
|
if (GLevelEditorModeTools().IsModeActive(FBuiltinEditorModes::EM_InterpEdit))
|
|
{
|
|
const FEdModeInterpEdit* InterpEditMode = (const FEdModeInterpEdit*)GLevelEditorModeTools().GetActiveMode(FBuiltinEditorModes::EM_InterpEdit);
|
|
if (InterpEditMode && InterpEditMode->MatineeActor)
|
|
{
|
|
TArray<AActor*> ControlledActors;
|
|
InterpEditMode->MatineeActor->GetControlledActors(ControlledActors);
|
|
|
|
// are any of the selected actors in the matinee
|
|
USelection* SelectedActors = GEditor->GetSelectedActors();
|
|
for (FSelectionIterator Iter(*SelectedActors); Iter; ++Iter)
|
|
{
|
|
AActor* Actor = CastChecked<AActor>(*Iter);
|
|
if (Actor != nullptr && (Actor == InterpEditMode->MatineeActor || ControlledActors.Contains(Actor)))
|
|
{
|
|
const bool ExitInterp = EAppReturnType::Yes == FMessageDialog::Open(EAppMsgType::YesNo, NSLOCTEXT("UnrealEd", "MatineeUnableToMove", "You must close Matinee before moving actors.\nDo you wish to do this now and continue?"));
|
|
if (!ExitInterp)
|
|
{
|
|
return;
|
|
}
|
|
GLevelEditorModeTools().DeactivateMode(FBuiltinEditorModes::EM_InterpEdit);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
GEditor->MoveSelectedActorsToLevel( SelectedLevels[0]->GetLevel().Get() );
|
|
}
|
|
}
|
|
|
|
|
|
//levels
|
|
void FLevelCollectionViewModel::SaveSelectedLevels_Executed()
|
|
{
|
|
// NOTE: We'll build a list of levels to save here. We don't want to use the SelectedLevels member
|
|
// since that list will be reset when Serialize is called
|
|
TArray< ULevel* > LevelsToSave;
|
|
for( auto SelectedLevelIt = SelectedLevels.CreateIterator(); SelectedLevelIt; ++SelectedLevelIt )
|
|
{
|
|
if ( (*SelectedLevelIt)->GetLevel().IsValid() )
|
|
{
|
|
LevelsToSave.Add( (*SelectedLevelIt)->GetLevel().Get() );
|
|
}
|
|
}
|
|
|
|
TArray< UPackage* > PackagesNotNeedingCheckout;
|
|
// Prompt the user to check out the levels from source control before saving
|
|
if ( FEditorFileUtils::PromptToCheckoutLevels( false, LevelsToSave, &PackagesNotNeedingCheckout ) )
|
|
{
|
|
for( auto LevelToSaveIt = LevelsToSave.CreateIterator(); LevelToSaveIt; ++LevelToSaveIt )
|
|
{
|
|
FEditorFileUtils::SaveLevel( *LevelToSaveIt );
|
|
}
|
|
}
|
|
else if ( PackagesNotNeedingCheckout.Num() > 0 )
|
|
{
|
|
// The user canceled the checkout dialog but some packages didnt need to be checked out in order to save
|
|
// For each selected level if the package its in didnt need to be saved, save the level!
|
|
for( auto LevelToSaveIt = LevelsToSave.CreateIterator(); LevelToSaveIt; ++LevelToSaveIt )
|
|
{
|
|
ULevel* Level = *LevelToSaveIt;
|
|
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
|
|
LevelsToSave.Remove( Level );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Select the levels that were saved successfully
|
|
SetSelectedLevelsInWorld( LevelsToSave );
|
|
}
|
|
|
|
void FLevelCollectionViewModel::OnSCCCheckOut()
|
|
{
|
|
TArray<UPackage*> PackagesToCheckOut;
|
|
GetSelectedLevelPackages(&PackagesToCheckOut, NULL);
|
|
|
|
// Update the source control status of all potentially relevant packages
|
|
ISourceControlModule::Get().GetProvider().Execute(ISourceControlOperation::Create<FUpdateStatus>(), PackagesToCheckOut);
|
|
|
|
// Now check them out
|
|
FEditorFileUtils::CheckoutPackages(PackagesToCheckOut);
|
|
}
|
|
|
|
void FLevelCollectionViewModel::OnSCCCheckIn()
|
|
{
|
|
TArray<UPackage*> Packages;
|
|
TArray<FString> PackageNames;
|
|
GetSelectedLevelPackages(&Packages, &PackageNames);
|
|
|
|
// Prompt the user to ask if they would like to first save any dirty packages they are trying to check-in
|
|
const FEditorFileUtils::EPromptReturnCode UserResponse = FEditorFileUtils::PromptForCheckoutAndSave( Packages, 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 )
|
|
{
|
|
FSourceControlWindows::PromptForCheckin(PackageNames);
|
|
}
|
|
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 FLevelCollectionViewModel::OnSCCOpenForAdd()
|
|
{
|
|
ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider();
|
|
|
|
TArray<FString> PackageNames;
|
|
GetSelectedLevelPackages(NULL, &PackageNames);
|
|
|
|
TArray<FString> PackagesToAdd;
|
|
TArray<UPackage*> PackagesToSave;
|
|
for ( auto PackageIt = PackageNames.CreateConstIterator(); PackageIt; ++PackageIt )
|
|
{
|
|
const FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(SourceControlHelpers::PackageFilename(*PackageIt), EStateCacheUsage::Use);
|
|
if ( SourceControlState.IsValid() && !SourceControlState->IsSourceControlled() )
|
|
{
|
|
PackagesToAdd.Add(*PackageIt);
|
|
|
|
// Make sure the file actually exists on disk before adding it
|
|
FString Filename;
|
|
if ( !FPackageName::DoesPackageExist(*PackageIt, NULL, &Filename) )
|
|
{
|
|
UPackage* Package = FindPackage(NULL, **PackageIt);
|
|
if ( Package )
|
|
{
|
|
PackagesToSave.Add(Package);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( PackagesToAdd.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 FEditorFileUtils::EPromptReturnCode Return = FEditorFileUtils::PromptForCheckoutAndSave(PackagesToSave, bCheckDirty, bPromptToSave);
|
|
}
|
|
|
|
SourceControlProvider.Execute(ISourceControlOperation::Create<FMarkForAdd>(), SourceControlHelpers::PackageFilenames(PackagesToAdd));
|
|
}
|
|
}
|
|
|
|
void FLevelCollectionViewModel::OnSCCHistory()
|
|
{
|
|
TArray<FString> PackageNames;
|
|
for( auto SelectedLevelIt = SelectedLevels.CreateIterator(); SelectedLevelIt; ++SelectedLevelIt )
|
|
{
|
|
if ( (*SelectedLevelIt)->GetLevel().IsValid() )
|
|
{
|
|
PackageNames.Add( (*SelectedLevelIt)->GetLevel().Get()->GetOutermost()->GetName() );
|
|
}
|
|
}
|
|
|
|
FSourceControlWindows::DisplayRevisionHistory(SourceControlHelpers::PackageFilenames(PackageNames));
|
|
}
|
|
|
|
void FLevelCollectionViewModel::OnSCCRefresh()
|
|
{
|
|
if(ISourceControlModule::Get().IsEnabled())
|
|
{
|
|
TArray<UPackage*> Packages;
|
|
for( auto SelectedLevelIt = SelectedLevels.CreateIterator(); SelectedLevelIt; ++SelectedLevelIt )
|
|
{
|
|
if ( (*SelectedLevelIt)->GetLevel().IsValid() )
|
|
{
|
|
Packages.Add( (*SelectedLevelIt)->GetLevel().Get()->GetOutermost() );
|
|
}
|
|
}
|
|
|
|
ISourceControlModule::Get().QueueStatusUpdate(Packages);
|
|
}
|
|
}
|
|
|
|
void FLevelCollectionViewModel::OnSCCDiffAgainstDepot()
|
|
{
|
|
// Load the asset registry module
|
|
FAssetToolsModule& AssetToolsModule = FModuleManager::GetModuleChecked<FAssetToolsModule>("AssetTools");
|
|
|
|
ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider();
|
|
|
|
// Iterate over each selected asset
|
|
for( auto SelectedLevelIt = SelectedLevels.CreateIterator(); SelectedLevelIt; ++SelectedLevelIt )
|
|
{
|
|
UPackage* OriginalPackage = (*SelectedLevelIt)->GetLevel().Get()->GetOutermost();
|
|
FString PackageName = OriginalPackage->GetName();
|
|
|
|
// Make sure our history is up to date
|
|
TSharedRef<FUpdateStatus, ESPMode::ThreadSafe> 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, NULL, &RelativeFileName))
|
|
{
|
|
if(SourceControlState->GetHistorySize() > 0)
|
|
{
|
|
TSharedPtr<ISourceControlRevision, ESPMode::ThreadSafe> 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(Revision->Get(TempFileName))
|
|
{
|
|
// Forcibly disable compile on load in case we are loading old blueprints that might try to update/compile
|
|
TGuardValue<bool> DisableCompileOnLoad(GForceDisableBlueprintCompileOnLoad, true);
|
|
|
|
// Try and load that package
|
|
FText NotMapReason;
|
|
UPackage* OldPackage = LoadPackage(NULL, *TempFileName, LOAD_None);
|
|
if(OldPackage != NULL && GEditor->PackageIsAMapFile(*TempFileName, NotMapReason))
|
|
{
|
|
/* Set the revision information*/
|
|
UPackage* Package = OriginalPackage;
|
|
|
|
FRevisionInfo OldRevision;
|
|
OldRevision.Changelist = Revision->GetCheckInIdentifier();
|
|
OldRevision.Date = Revision->GetDate();
|
|
OldRevision.Revision = Revision->GetRevisionNumber();
|
|
|
|
FRevisionInfo NewRevision;
|
|
NewRevision.Revision = -1;
|
|
|
|
// 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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FLevelCollectionViewModel::OnSCCConnect() const
|
|
{
|
|
ISourceControlModule::Get().ShowLoginDialog(FSourceControlLoginClosed(), ELoginWindowMode::Modeless);
|
|
}
|
|
|
|
void FLevelCollectionViewModel::MigrateSelectedLevels_Executed()
|
|
{
|
|
// Gather the package names for the levels
|
|
TArray<FName> PackageNames;
|
|
for( auto SelectedLevelIt = SelectedLevels.CreateIterator(); SelectedLevelIt; ++SelectedLevelIt )
|
|
{
|
|
if ( UObject* Level = (*SelectedLevelIt)->GetLevel().Get() )
|
|
{
|
|
PackageNames.Add( Level->GetOutermost()->GetFName() );
|
|
}
|
|
}
|
|
|
|
FAssetToolsModule& AssetToolsModule = FModuleManager::Get().LoadModuleChecked<FAssetToolsModule>("AssetTools");
|
|
AssetToolsModule.Get().MigratePackages( PackageNames );
|
|
}
|
|
|
|
void FLevelCollectionViewModel::CreateEmptyLevel_Executed()
|
|
{
|
|
EditorLevelUtils::CreateNewLevel( CurrentWorld.Get(), false, AddedLevelStreamingClass );
|
|
}
|
|
|
|
void FLevelCollectionViewModel::AddExistingLevel_Executed()
|
|
{
|
|
AddExistingLevel();
|
|
}
|
|
|
|
void FLevelCollectionViewModel::AddExistingLevel(bool bRemoveInvalidSelectedLevelsAfter)
|
|
{
|
|
if ( UEditorEngine::IsUsingWorldAssets() )
|
|
{
|
|
FEditorFileUtils::FOnLevelsChosen LevelsChosenDelegate = FEditorFileUtils::FOnLevelsChosen::CreateSP(this, &FLevelCollectionViewModel::HandleAddExistingLevelSelected, bRemoveInvalidSelectedLevelsAfter);
|
|
const bool bAllowMultipleSelection = true;
|
|
FEditorFileUtils::OpenLevelPickingDialog(LevelsChosenDelegate, bAllowMultipleSelection);
|
|
}
|
|
else
|
|
{
|
|
TArray<FString> OpenFilenames;
|
|
IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get();
|
|
bool bOpened = false;
|
|
if ( DesktopPlatform )
|
|
{
|
|
void* ParentWindowWindowHandle = NULL;
|
|
|
|
IMainFrameModule& MainFrameModule = FModuleManager::LoadModuleChecked<IMainFrameModule>(TEXT("MainFrame"));
|
|
const TSharedPtr<SWindow>& MainFrameParentWindow = MainFrameModule.GetParentWindow();
|
|
if ( MainFrameParentWindow.IsValid() && MainFrameParentWindow->GetNativeWindow().IsValid() )
|
|
{
|
|
ParentWindowWindowHandle = MainFrameParentWindow->GetNativeWindow()->GetOSWindowHandle();
|
|
}
|
|
|
|
bOpened = DesktopPlatform->OpenFileDialog(
|
|
ParentWindowWindowHandle,
|
|
NSLOCTEXT("UnrealEd", "Open", "Open").ToString(),
|
|
*FEditorDirectories::Get().GetLastDirectory(ELastDirectory::UNR),
|
|
TEXT(""),
|
|
*FEditorFileUtils::GetFilterString(FI_Load),
|
|
EFileDialogFlags::Multiple,
|
|
OpenFilenames
|
|
);
|
|
}
|
|
|
|
if( bOpened )
|
|
{
|
|
// Save the path as default for next time
|
|
FEditorDirectories::Get().SetLastDirectory( ELastDirectory::UNR, FPaths::GetPath( OpenFilenames[ 0 ] ) );
|
|
|
|
TArray<FString> Filenames;
|
|
for( int32 FileIndex = 0 ; FileIndex < OpenFilenames.Num() ; ++FileIndex )
|
|
{
|
|
// Strip paths from to get the level package names.
|
|
const FString FilePath( OpenFilenames[FileIndex] );
|
|
|
|
// make sure the level is in our package cache, because the async loading code will use this to find it
|
|
if (!FPaths::FileExists(FilePath))
|
|
{
|
|
FMessageDialog::Open( EAppMsgType::Ok, NSLOCTEXT("UnrealEd", "Error_LevelImportFromExternal", "Importing external sublevels is not allowed. Move the level files into the standard content directory and try again.\nAfter moving the level(s), restart the editor.") );
|
|
return;
|
|
}
|
|
|
|
FText ErrorMessage;
|
|
bool bFilenameIsValid = FEditorFileUtils::IsValidMapFilename(OpenFilenames[FileIndex], ErrorMessage);
|
|
if ( !bFilenameIsValid )
|
|
{
|
|
// Start the loop over, prompting for save again
|
|
const FText DisplayFilename = FText::FromString( IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*OpenFilenames[FileIndex]) );
|
|
FFormatNamedArguments Arguments;
|
|
Arguments.Add(TEXT("Filename"), DisplayFilename);
|
|
Arguments.Add(TEXT("LineTerminators"), FText::FromString(LINE_TERMINATOR LINE_TERMINATOR));
|
|
Arguments.Add(TEXT("ErrorMessage"), ErrorMessage);
|
|
const FText DisplayMessage = FText::Format( NSLOCTEXT("UnrealEd", "Error_InvalidLevelToAdd", "Unable to add streaming level {Filename}{LineTerminators}{ErrorMessage}"), Arguments );
|
|
FMessageDialog::Open( EAppMsgType::Ok, DisplayMessage );
|
|
return;
|
|
}
|
|
|
|
Filenames.Add( FilePath );
|
|
}
|
|
|
|
TArray<FString> PackageNames;
|
|
for ( const auto& Filename : Filenames )
|
|
{
|
|
const FString& PackageName = FPackageName::FilenameToLongPackageName(Filename);
|
|
PackageNames.Add(PackageName);
|
|
}
|
|
|
|
EditorLevelUtils::AddLevelsToWorld(CurrentWorld.Get(), PackageNames, AddedLevelStreamingClass);
|
|
|
|
if (bRemoveInvalidSelectedLevelsAfter)
|
|
{
|
|
RemoveInvalidSelectedLevels_Executed();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FLevelCollectionViewModel::HandleAddExistingLevelSelected(const TArray<FAssetData>& SelectedAssets, bool bRemoveInvalidSelectedLevelsAfter)
|
|
{
|
|
TArray<FString> PackageNames;
|
|
for (const auto& AssetData : SelectedAssets)
|
|
{
|
|
PackageNames.Add(AssetData.PackageName.ToString());
|
|
}
|
|
|
|
EditorLevelUtils::AddLevelsToWorld(CurrentWorld.Get(), PackageNames, AddedLevelStreamingClass);
|
|
|
|
if (bRemoveInvalidSelectedLevelsAfter)
|
|
{
|
|
RemoveInvalidSelectedLevels_Executed();
|
|
}
|
|
}
|
|
|
|
void FLevelCollectionViewModel::AddSelectedActorsToNewLevel_Executed()
|
|
{
|
|
EditorLevelUtils::CreateNewLevel( CurrentWorld.Get(), true, AddedLevelStreamingClass );
|
|
}
|
|
|
|
void FLevelCollectionViewModel::FixupInvalidReference_Executed()
|
|
{
|
|
// Browsing is essentially the same as adding an existing level
|
|
const bool bRemoveInvalidSelectedLevelsAfter = true;
|
|
AddExistingLevel(bRemoveInvalidSelectedLevelsAfter);
|
|
}
|
|
|
|
void FLevelCollectionViewModel::RemoveInvalidSelectedLevels_Executed()
|
|
{
|
|
for( auto InvalidLevelIt = InvalidSelectedLevels.CreateIterator(); InvalidLevelIt; ++InvalidLevelIt )
|
|
{
|
|
ULevelStreaming* LevelStreaming = (*InvalidLevelIt)->GetLevelStreaming().Get();
|
|
EditorLevelUtils::RemoveInvalidLevelFromWorld( LevelStreaming );
|
|
}
|
|
// Force a rebuild of the cached data
|
|
PopulateLevelsList();
|
|
}
|
|
|
|
void FLevelCollectionViewModel::RemoveSelectedLevels_Executed()
|
|
{
|
|
// If we have dirty levels that can be removed from the world
|
|
bool bHaveDirtyLevels = false;
|
|
// Gather levels to remove
|
|
TArray< ULevel* > LevelsToRemove;
|
|
for( auto LevelIt = SelectedLevels.CreateIterator(); LevelIt; ++LevelIt )
|
|
{
|
|
ULevel* CurLevel = (*LevelIt)->GetLevel().Get();
|
|
if( CurLevel != NULL )
|
|
{
|
|
if( CurLevel->GetOutermost()->IsDirty() && !FLevelUtils::IsLevelLocked( CurLevel ) )
|
|
{
|
|
// this level is dirty and can be removed from the world
|
|
bHaveDirtyLevels = true;
|
|
}
|
|
LevelsToRemove.Add( CurLevel );
|
|
}
|
|
}
|
|
|
|
// Depending on the state of the level, create a warning message
|
|
FText LevelWarning = LOCTEXT("RemoveLevel_Undo", "Removing levels cannot be undone. Proceed?");
|
|
if( bHaveDirtyLevels )
|
|
{
|
|
LevelWarning = LOCTEXT("RemoveLevel_Dirty", "Removing levels cannot be undone. Any changes to these levels will be lost. Proceed?");
|
|
}
|
|
|
|
// Ask the user if they really wish to remove the level(s)
|
|
FSuppressableWarningDialog::FSetupInfo Info( LevelWarning, LOCTEXT("RemoveLevel_Message", "Remove Level"), "RemoveLevelWarning" );
|
|
Info.ConfirmText = LOCTEXT( "RemoveLevel_Yes", "Yes");
|
|
Info.CancelText = LOCTEXT( "RemoveLevel_No", "No");
|
|
FSuppressableWarningDialog RemoveLevelWarning( Info );
|
|
if ( RemoveLevelWarning.ShowModal() == FSuppressableWarningDialog::Cancel )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// If matinee is opened, and if it belongs to the level being removed, close it
|
|
if( GLevelEditorModeTools().IsModeActive( FBuiltinEditorModes::EM_InterpEdit ) )
|
|
{
|
|
const FEdModeInterpEdit* InterpEditMode = (const FEdModeInterpEdit*)GLevelEditorModeTools().GetActiveMode( FBuiltinEditorModes::EM_InterpEdit );
|
|
|
|
if ( InterpEditMode && InterpEditMode->MatineeActor && LevelsToRemove.Contains( InterpEditMode->MatineeActor->GetLevel() ) )
|
|
{
|
|
GLevelEditorModeTools().ActivateDefaultMode();
|
|
}
|
|
}
|
|
else if( GLevelEditorModeTools().IsModeActive( FBuiltinEditorModes::EM_Landscape ) )
|
|
{
|
|
GLevelEditorModeTools().ActivateDefaultMode();
|
|
}
|
|
|
|
// Disassociate selected levels from streaming volumes since the levels will be removed
|
|
ClearStreamingLevelVolumes();
|
|
|
|
// Remove each level!
|
|
for( auto LevelIt = LevelsToRemove.CreateIterator(); LevelIt; ++LevelIt )
|
|
{
|
|
ULevel* CurLevel = (*LevelIt);
|
|
|
|
if( !FLevelUtils::IsLevelLocked(CurLevel) )
|
|
{
|
|
// If the level isn't locked (which means it can be removed) 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( int32 ActorIdx = 0; ActorIdx < CurLevel->Actors.Num(); ++ActorIdx )
|
|
{
|
|
GEditor->SelectActor( CurLevel->Actors[ ActorIdx ], /*bInSelected=*/ false, /*bSelectEvenIfHidden=*/ false );
|
|
}
|
|
}
|
|
|
|
EditorLevelUtils::RemoveLevelFromWorld( CurLevel );
|
|
}
|
|
|
|
// Collect garbage to clear out the destroyed level
|
|
CollectGarbage( GARBAGE_COLLECTION_KEEPFLAGS );
|
|
}
|
|
|
|
void FLevelCollectionViewModel::MergeSelectedLevels_Executed()
|
|
{
|
|
// Stash off a copy of the original array, so the selection can be restored
|
|
TArray< TSharedPtr< FLevelViewModel > > SelectedLevelsCopy;
|
|
SelectedLevelsCopy.Append( SelectedLevels );
|
|
|
|
//make sure the selected levels are made visible (and thus fully loaded) before merging
|
|
ShowSelectedLevels_Executed();
|
|
|
|
//restore the original selection and select all actors in the selected levels
|
|
SetSelectedLevels( SelectedLevelsCopy );
|
|
SelectActors_Executed();
|
|
|
|
//Create a new level with the selected actors
|
|
ULevel* NewLevel = EditorLevelUtils::CreateNewLevel( CurrentWorld.Get(), true, AddedLevelStreamingClass );
|
|
|
|
//If the new level was successfully created (ie the user did not cancel)
|
|
if( ( NewLevel != NULL ) && ( CurrentWorld.IsValid() ) )
|
|
{
|
|
if( CurrentWorld->SetCurrentLevel( NewLevel ) )
|
|
{
|
|
FEditorDelegates::NewCurrentLevel.Broadcast();
|
|
}
|
|
GEditor->NoteSelectionChange();
|
|
|
|
//restore the original selection and remove the levels that were merged
|
|
SetSelectedLevels( SelectedLevelsCopy );
|
|
RemoveSelectedLevels_Executed();
|
|
|
|
//select the resulting merged level for feedback
|
|
SetSelectedLevel( NewLevel->GetFName() );
|
|
}
|
|
}
|
|
|
|
void FLevelCollectionViewModel::SetAddedLevelStreamingClass_Executed(UClass* InClass)
|
|
{
|
|
AddedLevelStreamingClass = InClass;
|
|
}
|
|
|
|
bool FLevelCollectionViewModel::IsNewStreamingMethodChecked(UClass* InClass) const
|
|
{
|
|
return AddedLevelStreamingClass == InClass;
|
|
}
|
|
|
|
bool FLevelCollectionViewModel::IsStreamingMethodChecked(UClass* InClass) const
|
|
{
|
|
for (auto LevelIt = SelectedLevels.CreateConstIterator(); LevelIt; ++LevelIt )
|
|
{
|
|
const TSharedPtr<FLevelViewModel> Level = *LevelIt;
|
|
const TWeakObjectPtr< ULevelStreaming > LevelStreaming = Level->GetLevelStreaming();
|
|
if ( LevelStreaming.IsValid() && LevelStreaming->GetClass() == InClass )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void FLevelCollectionViewModel::SetStreamingLevelsClass_Executed(UClass* InClass)
|
|
{
|
|
// First prompt to save the selected levels, as changing the streaming method will unload/reload them
|
|
SaveSelectedLevels_Executed();
|
|
|
|
// Stash off a copy of the original array, as changing the streaming method can destroy the selection
|
|
TArray< TSharedPtr< FLevelViewModel > > SelectedLevelsCopy;
|
|
SelectedLevelsCopy.Append( SelectedLevels );
|
|
|
|
// Apply the new streaming method to the selected levels
|
|
for (auto LevelsToChangeIt = SelectedLevelsCopy.CreateIterator(); LevelsToChangeIt; ++LevelsToChangeIt )
|
|
{
|
|
(*LevelsToChangeIt)->SetStreamingClass(InClass);
|
|
}
|
|
|
|
SetSelectedLevels(SelectedLevelsCopy);
|
|
}
|
|
|
|
//level selection
|
|
void FLevelCollectionViewModel::SelectAllLevels_Executed()
|
|
{
|
|
SetSelectedLevels( FilteredLevelViewModels );
|
|
}
|
|
|
|
void FLevelCollectionViewModel::DeselectAllLevels_Executed()
|
|
{
|
|
TArray< TSharedPtr< FLevelViewModel > > NoLevels;
|
|
SetSelectedLevels( NoLevels );
|
|
}
|
|
|
|
void FLevelCollectionViewModel::InvertSelection_Executed()
|
|
{
|
|
TArray< TSharedPtr< FLevelViewModel > > InvertedLevels;
|
|
|
|
for( auto LevelIt = FilteredLevelViewModels.CreateIterator(); LevelIt; ++LevelIt )
|
|
{
|
|
// Only add the level if its valid
|
|
if( (*LevelIt)->IsValid() )
|
|
{
|
|
if ( !SelectedLevels.Contains( *LevelIt ) )
|
|
{
|
|
InvertedLevels.Add( *LevelIt );
|
|
}
|
|
}
|
|
}
|
|
|
|
SetSelectedLevels( InvertedLevels );
|
|
}
|
|
|
|
|
|
//actors
|
|
void FLevelCollectionViewModel::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 LevelIt = SelectedLevels.CreateIterator(); LevelIt; ++LevelIt )
|
|
{
|
|
if( (*LevelIt)->IsLevel() )
|
|
{
|
|
(*LevelIt)->SelectActors( /*bSelect*/ true, /*bNotify*/ true, /*bSelectEvenIfHidden*/ true );
|
|
}
|
|
}
|
|
}
|
|
|
|
void FLevelCollectionViewModel::DeselectActors_Executed()
|
|
{
|
|
const FScopedTransaction Transaction( LOCTEXT("DeselectActors", "Deselect Actors in Level") );
|
|
|
|
for( auto LevelIt = SelectedLevels.CreateIterator(); LevelIt; ++LevelIt )
|
|
{
|
|
if( (*LevelIt)->IsLevel() )
|
|
{
|
|
(*LevelIt)->SelectActors( /*bSelect*/ false, /*bNotify*/ true, /*bSelectEvenIfHidden*/ true );
|
|
}
|
|
}
|
|
}
|
|
|
|
//streaming volumes
|
|
void FLevelCollectionViewModel::SelectStreamingVolumes_Executed()
|
|
{
|
|
// Iterate over selected levels and make a list of volumes to select.
|
|
TArray<ALevelStreamingVolume*> LevelStreamingVolumesToSelect;
|
|
for( auto LevelIt = SelectedLevels.CreateIterator(); LevelIt; ++LevelIt )
|
|
{
|
|
// Only process the level if its valid.
|
|
if( (*LevelIt)->IsValid() )
|
|
{
|
|
ULevelStreaming* StreamingLevel = FLevelUtils::FindStreamingLevel( (*LevelIt)->GetLevel().Get() );
|
|
if ( StreamingLevel )
|
|
{
|
|
for ( int32 i = 0 ; i < StreamingLevel->EditorStreamingVolumes.Num() ; ++i )
|
|
{
|
|
ALevelStreamingVolume* LevelStreamingVolume = StreamingLevel->EditorStreamingVolumes[i];
|
|
if ( LevelStreamingVolume )
|
|
{
|
|
LevelStreamingVolumesToSelect.Add( LevelStreamingVolume );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Select the volumes.
|
|
const FScopedTransaction Transaction( LOCTEXT("SelectAssociatedStreamingVolumes", "Select Associated Streaming Volumes") );
|
|
GEditor->GetSelectedActors()->Modify();
|
|
GEditor->SelectNone( false, true );
|
|
|
|
for ( int32 i = 0 ; i < LevelStreamingVolumesToSelect.Num() ; ++i )
|
|
{
|
|
ALevelStreamingVolume* LevelStreamingVolume = LevelStreamingVolumesToSelect[i];
|
|
GEditor->SelectActor( LevelStreamingVolume, /*bInSelected=*/ true, false, true );
|
|
}
|
|
|
|
|
|
GEditor->NoteSelectionChange();
|
|
}
|
|
|
|
void FLevelCollectionViewModel::AssembleSelectedLevelStreamingVolumes(TArray<ALevelStreamingVolume*>& OutLevelStreamingVolumes)
|
|
{
|
|
OutLevelStreamingVolumes.Empty();
|
|
for ( FSelectionIterator It( GEditor->GetSelectedActorIterator() ) ; It ; ++It )
|
|
{
|
|
ALevelStreamingVolume* StreamingVolume = Cast<ALevelStreamingVolume>( *It );
|
|
if ( StreamingVolume && StreamingVolume->GetLevel() == GWorld->PersistentLevel )
|
|
{
|
|
OutLevelStreamingVolumes.Add( StreamingVolume );
|
|
}
|
|
}
|
|
}
|
|
|
|
void FLevelCollectionViewModel::SetStreamingLevelVolumes_Executed()
|
|
{
|
|
ClearStreamingLevelVolumes();
|
|
AddStreamingLevelVolumes();
|
|
}
|
|
|
|
void FLevelCollectionViewModel::AddStreamingLevelVolumes()
|
|
{
|
|
TArray<ALevelStreamingVolume*> LevelStreamingVolumes;
|
|
AssembleSelectedLevelStreamingVolumes(LevelStreamingVolumes);
|
|
|
|
if(LevelStreamingVolumes.Num() == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const FScopedTransaction Transaction( NSLOCTEXT("UnrealEd", "AddStreamingVolumes", "Add Streaming Volumes") );
|
|
|
|
for( auto LevelIt = SelectedLevels.CreateIterator(); LevelIt; ++LevelIt )
|
|
{
|
|
if( (*LevelIt)->IsLevel() )
|
|
{
|
|
ULevelStreaming* StreamingLevel = FLevelUtils::FindStreamingLevel( (*LevelIt)->GetLevel().Get() );
|
|
if ( StreamingLevel )
|
|
{
|
|
StreamingLevel->Modify();
|
|
for ( int32 i = 0 ; i < LevelStreamingVolumes.Num() ; ++i )
|
|
{
|
|
ALevelStreamingVolume* LevelStreamingVolume = LevelStreamingVolumes[i];
|
|
|
|
// Associate the level to the volume.
|
|
LevelStreamingVolume->Modify();
|
|
LevelStreamingVolume->StreamingLevelNames.AddUnique( StreamingLevel->GetWorldAssetPackageFName() );
|
|
|
|
// Associate the volume to the level.
|
|
StreamingLevel->EditorStreamingVolumes.AddUnique( LevelStreamingVolume );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FLevelCollectionViewModel::AddStreamingLevelVolumes_Executed()
|
|
{
|
|
AddStreamingLevelVolumes();
|
|
}
|
|
|
|
void FLevelCollectionViewModel::ClearStreamingVolumes_Executed()
|
|
{
|
|
int32 Result = FMessageDialog::Open( EAppMsgType::YesNo, LOCTEXT("ClearStreamingVolumesPrompt", "Are you sure you want to clear streaming volumes?") );
|
|
|
|
// If the user wants to continue with the restart set the pending project to swtich to and close the editor
|
|
if( Result == EAppReturnType::Yes )
|
|
{
|
|
ClearStreamingLevelVolumes();
|
|
}
|
|
}
|
|
|
|
|
|
//visibility
|
|
void FLevelCollectionViewModel::SetVisible(TArray< TSharedPtr< FLevelViewModel > >& LevelViewModels, bool bVisible )
|
|
{
|
|
TArray<TSharedPtr<FLevelViewModel>> CachedLevelViewModels = LevelViewModels;
|
|
|
|
for (const TSharedPtr<FLevelViewModel>& Level : CachedLevelViewModels)
|
|
{
|
|
// Only set visibility if the level its valid.
|
|
if (Level->IsValid())
|
|
{
|
|
Level->SetVisible(bVisible);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FLevelCollectionViewModel::ShowSelectedLevels_Executed()
|
|
{
|
|
//stash off a copy of the original array, as setting visibility can destroy the selection
|
|
TArray< TSharedPtr< FLevelViewModel > > SelectedLevelsCopy;
|
|
SelectedLevelsCopy.Append( SelectedLevels );
|
|
|
|
SetVisible( SelectedLevelsCopy, true );
|
|
|
|
//restore the selection
|
|
SetSelectedLevels( SelectedLevelsCopy );
|
|
}
|
|
|
|
void FLevelCollectionViewModel::HideSelectedLevels_Executed()
|
|
{
|
|
//stash off a copy of the original array, as setting visibility can destroy the selection
|
|
TArray< TSharedPtr< FLevelViewModel > > SelectedLevelsCopy;
|
|
SelectedLevelsCopy.Append( SelectedLevels );
|
|
|
|
SetVisible( SelectedLevelsCopy, false );
|
|
}
|
|
|
|
void FLevelCollectionViewModel::ShowOnlySelectedLevels_Executed()
|
|
{
|
|
//stash off a copy of the original array, as setting visibility can destroy the selection
|
|
TArray< TSharedPtr< FLevelViewModel > > SelectedLevelsCopy;
|
|
SelectedLevelsCopy.Append( SelectedLevels );
|
|
|
|
InvertSelection_Executed();
|
|
HideSelectedLevels_Executed();
|
|
SetSelectedLevels( SelectedLevelsCopy );
|
|
ShowSelectedLevels_Executed();
|
|
|
|
//restore the selection
|
|
SetSelectedLevels( SelectedLevelsCopy );
|
|
}
|
|
|
|
void FLevelCollectionViewModel::ShowAllLevels_Executed()
|
|
{
|
|
SetVisible( AllLevelViewModels, true );
|
|
}
|
|
|
|
void FLevelCollectionViewModel::HideAllLevels_Executed()
|
|
{
|
|
SetVisible( AllLevelViewModels, false );
|
|
}
|
|
|
|
|
|
//lock
|
|
void FLevelCollectionViewModel::LockSelectedLevels_Executed()
|
|
{
|
|
const FText UndoTransactionText = (SelectedLevels.Num() == 1) ?
|
|
LOCTEXT("LockLevel", "Lock Level") :
|
|
LOCTEXT("LockMultipleLevels", "Lock Multiple Levels");
|
|
|
|
const FScopedTransaction Transaction( UndoTransactionText );
|
|
|
|
for( auto LevelIt = SelectedLevels.CreateIterator(); LevelIt; ++LevelIt )
|
|
{
|
|
// Only lock the level if its valid.
|
|
if( (*LevelIt)->IsValid() )
|
|
{
|
|
(*LevelIt)->SetLocked( true );
|
|
}
|
|
}
|
|
}
|
|
|
|
void FLevelCollectionViewModel::UnockSelectedLevels_Executed()
|
|
{
|
|
const FText UndoTransactionText = (SelectedLevels.Num() == 1) ?
|
|
LOCTEXT("UnlockLevel", "Unlock Level") :
|
|
LOCTEXT("UnlockMultipleLevels", "Unlock Multiple Levels");
|
|
|
|
const FScopedTransaction Transaction( UndoTransactionText );
|
|
|
|
for( auto LevelIt = SelectedLevels.CreateIterator(); LevelIt; ++LevelIt )
|
|
{
|
|
// Only unlock the level if its valid.
|
|
if( (*LevelIt)->IsValid() )
|
|
{
|
|
(*LevelIt)->SetLocked( false );
|
|
}
|
|
}
|
|
}
|
|
|
|
void FLevelCollectionViewModel::LockAllLevels_Executed()
|
|
{
|
|
const FScopedTransaction Transaction( LOCTEXT("LockAllLevels", "Lock All Levels") );
|
|
|
|
for( auto LevelIt = FilteredLevelViewModels.CreateIterator(); LevelIt; ++LevelIt )
|
|
{
|
|
// Only lock the level if its valid.
|
|
if( (*LevelIt)->IsValid() )
|
|
{
|
|
(*LevelIt)->SetLocked( true );
|
|
}
|
|
}
|
|
}
|
|
|
|
void FLevelCollectionViewModel::UnockAllLevels_Executed()
|
|
{
|
|
const FScopedTransaction Transaction( LOCTEXT("UnlockAllLevels", "Unlock All Levels") );
|
|
|
|
for( auto LevelIt = FilteredLevelViewModels.CreateIterator(); LevelIt; ++LevelIt )
|
|
{
|
|
// Only unlock the level if its valid.
|
|
if( (*LevelIt)->IsValid() )
|
|
{
|
|
(*LevelIt)->SetLocked( false );
|
|
}
|
|
}
|
|
}
|
|
|
|
void FLevelCollectionViewModel::WorldAdded( UWorld* World )
|
|
{
|
|
if( World )
|
|
{
|
|
// If the current world is not valid set this world to be the current one
|
|
if( !CurrentWorld.IsValid() )
|
|
{
|
|
SetCurrentWorld( World );
|
|
}
|
|
}
|
|
}
|
|
|
|
void FLevelCollectionViewModel::WorldDestroyed( UWorld* InWorld )
|
|
{
|
|
if( InWorld )
|
|
{
|
|
// Remove the level change handlers
|
|
RemoveLevelChangeHandlers( InWorld );
|
|
|
|
// If this world is the one we are showing levels for remove the reference
|
|
if( InWorld == CurrentWorld.Get() )
|
|
{
|
|
CurrentWorld = NULL;
|
|
|
|
if (InWorld != GEditor->GetEditorWorldContext().World())
|
|
{
|
|
SetCurrentWorld( GEditor->GetEditorWorldContext().World() );
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void FLevelCollectionViewModel::UpdateLevelActorsCount()
|
|
{
|
|
for( auto It = AllLevelViewModels.CreateIterator(); It; ++It )
|
|
{
|
|
(*It)->UpdateLevelActorsCount();
|
|
}
|
|
|
|
bPendingUpdateActorsCount = false;
|
|
}
|
|
|
|
void FLevelCollectionViewModel::OnLevelActorAdded( AActor* InActor )
|
|
{
|
|
if (InActor &&
|
|
InActor->GetWorld() == CurrentWorld.Get()) // we care about our world only
|
|
{
|
|
bPendingUpdateActorsCount = true;
|
|
}
|
|
}
|
|
|
|
void FLevelCollectionViewModel::OnLevelActorDeleted( AActor* InActor )
|
|
{
|
|
bPendingUpdateActorsCount = true;
|
|
}
|
|
|
|
void FLevelCollectionViewModel::AddLevelChangeHandlers( UWorld* InWorld )
|
|
{
|
|
if( ensure( InWorld ) )
|
|
{
|
|
InWorld->OnLevelsChanged().AddSP( this, &FLevelCollectionViewModel::Refresh );
|
|
InWorld->OnSelectedLevelsChanged().AddSP( this, &FLevelCollectionViewModel::RefreshSelected );
|
|
}
|
|
}
|
|
|
|
void FLevelCollectionViewModel::RemoveLevelChangeHandlers( UWorld* InWorld )
|
|
{
|
|
if( ensure( InWorld ) )
|
|
{
|
|
InWorld->OnLevelsChanged().RemoveAll( this );
|
|
InWorld->OnSelectedLevelsChanged().RemoveAll( this );
|
|
}
|
|
}
|
|
|
|
void FLevelCollectionViewModel::SetCurrentWorld( UWorld* InWorld )
|
|
{
|
|
if( InWorld == CurrentWorld.Get() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( CurrentWorld.IsValid() )
|
|
{
|
|
RemoveLevelChangeHandlers( CurrentWorld.Get() );
|
|
}
|
|
CurrentWorld = InWorld;
|
|
AddLevelChangeHandlers( InWorld );
|
|
Refresh();
|
|
}
|
|
|
|
void FLevelCollectionViewModel::CacheCanExecuteSourceControlVars()
|
|
{
|
|
bCanExecuteSCCCheckOut = false;
|
|
bCanExecuteSCCOpenForAdd = false;
|
|
bCanExecuteSCCCheckIn = false;
|
|
bCanExecuteSCC = false;
|
|
|
|
ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider();
|
|
for( auto SelectedLevelIt = SelectedLevels.CreateIterator(); SelectedLevelIt; ++SelectedLevelIt )
|
|
{
|
|
if ( ISourceControlModule::Get().IsEnabled() && SourceControlProvider.IsAvailable() )
|
|
{
|
|
bCanExecuteSCC = true;
|
|
|
|
// Check the SCC state for each package in the selected paths
|
|
FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState((*SelectedLevelIt)->GetLevel().Get()->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 FLevelCollectionViewModel::IsValidMoveActorsToLevel()
|
|
{
|
|
static bool bCachedIsValidActorMoveResult = false;
|
|
if (bSelectionHasChanged)
|
|
{
|
|
bSelectionHasChanged = false;
|
|
bCachedIsValidActorMoveResult = false;
|
|
|
|
// We only operate on a single level
|
|
if ( SelectedLevels.Num() == 1 )
|
|
{
|
|
ULevel* Level = SelectedLevels[0]->GetLevel().Get();
|
|
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 && AreAllSelectedLevelsUnlockedAndVisible();
|
|
}
|
|
|
|
void FLevelCollectionViewModel::OnActorSelectionChanged(UObject* obj)
|
|
{
|
|
OnActorOrLevelSelectionChanged();
|
|
}
|
|
|
|
void FLevelCollectionViewModel::OnActorOrLevelSelectionChanged()
|
|
{
|
|
bSelectionHasChanged = true;
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|