JD2022-TU1/main/tools/apps/DataFileAnalyzer/DataFileAnalyzerModel.cs

568 lines
21 KiB
C#

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Windows.Threading;
using System.Xml.Linq;
using UAF;
using UAF.Application.Models;
using UAF.WPF.Models.Menus;
using DataFileAnalyzer.Commands;
using DataFileAnalyzer.Properties;
using System.Windows.Input;
using UAF.Perforce;
using UAF.WPF.Models;
using UAF.Perforce.Requests;
using UAF.Interfaces.Perforce;
using System.ComponentModel.Composition;
using UAF.Interfaces.Logging;
namespace DataFileAnalyzer
{
/// <summary>
/// The DataFileAnalyzer
/// </summary>
public class DataFileAnalyzerModel : PaneViewModel
{
// FIXME : Requires some thinking. How to handle the focused element globally with an access limited to the model?
// Playing with GotFocus / LostFocus on each control does not look like a nice solution to me...
public enum FocusedView
{
FileView,
ReferencedByMeView,
ReferencingMeView,
Other,
}
private FileLibrary m_fileLibrary = new FileLibrary();
private ObservableCollection<DataFile> m_allDataFile = new ObservableCollection<DataFile>();
private ObservableCollection<DataFile> m_currentFileList = new ObservableCollection<DataFile>();
private FileUpdaterState m_updaterState = new FileUpdaterState();
private FileUpdaterStatus m_lastStatus = FileUpdaterStatus.STARTING;
private FileUpdaterStatus m_status = FileUpdaterStatus.STARTING;
private int m_lastFinishedOperationsCount = 0;
private DispatcherTimer m_updateTimer = null;
private string m_filterString = null;
private FilterSearchType m_filterSearchType = FilterSearchType.SearchInPath;
private bool m_displayValidFiles = true;
private bool m_displayMissingFiles = true;
private bool m_displayFilesWithMissingDependencies = true;
private bool m_displayNonComputedFiles = true;
private bool m_displayOrphanFiles = true;
private MenuItemDataCollection m_dependenciesContextMenu;
private MenuItemDataCollection m_fileListContextMenu;
private DataFile m_selectedFile = null;
private FocusedView m_focusedView = FocusedView.Other;
private HistoryNavigatorData<DataFile> m_fileHistory;
private bool m_perforceUpdateDone = false;
private ILog m_logger = null;
private bool m_isPerforceEnabled = true;
public DataFileAnalyzerModel()
{
m_logger = (ILog)UAF.Interfaces.Composition.Objects.GetInstance(typeof(ILog));
m_fileLibrary.Changed += OnFileLibraryModified;
m_dependenciesContextMenu = new MenuItemDataCollection();
m_dependenciesContextMenu.Add(new MenuItemData("Open File", null, new OpenFileCommand(), data: this, inputGesture: new KeyWithModifier(Key.Enter, ModifierKeys.Control)));
m_dependenciesContextMenu.Add(new MenuItemData("Show in Explorer", null, new GoToFileCommand(), data: this, inputGesture: new KeyWithModifier(Key.F, ModifierKeys.Shift)));
m_dependenciesContextMenu.Add(new SeparatorMenuItemData(null));
m_dependenciesContextMenu.Add(new MenuItemData("Copy Selected", null, new CopySelectedCommand(), iconTemplateName: "Icon_CopySelected", data: this, inputGesture: new KeyWithModifier(Key.C, ModifierKeys.Control)));
m_dependenciesContextMenu.Add(new MenuItemData("Copy All", null, new CopyAllCommand(), iconTemplateName: "Icon_CopyAll", data: this, inputGesture: new KeyWithModifier(Key.C, ModifierKeys.Control | ModifierKeys.Shift)));
m_dependenciesContextMenu.Add(new SeparatorMenuItemData(null));
m_dependenciesContextMenu.Add(new MenuItemData("Show History", null, new ShowHistoryCommand(), data: this, inputGesture: new KeyWithModifier(Key.T, ModifierKeys.Control)));
m_dependenciesContextMenu.Add(new SeparatorMenuItemData(null));
m_dependenciesContextMenu.Add(new MenuItemData("Open For Edit", null, new OpenForEditCommand(), data: this));
m_dependenciesContextMenu.Add(new MenuItemData("Revert", null, new RevertCommand(), data: this));
m_dependenciesContextMenu.Add(new MenuItemData("Open For Delete", null, new OpenForDeleteCommand(), data: this));
m_fileListContextMenu = new MenuItemDataCollection();
m_fileListContextMenu.Add(new MenuItemData("Open File", null, new OpenFileCommand(), data: this, inputGesture: new KeyWithModifier(Key.Enter, ModifierKeys.Control)));
m_fileListContextMenu.Add(new MenuItemData("Show in Explorer", null, new GoToFileCommand(), data: this, inputGesture: new KeyWithModifier(Key.F, ModifierKeys.Shift)));
m_fileListContextMenu.Add(new MenuItemData("Force Refresh", null, new ForceRefreshCommand(), data: this, inputGesture: new KeyWithModifier(Key.F5, ModifierKeys.Control)));
m_fileListContextMenu.Add(new SeparatorMenuItemData(null));
m_fileListContextMenu.Add(new MenuItemData("Show History", null, new ShowHistoryCommand(), data: this, inputGesture: new KeyWithModifier(Key.T, ModifierKeys.Control)));
m_fileListContextMenu.Add(new SeparatorMenuItemData(null));
m_fileListContextMenu.Add(new MenuItemData("Open For Edit", null, new OpenForEditCommand(), data: this));
m_fileListContextMenu.Add(new MenuItemData("Revert", null, new RevertCommand(), data: this));
m_fileListContextMenu.Add(new MenuItemData("Open For Delete", null, new OpenForDeleteCommand(), data: this));
m_fileHistory = new HistoryNavigatorData<DataFile>(() => SelectedFile, file => SelectedFile = file, file => file.FilePath);
PerforceManager.Instance.InitPerforceWrapper(ProjectManager.Instance.CurrentProject);
}
/// <summary>
/// The file library
/// </summary>
public FileLibrary Library { get { return m_fileLibrary; } }
/// <summary>
/// The folder containing the data files
/// </summary>
public string DataFolder
{
get { return DataPath.Value; }
}
public void StartScaning()
{
if (m_updateTimer != null)
{
m_updateTimer.Stop();
m_updateTimer = null;
}
// Change the data folder
if (m_fileLibrary.SetDataFolder(DataFolder))
{
//RaisePropertyChanged("DataFolder");
m_updateTimer = new DispatcherTimer();
m_updateTimer.Interval = TimeSpan.FromMilliseconds(500);
m_updateTimer.Tick += new EventHandler(GetUpdaterState);
m_updateTimer.Start();
}
else
{
// TODO
}
PerforceManager.Instance.InitPerforceWrapper(ProjectManager.Instance.CurrentProject);
}
/// <summary>
/// The last state of the file updater we got
/// </summary>
public FileUpdaterState UpdaterState
{
get { return m_updaterState; }
}
/// <summary>
/// All the data files
/// </summary>
public ObservableCollection<DataFile> AllDataFiles
{
get { return m_allDataFile; }
set { m_allDataFile = value; }
}
/// <summary>
/// The data files to display
/// </summary>
public ObservableCollection<DataFile> CurrentDataFiles
{
get { return m_currentFileList; }
set { m_currentFileList = value; }
}
/// <summary>
/// The string used to filter the file list
/// </summary>
public string FilterString
{
get { return m_filterString; }
set
{
m_filterString = value;
RaisePropertyChanged("FilterString");
UpdateCurrentFileList();
}
}
public FilterSearchType FilterType
{
get { return m_filterSearchType; }
set
{
m_filterSearchType = value;
RaisePropertyChanged("FilterType");
UpdateCurrentFileList();
}
}
public bool DisplayValidFiles
{
get { return m_displayValidFiles; }
set
{
if (m_displayValidFiles != value)
{
m_displayValidFiles = value;
RaisePropertyChanged("DisplayValidFiles");
UpdateCurrentFileList();
}
}
}
public bool DisplayMissingFiles
{
get { return m_displayMissingFiles; }
set
{
if (m_displayMissingFiles != value)
{
m_displayMissingFiles = value;
RaisePropertyChanged("DisplayMissingFiles");
UpdateCurrentFileList();
}
}
}
public bool DisplayFilesWithMissingDependencies
{
get { return m_displayFilesWithMissingDependencies; }
set
{
if (m_displayFilesWithMissingDependencies != value)
{
m_displayFilesWithMissingDependencies = value;
RaisePropertyChanged("DisplayFilesWithMissingDependencies");
UpdateCurrentFileList();
}
}
}
public bool DisplayNonComputedFiles
{
get { return m_displayNonComputedFiles; }
set
{
if (m_displayNonComputedFiles != value)
{
m_displayNonComputedFiles = value;
RaisePropertyChanged("DisplayNonComputedFiles");
UpdateCurrentFileList();
}
}
}
public bool DisplayOrphanFiles
{
get { return m_displayOrphanFiles; }
set
{
if (m_displayOrphanFiles != value)
{
m_displayOrphanFiles = value;
RaisePropertyChanged("DisplayOrphanFiles");
UpdateCurrentFileList();
}
}
}
public bool IsFilterActive
{
get
{
return !String.IsNullOrEmpty(FilterString) || !DisplayValidFiles || !DisplayMissingFiles || !DisplayNonComputedFiles || !DisplayFilesWithMissingDependencies;
}
}
/// <summary>
/// Status of the library updater
/// </summary>
public FileUpdaterStatus Status { get { return m_status; } }
/// <summary>
/// Time spent by the library resolving references
/// </summary>
public TimeSpan ResolveDependenciesDuration { get; private set; }
/// <summary>
/// Library file count
/// </summary>
public Int32 FileCount { get; private set; }
public MenuItemDataCollection DependenciesContextMenu
{
get { return m_dependenciesContextMenu; }
}
public MenuItemDataCollection FileListContextMenu
{
get { return m_fileListContextMenu; }
}
/// <summary>
/// Selected file.
/// </summary>
public DataFile SelectedFile
{
get { return m_selectedFile; }
set
{
if (m_selectedFile != value)
{
if (m_selectedFile != null)
m_fileHistory.Push(m_selectedFile);
m_selectedFile = value;
RaisePropertyChanged("SelectedFile");
}
}
}
public IEnumerable<DataFile> SelectedFiles
{
get
{
foreach (DataFile file in CurrentDataFiles)
{
if (file.IsSelected)
yield return file;
}
}
}
public FocusedView ActiveView
{
get { return m_focusedView; }
set { m_focusedView = value; }
}
public HistoryNavigatorData<DataFile> FileHistory
{
get { return m_fileHistory; }
}
public bool IsPerforceEnabled
{
get { return m_isPerforceEnabled; }
set
{
if (m_isPerforceEnabled != value)
{
m_isPerforceEnabled = value;
RaisePropertyChanged("IsPerforceEnabled");
}
}
}
/// <summary>
/// Read the command line
/// </summary>
public void ReadCommandLine(UAF.CommandLineInterpreter _commandLine)
{
if (_commandLine.HasOption("DevMode"))
{
m_fileLibrary.DevMode = (int.Parse(_commandLine["DevMode"]) != 0);
}
}
/// <summary>
/// Close
/// </summary>
public void Close()
{
m_fileLibrary.Stop();
}
/// <summary>
/// The file library has been modified
/// </summary>
private void OnFileLibraryModified(object sender, EventArgs e)
{
// Do nothing in real-time
}
/// <summary>
/// Update the file list from the file library
/// </summary>
public void RefreshFileList()
{
m_allDataFile = new ObservableCollection<DataFile>();
foreach (KeyValuePair<DataFileKey, DataFile> kvp in m_fileLibrary.Files)
{
m_allDataFile.Add(kvp.Value);
}
UpdateCurrentFileList();
}
/// <summary>
/// Update the displayed list of file from the file list and the filters
/// </summary>
private void UpdateCurrentFileList()
{
if (IsFilterActive)
{
m_currentFileList = new ObservableCollection<DataFile>();
foreach (DataFile file in m_allDataFile)
{
if(MatchFilter(file))
m_currentFileList.Add(file);
}
}
else
{
m_currentFileList = new ObservableCollection<DataFile>(m_allDataFile);
}
RaisePropertyChanged("CurrentDataFiles");
}
public void UpdatePerforceStatus()
{
if (IsPerforceEnabled && PerforceManager.Instance.IsWrapperReady)
{
PerforceFStatRequest newRequest = new PerforceFStatRequest(FStatRequestHandler);
newRequest.AddFolder(ProjectManager.Instance.CurrentProject.DataPath, false);
newRequest.ApplyFlags(PerforceFStatRequest.RestrictFlags.OnlyInHaveList);
PerforceManager.Instance.SendRequest(newRequest);
}
}
private void FStatRequestHandler(IPerforceRequest request)
{
if (request is PerforceFStatRequest)
{
foreach (KeyValuePair<string, Ubisoft.Perforce.Data.DepotFileInfo> keyValue in ((PerforceFStatRequest)request).FileInfos)
{
string normalizedPath = PathUtils.NormalizeAndRemoveSubst(keyValue.Value.ClientFile);
normalizedPath = PathUtils.Normalize(normalizedPath, DataFolder);
m_fileLibrary.Updater.AskForPerforceUpdate(normalizedPath, keyValue.Value);
}
}
}
/// <summary>
/// Checks if a file match the selected filters.
/// </summary>
/// <param name="file">The file to check. </param>
/// <returns>True if the file matches the filters, false otherwise. </returns>
public bool MatchFilter(DataFile file)
{
if (!DisplayMissingFiles && file.IsMissing)
return false;
if (!DisplayFilesWithMissingDependencies && file.HasMissingDependencies)
return false;
if (!DisplayNonComputedFiles && file.MustComputeDependencies)
return false;
if (!DisplayOrphanFiles && file.IsOrphan)
return false;
if (!DisplayValidFiles && (!file.IsMissing && !file.HasMissingDependencies && !file.MustComputeDependencies && !file.IsOrphan))
if (!String.IsNullOrEmpty(m_filterString))
{
string filterStringLower = m_filterString.ToLower();
switch (m_filterSearchType)
{
case FilterSearchType.SearchInPath:
if (!file.NormalizedPath.Contains(filterStringLower))
return false;
break;
case FilterSearchType.SearchInFileName:
{
string fileName = file.NormalizedPath.Substring(file.NormalizedPath.LastIndexOf('/') + 1);
if (!fileName.Contains(filterStringLower))
return false;
}
break;
case FilterSearchType.SearchInPathAndDisplayFiles:
{
int index = file.NormalizedPath.LastIndexOf(filterStringLower);
if (index == -1)
return false;
string afterSearch = file.NormalizedPath.Substring(index + filterStringLower.Length);
if (afterSearch.Count(c => c == '/') > 1)
return false;
}
break;
}
}
return true;
}
/// <summary>
/// Resets the filter.
/// </summary>
public void ResetFilter()
{
FilterString = "";
DisplayMissingFiles = true;
DisplayFilesWithMissingDependencies = true;
DisplayNonComputedFiles = true;
DisplayValidFiles = true;
}
/// <summary>
/// Get the updater state
/// </summary>
private void GetUpdaterState(object sender, EventArgs e)
{
m_fileLibrary.Updater.GetCurrentState(m_updaterState);
RaisePropertyChanged("UpdaterState");
ResolveDependenciesDuration = m_fileLibrary.ResolveDependenciesDuration;
RaisePropertyChanged("ResolveDependenciesDuration");
FileCount = m_fileLibrary.Files.Count;
RaisePropertyChanged("FileCount");
if (!m_fileLibrary.Running)
{
m_status = FileUpdaterStatus.STOPPED;
}
else if (m_fileLibrary.Paused)
{
m_status = FileUpdaterStatus.PAUSED;
}
else if (m_updaterState.Initializing)
{
m_status = FileUpdaterStatus.INITIALIZING;
}
else
{
Int32 totalCount = m_updaterState.PendingCount + m_updaterState.RunningCount + m_updaterState.WaitingUpdateCount;
if (totalCount != 0)
{
m_status = FileUpdaterStatus.RUNNING;
}
else
{
m_status = FileUpdaterStatus.READY;
if (m_lastStatus != FileUpdaterStatus.READY)
{
RefreshFileList();
if (!m_perforceUpdateDone)
{
UpdatePerforceStatus();
m_perforceUpdateDone = true;
}
}
else if (m_lastFinishedOperationsCount != m_updaterState.FinishedCount)
RefreshFileList();
}
}
m_lastStatus = m_status;
m_lastFinishedOperationsCount = m_updaterState.FinishedCount;
RaisePropertyChanged("Status");
}
public void OnLoadApplicationSettings(XElement _applicationSettingsRoot)
{
}
public void OnSaveApplicationSettings(XElement _applicationSettingsRoot)
{
Settings.Default.LastDataFolder = DataFolder;
Settings.Default.Save();
}
public enum FilterSearchType
{
SearchInPath,
SearchInPathAndDisplayFiles,
SearchInFileName,
}
[Export(typeof(ILogCategory))]
public class DataFileAnalyzerLogCategory : LogCategory { }
}
}