282 lines
10 KiB
C#
282 lines
10 KiB
C#
using CodeContextGenerator.Interfaces;
|
||
using CodeContextGenerator.Models;
|
||
using CodeContextGenerator.Services;
|
||
using CommunityToolkit.Mvvm.ComponentModel;
|
||
using CommunityToolkit.Mvvm.Input;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Threading;
|
||
using System.Threading.Tasks;
|
||
using System.Windows;
|
||
|
||
namespace CodeContextGenerator.ViewModels
|
||
{
|
||
public partial class MainViewModel : ObservableObject
|
||
{
|
||
private readonly IProjectLoaderService _projectLoaderService;
|
||
private readonly IFileScannerService _fileScannerService;
|
||
private readonly IContextFileGenerator _contextFileGenerator;
|
||
private readonly IUIService _uiService;
|
||
private readonly ISettingsService _settingsService;
|
||
|
||
private CancellationTokenSource _cancellationTokenSource;
|
||
|
||
[ObservableProperty]
|
||
private FileItem rootDirectory;
|
||
|
||
[ObservableProperty]
|
||
private string selectedProjectPath;
|
||
|
||
[ObservableProperty]
|
||
private bool isProcessing;
|
||
|
||
[ObservableProperty]
|
||
private int progressValue;
|
||
|
||
[ObservableProperty]
|
||
private string progressText;
|
||
|
||
[ObservableProperty]
|
||
private bool isProjectLoaded;
|
||
|
||
// Явное объявление команд для гарантии их создания
|
||
public IRelayCommand SelectProjectCommand { get; }
|
||
public IAsyncRelayCommand GenerateContextFileCommand { get; }
|
||
public IRelayCommand CancelProcessingCommand { get; }
|
||
public IRelayCommand ExitApplicationCommand { get; }
|
||
|
||
public MainViewModel(
|
||
IProjectLoaderService projectLoaderService,
|
||
IFileScannerService fileScannerService,
|
||
IContextFileGenerator contextFileGenerator,
|
||
IUIService uiService,
|
||
ISettingsService settingsService)
|
||
{
|
||
_projectLoaderService = projectLoaderService;
|
||
_fileScannerService = fileScannerService;
|
||
_contextFileGenerator = contextFileGenerator;
|
||
_uiService = uiService;
|
||
_settingsService = settingsService;
|
||
|
||
// Явная инициализация команд
|
||
SelectProjectCommand = new RelayCommand(SelectProject, CanSelectProject);
|
||
GenerateContextFileCommand = new AsyncRelayCommand(GenerateContextFileAsync, CanGenerate);
|
||
CancelProcessingCommand = new RelayCommand(CancelProcessing);
|
||
ExitApplicationCommand = new RelayCommand(ExitApplication);
|
||
}
|
||
|
||
private bool CanSelectProject() => !IsProcessing;
|
||
|
||
private bool CanGenerate()
|
||
{
|
||
bool result = !IsProcessing &&
|
||
IsProjectLoaded &&
|
||
RootDirectory != null &&
|
||
HasSelectedFiles(RootDirectory);
|
||
|
||
// Отладочная информация
|
||
System.Diagnostics.Debug.WriteLine($"CanGenerate: {result}, IsProcessing: {IsProcessing}, IsProjectLoaded: {IsProjectLoaded}, RootDirectory: {(RootDirectory != null)}, HasSelectedFiles: {HasSelectedFiles(RootDirectory)}");
|
||
|
||
return result;
|
||
}
|
||
|
||
private void SelectProject()
|
||
{
|
||
var initialDir = !string.IsNullOrEmpty(SelectedProjectPath) && Directory.Exists(SelectedProjectPath)
|
||
? Path.GetDirectoryName(SelectedProjectPath)
|
||
: _settingsService.GetLastProjectPath();
|
||
|
||
if (_uiService.ShowOpenProjectFileDialog(out var filePath))
|
||
{
|
||
LoadProject(filePath);
|
||
return;
|
||
}
|
||
|
||
var folderPath = _uiService.ShowFolderBrowserDialog(initialDir);
|
||
if (!string.IsNullOrEmpty(folderPath))
|
||
{
|
||
LoadProject(folderPath);
|
||
}
|
||
}
|
||
|
||
private async Task GenerateContextFileAsync()
|
||
{
|
||
var selectedFiles = _fileScannerService.GetSelectedFiles(RootDirectory);
|
||
if (selectedFiles.Count == 0)
|
||
{
|
||
_uiService.ShowMessage("Пожалуйста, выберите хотя бы один файл для обработки.", "Предупреждение", MessageBoxImage.Warning);
|
||
return;
|
||
}
|
||
|
||
var defaultFileName = _projectLoaderService.GetDefaultOutputFileName(SelectedProjectPath);
|
||
var initialDir = _settingsService.GetLastProjectPath() ?? Path.GetDirectoryName(SelectedProjectPath);
|
||
|
||
if (!_uiService.ShowSaveFileDialog(defaultFileName, initialDir, out var savePath))
|
||
return;
|
||
|
||
IsProcessing = true;
|
||
ProgressText = "Генерация контекстного файла...";
|
||
ProgressValue = 0;
|
||
|
||
try
|
||
{
|
||
_cancellationTokenSource = new CancellationTokenSource();
|
||
var progress = new Progress<int>(value =>
|
||
{
|
||
ProgressValue = value;
|
||
ProgressText = $"Генерация: {value}%";
|
||
});
|
||
|
||
await _contextFileGenerator.GenerateContextFileAsync(
|
||
selectedFiles,
|
||
savePath,
|
||
Path.GetDirectoryName(SelectedProjectPath),
|
||
progress,
|
||
_cancellationTokenSource.Token);
|
||
|
||
_uiService.ShowMessage($"Файл успешно создан:\n{savePath}", "Успех");
|
||
}
|
||
catch (OperationCanceledException)
|
||
{
|
||
ProgressText = "Генерация отменена";
|
||
}
|
||
catch (IOException ex)
|
||
{
|
||
_uiService.ShowMessage($"Ошибка доступа к файлу: {ex.Message}\nПожалуйста, закройте файлы или предоставьте необходимые права доступа.", "Ошибка доступа", MessageBoxImage.Error);
|
||
}
|
||
catch (System.Exception ex)
|
||
{
|
||
_uiService.ShowMessage($"Ошибка при генерации файла: {ex.Message}", "Ошибка", MessageBoxImage.Error);
|
||
}
|
||
finally
|
||
{
|
||
IsProcessing = false;
|
||
// Принудительно обновляем команды после завершения операции
|
||
UpdateCommandsCanExecute();
|
||
}
|
||
}
|
||
|
||
private void CancelProcessing()
|
||
{
|
||
_cancellationTokenSource?.Cancel();
|
||
ProgressText = "Операция отменена";
|
||
}
|
||
|
||
private void ExitApplication()
|
||
{
|
||
Application.Current.Shutdown();
|
||
}
|
||
|
||
private void LoadProject(string projectPath)
|
||
{
|
||
SelectedProjectPath = projectPath;
|
||
_settingsService.SaveLastProjectPath(Path.GetDirectoryName(projectPath));
|
||
|
||
IsProjectLoaded = false;
|
||
RootDirectory = null;
|
||
UpdateCommandsCanExecute();
|
||
|
||
LoadProjectAsync(projectPath);
|
||
}
|
||
|
||
private async void LoadProjectAsync(string projectPath)
|
||
{
|
||
ProgressText = "Загрузка проекта...";
|
||
ProgressValue = 0;
|
||
|
||
try
|
||
{
|
||
var progress = new Progress<int>(value =>
|
||
{
|
||
ProgressValue = value;
|
||
ProgressText = $"Загрузка: {value}%";
|
||
});
|
||
|
||
_cancellationTokenSource = new CancellationTokenSource();
|
||
RootDirectory = await _projectLoaderService.LoadProjectFromPathAsync(projectPath, progress, _cancellationTokenSource.Token);
|
||
|
||
IsProjectLoaded = true;
|
||
ProgressText = "Проект загружен успешно";
|
||
|
||
// Подписываемся на события изменения выбора
|
||
SubscribeToSelectionChanges(RootDirectory);
|
||
|
||
// Сбрасываем выделение после загрузки
|
||
if (RootDirectory != null)
|
||
{
|
||
ClearSelections(RootDirectory);
|
||
}
|
||
|
||
// Принудительно обновляем команды
|
||
UpdateCommandsCanExecute();
|
||
}
|
||
catch (OperationCanceledException)
|
||
{
|
||
ProgressText = "Загрузка отменена";
|
||
}
|
||
catch (System.Exception ex)
|
||
{
|
||
_uiService.ShowMessage($"Ошибка при загрузке проекта: {ex.Message}", "Ошибка", MessageBoxImage.Error);
|
||
}
|
||
finally
|
||
{
|
||
IsProcessing = false;
|
||
UpdateCommandsCanExecute();
|
||
}
|
||
}
|
||
|
||
// Рекурсивная подписка на события изменения выбора
|
||
private void SubscribeToSelectionChanges(FileItem item)
|
||
{
|
||
if (item == null) return;
|
||
|
||
item.SelectionChanged += (sender, args) =>
|
||
{
|
||
UpdateCommandsCanExecute();
|
||
};
|
||
|
||
foreach (var child in item.Children)
|
||
{
|
||
SubscribeToSelectionChanges(child);
|
||
}
|
||
}
|
||
|
||
private void ClearSelections(FileItem item)
|
||
{
|
||
if (item == null) return;
|
||
|
||
item.IsSelected = false;
|
||
foreach (var child in item.Children)
|
||
{
|
||
ClearSelections(child);
|
||
}
|
||
}
|
||
|
||
private bool HasSelectedFiles(FileItem item)
|
||
{
|
||
if (item == null) return false;
|
||
|
||
if (!item.IsDirectory && item.IsSelected == true)
|
||
return true;
|
||
|
||
if (item.IsDirectory)
|
||
{
|
||
foreach (var child in item.Children)
|
||
{
|
||
if (HasSelectedFiles(child))
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
// Метод для принудительного обновления всех команд
|
||
private void UpdateCommandsCanExecute()
|
||
{
|
||
(SelectProjectCommand as RelayCommand)?.NotifyCanExecuteChanged();
|
||
(GenerateContextFileCommand as AsyncRelayCommand)?.NotifyCanExecuteChanged();
|
||
}
|
||
}
|
||
} |