568 lines
21 KiB
C#
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 { }
|
|
}
|
|
}
|