Catalog/imagecatalog/MainForm.cs

842 lines
32 KiB
C#
Raw Normal View History

#if WINDOWS
using System;
2021-03-04 10:44:09 +01:00
using System.Collections.Concurrent;
using System.Collections.Generic;
2025-07-08 14:35:27 +02:00
using System.ComponentModel;
2021-03-04 10:44:09 +01:00
using System.Drawing;
using System.Drawing.Text;
using System.IO;
using System.Linq;
2024-10-14 19:57:24 +02:00
using System.Reflection;
using System.Diagnostics;
2021-03-04 10:44:09 +01:00
using System.Runtime.InteropServices;
using System.Text;
2021-03-04 10:44:09 +01:00
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
2024-10-14 22:55:52 +02:00
using ImageCatalog_2;
2026-02-04 23:16:06 +01:00
using ImageCatalog_2.Commands;
2024-10-14 22:18:03 +02:00
using ImageCatalog_2.Services;
2021-03-04 10:44:09 +01:00
using MaddoShared;
using Microsoft.Extensions.Logging;
2021-03-04 10:44:09 +01:00
2025-07-28 09:00:07 +02:00
namespace ImageCatalog;
public partial class MainForm
2021-03-04 10:44:09 +01:00
{
private readonly DataModel Model;
2025-07-29 10:34:23 +02:00
2025-07-28 09:00:07 +02:00
private readonly ILogger<MainForm> _logger;
2025-07-28 09:15:45 +02:00
private readonly ParametriSetup _parametriSetup;
2025-07-28 10:22:08 +02:00
private readonly PicSettings _picSettings;
// Prevent re-entrant updates between UI events and model PropertyChanged handling
private bool _suppressRadioUpdates = false;
private bool _transparentDialogOpen = false;
2025-07-29 10:34:23 +02:00
2026-02-21 15:53:52 +01:00
public MainForm(DataModel model, ImageCreationService imageCreationStuff, PicSettings picSettings,
ParametriSetup parametriSetup, ILogger<MainForm> logger)
2021-03-04 10:44:09 +01:00
{
Model = model;
2025-07-28 09:15:45 +02:00
_parametriSetup = parametriSetup;
2025-07-28 10:22:08 +02:00
_picSettings = picSettings;
2025-07-28 09:00:07 +02:00
_logger = logger;
_logger.LogDebug("Start");
InitializeComponent();
// Set this form as the control for thread marshalling in the DataModel
Model.SetControl(this);
// Ensure the designer data bindings have a concrete DataSource immediately so
// that UI controls (radio buttons) reflect the ViewModel state and propagate
// user changes back to the ViewModel.
bindingSource1.DataSource = Model;
BindControls();
// The designer originally bound the radio buttons to boolean helpers on the ViewModel.
// Those two separate bindings can fight with each other. Clear designer bindings and
// wire explicit Click handlers that update the single authoritative property
// `Model.ImageLibrary`. Also keep a PropertyChanged listener to reflect external
// changes back into the radio buttons.
rdbLibrary1.DataBindings.Clear();
rdbLibrary2.DataBindings.Clear();
// Initialize radio state from model
rdbLibrary1.Checked = Model.UseSystemGraphics;
rdbLibrary2.Checked = Model.UseImageSharp;
// Use Click handlers (not CheckedChanged) to avoid competing binding updates
rdbLibrary1.Click += (_, _) =>
{
if (_suppressRadioUpdates) return;
if (Model.ImageLibrary != "System.Graphics")
Model.ImageLibrary = "System.Graphics";
};
rdbLibrary2.Click += (_, _) =>
{
if (_suppressRadioUpdates) return;
if (Model.ImageLibrary != "ImageSharp")
Model.ImageLibrary = "ImageSharp";
};
// Watch for model changes so we can reflect external updates
Model.PropertyChanged += Model_PropertyChanged;
2025-07-28 09:00:07 +02:00
// Thumbnail options moved to ComboBox to avoid conflicting bindings with multiple radio buttons
// Initialize ComboBox with Italian descriptions
comboThumbnailOption.Items.Clear();
comboThumbnailOption.Items.AddRange(new object[] {
"Nessuna",
"Aggiungi scritta",
"Nome file",
"Aggiungi orario",
"Nome+Orario",
"Tempo gara"
});
// Bind to model via helper index property ThumbnailOptionIndex
comboThumbnailOption.DataBindings.Add(new Binding("SelectedIndex", bindingSource1, "ThumbnailOptionIndex", true, DataSourceUpdateMode.OnPropertyChanged));
// Save user preferences on form close instead of immediately when dialogs are used
this.FormClosing += MainForm_FormClosing;
// Wire up 'Open folder in Explorer' buttons
btnOpenSourceFolder.Click += BtnOpenSourceFolder_Click;
btnOpenDestFolder.Click += BtnOpenDestFolder_Click;
// Show currently selected color in small PictureBox3
PictureBox3.BackColor = ColorTranslator.FromHtml(Model.TransparentColor);
// Version label is data-bound to DataModel.AppVersion; DataModel is populated with the version via DI
2025-07-28 09:00:07 +02:00
}
2024-10-14 22:55:52 +02:00
private void RdbLibrary_CheckedChanged(object? sender, EventArgs e)
2025-07-28 09:00:07 +02:00
{
// Keep behavior simple: when a radio button becomes checked, update the ViewModel
// so that the designer binding and PicSettings stay in sync.
if (sender is RadioButton rb && rb.Checked)
{
_logger?.LogDebug("Radio library changed: {RadioName}", rb.Name);
if (rb == rdbLibrary2)
{
Model.ImageLibrary = "ImageSharp";
}
else if (rb == rdbLibrary1)
{
Model.ImageLibrary = "System.Graphics";
}
}
}
private void Model_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName is null) return;
if (e.PropertyName == nameof(Model.ImageLibrary) || e.PropertyName == nameof(Model.UseImageSharp) || e.PropertyName == nameof(Model.UseSystemGraphics))
{
_logger?.LogDebug("Model property changed: {Property} => ImageLibrary={ImageLibrary}, PicSettings.Provider={Provider}", e.PropertyName, Model.ImageLibrary, _picSettings.ImageCreatorProvider);
// Reflect authoritative model value into the radio buttons in a thread-safe, re-entrancy-safe way
try
{
_suppressRadioUpdates = true;
rdbLibrary1.Checked = Model.UseSystemGraphics;
rdbLibrary2.Checked = Model.UseImageSharp;
}
finally
{
_suppressRadioUpdates = false;
}
}
// Thumbnail mode changes - reflect back to combo box index
if (e.PropertyName == nameof(Model.ThumbnailMode) ||
e.PropertyName == nameof(Model.ThumbnailOption) ||
e.PropertyName == nameof(Model.ThumbnailOptionIndex))
{
try
{
_suppressRadioUpdates = true;
comboThumbnailOption.SelectedIndex = Model.ThumbnailOptionIndex;
}
finally
{
_suppressRadioUpdates = false;
}
}
}
protected void BindControls()
{
// Bind buttons to ViewModel commands using command binding
_btnCreaCatalogoAsync.BindCommand(Model.ProcessImagesCommand);
// Note: `button1` control does not exist in the designer. Use the primary create button only.
_Button2.BindCommand(Model.SelectSourceFolderCommand);
_Button3.BindCommand(Model.SelectDestinationFolderCommand);
_Button4.BindCommand(Model.SelectLogoFileCommand);
_Button5.BindCommand(Model.SaveSettingsCommand);
_Button6.BindCommand(Model.LoadSettingsCommand);
_Button8.BindCommand(Model.SelectColorCommand);
// Bind the transparency chooser button/command
btnSetTransparency.BindCommand(Model.SelectTransparentColorCommand);
2026-02-04 23:16:06 +01:00
// Subscribe to ViewModel events for UI dialogs (these need UI context)
Model.SelectSourceFolderRequested += OnSelectSourceFolderRequested;
Model.SelectDestinationFolderRequested += OnSelectDestinationFolderRequested;
Model.SelectLogoFileRequested += OnSelectLogoFileRequested;
Model.SaveSettingsRequested += OnSaveSettingsRequested;
Model.LoadSettingsRequested += OnLoadSettingsRequested;
Model.SelectColorRequested += OnSelectColorRequested;
Model.SelectTransparentColorRequested += OnSelectTransparentColorRequested;
// Show message requests (from ViewModel validation)
Model.ShowMessageRequested += OnShowMessageRequested;
}
private void OnSelectTransparentColorRequested(object? sender, EventArgs e)
{
// Ensure UI thread
if (InvokeRequired)
{
Invoke(new Action<object, EventArgs>(OnSelectTransparentColorRequested), sender, e as EventArgs ?? EventArgs.Empty);
return;
}
// Prevent re-entrancy: if the dialog is already open, ignore subsequent requests
if (_transparentDialogOpen) return;
_transparentDialogOpen = true;
var dlg = new ColorDialog { AllowFullOpen = true };
try
{
dlg.Color = ColorTranslator.FromHtml(Model.TransparentColor);
}
catch { }
try
{
if (dlg.ShowDialog() == DialogResult.OK)
{
Model.TransparentColor = ColorTranslator.ToHtml(dlg.Color);
PictureBox3.BackColor = dlg.Color;
// Update preview if logo exists
if (!string.IsNullOrWhiteSpace(Model.LogoFile) && File.Exists(Model.LogoFile))
{
UpdateLogoPictureBox(Model.LogoFile);
}
}
}
finally
{
_transparentDialogOpen = false;
}
}
private void BtnSetTransparency_Click(object? sender, EventArgs e)
{
Model.SelectTransparentColorCommand.Execute(null);
}
private void OnShowMessageRequested(object? sender, Tuple<string, string, MessageBoxIcon> args)
{
if (args is null) return;
// Ensure call on UI thread
if (InvokeRequired)
{
Invoke(new Action(() => OnShowMessageRequested(sender, args)));
return;
}
MessageBox.Show(this, args.Item1, args.Item2, MessageBoxButtons.OK, args.Item3);
2025-07-28 09:00:07 +02:00
}
2025-07-29 11:10:54 +02:00
2025-07-28 14:45:03 +02:00
private void SetDefaults()
2025-07-28 09:00:07 +02:00
{
2026-02-04 22:10:16 +01:00
// Bind ComboBoxes to Model using proper data binding
2026-02-04 19:48:03 +01:00
ComboBox1.DataSource = new List<string>(Model.VerticalPositions);
2026-02-04 22:10:16 +01:00
ComboBox1.DataBindings.Add(new Binding("SelectedItem", bindingSource1, nameof(Model.VerticalPosition),
false, DataSourceUpdateMode.OnPropertyChanged));
2026-02-04 19:48:03 +01:00
ComboBox2.DataSource = new List<string>(Model.HorizontalAlignments);
2026-02-04 22:10:16 +01:00
ComboBox2.DataBindings.Add(new Binding("SelectedItem", bindingSource1, nameof(Model.HorizontalAlignment),
false, DataSourceUpdateMode.OnPropertyChanged));
2026-02-04 19:48:03 +01:00
ComboBox3.DataSource = new List<string>(Model.AvailableFonts);
2026-02-04 22:10:16 +01:00
ComboBox3.DataBindings.Add(new Binding("SelectedItem", bindingSource1, nameof(Model.FontName),
false, DataSourceUpdateMode.OnPropertyChanged));
2026-02-04 19:48:03 +01:00
ComboBox4.DataSource = new List<string>(Model.HorizontalAlignments);
2026-02-04 22:10:16 +01:00
ComboBox4.DataBindings.Add(new Binding("SelectedItem", bindingSource1, nameof(Model.LogoHorizontalPosition),
false, DataSourceUpdateMode.OnPropertyChanged));
2026-02-04 19:48:03 +01:00
ComboBox5.DataSource = new List<string> { "Alto", "Centro", "Basso" };
2026-02-04 22:10:16 +01:00
ComboBox5.DataBindings.Add(new Binding("SelectedItem", bindingSource1, nameof(Model.LogoVerticalPosition),
false, DataSourceUpdateMode.OnPropertyChanged));
2026-02-04 23:16:06 +01:00
// Bind progress bar and status labels
ProgressBar1.DataBindings.Add(new Binding("Maximum", bindingSource1, nameof(Model.ProgressBarMaximum),
false, DataSourceUpdateMode.OnPropertyChanged));
ProgressBar1.DataBindings.Add(new Binding("Value", bindingSource1, nameof(Model.ProgressBarValue),
false, DataSourceUpdateMode.OnPropertyChanged));
Label18.DataBindings.Add(new Binding("Text", bindingSource1, nameof(Model.ProcessedImagesCount),
false, DataSourceUpdateMode.OnPropertyChanged));
lblFotoTotaliNum.DataBindings.Add(new Binding("Text", bindingSource1, nameof(Model.TotalImagesCount),
false, DataSourceUpdateMode.OnPropertyChanged));
Label10.DataBindings.Add(new Binding("Text", bindingSource1, nameof(Model.ProcessingStatus),
false, DataSourceUpdateMode.OnPropertyChanged));
// Bind transparency model properties to UI
chkUseTransparentColor.DataBindings.Add(new Binding("Checked", bindingSource1, nameof(Model.UseTransparentColor), false, DataSourceUpdateMode.OnPropertyChanged));
// Show currently selected color in PictureBox3
PictureBox3.Visible = false;
if (!string.IsNullOrWhiteSpace(Model.TransparentColor))
{
try
{
PictureBox3.BackColor = ColorTranslator.FromHtml(Model.TransparentColor);
PictureBox3.Visible = true;
}
catch
{
PictureBox3.Visible = false;
}
}
// When logo file changes, update preview
Model.PropertyChanged += (s, e) =>
{
if (e.PropertyName == nameof(Model.LogoFile) || e.PropertyName == nameof(Model.UseTransparentColor) || e.PropertyName == nameof(Model.TransparentColor))
{
if (!string.IsNullOrWhiteSpace(Model.LogoFile) && System.IO.File.Exists(Model.LogoFile))
{
UpdateLogoPictureBox(Model.LogoFile);
}
}
};
// Bind transparent color hex and show color in PictureBox3
// Bind UseTransparentColor checkbox (designer control named CheckBox5 used for AddLogo earlier, add new binding control exists in designer)
// Use PictureBox3 to display color value
var colorBinding = new Binding("BackColor", bindingSource1, nameof(Model.TransparentColor), true, DataSourceUpdateMode.OnPropertyChanged);
colorBinding.Format += (s, e) =>
{
try
{
e.Value = ColorTranslator.FromHtml(e.Value?.ToString() ?? "#FFFFFF");
}
catch
{
e.Value = Color.White;
}
};
PictureBox3.DataBindings.Add(colorBinding);
// Bind checkbox for using color key transparency if such control exists (CheckBox5 was repurposed as AddLogo); create binding if available
try
{
// The designer has CheckBox5 for 'AddLogo'. We'll add a separate binding to a new control named CheckBoxUseTransparentColor if present.
var chk = this.Controls.Find("chkUseTransparentColor", true).FirstOrDefault() as CheckBox;
if (chk != null)
{
chk.DataBindings.Add(new Binding("Checked", bindingSource1, nameof(Model.UseTransparentColor), false, DataSourceUpdateMode.OnPropertyChanged));
}
}
catch { }
2026-02-04 21:12:27 +01:00
}
2021-03-04 10:44:09 +01:00
2025-07-28 09:00:07 +02:00
private void Form1_Load(object sender, EventArgs e)
{
bindingSource1.DataSource = Model;
Application.EnableVisualStyles();
2025-07-28 14:45:03 +02:00
SetDefaults();
2025-07-29 10:34:23 +02:00
2025-07-28 09:00:07 +02:00
_logger.LogInformation("Programma Avviato");
// If settings were loaded before the form was shown, ensure the logo preview is updated
try
{
if (!string.IsNullOrWhiteSpace(Model.LogoFile) && File.Exists(Model.LogoFile))
{
UpdateLogoPictureBox(Model.LogoFile);
}
}
catch (Exception ex)
{
_logger.LogDebug(ex, "Failed to load logo during form load");
}
2025-07-28 09:00:07 +02:00
}
private string CalcTime(DateTime timeStart, DateTime timeStop, int numFoto)
{
long timediffH, timediffS;
long timediffM;
2025-07-29 10:34:23 +02:00
2025-07-28 09:00:07 +02:00
TimeSpan timeDiff = timeStop - timeStart;
timediffM = (int)timeDiff.TotalMinutes;
timediffS = (int)timeDiff.TotalSeconds;
timediffH = (int)timeDiff.TotalHours;
2025-07-29 10:34:23 +02:00
2025-07-28 09:00:07 +02:00
// timediffM = DateAndTime.DateDiff(DateInterval.Minute, timeStart, timeStop);
// timediffS = DateAndTime.DateDiff(DateInterval.Second, timeStart, timeStop);
// timediffH = DateAndTime.DateDiff(DateInterval.Hour, timeStart, timeStop);
2025-07-29 10:34:23 +02:00
2025-07-28 09:00:07 +02:00
double fotoSec = numFoto / (double)timediffS;
double fotoMin = numFoto / (double)timediffM;
double fotoOra = numFoto / (double)timediffH;
string s = "S: " + timediffS.ToString() + "; F/s: " +
fotoSec.ToString(
"0.000"); // + " F/m: " + fotoMin.ToString("0.00") + " F/h: " + fotoOra.ToString("0.00")
return s;
}
private string SelectFolder(string startingFolder)
{
var dialog = new FolderBrowserDialog();
dialog.InitialDirectory = startingFolder;
if (dialog.ShowDialog() != DialogResult.OK) return string.Empty;
2025-07-29 10:34:23 +02:00
2026-02-04 19:48:03 +01:00
return FixPath(dialog.SelectedPath);
2025-07-28 09:00:07 +02:00
}
2026-02-04 23:16:06 +01:00
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('/', Path.DirectorySeparatorChar)
.Replace('\\', Path.DirectorySeparatorChar);
// Remove trailing separators then add one back
path = path.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar;
return path;
}
2026-02-04 19:48:03 +01:00
private void OnSelectSourceFolderRequested(object sender, EventArgs e)
2025-07-28 09:00:07 +02:00
{
// Prefer model value; if empty fall back to last-used value stored in user prefs
var starting = !string.IsNullOrWhiteSpace(Model.SourcePath)
? Model.SourcePath
: _parametriSetup.LeggiParametroString("LastSourceFolder");
var dialogResult = SelectFolder(starting);
2025-07-28 09:00:07 +02:00
if (!string.IsNullOrWhiteSpace(dialogResult))
2021-03-04 10:44:09 +01:00
{
2025-07-28 09:00:07 +02:00
Model.SourcePath = dialogResult;
_parametriSetup.AggiornaParametro("LastSourceFolder", dialogResult);
_parametriSetup.SalvaParametriSetup();
2021-03-04 10:44:09 +01:00
}
2025-07-28 09:00:07 +02:00
}
private void BtnOpenSourceFolder_Click(object? sender, EventArgs e)
{
// Prefer the model value but fall back to the textbox if needed
var path = string.IsNullOrWhiteSpace(Model.SourcePath) ? txtSorgente.Text : Model.SourcePath;
OpenFolder(path);
}
private void BtnOpenDestFolder_Click(object? sender, EventArgs e)
{
var path = string.IsNullOrWhiteSpace(Model.DestinationPath) ? txtDestinazione.Text : Model.DestinationPath;
OpenFolder(path);
}
private void OpenFolder(string? path)
{
if (string.IsNullOrWhiteSpace(path))
{
MessageBox.Show(this, "Folder path is empty.", "Open Folder", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
path = path.Trim().Trim('"');
try
{
if (File.Exists(path))
{
// If a file was provided, open its folder and select it
Process.Start("explorer.exe", $"/select,\"{path}\"");
return;
}
if (Directory.Exists(path))
{
Process.Start(new ProcessStartInfo
{
FileName = path,
UseShellExecute = true
});
return;
}
MessageBox.Show(this, $"Folder does not exist: {path}", "Open Folder", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
catch (Exception ex)
{
_logger?.LogError(ex, "Failed to open folder {Path}", path);
MessageBox.Show(this, $"Failed to open folder: {ex.Message}", "Open Folder", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
2026-02-04 19:48:03 +01:00
private void OnSelectDestinationFolderRequested(object sender, EventArgs e)
2025-07-28 09:00:07 +02:00
{
var starting = !string.IsNullOrWhiteSpace(Model.DestinationPath)
? Model.DestinationPath
: _parametriSetup.LeggiParametroString("LastDestinationFolder");
var dialogResult = SelectFolder(starting);
2025-07-28 09:00:07 +02:00
if (!string.IsNullOrWhiteSpace(dialogResult))
2021-03-04 10:44:09 +01:00
{
2025-07-28 09:00:07 +02:00
Model.DestinationPath = dialogResult;
_parametriSetup.AggiornaParametro("LastDestinationFolder", dialogResult);
_parametriSetup.SalvaParametriSetup();
2021-03-04 10:44:09 +01:00
}
2025-07-28 09:00:07 +02:00
}
2026-02-04 19:48:03 +01:00
private void OnSelectLogoFileRequested(object sender, EventArgs e)
2025-07-28 09:00:07 +02:00
{
2026-02-04 19:48:03 +01:00
var dialog = new OpenFileDialog();
dialog.Filter = "Image Files|*.jpg;*.jpeg;*.png;*.bmp;*.gif";
if (!string.IsNullOrWhiteSpace(Model.LogoFile))
2021-03-04 10:44:09 +01:00
{
2026-02-04 19:48:03 +01:00
dialog.FileName = Model.LogoFile;
2021-03-04 10:44:09 +01:00
}
else
{
var lastLogoFolder = _parametriSetup.LeggiParametroString("LastLogoFolder");
if (!string.IsNullOrWhiteSpace(lastLogoFolder) && Directory.Exists(lastLogoFolder))
{
dialog.InitialDirectory = lastLogoFolder;
}
}
2026-02-04 19:48:03 +01:00
if (dialog.ShowDialog() == DialogResult.OK)
2025-07-28 14:45:03 +02:00
{
2026-02-04 19:48:03 +01:00
Model.LogoFile = dialog.FileName;
UpdateLogoPictureBox(Model.LogoFile);
try
{
var folder = Path.GetDirectoryName(dialog.FileName) ?? string.Empty;
if (!string.IsNullOrWhiteSpace(folder))
{
_parametriSetup.AggiornaParametro("LastLogoFolder", folder);
_parametriSetup.SalvaParametriSetup();
}
}
catch
{
// ignore preferences save failures
}
2025-07-28 14:45:03 +02:00
}
2026-02-04 19:48:03 +01:00
}
2025-07-28 14:45:03 +02:00
2026-02-04 19:48:03 +01:00
private async void OnSaveSettingsRequested(object sender, string e)
{
var saveDialog = new SaveFileDialog
{
Filter = "Setup (*.xml)|*.xml|All valid files (*.*)|*.*",
FilterIndex = 0,
RestoreDirectory = true
};
var lastSettings = _parametriSetup.LeggiParametroString("LastSettingsFolder");
if (!string.IsNullOrWhiteSpace(lastSettings) && Directory.Exists(lastSettings))
saveDialog.InitialDirectory = lastSettings;
2026-02-04 19:48:03 +01:00
if (saveDialog.ShowDialog() != DialogResult.OK) return;
await Model.SaveSettingsToFileAsync(saveDialog.FileName);
Text = "Image Catalog - " + Path.GetFileName(saveDialog.FileName);
try
{
var folder = Path.GetDirectoryName(saveDialog.FileName) ?? string.Empty;
if (!string.IsNullOrWhiteSpace(folder))
{
_parametriSetup.AggiornaParametro("LastSettingsFolder", folder);
_parametriSetup.SalvaParametriSetup();
}
}
catch
{
// ignore
}
2025-07-28 09:00:07 +02:00
}
2021-03-04 10:44:09 +01:00
2026-02-04 19:48:03 +01:00
private async void OnLoadSettingsRequested(object sender, string e)
2025-07-28 09:00:07 +02:00
{
2026-02-04 19:48:03 +01:00
var openDialog = new OpenFileDialog
{
Filter = "Setup (*.xml)|*.xml|All valid files (*.*)|*.*",
FilterIndex = 0,
RestoreDirectory = true
};
var lastSettings = _parametriSetup.LeggiParametroString("LastSettingsFolder");
if (!string.IsNullOrWhiteSpace(lastSettings) && Directory.Exists(lastSettings))
openDialog.InitialDirectory = lastSettings;
2026-02-04 19:48:03 +01:00
if (openDialog.ShowDialog() != DialogResult.OK) return;
2026-02-04 22:10:16 +01:00
try
2025-09-19 09:53:31 +02:00
{
2026-02-04 22:10:16 +01:00
await Model.LoadSettingsFromFileAsync(openDialog.FileName);
2026-02-04 22:10:16 +01:00
// Explicitly ensure UI is enabled after loading
Model.UiEnabled = true;
// If a logo path was stored in the settings, try to resolve and display it.
// The stored path may be absolute or relative to the settings file location.
try
2026-02-04 22:10:16 +01:00
{
var storedLogo = Model.LogoFile;
// Trim whitespace and surrounding quotes which may be present in saved settings
if (!string.IsNullOrWhiteSpace(storedLogo))
{
storedLogo = storedLogo.Trim();
storedLogo = storedLogo.Trim('"');
}
if (!string.IsNullOrWhiteSpace(storedLogo))
{
string resolved = storedLogo;
// If not rooted, try to resolve relative to the settings file folder
var settingsFolder = Path.GetDirectoryName(openDialog.FileName) ?? string.Empty;
if (!Path.IsPathRooted(resolved) && !string.IsNullOrWhiteSpace(settingsFolder))
{
var candidate = Path.Combine(settingsFolder, resolved);
if (File.Exists(candidate)) resolved = candidate;
}
// If rooted but file doesn't exist, try filename near settings file
if (!File.Exists(resolved) && !string.IsNullOrWhiteSpace(settingsFolder))
{
var candidate2 = Path.Combine(settingsFolder, Path.GetFileName(resolved));
if (File.Exists(candidate2)) resolved = candidate2;
}
if (File.Exists(resolved))
{
// Update the model so data-bound controls reflect the resolved absolute path
Model.LogoFile = resolved;
UpdateLogoPictureBox(resolved);
}
}
}
catch (Exception ex)
{
_logger.LogDebug(ex, "Error resolving logo path after loading settings");
2026-02-04 22:10:16 +01:00
}
2026-02-04 19:48:03 +01:00
2026-02-04 22:10:16 +01:00
Text = "Image Catalog - " + Path.GetFileName(openDialog.FileName);
try
{
var folder = Path.GetDirectoryName(openDialog.FileName) ?? string.Empty;
if (!string.IsNullOrWhiteSpace(folder))
{
_parametriSetup.AggiornaParametro("LastSettingsFolder", folder);
_parametriSetup.SalvaParametriSetup();
}
}
catch
{
// ignore preferences save failures
}
2026-02-04 22:10:16 +01:00
_logger.LogInformation($"Settings loaded successfully from {openDialog.FileName}");
}
catch (Exception ex)
{
_logger.LogError(ex, "Error loading settings");
MessageBox.Show($"Error loading settings: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
2026-02-04 19:48:03 +01:00
}
private void MainForm_FormClosing(object? sender, FormClosingEventArgs e)
{
try
{
// Persist last-used dialogs paths (user preferences)
// These keys are managed independently from settings files
// and must be saved when the form closes.
_parametriSetup.AggiornaParametro("LastSourceFolder", Model.SourcePath ?? string.Empty);
_parametriSetup.AggiornaParametro("LastDestinationFolder", Model.DestinationPath ?? string.Empty);
_parametriSetup.AggiornaParametro("LastLogoFolder", Path.GetDirectoryName(Model.LogoFile ?? string.Empty) ?? string.Empty);
_parametriSetup.SalvaParametriSetup();
}
catch (Exception ex)
{
_logger?.LogWarning(ex, "Failed to save user preferences on exit");
}
}
2026-02-04 19:48:03 +01:00
private void OnSelectColorRequested(object sender, EventArgs e)
{
var colorDialog = new ColorDialog
{
AllowFullOpen = true
};
if (!string.IsNullOrWhiteSpace(Model.TextColorRGB))
2021-03-04 10:44:09 +01:00
{
2026-02-04 19:48:03 +01:00
try
{
colorDialog.Color = ColorTranslator.FromHtml(Model.TextColorRGB);
}
catch
{
// Invalid color, use default
}
2025-09-19 09:53:31 +02:00
}
2026-02-04 19:48:03 +01:00
if (colorDialog.ShowDialog() == DialogResult.OK)
2025-09-19 09:53:31 +02:00
{
2026-02-04 19:48:03 +01:00
Model.TextColorRGB = ColorTranslator.ToHtml(colorDialog.Color);
TextBox34.BackColor = colorDialog.Color;
2025-09-19 09:53:31 +02:00
}
2026-02-04 19:48:03 +01:00
}
2025-09-19 09:53:31 +02:00
2026-02-04 19:48:03 +01:00
private void UpdateLogoPictureBox(string logoPath)
{
try
2025-09-19 09:53:31 +02:00
{
// Load image via System.Drawing for preview so we can use MakeTransparent when requested
using var img = System.Drawing.Image.FromFile(logoPath);
System.Drawing.Bitmap previewBmp;
// If using color-key transparency and a color is selected, apply MakeTransparent for preview
if (Model.UseTransparentColor && !string.IsNullOrWhiteSpace(Model.TransparentColor))
2021-03-04 10:44:09 +01:00
{
try
{
var key = ColorTranslator.FromHtml(Model.TransparentColor);
previewBmp = new System.Drawing.Bitmap(img.Width, img.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
using (var g = System.Drawing.Graphics.FromImage(previewBmp))
{
g.Clear(System.Drawing.Color.Transparent);
g.DrawImage(img, 0, 0, img.Width, img.Height);
}
// Apply exact color-key transparency
previewBmp.MakeTransparent(key);
PictureBox3.BackColor = key;
PictureBox3.Visible = true;
}
catch
{
previewBmp = new System.Drawing.Bitmap(img);
}
2021-03-04 10:44:09 +01:00
}
2025-07-28 09:00:07 +02:00
else
2021-03-04 10:44:09 +01:00
{
previewBmp = new System.Drawing.Bitmap(img);
}
// Resize preview to fit into PictureBox1 while preserving aspect ratio
// Resize preview to fit into PictureBox1 while preserving aspect ratio
var boxW = PictureBox1.ClientSize.Width > 0 ? PictureBox1.ClientSize.Width : 449;
var boxH = PictureBox1.ClientSize.Height > 0 ? PictureBox1.ClientSize.Height : 369;
var ratio = Math.Min((double)boxW / previewBmp.Width, (double)boxH / previewBmp.Height);
var destW = Math.Max(1, (int)(previewBmp.Width * ratio));
var destH = Math.Max(1, (int)(previewBmp.Height * ratio));
var scaled = new System.Drawing.Bitmap(destW, destH, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
using (var g = System.Drawing.Graphics.FromImage(scaled))
{
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.Clear(System.Drawing.Color.Transparent);
// Center the image in the PictureBox
var offsetX = Math.Max(0, (boxW - destW) / 2);
var offsetY = Math.Max(0, (boxH - destH) / 2);
using var canvas = new System.Drawing.Bitmap(boxW, boxH, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
using (var cg = System.Drawing.Graphics.FromImage(canvas))
{
cg.Clear(System.Drawing.Color.Transparent);
cg.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
cg.DrawImage(previewBmp, offsetX, offsetY, destW, destH);
}
g.DrawImage(canvas, 0, 0);
2021-03-04 10:44:09 +01:00
}
// Set PictureBox1 image (dispose previous)
var old = PictureBox1.Image;
PictureBox1.SizeMode = PictureBoxSizeMode.CenterImage;
PictureBox1.Image = scaled;
old?.Dispose();
previewBmp.Dispose();
2025-09-19 09:53:31 +02:00
}
2026-02-04 19:48:03 +01:00
catch
{
// Image loading failed, ignore
}
2025-07-28 09:00:07 +02:00
}
2021-03-04 10:44:09 +01:00
private void UpdateLogoPreviewWithColorKey(string logoPath, Color keyColor)
{
try
{
using var img = (Bitmap)Image.FromFile(logoPath);
// Create ARGB copy
var bmp = new Bitmap(img.Width, img.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
using (var g = Graphics.FromImage(bmp))
{
g.DrawImage(img, 0, 0, img.Width, img.Height);
}
bmp.MakeTransparent(keyColor);
// Resize to PictureBox1 size similar to previous logic
Bitmap finalBmp;
if (bmp.Height >= bmp.Width)
{
finalBmp = new Bitmap(bmp, new Size((int)(160 * bmp.Width / (double)bmp.Height), 160));
}
else
{
finalBmp = new Bitmap(bmp, new Size(160, (int)(160 * bmp.Height / (double)bmp.Width)));
}
PictureBox1.Image = finalBmp;
}
catch
{
// ignore preview failures
}
}
2025-07-28 09:00:07 +02:00
private void setLabel18Text(string text)
{
if (Label18.InvokeRequired)
2021-03-04 10:44:09 +01:00
{
2025-07-28 09:00:07 +02:00
Label18.Invoke(new Action<string>(setLabel18Text), text);
}
else
{
Label18.Text = text;
2021-03-04 10:44:09 +01:00
}
2025-07-28 09:00:07 +02:00
}
}
public class PicInfo
{
public DirectoryInfo DirSource, DirDest, DirDestStart;
public string NomeImmagine;
public PicInfo(DirectoryInfo Dir_Source, DirectoryInfo Dir_Dest, DirectoryInfo Dir_DestStart,
string Nome_Immagine)
{
DirSource = Dir_Source;
DirDest = Dir_Dest;
DirDestStart = Dir_DestStart;
NomeImmagine = Nome_Immagine;
2021-03-04 10:44:09 +01:00
}
}
#endif