Files
UnrealEngineUWP/Engine/Source/Editor/SourceControlWindows/Private/SSourceControlRevert.cpp
Barnabas McManners 5c9c346ce7 Updated the revert window to use Revert Unchanged Only correctly.
#TTP 334818 - Editor: Source Control: Revert files dialog checkboxes not behaving properly
#branch UE4

#proj Editor.SourceControlWindows

#change Changed the behaviour of the revert window as follows:
Previously:
Clicking revert unchanged only attempted to disable the changed files, all disabling did at this point was to make it seem as if the item was not selected even if it was. This meant that this check box did not fulfil its actual description

Now:
Checking revert unchanged only disables all of the file items as well as the checkbox to enable or disable all of the file items. It then enables the revert button and if pressed it will only send the unchanged files for reversion, this means it fulfills the buttons description.

#change Misc changes: Updated the buttons to use the standardized button layout, and changed "revert files" button to "revert" as this lead to a nicer layout. Added icons to show which of the selected files have been modified.

#reviewedby Thomas.Sarkanen

[CL 2093142 by Barnabas McManners in Main branch]
2014-06-03 10:41:50 -04:00

446 lines
13 KiB
C++

// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
#include "SourceControlWindowsPCH.h"
#include "AssetToolsModule.h"
#include "PackageTools.h"
#define LOCTEXT_NAMESPACE "SSourceControlRevert"
//-------------------------------------
//Source Control Window Constants
//-------------------------------------
namespace ERevertResults
{
enum Type
{
REVERT_ACCEPTED,
REVERT_CANCELED
};
}
struct FRevertCheckBoxListViewItem
{
/**
* Constructor
*
* @param InText String that should appear for the item in the list view
*/
FRevertCheckBoxListViewItem( FString InText )
{
Text = InText;
IsSelected = false;
IsModified = false;
}
void OnCheckStateChanged( const ESlateCheckBoxState::Type NewCheckedState )
{
IsSelected = (NewCheckedState == ESlateCheckBoxState::Checked);
}
ESlateCheckBoxState::Type OnIsChecked() const
{
return ( IsSelected ) ? ESlateCheckBoxState::Checked : ESlateCheckBoxState::Unchecked;
}
EVisibility OnGetModifiedStateVisibility() const
{
return (IsModified) ? EVisibility::Visible : EVisibility::Hidden;
}
bool IsSelected;
bool IsModified;
FString Text;
};
/**
* Source control panel for reverting files. Allows the user to select which files should be reverted, as well as
* provides the option to only allow unmodified files to be reverted.
*/
class SSourceControlRevertWidget : public SCompoundWidget
{
public:
//* @param InXamlName Name of the XAML file defining this panel
//* @param InPackageNames Names of the packages to be potentially reverted
SLATE_BEGIN_ARGS( SSourceControlRevertWidget )
: _ParentWindow()
, _CheckedOutPackages()
{}
SLATE_ATTRIBUTE( TSharedPtr<SWindow>, ParentWindow )
SLATE_ATTRIBUTE( TArray<FString>, CheckedOutPackages )
SLATE_END_ARGS()
/**
* Constructor.
*/
SSourceControlRevertWidget()
{
}
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void Construct( const FArguments& InArgs )
{
ParentFrame = InArgs._ParentWindow.Get();
for ( TArray<FString>::TConstIterator PackageIter( InArgs._CheckedOutPackages.Get() ); PackageIter; ++PackageIter )
{
ListViewItemSource.Add( MakeShareable(new FRevertCheckBoxListViewItem(*PackageIter) ));
}
ChildSlot
[
SNew(SBorder)
.BorderImage(FEditorStyle::GetBrush("ToolPanel.GroupBorder"))
[
SNew(SVerticalBox)
+SVerticalBox::Slot()
.AutoHeight()
.Padding(10)
[
SNew(STextBlock)
.Text(NSLOCTEXT("SourceControl.Revert", "SelectFiles", "Select the files that should be reverted below").ToString())
]
+SVerticalBox::Slot()
.AutoHeight()
.Padding(10,0)
[
SNew(SBorder)
.BorderImage(FEditorStyle::GetBrush("ToolPanel.GroupBorder"))
.Padding(5)
[
SNew(SCheckBox)
.OnCheckStateChanged(this, &SSourceControlRevertWidget::ColumnHeaderClicked)
.IsEnabled(this, &SSourceControlRevertWidget::OnGetItemsEnabled)
[
SNew(STextBlock)
.Text(NSLOCTEXT("SourceControl.Revert", "ListHeader", "File Name").ToString())
]
]
]
+SVerticalBox::Slot()
.AutoHeight()
.Padding(10,0)
.MaxHeight(300)
[
SNew(SBorder)
.Padding(5)
[
SAssignNew(RevertListView, SListViewType)
.ItemHeight(24)
.ListItemsSource(&ListViewItemSource)
.OnGenerateRow(this, &SSourceControlRevertWidget::OnGenerateRowForList)
]
]
+SVerticalBox::Slot()
.Padding(0, 10, 0, 0)
.FillHeight(1)
.VAlign(VAlign_Bottom)
.HAlign(HAlign_Fill)
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.AutoWidth()
.Padding(15,5)
.HAlign(HAlign_Left)
[
SNew(SCheckBox)
.OnCheckStateChanged(this, &SSourceControlRevertWidget::RevertUnchangedToggled)
[
SNew(STextBlock)
.Text(NSLOCTEXT("SourceControl.Revert", "RevertUnchanged", "Revert Unchanged Only").ToString())
]
]
+SHorizontalBox::Slot()
.HAlign(HAlign_Right)
.FillWidth(1)
.Padding(5)
[
SNew(SUniformGridPanel)
.SlotPadding(FEditorStyle::GetMargin("StandardDialog.SlotPadding"))
.MinDesiredSlotWidth(FEditorStyle::GetFloat("StandardDialog.MinDesiredSlotWidth"))
.MinDesiredSlotHeight(FEditorStyle::GetFloat("StandardDialog.MinDesiredSlotHeight"))
+SUniformGridPanel::Slot(0,0)
[
SNew(SButton)
.HAlign(HAlign_Center)
.ContentPadding(FEditorStyle::GetMargin("StandardDialog.ContentPadding"))
.OnClicked(this, &SSourceControlRevertWidget::OKClicked)
.IsEnabled(this, &SSourceControlRevertWidget::IsOKEnabled)
.Text(LOCTEXT("RevertButton", "Revert"))
]
+SUniformGridPanel::Slot(1,0)
[
SNew(SButton)
.HAlign(HAlign_Center)
.ContentPadding(FEditorStyle::GetMargin("StandardDialog.ContentPadding"))
.OnClicked(this, &SSourceControlRevertWidget::CancelClicked)
.Text(LOCTEXT("CancelButton", "Cancel"))
]
]
]
]
];
// update the modified state of all the files.
UpdateSCCStatus();
DialogResult = ERevertResults::REVERT_CANCELED;
bRevertUnchangedFilesOnly = false;
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
/**
* Populates the provided array with the names of the packages the user elected to revert, if any.
*
* @param OutPackagesToRevert Array of package names to revert, as specified by the user in the dialog
*/
void GetPackagesToRevert( TArray<FString>& OutPackagesToRevert )
{
for ( const auto& ListViewItem : ListViewItemSource )
{
if ((bRevertUnchangedFilesOnly && !ListViewItem->IsModified) ||
(!bRevertUnchangedFilesOnly && ListViewItem->IsSelected))
{
OutPackagesToRevert.Add(ListViewItem->Text);
}
}
}
ERevertResults::Type GetResult()
{
return DialogResult;
}
private:
TSharedRef<ITableRow> OnGenerateRowForList( TSharedPtr<FRevertCheckBoxListViewItem> ListItemPtr, const TSharedRef<STableViewBase>& OwnerTable )
{
TSharedPtr<SCheckBox> CheckBox;
TSharedRef<ITableRow> Row =
SNew(STableRow< TSharedPtr<FString> >, OwnerTable)
.IsEnabled(this, &SSourceControlRevertWidget::OnGetItemsEnabled)
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.HAlign(HAlign_Left)
.AutoWidth()
[
SAssignNew(CheckBox, SCheckBox)
.OnCheckStateChanged(ListItemPtr.ToSharedRef(), &FRevertCheckBoxListViewItem::OnCheckStateChanged)
.IsChecked(ListItemPtr.ToSharedRef(), &FRevertCheckBoxListViewItem::OnIsChecked)
[
SNew(STextBlock)
.Text(ListItemPtr->Text)
]
]
+SHorizontalBox::Slot()
.HAlign(HAlign_Right)
[
SNew(SImage)
.Image(FEditorStyle::GetBrush(TEXT("ContentBrowser.ContentDirty")))
.Visibility(ListItemPtr.ToSharedRef(), &FRevertCheckBoxListViewItem::OnGetModifiedStateVisibility)
.ToolTipText(LOCTEXT("ModifiedFileToolTip","This file has been modified from the source version"))
]
];
return Row;
}
/** Called when the settings of the dialog are to be accepted*/
FReply OKClicked()
{
DialogResult = ERevertResults::REVERT_ACCEPTED;
ParentFrame.Pin()->RequestDestroyWindow();
return FReply::Handled();
}
bool IsOKEnabled() const
{
if (bRevertUnchangedFilesOnly)
{
return true;
}
for (int32 i=0; i<ListViewItemSource.Num(); i++)
{
if (ListViewItemSource[i]->IsSelected)
{
return true;
}
}
return false;
}
/** Called when the settings of the dialog are to be ignored*/
FReply CancelClicked()
{
DialogResult = ERevertResults::REVERT_CANCELED;
ParentFrame.Pin()->RequestDestroyWindow();
return FReply::Handled();
}
/** Called when the user checks or unchecks the revert unchanged checkbox; updates the list view accordingly */
void RevertUnchangedToggled( const ESlateCheckBoxState::Type NewCheckedState )
{
bRevertUnchangedFilesOnly = (NewCheckedState == ESlateCheckBoxState::Checked);
}
/**
* Called whenever a column header is clicked, or in the case of the dialog, also when the "Check/Uncheck All" column header
* checkbox is called, because its event bubbles to the column header.
*/
void ColumnHeaderClicked( const ESlateCheckBoxState::Type NewCheckedState )
{
for (int32 i=0; i<ListViewItemSource.Num(); i++)
{
TSharedPtr<FRevertCheckBoxListViewItem> CurListViewItem = ListViewItemSource[i];
if (OnGetItemsEnabled())
{
CurListViewItem->IsSelected = (NewCheckedState == ESlateCheckBoxState::Checked);
}
}
}
/** Caches the current state of the files, */
void UpdateSCCStatus()
{
TArray<FString> PackagesToCheck;
for ( const auto& CurItem : ListViewItemSource )
{
PackagesToCheck.Add(SourceControlHelpers::PackageFilename(CurItem->Text));
}
// Make sure we update the modified state of the files
TSharedRef<FUpdateStatus, ESPMode::ThreadSafe> UpdateStatusOperation = ISourceControlOperation::Create<FUpdateStatus>();
UpdateStatusOperation->SetUpdateModifiedState(true);
ISourceControlModule::Get().GetProvider().Execute(UpdateStatusOperation, PackagesToCheck);
// Find the files modified from the server version
TArray< FSourceControlStateRef > SourceControlStates;
ISourceControlModule::Get().GetProvider().GetState( PackagesToCheck, SourceControlStates, EStateCacheUsage::Use );
ModifiedPackages.Empty();
for( const auto& ControlState : SourceControlStates )
{
FString PackageName;
FPackageName::TryConvertFilenameToLongPackageName(ControlState->GetFilename(), PackageName);
for ( const auto& CurItem : ListViewItemSource )
{
if (CurItem->Text == PackageName)
{
CurItem->IsModified = ControlState->IsModified();
}
}
}
}
/** Check for whether the list items are enabled or not */
bool OnGetItemsEnabled() const
{
return !bRevertUnchangedFilesOnly;
}
TWeakPtr<SWindow> ParentFrame;
ERevertResults::Type DialogResult;
/** ListView for the packages the user can revert */
typedef SListView<TSharedPtr<FRevertCheckBoxListViewItem>> SListViewType;
TSharedPtr<SListViewType> RevertListView;
/** Collection of items serving as the data source for the list view */
TArray<TSharedPtr<FRevertCheckBoxListViewItem>> ListViewItemSource;
/** List of package names that are modified from the versions stored in source control; Used as an optimization */
TArray<FString> ModifiedPackages;
/** Flag set by the user to only revert non modified files */
bool bRevertUnchangedFilesOnly;
};
bool FSourceControlWindows::PromptForRevert( const TArray<FString>& InPackageNames )
{
bool bReverted = false;
ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider();
// Only add packages that are actually already checked out to the prompt
TArray<FString> CheckedOutPackages;
for ( TArray<FString>::TConstIterator PackageIter( InPackageNames ); PackageIter; ++PackageIter )
{
FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(SourceControlHelpers::PackageFilename(*PackageIter), EStateCacheUsage::Use);
if( SourceControlState.IsValid() && (SourceControlState->IsCheckedOut() || SourceControlState->IsAdded()) )
{
CheckedOutPackages.Add( *PackageIter );
}
}
// If any of the packages are checked out, provide the revert prompt
if ( CheckedOutPackages.Num() > 0 )
{
TSharedRef<SWindow> NewWindow = SNew(SWindow)
.Title( NSLOCTEXT("SourceControl.RevertWindow", "Title", "Revert Files") )
.SizingRule( ESizingRule::Autosized )
.SupportsMinimize(false)
.SupportsMaximize(false);
TSharedRef<SSourceControlRevertWidget> SourceControlWidget =
SNew(SSourceControlRevertWidget)
.ParentWindow(NewWindow)
.CheckedOutPackages(CheckedOutPackages);
NewWindow->SetContent(SourceControlWidget);
FSlateApplication::Get().AddModalWindow(NewWindow, NULL);
// If the user decided to revert some packages, go ahead and do revert the ones they selected
if ( SourceControlWidget->GetResult() == ERevertResults::REVERT_ACCEPTED )
{
TArray<FString> PackagesToRevert;
SourceControlWidget->GetPackagesToRevert( PackagesToRevert );
check( PackagesToRevert.Num() > 0 );
// attempt to unload the packages we are about to revert
TArray<UPackage*> PackagesToUnload;
for ( TArray<FString>::TConstIterator PackageIter( InPackageNames ); PackageIter; ++PackageIter )
{
UPackage* Package = FindPackage(NULL, **PackageIter);
if(Package != NULL)
{
PackagesToUnload.Add(Package);
}
}
if(PackagesToUnload.Num() == 0 || PackageTools::UnloadPackages(PackagesToUnload))
{
for( auto PackageIter( PackagesToUnload.CreateIterator() ); PackageIter; ++PackageIter )
{
(*PackageIter)->ClearFlags(RF_WasLoaded);
}
SourceControlProvider.Execute( ISourceControlOperation::Create<FRevert>(), SourceControlHelpers::PackageFilenames(PackagesToRevert) );
}
else
{
FMessageDialog::Open( EAppMsgType::Ok, LOCTEXT("SCC_Revert_PackageUnloadFailed", "Could not unload assets when reverting files") );
}
bReverted = true;
}
}
return bReverted;
}
#undef LOCTEXT_NAMESPACE