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
{
///
/// The DataFileAnalyzer
///
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 m_allDataFile = new ObservableCollection();
private ObservableCollection m_currentFileList = new ObservableCollection();
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 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(() => SelectedFile, file => SelectedFile = file, file => file.FilePath);
PerforceManager.Instance.InitPerforceWrapper(ProjectManager.Instance.CurrentProject);
}
///
/// The file library
///
public FileLibrary Library { get { return m_fileLibrary; } }
///
/// The folder containing the data files
///
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);
}
///
/// The last state of the file updater we got
///
public FileUpdaterState UpdaterState
{
get { return m_updaterState; }
}
///
/// All the data files
///
public ObservableCollection AllDataFiles
{
get { return m_allDataFile; }
set { m_allDataFile = value; }
}
///
/// The data files to display
///
public ObservableCollection CurrentDataFiles
{
get { return m_currentFileList; }
set { m_currentFileList = value; }
}
///
/// The string used to filter the file list
///
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;
}
}
///
/// Status of the library updater
///
public FileUpdaterStatus Status { get { return m_status; } }
///
/// Time spent by the library resolving references
///
public TimeSpan ResolveDependenciesDuration { get; private set; }
///
/// Library file count
///
public Int32 FileCount { get; private set; }
public MenuItemDataCollection DependenciesContextMenu
{
get { return m_dependenciesContextMenu; }
}
public MenuItemDataCollection FileListContextMenu
{
get { return m_fileListContextMenu; }
}
///
/// Selected file.
///
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 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 FileHistory
{
get { return m_fileHistory; }
}
public bool IsPerforceEnabled
{
get { return m_isPerforceEnabled; }
set
{
if (m_isPerforceEnabled != value)
{
m_isPerforceEnabled = value;
RaisePropertyChanged("IsPerforceEnabled");
}
}
}
///
/// Read the command line
///
public void ReadCommandLine(UAF.CommandLineInterpreter _commandLine)
{
if (_commandLine.HasOption("DevMode"))
{
m_fileLibrary.DevMode = (int.Parse(_commandLine["DevMode"]) != 0);
}
}
///
/// Close
///
public void Close()
{
m_fileLibrary.Stop();
}
///
/// The file library has been modified
///
private void OnFileLibraryModified(object sender, EventArgs e)
{
// Do nothing in real-time
}
///
/// Update the file list from the file library
///
public void RefreshFileList()
{
m_allDataFile = new ObservableCollection();
foreach (KeyValuePair kvp in m_fileLibrary.Files)
{
m_allDataFile.Add(kvp.Value);
}
UpdateCurrentFileList();
}
///
/// Update the displayed list of file from the file list and the filters
///
private void UpdateCurrentFileList()
{
if (IsFilterActive)
{
m_currentFileList = new ObservableCollection();
foreach (DataFile file in m_allDataFile)
{
if(MatchFilter(file))
m_currentFileList.Add(file);
}
}
else
{
m_currentFileList = new ObservableCollection(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 keyValue in ((PerforceFStatRequest)request).FileInfos)
{
string normalizedPath = PathUtils.NormalizeAndRemoveSubst(keyValue.Value.ClientFile);
normalizedPath = PathUtils.Normalize(normalizedPath, DataFolder);
m_fileLibrary.Updater.AskForPerforceUpdate(normalizedPath, keyValue.Value);
}
}
}
///
/// Checks if a file match the selected filters.
///
/// The file to check.
/// True if the file matches the filters, false otherwise.
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;
}
///
/// Resets the filter.
///
public void ResetFilter()
{
FilterString = "";
DisplayMissingFiles = true;
DisplayFilesWithMissingDependencies = true;
DisplayNonComputedFiles = true;
DisplayValidFiles = true;
}
///
/// Get the updater state
///
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 { }
}
}