2021-03-04 10:44:09 +01:00
|
|
|
|
using System;
|
|
|
|
|
|
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;
|
2026-02-14 19:20:25 +01:00
|
|
|
|
using System.Diagnostics;
|
2021-03-04 10:44:09 +01:00
|
|
|
|
using System.Runtime.InteropServices;
|
2025-07-23 15:08:25 +02:00
|
|
|
|
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;
|
2025-07-23 15:08:25 +02:00
|
|
|
|
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
|
|
|
|
{
|
2025-07-28 09:49:55 +02: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;
|
2026-02-15 01:03:26 +01:00
|
|
|
|
// Prevent re-entrant updates between UI events and model PropertyChanged handling
|
|
|
|
|
|
private bool _suppressRadioUpdates = false;
|
2026-02-15 11:13:23 +01:00
|
|
|
|
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,
|
2026-02-14 22:18:56 +01:00
|
|
|
|
ParametriSetup parametriSetup, ILogger<MainForm> logger)
|
2021-03-04 10:44:09 +01:00
|
|
|
|
{
|
2025-07-28 09:49:55 +02: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();
|
2026-02-15 01:03:26 +01:00
|
|
|
|
// 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
|
|
|
|
|
2026-02-16 19:55:37 +01: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));
|
2026-02-16 18:58:15 +01:00
|
|
|
|
|
2026-02-14 20:38:51 +01:00
|
|
|
|
// Save user preferences on form close instead of immediately when dialogs are used
|
|
|
|
|
|
this.FormClosing += MainForm_FormClosing;
|
|
|
|
|
|
|
2026-02-14 19:20:25 +01:00
|
|
|
|
// Wire up 'Open folder in Explorer' buttons
|
|
|
|
|
|
btnOpenSourceFolder.Click += BtnOpenSourceFolder_Click;
|
|
|
|
|
|
btnOpenDestFolder.Click += BtnOpenDestFolder_Click;
|
|
|
|
|
|
|
2026-02-15 11:13:23 +01:00
|
|
|
|
// Show currently selected color in small PictureBox3
|
|
|
|
|
|
PictureBox3.BackColor = ColorTranslator.FromHtml(Model.TransparentColor);
|
|
|
|
|
|
|
2026-02-14 22:18:56 +01:00
|
|
|
|
// 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
|
|
|
|
|
2026-02-15 11:13:23 +01:00
|
|
|
|
|
|
|
|
|
|
|
2026-02-15 01:03:26 +01:00
|
|
|
|
private void RdbLibrary_CheckedChanged(object? sender, EventArgs e)
|
2025-07-28 09:00:07 +02:00
|
|
|
|
{
|
2026-02-15 01:03:26 +01: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;
|
2026-02-16 18:58:15 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-16 19:55:37 +01:00
|
|
|
|
// Thumbnail mode changes - reflect back to combo box index
|
2026-02-16 18:58:15 +01:00
|
|
|
|
if (e.PropertyName == nameof(Model.ThumbnailMode) ||
|
2026-02-16 19:55:37 +01:00
|
|
|
|
e.PropertyName == nameof(Model.ThumbnailOption) ||
|
|
|
|
|
|
e.PropertyName == nameof(Model.ThumbnailOptionIndex))
|
2026-02-16 18:58:15 +01:00
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
_suppressRadioUpdates = true;
|
2026-02-16 19:55:37 +01:00
|
|
|
|
comboThumbnailOption.SelectedIndex = Model.ThumbnailOptionIndex;
|
2026-02-16 18:58:15 +01:00
|
|
|
|
}
|
|
|
|
|
|
finally
|
|
|
|
|
|
{
|
|
|
|
|
|
_suppressRadioUpdates = false;
|
2026-02-15 01:03:26 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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);
|
2026-02-15 11:13:23 +01:00
|
|
|
|
// 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;
|
2026-02-15 11:13:23 +01:00
|
|
|
|
Model.SelectTransparentColorRequested += OnSelectTransparentColorRequested;
|
2026-02-14 19:20:25 +01:00
|
|
|
|
// Show message requests (from ViewModel validation)
|
|
|
|
|
|
Model.ShowMessageRequested += OnShowMessageRequested;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-15 11:13:23 +01:00
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-14 19:20:25 +01:00
|
|
|
|
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));
|
2026-02-15 11:13:23 +01:00
|
|
|
|
|
|
|
|
|
|
// 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");
|
2026-02-15 11:13:23 +01:00
|
|
|
|
// 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
|
|
|
|
{
|
2026-02-14 20:38:51 +01: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;
|
2026-02-14 20:38:51 +01:00
|
|
|
|
_parametriSetup.AggiornaParametro("LastSourceFolder", dialogResult);
|
|
|
|
|
|
_parametriSetup.SalvaParametriSetup();
|
2021-03-04 10:44:09 +01:00
|
|
|
|
}
|
2025-07-28 09:00:07 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-14 19:20:25 +01: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
|
|
|
|
{
|
2026-02-14 20:38:51 +01: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;
|
2026-02-14 20:38:51 +01:00
|
|
|
|
_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";
|
2026-02-14 20:38:51 +01:00
|
|
|
|
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
|
|
|
|
}
|
2026-02-14 20:38:51 +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);
|
2026-02-14 20:38:51 +01:00
|
|
|
|
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
|
|
|
|
|
|
};
|
2026-02-14 20:38:51 +01:00
|
|
|
|
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);
|
2026-02-14 20:38:51 +01:00
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-02-14 20:38:51 +01:00
|
|
|
|
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-15 11:13:23 +01:00
|
|
|
|
|
2026-02-04 22:10:16 +01:00
|
|
|
|
// Explicitly ensure UI is enabled after loading
|
|
|
|
|
|
Model.UiEnabled = true;
|
2026-02-15 11:13:23 +01:00
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
{
|
2026-02-15 11:13:23 +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);
|
|
|
|
|
|
|
2026-02-14 20:38:51 +01:00
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-14 20:38:51 +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
|
|
|
|
{
|
2026-02-15 11:13:23 +01: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
|
|
|
|
{
|
2026-02-15 11:13:23 +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
|
|
|
|
{
|
2026-02-15 11:13:23 +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
|
|
|
|
}
|
2026-02-15 11:13:23 +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
|
|
|
|
|
2026-02-15 11:13:23 +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
|
|
|
|
}
|
|
|
|
|
|
}
|