Commit 1c847220 authored by jang dong hyeok's avatar jang dong hyeok
Browse files

.

parent 076f0c68
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using Unity.Cloud.Collaborate.Models.Structures;
using Unity.Cloud.Collaborate.Presenters;
namespace Unity.Cloud.Collaborate.Views
{
internal interface IHistoryView : IView<IHistoryPresenter>
{
/// <summary>
/// Set busy status in the view.
/// </summary>
/// <param name="busy">Whether or not the presenter is busy with a request.</param>
void SetBusyStatus(bool busy);
/// <summary>
/// Set the history list to be displayed. If the history list is not the current display, the view should switch
/// to it upon receiving this call.
/// </summary>
/// <param name="list">List of entries to display.</param>
void SetHistoryList(IReadOnlyList<IHistoryEntry> list);
/// <summary>
/// Set the current page and max page so that the paginator can be populated.
/// </summary>
/// <param name="page">Current page.</param>
/// <param name="max">Max page.</param>
void SetPage(int page, int max);
/// <summary>
/// Set the single history entry to display the provided entry. If the history single entry view is not the
/// current display, the view should switch to it upon receiving this call.
/// </summary>
/// <param name="entry">Entry to display.</param>
void SetSelection([NotNull] IHistoryEntry entry);
/// <summary>
/// Display a dialogue to the user.
/// </summary>
/// <param name="title">Title for the dialogue.</param>
/// <param name="message">Message inside the dialogue.</param>
/// <param name="affirmative">Affirmative button text.</param>
/// <returns>True if affirmative is clicked.</returns>
bool DisplayDialogue([NotNull] string title, [NotNull] string message, [NotNull] string affirmative);
/// <summary>
/// Display a dialogue to the user.
/// </summary>
/// <param name="title">Title for the dialogue.</param>
/// <param name="message">Message inside the dialogue.</param>
/// <param name="affirmative">Affirmative button text.</param>
/// <param name="negative">Negative button text.</param>
/// <returns>True if affirmative is clicked.</returns>
bool DisplayDialogue([NotNull] string title, [NotNull] string message, [NotNull] string affirmative, [NotNull] string negative);
}
}
using System;
using JetBrains.Annotations;
using Unity.Cloud.Collaborate.Components;
using Unity.Cloud.Collaborate.Presenters;
namespace Unity.Cloud.Collaborate.Views
{
internal interface IMainView : IView<IMainPresenter>
{
/// <summary>
/// Add or update an alert with the provided id.
/// </summary>
/// <param name="id">Id of the alert to add or update.</param>
/// <param name="level">Level of severity.</param>
/// <param name="message">Message for the alert.</param>
/// <param name="button">Optional button with text and a callback.</param>
void AddAlert([NotNull] string id, AlertBox.AlertLevel level, [NotNull] string message, (string text, Action action)? button = null);
/// <summary>
/// Remove alert with the provided id.
/// </summary>
/// <param name="id">Id of the alert to remove.</param>
void RemoveAlert([NotNull] string id);
/// <summary>
/// Switch to the given tab index.
/// </summary>
/// <param name="index">Index of tab to switch to.</param>
void SetTab(int index);
/// <summary>
/// Display progress view. Should only be called once, so only call when progress starts.
/// </summary>
void AddOperationProgress();
/// <summary>
/// Hide progress view. Should only be called once, so only call when progress finishes.
/// </summary>
void RemoveOperationProgress();
/// <summary>
/// Update the progress value displayed in the progress view.
/// </summary>
/// <param name="title">Title of the job in progress.</param>
/// <param name="details">Description/details/status of the job in progress.</param>
/// <param name="percentage">Current percentage completion of the job in progress. Used for percentage display.</param>
/// <param name="completed">Current number of job items completed. Used for discrete display.</param>
/// <param name="total">Total number of job items completed. Used for discrete display.</param>
/// <param name="isPercentage">True if the progress bar uses percentage, false if its discrete completed-of-total.</param>
/// <param name="canCancel">True if the job in progress can be cancelled.</param>
void SetOperationProgress(string title, string details, int percentage, int completed, int total, bool isPercentage, bool canCancel);
/// <summary>
/// Clear the current back navigation.
/// </summary>
void ClearBackNavigation();
/// <summary>
/// Set back navigation to be displayed with the provided text.
/// </summary>
/// <param name="text">Destination of the back navigation</param>
void DisplayBackNavigation([NotNull] string text);
}
}
using Unity.Cloud.Collaborate.Presenters;
namespace Unity.Cloud.Collaborate.Views
{
internal interface IStartView : IView<IStartPresenter>
{
/// <summary>
/// Set the text for the view.
/// </summary>
string Text { set; }
/// <summary>
/// Set the text for the button in the view.
/// </summary>
string ButtonText { set; }
/// <summary>
/// Set the visibility of the button.
/// </summary>
/// <param name="isVisible">True if the button should be visible.</param>
void SetButtonVisible(bool isVisible);
}
}
using System;
using JetBrains.Annotations;
using Unity.Cloud.Collaborate.Presenters;
namespace Unity.Cloud.Collaborate.Views
{
/// <summary>
/// Interface for all views in the UI.
/// </summary>
/// <typeparam name="T">Type of presenter this view takes.</typeparam>
interface IView<in T> where T : IPresenter
{
/// <summary>
/// Presenter for this view.
/// </summary>
[NotNull]
T Presenter { set; }
}
}
using System;
using JetBrains.Annotations;
using Unity.Cloud.Collaborate.Assets;
using Unity.Cloud.Collaborate.Components;
using Unity.Cloud.Collaborate.Presenters;
using Unity.Cloud.Collaborate.UserInterface;
using UnityEditor;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.UIElements;
namespace Unity.Cloud.Collaborate.Views
{
internal class MainPageView : PageComponent, IMainView
{
IMainPresenter m_Presenter;
public const string UssClassName = "main-page-view";
public const string TopBarUssClassName = UssClassName + "__top-bar";
public const string AlertBoxUssClassName = UssClassName + "__alert-box";
public const string TabViewUssClassName = UssClassName + "__tab-view";
public const string ContainerUssClassName = UssClassName + "__container";
// WARNING - These are hard-coded values. If you do anything to change the order
// these tabs are initialized, you'll need to change these
public const int ChangesTabIndex = 0;
public const int HistoryTabIndex = 1;
static readonly string k_LayoutPath = $"{CollaborateWindow.LayoutPath}/{nameof(MainPageView)}.uxml";
static readonly string k_StylePath = $"{CollaborateWindow.StylePath}/{nameof(MainPageView)}.uss";
readonly AlertBox m_AlertBox;
readonly TabView m_TabView;
readonly HistoryTabPageView m_HistoryView;
readonly ChangesTabPageView m_ChangesView;
readonly VisualElement m_Container;
readonly TopBar m_TopBar;
ProgressView m_ProgressView;
DisplayMode m_DisplayMode;
public MainPageView()
{
AddToClassList(UssClassName);
AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(k_LayoutPath).CloneTree(this);
styleSheets.Add(AssetDatabase.LoadAssetAtPath<StyleSheet>(k_StylePath));
m_TopBar = this.Q<TopBar>(className: TopBarUssClassName);
m_AlertBox = this.Q<AlertBox>(className: AlertBoxUssClassName);
m_TabView = this.Q<TabView>(className: TabViewUssClassName);
m_Container = this.Q<VisualElement>(className: ContainerUssClassName);
m_ChangesView = new ChangesTabPageView();
m_HistoryView = new HistoryTabPageView();
m_TabView.AddTab(StringAssets.changes, m_ChangesView);
m_TabView.AddTab(StringAssets.history, m_HistoryView);
// Set the current display mode.
m_DisplayMode = DisplayMode.TabView;
}
/// <inheritdoc />
public IMainPresenter Presenter
{
set
{
m_Presenter = value;
m_Presenter.AssignHistoryPresenter(m_HistoryView);
m_Presenter.AssignChangesPresenter(m_ChangesView);
m_TabView.TabSwitched += OnTabSwitched;
m_TopBar.BackButtonClicked += OnBackButtonClicked;
// If page active before presenter has been added, call start once we have it.
if (Active)
{
m_Presenter.Start();
}
}
}
/// <inheritdoc />
protected override void SetActive()
{
// Set TabView active if it's currently being displayed.
if (m_DisplayMode == DisplayMode.TabView)
{
m_TabView.SetActive();
}
m_Presenter?.Start();
}
/// <inheritdoc />
protected override void SetInactive()
{
// Set TabView inactive if it's current being displayed.
if (m_DisplayMode == DisplayMode.TabView)
{
m_TabView.SetInactive();
}
m_Presenter?.Stop();
}
/// <inheritdoc />
public void AddAlert(string id, AlertBox.AlertLevel level, string message, (string text, Action action)? button = null)
{
m_AlertBox.QueueAlert(id, level, message, button);
}
/// <inheritdoc />
public void RemoveAlert(string id)
{
m_AlertBox.DequeueAlert(id);
}
/// <inheritdoc />
public void SetTab(int index)
{
m_TabView.SwitchTab(index);
}
/// <inheritdoc />
public void AddOperationProgress()
{
SetDisplay(DisplayMode.ProgressView);
}
/// <inheritdoc />
public void RemoveOperationProgress()
{
SetDisplay(DisplayMode.TabView);
}
/// <inheritdoc />
public void SetOperationProgress(string title, string details, int percentage, int completed, int total, bool isPercentage, bool canCancel)
{
Assert.IsNotNull(m_ProgressView);
if (m_ProgressView == null) return;
var progress = isPercentage ? $"{percentage}%" : $"({completed} of {total})";
m_ProgressView.SetText($"{title}\n\n{details}", progress);
m_ProgressView.SetPercentComplete(percentage);
m_ProgressView.SetCancelButtonActive(canCancel);
}
/// <inheritdoc />
public void ClearBackNavigation()
{
m_TopBar.HideBackNavigation();
}
/// <inheritdoc />
public void DisplayBackNavigation(string text)
{
m_TopBar.DisplayBackNavigation(text);
}
void SetDisplay(DisplayMode mode)
{
Assert.AreNotEqual(m_DisplayMode, mode, "Cannot switch to the current display mode.");
m_DisplayMode = mode;
// Switch into tab or progress view.
if (m_DisplayMode == DisplayMode.TabView)
{
m_ProgressView?.AddToClassList(UiConstants.ussHidden);
m_TabView.RemoveFromClassList(UiConstants.ussHidden);
m_TabView.SetActive();
}
else
{
if (m_ProgressView == null)
{
m_ProgressView = new ProgressView();
m_ProgressView.SetCancelCallback(m_Presenter.RequestCancelJob);
m_Container.Add(m_ProgressView);
}
m_ProgressView.RemoveFromClassList(UiConstants.ussHidden);
m_TabView.AddToClassList(UiConstants.ussHidden);
m_TabView.SetInactive();
}
}
void OnTabSwitched(int index)
{
m_Presenter.UpdateTabIndex(index);
}
void OnBackButtonClicked()
{
m_Presenter.NavigateBack();
}
[UsedImplicitly]
public new class UxmlFactory : UxmlFactory<MainPageView> { }
enum DisplayMode
{
TabView,
ProgressView
}
}
}
# Views
In this directory, we have all of the interfaces and implementations of the Views in the package's **MVP** architecture.
ListAdapters are also stored under `Adapters/` as they are considered a sub-View.
using System;
using JetBrains.Annotations;
using Unity.Cloud.Collaborate.Assets;
using Unity.Cloud.Collaborate.Components;
using Unity.Cloud.Collaborate.Presenters;
using Unity.Cloud.Collaborate.UserInterface;
using UnityEditor;
using UnityEngine.UIElements;
namespace Unity.Cloud.Collaborate.Views
{
internal class StartPageView : PageComponent, IStartView
{
public const string UssClassName = "start-page-view";
public const string UssTitleClassName = UssClassName + "__title";
public const string UssButtonClassName = UssClassName + "__button";
static readonly string k_LayoutPath = $"{CollaborateWindow.LayoutPath}/{nameof(StartPageView)}.uxml";
static readonly string k_StylePath = $"{CollaborateWindow.StylePath}/{nameof(StartPageView)}.uss";
IStartPresenter m_Presenter;
readonly Label m_Text;
readonly Button m_Button;
public StartPageView()
{
AddToClassList(UssClassName);
AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(k_LayoutPath).CloneTree(this);
styleSheets.Add(AssetDatabase.LoadAssetAtPath<StyleSheet>(k_StylePath));
m_Text = this.Q<Label>(className: UssTitleClassName);
m_Button = this.Q<Button>(className: UssButtonClassName);
}
/// <inheritdoc />
public IStartPresenter Presenter
{
set
{
m_Presenter = value;
SetupEvents();
// If page active before presenter has been added, call start once we have it.
if (Active)
{
m_Presenter.Start();
}
}
}
/// <inheritdoc />
protected override void SetActive()
{
m_Presenter?.Start();
}
/// <inheritdoc />
protected override void SetInactive()
{
m_Presenter?.Stop();
}
void SetupEvents()
{
m_Button.clickable.clicked += m_Presenter.RequestStart;
}
/// <inheritdoc />
public string Text
{
set => m_Text.text = value;
}
/// <inheritdoc />
public string ButtonText
{
set => m_Button.text = value;
}
/// <inheritdoc />
public void SetButtonVisible(bool isVisible)
{
if (isVisible)
{
m_Button.RemoveFromClassList(UiConstants.ussHidden);
}
else
{
m_Button.AddToClassList(UiConstants.ussHidden);
}
}
[UsedImplicitly]
public new class UxmlFactory : UxmlFactory<StartPageView> { }
}
}
using System.Runtime.CompilerServices;
using UnityEngine;
[assembly: InternalsVisibleTo("Unity.PlasticSCM.EditorTests")]
[assembly: InternalsVisibleTo("Unity.PlasticSCM.DevTools")]
\ No newline at end of file
using PlasticGui;
using Unity.PlasticSCM.Editor.AssetsOverlays.Cache;
using Unity.PlasticSCM.Editor.UI;
namespace Unity.PlasticSCM.Editor.AssetMenu
{
internal class AssetMenuItems
{
internal static void Enable(
IAssetMenuOperations operations,
IAssetStatusCache assetStatusCache,
AssetOperations.IAssetSelection assetsSelection)
{
mOperations = operations;
mAssetStatusCache = assetStatusCache;
mAssetsSelection = assetsSelection;
AddMenuItems();
}
internal static void Disable()
{
RemoveMenuItems();
}
static void AddMenuItems()
{
HandleMenuItem.AddMenuItem(
GetPlasticMenuItemName(PlasticLocalization.Name.PendingChangesPlasticMenu),
PENDING_CHANGES_MENU_ITEM_PRIORITY,
PendingChanges, ValidatePendingChanges);
HandleMenuItem.AddMenuItem(
GetPlasticMenuItemName(PlasticLocalization.Name.AddPlasticMenu),
ADD_MENU_ITEM_PRIORITY,
Add, ValidateAdd);
HandleMenuItem.AddMenuItem(
GetPlasticMenuItemName(PlasticLocalization.Name.CheckoutPlasticMenu),
CHECKOUT_MENU_ITEM_PRIORITY,
Checkout, ValidateCheckout);
HandleMenuItem.AddMenuItem(
GetPlasticMenuItemName(PlasticLocalization.Name.CheckinPlasticMenu),
CHECKIN_MENU_ITEM_PRIORITY,
Checkin, ValidateCheckin);
HandleMenuItem.AddMenuItem(
GetPlasticMenuItemName(PlasticLocalization.Name.UndoPlasticMenu),
UNDO_MENU_ITEM_PRIORITY,
Undo, ValidateUndo);
HandleMenuItem.AddMenuItem(
GetPlasticMenuItemName(PlasticLocalization.Name.DiffPlasticMenu),
GetPlasticShortcut.ForAssetDiff(),
DIFF_MENU_ITEM_PRIORITY,
Diff, ValidateDiff);
HandleMenuItem.AddMenuItem(
GetPlasticMenuItemName(PlasticLocalization.Name.HistoryPlasticMenu),
GetPlasticShortcut.ForHistory(),
HISTORY_MENU_ITEM_PRIORITY,
History, ValidateHistory);
HandleMenuItem.UpdateAllMenus();
}
static void RemoveMenuItems()
{
HandleMenuItem.RemoveMenuItem(
PlasticLocalization.GetString(PlasticLocalization.Name.PrefixPlasticMenu));
HandleMenuItem.UpdateAllMenus();
}
internal static void PendingChanges()
{
ShowWindow.Plastic();
mOperations.ShowPendingChanges();
}
internal static bool ValidatePendingChanges()
{
return true;
}
internal static void Add()
{
mOperations.Add();
}
static bool ValidateAdd()
{
return ShouldMenuItemBeEnabled(AssetMenuOperations.Add);
}
internal static void Checkout()
{
mOperations.Checkout();
}
static bool ValidateCheckout()
{
return ShouldMenuItemBeEnabled(AssetMenuOperations.Checkout);
}
internal static void Checkin()
{
mOperations.Checkin();
}
static bool ValidateCheckin()
{
return ShouldMenuItemBeEnabled(AssetMenuOperations.Checkin);
}
internal static void Undo()
{
mOperations.Undo();
}
static bool ValidateUndo()
{
return ShouldMenuItemBeEnabled(AssetMenuOperations.Undo);
}
internal static void Diff()
{
mOperations.ShowDiff();
}
static bool ValidateDiff()
{
return ShouldMenuItemBeEnabled(AssetMenuOperations.Diff);
}
internal static void History()
{
ShowWindow.Plastic();
mOperations.ShowHistory();
}
static bool ValidateHistory()
{
return ShouldMenuItemBeEnabled(AssetMenuOperations.History);
}
static bool ShouldMenuItemBeEnabled(AssetMenuOperations operation)
{
if (mOperations == null)
return false;
SelectedAssetGroupInfo selectedGroupInfo = SelectedAssetGroupInfo.
BuildFromAssetList(
mAssetsSelection.GetSelectedAssets(),
mAssetStatusCache);
AssetMenuOperations operations = AssetMenuUpdater.
GetAvailableMenuOperations(selectedGroupInfo);
return operations.HasFlag(operation);
}
static string GetPlasticMenuItemName(PlasticLocalization.Name name)
{
return string.Format("{0}/{1}",
PlasticLocalization.GetString(PlasticLocalization.Name.PrefixPlasticMenu),
PlasticLocalization.GetString(name));
}
static IAssetMenuOperations mOperations;
static IAssetStatusCache mAssetStatusCache;
static AssetOperations.IAssetSelection mAssetsSelection;
const int BASE_MENU_ITEM_PRIORITY = 19; // Puts Plastic SCM right below Create menu
// incrementing the "order" param by 11 causes the menu system to add a separator
const int PENDING_CHANGES_MENU_ITEM_PRIORITY = BASE_MENU_ITEM_PRIORITY;
const int ADD_MENU_ITEM_PRIORITY = PENDING_CHANGES_MENU_ITEM_PRIORITY + 11;
const int CHECKOUT_MENU_ITEM_PRIORITY = PENDING_CHANGES_MENU_ITEM_PRIORITY + 12;
const int CHECKIN_MENU_ITEM_PRIORITY = PENDING_CHANGES_MENU_ITEM_PRIORITY + 13;
const int UNDO_MENU_ITEM_PRIORITY = PENDING_CHANGES_MENU_ITEM_PRIORITY + 14;
const int DIFF_MENU_ITEM_PRIORITY = PENDING_CHANGES_MENU_ITEM_PRIORITY + 25;
const int HISTORY_MENU_ITEM_PRIORITY = PENDING_CHANGES_MENU_ITEM_PRIORITY + 26;
}
}
using System;
using System.IO;
using UnityEditor.VersionControl;
using Codice;
using Codice.Client.Commands.WkTree;
using PlasticGui;
using Unity.PlasticSCM.Editor.AssetsOverlays.Cache;
using Unity.PlasticSCM.Editor.AssetsOverlays;
namespace Unity.PlasticSCM.Editor.AssetMenu
{
[Flags]
internal enum AssetMenuOperations : byte
{
None = 0,
Checkout = 1 << 0,
Diff = 1 << 1,
History = 1 << 2,
Add = 1 << 3,
Checkin = 1 << 4,
Undo = 1 << 5,
}
internal class SelectedAssetGroupInfo
{
internal int SelectedCount;
internal bool IsControlledSelection;
internal bool IsCheckedInSelection;
internal bool IsCheckedOutSelection;
internal bool IsPrivateSelection;
internal bool IsAddedSelection;
internal bool IsFileSelection;
internal bool HasAnyAddedInSelection;
internal bool HasAnyRemoteLockedInSelection;
internal static SelectedAssetGroupInfo BuildFromAssetList(
AssetList assetList,
IAssetStatusCache statusCache)
{
bool isCheckedInSelection = true;
bool isControlledSelection = true;
bool isCheckedOutSelection = true;
bool isPrivateSelection = true;
bool isAddedSelection = true;
bool isFileSelection = true;
bool hasAnyAddedInSelection = false;
bool hasAnyRemoteLockedInSelection = false;
int selectedCount = 0;
foreach (Asset asset in assetList)
{
if (string.IsNullOrEmpty(asset.path))
continue;
SelectedAssetGroupInfo singleFileGroupInfo = BuildFromSingleFile(
asset.path,
asset.isFolder,
statusCache);
if (!singleFileGroupInfo.IsCheckedInSelection)
isCheckedInSelection = false;
if (!singleFileGroupInfo.IsControlledSelection)
isControlledSelection = false;
if (!singleFileGroupInfo.IsCheckedOutSelection)
isCheckedOutSelection = false;
if (!singleFileGroupInfo.IsPrivateSelection)
isPrivateSelection = false;
if (!singleFileGroupInfo.IsAddedSelection)
isAddedSelection = false;
if (!singleFileGroupInfo.IsFileSelection)
isFileSelection = false;
if (singleFileGroupInfo.HasAnyAddedInSelection)
hasAnyAddedInSelection = true;
if (singleFileGroupInfo.HasAnyRemoteLockedInSelection)
hasAnyRemoteLockedInSelection = true;
selectedCount++;
}
return new SelectedAssetGroupInfo()
{
IsCheckedInSelection = isCheckedInSelection,
IsCheckedOutSelection = isCheckedOutSelection,
IsControlledSelection = isControlledSelection,
IsPrivateSelection = isPrivateSelection,
IsAddedSelection = isAddedSelection,
IsFileSelection = isFileSelection,
HasAnyAddedInSelection = hasAnyAddedInSelection,
HasAnyRemoteLockedInSelection = hasAnyRemoteLockedInSelection,
SelectedCount = selectedCount,
};
}
internal static SelectedAssetGroupInfo BuildFromSingleFile(
string path,
bool isDirectory,
IAssetStatusCache statusCache)
{
bool isCheckedInSelection = true;
bool isControlledSelection = true;
bool isCheckedOutSelection = true;
bool isPrivateSelection = true;
bool isAddedSelection = true;
bool isFileSelection = true;
bool hasAnyAddedInSelection = false;
bool hasAnyRemoteLockedInSelection = false;
string assetPath = Path.GetFullPath(path);
WorkspaceTreeNode wkTreeNode =
Plastic.API.GetWorkspaceTreeNode(assetPath);
if (isDirectory)
isFileSelection = false;
if (CheckWorkspaceTreeNodeStatus.IsPrivate(wkTreeNode))
isControlledSelection = false;
else
isPrivateSelection = false;
if (CheckWorkspaceTreeNodeStatus.IsCheckedOut(wkTreeNode))
isCheckedInSelection = false;
else
isCheckedOutSelection = false;
if (CheckWorkspaceTreeNodeStatus.IsAdded(wkTreeNode))
hasAnyAddedInSelection = true;
else
isAddedSelection = false;
AssetsOverlays.AssetStatus assetStatus = statusCache.GetStatusForPath(assetPath);
if (ClassifyAssetStatus.IsLockedRemote(assetStatus))
hasAnyRemoteLockedInSelection = true;
return new SelectedAssetGroupInfo()
{
IsCheckedInSelection = isCheckedInSelection,
IsCheckedOutSelection = isCheckedOutSelection,
IsControlledSelection = isControlledSelection,
IsPrivateSelection = isPrivateSelection,
IsAddedSelection = isAddedSelection,
IsFileSelection = isFileSelection,
HasAnyAddedInSelection = hasAnyAddedInSelection,
HasAnyRemoteLockedInSelection = hasAnyRemoteLockedInSelection,
SelectedCount = 1,
};
}
}
internal interface IAssetMenuOperations
{
void ShowPendingChanges();
void Add();
void Checkout();
void Checkin();
void Undo();
void ShowDiff();
void ShowHistory();
}
internal static class AssetMenuUpdater
{
internal static AssetMenuOperations GetAvailableMenuOperations(
SelectedAssetGroupInfo info)
{
AssetMenuOperations result = AssetMenuOperations.None;
if (info.SelectedCount == 0)
return result;
if (info.IsControlledSelection &&
info.IsCheckedInSelection &&
info.IsFileSelection &&
!info.HasAnyRemoteLockedInSelection)
{
result |= AssetMenuOperations.Checkout;
}
if (info.IsFileSelection &&
info.IsPrivateSelection)
{
result |= AssetMenuOperations.Add;
}
if (info.IsFileSelection &&
info.IsControlledSelection &&
info.IsCheckedOutSelection)
{
result |= AssetMenuOperations.Checkin;
result |= AssetMenuOperations.Undo;
}
if (info.SelectedCount == 1 &&
info.IsControlledSelection &&
!info.HasAnyAddedInSelection &&
info.IsFileSelection)
{
result |= AssetMenuOperations.Diff;
}
if (info.SelectedCount == 1 &&
info.IsControlledSelection &&
!info.HasAnyAddedInSelection)
{
result |= AssetMenuOperations.History;
}
return result;
}
}
}
using System.IO;
using System.Collections.Generic;
using UnityEditor;
using UnityEditor.VersionControl;
using Codice.Client.Commands;
using Codice.Client.Commands.WkTree;
using Codice.Client.Common;
using Codice.Client.Common.Threading;
using Codice.CM.Common;
using GluonGui;
using PlasticGui;
using PlasticGui.Gluon;
using PlasticGui.WorkspaceWindow;
using PlasticGui.WorkspaceWindow.Diff;
using PlasticGui.WorkspaceWindow.Items;
using Unity.PlasticSCM.Editor.AssetMenu.Dialogs;
using Unity.PlasticSCM.Editor.AssetsOverlays.Cache;
using Unity.PlasticSCM.Editor.AssetUtils;
using Unity.PlasticSCM.Editor.UI;
using GluonCheckoutOperation = GluonGui.WorkspaceWindow.Views.WorkspaceExplorer.Explorer.Operations.CheckoutOperation;
using GluonUndoCheckoutOperation = GluonGui.WorkspaceWindow.Views.WorkspaceExplorer.Explorer.Operations.UndoCheckoutOperation;
using GluonAddoperation = GluonGui.WorkspaceWindow.Views.WorkspaceExplorer.Explorer.Operations.AddOperation;
namespace Unity.PlasticSCM.Editor.AssetMenu
{
internal class AssetOperations : IAssetMenuOperations
{
internal interface IAssetSelection
{
AssetList GetSelectedAssets();
}
internal AssetOperations(
WorkspaceInfo wkInfo,
IWorkspaceWindow workspaceWindow,
IViewSwitcher viewSwitcher,
IHistoryViewLauncher historyViewLauncher,
ViewHost viewHost,
NewIncomingChangesUpdater newIncomingChangesUpdater,
IAssetStatusCache assetStatusCache,
IMergeViewLauncher mergeViewLauncher,
IGluonViewSwitcher gluonViewSwitcher,
EditorWindow parentWindow,
IAssetSelection assetSelection,
bool isGluonMode)
{
mWkInfo = wkInfo;
mWorkspaceWindow = workspaceWindow;
mViewSwitcher = viewSwitcher;
mHistoryViewLauncher = historyViewLauncher;
mViewHost = viewHost;
mNewIncomingChangesUpdater = newIncomingChangesUpdater;
mAssetStatusCache = assetStatusCache;
mMergeViewLauncher = mergeViewLauncher;
mGluonViewSwitcher = gluonViewSwitcher;
mAssetSelection = assetSelection;
mIsGluonMode = isGluonMode;
mParentWindow = parentWindow;
mGuiMessage = new UnityPlasticGuiMessage(parentWindow);
mProgressControls = new EditorProgressControls(mGuiMessage);
}
void IAssetMenuOperations.ShowPendingChanges()
{
mViewSwitcher.ShowPendingChanges();
}
void IAssetMenuOperations.Add()
{
List<string> selectedPaths = GetSelectedPaths.ForOperation(
mAssetSelection,
mAssetStatusCache,
AssetMenuOperations.Add);
if (mIsGluonMode)
{
GluonAddoperation.Add(
mViewHost,
mProgressControls,
mGuiMessage,
selectedPaths.ToArray(),
false,
RefreshAsset.VersionControlCache);
return;
}
AddOperation.Run(
mWorkspaceWindow,
mProgressControls,
null,
null,
selectedPaths,
false,
mNewIncomingChangesUpdater,
RefreshAsset.VersionControlCache);
}
void IAssetMenuOperations.Checkout()
{
List<string> selectedPaths = GetSelectedPaths.ForOperation(
mAssetSelection,
mAssetStatusCache,
AssetMenuOperations.Checkout);
if (mIsGluonMode)
{
GluonCheckoutOperation.Checkout(
mViewHost,
mProgressControls,
mGuiMessage,
selectedPaths.ToArray(),
false,
RefreshAsset.VersionControlCache);
return;
}
CheckoutOperation.Checkout(
mWorkspaceWindow,
null,
mProgressControls,
selectedPaths,
mNewIncomingChangesUpdater,
RefreshAsset.VersionControlCache);
}
void IAssetMenuOperations.Checkin()
{
List<string> selectedPaths = GetSelectedPaths.ForOperation(
mAssetSelection,
mAssetStatusCache,
AssetMenuOperations.Checkin);
if (!CheckinDialog.CheckinPaths(
mWkInfo,
selectedPaths,
mAssetStatusCache,
mIsGluonMode,
mParentWindow,
mWorkspaceWindow,
mViewHost,
mGuiMessage,
mMergeViewLauncher,
mGluonViewSwitcher))
return;
RefreshAsset.UnityAssetDatabase();
}
void IAssetMenuOperations.Undo()
{
List<string> selectedPaths = GetSelectedPaths.ForOperation(
mAssetSelection,
mAssetStatusCache,
AssetMenuOperations.Undo);
SaveAssets.ForPathsWithoutConfirmation(selectedPaths);
if (mIsGluonMode)
{
GluonUndoCheckoutOperation.UndoCheckout(
mWkInfo,
mViewHost,
mProgressControls,
mGuiMessage,
selectedPaths.ToArray(),
false,
RefreshAsset.UnityAssetDatabase);
return;
}
UndoCheckoutOperation.Run(
mWorkspaceWindow,
null,
mProgressControls,
selectedPaths,
mNewIncomingChangesUpdater,
RefreshAsset.UnityAssetDatabase);
}
void IAssetMenuOperations.ShowDiff()
{
string selectedPath = AssetsSelection.GetSelectedPath(
mAssetSelection.GetSelectedAssets());
DiffInfo diffInfo = null;
IThreadWaiter waiter = ThreadWaiter.GetWaiter(10);
waiter.Execute(
/*threadOperationDelegate*/ delegate
{
string symbolicName = GetSymbolicName(selectedPath);
string extension = Path.GetExtension(selectedPath);
diffInfo = Plastic.API.BuildDiffInfoForDiffWithPrevious(
selectedPath, symbolicName, selectedPath, extension, mWkInfo);
},
/*afterOperationDelegate*/ delegate
{
if (waiter.Exception != null)
{
ExceptionsHandler.DisplayException(waiter.Exception);
return;
}
DiffOperation.DiffWithPrevious(
diffInfo,
null,
null);
});
}
void IAssetMenuOperations.ShowHistory()
{
AssetList assetList = mAssetSelection.GetSelectedAssets();
Asset selectedAsset = AssetsSelection.GetSelectedAsset(
assetList);
string selectedPath = AssetsSelection.GetSelectedPath(
assetList);
WorkspaceTreeNode node = Plastic.API.
GetWorkspaceTreeNode(selectedPath);
mHistoryViewLauncher.ShowHistoryView(
node.RepSpec,
node.RevInfo.ItemId,
selectedPath,
selectedAsset.isFolder);
}
static string GetSymbolicName(string selectedPath)
{
WorkspaceTreeNode node = Plastic.API.
GetWorkspaceTreeNode(selectedPath);
string branchName = string.Empty;
BranchInfoCache.TryGetBranchName(
node.RepSpec, node.RevInfo.BranchId, out branchName);
string userName = Plastic.API.GetUserName(
node.RepSpec.Server, node.RevInfo.Owner);
string symbolicName = string.Format(
"cs:{0}@{1} {2} {3}",
node.RevInfo.Changeset,
string.Format("br:{0}", branchName),
userName,
"Workspace Revision");
return symbolicName;
}
readonly WorkspaceInfo mWkInfo;
readonly IViewSwitcher mViewSwitcher;
readonly IHistoryViewLauncher mHistoryViewLauncher;
readonly IWorkspaceWindow mWorkspaceWindow;
readonly ViewHost mViewHost;
readonly NewIncomingChangesUpdater mNewIncomingChangesUpdater;
readonly IAssetStatusCache mAssetStatusCache;
readonly IMergeViewLauncher mMergeViewLauncher;
readonly IGluonViewSwitcher mGluonViewSwitcher;
readonly bool mIsGluonMode;
readonly GuiMessage.IGuiMessage mGuiMessage;
readonly EditorProgressControls mProgressControls;
readonly EditorWindow mParentWindow;
readonly IAssetSelection mAssetSelection;
static class GetSelectedPaths
{
internal static List<string> ForOperation(
IAssetSelection assetSelection,
IAssetStatusCache assetStatusCache,
AssetMenuOperations operation)
{
List<string> selectedPaths = AssetsSelection.GetSelectedPaths(
assetSelection.GetSelectedAssets());
List<string> result = new List<string>(selectedPaths);
foreach (string path in selectedPaths)
{
if (MetaPath.IsMetaPath(path))
continue;
string metaPath = MetaPath.GetMetaPath(path);
if (!File.Exists(metaPath))
continue;
if (result.Contains(metaPath))
continue;
if (!IsApplicableForOperation(
metaPath, false, operation, assetStatusCache))
continue;
result.Add(metaPath);
}
return result;
}
static bool IsApplicableForOperation(
string path,
bool isDirectory,
AssetMenuOperations operation,
IAssetStatusCache assetStatusCache)
{
SelectedAssetGroupInfo info = SelectedAssetGroupInfo.BuildFromSingleFile(
path, isDirectory, assetStatusCache);
return AssetMenuUpdater.GetAvailableMenuOperations(info).HasFlag(operation);
}
}
}
}
using System;
using System.Collections.Generic;
using System.IO;
using Unity.PlasticSCM.Editor.AssetUtils;
using UnityEditor.VersionControl;
namespace Unity.PlasticSCM.Editor.AssetMenu
{
internal static class AssetsSelection
{
internal static Asset GetSelectedAsset(AssetList assetList)
{
if (assetList.Count == 0)
return null;
return assetList[0];
}
internal static string GetSelectedPath(AssetList assetList)
{
if (assetList.Count == 0)
return null;
return Path.GetFullPath(assetList[0].path);
}
internal static List<string> GetSelectedPaths(AssetList selectedAssets)
{
List<string> result = new List<string>();
foreach (Asset asset in selectedAssets)
{
string fullPath = Path.GetFullPath(asset.path);
result.Add(fullPath);
}
return result;
}
}
}
using System;
using System.Collections.Generic;
using System.IO;
using Codice.Client.Common;
using Codice.CM.Common;
using GluonGui;
using PlasticGui;
using PlasticGui.Gluon;
using Unity.PlasticSCM.Editor.AssetsOverlays;
using Unity.PlasticSCM.Editor.AssetsOverlays.Cache;
using Unity.PlasticSCM.Editor.AssetUtils;
using Unity.PlasticSCM.Editor.UI;
using Unity.PlasticSCM.Editor.UI.Progress;
using Unity.PlasticSCM.Editor.UI.Tree;
using UnityEditor;
using UnityEngine;
namespace Unity.PlasticSCM.Editor.AssetMenu.Dialogs
{
internal class CheckinDialog : PlasticDialog
{
protected override Rect DefaultRect
{
get
{
var baseRect = base.DefaultRect;
return new Rect(baseRect.x, baseRect.y, 700, 450);
}
}
protected override string GetTitle()
{
return PlasticLocalization.GetString(
PlasticLocalization.Name.CheckinChanges);
}
internal static bool CheckinPaths(
WorkspaceInfo wkInfo,
List<string> paths,
IAssetStatusCache assetStatusCache,
bool isGluonMode,
EditorWindow parentWindow,
IWorkspaceWindow workspaceWindow,
ViewHost viewHost,
GuiMessage.IGuiMessage guiMessage,
IMergeViewLauncher mergeViewLauncher,
IGluonViewSwitcher gluonViewSwitcher)
{
MetaCache metaCache = new MetaCache();
metaCache.Build(paths);
CheckinDialog dialog = Create(
wkInfo,
paths,
assetStatusCache,
metaCache,
isGluonMode,
new ProgressControlsForDialogs(),
workspaceWindow,
viewHost,
guiMessage,
mergeViewLauncher,
gluonViewSwitcher);
return dialog.RunModal(parentWindow) == ResponseType.Ok;
}
protected override void OnModalGUI()
{
Title(PlasticLocalization.GetString(
PlasticLocalization.Name.CheckinComment));
GUI.SetNextControlName(CHECKIN_TEXTAREA_NAME);
mComment = GUILayout.TextArea(
mComment,
EditorStyles.textArea,
GUILayout.MinHeight(120));
if (!mTextAreaFocused)
{
EditorGUI.FocusTextInControl(CHECKIN_TEXTAREA_NAME);
mTextAreaFocused = true;
}
Title(PlasticLocalization.GetString(PlasticLocalization.Name.Files));
DoFileList(
mWkInfo,
mPaths,
mAssetStatusCache,
mMetaCache);
DrawProgressForDialogs.For(
mProgressControls.ProgressData);
DoButtonsArea();
mProgressControls.ForcedUpdateProgress(this);
}
void DoFileList(
WorkspaceInfo wkInfo,
List<string> paths,
IAssetStatusCache assetStatusCache,
MetaCache metaCache)
{
mFileListScrollPosition = GUILayout.BeginScrollView(
mFileListScrollPosition,
EditorStyles.helpBox,
GUILayout.ExpandHeight(true));
foreach (string path in paths)
{
if (MetaPath.IsMetaPath(path))
continue;
Texture fileIcon = Directory.Exists(path) ?
Images.GetDirectoryIcon() :
Images.GetFileIcon(path);
string label = WorkspacePath.GetWorkspaceRelativePath(
wkInfo.ClientPath, path);
if (metaCache.HasMeta(path))
label = string.Concat(label, UnityConstants.TREEVIEW_META_LABEL);
GUIContent content = new GUIContent(
label, fileIcon);
GUILayout.Label(
content,
GUILayout.Height(UnityConstants.TREEVIEW_ROW_HEIGHT));
Rect iconRect = GUILayoutUtility.GetLastRect();
DoStatusOverlays(
iconRect,
assetStatusCache,
path);
}
GUILayout.EndScrollView();
}
static void DoStatusOverlays(
Rect iconRect,
IAssetStatusCache assetStatusCache,
string path)
{
AssetsOverlays.AssetStatus statusesToDraw = DrawAssetOverlay.GetStatusesToDraw(
assetStatusCache.GetStatusForPath(path));
foreach (AssetsOverlays.AssetStatus status in Enum.GetValues(typeof(AssetsOverlays.AssetStatus)))
{
if (status == AssetsOverlays.AssetStatus.None)
continue;
if (!statusesToDraw.HasFlag(status))
continue;
GetChangesOverlayIcon.Data overlayIconData =
GetChangesOverlayIcon.ForAssetStatus(status);
if (overlayIconData != null)
{
Rect overlayIconRect = new Rect(
iconRect.x + overlayIconData.XOffset,
iconRect.y + overlayIconData.YOffset,
overlayIconData.Size, overlayIconData.Size);
GUI.DrawTexture(
overlayIconRect, overlayIconData.Texture,
ScaleMode.ScaleToFit);
}
}
}
void DoButtonsArea()
{
using (new EditorGUILayout.HorizontalScope())
{
GUILayout.FlexibleSpace();
if (Application.platform == RuntimePlatform.WindowsEditor)
{
DoCheckinButton();
DoCancelButton();
return;
}
DoCancelButton();
DoCheckinButton();
}
}
void DoCheckinButton()
{
GUI.enabled = !string.IsNullOrEmpty(mComment) && !mIsRunningCheckin;
try
{
if (!AcceptButton(PlasticLocalization.GetString(
PlasticLocalization.Name.CheckinButton)))
return;
}
finally
{
GUI.enabled = true;
}
OkButtonWithCheckinAction();
}
void DoCancelButton()
{
if (!NormalButton(PlasticLocalization.GetString(
PlasticLocalization.Name.CancelButton)))
return;
CancelButtonAction();
}
void OkButtonWithCheckinAction()
{
bool isCancelled;
SaveAssets.ForPathsWithConfirmation(mPaths, out isCancelled);
if (isCancelled)
return;
mIsRunningCheckin = true;
mPaths.AddRange(mMetaCache.GetExistingMeta(mPaths));
if (mIsGluonMode)
{
CheckinDialogOperations.CheckinPathsPartial(
mWkInfo,
mPaths,
mComment,
mViewHost,
this,
mGuiMessage,
mProgressControls,
mGluonViewSwitcher);
return;
}
CheckinDialogOperations.CheckinPaths(
mWkInfo,
mPaths,
mComment,
mWorkspaceWindow,
this,
mGuiMessage,
mProgressControls,
mMergeViewLauncher);
}
static CheckinDialog Create(
WorkspaceInfo wkInfo,
List<string> paths,
IAssetStatusCache assetStatusCache,
MetaCache metaCache,
bool isGluonMode,
ProgressControlsForDialogs progressControls,
IWorkspaceWindow workspaceWindow,
ViewHost viewHost,
GuiMessage.IGuiMessage guiMessage,
IMergeViewLauncher mergeViewLauncher,
IGluonViewSwitcher gluonViewSwitcher)
{
var instance = CreateInstance<CheckinDialog>();
instance.IsResizable = true;
instance.minSize = new Vector2(520, 370);
instance.mWkInfo = wkInfo;
instance.mPaths = paths;
instance.mAssetStatusCache = assetStatusCache;
instance.mMetaCache = metaCache;
instance.mIsGluonMode = isGluonMode;
instance.mProgressControls = progressControls;
instance.mWorkspaceWindow = workspaceWindow;
instance.mViewHost = viewHost;
instance.mGuiMessage = guiMessage;
instance.mMergeViewLauncher = mergeViewLauncher;
instance.mGluonViewSwitcher = gluonViewSwitcher;
instance.mEnterKeyAction = instance.OkButtonAction;
instance.mEscapeKeyAction = instance.CancelButtonAction;
return instance;
}
WorkspaceInfo mWkInfo;
List<string> mPaths;
IAssetStatusCache mAssetStatusCache;
MetaCache mMetaCache;
bool mIsGluonMode;
bool mTextAreaFocused;
string mComment;
bool mIsRunningCheckin;
Vector2 mFileListScrollPosition;
ProgressControlsForDialogs mProgressControls;
IWorkspaceWindow mWorkspaceWindow;
ViewHost mViewHost;
IMergeViewLauncher mMergeViewLauncher;
IGluonViewSwitcher mGluonViewSwitcher;
GuiMessage.IGuiMessage mGuiMessage;
const string CHECKIN_TEXTAREA_NAME = "checkin_textarea";
class MetaCache
{
internal bool HasMeta(string path)
{
return mCache.Contains(MetaPath.GetMetaPath(path));
}
internal List<string> GetExistingMeta(List<string> paths)
{
List<string> result = new List<string>();
foreach (string path in paths)
{
string metaPath = MetaPath.GetMetaPath(path);
if (!mCache.Contains(metaPath))
continue;
result.Add(metaPath);
}
return result;
}
internal void Build(List<string> paths)
{
HashSet<string> indexedKeys = BuildIndexedKeys(paths);
for (int i = paths.Count - 1; i >= 0; i--)
{
string currentPath = paths[i];
if (!MetaPath.IsMetaPath(currentPath))
continue;
string realPath = MetaPath.GetPathFromMetaPath(currentPath);
if (!indexedKeys.Contains(realPath))
continue;
// found foo.c and foo.c.meta
// with the same chage types - move .meta to cache
mCache.Add(currentPath);
paths.RemoveAt(i);
}
}
static HashSet<string> BuildIndexedKeys(List<string> paths)
{
HashSet<string> result = new HashSet<string>();
foreach (string path in paths)
{
if (MetaPath.IsMetaPath(path))
continue;
result.Add(path);
}
return result;
}
HashSet<string> mCache =
new HashSet<string>();
}
}
}
using System;
using System.Collections.Generic;
using Codice.Client.BaseCommands;
using Codice.Client.Commands.CheckIn;
using Codice.Client.Common;
using Codice.Client.Common.Threading;
using Codice.Client.GameUI.Checkin;
using Codice.CM.Common;
using GluonGui;
using PlasticGui;
using PlasticGui.Gluon;
using PlasticGui.WorkspaceWindow.PendingChanges;
namespace Unity.PlasticSCM.Editor.AssetMenu.Dialogs
{
internal static class CheckinDialogOperations
{
internal static void CheckinPaths(
WorkspaceInfo wkInfo,
List<string> paths,
string comment,
IWorkspaceWindow workspaceWindow,
CheckinDialog dialog,
GuiMessage.IGuiMessage guiMessage,
IProgressControls progressControls,
IMergeViewLauncher mergeViewLauncher)
{
BaseCommandsImpl baseCommands = new BaseCommandsImpl();
progressControls.ShowProgress("Checkin in files");
IThreadWaiter waiter = ThreadWaiter.GetWaiter(50);
waiter.Execute(
/*threadOperationDelegate*/ delegate
{
CheckinParams ciParams = new CheckinParams();
ciParams.paths = paths.ToArray();
ciParams.comment = comment;
ciParams.time = DateTime.MinValue;
ciParams.flags = CheckinFlags.Recurse | CheckinFlags.ProcessSymlinks;
baseCommands.CheckIn(ciParams);
},
/*afterOperationDelegate*/ delegate
{
progressControls.HideProgress();
((IPlasticDialogCloser)dialog).CloseDialog();
if (waiter.Exception is CmClientMergeNeededException ||
waiter.Exception is CmClientUpdateMergeNeededException)
{
// we need to explicitly call EditorWindow.Close() to ensure
// that the dialog is closed before asking the user
dialog.Close();
if (!UserWantsToShowIncomingView(guiMessage))
return;
ShowIncomingChanges.FromCheckin(
wkInfo,
mergeViewLauncher,
progressControls);
return;
}
if (waiter.Exception != null)
{
ExceptionsHandler.DisplayException(waiter.Exception);
return;
}
workspaceWindow.RefreshView(ViewType.PendingChangesView);
workspaceWindow.RefreshView(ViewType.HistoryView);
});
}
internal static void CheckinPathsPartial(
WorkspaceInfo wkInfo,
List<string> paths,
string comment,
ViewHost viewHost,
CheckinDialog dialog,
GuiMessage.IGuiMessage guiMessage,
IProgressControls progressControls,
IGluonViewSwitcher gluonViewSwitcher)
{
BaseCommandsImpl baseCommands = new BaseCommandsImpl();
progressControls.ShowProgress(PlasticLocalization.GetString(
PlasticLocalization.Name.CheckinInFilesProgress));
IThreadWaiter waiter = ThreadWaiter.GetWaiter(50);
waiter.Execute(
/*threadOperationDelegate*/ delegate
{
baseCommands.PartialCheckin(wkInfo, paths, comment);
},
/*afterOperationDelegate*/ delegate
{
progressControls.HideProgress();
((IPlasticDialogCloser)dialog).CloseDialog();
if (waiter.Exception is CheckinConflictsException)
{
// we need to explicitly call EditorWindow.Close() to ensure
// that the dialog is closed before asking the user
dialog.Close();
if (!UserWantsToShowIncomingView(guiMessage))
return;
gluonViewSwitcher.ShowIncomingChangesView();
return;
}
if (waiter.Exception != null)
{
ExceptionsHandler.DisplayException(waiter.Exception);
return;
}
viewHost.RefreshView(ViewType.CheckinView);
viewHost.RefreshView(ViewType.HistoryView);
});
}
static bool UserWantsToShowIncomingView(GuiMessage.IGuiMessage guiMessage)
{
GuiMessage.GuiMessageResponseButton result = guiMessage.ShowQuestion(
PlasticLocalization.GetString(PlasticLocalization.Name.CheckinConflictsTitle),
PlasticLocalization.GetString(PlasticLocalization.Name.UnityCheckinConflictsExplanation),
"",
PlasticLocalization.GetString(PlasticLocalization.Name.CheckinShowIncomingChangesView),
PlasticLocalization.GetString(PlasticLocalization.Name.CancelButton),
false);
return result == GuiMessage.GuiMessageResponseButton.Second;
}
}
}
using UnityEditor.VersionControl;
namespace Unity.PlasticSCM.Editor.AssetMenu
{
internal class ProjectViewAssetSelection : AssetOperations.IAssetSelection
{
AssetList AssetOperations.IAssetSelection.GetSelectedAssets()
{
return Provider.GetAssetListFromSelection();
}
}
}
using System;
namespace Unity.PlasticSCM.Editor.AssetsOverlays
{
[Flags]
internal enum AssetStatus
{
None = 0,
Private = 1 << 0,
Ignored = 1 << 2,
Added = 1 << 3,
Checkout = 1 << 4,
Controlled = 1 << 5,
UpToDate = 1 << 6,
OutOfDate = 1 << 7,
Conflicted = 1 << 8,
DeletedOnServer = 1 << 9,
Locked = 1 << 10,
LockedRemote = 1 << 11,
}
internal class LockStatusData
{
internal readonly AssetStatus Status;
internal readonly string LockedBy;
internal readonly string WorkspaceName;
internal LockStatusData(
AssetStatus status,
string lockedBy,
string workspaceName)
{
Status = status;
LockedBy = lockedBy;
WorkspaceName = workspaceName;
}
}
internal class ClassifyAssetStatus
{
internal static bool IsControlled(AssetStatus status)
{
return ContainsAny(status, AssetStatus.Controlled);
}
internal static bool IsLockedRemote(AssetStatus status)
{
return ContainsAny(status, AssetStatus.LockedRemote);
}
internal static bool IsConflicted(AssetStatus status)
{
return ContainsAny(status, AssetStatus.Conflicted);
}
internal static bool IsAdded(AssetStatus status)
{
return ContainsAny(status, AssetStatus.Added);
}
internal static bool IsCheckedOut(AssetStatus status)
{
return ContainsAny(status, AssetStatus.Checkout);
}
static bool ContainsAny(AssetStatus status, AssetStatus matchTo)
{
return (status & matchTo) != AssetStatus.None;
}
}
}
using System;
using System.IO;
using UnityEditor;
using Codice.CM.Common;
namespace Unity.PlasticSCM.Editor.AssetsOverlays.Cache
{
internal interface IAssetStatusCache
{
AssetStatus GetStatusForPath(string fullPath);
AssetStatus GetStatusForGuid(string guid);
LockStatusData GetLockStatusData(string guid);
LockStatusData GetLockStatusDataForPath(string path);
void Clear();
}
internal class AssetStatusCache : IAssetStatusCache
{
internal AssetStatusCache(
WorkspaceInfo wkInfo,
bool isGluonMode,
Action repaintProjectWindow)
{
mLocalStatusCache = new LocalStatusCache(wkInfo);
mRemoteStatusCache = new RemoteStatusCache(
wkInfo,
isGluonMode,
repaintProjectWindow);
mLockStatusCache = new LockStatusCache(
wkInfo,
repaintProjectWindow);
}
AssetStatus IAssetStatusCache.GetStatusForPath(string fullPath)
{
AssetStatus localStatus = mLocalStatusCache.GetStatus(fullPath);
if (!ClassifyAssetStatus.IsControlled(localStatus))
return localStatus;
AssetStatus remoteStatus = mRemoteStatusCache.GetStatus(fullPath);
AssetStatus lockStatus = mLockStatusCache.GetStatus(fullPath);
return localStatus | remoteStatus | lockStatus;
}
AssetStatus IAssetStatusCache.GetStatusForGuid(string guid)
{
string fullPath = GetAssetPath(guid);
if (string.IsNullOrEmpty(fullPath))
return AssetStatus.None;
return ((IAssetStatusCache)this).GetStatusForPath(fullPath);
}
LockStatusData IAssetStatusCache.GetLockStatusDataForPath(string path)
{
if (string.IsNullOrEmpty(path))
return null;
return mLockStatusCache.GetLockStatusData(path);
}
LockStatusData IAssetStatusCache.GetLockStatusData(string guid)
{
string fullPath = GetAssetPath(guid);
return ((IAssetStatusCache)this).GetLockStatusDataForPath(fullPath);
}
void IAssetStatusCache.Clear()
{
mLocalStatusCache.Clear();
mRemoteStatusCache.Clear();
mLockStatusCache.Clear();
}
static string GetAssetPath(string guid)
{
string assetPath = AssetDatabase.GUIDToAssetPath(guid);
if (string.IsNullOrEmpty(assetPath))
return null;
return Path.GetFullPath(assetPath);
}
readonly LocalStatusCache mLocalStatusCache;
readonly RemoteStatusCache mRemoteStatusCache;
readonly LockStatusCache mLockStatusCache;
}
}
using System;
using System.Collections.Generic;
using Codice.Utils;
namespace Unity.PlasticSCM.Editor.AssetsOverlays.Cache
{
internal static class BuildPathDictionary
{
internal static Dictionary<string, T> ForPlatform<T>()
{
if (PlatformIdentifier.IsWindows())
return new Dictionary<string, T>(
StringComparer.OrdinalIgnoreCase);
return new Dictionary<string, T>();
}
}
}
using System.Collections.Generic;
using Codice;
using Codice.Client.BaseCommands;
using Codice.Client.Commands.WkTree;
using Codice.Client.Common;
using Codice.CM.Common;
using PlasticGui;
using PlasticGui.WorkspaceWindow;
namespace Unity.PlasticSCM.Editor.AssetsOverlays.Cache
{
internal class LocalStatusCache
{
internal LocalStatusCache(WorkspaceInfo wkInfo)
{
mWkInfo = wkInfo;
}
internal AssetStatus GetStatus(string fullPath)
{
AssetStatus result;
if (mStatusByPathCache.TryGetValue(fullPath, out result))
return result;
result = CalculateStatus(
fullPath,
mWkInfo.ClientPath,
FilterManager.Get().GetIgnoredFilter());
mStatusByPathCache.Add(fullPath, result);
return result;
}
internal void Clear()
{
FilterManager.Get().Reload();
mStatusByPathCache.Clear();
}
static AssetStatus CalculateStatus(
string fullPath,
string wkPath,
IgnoredFilesFilter ignoredFilter)
{
if (!IsOnWorkspace(fullPath, wkPath))
return AssetStatus.None;
WorkspaceTreeNode treeNode = Plastic.API.GetWorkspaceTreeNode(fullPath);
if (CheckWorkspaceTreeNodeStatus.IsPrivate(treeNode))
{
return ignoredFilter.IsIgnored(fullPath) ?
AssetStatus.Ignored : AssetStatus.Private;
}
if (CheckWorkspaceTreeNodeStatus.IsAdded(treeNode))
return AssetStatus.Added;
AssetStatus result = AssetStatus.Controlled;
if (CheckWorkspaceTreeNodeStatus.IsCheckedOut(treeNode) &&
!CheckWorkspaceTreeNodeStatus.IsDirectory(treeNode))
result |= AssetStatus.Checkout;
return result;
}
static bool IsOnWorkspace(string fullPath, string clientPath)
{
return PathHelper.IsContainedOn(fullPath, clientPath);
}
Dictionary<string, AssetStatus> mStatusByPathCache =
BuildPathDictionary.ForPlatform<AssetStatus>();
readonly WorkspaceInfo mWkInfo;
}
}
using System;
using System.Collections.Generic;
using Codice;
using Codice.Client.BaseCommands;
using Codice.Client.Commands.WkTree;
using Codice.Client.Common;
using Codice.Client.Common.Locks;
using Codice.Client.Common.Threading;
using Codice.Client.Common.WkTree;
using Codice.CM.Common;
using Codice.Utils;
namespace Unity.PlasticSCM.Editor.AssetsOverlays.Cache
{
internal class LockStatusCache
{
internal LockStatusCache(
WorkspaceInfo wkInfo,
Action repaintProjectWindow)
{
mWkInfo = wkInfo;
mRepaintProjectWindow = repaintProjectWindow;
}
internal AssetStatus GetStatus(string fullPath)
{
LockStatusData lockStatusData = GetLockStatusData(fullPath);
if (lockStatusData == null)
return AssetStatus.None;
return lockStatusData.Status;
}
internal LockStatusData GetLockStatusData(string fullPath)
{
lock (mLock)
{
if (mStatusByPathCache == null)
{
mStatusByPathCache = BuildPathDictionary.ForPlatform<LockStatusData>();
mCurrentCancelToken.Cancel();
mCurrentCancelToken = new CancelToken();
AsyncCalculateStatus(mCurrentCancelToken);
return null;
}
LockStatusData result;
if (mStatusByPathCache.TryGetValue(fullPath, out result))
return result;
return null;
}
}
internal void Clear()
{
lock (mLock)
{
mCurrentCancelToken.Cancel();
mStatusByPathCache = null;
}
}
void AsyncCalculateStatus(CancelToken cancelToken)
{
Dictionary<string, LockStatusData> statusByPathCache = null;
IThreadWaiter waiter = ThreadWaiter.GetWaiter(50);
waiter.Execute(
/*threadOperationDelegate*/ delegate
{
Dictionary<RepositorySpec, List<WorkspaceTreeNode>> lockCandidates =
new Dictionary<RepositorySpec, List<WorkspaceTreeNode>>();
FillLockCandidates.ForTree(mWkInfo, lockCandidates);
if (cancelToken.IsCancelled())
return;
Dictionary<WorkspaceTreeNode, LockInfo> lockInfoByNode =
SearchLocks.GetLocksInfo(mWkInfo, lockCandidates);
if (cancelToken.IsCancelled())
return;
statusByPathCache = BuildStatusByNodeCache.
ForLocks(mWkInfo.ClientPath, lockInfoByNode);
},
/*afterOperationDelegate*/ delegate
{
if (waiter.Exception != null)
{
ExceptionsHandler.LogException(
"LockStatusCache",
waiter.Exception);
return;
}
if (cancelToken.IsCancelled())
return;
lock (mLock)
{
mStatusByPathCache = statusByPathCache;
}
mRepaintProjectWindow();
});
}
static class FillLockCandidates
{
internal static void ForTree(
WorkspaceInfo wkInfo,
Dictionary<RepositorySpec, List<WorkspaceTreeNode>> lockCandidates)
{
WorkspaceTreeNode rootNode = CmConnection.Get().GetWorkspaceTreeHandler().
GetWorkspaceTree(wkInfo, wkInfo.ClientPath, true);
Queue<WorkspaceTreeNode> pendingDirectories = new Queue<WorkspaceTreeNode>();
pendingDirectories.Enqueue(rootNode);
while (pendingDirectories.Count > 0)
{
WorkspaceTreeNode directoryNode = pendingDirectories.Dequeue();
ForChildren(directoryNode, pendingDirectories, lockCandidates);
}
}
static void ForChildren(
WorkspaceTreeNode directoryNode,
Queue<WorkspaceTreeNode> pendingDirectories,
Dictionary<RepositorySpec, List<WorkspaceTreeNode>> lockCandidates)
{
if (!directoryNode.HasChildren)
return;
foreach (WorkspaceTreeNode child in directoryNode.Children)
{
if (CheckWorkspaceTreeNodeStatus.IsDirectory(child))
{
pendingDirectories.Enqueue(child);
continue;
}
if (CheckWorkspaceTreeNodeStatus.IsAdded(child))
continue;
List<WorkspaceTreeNode> nodes = null;
if (!lockCandidates.TryGetValue(child.RepSpec, out nodes))
{
nodes = new List<WorkspaceTreeNode>();
lockCandidates.Add(child.RepSpec, nodes);
}
nodes.Add(child);
}
}
}
static class BuildStatusByNodeCache
{
internal static Dictionary<string, LockStatusData> ForLocks(
string wkPath,
Dictionary<WorkspaceTreeNode, LockInfo> lockInfoByNode)
{
Dictionary<string, LockStatusData> result =
BuildPathDictionary.ForPlatform<LockStatusData>();
LockOwnerNameResolver nameResolver = new LockOwnerNameResolver();
foreach (WorkspaceTreeNode node in lockInfoByNode.Keys)
{
LockStatusData lockStatusData = BuildLockStatusData(
node, lockInfoByNode[node], nameResolver);
string nodeWkPath = WorkspacePath.GetWorkspacePathFromCmPath(
wkPath,
WorkspaceNodeOperations.GetCmPath(node),
PathHelper.GetDirectorySeparatorChar(wkPath));
result.Add(nodeWkPath, lockStatusData);
}
return result;
}
static LockStatusData BuildLockStatusData(
WorkspaceTreeNode node,
LockInfo lockInfo,
LockOwnerNameResolver nameResolver)
{
AssetStatus status = CheckWorkspaceTreeNodeStatus.IsCheckedOut(node) ?
AssetStatus.Locked : AssetStatus.LockedRemote;
return new LockStatusData(
status,
nameResolver.GetSeidName(lockInfo.SEIDData),
LockWkInfo.GetWkCleanName(lockInfo));
}
}
CancelToken mCurrentCancelToken = new CancelToken();
Dictionary<string, LockStatusData> mStatusByPathCache;
readonly WorkspaceInfo mWkInfo;
readonly Action mRepaintProjectWindow;
static object mLock = new object();
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment