Files
LuaCsForBarotraumaEP/Libraries/MonoGame.Framework/Src/Tools/Pipeline/Common/PipelineController.cs
2019-06-25 16:00:44 +03:00

1056 lines
34 KiB
C#

// MonoGame - Copyright (C) The MonoGame Team
// This file is subject to the terms and conditions defined in
// file 'LICENSE.txt', which is part of this source code package.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using MGCB;
using PathHelper = MonoGame.Framework.Content.Pipeline.Builder.PathHelper;
namespace MonoGame.Tools.Pipeline
{
public partial class PipelineController : IController
{
public static PipelineController Instance;
private PipelineProject _project;
private Task _buildTask;
private Process _buildProcess;
private readonly List<ContentItemTemplate> _templateItems;
private static readonly string [] _mgcbSearchPaths = new []
{
"/Library/Frameworks/MonoGame.framework/Current/Tools",
#if DEBUG
"../../../../../MGCB/bin/Windows/AnyCPU/Debug",
Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "../../../../../MGCB/bin/Windows/AnyCPU/Debug"),
#else
"../../../../../MGCB/bin/Windows/AnyCPU/Release",
Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "../../../../../MGCB/bin/Windows/AnyCPU/Release"),
#endif
"../MGCB",
Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "../MGCB"),
Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
"",
};
public IEnumerable<ContentItemTemplate> Templates
{
get { return _templateItems; }
}
public PipelineProject ProjectItem
{
get
{
return _project;
}
}
public string ProjectLocation
{
get
{
var ret = _project.Location;
if (!_project.Location.EndsWith(Path.DirectorySeparatorChar.ToString()))
ret += Path.DirectorySeparatorChar;
return ret;
}
}
public string ProjectOutputDir
{
get { return _project.OutputDir; }
}
public List<IProjectItem> SelectedItems { get; private set; }
public IProjectItem SelectedItem { get; private set; }
public bool ProjectOpen { get; private set; }
public bool ProjectDirty { get; set; }
public bool ProjectBuilding
{
get
{
return _buildTask != null && !_buildTask.IsCompleted;
}
}
public IView View { get; private set; }
public event Action OnProjectLoading;
public event Action OnProjectLoaded;
private PipelineController(IView view)
{
Instance = this;
SelectedItems = new List<IProjectItem>();
_actionStack = new ActionStack(this);
View = view;
View.Attach(this);
ProjectOpen = false;
_templateItems = new List<ContentItemTemplate>();
var root = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
if (Directory.Exists(Path.Combine (root, "..", "Resources", "Templates")))
{
root = Path.Combine(root, "..", "Resources");
}
LoadTemplates(Path.Combine(root, "Templates"));
UpdateMenu();
view.UpdateRecentList(PipelineSettings.Default.ProjectHistory);
}
public static PipelineController Create(IView view)
{
return new PipelineController(view);
}
public void OnProjectModified()
{
Debug.Assert(ProjectOpen, "OnProjectModified called with no project open?");
ProjectDirty = true;
}
public void OnReferencesModified()
{
Debug.Assert(ProjectOpen, "OnReferencesModified called with no project open?");
ProjectDirty = true;
ResolveTypes();
View.UpdateProperties();
}
public void OnItemModified(ContentItem contentItem)
{
Debug.Assert(ProjectOpen, "OnItemModified called with no project open?");
ProjectDirty = true;
View.BeginTreeUpdate();
View.UpdateTreeItem(contentItem);
View.EndTreeUpdate();
}
public void NewProject()
{
// Make sure we give the user a chance to
// save the project if they need too.
if (!AskSaveProject())
return;
// A project needs a root directory or it is impossible to resolve relative paths.
// So we need the user to choose that location even though the project has not
// yet actually been saved to disk.
var projectFilePath = Environment.CurrentDirectory;
if (!View.AskSaveName(ref projectFilePath, "New Project"))
return;
CloseProject();
if (OnProjectLoading != null)
OnProjectLoading();
// Clear existing project data, initialize to a new blank project.
_actionStack.Clear();
_project = new PipelineProject();
PipelineTypes.Load(_project);
// Save the new project.
_project.OriginalPath = projectFilePath;
ProjectOpen = true;
ProjectDirty = true;
UpdateTree();
if (OnProjectLoaded != null)
OnProjectLoaded();
UpdateMenu();
}
public void ImportProject()
{
// Make sure we give the user a chance to
// save the project if they need too.
if (!AskSaveProject())
return;
string projectFilePath;
if (!View.AskImportProject(out projectFilePath))
return;
CloseProject();
if (OnProjectLoading != null)
OnProjectLoading();
#if SHIPPING
try
#endif
{
_actionStack.Clear();
_project = new PipelineProject();
var parser = new PipelineProjectParser(this, _project);
parser.ImportProject(projectFilePath);
ResolveTypes();
ProjectOpen = true;
ProjectDirty = true;
}
#if SHIPPING
catch (Exception e)
{
View.ShowError("Open Project", "Failed to open project!");
return;
}
#endif
UpdateTree();
View.UpdateTreeItem(_project);
if (OnProjectLoaded != null)
OnProjectLoaded();
UpdateMenu();
}
public void OpenProject()
{
// Make sure we give the user a chance to
// save the project if they need too.
if (!AskSaveProject())
return;
string projectFilePath;
if (!View.AskOpenProject(out projectFilePath))
return;
OpenProject(projectFilePath);
}
public void OpenProject(string projectFilePath)
{
CloseProject();
if (OnProjectLoading != null)
OnProjectLoading();
var errortext = "Failed to open the project due to an unknown error.";
try
{
_actionStack.Clear();
_project = new PipelineProject();
var parser = new PipelineProjectParser(this, _project);
var errorCallback = new MGBuildParser.ErrorCallback((msg, args) =>
{
errortext = string.Format(msg, args);
throw new Exception();
});
parser.OpenProject(projectFilePath, errorCallback);
ResolveTypes();
ProjectOpen = true;
ProjectDirty = false;
PipelineSettings.Default.AddProjectHistory(projectFilePath);
PipelineSettings.Default.StartupProject = projectFilePath;
PipelineSettings.Default.Save();
View.UpdateRecentList(PipelineSettings.Default.ProjectHistory);
}
catch (Exception)
{
View.ShowError("Error Opening Project", Path.GetFileName(projectFilePath) + ": " + errortext);
return;
}
UpdateTree();
View.UpdateTreeItem(_project);
if (OnProjectLoaded != null)
OnProjectLoaded();
UpdateMenu();
}
public void ClearRecentList()
{
PipelineSettings.Default.ProjectHistory.Clear();
PipelineSettings.Default.Save();
View.UpdateRecentList(PipelineSettings.Default.ProjectHistory);
}
public void CloseProject()
{
if (!ProjectOpen)
return;
// Make sure we give the user a chance to
// save the project if they need too.
if (!AskSaveProject())
return;
ProjectOpen = false;
ProjectDirty = false;
_project = null;
_actionStack.Clear();
View.OutputClear();
PipelineSettings.Default.StartupProject = null;
PipelineSettings.Default.Save();
UpdateTree();
UpdateMenu();
}
public bool MoveProject(string newname)
{
string opath = _project.OriginalPath;
string ext = Path.GetExtension(opath);
PipelineSettings.Default.ProjectHistory.Remove(opath);
try
{
File.Delete(_project.OriginalPath);
}
catch {
View.ShowError("Error", "Could not delete old project file.");
return false;
}
_project.OriginalPath = Path.GetDirectoryName(opath) + Path.DirectorySeparatorChar + newname + ext;
if (!SaveProject(false))
{
_project.OriginalPath = opath;
SaveProject(false);
View.ShowError("Error", "Could not save the new project file.");
return false;
}
View.SetTreeRoot(_project);
return true;
}
public bool SaveProject(bool saveAs)
{
// Do we need file name?
if (saveAs || string.IsNullOrEmpty(_project.OriginalPath))
{
string newFilePath = _project.OriginalPath;
if (!View.AskSaveName(ref newFilePath, null))
return false;
_project.OriginalPath = newFilePath;
View.SetTreeRoot(_project);
}
// Do the save.
ProjectDirty = false;
var parser = new PipelineProjectParser(this, _project);
parser.SaveProject();
// Note: This is where a project loaded via 'new project' or 'import project'
// get recorded into PipelineSettings because up until this point they did not
// exist as files on disk.
PipelineSettings.Default.AddProjectHistory(_project.OriginalPath);
PipelineSettings.Default.StartupProject = _project.OriginalPath;
PipelineSettings.Default.Save();
View.UpdateRecentList(PipelineSettings.Default.ProjectHistory);
UpdateMenu();
return true;
}
public void Build(bool rebuild)
{
var commands = string.Format("/@:\"{0}\" {1}", _project.OriginalPath, rebuild ? "/rebuild" : string.Empty);
if (PipelineSettings.Default.DebugMode)
commands += " /launchdebugger";
BuildCommand(commands);
}
private IEnumerable<IProjectItem> GetItems(IProjectItem dir)
{
foreach (var item in _project.ContentItems)
if (item.OriginalPath.StartsWith(dir.OriginalPath + "/"))
yield return item;
}
public void RebuildItems()
{
var items = new List<IProjectItem>();
// If the project itself was selected, just
// rebuild the entire project
if (items.Contains(_project))
{
Build(true);
return;
}
// Convert selected DirectoryItems into ContentItems
foreach (var item in SelectedItems)
{
if (item is ContentItem)
{
if (!items.Contains(item))
items.Add(item);
continue;
}
foreach (var subitem in GetItems(item))
if (!items.Contains(subitem))
items.Add(subitem);
}
// Create a unique file within the same folder as
// the normal project to store this incremental build.
var uniqueName = Guid.NewGuid().ToString();
var tempPath = Path.Combine(Path.GetDirectoryName(_project.OriginalPath), uniqueName);
// Write the incremental project file limiting the
// content to just the files we want to rebuild.
using (var io = File.CreateText(tempPath))
{
var parser = new PipelineProjectParser(this, _project);
parser.SaveProject(io, (i) => !items.Contains(i));
}
// Run the build the command.
var commands = string.Format("/@:\"{0}\" /rebuild /incremental", tempPath);
if (PipelineSettings.Default.DebugMode)
commands += " /launchdebugger";
BuildCommand(commands);
// Cleanup the temp file once we're done.
_buildTask.ContinueWith((e) => File.Delete(tempPath));
}
private void BuildCommand(string commands)
{
Debug.Assert(_buildTask == null || _buildTask.IsCompleted, "The previous build wasn't completed!");
if (ProjectDirty)
SaveProject(false);
View.OutputClear();
_buildTask = Task.Factory.StartNew(() => DoBuild(commands));
_buildTask.ContinueWith((e) => View.Invoke(UpdateMenu));
UpdateMenu();
}
public void Clean()
{
Debug.Assert(_buildTask == null || _buildTask.IsCompleted, "The previous build wasn't completed!");
// Make sure we save first!
if (!AskSaveProject())
return;
View.OutputClear();
var commands = string.Format("/clean /intermediateDir:\"{0}\" /outputDir:\"{1}\"", _project.IntermediateDir, _project.OutputDir);
if (PipelineSettings.Default.DebugMode)
commands += " /launchdebugger";
_buildTask = Task.Factory.StartNew(() => DoBuild(commands));
_buildTask.ContinueWith((e) => View.Invoke(UpdateMenu));
UpdateMenu();
}
private string FindMGCB()
{
foreach (var root in _mgcbSearchPaths)
{
var mgcbPath = Path.Combine(root, "MGCB.exe");
if (File.Exists(mgcbPath))
return Path.GetFullPath(mgcbPath);
}
throw new FileNotFoundException("MGCB.exe is not in the search path!");
}
private void DoBuild(string commands)
{
Encoding encoding;
try {
encoding = Encoding.GetEncoding(CultureInfo.CurrentCulture.TextInfo.OEMCodePage);
} catch (NotSupportedException) {
encoding = Encoding.UTF8;
}
var currentDir = Environment.CurrentDirectory;
try
{
// Prepare the process.
_buildProcess = View.CreateProcess(FindMGCB(), commands);
_buildProcess.StartInfo.WorkingDirectory = Path.GetDirectoryName(_project.OriginalPath);
_buildProcess.StartInfo.CreateNoWindow = true;
_buildProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
_buildProcess.StartInfo.UseShellExecute = false;
_buildProcess.StartInfo.RedirectStandardOutput = true;
_buildProcess.StartInfo.StandardOutputEncoding = encoding;
_buildProcess.OutputDataReceived += (sender, args) => View.OutputAppend(args.Data);
// Fire off the process.
Environment.CurrentDirectory = _buildProcess.StartInfo.WorkingDirectory;
_buildProcess.Start();
_buildProcess.BeginOutputReadLine();
_buildProcess.WaitForExit();
}
catch (Exception ex)
{
// If we got a message assume it has everything the user needs to know.
if (!string.IsNullOrEmpty(ex.Message))
View.OutputAppend("Build failed: " + ex.Message);
else
{
// Else we need to get verbose.
View.OutputAppend("Build failed:" + Environment.NewLine);
View.OutputAppend(ex.ToString());
}
}
finally {
Environment.CurrentDirectory = currentDir;
}
// Clear the process pointer, so that cancel
// can run after we've already finished.
lock (_buildTask)
_buildProcess = null;
}
public void CancelBuild()
{
if (_buildTask == null || _buildTask.IsCompleted)
return;
lock (_buildTask)
{
if (_buildProcess == null)
return;
_buildProcess.Kill();
_buildProcess = null;
View.OutputAppend("Build terminated!");
}
}
/// <summary>
/// Prompt the user if they wish to save the project.
/// Save it if yes is chosen.
/// Return true if yes or no is chosen.
/// Return false if cancel is chosen.
/// </summary>
private bool AskSaveProject()
{
// If the project is not dirty or open
// then we can simply skip it.
if (!ProjectDirty)
return true;
// Ask the user if they want to save or cancel.
var result = View.AskSaveOrCancel();
// Did we cancel the exit?
if (result == AskResult.Cancel)
return false;
// Did we want to skip saving?
if (result == AskResult.No)
return true;
return SaveProject(false);
}
private void UpdateTree()
{
View.BeginTreeUpdate();
if (_project == null || string.IsNullOrEmpty(_project.OriginalPath))
View.SetTreeRoot(null);
else
{
View.SetTreeRoot(_project);
foreach (var item in _project.ContentItems)
View.AddTreeItem(item);
}
View.EndTreeUpdate();
}
public bool Exit()
{
// Can't exit if we're building!
if (ProjectBuilding)
{
View.ShowMessage("You cannot exit while the project is building!");
return false;
}
// Make sure we give the user a chance to
// save the project if they need too.
var ret = AskSaveProject();
if (ret)
PipelineSettings.Default.Save();
return ret;
}
public void DragDrop(string initialDirectory, string[] folders, string[] files)
{
initialDirectory = GetFullPath(initialDirectory);
//IncludeFolder(initialDirectory, folders);
//Include(initialDirectory, files);
}
private string GetCurrentPath()
{
if (SelectedItem is DirectoryItem)
return SelectedItem.OriginalPath;
if (SelectedItem is ContentItem)
return SelectedItem.Location;
return string.Empty;
}
public void Include()
{
var path = GetFullPath(GetCurrentPath());
List<string> files;
if (View.ChooseContentFile(path, out files))
{
var items = new List<IncludeItem>();
var repeat = false;
var action = IncludeType.Copy;
if (!IncludeFiles(items, path, files.ToArray(), ref repeat, ref action))
return;
if (items.Count == 0)
return;
var includeaction = new IncludeAction(items);
if (includeaction.Do())
_actionStack.Add(includeaction);
}
}
public void IncludeFolder()
{
var path = GetFullPath(GetCurrentPath());
string folder;
if (!View.ChooseContentFolder(path, out folder))
return;
var items = new List<IncludeItem>();
var repeat = false;
var action = IncludeType.Copy;
if (!IncludeDirectory(items, path, folder, ref repeat, ref action))
return;
if (items.Count == 0)
return;
var includeaction = new IncludeAction(items);
if (includeaction.Do())
_actionStack.Add(includeaction);
}
private bool IncludeDirectory(List<IncludeItem> items, string initialDirectory, string folder, ref bool repeat, ref IncludeType action)
{
var relative = Util.GetRelativePath(initialDirectory, ProjectLocation);
if (!IncludeFiles(items, initialDirectory, Directory.GetFiles(folder), ref repeat, ref action))
return false;
foreach(var dir in Directory.GetDirectories(folder))
{
var dirname = Path.GetFileName(dir);
var initdir = Path.Combine(initialDirectory, dirname);
var diritem = new IncludeItem
{
SourcePath = initdir,
IsDirectory = true,
IncludeType = IncludeType.Create,
RelativeDestPath = Path.Combine(relative, dirname)
};
items.Add(diritem);
if (!IncludeDirectory(items, initdir, dir, ref repeat, ref action))
return false;
}
return true;
}
private bool IncludeFiles(List<IncludeItem> items, string initialDirectory, string[] files, ref bool repeat, ref IncludeType action)
{
var relative = Util.GetRelativePath(initialDirectory, ProjectLocation);
foreach (var file in files)
{
var item = new IncludeItem();
item.SourcePath = file;
if (file.StartsWith(ProjectLocation))
{
// If the file is in the same directory as the .mgcb file, just add it and skip showing file dialogs
item.RelativeDestPath = PathHelper.GetRelativePath(ProjectLocation, file);
item.IncludeType = IncludeType.Link;
}
else
{
item.RelativeDestPath = Path.Combine(relative, Path.GetFileName(file));
if (!repeat)
{
if (!View.CopyOrLinkFile(file, File.Exists(Path.Combine(ProjectLocation, item.RelativeDestPath)), out action, out repeat))
return false;
}
if (action == IncludeType.Skip)
continue;
if (action == IncludeType.Copy && File.Exists(Path.Combine(ProjectLocation, item.RelativeDestPath)))
item.IncludeType = IncludeType.Link;
else
item.IncludeType = action;
}
items.Add(item);
}
return true;
}
private List<string> GetFiles(string folder)
{
List<string> ret = new List<string>();
string[] directories = Directory.GetDirectories(folder);
foreach (string d in directories)
ret.AddRange(GetFiles(d));
ret.AddRange(Directory.GetFiles(folder));
return ret;
}
private List<string> GetDirectories(string folder)
{
List<string> ret = new List<string>();
string[] directories = Directory.GetDirectories(folder);
foreach (string d in directories)
{
ret.Add(d);
ret.AddRange(GetDirectories(d));
}
return ret;
}
public void Exclude(bool delete)
{
// We don't want to show a delete confirmation for any items outside the project folder
var filteredItems = new List<IProjectItem>(SelectedItems.Where(i => !i.OriginalPath.Contains("..")));
if (filteredItems.Count > 0 && delete && !View.ShowDeleteDialog(filteredItems))
return;
// Still need to pass all items to the Exclude action so it can remove them from the view.
// Filtering is done internally so it only deletes files in the project folder
var action = new ExcludeAction(this, SelectedItems, delete);
if(action.Do())
_actionStack.Add(action);
UpdateMenu();
}
public void NewItem()
{
string name;
ContentItemTemplate template;
if (!View.ChooseItemTemplate(GetFullPath(GetCurrentPath()), out template, out name))
return;
var destpath = Path.Combine(GetCurrentPath(), name);
var action = new IncludeAction(
new IncludeItem {
SourcePath = GetFullPath(destpath),
RelativeDestPath = destpath,
IncludeType = IncludeType.Create,
ItemTemplate = template
}
);
if(action.Do())
_actionStack.Add(action);
}
public void NewFolder()
{
string name;
if (!View.ShowEditDialog("New Folder", "Folder Name:", "", true, out name))
return;
var action = new IncludeAction(new IncludeItem {
SourcePath = Path.Combine(GetFullPath(GetCurrentPath()), name),
RelativeDestPath = Path.Combine(GetCurrentPath(), name),
IncludeType = IncludeType.Create,
IsDirectory = true
});
if (action.Do())
_actionStack.Add(action);
}
public void Rename()
{
string name;
if (SelectedItem == null || !View.ShowEditDialog("Rename Item", "New Name:", Path.GetFileName(SelectedItem.DestinationPath), true, out name))
return;
var action = new MoveAction(SelectedItem, name);
if (action.Do())
_actionStack.Add(action);
}
public void AddAction(IProjectAction action)
{
_actionStack.Add(action);
if (!ProjectDirty)
{
ProjectDirty = true;
UpdateMenu();
}
}
public IProjectItem GetItem(string originalPath)
{
if (_project.OriginalPath.Equals(originalPath, StringComparison.OrdinalIgnoreCase))
return _project;
foreach (var i in _project.ContentItems)
{
if (string.Equals(i.OriginalPath, originalPath, StringComparison.OrdinalIgnoreCase))
{
return i;
}
}
return null;
}
public void CopyAssetPath()
{
var item = SelectedItem as ContentItem;
if (item != null)
{
var path = item.OriginalPath;
path = path.Remove(path.Length - Path.GetExtension(path).Length);
path = path.Replace('\\', '/');
View.SetClipboard(path);
}
}
#region Undo, Redo
private readonly ActionStack _actionStack;
public bool CanUndo { get { return _actionStack.CanUndo; } }
public bool CanRedo { get { return _actionStack.CanRedo; } }
public void Undo()
{
_actionStack.Undo();
}
public void Redo()
{
_actionStack.Redo();
}
#endregion
private void ResolveTypes()
{
PipelineTypes.Load(_project);
foreach (var i in _project.ContentItems)
{
i.Observer = this;
i.ResolveTypes();
}
View.UpdateProperties();
LoadTemplates(Path.Combine(_project.Location, "MGTemplates"));
}
private void LoadTemplates(string path)
{
if (!Directory.Exists(path))
return;
var files = Directory.GetFiles(path, "*.template", SearchOption.AllDirectories);
foreach (var f in files)
{
var lines = File.ReadAllLines(f);
if (lines.Length != 5)
throw new Exception("Invalid template");
var item = new ContentItemTemplate()
{
Label = lines[0],
Icon = lines[1],
ImporterName = lines[2],
ProcessorName = lines[3],
TemplateFile = lines[4],
};
if (_templateItems.Any(i => i.Label == item.Label))
continue;
var fpath = Path.GetDirectoryName(f);
item.TemplateFile = Path.GetFullPath(Path.Combine(fpath, item.TemplateFile));
_templateItems.Add(item);
}
}
public string GetFullPath(string filePath)
{
if (_project == null || Path.IsPathRooted(filePath))
{
if (filePath.Length == 2 && filePath[0] != '/')
filePath += "\\";
return filePath;
}
filePath = filePath.Replace("/", Path.DirectorySeparatorChar.ToString());
if (filePath.StartsWith("\\"))
filePath = filePath.Substring(1);
return _project.Location + Path.DirectorySeparatorChar + filePath;
}
public string GetRelativePath(string path)
{
if (!ProjectOpen)
return path;
var dirUri = new Uri(ProjectLocation);
var fileUri = new Uri(path);
var relativeUri = dirUri.MakeRelativeUri(fileUri);
if (relativeUri == null)
return path;
return Uri.UnescapeDataString(relativeUri.ToString().Replace('/', Path.DirectorySeparatorChar));
}
public void SelectionChanged(List<IProjectItem> items)
{
SelectedItems = items;
if (items.Count < 2)
{
if (items.Count == 1)
SelectedItem = items[0];
else
SelectedItem = _project;
}
else
SelectedItem = null;
UpdateContextMenu();
View.UpdateCommands(info);
View.UpdateProperties();
}
MenuInfo info;
public void UpdateMenu()
{
var notBuilding = !ProjectBuilding;
var projectOpenAndNotBuilding = ProjectOpen && notBuilding;
info = new MenuInfo();
info.New = notBuilding;
info.Open = notBuilding;
info.Import = notBuilding;
info.Save = projectOpenAndNotBuilding;
info.SaveAs = projectOpenAndNotBuilding;
info.Close = projectOpenAndNotBuilding;
info.Exit = notBuilding;
info.Undo = _actionStack.CanUndo;
info.Redo = _actionStack.CanRedo;
info.Build = projectOpenAndNotBuilding;
info.Rebuild = projectOpenAndNotBuilding;
info.Clean = projectOpenAndNotBuilding;
info.Cancel = ProjectBuilding;
UpdateContextMenu();
View.UpdateCommands(info);
}
private void UpdateContextMenu()
{
var oneselected = SelectedItems.Count == 1;
var somethingselected = SelectedItems.Count > 0;
var exists = true;
info.OpenItem = exists && oneselected && SelectedItem is ContentItem;
info.OpenItemWith = exists && oneselected && !(SelectedItem is DirectoryItem);
info.OpenItemLocation = exists && oneselected;
info.OpenOutputItemLocation = exists && oneselected && SelectedItem is ContentItem;
info.CopyAssetPath = exists && oneselected && SelectedItem is ContentItem;
info.Add = (exists && oneselected && !(SelectedItem is ContentItem)) || !somethingselected && ProjectOpen;
info.Exclude = somethingselected && !SelectedItems.Contains(_project);
info.Rename = exists && oneselected && !(SelectedItem is PipelineProject);
info.Delete = exists && info.Exclude;
info.RebuildItem = exists && somethingselected && !ProjectBuilding;
}
}
}