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 { } } }