/*
Copyright (C) 2014-2019 de4dot@gmail.com
This file is part of dnSpy
dnSpy is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
dnSpy is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with dnSpy. If not, see .
*/
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Windows.Threading;
using dnSpy.AsmEditor.Properties;
using dnSpy.Contracts.Documents;
using dnSpy.Contracts.ETW;
using dnSpy.Contracts.Hex;
using dnSpy.Contracts.MVVM;
namespace dnSpy.AsmEditor.SaveModule {
sealed class SaveMultiModuleVM : INotifyPropertyChanged {
enum SaveState {
///
/// We haven't started saving yet
///
Loaded,
///
/// We're saving
///
Saving,
///
/// We're canceling
///
Canceling,
///
/// Final state even if some files weren't saved
///
Saved,
}
SaveState State {
get => saveState;
set {
if (value != saveState) {
saveState = value;
OnPropertyChanged(nameof(IsLoaded));
OnPropertyChanged(nameof(IsSaving));
OnPropertyChanged(nameof(IsCanceling));
OnPropertyChanged(nameof(IsSaved));
OnPropertyChanged(nameof(CanSave));
OnPropertyChanged(nameof(CanCancel));
OnPropertyChanged(nameof(CanClose));
OnPropertyChanged(nameof(IsSavingOrCanceling));
OnModuleSettingsSaved();
if (saveState == SaveState.Saved && OnSavedEvent is not null)
OnSavedEvent(this, EventArgs.Empty);
}
}
}
SaveState saveState = SaveState.Loaded;
public ICommand SaveCommand => new RelayCommand(a => Save(), a => CanExecuteSave);
public ICommand CancelSaveCommand => new RelayCommand(a => CancelSave(), a => IsSaving && moduleSaver is not null);
public event EventHandler? OnSavedEvent;
public bool IsLoaded => State == SaveState.Loaded;
public bool IsSaving => State == SaveState.Saving;
public bool IsCanceling => State == SaveState.Canceling;
public bool IsSaved => State == SaveState.Saved;
public bool CanSave => IsLoaded;
public bool CanCancel => IsLoaded || IsSaving;
public bool CanClose => !CanCancel;
public bool IsSavingOrCanceling => IsSaving || IsCanceling;
public bool CanExecuteSave => string.IsNullOrEmpty(CanExecuteSaveError);
public bool CanShowModuleErrors => IsLoaded && !CanExecuteSave;
public string? CanExecuteSaveError {
get {
if (!IsLoaded)
return "It's only possible to save when loaded";
for (int i = 0; i < Modules.Count; i++) {
var module = Modules[i];
if (module.HasError)
return string.Format(dnSpy_AsmEditor_Resources.SaveModules_FileHasErrors, i + 1, module.FileName.Trim() == string.Empty ? dnSpy_AsmEditor_Resources.EmptyFilename : module.FileName);
}
return null;
}
}
public void OnModuleSettingsSaved() {
OnPropertyChanged(nameof(CanExecuteSaveError));
OnPropertyChanged(nameof(CanExecuteSave));
OnPropertyChanged(nameof(CanShowModuleErrors));
}
public bool HasError {
get => hasError;
private set {
if (hasError != value) {
hasError = value;
OnPropertyChanged(nameof(HasError));
OnPropertyChanged(nameof(HasNoError));
}
}
}
bool hasError;
public bool HasNoError => !HasError;
public int ErrorCount {
get => errorCount;
set {
if (errorCount != value) {
errorCount = value;
OnPropertyChanged(nameof(ErrorCount));
HasError = errorCount != 0;
}
}
}
int errorCount;
public string LogMessage => logMessage.ToString();
StringBuilder logMessage = new StringBuilder();
public double ProgressMinimum => 0;
public double ProgressMaximum => 100;
public double TotalProgress {
get => totalProgress;
private set {
if (totalProgress != value) {
totalProgress = value;
OnPropertyChanged(nameof(TotalProgress));
}
}
}
double totalProgress = 0;
public double CurrentFileProgress {
get => currentFileProgress;
private set {
if (currentFileProgress != value) {
currentFileProgress = value;
OnPropertyChanged(nameof(CurrentFileProgress));
}
}
}
double currentFileProgress = 0;
public string CurrentFileName {
get => currentFileName;
set {
if (currentFileName != value) {
currentFileName = value;
OnPropertyChanged(nameof(CurrentFileName));
}
}
}
string currentFileName = string.Empty;
public ObservableCollection Modules { get; } = new ObservableCollection();
readonly IMmapDisabler mmapDisabler;
readonly Dispatcher dispatcher;
public SaveMultiModuleVM(IMmapDisabler mmapDisabler, Dispatcher dispatcher, SaveOptionsVM options) {
this.mmapDisabler = mmapDisabler;
this.dispatcher = dispatcher;
Modules.Add(options);
}
public SaveMultiModuleVM(IMmapDisabler mmapDisabler, Dispatcher dispatcher, IEnumerable