using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
using UnityEngine;
using Codice.Client.Common;
using Codice.CM.Common;
using Codice.Utils;
using PlasticGui;
using PlasticGui.CloudDrive.Workspaces;
using PlasticGui.WorkspaceWindow.Items;
using Unity.PlasticSCM.Editor.CloudDrive.ShareWorkspace;
using Unity.PlasticSCM.Editor.CloudDrive.Workspaces.DirectoryContent;
using Unity.PlasticSCM.Editor.UI;
using Unity.PlasticSCM.Editor.UI.Progress;
using Unity.PlasticSCM.Editor.UI.Tree;
using Unity.PlasticSCM.Editor.Views;
#if UNITY_6000_2_OR_NEWER
using TreeViewItem = UnityEditor.IMGUI.Controls.TreeViewItem<int>;
#endif
namespace Unity.PlasticSCM.Editor.CloudDrive.Workspaces.Tree
{
internal class CloudWorkspacesTreeView :
PlasticTreeView,
FillCloudWorkspaces.ICloudWorkspacesTree,
DirectoryContentPanel.ICloudWorkspacesTreeView,
ICloudWorkspacesTreeMenuOperations
{
internal float Width { get { return mLastValidWidth; } }
internal WorkspaceInfo WorkspaceToSelect { set { mWorkspaceToSelect = value; } }
internal CloudWorkspacesTreeView(
Action delayedSelectionChangedAction,
IProgressControls progressControls,
EditorWindow parentWindow) : base(showCustomBackground: false)
{
mDelayedSelectionChangedAction = delayedSelectionChangedAction;
mProgressControls = progressControls;
mParentWindow = parentWindow;
mDelayedSelectionRunner = new DelayedActionBySecondsRunner(
DelayedSelectionChanged, UnityConstants.SELECTION_DELAYED_INPUT_ACTION_INTERVAL);
mMenu = new CloudWorkspacesTreeViewMenu(this);
rowHeight = 16;
}
protected override bool CanChangeExpandedState(TreeViewItem item)
{
if (item is CloudWorkspacesLabelTreeViewItem)
return false;
return ((CloudWorkspacesTreeViewItem)item).IsExpandable;
}
protected override IList<TreeViewItem> BuildRows(TreeViewItem rootItem)
{
RegenerateRows(
mMyDrivesByWorkspace.Values.ToList(),
mDrivesSharedWithMeByWorkspace.Values.ToList(),
mTreeViewItemIds,
this,
rootItem,
mRows);
SelectWorkspaceIfNeeded();
if (GetSelectedNode() != null)
DelayedSelectionChanged();
return mRows;
}
protected override float GetCustomRowHeight(int row, TreeViewItem item)
{
if (item is CloudWorkspacesLabelTreeViewItem)
return 30f;
return base.GetCustomRowHeight(row, item);
}
public override void OnGUI(Rect rect)
{
if (Event.current.type == EventType.Repaint && mLastValidWidth != rect.width)
mLastValidWidth = rect.width;
base.OnGUI(rect);
if (!HasFocus())
return;
Event e = Event.current;
if (e.type != EventType.KeyDown)
return;
bool isProcessed = mMenu.ProcessKeyActionIfNeeded(e);
if (isProcessed)
e.Use();
}
protected override void ContextClickedItem(int id)
{
mMenu.Popup();
Repaint();
}
protected override void RowGUI(RowGUIArgs args)
{
if (args.item is CloudWorkspacesLabelTreeViewItem)
{
EditorGUI.LabelField(
args.rowRect,
((CloudWorkspacesLabelTreeViewItem)args.item).Label,
UnityStyles.Tree.BoldLabelWithMargin);
return;
}
if (args.item is CloudWorkspacesTreeViewItem)
{
CloudWorkspacesTreeViewItem item = (CloudWorkspacesTreeViewItem)args.item;
args.rowRect.width = mLastValidWidth;
WorkspaceContentTreeViewItemGUI(
args.rowRect,
this,
item,
GetProgressControlsDataForNode(
item.ExpandedTreeNode,
mProgressControlsByWorkspace),
args.selected,
args.focused);
return;
}
base.RowGUI(args);
}
protected override void SelectionChanged(IList<int> selectedIds)
{
List<int> selectedIdsToSet = new List<int>();
foreach (int selectedId in selectedIds)
{
ExpandedTreeNode selectedNode;
if (mTreeViewItemIds.TryGetItemById(selectedId, out selectedNode))
selectedIdsToSet.Add(selectedId);
}
SetSelection(selectedIdsToSet);
mDelayedSelectionRunner.Run();
}
protected override void ExpandedStateChanged()
{
IList<int> expandedIds = GetExpanded();
ReloadExpandedTreeNodes(
expandedIds.Except(mLastExpandedIds),
mTreeViewItemIds,
mMyDrivesByWorkspace,
mDrivesSharedWithMeByWorkspace);
mLastExpandedIds = expandedIds;
}
internal bool IsOperationRunning()
{
foreach (ProgressControlsForViews progressControls
in mProgressControlsByWorkspace.Values)
{
if (progressControls.IsOperationRunning())
return true;
}
return false;
}
internal ProgressControlsForViews GetWorkspaceProgressControls(WorkspaceInfo wkInfo)
{
ProgressControlsForViews result;
if (mProgressControlsByWorkspace.TryGetValue(wkInfo, out result))
return result;
mProgressControlsByWorkspace[wkInfo] = new ProgressControlsForViews();
return mProgressControlsByWorkspace[wkInfo];
}
internal WorkspaceInfo GetSelectedWorkspaceInfo()
{
ExpandedTreeNode selectedNode = GetSelectedNode();
if (selectedNode == null)
return null;
return selectedNode.WkInfo;
}
internal ExpandedTreeNode GetSelectedNode()
{
IList<int> selectedIds = GetSelection();
if (selectedIds.Count != 1)
return null;
int selectedId = selectedIds[0];
ExpandedTreeNode result;
if (!mTreeViewItemIds.TryGetItemById(selectedId, out result))
return null;
return result;
}
internal List<ExpandedTreeNode> GetSelectedNodes()
{
List<ExpandedTreeNode> result = new List<ExpandedTreeNode>();
IList<int> selectedIds = GetSelection();
foreach (int selectedId in selectedIds)
{
ExpandedTreeNode selectedNode;
if (!mTreeViewItemIds.TryGetItemById(selectedId, out selectedNode))
continue;
result.Add(selectedNode);
}
return result;
}
internal void Update(EditorWindow parentWindow)
{
foreach (ProgressControlsForViews progressControls
in mProgressControlsByWorkspace.Values)
{
progressControls.UpdateProgress(parentWindow);
}
}
void FillCloudWorkspaces.ICloudWorkspacesTree.ExpandedTreesRetrieved(
List<ExpandedTree> myDrives,
List<ExpandedTree> drivesSharedWithMe)
{
mMyDrivesByWorkspace = BuildExpandedTrees(myDrives);
mDrivesSharedWithMeByWorkspace = BuildExpandedTrees(drivesSharedWithMe);
BuildProgressControls(
myDrives.Concat(drivesSharedWithMe),
mProgressControlsByWorkspace);
Reload();
}
void DirectoryContentPanel.ICloudWorkspacesTreeView.SelectNode(
string wkPath, string fullPath)
{
SelectNode(wkPath, fullPath);
}
int ICloudWorkspacesTreeMenuOperations.GetSelectedItemsCount()
{
return GetSelection().Count;
}
bool ICloudWorkspacesTreeMenuOperations.IsAnyNonRootItemSelected()
{
List<ExpandedTreeNode> selectedNodes = GetSelectedNodes();
foreach (ExpandedTreeNode node in selectedNodes)
{
if (!ExpandedTreeNode.IsRootNode(node))
return true;
}
return false;
}
bool ICloudWorkspacesTreeMenuOperations.IsAnySharedDriveSelected()
{
List<ExpandedTreeNode> selectedNodes = GetSelectedNodes();
foreach (ExpandedTreeNode node in selectedNodes)
{
if (mDrivesSharedWithMeByWorkspace.ContainsKey(node.WkInfo))
return true;
}
return false;
}
void ICloudWorkspacesTreeMenuOperations.OpenInExplorer()
{
List<ExpandedTreeNode> selectedNodes = GetSelectedNodes();
if (selectedNodes.Count != 1)
return;
FileSystemOperation.OpenInExplorer(selectedNodes[0].GetFullPath());
}
void ICloudWorkspacesTreeMenuOperations.OpenUnityCloud()
{
List<ExpandedTreeNode> selectedNodes = GetSelectedNodes();
if (selectedNodes.Count != 1)
return;
WorkspaceInfo workspaceInfo = selectedNodes[0].WkInfo;
RepositorySpec repSpec = PlasticGui.Plastic.API.GetRepositorySpec(workspaceInfo);
string cloudServer = CloudServer.GetOrganizationName(repSpec.Server);
RepositoryInfo repInfo = PlasticGui.Plastic.API.GetRepositoryInfo(repSpec);
OpenBrowser.TryOpen(UnityUrl.UnityDashboard.
CloudDrive.GetWorkspace(cloudServer, repInfo.GUID.ToString()));
}
void ICloudWorkspacesTreeMenuOperations.Delete()
{
List<ExpandedTreeNode> selectedNodes = GetSelectedNodes();
if (selectedNodes.Count != 1)
return;
WorkspaceInfo workspaceInfo = selectedNodes[0].WkInfo;
ProgressControlsForViews workspaceProgressControls =
GetWorkspaceProgressControls(workspaceInfo);
if (workspaceProgressControls.IsOperationRunning())
{
GuiMessage.ShowInformation(
PlasticLocalization.Name.OperationInProgress.GetString());
return;
}
string workspaceName = Path.GetFileName(workspaceInfo.ClientPath);
if (!DeleteWorkspaceDialog.UserConfirmsDelete(workspaceName, mParentWindow))
return;
DeleteWorkspaceOperation.DeleteWorkspace(
workspaceInfo,
mProgressControls,
Reload);
}
void ICloudWorkspacesTreeMenuOperations.Share()
{
List<ExpandedTreeNode> selectedNodes = GetSelectedNodes();
if (selectedNodes.Count != 1)
return;
WorkspaceInfo workspaceInfo = selectedNodes[0].WkInfo;
ProgressControlsForViews workspaceProgressControls =
GetWorkspaceProgressControls(workspaceInfo);
if (workspaceProgressControls.IsOperationRunning())
{
GuiMessage.ShowInformation(
PlasticLocalization.Name.OperationInProgress.GetString());
return;
}
List<SecurityMember> collaboratorsToAdd;
List<SecurityMember> collaboratorsToRemove;
if (!ShareWorkspaceDialog.ShareWorkspace(
workspaceInfo,
mParentWindow,
out collaboratorsToAdd,
out collaboratorsToRemove))
return;
WorkspaceShareOperations.ShareWorkspace(
workspaceInfo, collaboratorsToAdd, mProgressControls);
WorkspaceShareOperations.UnshareWorkspace(
workspaceInfo, collaboratorsToRemove, mProgressControls);
}
void ICloudWorkspacesTreeMenuOperations.UnshareWithMe()
{
List<ExpandedTreeNode> selectedNodes = GetSelectedNodes();
if (selectedNodes.Count != 1)
return;
WorkspaceInfo workspaceInfo = selectedNodes[0].WkInfo;
ProgressControlsForViews workspaceProgressControls =
GetWorkspaceProgressControls(workspaceInfo);
if (workspaceProgressControls.IsOperationRunning())
{
GuiMessage.ShowInformation(
PlasticLocalization.Name.OperationInProgress.GetString());
return;
}
if (!GuiMessage.ShowQuestion(
PlasticLocalization.Name.UnshareWithMeMenuItem.GetString(),
PlasticLocalization.Name.UnshareConfirmationMessage.GetString(),
PlasticLocalization.Name.UnshareButton.GetString()))
{
return;
}
WorkspaceShareOperations.UnshareWorkspaceWithMe(
workspaceInfo,
mProgressControls,
Reload);
}
void SelectWorkspaceIfNeeded()
{
if (mWorkspaceToSelect == null)
return;
int idToSelect;
if (!mTreeViewItemIds.TryGetItemIdByKey(mWorkspaceToSelect.ClientPath, out idToSelect))
return;
SetSelection(new List<int> { idToSelect });
mWorkspaceToSelect = null;
}
void DelayedSelectionChanged()
{
if (mIsSelectionChangedEventDisabled)
return;
IList<int> selectedIds = GetSelection();
if (selectedIds.Count == 1)
ReloadExpandedTreeNodeIfChildrenNotLoaded(
selectedIds[0],
mTreeViewItemIds,
mMyDrivesByWorkspace,
mDrivesSharedWithMeByWorkspace);
mDelayedSelectionChangedAction();
}
void SelectNode(string wkPath, string fullPath)
{
if (ExpandAncestors(wkPath, fullPath, mTreeViewItemIds, this))
Reload();
int idToSelect;
if (!mTreeViewItemIds.TryGetItemIdByKey(fullPath, out idToSelect))
return;
mIsSelectionChangedEventDisabled = true;
try
{
TableViewOperations.SetSelectionAndScroll(
this, new List<int> { idToSelect });
}
finally
{
mIsSelectionChangedEventDisabled = false;
}
DelayedSelectionChanged();
}
static void WorkspaceContentTreeViewItemGUI(
Rect rowRect,
CloudWorkspacesTreeView tree,
CloudWorkspacesTreeViewItem item,
ProgressControlsForViews.Data progressControlsData,
bool isSelected,
bool isFocused)
{
DrawTreeViewItem.ForIndentedItemWithIcon(
rowRect,
tree.rowHeight,
item.depth,
Path.GetFileName(item.ExpandedTreeNode.GetFullPath()),
string.Empty,
GetItemIcon(tree, item),
isSelected,
isFocused);
if (progressControlsData == null || !progressControlsData.IsOperationRunning)
return;
DoProgress(rowRect, progressControlsData);
}
static void DoProgress(
Rect rowRect,
ProgressControlsForViews.Data progressControlsData)
{
GUILayout.BeginArea(rowRect);
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
LoadingSpinner.OnGUI(
progressControlsData.ProgressPercent,
progressControlsData.ProgressMessage);
GUILayout.Space(2);
GUILayout.EndHorizontal();
GUILayout.EndArea();
}
static void ReloadExpandedTreeNodes(
IEnumerable<int> idsToReload,
TreeViewItemIds<ExpandedTreeNode> treeViewItemIds,
Dictionary<WorkspaceInfo, ExpandedTree> myDrivesByWorkspace,
Dictionary<WorkspaceInfo, ExpandedTree> drivesSharedWithMeByWorkspace)
{
foreach (int id in idsToReload)
{
ExpandedTreeNode node;
if (!treeViewItemIds.TryGetItemById(id, out node))
continue;
ExpandedTree expandedTree;
if (!myDrivesByWorkspace.TryGetValue(node.WkInfo, out expandedTree) &&
!drivesSharedWithMeByWorkspace.TryGetValue(node.WkInfo, out expandedTree))
continue;
expandedTree.ReloadNode(node);
}
}
static void ReloadExpandedTreeNodeIfChildrenNotLoaded(
int idToReload,
TreeViewItemIds<ExpandedTreeNode> treeViewItemIds,
Dictionary<WorkspaceInfo, ExpandedTree> myDrivesByWorkspace,
Dictionary<WorkspaceInfo, ExpandedTree> drivesSharedWithMeByWorkspace)
{
ExpandedTreeNode node;
if (!treeViewItemIds.TryGetItemById(idToReload, out node))
return;
if (node.GetChildren() != null)
return;
ExpandedTree expandedTree;
if (!myDrivesByWorkspace.TryGetValue(node.WkInfo, out expandedTree) &&
!drivesSharedWithMeByWorkspace.TryGetValue(node.WkInfo, out expandedTree))
return;
expandedTree.ReloadNode(node);
}
static bool ExpandAncestors(
string wkPath,
string nodePath,
TreeViewItemIds<ExpandedTreeNode> treeViewItemIds,
CloudWorkspacesTreeView treeView)
{
List<string> ancestors = GetAncestorsPaths(wkPath, nodePath);
bool result = false;
foreach (string ancestor in ancestors)
{
int ancestorId;
if (!treeViewItemIds.TryGetItemIdByKey(ancestor, out ancestorId))
continue;
if (treeView.IsExpanded(ancestorId))
continue;
treeView.SetExpanded(ancestorId, true);
result = true;
}
return result;
}
static void RegenerateRows(
List<ExpandedTree> myDrives,
List<ExpandedTree> drivesSharedWithMe,
TreeViewItemIds<ExpandedTreeNode> treeViewItemIds,
CloudWorkspacesTreeView treeView,
TreeViewItem rootItem,
List<TreeViewItem> rows)
{
ClearRows(rootItem, rows);
treeViewItemIds.ClearItems();
AddLabelRow(PlasticLocalization.Name.MyDrivesLabel.GetString(), treeViewItemIds, rows);
AddExpandedTrees(myDrives, treeViewItemIds, treeView, rootItem, rows);
AddLabelRow(
PlasticLocalization.Name.DrivesSharedWithMeLabel.GetString(), treeViewItemIds, rows);
AddExpandedTrees(drivesSharedWithMe, treeViewItemIds, treeView, rootItem, rows);
}
static void AddExpandedTrees(
List<ExpandedTree> expandedTrees,
TreeViewItemIds<ExpandedTreeNode> treeViewItemIds,
CloudWorkspacesTreeView treeView,
TreeViewItem rootItem,
List<TreeViewItem> rows)
{
foreach (ExpandedTree expandedTree in expandedTrees)
{
ExpandedTreeNode rootNode = expandedTree.GetRootNode();
if (rootNode == null)
continue;
AddNode(
rootNode,
expandedTree,
treeViewItemIds,
treeView,
rootItem,
rows);
}
}
static void AddLabelRow(
string label,
TreeViewItemIds<ExpandedTreeNode> treeViewItemIds,
List<TreeViewItem> rows)
{
int nodeId;
if (!treeViewItemIds.TryGetItemIdByKey(label, out nodeId))
nodeId = treeViewItemIds.AddItemIdByKey(label);
rows.Add(new CloudWorkspacesLabelTreeViewItem(nodeId, label));
}
static void AddNode(
ExpandedTreeNode node,
ExpandedTree expandedTree,
TreeViewItemIds<ExpandedTreeNode> treeViewItemIds,
CloudWorkspacesTreeView treeView,
TreeViewItem parentItem,
List<TreeViewItem> rows)
{
if (!ExpandedTreeNode.IsOnDisk(node))
return;
int nodeId;
if (!treeViewItemIds.TryGetItemIdByKey(node.GetFullPath(), out nodeId))
nodeId = treeViewItemIds.AddItemIdByKey(node.GetFullPath());
treeViewItemIds.AddItemById(nodeId, node);
CloudWorkspacesTreeViewItem viewItem =
new CloudWorkspacesTreeViewItem(
nodeId,
node,
Directory.GetDirectories(node.GetFullPath()).Length > 0,
parentItem.depth + 1);
rows.Add(viewItem);
parentItem.AddChild(viewItem);
if (!treeView.IsExpanded(nodeId))
return;
if (node.GetChildren() == null)
expandedTree.ReloadNode(node);
foreach (ExpandedTreeNode childNode in node.GetChildren())
{
if (!ExpandedTreeNode.IsDirectory(childNode))
continue;
AddNode(
childNode,
expandedTree,
treeViewItemIds,
treeView,
viewItem,
rows);
}
}
static void ClearRows(
TreeViewItem rootItem,
List<TreeViewItem> rows)
{
if (rootItem.hasChildren)
rootItem.children.Clear();
rows.Clear();
}
static Texture GetItemIcon(
CloudWorkspacesTreeView tree,
CloudWorkspacesTreeViewItem item)
{
if (item.parent.id == 0)
return Images.GetCloudWorkspaceIcon();
if (tree.IsExpanded(item.id))
return Images.GetFolderOpenedIcon();
return Images.GetFolderIcon();
}
static Dictionary<WorkspaceInfo, ExpandedTree> BuildExpandedTrees(
List<ExpandedTree> expandedTrees)
{
Dictionary<WorkspaceInfo, ExpandedTree> result =
new Dictionary<WorkspaceInfo, ExpandedTree>();
foreach (ExpandedTree expandedTree in expandedTrees)
result[expandedTree.WkInfo] = expandedTree;
return result;
}
static void BuildProgressControls(
IEnumerable<ExpandedTree> expandedTrees,
Dictionary<WorkspaceInfo, ProgressControlsForViews> progressControlsByWorkspace)
{
List<WorkspaceInfo> workspaceInfos = new List<WorkspaceInfo>();
foreach (ExpandedTree expandedTree in expandedTrees)
workspaceInfos.Add(expandedTree.GetRootNode().WkInfo);
for (int i = progressControlsByWorkspace.Keys.Count - 1; i >= 0; i--)
{
WorkspaceInfo wkInfo = progressControlsByWorkspace.Keys.ElementAt(i);
if (!workspaceInfos.Contains(wkInfo))
progressControlsByWorkspace.Remove(wkInfo);
}
foreach (WorkspaceInfo wkInfo in workspaceInfos)
{
if (progressControlsByWorkspace.ContainsKey(wkInfo))
continue;
progressControlsByWorkspace[wkInfo] = new ProgressControlsForViews();
}
}
static List<string> GetAncestorsPaths(
string wkPath,
string nodePath)
{
List<string> ancestors = new List<string>();
while (!PathHelper.IsSamePath(wkPath, nodePath))
{
nodePath = Directory.GetParent(nodePath).FullName;
ancestors.Add(nodePath);
}
ancestors.Reverse();
return ancestors;
}
static ProgressControlsForViews.Data GetProgressControlsDataForNode(
ExpandedTreeNode node,
Dictionary<WorkspaceInfo, ProgressControlsForViews> progressControlsByWorkspace)
{
if (!ExpandedTreeNode.IsRootNode(node))
return null;
ProgressControlsForViews progressControls;
if (!progressControlsByWorkspace.TryGetValue(
node.WkInfo,
out progressControls))
return null;
return progressControls.ProgressData;
}
float mLastValidWidth;
bool mIsSelectionChangedEventDisabled = false;
IEnumerable<int> mLastExpandedIds = new List<int>();
Dictionary<WorkspaceInfo, ExpandedTree> mMyDrivesByWorkspace =
new Dictionary<WorkspaceInfo, ExpandedTree>();
Dictionary<WorkspaceInfo, ExpandedTree> mDrivesSharedWithMeByWorkspace =
new Dictionary<WorkspaceInfo, ExpandedTree>();
WorkspaceInfo mWorkspaceToSelect;
readonly CloudWorkspacesTreeViewMenu mMenu;
readonly DelayedActionBySecondsRunner mDelayedSelectionRunner;
readonly Action mDelayedSelectionChangedAction;
readonly IProgressControls mProgressControls;
readonly EditorWindow mParentWindow;
readonly TreeViewItemIds<ExpandedTreeNode> mTreeViewItemIds =
new TreeViewItemIds<ExpandedTreeNode>();
readonly Dictionary<WorkspaceInfo, ProgressControlsForViews> mProgressControlsByWorkspace =
new Dictionary<WorkspaceInfo, ProgressControlsForViews>();
}
}