Replaced multiple mutually-exclusive boolean properties for thumbnail text options with a single enum (`ThumbnailOption`) in the data model. Updated WinForms UI to use a ComboBox instead of radio buttons for selecting thumbnail mode. Adjusted designer, mapping profile, settings DTO, and settings service for enum support and backward compatibility. Simplified thumbnail generation logic and improved maintainability by ensuring only one mode can be selected at a time.
1572 lines
50 KiB
C#
1572 lines
50 KiB
C#
using ImageCatalog_2.Commands;
|
|
using ImageCatalog_2.Services;
|
|
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Diagnostics;
|
|
using System.Drawing.Text;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using System.Windows.Forms;
|
|
using System.Windows.Input;
|
|
using AutoMapper;
|
|
using MaddoShared;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace ImageCatalog_2
|
|
{
|
|
public class DataModel : ViewModelBase
|
|
{
|
|
public ICommand TestCommand { get; }
|
|
public ICommand AsyncTestCommand { get; }
|
|
public ICommand AsyncCancelOperationCommand { get; }
|
|
public ICommand ProcessImagesCommand { get; }
|
|
public ICommand SelectSourceFolderCommand { get; }
|
|
public ICommand SelectDestinationFolderCommand { get; }
|
|
public ICommand SelectLogoFileCommand { get; }
|
|
public ICommand SaveSettingsCommand { get; }
|
|
public ICommand LoadSettingsCommand { get; }
|
|
public ICommand SelectColorCommand { get; }
|
|
public ICommand SelectTransparentColorCommand { get; }
|
|
public ICommand SelectModelsFolderCommand { get; }
|
|
public ICommand SelectCsvOutputCommand { get; }
|
|
|
|
private readonly ITestService _service;
|
|
private readonly ILogger<DataModel> _logger;
|
|
private readonly ISettingsService _settingsService;
|
|
private readonly ImageCreationStuff _imageCreationService;
|
|
private readonly PicSettings _picSettings;
|
|
private readonly IMapper _mapper;
|
|
|
|
// ComboBox collections
|
|
public List<string> AvailableFonts { get; }
|
|
public List<string> VerticalPositions { get; } = new() { "Alto", "Centro", "Basso" };
|
|
public List<string> HorizontalAlignments { get; } = new() { "Sinistra", "Centro", "Destra" };
|
|
|
|
public DataModel(ITestService testService, ISettingsService settingsService,
|
|
ImageCreationStuff imageCreationService, PicSettings picSettings,
|
|
IMapper mapper, ILogger<DataModel> logger, MaddoShared.IVersionProvider? versionProvider = null)
|
|
{
|
|
_service = testService;
|
|
_logger = logger;
|
|
_settingsService = settingsService;
|
|
_imageCreationService = imageCreationService;
|
|
_picSettings = picSettings;
|
|
_mapper = mapper;
|
|
// Populate AppVersion from version provider when available
|
|
AppVersion = versionProvider?.GetVersionString() ?? string.Empty;
|
|
|
|
TestCommand = new RelayCommand(Test);
|
|
AsyncTestCommand = new AsyncCommand(TestAsync);
|
|
AsyncCancelOperationCommand = new AsyncCommand(CancelOperation);
|
|
ProcessImagesCommand = new AsyncCommand(ProcessImages);
|
|
SelectModelsFolderCommand = new RelayCommand(SelectModelsFolder);
|
|
SelectCsvOutputCommand = new RelayCommand(SelectCsvOutput);
|
|
|
|
SelectSourceFolderCommand = new RelayCommand(SelectSourceFolder);
|
|
SelectDestinationFolderCommand = new RelayCommand(SelectDestinationFolder);
|
|
SelectLogoFileCommand = new RelayCommand(SelectLogoFile);
|
|
SaveSettingsCommand = new RelayCommand(SaveSettings);
|
|
LoadSettingsCommand = new RelayCommand(LoadSettings);
|
|
SelectColorCommand = new RelayCommand(SelectColor);
|
|
SelectTransparentColorCommand = new RelayCommand(SelectTransparentColor);
|
|
|
|
// Load available fonts
|
|
AvailableFonts = LoadAvailableFonts();
|
|
}
|
|
|
|
private async Task RunAiExtractionAsync(CancellationToken token)
|
|
{
|
|
// Simple stub: scan source folder for supported images and either call AIFotoONLUS.Core
|
|
// or simulate results. Write CSV output and populate PreviewResults.
|
|
if (string.IsNullOrWhiteSpace(SourcePath) || !System.IO.Directory.Exists(SourcePath))
|
|
{
|
|
_logger.LogWarning("Source path invalid for AI extraction: {SourcePath}", SourcePath);
|
|
return;
|
|
}
|
|
|
|
var imageFiles = System.IO.Directory.EnumerateFiles(SourcePath, "*.*", System.IO.SearchOption.TopDirectoryOnly)
|
|
.Where(f => f.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase)
|
|
|| f.EndsWith(".jpeg", StringComparison.OrdinalIgnoreCase)
|
|
|| f.EndsWith(".png", StringComparison.OrdinalIgnoreCase)
|
|
|| f.EndsWith(".bmp", StringComparison.OrdinalIgnoreCase)
|
|
|| f.EndsWith(".gif", StringComparison.OrdinalIgnoreCase))
|
|
.ToList();
|
|
|
|
if (imageFiles.Count == 0)
|
|
{
|
|
_logger.LogInformation("No image files found for AI extraction in {SourcePath}", SourcePath);
|
|
return;
|
|
}
|
|
|
|
// Clear preview
|
|
await InvokeOnUiThreadAsync(() => { PreviewResults.Clear(); });
|
|
|
|
// Try to locate AIFotoONLUS.Core types via reflection to avoid hard reference at compile time
|
|
Type? aiProcessorType = null;
|
|
object? aiProcessor = null;
|
|
|
|
try
|
|
{
|
|
var assembly = AppDomain.CurrentDomain.GetAssemblies()
|
|
.FirstOrDefault(a => a.GetName().Name?.Equals("AIFotoONLUS.Core", StringComparison.OrdinalIgnoreCase) == true);
|
|
if (assembly != null)
|
|
{
|
|
aiProcessorType = assembly.GetType("AIFotoONLUS.Core.AiProcessor");
|
|
if (aiProcessorType != null)
|
|
{
|
|
// Create instance assuming parameterless ctor
|
|
aiProcessor = Activator.CreateInstance(aiProcessorType);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogDebug(ex, "AIFotoONLUS.Core not available or failed to load via reflection");
|
|
}
|
|
|
|
var results = new List<AiResult>();
|
|
|
|
foreach (var file in imageFiles)
|
|
{
|
|
token.ThrowIfCancellationRequested();
|
|
|
|
string extracted = string.Empty;
|
|
|
|
if (aiProcessorType is not null && aiProcessor is not null)
|
|
{
|
|
try
|
|
{
|
|
// Preferred method name: ExtractNumbersFromImage(string imagePath)
|
|
var method = aiProcessorType.GetMethod("ExtractNumbersFromImage") ?? aiProcessorType.GetMethod("ExtractTextFromImage");
|
|
if (method is not null)
|
|
{
|
|
var value = method.Invoke(aiProcessor, new object[] { file });
|
|
if (value != null)
|
|
extracted = value.ToString() ?? string.Empty;
|
|
}
|
|
else
|
|
{
|
|
// No expected method found, fallback to simulated result
|
|
extracted = SimulateExtraction(file);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogWarning(ex, "Error invoking AI processor for {File}", file);
|
|
extracted = SimulateExtraction(file);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Simulate extraction when library not available
|
|
extracted = SimulateExtraction(file);
|
|
}
|
|
|
|
var res = new AiResult { Path = file, Text = extracted };
|
|
results.Add(res);
|
|
|
|
await InvokeOnUiThreadAsync(() => PreviewResults.Add(res));
|
|
}
|
|
|
|
// Write CSV if requested
|
|
if (!string.IsNullOrWhiteSpace(CsvOutputPath))
|
|
{
|
|
try
|
|
{
|
|
var dir = System.IO.Path.GetDirectoryName(CsvOutputPath) ?? string.Empty;
|
|
if (!string.IsNullOrWhiteSpace(dir) && !System.IO.Directory.Exists(dir))
|
|
{
|
|
System.IO.Directory.CreateDirectory(dir);
|
|
}
|
|
|
|
using var sw = new System.IO.StreamWriter(CsvOutputPath, false, System.Text.Encoding.UTF8);
|
|
sw.WriteLine("Path,Text");
|
|
foreach (var r in results)
|
|
{
|
|
var safeText = (r.Text ?? string.Empty).Replace("\"", "\"\"");
|
|
sw.WriteLine($"\"{r.Path}\",\"{safeText}\"");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Failed to write CSV to {CsvOutputPath}", CsvOutputPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
private string SimulateExtraction(string file)
|
|
{
|
|
// Cheap heuristic: return filename digits
|
|
var name = System.IO.Path.GetFileNameWithoutExtension(file);
|
|
var digits = new string(name.Where(char.IsDigit).ToArray());
|
|
if (string.IsNullOrEmpty(digits)) return "";
|
|
return digits;
|
|
}
|
|
|
|
private Task InvokeOnUiThreadAsync(Action action)
|
|
{
|
|
// Use SynchronizationContext via Task to ensure UI thread update
|
|
return Task.Run(() =>
|
|
{
|
|
System.Windows.Application.Current?.Dispatcher.Invoke(action);
|
|
});
|
|
}
|
|
|
|
// AI properties
|
|
private bool _extractNumbers;
|
|
public bool ExtractNumbers
|
|
{
|
|
get => _extractNumbers;
|
|
set { _extractNumbers = value; NotifyPropertyChanged(); }
|
|
}
|
|
|
|
private string _modelsFolderPath = string.Empty;
|
|
public string ModelsFolderPath
|
|
{
|
|
get => _modelsFolderPath;
|
|
set { _modelsFolderPath = value; NotifyPropertyChanged(); }
|
|
}
|
|
|
|
private string _csvOutputPath = string.Empty;
|
|
public string CsvOutputPath
|
|
{
|
|
get => _csvOutputPath;
|
|
set { _csvOutputPath = value; NotifyPropertyChanged(); }
|
|
}
|
|
|
|
// Preview results for DataGrid
|
|
private System.Collections.ObjectModel.ObservableCollection<AiResult> _previewResults = new();
|
|
public System.Collections.ObjectModel.ObservableCollection<AiResult> PreviewResults => _previewResults;
|
|
|
|
public class AiResult
|
|
{
|
|
public string Path { get; set; } = string.Empty;
|
|
public string Text { get; set; } = string.Empty;
|
|
}
|
|
|
|
private List<string> LoadAvailableFonts()
|
|
{
|
|
var fonts = new List<string>();
|
|
using (var installedFonts = new InstalledFontCollection())
|
|
{
|
|
fonts.AddRange(installedFonts.Families.Select(f => f.Name));
|
|
}
|
|
return fonts;
|
|
}
|
|
|
|
private CancellationTokenSource? _mainToken;
|
|
|
|
public CancellationTokenSource? MainToken
|
|
{
|
|
get => _mainToken;
|
|
set
|
|
{
|
|
_mainToken = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private string _sourcePath;
|
|
|
|
public string SourcePath
|
|
{
|
|
get => _sourcePath;
|
|
set
|
|
{
|
|
_sourcePath = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private string _destinationPath;
|
|
|
|
public string DestinationPath
|
|
{
|
|
get => _destinationPath;
|
|
set
|
|
{
|
|
_destinationPath = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private string _horizontalText;
|
|
|
|
public string HorizontalText
|
|
{
|
|
get => _horizontalText;
|
|
set
|
|
{
|
|
_horizontalText = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private string _verticalText;
|
|
|
|
public string VerticalText
|
|
{
|
|
get => _verticalText;
|
|
set
|
|
{
|
|
_verticalText = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private bool _overwriteImages;
|
|
|
|
public bool OverwriteImages
|
|
{
|
|
get => _overwriteImages;
|
|
set
|
|
{
|
|
_overwriteImages = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private bool _uiEnabled = true;
|
|
|
|
public bool UiEnabled
|
|
{
|
|
get => _uiEnabled;
|
|
set
|
|
{
|
|
_uiEnabled = value;
|
|
NotifyPropertyChanged();
|
|
NotifyPropertyChanged(nameof(UiDisabled));
|
|
}
|
|
}
|
|
|
|
public bool UiDisabled => !_uiEnabled;
|
|
|
|
private string _speedCounter = "-";
|
|
|
|
public string SpeedCounter
|
|
{
|
|
get => _speedCounter;
|
|
set
|
|
{
|
|
_speedCounter = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private int _chunkSize;
|
|
|
|
public int ChunkSize
|
|
{
|
|
get => _chunkSize;
|
|
set
|
|
{
|
|
_chunkSize = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private int _threadsCount;
|
|
|
|
public int ThreadsCount
|
|
{
|
|
get => _threadsCount;
|
|
set
|
|
{
|
|
_threadsCount = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
// Thumbnail settings
|
|
private string _thumbnailPrefix = "tn_";
|
|
public string ThumbnailPrefix
|
|
{
|
|
get => _thumbnailPrefix;
|
|
set
|
|
{
|
|
_thumbnailPrefix = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private int _thumbnailHeight = 350;
|
|
public int ThumbnailHeight
|
|
{
|
|
get => _thumbnailHeight;
|
|
set
|
|
{
|
|
_thumbnailHeight = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private int _thumbnailWidth = 350;
|
|
public int ThumbnailWidth
|
|
{
|
|
get => _thumbnailWidth;
|
|
set
|
|
{
|
|
_thumbnailWidth = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
// Big photo settings
|
|
private int _photoBigHeight = 2240;
|
|
public int PhotoBigHeight
|
|
{
|
|
get => _photoBigHeight;
|
|
set
|
|
{
|
|
_photoBigHeight = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private int _photoBigWidth = 2240;
|
|
public int PhotoBigWidth
|
|
{
|
|
get => _photoBigWidth;
|
|
set
|
|
{
|
|
_photoBigWidth = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
// Font settings
|
|
private int _fontSize = 20;
|
|
public int FontSize
|
|
{
|
|
get => _fontSize;
|
|
set
|
|
{
|
|
_fontSize = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private int _fontSizeThumbnail = 50;
|
|
public int FontSizeThumbnail
|
|
{
|
|
get => _fontSizeThumbnail;
|
|
set
|
|
{
|
|
_fontSizeThumbnail = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private string _fontName = "Arial";
|
|
public string FontName
|
|
{
|
|
get => _fontName;
|
|
set
|
|
{
|
|
_fontName = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private bool _fontBold = false;
|
|
public bool FontBold
|
|
{
|
|
get => _fontBold;
|
|
set
|
|
{
|
|
_fontBold = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
// Text settings
|
|
private int _textTransparency = 0;
|
|
public int TextTransparency
|
|
{
|
|
get => _textTransparency;
|
|
set
|
|
{
|
|
_textTransparency = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private int _textMargin = 8;
|
|
public int TextMargin
|
|
{
|
|
get => _textMargin;
|
|
set
|
|
{
|
|
_textMargin = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private string _textColorRGB = "Yellow";
|
|
public string TextColorRGB
|
|
{
|
|
get => _textColorRGB;
|
|
set
|
|
{
|
|
_textColorRGB = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private string _transparentColor = "#FFFFFF";
|
|
public string TransparentColor
|
|
{
|
|
get => _transparentColor;
|
|
set
|
|
{
|
|
_transparentColor = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private bool _useTransparentColor;
|
|
public bool UseTransparentColor
|
|
{
|
|
get => _useTransparentColor;
|
|
set
|
|
{
|
|
_useTransparentColor = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
// Logo/Watermark settings
|
|
private string _logoFile = "";
|
|
public string LogoFile
|
|
{
|
|
get => _logoFile;
|
|
set
|
|
{
|
|
_logoFile = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private int _logoHeight = 430;
|
|
public int LogoHeight
|
|
{
|
|
get => _logoHeight;
|
|
set
|
|
{
|
|
_logoHeight = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private int _logoWidth = 430;
|
|
public int LogoWidth
|
|
{
|
|
get => _logoWidth;
|
|
set
|
|
{
|
|
_logoWidth = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private int _logoMargin = 290;
|
|
public int LogoMargin
|
|
{
|
|
get => _logoMargin;
|
|
set
|
|
{
|
|
_logoMargin = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private int _logoTransparency = 100;
|
|
public int LogoTransparency
|
|
{
|
|
get => _logoTransparency;
|
|
set
|
|
{
|
|
_logoTransparency = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
// Image library selection (UI radio buttons bind to the boolean helpers)
|
|
private string _imageLibrary = "System.Graphics";
|
|
|
|
/// <summary>
|
|
/// The selected image processing library. Possible values: "System.Graphics" or "ImageSharp".
|
|
/// This value is mirrored into PicSettings.ImageCreatorProvider so the runtime mapper picks the implementation.
|
|
/// </summary>
|
|
public string ImageLibrary
|
|
{
|
|
get => _imageLibrary;
|
|
set
|
|
{
|
|
if (_imageLibrary == value) return;
|
|
_imageLibrary = value;
|
|
// Reflect selection into PicSettings so mapper can resolve at runtime
|
|
_picSettings.ImageCreatorProvider = string.Equals(value, "ImageSharp", StringComparison.OrdinalIgnoreCase)
|
|
? "ALTERNATE"
|
|
: "Sharp";
|
|
NotifyPropertyChanged();
|
|
NotifyPropertyChanged(nameof(UseSystemGraphics));
|
|
NotifyPropertyChanged(nameof(UseImageSharp));
|
|
}
|
|
}
|
|
|
|
public bool UseSystemGraphics
|
|
{
|
|
get => string.Equals(ImageLibrary, "System.Graphics", StringComparison.OrdinalIgnoreCase);
|
|
set
|
|
{
|
|
if (value) ImageLibrary = "System.Graphics";
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
public bool UseImageSharp
|
|
{
|
|
get => string.Equals(ImageLibrary, "ImageSharp", StringComparison.OrdinalIgnoreCase);
|
|
set
|
|
{
|
|
if (value) ImageLibrary = "ImageSharp";
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
// Folder division settings
|
|
private int _filesPerFolder = 99;
|
|
public int FilesPerFolder
|
|
{
|
|
get => _filesPerFolder;
|
|
set
|
|
{
|
|
_filesPerFolder = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private string _folderSuffix = "";
|
|
public string FolderSuffix
|
|
{
|
|
get => _folderSuffix;
|
|
set
|
|
{
|
|
_folderSuffix = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private int _counterDigits = 2;
|
|
public int CounterDigits
|
|
{
|
|
get => _counterDigits;
|
|
set
|
|
{
|
|
_counterDigits = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
// Vertical text settings
|
|
private int _verticalTextSize = 20;
|
|
public int VerticalTextSize
|
|
{
|
|
get => _verticalTextSize;
|
|
set
|
|
{
|
|
_verticalTextSize = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private int _verticalTextMargin = 6;
|
|
public int VerticalTextMargin
|
|
{
|
|
get => _verticalTextMargin;
|
|
set
|
|
{
|
|
_verticalTextMargin = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
// JPEG compression settings
|
|
private int _jpegQuality = 85;
|
|
public int JpegQuality
|
|
{
|
|
get => _jpegQuality;
|
|
set
|
|
{
|
|
_jpegQuality = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private int _jpegQualityThumbnail = 30;
|
|
public int JpegQualityThumbnail
|
|
{
|
|
get => _jpegQualityThumbnail;
|
|
set
|
|
{
|
|
_jpegQualityThumbnail = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
// CheckBox settings
|
|
private bool _createThumbnails = true;
|
|
public bool CreateThumbnails
|
|
{
|
|
get => _createThumbnails;
|
|
set
|
|
{
|
|
_createThumbnails = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private bool _automaticRotation;
|
|
public bool AutomaticRotation
|
|
{
|
|
get => _automaticRotation;
|
|
set
|
|
{
|
|
_automaticRotation = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private bool _forceJpeg;
|
|
public bool ForceJpeg
|
|
{
|
|
get => _forceJpeg;
|
|
set
|
|
{
|
|
_forceJpeg = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private bool _updateSubdirectories;
|
|
public bool UpdateSubdirectories
|
|
{
|
|
get => _updateSubdirectories;
|
|
set
|
|
{
|
|
_updateSubdirectories = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private bool _createSubfolders;
|
|
public bool CreateSubfolders
|
|
{
|
|
get => _createSubfolders;
|
|
set
|
|
{
|
|
_createSubfolders = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private bool _addTime;
|
|
public bool AddTime
|
|
{
|
|
get => _addTime;
|
|
set
|
|
{
|
|
_addTime = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private bool _addRaceTime;
|
|
public bool AddRaceTime
|
|
{
|
|
get => _addRaceTime;
|
|
set
|
|
{
|
|
_addRaceTime = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private bool _addLogo;
|
|
public bool AddLogo
|
|
{
|
|
get => _addLogo;
|
|
set
|
|
{
|
|
_addLogo = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private bool _keepOriginalDimensions;
|
|
public bool KeepOriginalDimensions
|
|
{
|
|
get => _keepOriginalDimensions;
|
|
set
|
|
{
|
|
_keepOriginalDimensions = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private bool _showDate;
|
|
public bool ShowDate
|
|
{
|
|
get => _showDate;
|
|
set
|
|
{
|
|
_showDate = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private bool _showPhotoNumber;
|
|
public bool ShowPhotoNumber
|
|
{
|
|
get => _showPhotoNumber;
|
|
set
|
|
{
|
|
if (_showPhotoNumber == value) return;
|
|
_showPhotoNumber = value;
|
|
if (value)
|
|
{
|
|
// ensure mutually exclusive choices
|
|
_addTimeToThumbnails = false;
|
|
_addTextToThumbnails = false;
|
|
_addNumberAndTimeToThumbnails = false;
|
|
_addRaceTimeToThumbnails = false;
|
|
NotifyPropertyChanged(nameof(AddTimeToThumbnails));
|
|
NotifyPropertyChanged(nameof(AddTextToThumbnails));
|
|
NotifyPropertyChanged(nameof(AddNumberAndTimeToThumbnails));
|
|
NotifyPropertyChanged(nameof(AddRaceTimeToThumbnails));
|
|
}
|
|
NotifyPropertyChanged();
|
|
NotifyPropertyChanged(nameof(ThumbnailMode));
|
|
}
|
|
}
|
|
|
|
private bool _shutdownSystem;
|
|
public bool ShutdownSystem
|
|
{
|
|
get => _shutdownSystem;
|
|
set
|
|
{
|
|
_shutdownSystem = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
// ComboBox position/alignment settings
|
|
private string _verticalPosition = "Basso";
|
|
public string VerticalPosition
|
|
{
|
|
get => _verticalPosition;
|
|
set
|
|
{
|
|
_verticalPosition = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private string _horizontalAlignment = "Centro";
|
|
public string HorizontalAlignment
|
|
{
|
|
get => _horizontalAlignment;
|
|
set
|
|
{
|
|
_horizontalAlignment = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private string _logoHorizontalPosition = "Destra";
|
|
public string LogoHorizontalPosition
|
|
{
|
|
get => _logoHorizontalPosition;
|
|
set
|
|
{
|
|
_logoHorizontalPosition = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private string _logoVerticalPosition = "Basso";
|
|
public string LogoVerticalPosition
|
|
{
|
|
get => _logoVerticalPosition;
|
|
set
|
|
{
|
|
_logoVerticalPosition = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
// RadioButton settings
|
|
private bool _useProgressiveNumbering = true;
|
|
public bool UseProgressiveNumbering
|
|
{
|
|
get => _useProgressiveNumbering;
|
|
set
|
|
{
|
|
_useProgressiveNumbering = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private bool _useFileNumbering;
|
|
public bool UseFileNumbering
|
|
{
|
|
get => _useFileNumbering;
|
|
set
|
|
{
|
|
_useFileNumbering = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private bool _useParallelProcessing = true;
|
|
public bool UseParallelProcessing
|
|
{
|
|
get => _useParallelProcessing;
|
|
set
|
|
{
|
|
_useParallelProcessing = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private bool _useSequentialProcessing;
|
|
public bool UseSequentialProcessing
|
|
{
|
|
get => _useSequentialProcessing;
|
|
set
|
|
{
|
|
_useSequentialProcessing = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
// Additional settings that were missing
|
|
private bool _addTimeToThumbnails;
|
|
public bool AddTimeToThumbnails
|
|
{
|
|
get => _thumbnailOption == ThumbnailOptionEnum.Time;
|
|
set
|
|
{
|
|
if (value)
|
|
{
|
|
ThumbnailOption = ThumbnailOptionEnum.Time;
|
|
}
|
|
else if (_thumbnailOption == ThumbnailOptionEnum.Time)
|
|
{
|
|
ThumbnailOption = ThumbnailOptionEnum.None;
|
|
}
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private bool _showFileNameOnThumbnails;
|
|
public bool ShowFileNameOnThumbnails
|
|
{
|
|
get => _thumbnailOption == ThumbnailOptionEnum.FileName;
|
|
set
|
|
{
|
|
if (value)
|
|
ThumbnailOption = ThumbnailOptionEnum.FileName;
|
|
else if (_thumbnailOption == ThumbnailOptionEnum.FileName)
|
|
ThumbnailOption = ThumbnailOptionEnum.None;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private DateTime _raceStartDate = DateTime.Now;
|
|
public DateTime RaceStartDate
|
|
{
|
|
get => _raceStartDate;
|
|
set
|
|
{
|
|
_raceStartDate = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private string _timeLabel = "";
|
|
public string TimeLabel
|
|
{
|
|
get => _timeLabel;
|
|
set
|
|
{
|
|
_timeLabel = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private string _bigPhotoSuffix = "";
|
|
public string BigPhotoSuffix
|
|
{
|
|
get => _bigPhotoSuffix;
|
|
set
|
|
{
|
|
_bigPhotoSuffix = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private bool _addTextToThumbnails;
|
|
public bool AddTextToThumbnails
|
|
{
|
|
get => _thumbnailOption == ThumbnailOptionEnum.Text;
|
|
set
|
|
{
|
|
if (value)
|
|
ThumbnailOption = ThumbnailOptionEnum.Text;
|
|
else if (_thumbnailOption == ThumbnailOptionEnum.Text)
|
|
ThumbnailOption = ThumbnailOptionEnum.None;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private bool _addRaceTimeToThumbnails;
|
|
public bool AddRaceTimeToThumbnails
|
|
{
|
|
get => _thumbnailOption == ThumbnailOptionEnum.RaceTime;
|
|
set
|
|
{
|
|
if (value)
|
|
ThumbnailOption = ThumbnailOptionEnum.RaceTime;
|
|
else if (_thumbnailOption == ThumbnailOptionEnum.RaceTime)
|
|
ThumbnailOption = ThumbnailOptionEnum.None;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private bool _addNumberAndTimeToThumbnails;
|
|
public bool AddNumberAndTimeToThumbnails
|
|
{
|
|
get => _thumbnailOption == ThumbnailOptionEnum.FileNameAndTime;
|
|
set
|
|
{
|
|
if (value)
|
|
ThumbnailOption = ThumbnailOptionEnum.FileNameAndTime;
|
|
else if (_thumbnailOption == ThumbnailOptionEnum.FileNameAndTime)
|
|
ThumbnailOption = ThumbnailOptionEnum.None;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
// New enum and authoritative property for thumbnail selection
|
|
public enum ThumbnailOptionEnum
|
|
{
|
|
None = 0,
|
|
Text = 1,
|
|
FileName = 2,
|
|
Time = 3,
|
|
FileNameAndTime = 4,
|
|
RaceTime = 5
|
|
}
|
|
|
|
private ThumbnailOptionEnum _thumbnailOption = ThumbnailOptionEnum.None;
|
|
|
|
// Name matches DTO property so SettingsService mapping works
|
|
public ThumbnailOptionEnum ThumbnailOption
|
|
{
|
|
get => _thumbnailOption;
|
|
set
|
|
{
|
|
if (_thumbnailOption == value) return;
|
|
_thumbnailOption = value;
|
|
// Notify all dependent properties so UI updates
|
|
NotifyPropertyChanged();
|
|
NotifyPropertyChanged(nameof(AddTextToThumbnails));
|
|
NotifyPropertyChanged(nameof(AddTimeToThumbnails));
|
|
NotifyPropertyChanged(nameof(ShowPhotoNumber));
|
|
NotifyPropertyChanged(nameof(AddNumberAndTimeToThumbnails));
|
|
NotifyPropertyChanged(nameof(AddRaceTimeToThumbnails));
|
|
NotifyPropertyChanged(nameof(ShowFileNameOnThumbnails));
|
|
NotifyPropertyChanged(nameof(ThumbnailMode));
|
|
}
|
|
}
|
|
|
|
// Helper int property to bind ComboBox SelectedIndex in the WinForms designer
|
|
public int ThumbnailOptionIndex
|
|
{
|
|
get => (int)ThumbnailOption;
|
|
set
|
|
{
|
|
var opt = (ThumbnailOptionEnum)value;
|
|
if (opt == ThumbnailOption) return;
|
|
ThumbnailOption = opt;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
// Single authoritative thumbnail mode string to avoid conflicting bindings
|
|
// Possible values: "None", "Text", "Time", "Number", "NumberAndTime", "RaceTime"
|
|
public string ThumbnailMode
|
|
{
|
|
get
|
|
{
|
|
return _thumbnailOption switch
|
|
{
|
|
ThumbnailOptionEnum.Text => "Text",
|
|
ThumbnailOptionEnum.Time => "Time",
|
|
ThumbnailOptionEnum.FileName => "Number",
|
|
ThumbnailOptionEnum.FileNameAndTime => "NumberAndTime",
|
|
ThumbnailOptionEnum.RaceTime => "RaceTime",
|
|
_ => "None",
|
|
};
|
|
}
|
|
set
|
|
{
|
|
// Map incoming string to enum and set the authoritative property
|
|
switch ((value ?? string.Empty).ToLowerInvariant())
|
|
{
|
|
case "text":
|
|
ThumbnailOption = ThumbnailOptionEnum.Text;
|
|
break;
|
|
case "time":
|
|
ThumbnailOption = ThumbnailOptionEnum.Time;
|
|
break;
|
|
case "number":
|
|
ThumbnailOption = ThumbnailOptionEnum.FileName;
|
|
break;
|
|
case "numberandtime":
|
|
ThumbnailOption = ThumbnailOptionEnum.FileNameAndTime;
|
|
break;
|
|
case "racetime":
|
|
ThumbnailOption = ThumbnailOptionEnum.RaceTime;
|
|
break;
|
|
default:
|
|
ThumbnailOption = ThumbnailOptionEnum.None;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Image processing progress and status
|
|
private string _processingStatus = "";
|
|
public string ProcessingStatus
|
|
{
|
|
get => _processingStatus;
|
|
set
|
|
{
|
|
_processingStatus = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private int _processedImagesCount = 0;
|
|
public int ProcessedImagesCount
|
|
{
|
|
get => _processedImagesCount;
|
|
set
|
|
{
|
|
_processedImagesCount = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private int _totalImagesCount = 0;
|
|
public int TotalImagesCount
|
|
{
|
|
get => _totalImagesCount;
|
|
set
|
|
{
|
|
_totalImagesCount = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private int _progressBarValue = 0;
|
|
public int ProgressBarValue
|
|
{
|
|
get => _progressBarValue;
|
|
set
|
|
{
|
|
_progressBarValue = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private int _progressBarMaximum = 100;
|
|
public int ProgressBarMaximum
|
|
{
|
|
get => _progressBarMaximum;
|
|
set
|
|
{
|
|
_progressBarMaximum = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
|
|
private ConcurrentBag<string> _results = new();
|
|
private int _currentAmount = 0;
|
|
private int _previousAmount = 0;
|
|
// Atomic counter for processed images — avoids expensive ConcurrentBag.Count enumerations
|
|
private int _processedAtomic = 0;
|
|
private System.Threading.Timer? _speedTimer;
|
|
// Stopwatch used to compute run-wide averages
|
|
private Stopwatch? _speedWatch;
|
|
// Recent diffs queue to smooth short-term fluctuations
|
|
private readonly Queue<int> _recentDiffs = new();
|
|
private int _recentWindowSize = 5; // average over last 5 samples (~5s)
|
|
|
|
private void Test(object parameter)
|
|
{
|
|
Debug.WriteLine("Yep");
|
|
this.UiEnabled = !this.UiEnabled;
|
|
}
|
|
|
|
private async Task TestAsync()
|
|
{
|
|
Debug.WriteLine("Yep c");
|
|
}
|
|
|
|
private async Task ProcessImages()
|
|
{
|
|
_logger.LogInformation("Avvio elaborazione...");
|
|
UiEnabled = false;
|
|
|
|
MainToken?.Dispose();
|
|
MainToken = new CancellationTokenSource();
|
|
var token = MainToken.Token;
|
|
|
|
// Fix paths
|
|
FixPaths();
|
|
|
|
// Reset counters
|
|
ProcessingStatus = "Elaborazione in corso...";
|
|
TotalImagesCount = 0;
|
|
ProcessedImagesCount = 0;
|
|
SpeedCounter = "-f/s";
|
|
ProgressBarValue = 0;
|
|
ProgressBarMaximum = 100;
|
|
|
|
// Update PicSettings from DataModel using AutoMapper
|
|
_mapper.Map(this, _picSettings);
|
|
// Explicitly ensure thumbnail-related flags are applied to PicSettings
|
|
// because AutoMapper may not map differently-named properties.
|
|
try
|
|
{
|
|
_picSettings.AggiungiScritteMiniature = this.AddTextToThumbnails;
|
|
_picSettings.UsaOrarioMiniatura = this.AddTimeToThumbnails;
|
|
_picSettings.AggNumTempMin = this.AddNumberAndTimeToThumbnails;
|
|
_picSettings.AggTempoGaraMin = this.AddRaceTimeToThumbnails;
|
|
_picSettings.CreaMiniature = this.CreateThumbnails;
|
|
_picSettings.LarghezzaSmall = this.ThumbnailWidth;
|
|
_picSettings.AltezzaSmall = this.ThumbnailHeight;
|
|
_picSettings.DimMin = this.FontSizeThumbnail;
|
|
_picSettings.JpegQualityMin = this.JpegQualityThumbnail;
|
|
}
|
|
catch
|
|
{
|
|
// Best-effort; do not fail processing on mapping issues
|
|
}
|
|
|
|
var imageCreationOptions = new ImageCreationStuff.Options
|
|
{
|
|
AggiornaSottodirectory = UpdateSubdirectories,
|
|
CreaSottocartelle = CreateSubfolders,
|
|
FilePerCartella = FilesPerFolder,
|
|
SuffissoCartelle = FolderSuffix,
|
|
CifreContatore = CounterDigits,
|
|
NumerazioneType = UseProgressiveNumbering ? NumerazioneType.Progressiva : NumerazioneType.Files,
|
|
SourcePath = SourcePath,
|
|
DestinationPath = DestinationPath,
|
|
MaxThreads = ThreadsCount,
|
|
ChunksSize = ChunkSize,
|
|
LinearExecution = UseSequentialProcessing
|
|
};
|
|
|
|
try
|
|
{
|
|
_results = new ConcurrentBag<string>();
|
|
_currentAmount = 0;
|
|
_previousAmount = 0;
|
|
_processedAtomic = 0;
|
|
|
|
// Start speed timer (sample every second using lightweight atomic reads)
|
|
_speedWatch = Stopwatch.StartNew();
|
|
_recentDiffs.Clear();
|
|
_speedTimer = new System.Threading.Timer(UpdateSpeedCounter, null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1));
|
|
|
|
var time = await _imageCreationService.CreaCatalogoParallel(
|
|
imageCreationOptions,
|
|
_results,
|
|
OnImageProcessed,
|
|
token);
|
|
|
|
// AI integration stub: if ExtractNumbers is enabled, simulate or invoke OCR processing
|
|
if (ExtractNumbers)
|
|
{
|
|
try
|
|
{
|
|
await RunAiExtractionAsync(token);
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
_logger.LogInformation("AI extraction canceled");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "AI extraction failed");
|
|
}
|
|
}
|
|
|
|
// Compute final averages and show only averages (do not show raw seconds)
|
|
var finalProcessed = System.Threading.Volatile.Read(ref _processedAtomic);
|
|
double overallAvg = 0.0;
|
|
double overallPerMin = 0.0;
|
|
if (_speedWatch is not null && _speedWatch.Elapsed.TotalSeconds > 0.0)
|
|
{
|
|
overallAvg = finalProcessed / _speedWatch.Elapsed.TotalSeconds;
|
|
overallPerMin = overallAvg * 60.0;
|
|
}
|
|
|
|
// Compute elapsed time as h m s and show final averages (no raw seconds parentheses)
|
|
var finalElapsed = _speedWatch?.Elapsed ?? TimeSpan.Zero;
|
|
int fh = (int)finalElapsed.TotalHours;
|
|
int fm = finalElapsed.Minutes;
|
|
int fs = finalElapsed.Seconds;
|
|
|
|
SpeedCounter = $"{fh}h {fm}m {fs}s{Environment.NewLine}media: {overallAvg:0.00} f/s{Environment.NewLine}media: {overallPerMin:0.00} f/m";
|
|
|
|
_speedTimer?.Dispose();
|
|
_speedTimer = null;
|
|
_speedWatch?.Stop();
|
|
_speedWatch = null;
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
_logger.LogInformation("Operazione Cancellata");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Errore durante l'elaborazione delle immagini");
|
|
ProcessingStatus = $"Errore: {ex.Message}";
|
|
}
|
|
finally
|
|
{
|
|
MainToken?.Dispose();
|
|
MainToken = null;
|
|
_speedTimer?.Dispose();
|
|
_speedTimer = null;
|
|
}
|
|
|
|
ProcessingStatus = "Finito";
|
|
UiEnabled = true;
|
|
}
|
|
|
|
private void UpdateSpeedCounter(object? state)
|
|
{
|
|
try
|
|
{
|
|
_previousAmount = _currentAmount;
|
|
// Read the atomic counter without enumerating the ConcurrentBag
|
|
_currentAmount = System.Threading.Volatile.Read(ref _processedAtomic);
|
|
int diff = _currentAmount - _previousAmount;
|
|
|
|
// Protect against negative or spurious diffs
|
|
if (diff < 0) diff = 0;
|
|
|
|
// Maintain a small sliding window of recent diffs to smooth the display
|
|
lock (_recentDiffs)
|
|
{
|
|
_recentDiffs.Enqueue(diff);
|
|
if (_recentDiffs.Count > _recentWindowSize)
|
|
_recentDiffs.Dequeue();
|
|
}
|
|
|
|
double avgRecent;
|
|
lock (_recentDiffs)
|
|
{
|
|
avgRecent = _recentDiffs.Count == 0 ? 0.0 : _recentDiffs.Average();
|
|
}
|
|
|
|
// Compute overall average (since start) if we have a stopwatch
|
|
double overall = 0.0;
|
|
if (_speedWatch is not null && _speedWatch.Elapsed.TotalSeconds >= 1)
|
|
{
|
|
var elapsedSeconds = _speedWatch.Elapsed.TotalSeconds;
|
|
var total = System.Threading.Volatile.Read(ref _processedAtomic);
|
|
overall = elapsedSeconds > 0 ? total / elapsedSeconds : 0.0;
|
|
}
|
|
|
|
// Recent per-minute estimate
|
|
var recentPerMin = avgRecent * 60.0;
|
|
var overallPerMin = overall * 60.0;
|
|
|
|
// Build a two-line display plus elapsed time: first line shows f/s with overall media and elapsed time,
|
|
// second line shows recent photos per minute (media)
|
|
var elapsed = _speedWatch?.Elapsed ?? TimeSpan.Zero;
|
|
int hours = (int)elapsed.TotalHours;
|
|
int minutes = elapsed.Minutes;
|
|
int seconds = elapsed.Seconds;
|
|
var elapsedStr = $"{hours}h {minutes}m {seconds}s";
|
|
|
|
SpeedCounter = $"{avgRecent:0.00} f/s (media: {overall:0.00} f/s) - {elapsedStr}{Environment.NewLine}media: {recentPerMin:0.00} f/m";
|
|
}
|
|
catch
|
|
{
|
|
// Swallow unlikely errors from timing/queue operations but keep UI responsive
|
|
}
|
|
}
|
|
|
|
private void OnImageProcessed(object? sender, Tuple<string, int> args)
|
|
{
|
|
// Increment atomic processed counter once and use its value for all UI updates
|
|
var processed = System.Threading.Interlocked.Increment(ref _processedAtomic);
|
|
ProcessedImagesCount = processed;
|
|
TotalImagesCount = args.Item2;
|
|
ProgressBarMaximum = args.Item2;
|
|
ProgressBarValue = processed;
|
|
ProcessingStatus = args.Item1;
|
|
}
|
|
|
|
private void FixPaths()
|
|
{
|
|
SourcePath = FixPath(SourcePath);
|
|
DestinationPath = FixPath(DestinationPath);
|
|
}
|
|
|
|
private string FixPath(string path)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(path))
|
|
{
|
|
return string.Empty;
|
|
}
|
|
|
|
// Trim leading/trailing quotes
|
|
path = path.Trim().Trim('"');
|
|
|
|
// Normalize directory separators
|
|
path = path.Replace('/', System.IO.Path.DirectorySeparatorChar)
|
|
.Replace('\\', System.IO.Path.DirectorySeparatorChar);
|
|
|
|
// Remove trailing separators then add one back
|
|
path = path.TrimEnd(System.IO.Path.DirectorySeparatorChar) + System.IO.Path.DirectorySeparatorChar;
|
|
|
|
return path;
|
|
}
|
|
|
|
private async Task CancelOperation()
|
|
{
|
|
try
|
|
{
|
|
var tokenSource = MainToken;
|
|
if (tokenSource is not null)
|
|
{
|
|
// Cancel synchronously and return to caller. Some CTSource implementations
|
|
// may provide async helpers but cancelling is immediate.
|
|
try
|
|
{
|
|
tokenSource.Cancel();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogWarning(ex, "Exception while cancelling token");
|
|
}
|
|
}
|
|
|
|
UiEnabled = true;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
_logger.LogError(e, "Error canceling the token");
|
|
_logger.LogInformation("Ignora questo errore");
|
|
}
|
|
}
|
|
|
|
// Note: These commands will trigger events that the View will handle to show dialogs
|
|
// since dialogs require UI context
|
|
public event EventHandler SelectSourceFolderRequested;
|
|
public event EventHandler SelectDestinationFolderRequested;
|
|
public event EventHandler SelectLogoFileRequested;
|
|
public event EventHandler SelectModelsFolderRequested;
|
|
public event EventHandler SelectCsvOutputRequested;
|
|
public event EventHandler<string> SaveSettingsRequested;
|
|
public event EventHandler<string> LoadSettingsRequested;
|
|
public event EventHandler SelectColorRequested;
|
|
// Request that the View shows a message to the user (message, caption, icon)
|
|
public event EventHandler<Tuple<string, string, MessageBoxIcon>> ShowMessageRequested;
|
|
public event EventHandler SelectTransparentColorRequested;
|
|
|
|
private void SelectSourceFolder(object parameter)
|
|
{
|
|
SelectSourceFolderRequested?.Invoke(this, EventArgs.Empty);
|
|
}
|
|
|
|
private void SelectDestinationFolder(object parameter)
|
|
{
|
|
SelectDestinationFolderRequested?.Invoke(this, EventArgs.Empty);
|
|
}
|
|
|
|
private void SelectLogoFile(object parameter)
|
|
{
|
|
SelectLogoFileRequested?.Invoke(this, EventArgs.Empty);
|
|
}
|
|
|
|
private void SelectModelsFolder(object parameter)
|
|
{
|
|
SelectModelsFolderRequested?.Invoke(this, EventArgs.Empty);
|
|
}
|
|
|
|
private void SelectCsvOutput(object parameter)
|
|
{
|
|
SelectCsvOutputRequested?.Invoke(this, EventArgs.Empty);
|
|
}
|
|
|
|
private void SaveSettings(object parameter)
|
|
{
|
|
SaveSettingsRequested?.Invoke(this, null);
|
|
}
|
|
|
|
private void LoadSettings(object parameter)
|
|
{
|
|
LoadSettingsRequested?.Invoke(this, null);
|
|
}
|
|
|
|
private void SelectColor(object parameter)
|
|
{
|
|
SelectColorRequested?.Invoke(this, EventArgs.Empty);
|
|
}
|
|
|
|
private void SelectTransparentColor(object parameter)
|
|
{
|
|
SelectTransparentColorRequested?.Invoke(this, EventArgs.Empty);
|
|
}
|
|
|
|
public async Task SaveSettingsToFileAsync(string filePath)
|
|
{
|
|
await _settingsService.SaveSettingsAsync(filePath, this);
|
|
}
|
|
|
|
public async Task LoadSettingsFromFileAsync(string filePath)
|
|
{
|
|
await _settingsService.LoadSettingsAsync(filePath, this);
|
|
}
|
|
|
|
private string _appVersion = string.Empty;
|
|
public string AppVersion
|
|
{
|
|
get => _appVersion;
|
|
set
|
|
{
|
|
_appVersion = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|