From d1e86b18e3ee302d9f6b33346419690244d9aed2 Mon Sep 17 00:00:00 2001 From: Ben Olden-Cooligan Date: Sat, 2 Jul 2022 16:12:34 -0700 Subject: [PATCH] Use file-scoped namespaces in forms --- NAPS2.Lib.Common/WinForms/DialogHelper.cs | 15 +- .../WinForms/EmailProviderWidget.cs | 75 +- NAPS2.Lib.WinForms/WinForms/FAuthorize.cs | 107 +- NAPS2.Lib.WinForms/WinForms/FBatchScan.cs | 659 ++++---- NAPS2.Lib.WinForms/WinForms/FCrop.cs | 397 +++-- NAPS2.Lib.WinForms/WinForms/FDesktop.cs | 1483 ++++++++--------- NAPS2.Lib.WinForms/WinForms/FEmailProvider.cs | 227 ++- NAPS2.Lib.WinForms/WinForms/FHueSaturation.cs | 139 +- .../WinForms/FOcrLanguageDownload.cs | 157 +- NAPS2.Lib.WinForms/WinForms/FPdfPassword.cs | 63 +- NAPS2.Lib.WinForms/WinForms/FProgress.cs | 157 +- NAPS2.Lib.WinForms/WinForms/FRecover.cs | 65 +- NAPS2.Lib.WinForms/WinForms/FSelectDevice.cs | 107 +- NAPS2.Lib.WinForms/WinForms/FViewer.cs | 1215 +++++++------- NAPS2.Lib.WinForms/WinForms/ILProfileIcons.cs | 27 +- NAPS2.Lib.WinForms/WinForms/ImageForm.cs | 319 ++-- .../WinForms/OperationProgressNotifyWidget.cs | 115 +- NAPS2.Lib.WinForms/WinForms/TiffViewer.cs | 317 ++-- 18 files changed, 2813 insertions(+), 2831 deletions(-) diff --git a/NAPS2.Lib.Common/WinForms/DialogHelper.cs b/NAPS2.Lib.Common/WinForms/DialogHelper.cs index c213e9f83..cdca7ac60 100644 --- a/NAPS2.Lib.Common/WinForms/DialogHelper.cs +++ b/NAPS2.Lib.Common/WinForms/DialogHelper.cs @@ -1,11 +1,10 @@ -namespace NAPS2.WinForms +namespace NAPS2.WinForms; + +public abstract class DialogHelper { - public abstract class DialogHelper - { - public abstract bool PromptToSavePdfOrImage(string defaultPath, out string savePath); + public abstract bool PromptToSavePdfOrImage(string defaultPath, out string savePath); - public abstract bool PromptToSavePdf(string defaultPath, out string savePath); + public abstract bool PromptToSavePdf(string defaultPath, out string savePath); - public abstract bool PromptToSaveImage(string defaultPath, out string savePath); - } -} + public abstract bool PromptToSaveImage(string defaultPath, out string savePath); +} \ No newline at end of file diff --git a/NAPS2.Lib.WinForms/WinForms/EmailProviderWidget.cs b/NAPS2.Lib.WinForms/WinForms/EmailProviderWidget.cs index 2e43a909d..0a6891e88 100644 --- a/NAPS2.Lib.WinForms/WinForms/EmailProviderWidget.cs +++ b/NAPS2.Lib.WinForms/WinForms/EmailProviderWidget.cs @@ -2,44 +2,43 @@ using System.Windows.Forms; using NAPS2.ImportExport.Email; -namespace NAPS2.WinForms +namespace NAPS2.WinForms; + +public partial class EmailProviderWidget : Button { - public partial class EmailProviderWidget : Button + public EmailProviderWidget() { - public EmailProviderWidget() - { - InitializeComponent(); - } - - public EmailProviderType ProviderType { get; set; } - - public Image ProviderIcon - { - get => pboxIcon.Image; - set => pboxIcon.Image = value; - } - - public string ProviderName - { - get => Text; - set => Text = value; - } - - public Action? ClickAction { get; set; } - - private void EmailProviderWidget_MouseEnter(object sender, EventArgs e) - { - BackColor = Color.FromArgb(229, 241, 251); - } - - private void EmailProviderWidget_MouseLeave(object sender, EventArgs e) - { - BackColor = DefaultBackColor; - } - - private void EmailProviderWidget_Click(object sender, EventArgs e) - { - ClickAction?.Invoke(); - } + InitializeComponent(); } -} + + public EmailProviderType ProviderType { get; set; } + + public Image ProviderIcon + { + get => pboxIcon.Image; + set => pboxIcon.Image = value; + } + + public string ProviderName + { + get => Text; + set => Text = value; + } + + public Action? ClickAction { get; set; } + + private void EmailProviderWidget_MouseEnter(object sender, EventArgs e) + { + BackColor = Color.FromArgb(229, 241, 251); + } + + private void EmailProviderWidget_MouseLeave(object sender, EventArgs e) + { + BackColor = DefaultBackColor; + } + + private void EmailProviderWidget_Click(object sender, EventArgs e) + { + ClickAction?.Invoke(); + } +} \ No newline at end of file diff --git a/NAPS2.Lib.WinForms/WinForms/FAuthorize.cs b/NAPS2.Lib.WinForms/WinForms/FAuthorize.cs index d0544af65..2109f9693 100644 --- a/NAPS2.Lib.WinForms/WinForms/FAuthorize.cs +++ b/NAPS2.Lib.WinForms/WinForms/FAuthorize.cs @@ -3,60 +3,59 @@ using System.Threading; using System.Windows.Forms; using NAPS2.ImportExport.Email.Oauth; -namespace NAPS2.WinForms +namespace NAPS2.WinForms; + +public partial class FAuthorize : FormBase { - public partial class FAuthorize : FormBase + private readonly ErrorOutput _errorOutput; + private CancellationTokenSource? _cancelTokenSource; + + public FAuthorize(ErrorOutput errorOutput) { - private readonly ErrorOutput _errorOutput; - private CancellationTokenSource? _cancelTokenSource; - - public FAuthorize(ErrorOutput errorOutput) - { - _errorOutput = errorOutput; - RestoreFormState = false; - InitializeComponent(); - } - - public OauthProvider? OauthProvider { get; set; } - - private void FAuthorize_Load(object sender, EventArgs e) - { - if (OauthProvider == null) throw new InvalidOperationException("OauthProvider must be specified"); - - MaximumSize = new Size(Math.Max(lblWaiting.Width + 142, 272), Height); - MinimumSize = new Size(Math.Max(lblWaiting.Width + 142, 272), Height); - - _cancelTokenSource = new CancellationTokenSource(); - Task.Run(() => - { - try - { - OauthProvider.AcquireToken(_cancelTokenSource.Token); - Invoke(() => - { - DialogResult = DialogResult.OK; - Close(); - }); - } - catch (OperationCanceledException) - { - } - catch (Exception ex) - { - _errorOutput.DisplayError(MiscResources.AuthError, ex); - Log.ErrorException("Error acquiring Oauth token", ex); - Invoke(() => - { - DialogResult = DialogResult.Cancel; - Close(); - }); - } - }); - } - - private void FAuthorize_FormClosed(object sender, FormClosedEventArgs e) - { - _cancelTokenSource?.Cancel(); - } + _errorOutput = errorOutput; + RestoreFormState = false; + InitializeComponent(); } -} + + public OauthProvider? OauthProvider { get; set; } + + private void FAuthorize_Load(object sender, EventArgs e) + { + if (OauthProvider == null) throw new InvalidOperationException("OauthProvider must be specified"); + + MaximumSize = new Size(Math.Max(lblWaiting.Width + 142, 272), Height); + MinimumSize = new Size(Math.Max(lblWaiting.Width + 142, 272), Height); + + _cancelTokenSource = new CancellationTokenSource(); + Task.Run(() => + { + try + { + OauthProvider.AcquireToken(_cancelTokenSource.Token); + Invoke(() => + { + DialogResult = DialogResult.OK; + Close(); + }); + } + catch (OperationCanceledException) + { + } + catch (Exception ex) + { + _errorOutput.DisplayError(MiscResources.AuthError, ex); + Log.ErrorException("Error acquiring Oauth token", ex); + Invoke(() => + { + DialogResult = DialogResult.Cancel; + Close(); + }); + } + }); + } + + private void FAuthorize_FormClosed(object sender, FormClosedEventArgs e) + { + _cancelTokenSource?.Cancel(); + } +} \ No newline at end of file diff --git a/NAPS2.Lib.WinForms/WinForms/FBatchScan.cs b/NAPS2.Lib.WinForms/WinForms/FBatchScan.cs index e0183bea0..452787bec 100644 --- a/NAPS2.Lib.WinForms/WinForms/FBatchScan.cs +++ b/NAPS2.Lib.WinForms/WinForms/FBatchScan.cs @@ -7,367 +7,366 @@ using NAPS2.Scan; using NAPS2.Scan.Batch; using NAPS2.Scan.Exceptions; -namespace NAPS2.WinForms +namespace NAPS2.WinForms; + +public partial class FBatchScan : FormBase { - public partial class FBatchScan : FormBase - { - public const string PATCH_CODE_INFO_URL = "http://www.naps2.com/doc-batch-scan.html#patch-t"; + public const string PATCH_CODE_INFO_URL = "http://www.naps2.com/doc-batch-scan.html#patch-t"; - private readonly IBatchScanPerformer _batchScanPerformer; - private readonly ErrorOutput _errorOutput; - private readonly DialogHelper _dialogHelper; - private readonly IProfileManager _profileManager; - private TransactionConfigScope _userTransact; - private Naps2Config _transactionConfig; + private readonly IBatchScanPerformer _batchScanPerformer; + private readonly ErrorOutput _errorOutput; + private readonly DialogHelper _dialogHelper; + private readonly IProfileManager _profileManager; + private TransactionConfigScope _userTransact; + private Naps2Config _transactionConfig; - private bool _batchRunning; - private CancellationTokenSource _cts = new CancellationTokenSource(); + private bool _batchRunning; + private CancellationTokenSource _cts = new CancellationTokenSource(); - public FBatchScan(IBatchScanPerformer batchScanPerformer, ErrorOutput errorOutput, DialogHelper dialogHelper, IProfileManager profileManager) + public FBatchScan(IBatchScanPerformer batchScanPerformer, ErrorOutput errorOutput, DialogHelper dialogHelper, IProfileManager profileManager) + { + _batchScanPerformer = batchScanPerformer; + _errorOutput = errorOutput; + _dialogHelper = dialogHelper; + _profileManager = profileManager; + InitializeComponent(); + + RestoreFormState = false; + } + + public Action ImageCallback { get; set; } + + protected override void OnLoad(object sender, EventArgs eventArgs) + { + new LayoutManager(this) + .Bind(groupboxScanConfig, groupboxOutput, + panelSaveSeparator, panelSaveTo, panelSaveType, panelScanDetails, panelScanType, + comboProfile, txtFilePath, lblStatus) + .WidthToForm() + .Bind(btnEditProfile, btnAddProfile, btnStart, btnCancel, btnChooseFolder) + .RightToForm() + .Activate(); + + btnAddProfile.Enabled = !(Config.Get(c => c.NoUserProfiles) && _profileManager.Profiles.Any(x => x.IsLocked)); + + ConditionalControls.LockHeight(this); + + // TODO: Granular + _userTransact = Config.User.BeginTransaction(); + _transactionConfig = Config.WithTransaction(_userTransact); + UpdateUIFromSettings(); + } + + private void UpdateUIFromSettings() + { + UpdateProfiles(); + + rdSingleScan.Checked = _transactionConfig.Get(c => c.BatchSettings.ScanType) == BatchScanType.Single; + rdMultipleScansPrompt.Checked = _transactionConfig.Get(c => c.BatchSettings.ScanType) == BatchScanType.MultipleWithPrompt; + rdMultipleScansDelay.Checked = _transactionConfig.Get(c => c.BatchSettings.ScanType) == BatchScanType.MultipleWithDelay; + + // TODO: Verify culture (+ vaildation ofc) + txtNumberOfScans.Text = _transactionConfig.Get(c => c.BatchSettings.ScanCount).ToString(CultureInfo.CurrentCulture); + txtTimeBetweenScans.Text = _transactionConfig.Get(c => c.BatchSettings.ScanIntervalSeconds).ToString(CultureInfo.CurrentCulture); + + rdLoadIntoNaps2.Checked = _transactionConfig.Get(c => c.BatchSettings.OutputType) == BatchOutputType.Load; + rdSaveToSingleFile.Checked = _transactionConfig.Get(c => c.BatchSettings.OutputType) == BatchOutputType.SingleFile; + rdSaveToMultipleFiles.Checked = _transactionConfig.Get(c => c.BatchSettings.OutputType) == BatchOutputType.MultipleFiles; + + rdFilePerScan.Checked = _transactionConfig.Get(c => c.BatchSettings.SaveSeparator) == SaveSeparator.FilePerScan; + rdFilePerPage.Checked = _transactionConfig.Get(c => c.BatchSettings.SaveSeparator) == SaveSeparator.FilePerPage; + rdSeparateByPatchT.Checked = _transactionConfig.Get(c => c.BatchSettings.SaveSeparator) == SaveSeparator.PatchT; + + txtFilePath.Text = _transactionConfig.Get(c => c.BatchSettings.SavePath); + } + + private bool ValidateSettings() + { + bool ok = true; + + _userTransact.Set(c => c.BatchSettings.ProfileDisplayName, comboProfile.Text); + if (comboProfile.SelectedIndex == -1) { - _batchScanPerformer = batchScanPerformer; - _errorOutput = errorOutput; - _dialogHelper = dialogHelper; - _profileManager = profileManager; - InitializeComponent(); - - RestoreFormState = false; + ok = false; + comboProfile.Focus(); } - public Action ImageCallback { get; set; } + _userTransact.Set(c => c.BatchSettings.ScanType, rdMultipleScansPrompt.Checked ? BatchScanType.MultipleWithPrompt + : rdMultipleScansDelay.Checked ? BatchScanType.MultipleWithDelay + : BatchScanType.Single); - protected override void OnLoad(object sender, EventArgs eventArgs) + if (rdMultipleScansDelay.Checked) { - new LayoutManager(this) - .Bind(groupboxScanConfig, groupboxOutput, - panelSaveSeparator, panelSaveTo, panelSaveType, panelScanDetails, panelScanType, - comboProfile, txtFilePath, lblStatus) - .WidthToForm() - .Bind(btnEditProfile, btnAddProfile, btnStart, btnCancel, btnChooseFolder) - .RightToForm() - .Activate(); - - btnAddProfile.Enabled = !(Config.Get(c => c.NoUserProfiles) && _profileManager.Profiles.Any(x => x.IsLocked)); - - ConditionalControls.LockHeight(this); - - // TODO: Granular - _userTransact = Config.User.BeginTransaction(); - _transactionConfig = Config.WithTransaction(_userTransact); - UpdateUIFromSettings(); - } - - private void UpdateUIFromSettings() - { - UpdateProfiles(); - - rdSingleScan.Checked = _transactionConfig.Get(c => c.BatchSettings.ScanType) == BatchScanType.Single; - rdMultipleScansPrompt.Checked = _transactionConfig.Get(c => c.BatchSettings.ScanType) == BatchScanType.MultipleWithPrompt; - rdMultipleScansDelay.Checked = _transactionConfig.Get(c => c.BatchSettings.ScanType) == BatchScanType.MultipleWithDelay; - - // TODO: Verify culture (+ vaildation ofc) - txtNumberOfScans.Text = _transactionConfig.Get(c => c.BatchSettings.ScanCount).ToString(CultureInfo.CurrentCulture); - txtTimeBetweenScans.Text = _transactionConfig.Get(c => c.BatchSettings.ScanIntervalSeconds).ToString(CultureInfo.CurrentCulture); - - rdLoadIntoNaps2.Checked = _transactionConfig.Get(c => c.BatchSettings.OutputType) == BatchOutputType.Load; - rdSaveToSingleFile.Checked = _transactionConfig.Get(c => c.BatchSettings.OutputType) == BatchOutputType.SingleFile; - rdSaveToMultipleFiles.Checked = _transactionConfig.Get(c => c.BatchSettings.OutputType) == BatchOutputType.MultipleFiles; - - rdFilePerScan.Checked = _transactionConfig.Get(c => c.BatchSettings.SaveSeparator) == SaveSeparator.FilePerScan; - rdFilePerPage.Checked = _transactionConfig.Get(c => c.BatchSettings.SaveSeparator) == SaveSeparator.FilePerPage; - rdSeparateByPatchT.Checked = _transactionConfig.Get(c => c.BatchSettings.SaveSeparator) == SaveSeparator.PatchT; - - txtFilePath.Text = _transactionConfig.Get(c => c.BatchSettings.SavePath); - } - - private bool ValidateSettings() - { - bool ok = true; - - _userTransact.Set(c => c.BatchSettings.ProfileDisplayName, comboProfile.Text); - if (comboProfile.SelectedIndex == -1) + if (!int.TryParse(txtNumberOfScans.Text, out int scanCount) || scanCount <= 0) { ok = false; - comboProfile.Focus(); + scanCount = 0; + txtNumberOfScans.Focus(); } + _userTransact.Set(c => c.BatchSettings.ScanCount, scanCount); - _userTransact.Set(c => c.BatchSettings.ScanType, rdMultipleScansPrompt.Checked ? BatchScanType.MultipleWithPrompt - : rdMultipleScansDelay.Checked ? BatchScanType.MultipleWithDelay - : BatchScanType.Single); - - if (rdMultipleScansDelay.Checked) - { - if (!int.TryParse(txtNumberOfScans.Text, out int scanCount) || scanCount <= 0) - { - ok = false; - scanCount = 0; - txtNumberOfScans.Focus(); - } - _userTransact.Set(c => c.BatchSettings.ScanCount, scanCount); - - if (!double.TryParse(txtTimeBetweenScans.Text, out double scanInterval) || scanInterval < 0) - { - ok = false; - scanInterval = 0; - txtTimeBetweenScans.Focus(); - } - _userTransact.Set(c => c.BatchSettings.ScanIntervalSeconds, scanInterval); - } - - _userTransact.Set(c => c.BatchSettings.OutputType, rdSaveToSingleFile.Checked ? BatchOutputType.SingleFile - : rdSaveToMultipleFiles.Checked ? BatchOutputType.MultipleFiles - : BatchOutputType.Load); - - _userTransact.Set(c => c.BatchSettings.SaveSeparator, rdFilePerScan.Checked ? SaveSeparator.FilePerScan - : rdSeparateByPatchT.Checked ? SaveSeparator.PatchT - : SaveSeparator.FilePerPage); - - _userTransact.Set(c => c.BatchSettings.SavePath, txtFilePath.Text); - if (_transactionConfig.Get(c => c.BatchSettings.OutputType) != BatchOutputType.Load && string.IsNullOrWhiteSpace(_transactionConfig.Get(c => c.BatchSettings.SavePath))) + if (!double.TryParse(txtTimeBetweenScans.Text, out double scanInterval) || scanInterval < 0) { ok = false; - txtFilePath.Focus(); + scanInterval = 0; + txtTimeBetweenScans.Focus(); } - - return ok; + _userTransact.Set(c => c.BatchSettings.ScanIntervalSeconds, scanInterval); } - private void UpdateProfiles() + _userTransact.Set(c => c.BatchSettings.OutputType, rdSaveToSingleFile.Checked ? BatchOutputType.SingleFile + : rdSaveToMultipleFiles.Checked ? BatchOutputType.MultipleFiles + : BatchOutputType.Load); + + _userTransact.Set(c => c.BatchSettings.SaveSeparator, rdFilePerScan.Checked ? SaveSeparator.FilePerScan + : rdSeparateByPatchT.Checked ? SaveSeparator.PatchT + : SaveSeparator.FilePerPage); + + _userTransact.Set(c => c.BatchSettings.SavePath, txtFilePath.Text); + if (_transactionConfig.Get(c => c.BatchSettings.OutputType) != BatchOutputType.Load && string.IsNullOrWhiteSpace(_transactionConfig.Get(c => c.BatchSettings.SavePath))) { - comboProfile.Items.Clear(); - comboProfile.Items.AddRange(_profileManager.Profiles.Cast().ToArray()); - if (!string.IsNullOrEmpty(_transactionConfig.Get(c => c.BatchSettings.ProfileDisplayName)) && - _profileManager.Profiles.Any(x => x.DisplayName == _transactionConfig.Get(c => c.BatchSettings.ProfileDisplayName))) - { - comboProfile.Text = _transactionConfig.Get(c => c.BatchSettings.ProfileDisplayName); - } - else if (_profileManager.DefaultProfile != null) - { - comboProfile.Text = _profileManager.DefaultProfile.DisplayName; - } - else - { - comboProfile.Text = ""; - } + ok = false; + txtFilePath.Focus(); } - private void rdSingleScan_CheckedChanged(object sender, EventArgs e) - { - ConditionalControls.UnlockHeight(this); - ConditionalControls.SetVisible(rdFilePerScan, !rdSingleScan.Checked && rdSaveToMultipleFiles.Checked); - ConditionalControls.LockHeight(this); - } + return ok; + } - private void rdMultipleScansDelay_CheckedChanged(object sender, EventArgs e) + private void UpdateProfiles() + { + comboProfile.Items.Clear(); + comboProfile.Items.AddRange(_profileManager.Profiles.Cast().ToArray()); + if (!string.IsNullOrEmpty(_transactionConfig.Get(c => c.BatchSettings.ProfileDisplayName)) && + _profileManager.Profiles.Any(x => x.DisplayName == _transactionConfig.Get(c => c.BatchSettings.ProfileDisplayName))) { - ConditionalControls.UnlockHeight(this); - ConditionalControls.SetVisible(panelScanDetails, rdMultipleScansDelay.Checked); - ConditionalControls.LockHeight(this); + comboProfile.Text = _transactionConfig.Get(c => c.BatchSettings.ProfileDisplayName); } - - private void rdLoadIntoNaps2_CheckedChanged(object sender, EventArgs e) + else if (_profileManager.DefaultProfile != null) { - ConditionalControls.UnlockHeight(this); - ConditionalControls.SetVisible(panelSaveTo, !rdLoadIntoNaps2.Checked); - ConditionalControls.LockHeight(this); + comboProfile.Text = _profileManager.DefaultProfile.DisplayName; } - - private void rdSaveToMultipleFiles_CheckedChanged(object sender, EventArgs e) + else { - ConditionalControls.UnlockHeight(this); - ConditionalControls.SetVisible(panelSaveSeparator, rdSaveToMultipleFiles.Checked); - ConditionalControls.SetVisible(rdFilePerScan, !rdSingleScan.Checked && rdSaveToMultipleFiles.Checked); - ConditionalControls.LockHeight(this); + comboProfile.Text = ""; } + } - private void btnChooseFolder_Click(object sender, EventArgs e) + private void rdSingleScan_CheckedChanged(object sender, EventArgs e) + { + ConditionalControls.UnlockHeight(this); + ConditionalControls.SetVisible(rdFilePerScan, !rdSingleScan.Checked && rdSaveToMultipleFiles.Checked); + ConditionalControls.LockHeight(this); + } + + private void rdMultipleScansDelay_CheckedChanged(object sender, EventArgs e) + { + ConditionalControls.UnlockHeight(this); + ConditionalControls.SetVisible(panelScanDetails, rdMultipleScansDelay.Checked); + ConditionalControls.LockHeight(this); + } + + private void rdLoadIntoNaps2_CheckedChanged(object sender, EventArgs e) + { + ConditionalControls.UnlockHeight(this); + ConditionalControls.SetVisible(panelSaveTo, !rdLoadIntoNaps2.Checked); + ConditionalControls.LockHeight(this); + } + + private void rdSaveToMultipleFiles_CheckedChanged(object sender, EventArgs e) + { + ConditionalControls.UnlockHeight(this); + ConditionalControls.SetVisible(panelSaveSeparator, rdSaveToMultipleFiles.Checked); + ConditionalControls.SetVisible(rdFilePerScan, !rdSingleScan.Checked && rdSaveToMultipleFiles.Checked); + ConditionalControls.LockHeight(this); + } + + private void btnChooseFolder_Click(object sender, EventArgs e) + { + if (_dialogHelper.PromptToSavePdfOrImage(null, out string savePath)) { - if (_dialogHelper.PromptToSavePdfOrImage(null, out string savePath)) + txtFilePath.Text = savePath; + } + } + + private void linkPatchCodeInfo_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) + { + Process.Start(PATCH_CODE_INFO_URL); + } + + private void comboProfile_Format(object sender, ListControlConvertEventArgs e) + { + e.Value = ((ScanProfile)e.ListItem).DisplayName; + } + + private void btnEditProfile_Click(object sender, EventArgs e) + { + var originalProfile = (ScanProfile) comboProfile.SelectedItem; + if (originalProfile != null) + { + var fedit = FormFactory.Create(); + fedit.ScanProfile = originalProfile; + fedit.ShowDialog(); + if (fedit.Result) { - txtFilePath.Text = savePath; - } - } - - private void linkPatchCodeInfo_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) - { - Process.Start(PATCH_CODE_INFO_URL); - } - - private void comboProfile_Format(object sender, ListControlConvertEventArgs e) - { - e.Value = ((ScanProfile)e.ListItem).DisplayName; - } - - private void btnEditProfile_Click(object sender, EventArgs e) - { - var originalProfile = (ScanProfile) comboProfile.SelectedItem; - if (originalProfile != null) - { - var fedit = FormFactory.Create(); - fedit.ScanProfile = originalProfile; - fedit.ShowDialog(); - if (fedit.Result) - { - _profileManager.Mutate(new ListMutation.ReplaceWith(fedit.ScanProfile), ListSelection.Of(originalProfile)); - _userTransact.Set(c => c.BatchSettings.ProfileDisplayName, fedit.ScanProfile.DisplayName); - UpdateProfiles(); - } - } - } - - private void btnAddProfile_Click(object sender, EventArgs e) - { - if (!(Config.Get(c => c.NoUserProfiles) && _profileManager.Profiles.Any(x => x.IsLocked))) - { - var fedit = FormFactory.Create(); - fedit.ScanProfile = Config.Get(c => c.DefaultProfileSettings); - fedit.ShowDialog(); - if (fedit.Result) - { - _profileManager.Mutate(new ListMutation.Append(fedit.ScanProfile), ListSelection.Empty()); - _userTransact.Set(c => c.BatchSettings.ProfileDisplayName, fedit.ScanProfile.DisplayName); - UpdateProfiles(); - } - } - } - - private void btnStart_Click(object sender, EventArgs e) - { - if (_batchRunning) - { - return; - } - if (!ValidateSettings()) - { - return; - } - - // Update state - _batchRunning = true; - _cts = new CancellationTokenSource(); - - // Update UI - btnStart.Enabled = false; - btnCancel.Enabled = true; - btnCancel.Text = MiscResources.Cancel; - EnableDisableSettings(false); - - // Start the batch - DoBatchScan().AssertNoAwait(); - - // Save settings for next time (could also do on form close) - _userTransact.Commit(); - } - - private void EnableDisableSettings(bool enabled) - { - EnableDisable(groupboxScanConfig, enabled); - EnableDisable(groupboxOutput, enabled); - } - - private void EnableDisable(Control root, bool enabled) - { - foreach (Control control in root.Controls) - { - if (control.Controls.Count > 0) - { - EnableDisable(control, enabled); - } - else - { - control.Enabled = enabled; - } - } - } - - private async Task DoBatchScan() - { - try - { - await _batchScanPerformer.PerformBatchScan(Config.Get(c => c.BatchSettings), this, - image => SafeInvoke(() => ImageCallback(image)), ProgressCallback, _cts.Token); - SafeInvoke(() => - { - lblStatus.Text = _cts.IsCancellationRequested - ? MiscResources.BatchStatusCancelled - : MiscResources.BatchStatusComplete; - }); - } - catch (ScanDriverException ex) - { - if (ex is ScanDriverUnknownException) - { - Log.ErrorException("Error in batch scan", ex); - _errorOutput.DisplayError(ex.Message, ex); - } - else - { - _errorOutput.DisplayError(ex.Message); - } - } - catch (Exception ex) - { - Log.ErrorException("Error in batch scan", ex); - _errorOutput.DisplayError(MiscResources.BatchError, ex); - SafeInvoke(() => - { - lblStatus.Text = MiscResources.BatchStatusError; - }); - } - SafeInvoke(() => - { - _batchRunning = false; - _cts = new CancellationTokenSource(); - btnStart.Enabled = true; - btnCancel.Enabled = true; - btnCancel.Text = MiscResources.Close; - EnableDisableSettings(true); - Activate(); - }); - } - - private void ProgressCallback(string status) - { - SafeInvoke(() => - { - lblStatus.Text = status; - }); - } - - private void btnCancel_Click(object sender, EventArgs e) - { - if (_batchRunning) - { - if (MessageBox.Show(MiscResources.ConfirmCancelBatch, MiscResources.CancelBatch, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) - { - _cts.Cancel(); - btnCancel.Enabled = false; - lblStatus.Text = MiscResources.BatchStatusCancelling; - } - } - else - { - Close(); - } - } - - private void FBatchScan_FormClosing(object sender, FormClosingEventArgs e) - { - if (_cts.IsCancellationRequested) - { - // Keep dialog open while cancel is in progress to avoid concurrency issues - e.Cancel = true; - } - } - - private void linkPlaceholders_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) - { - var form = FormFactory.Create(); - form.FileName = txtFilePath.Text; - if (form.ShowDialog() == DialogResult.OK) - { - txtFilePath.Text = form.FileName; + _profileManager.Mutate(new ListMutation.ReplaceWith(fedit.ScanProfile), ListSelection.Of(originalProfile)); + _userTransact.Set(c => c.BatchSettings.ProfileDisplayName, fedit.ScanProfile.DisplayName); + UpdateProfiles(); } } } -} + + private void btnAddProfile_Click(object sender, EventArgs e) + { + if (!(Config.Get(c => c.NoUserProfiles) && _profileManager.Profiles.Any(x => x.IsLocked))) + { + var fedit = FormFactory.Create(); + fedit.ScanProfile = Config.Get(c => c.DefaultProfileSettings); + fedit.ShowDialog(); + if (fedit.Result) + { + _profileManager.Mutate(new ListMutation.Append(fedit.ScanProfile), ListSelection.Empty()); + _userTransact.Set(c => c.BatchSettings.ProfileDisplayName, fedit.ScanProfile.DisplayName); + UpdateProfiles(); + } + } + } + + private void btnStart_Click(object sender, EventArgs e) + { + if (_batchRunning) + { + return; + } + if (!ValidateSettings()) + { + return; + } + + // Update state + _batchRunning = true; + _cts = new CancellationTokenSource(); + + // Update UI + btnStart.Enabled = false; + btnCancel.Enabled = true; + btnCancel.Text = MiscResources.Cancel; + EnableDisableSettings(false); + + // Start the batch + DoBatchScan().AssertNoAwait(); + + // Save settings for next time (could also do on form close) + _userTransact.Commit(); + } + + private void EnableDisableSettings(bool enabled) + { + EnableDisable(groupboxScanConfig, enabled); + EnableDisable(groupboxOutput, enabled); + } + + private void EnableDisable(Control root, bool enabled) + { + foreach (Control control in root.Controls) + { + if (control.Controls.Count > 0) + { + EnableDisable(control, enabled); + } + else + { + control.Enabled = enabled; + } + } + } + + private async Task DoBatchScan() + { + try + { + await _batchScanPerformer.PerformBatchScan(Config.Get(c => c.BatchSettings), this, + image => SafeInvoke(() => ImageCallback(image)), ProgressCallback, _cts.Token); + SafeInvoke(() => + { + lblStatus.Text = _cts.IsCancellationRequested + ? MiscResources.BatchStatusCancelled + : MiscResources.BatchStatusComplete; + }); + } + catch (ScanDriverException ex) + { + if (ex is ScanDriverUnknownException) + { + Log.ErrorException("Error in batch scan", ex); + _errorOutput.DisplayError(ex.Message, ex); + } + else + { + _errorOutput.DisplayError(ex.Message); + } + } + catch (Exception ex) + { + Log.ErrorException("Error in batch scan", ex); + _errorOutput.DisplayError(MiscResources.BatchError, ex); + SafeInvoke(() => + { + lblStatus.Text = MiscResources.BatchStatusError; + }); + } + SafeInvoke(() => + { + _batchRunning = false; + _cts = new CancellationTokenSource(); + btnStart.Enabled = true; + btnCancel.Enabled = true; + btnCancel.Text = MiscResources.Close; + EnableDisableSettings(true); + Activate(); + }); + } + + private void ProgressCallback(string status) + { + SafeInvoke(() => + { + lblStatus.Text = status; + }); + } + + private void btnCancel_Click(object sender, EventArgs e) + { + if (_batchRunning) + { + if (MessageBox.Show(MiscResources.ConfirmCancelBatch, MiscResources.CancelBatch, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) + { + _cts.Cancel(); + btnCancel.Enabled = false; + lblStatus.Text = MiscResources.BatchStatusCancelling; + } + } + else + { + Close(); + } + } + + private void FBatchScan_FormClosing(object sender, FormClosingEventArgs e) + { + if (_cts.IsCancellationRequested) + { + // Keep dialog open while cancel is in progress to avoid concurrency issues + e.Cancel = true; + } + } + + private void linkPlaceholders_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) + { + var form = FormFactory.Create(); + form.FileName = txtFilePath.Text; + if (form.ShowDialog() == DialogResult.OK) + { + txtFilePath.Text = form.FileName; + } + } +} \ No newline at end of file diff --git a/NAPS2.Lib.WinForms/WinForms/FCrop.cs b/NAPS2.Lib.WinForms/WinForms/FCrop.cs index a8c0fd8c1..79cf818ee 100644 --- a/NAPS2.Lib.WinForms/WinForms/FCrop.cs +++ b/NAPS2.Lib.WinForms/WinForms/FCrop.cs @@ -2,230 +2,229 @@ using System.Drawing; using System.Drawing.Imaging; using System.Windows.Forms; -namespace NAPS2.WinForms +namespace NAPS2.WinForms; + +partial class FCrop : ImageForm { - partial class FCrop : ImageForm + private static CropTransform _lastTransform; + private static Size _lastSize; + + private Point _dragStartCoords; + private LayoutManager _lm; + + private int _originalWidth, _originalHeight; + + public FCrop(ImageContext imageContext) + : base(imageContext) { - private static CropTransform _lastTransform; - private static Size _lastSize; + InitializeComponent(); - private Point _dragStartCoords; - private LayoutManager _lm; + _lm = new LayoutManager(this) + .Bind(tbLeft, tbRight) + .WidthTo(() => (int)(GetImageWidthRatio() * pictureBox.Width)) + .LeftTo(() => (int)((1 - GetImageWidthRatio()) * pictureBox.Width / 2)) + .Bind(tbTop, tbBottom) + .HeightTo(() => (int)(GetImageHeightRatio() * pictureBox.Height)) + .TopTo(() => (int)((1 - GetImageHeightRatio()) * pictureBox.Height / 2)) + .Activate(); - private int _originalWidth, _originalHeight; + _originalWidth = _originalHeight = 1000; + } - public FCrop(ImageContext imageContext) - : base(imageContext) + public CropTransform CropTransform { get; private set; } + + protected override IEnumerable Transforms => new[] { CropTransform }; + + protected override PictureBox PictureBox => pictureBox; + + protected override Bitmap RenderPreview() + { + var bitmap = new Bitmap(_originalWidth, _originalHeight); + using (var g = Graphics.FromImage(bitmap)) { - InitializeComponent(); - - _lm = new LayoutManager(this) - .Bind(tbLeft, tbRight) - .WidthTo(() => (int)(GetImageWidthRatio() * pictureBox.Width)) - .LeftTo(() => (int)((1 - GetImageWidthRatio()) * pictureBox.Width / 2)) - .Bind(tbTop, tbBottom) - .HeightTo(() => (int)(GetImageHeightRatio() * pictureBox.Height)) - .TopTo(() => (int)((1 - GetImageHeightRatio()) * pictureBox.Height / 2)) - .Activate(); - - _originalWidth = _originalHeight = 1000; - } - - public CropTransform CropTransform { get; private set; } - - protected override IEnumerable Transforms => new[] { CropTransform }; - - protected override PictureBox PictureBox => pictureBox; - - protected override Bitmap RenderPreview() - { - var bitmap = new Bitmap(_originalWidth, _originalHeight); - using (var g = Graphics.FromImage(bitmap)) - { - g.Clear(Color.Transparent); - var attrs = new ImageAttributes(); - attrs.SetColorMatrix(new ColorMatrix { Matrix33 = 0.5f }); - g.DrawImage(workingImage2, - new Rectangle(0, 0, _originalWidth, _originalHeight), - 0, - 0, - _originalWidth, - _originalHeight, - GraphicsUnit.Pixel, - attrs); - var cropBorderRect = new Rectangle(CropTransform.Left, CropTransform.Top, - _originalWidth - CropTransform.Left - CropTransform.Right, - _originalHeight - CropTransform.Top - CropTransform.Bottom); - g.SetClip(cropBorderRect); - g.DrawImage(workingImage2, new Rectangle(0, 0, _originalWidth, _originalHeight)); - g.ResetClip(); - g.DrawRectangle(new Pen(Color.Black, 2.0f), cropBorderRect); - } - - return bitmap; - } - - protected override void InitTransform() - { - _originalWidth = workingImage.Width; - _originalHeight = workingImage.Height; - if (_lastTransform != null && _lastSize == workingImage.Size) - { - CropTransform = _lastTransform; - } - else - { - ResetTransform(); - } - UpdateCropBounds(); - _lm.UpdateLayout(); - } - - protected override void ResetTransform() - { - CropTransform = new CropTransform(0, 0, 0, 0, _originalWidth, _originalHeight); - } - - protected override void TransformSaved() - { - _lastTransform = CropTransform; - _lastSize = workingImage.Size; - } - - private double GetImageWidthRatio() - { - if (workingImage == null) - { - return 1; - } - double imageAspect = _originalWidth / (double)_originalHeight; - double pboxAspect = pictureBox.Width / (double)pictureBox.Height; - if (imageAspect > pboxAspect) - { - return 1; - } - return imageAspect / pboxAspect; - } - - private double GetImageHeightRatio() - { - if (workingImage == null) - { - return 1; - } - double imageAspect = _originalWidth / (double)_originalHeight; - double pboxAspect = pictureBox.Width / (double)pictureBox.Height; - if (pboxAspect > imageAspect) - { - return 1; - } - return pboxAspect / imageAspect; - } - - private void UpdateCropBounds() - { - tbLeft.Maximum = tbRight.Maximum = _originalWidth; - tbTop.Maximum = tbBottom.Maximum = _originalHeight; - - tbLeft.Value = CropTransform.Left; - tbRight.Value = _originalWidth - CropTransform.Right; - tbTop.Value = _originalHeight - CropTransform.Top; - tbBottom.Value = CropTransform.Bottom; - } - - private void UpdateTransform() - { - CropTransform = new CropTransform - ( - Math.Min(tbLeft.Value, tbRight.Value), - _originalWidth - Math.Max(tbLeft.Value, tbRight.Value), - _originalHeight - Math.Max(tbTop.Value, tbBottom.Value), - Math.Min(tbTop.Value, tbBottom.Value), + g.Clear(Color.Transparent); + var attrs = new ImageAttributes(); + attrs.SetColorMatrix(new ColorMatrix { Matrix33 = 0.5f }); + g.DrawImage(workingImage2, + new Rectangle(0, 0, _originalWidth, _originalHeight), + 0, + 0, _originalWidth, - _originalHeight - ); - UpdatePreviewBox(); + _originalHeight, + GraphicsUnit.Pixel, + attrs); + var cropBorderRect = new Rectangle(CropTransform.Left, CropTransform.Top, + _originalWidth - CropTransform.Left - CropTransform.Right, + _originalHeight - CropTransform.Top - CropTransform.Bottom); + g.SetClip(cropBorderRect); + g.DrawImage(workingImage2, new Rectangle(0, 0, _originalWidth, _originalHeight)); + g.ResetClip(); + g.DrawRectangle(new Pen(Color.Black, 2.0f), cropBorderRect); } - private void tbLeft_Scroll(object sender, EventArgs e) - { - UpdateTransform(); - } + return bitmap; + } - private void tbRight_Scroll(object sender, EventArgs e) + protected override void InitTransform() + { + _originalWidth = workingImage.Width; + _originalHeight = workingImage.Height; + if (_lastTransform != null && _lastSize == workingImage.Size) { - UpdateTransform(); + CropTransform = _lastTransform; } - - private void tbBottom_Scroll(object sender, EventArgs e) + else { - UpdateTransform(); + ResetTransform(); } + UpdateCropBounds(); + _lm.UpdateLayout(); + } - private void tbTop_Scroll(object sender, EventArgs e) + protected override void ResetTransform() + { + CropTransform = new CropTransform(0, 0, 0, 0, _originalWidth, _originalHeight); + } + + protected override void TransformSaved() + { + _lastTransform = CropTransform; + _lastSize = workingImage.Size; + } + + private double GetImageWidthRatio() + { + if (workingImage == null) { - UpdateTransform(); + return 1; } - - private void pictureBox_MouseDown(object sender, MouseEventArgs e) + double imageAspect = _originalWidth / (double)_originalHeight; + double pboxAspect = pictureBox.Width / (double)pictureBox.Height; + if (imageAspect > pboxAspect) { - _dragStartCoords = TranslatePboxCoords(e.Location); + return 1; } + return imageAspect / pboxAspect; + } - private void pictureBox_MouseMove(object sender, MouseEventArgs e) + private double GetImageHeightRatio() + { + if (workingImage == null) { - if (e.Button == MouseButtons.Left) + return 1; + } + double imageAspect = _originalWidth / (double)_originalHeight; + double pboxAspect = pictureBox.Width / (double)pictureBox.Height; + if (pboxAspect > imageAspect) + { + return 1; + } + return pboxAspect / imageAspect; + } + + private void UpdateCropBounds() + { + tbLeft.Maximum = tbRight.Maximum = _originalWidth; + tbTop.Maximum = tbBottom.Maximum = _originalHeight; + + tbLeft.Value = CropTransform.Left; + tbRight.Value = _originalWidth - CropTransform.Right; + tbTop.Value = _originalHeight - CropTransform.Top; + tbBottom.Value = CropTransform.Bottom; + } + + private void UpdateTransform() + { + CropTransform = new CropTransform + ( + Math.Min(tbLeft.Value, tbRight.Value), + _originalWidth - Math.Max(tbLeft.Value, tbRight.Value), + _originalHeight - Math.Max(tbTop.Value, tbBottom.Value), + Math.Min(tbTop.Value, tbBottom.Value), + _originalWidth, + _originalHeight + ); + UpdatePreviewBox(); + } + + private void tbLeft_Scroll(object sender, EventArgs e) + { + UpdateTransform(); + } + + private void tbRight_Scroll(object sender, EventArgs e) + { + UpdateTransform(); + } + + private void tbBottom_Scroll(object sender, EventArgs e) + { + UpdateTransform(); + } + + private void tbTop_Scroll(object sender, EventArgs e) + { + UpdateTransform(); + } + + private void pictureBox_MouseDown(object sender, MouseEventArgs e) + { + _dragStartCoords = TranslatePboxCoords(e.Location); + } + + private void pictureBox_MouseMove(object sender, MouseEventArgs e) + { + if (e.Button == MouseButtons.Left) + { + var dragEndCoords = TranslatePboxCoords(e.Location); + if (dragEndCoords.X > _dragStartCoords.X) { - var dragEndCoords = TranslatePboxCoords(e.Location); - if (dragEndCoords.X > _dragStartCoords.X) - { - tbLeft.Value = _dragStartCoords.X; - tbRight.Value = dragEndCoords.X; - } - else - { - tbLeft.Value = dragEndCoords.X; - tbRight.Value = _dragStartCoords.X; - } - if (dragEndCoords.Y > _dragStartCoords.Y) - { - tbTop.Value = _originalHeight - _dragStartCoords.Y; - tbBottom.Value = _originalHeight - dragEndCoords.Y; - } - else - { - tbTop.Value = _originalHeight - dragEndCoords.Y; - tbBottom.Value = _originalHeight - _dragStartCoords.Y; - } - UpdateTransform(); - } - } - - private Point TranslatePboxCoords(Point point) - { - double px = point.X - 1; - double py = point.Y - 1; - double imageAspect = _originalWidth / (double)_originalHeight; - double pboxWidth = (pictureBox.Width - 2); - double pboxHeight = (pictureBox.Height - 2); - double pboxAspect = pboxWidth / pboxHeight; - if (pboxAspect > imageAspect) - { - // Empty space on left/right - var emptyWidth = ((1 - imageAspect / pboxAspect) / 2 * pboxWidth); - px = (pboxAspect / imageAspect * (px - emptyWidth)); + tbLeft.Value = _dragStartCoords.X; + tbRight.Value = dragEndCoords.X; } else { - // Empty space on top/bottom - var emptyHeight = ((1 - pboxAspect / imageAspect) / 2 * pboxHeight); - py = (imageAspect / pboxAspect * (py - emptyHeight)); + tbLeft.Value = dragEndCoords.X; + tbRight.Value = _dragStartCoords.X; } - double x = px / pboxWidth * _originalWidth; - double y = py / pboxHeight * _originalHeight; - x = Math.Max(Math.Min(x, _originalWidth), 0); - y = Math.Max(Math.Min(y, _originalHeight), 0); - return new Point((int)Math.Round(x), (int)Math.Round(y)); + if (dragEndCoords.Y > _dragStartCoords.Y) + { + tbTop.Value = _originalHeight - _dragStartCoords.Y; + tbBottom.Value = _originalHeight - dragEndCoords.Y; + } + else + { + tbTop.Value = _originalHeight - dragEndCoords.Y; + tbBottom.Value = _originalHeight - _dragStartCoords.Y; + } + UpdateTransform(); } } -} + + private Point TranslatePboxCoords(Point point) + { + double px = point.X - 1; + double py = point.Y - 1; + double imageAspect = _originalWidth / (double)_originalHeight; + double pboxWidth = (pictureBox.Width - 2); + double pboxHeight = (pictureBox.Height - 2); + double pboxAspect = pboxWidth / pboxHeight; + if (pboxAspect > imageAspect) + { + // Empty space on left/right + var emptyWidth = ((1 - imageAspect / pboxAspect) / 2 * pboxWidth); + px = (pboxAspect / imageAspect * (px - emptyWidth)); + } + else + { + // Empty space on top/bottom + var emptyHeight = ((1 - pboxAspect / imageAspect) / 2 * pboxHeight); + py = (imageAspect / pboxAspect * (py - emptyHeight)); + } + double x = px / pboxWidth * _originalWidth; + double y = py / pboxHeight * _originalHeight; + x = Math.Max(Math.Min(x, _originalWidth), 0); + y = Math.Max(Math.Min(y, _originalHeight), 0); + return new Point((int)Math.Round(x), (int)Math.Round(y)); + } +} \ No newline at end of file diff --git a/NAPS2.Lib.WinForms/WinForms/FDesktop.cs b/NAPS2.Lib.WinForms/WinForms/FDesktop.cs index 5b776e562..4c47af197 100644 --- a/NAPS2.Lib.WinForms/WinForms/FDesktop.cs +++ b/NAPS2.Lib.WinForms/WinForms/FDesktop.cs @@ -12,782 +12,781 @@ using NAPS2.ImportExport.Images; #endregion -namespace NAPS2.WinForms +namespace NAPS2.WinForms; + +public partial class FDesktop : FormBase { - public partial class FDesktop : FormBase + private readonly ToolbarFormatter _toolbarFormatter; + private readonly TesseractLanguageManager _tesseractLanguageManager; + private readonly IScannedImagePrinter _scannedImagePrinter; + private readonly KeyboardShortcutManager _ksm; + private readonly ThumbnailRenderer _thumbnailRenderer; + private readonly INotificationManager _notify; + private readonly CultureHelper _cultureHelper; + private readonly IProfileManager _profileManager; + private readonly UiImageList _imageList; + private readonly ImageTransfer _imageTransfer; + private readonly ThumbnailRenderQueue _thumbnailRenderQueue; + private readonly UiThumbnailProvider _thumbnailProvider; + private readonly DesktopController _desktopController; + private readonly IDesktopScanController _desktopScanController; + private readonly ImageListActions _imageListActions; + private readonly DesktopFormProvider _desktopFormProvider; + private readonly DesktopSubFormController _desktopSubFormController; + + private WinFormsListView _listView; + private ImageListSyncer? _imageListSyncer; + private LayoutManager _layoutManager; + + #region Initialization and Culture + + public FDesktop( + ToolbarFormatter toolbarFormatter, + TesseractLanguageManager tesseractLanguageManager, + IScannedImagePrinter scannedImagePrinter, + KeyboardShortcutManager ksm, + ThumbnailRenderer thumbnailRenderer, + INotificationManager notify, + CultureHelper cultureHelper, + IProfileManager profileManager, + UiImageList imageList, + ImageTransfer imageTransfer, + ThumbnailRenderQueue thumbnailRenderQueue, + UiThumbnailProvider thumbnailProvider, + DesktopController desktopController, + IDesktopScanController desktopScanController, + ImageListActions imageListActions, + DesktopFormProvider desktopFormProvider, DesktopSubFormController desktopSubFormController) { - private readonly ToolbarFormatter _toolbarFormatter; - private readonly TesseractLanguageManager _tesseractLanguageManager; - private readonly IScannedImagePrinter _scannedImagePrinter; - private readonly KeyboardShortcutManager _ksm; - private readonly ThumbnailRenderer _thumbnailRenderer; - private readonly INotificationManager _notify; - private readonly CultureHelper _cultureHelper; - private readonly IProfileManager _profileManager; - private readonly UiImageList _imageList; - private readonly ImageTransfer _imageTransfer; - private readonly ThumbnailRenderQueue _thumbnailRenderQueue; - private readonly UiThumbnailProvider _thumbnailProvider; - private readonly DesktopController _desktopController; - private readonly IDesktopScanController _desktopScanController; - private readonly ImageListActions _imageListActions; - private readonly DesktopFormProvider _desktopFormProvider; - private readonly DesktopSubFormController _desktopSubFormController; + _toolbarFormatter = toolbarFormatter; + _tesseractLanguageManager = tesseractLanguageManager; + _scannedImagePrinter = scannedImagePrinter; + _ksm = ksm; + _thumbnailRenderer = thumbnailRenderer; + _notify = notify; + _cultureHelper = cultureHelper; + _profileManager = profileManager; + _imageList = imageList; + _imageTransfer = imageTransfer; + _thumbnailRenderQueue = thumbnailRenderQueue; + _thumbnailProvider = thumbnailProvider; + _desktopController = desktopController; + _desktopScanController = desktopScanController; + _imageListActions = imageListActions; + _desktopFormProvider = desktopFormProvider; + _desktopSubFormController = desktopSubFormController; + InitializeComponent(); - private WinFormsListView _listView; - private ImageListSyncer? _imageListSyncer; - private LayoutManager _layoutManager; - - #region Initialization and Culture - - public FDesktop( - ToolbarFormatter toolbarFormatter, - TesseractLanguageManager tesseractLanguageManager, - IScannedImagePrinter scannedImagePrinter, - KeyboardShortcutManager ksm, - ThumbnailRenderer thumbnailRenderer, - INotificationManager notify, - CultureHelper cultureHelper, - IProfileManager profileManager, - UiImageList imageList, - ImageTransfer imageTransfer, - ThumbnailRenderQueue thumbnailRenderQueue, - UiThumbnailProvider thumbnailProvider, - DesktopController desktopController, - IDesktopScanController desktopScanController, - ImageListActions imageListActions, - DesktopFormProvider desktopFormProvider, DesktopSubFormController desktopSubFormController) + notify.ParentForm = this; + Shown += FDesktop_Shown; + FormClosing += FDesktop_FormClosing; + Closed += FDesktop_Closed; + imageList.SelectionChanged += (_, _) => { - _toolbarFormatter = toolbarFormatter; - _tesseractLanguageManager = tesseractLanguageManager; - _scannedImagePrinter = scannedImagePrinter; - _ksm = ksm; - _thumbnailRenderer = thumbnailRenderer; - _notify = notify; - _cultureHelper = cultureHelper; - _profileManager = profileManager; - _imageList = imageList; - _imageTransfer = imageTransfer; - _thumbnailRenderQueue = thumbnailRenderQueue; - _thumbnailProvider = thumbnailProvider; - _desktopController = desktopController; - _desktopScanController = desktopScanController; - _imageListActions = imageListActions; - _desktopFormProvider = desktopFormProvider; - _desktopSubFormController = desktopSubFormController; - InitializeComponent(); - - notify.ParentForm = this; - Shown += FDesktop_Shown; - FormClosing += FDesktop_FormClosing; - Closed += FDesktop_Closed; - imageList.SelectionChanged += (_, _) => + SafeInvoke(() => { - SafeInvoke(() => - { - UpdateToolbar(); - _listView!.Selection = _imageList.Selection; - }); + UpdateToolbar(); + _listView!.Selection = _imageList.Selection; + }); + }; + imageList.ImagesUpdated += (_, _) => SafeInvoke(UpdateToolbar); + _profileManager.ProfilesUpdated += (_, _) => UpdateScanButton(); + _desktopFormProvider.DesktopForm = this; + } + + protected override void OnLoad(object sender, EventArgs args) => PostInitializeComponent(); + + protected override void AfterLoad(object sender, EventArgs args) => AfterLayout(); + + /// + /// Runs when the form is first loaded and every time the language is changed. + /// + private void PostInitializeComponent() + { + // TODO: Migrate the whole FDesktop to Eto + // For now, as a partial migration, we're using our Eto ListView abstraction directly. + _listView = new WinFormsListView(new ImageListViewBehavior(_thumbnailProvider, _imageTransfer)) + { + AllowDrag = true, + AllowDrop = true + }; + _listView.Selection = _imageList.Selection; + _listView.ItemClicked += ListViewItemClicked; + _listView.Drop += ListViewDrop; + _listView.SelectionChanged += ListViewSelectionChanged; + _listView.NativeControl.TabIndex = 7; + _listView.NativeControl.Dock = DockStyle.Fill; + _listView.NativeControl.ContextMenuStrip = contextMenuStrip; + _listView.NativeControl.KeyDown += ListViewKeyDown; + _listView.NativeControl.MouseWheel += ListViewMouseWheel; + toolStripContainer1.ContentPanel.Controls.Add(_listView.NativeControl); + _imageListSyncer?.Dispose(); + _imageListSyncer = new ImageListSyncer(_imageList, _listView.ApplyDiffs, SynchronizationContext.Current); + + foreach (var panel in toolStripContainer1.Controls.OfType()) + { + // Allow tabbing through the toolbar for accessibility + WinFormsHacks.SetControlStyle(panel, ControlStyles.Selectable, true); + } + _imageList.ThumbnailRenderer = _thumbnailRenderer; + int thumbnailSize = Config.Get(c => c.ThumbnailSize); + _listView.ImageSize = thumbnailSize; + SetThumbnailSpacing(thumbnailSize); + + // TODO: Verify that hidden buttons can't be accessed via keyboard shortcut + var hiddenButtons = Config.Get(c => c.HiddenButtons); + var buttonMap = new List<(ToolbarButtons, ToolStripItem)> + { + (ToolbarButtons.Scan, tsScan), + (ToolbarButtons.Profiles, tsProfiles), + (ToolbarButtons.Ocr, tsOcr), + (ToolbarButtons.Import, tsImport), + (ToolbarButtons.SavePdf, tsdSavePDF), + (ToolbarButtons.SaveImages, tsdSaveImages), + (ToolbarButtons.EmailPdf, tsdEmailPDF), + (ToolbarButtons.Print, tsPrint), + (ToolbarButtons.Image, tsdImage), + (ToolbarButtons.Rotate, tsdRotate), + (ToolbarButtons.Move, tsMove), + (ToolbarButtons.Reorder, tsdReorder), + (ToolbarButtons.Delete, tsDelete), + (ToolbarButtons.Clear, tsClear), + (ToolbarButtons.Language, toolStripDropDownButton1), + (ToolbarButtons.Settings, tsSettingsAbout), + (ToolbarButtons.About, tsSettingsAbout), + }; + foreach (var (flag, button) in buttonMap) + { + if (hiddenButtons.HasFlag(flag)) + { + tStrip.Items.Remove(button); + } + } + + LoadToolStripLocation(); + InitLanguageDropdown(); + AssignKeyboardShortcuts(); + UpdateScanButton(); + + _layoutManager?.Deactivate(); + btnZoomIn.Location = new Point(btnZoomIn.Location.X, _listView.NativeControl.Height - 33); + btnZoomOut.Location = new Point(btnZoomOut.Location.X, _listView.NativeControl.Height - 33); + btnZoomMouseCatcher.Location = + new Point(btnZoomMouseCatcher.Location.X, _listView.NativeControl.Height - 33); + _layoutManager = new LayoutManager(this) + .Bind(btnZoomIn, btnZoomOut, btnZoomMouseCatcher) + .BottomTo(() => _listView.NativeControl.Height) + .Activate(); + _listView.NativeControl.SizeChanged += (_, _) => _layoutManager.UpdateLayout(); + + _imageListSyncer.SyncNow(); + _listView.NativeControl.Focus(); + } + + private void AfterLayout() + { + _toolbarFormatter.RelayoutToolbar(tStrip); + } + + private void InitLanguageDropdown() + { + foreach (var (langCode, langName) in _cultureHelper.GetAvailableCultures()) + { + var button = new ToolStripMenuItem(langName, null, (_, _) => SetCulture(langCode)); + toolStripDropDownButton1.DropDownItems.Add(button); + } + } + + private void SetCulture(string cultureId) + { + SaveToolStripLocation(); + Config.User.Set(c => c.Culture, cultureId); + _cultureHelper.SetCulturesFromConfig(); + + // Update localized values + // Since all forms are opened modally and this is the root form, it should be the only one that needs to be updated live + SaveFormState = false; + Controls.Clear(); + UpdateRTL(); + InitializeComponent(); + PostInitializeComponent(); + AfterLayout(); + _notify.Rebuild(); + Focus(); + WindowState = FormWindowState.Normal; + DoRestoreFormState(); + SaveFormState = true; + } + + private async void FDesktop_Shown(object sender, EventArgs e) + { + // TODO: Start the Eto application in the entry point once all forms (or at least FDesktop?) are migrated + new Eto.Forms.Application(Eto.Platforms.WinForms).Attach(); + + UpdateToolbar(); + await _desktopController.Initialize(); + } + + #endregion + + #region Cleanup + + private void FDesktop_FormClosing(object sender, FormClosingEventArgs e) + { + if (!_desktopController.PrepareForClosing(e.CloseReason == CloseReason.UserClosing)) + { + e.Cancel = true; + } + } + + private void FDesktop_Closed(object sender, EventArgs e) + { + SaveToolStripLocation(); + _desktopController.Cleanup(); + } + + #endregion + + #region Toolbar + + private void UpdateToolbar() + { + // "All" dropdown items + tsSavePDFAll.Text = tsSaveImagesAll.Text = tsEmailPDFAll.Text = tsReverseAll.Text = + string.Format(MiscResources.AllCount, _imageList.Images.Count); + tsSavePDFAll.Enabled = tsSaveImagesAll.Enabled = tsEmailPDFAll.Enabled = tsReverseAll.Enabled = + _imageList.Images.Any(); + + // "Selected" dropdown items + tsSavePDFSelected.Text = tsSaveImagesSelected.Text = tsEmailPDFSelected.Text = tsReverseSelected.Text = + string.Format(MiscResources.SelectedCount, _imageList.Selection.Count); + tsSavePDFSelected.Enabled = tsSaveImagesSelected.Enabled = tsEmailPDFSelected.Enabled = + tsReverseSelected.Enabled = + _imageList.Selection.Any(); + + // Top-level toolbar actions + tsdImage.Enabled = tsdRotate.Enabled = tsMove.Enabled = tsDelete.Enabled = _imageList.Selection.Any(); + tsdReorder.Enabled = tsdSavePDF.Enabled = tsdSaveImages.Enabled = + tsdEmailPDF.Enabled = tsPrint.Enabled = tsClear.Enabled = _imageList.Images.Any(); + + // Context-menu actions + ctxView.Visible = ctxCopy.Visible = ctxDelete.Visible = + ctxSeparator1.Visible = ctxSeparator2.Visible = _imageList.Selection.Any(); + ctxSelectAll.Enabled = _imageList.Images.Any(); + + // Other + btnZoomIn.Enabled = _imageList.Images.Any() && Config.Get(c => c.ThumbnailSize) < ThumbnailSizes.MAX_SIZE; + btnZoomOut.Enabled = _imageList.Images.Any() && Config.Get(c => c.ThumbnailSize) > ThumbnailSizes.MIN_SIZE; + tsNewProfile.Enabled = + !(Config.Get(c => c.NoUserProfiles) && _profileManager.Profiles.Any(x => x.IsLocked)); + + if (PlatformCompat.Runtime.RefreshListViewAfterChange) + { + // TODO: Eventually, once we have a native linux UI, we can get rid of mono hacks + // TODO: But in the meantime we should verify mono behavior + _listView.NativeControl.Size = + new Size(_listView.NativeControl.Width - 1, _listView.NativeControl.Height - 1); + _listView.NativeControl.Size = + new Size(_listView.NativeControl.Width + 1, _listView.NativeControl.Height + 1); + } + } + + private void UpdateScanButton() + { + const int staticButtonCount = 2; + + // Clean up the dropdown + while (tsScan.DropDownItems.Count > staticButtonCount) + { + tsScan.DropDownItems.RemoveAt(0); + } + + // Populate the dropdown + var defaultProfile = _profileManager.DefaultProfile; + int i = 1; + foreach (var profile in _profileManager.Profiles) + { + var item = new ToolStripMenuItem + { + Text = profile.DisplayName.Replace("&", "&&"), + Image = profile == defaultProfile ? Icons.accept_small : null, + ImageScaling = ToolStripItemImageScaling.None }; - imageList.ImagesUpdated += (_, _) => SafeInvoke(UpdateToolbar); - _profileManager.ProfilesUpdated += (_, _) => UpdateScanButton(); - _desktopFormProvider.DesktopForm = this; + AssignProfileShortcut(i, item); + item.Click += async (_, _) => await _desktopScanController.ScanWithProfile(profile); + tsScan.DropDownItems.Insert(tsScan.DropDownItems.Count - staticButtonCount, item); + + i++; } - protected override void OnLoad(object sender, EventArgs args) => PostInitializeComponent(); - - protected override void AfterLoad(object sender, EventArgs args) => AfterLayout(); - - /// - /// Runs when the form is first loaded and every time the language is changed. - /// - private void PostInitializeComponent() + if (_profileManager.Profiles.Any()) { - // TODO: Migrate the whole FDesktop to Eto - // For now, as a partial migration, we're using our Eto ListView abstraction directly. - _listView = new WinFormsListView(new ImageListViewBehavior(_thumbnailProvider, _imageTransfer)) - { - AllowDrag = true, - AllowDrop = true - }; - _listView.Selection = _imageList.Selection; - _listView.ItemClicked += ListViewItemClicked; - _listView.Drop += ListViewDrop; - _listView.SelectionChanged += ListViewSelectionChanged; - _listView.NativeControl.TabIndex = 7; - _listView.NativeControl.Dock = DockStyle.Fill; - _listView.NativeControl.ContextMenuStrip = contextMenuStrip; - _listView.NativeControl.KeyDown += ListViewKeyDown; - _listView.NativeControl.MouseWheel += ListViewMouseWheel; - toolStripContainer1.ContentPanel.Controls.Add(_listView.NativeControl); - _imageListSyncer?.Dispose(); - _imageListSyncer = new ImageListSyncer(_imageList, _listView.ApplyDiffs, SynchronizationContext.Current); - - foreach (var panel in toolStripContainer1.Controls.OfType()) - { - // Allow tabbing through the toolbar for accessibility - WinFormsHacks.SetControlStyle(panel, ControlStyles.Selectable, true); - } - _imageList.ThumbnailRenderer = _thumbnailRenderer; - int thumbnailSize = Config.Get(c => c.ThumbnailSize); - _listView.ImageSize = thumbnailSize; - SetThumbnailSpacing(thumbnailSize); - - // TODO: Verify that hidden buttons can't be accessed via keyboard shortcut - var hiddenButtons = Config.Get(c => c.HiddenButtons); - var buttonMap = new List<(ToolbarButtons, ToolStripItem)> - { - (ToolbarButtons.Scan, tsScan), - (ToolbarButtons.Profiles, tsProfiles), - (ToolbarButtons.Ocr, tsOcr), - (ToolbarButtons.Import, tsImport), - (ToolbarButtons.SavePdf, tsdSavePDF), - (ToolbarButtons.SaveImages, tsdSaveImages), - (ToolbarButtons.EmailPdf, tsdEmailPDF), - (ToolbarButtons.Print, tsPrint), - (ToolbarButtons.Image, tsdImage), - (ToolbarButtons.Rotate, tsdRotate), - (ToolbarButtons.Move, tsMove), - (ToolbarButtons.Reorder, tsdReorder), - (ToolbarButtons.Delete, tsDelete), - (ToolbarButtons.Clear, tsClear), - (ToolbarButtons.Language, toolStripDropDownButton1), - (ToolbarButtons.Settings, tsSettingsAbout), - (ToolbarButtons.About, tsSettingsAbout), - }; - foreach (var (flag, button) in buttonMap) - { - if (hiddenButtons.HasFlag(flag)) - { - tStrip.Items.Remove(button); - } - } - - LoadToolStripLocation(); - InitLanguageDropdown(); - AssignKeyboardShortcuts(); - UpdateScanButton(); - - _layoutManager?.Deactivate(); - btnZoomIn.Location = new Point(btnZoomIn.Location.X, _listView.NativeControl.Height - 33); - btnZoomOut.Location = new Point(btnZoomOut.Location.X, _listView.NativeControl.Height - 33); - btnZoomMouseCatcher.Location = - new Point(btnZoomMouseCatcher.Location.X, _listView.NativeControl.Height - 33); - _layoutManager = new LayoutManager(this) - .Bind(btnZoomIn, btnZoomOut, btnZoomMouseCatcher) - .BottomTo(() => _listView.NativeControl.Height) - .Activate(); - _listView.NativeControl.SizeChanged += (_, _) => _layoutManager.UpdateLayout(); - - _imageListSyncer.SyncNow(); - _listView.NativeControl.Focus(); + tsScan.DropDownItems.Insert(tsScan.DropDownItems.Count - staticButtonCount, new ToolStripSeparator()); } + } - private void AfterLayout() - { - _toolbarFormatter.RelayoutToolbar(tStrip); - } + private void SaveToolStripLocation() + { + Config.User.Set(c => c.DesktopToolStripDock, tStrip.Parent.Dock); + } - private void InitLanguageDropdown() + private void LoadToolStripLocation() + { + var dock = Config.Get(c => c.DesktopToolStripDock); + if (dock != DockStyle.None) { - foreach (var (langCode, langName) in _cultureHelper.GetAvailableCultures()) + var panel = toolStripContainer1.Controls.OfType().FirstOrDefault(x => x.Dock == dock); + if (panel != null) { - var button = new ToolStripMenuItem(langName, null, (_, _) => SetCulture(langCode)); - toolStripDropDownButton1.DropDownItems.Add(button); + tStrip.Parent = panel; } } + tStrip.Parent.TabStop = true; + } - private void SetCulture(string cultureId) + #endregion + + #region Keyboard Shortcuts + + private void AssignKeyboardShortcuts() + { + // Defaults + + _ksm.Assign("Ctrl+Enter", tsScan); + _ksm.Assign("Ctrl+B", tsBatchScan); + _ksm.Assign("Ctrl+O", tsImport); + _ksm.Assign("Ctrl+S", tsdSavePDF); + _ksm.Assign("Ctrl+P", tsPrint); + _ksm.Assign("Ctrl+Up", _imageListActions.MoveUp); + _ksm.Assign("Ctrl+Left", _imageListActions.MoveUp); + _ksm.Assign("Ctrl+Down", _imageListActions.MoveDown); + _ksm.Assign("Ctrl+Right", _imageListActions.MoveDown); + _ksm.Assign("Ctrl+Shift+Del", tsClear); + _ksm.Assign("F1", _desktopSubFormController.ShowAboutForm); + _ksm.Assign("Ctrl+OemMinus", btnZoomOut); + _ksm.Assign("Ctrl+Oemplus", btnZoomIn); + _ksm.Assign("Del", ctxDelete); + _ksm.Assign("Ctrl+A", ctxSelectAll); + _ksm.Assign("Ctrl+C", ctxCopy); + _ksm.Assign("Ctrl+V", ctxPaste); + + // Configured + + var ks = Config.Get(c => c.KeyboardShortcuts); + + _ksm.Assign(ks.About, _desktopSubFormController.ShowAboutForm); + _ksm.Assign(ks.BatchScan, tsBatchScan); + _ksm.Assign(ks.Clear, tsClear); + _ksm.Assign(ks.Delete, tsDelete); + _ksm.Assign(ks.EmailPDF, tsdEmailPDF); + _ksm.Assign(ks.EmailPDFAll, tsEmailPDFAll); + _ksm.Assign(ks.EmailPDFSelected, tsEmailPDFSelected); + _ksm.Assign(ks.ImageBlackWhite, tsBlackWhite); + _ksm.Assign(ks.ImageBrightness, tsBrightnessContrast); + _ksm.Assign(ks.ImageContrast, tsBrightnessContrast); + _ksm.Assign(ks.ImageCrop, tsCrop); + _ksm.Assign(ks.ImageHue, tsHueSaturation); + _ksm.Assign(ks.ImageSaturation, tsHueSaturation); + _ksm.Assign(ks.ImageSharpen, tsSharpen); + _ksm.Assign(ks.ImageReset, tsReset); + _ksm.Assign(ks.ImageView, tsView); + _ksm.Assign(ks.Import, tsImport); + _ksm.Assign(ks.MoveDown, _imageListActions.MoveDown); + _ksm.Assign(ks.MoveUp, _imageListActions.MoveUp); + _ksm.Assign(ks.NewProfile, tsNewProfile); + _ksm.Assign(ks.Ocr, tsOcr); + _ksm.Assign(ks.Print, tsPrint); + _ksm.Assign(ks.Profiles, tsProfiles); + + _ksm.Assign(ks.ReorderAltDeinterleave, tsAltDeinterleave); + _ksm.Assign(ks.ReorderAltInterleave, tsAltInterleave); + _ksm.Assign(ks.ReorderDeinterleave, tsDeinterleave); + _ksm.Assign(ks.ReorderInterleave, tsInterleave); + _ksm.Assign(ks.ReorderReverseAll, tsReverseAll); + _ksm.Assign(ks.ReorderReverseSelected, tsReverseSelected); + _ksm.Assign(ks.RotateCustom, tsCustomRotation); + _ksm.Assign(ks.RotateFlip, tsFlip); + _ksm.Assign(ks.RotateLeft, tsRotateLeft); + _ksm.Assign(ks.RotateRight, tsRotateRight); + _ksm.Assign(ks.SaveImages, tsdSaveImages); + _ksm.Assign(ks.SaveImagesAll, tsSaveImagesAll); + _ksm.Assign(ks.SaveImagesSelected, tsSaveImagesSelected); + _ksm.Assign(ks.SavePDF, tsdSavePDF); + _ksm.Assign(ks.SavePDFAll, tsSavePDFAll); + _ksm.Assign(ks.SavePDFSelected, tsSavePDFSelected); + _ksm.Assign(ks.ScanDefault, tsScan); + + _ksm.Assign(ks.ZoomIn, btnZoomIn); + _ksm.Assign(ks.ZoomOut, btnZoomOut); + } + + private void AssignProfileShortcut(int i, ToolStripMenuItem item) + { + var sh = GetProfileShortcut(i); + if (string.IsNullOrWhiteSpace(sh) && i <= 11) { - SaveToolStripLocation(); - Config.User.Set(c => c.Culture, cultureId); - _cultureHelper.SetCulturesFromConfig(); - - // Update localized values - // Since all forms are opened modally and this is the root form, it should be the only one that needs to be updated live - SaveFormState = false; - Controls.Clear(); - UpdateRTL(); - InitializeComponent(); - PostInitializeComponent(); - AfterLayout(); - _notify.Rebuild(); - Focus(); - WindowState = FormWindowState.Normal; - DoRestoreFormState(); - SaveFormState = true; + sh = "F" + (i + 1); } + _ksm.Assign(sh, item); + } - private async void FDesktop_Shown(object sender, EventArgs e) + private string? GetProfileShortcut(int i) + { + // TODO: Granular + var ks = Config.Get(c => c.KeyboardShortcuts); + switch (i) { - // TODO: Start the Eto application in the entry point once all forms (or at least FDesktop?) are migrated - new Eto.Forms.Application(Eto.Platforms.WinForms).Attach(); - - UpdateToolbar(); - await _desktopController.Initialize(); + case 1: + return ks.ScanProfile1; + case 2: + return ks.ScanProfile2; + case 3: + return ks.ScanProfile3; + case 4: + return ks.ScanProfile4; + case 5: + return ks.ScanProfile5; + case 6: + return ks.ScanProfile6; + case 7: + return ks.ScanProfile7; + case 8: + return ks.ScanProfile8; + case 9: + return ks.ScanProfile9; + case 10: + return ks.ScanProfile10; + case 11: + return ks.ScanProfile11; + case 12: + return ks.ScanProfile12; } + return null; + } - #endregion + private void ListViewKeyDown(object? sender, KeyEventArgs e) + { + e.Handled = _ksm.Perform(e.KeyData); + } - #region Cleanup - - private void FDesktop_FormClosing(object sender, FormClosingEventArgs e) + private void ListViewMouseWheel(object? sender, MouseEventArgs e) + { + if (ModifierKeys.HasFlag(Keys.Control)) { - if (!_desktopController.PrepareForClosing(e.CloseReason == CloseReason.UserClosing)) - { - e.Cancel = true; - } + StepThumbnailSize(e.Delta / (double) SystemInformation.MouseWheelScrollDelta); } + } - private void FDesktop_Closed(object sender, EventArgs e) + #endregion + + #region Event Handlers - Misc + + private void tStrip_ParentChanged(object sender, EventArgs e) => _toolbarFormatter.RelayoutToolbar(tStrip); + + #endregion + + #region Event Handlers - Toolbar + + private async void tsScan_ButtonClick(object sender, EventArgs e) => await _desktopScanController.ScanDefault(); + + private async void tsNewProfile_Click(object sender, EventArgs e) => + await _desktopScanController.ScanWithNewProfile(); + + private void tsBatchScan_Click(object sender, EventArgs e) => _desktopSubFormController.ShowBatchScanForm(); + private void tsProfiles_Click(object sender, EventArgs e) => _desktopSubFormController.ShowProfilesForm(); + + private void tsOcr_Click(object sender, EventArgs e) + { + if (_tesseractLanguageManager.InstalledLanguages.Any()) { - SaveToolStripLocation(); - _desktopController.Cleanup(); + FormFactory.Create().ShowDialog(); } - - #endregion - - #region Toolbar - - private void UpdateToolbar() - { - // "All" dropdown items - tsSavePDFAll.Text = tsSaveImagesAll.Text = tsEmailPDFAll.Text = tsReverseAll.Text = - string.Format(MiscResources.AllCount, _imageList.Images.Count); - tsSavePDFAll.Enabled = tsSaveImagesAll.Enabled = tsEmailPDFAll.Enabled = tsReverseAll.Enabled = - _imageList.Images.Any(); - - // "Selected" dropdown items - tsSavePDFSelected.Text = tsSaveImagesSelected.Text = tsEmailPDFSelected.Text = tsReverseSelected.Text = - string.Format(MiscResources.SelectedCount, _imageList.Selection.Count); - tsSavePDFSelected.Enabled = tsSaveImagesSelected.Enabled = tsEmailPDFSelected.Enabled = - tsReverseSelected.Enabled = - _imageList.Selection.Any(); - - // Top-level toolbar actions - tsdImage.Enabled = tsdRotate.Enabled = tsMove.Enabled = tsDelete.Enabled = _imageList.Selection.Any(); - tsdReorder.Enabled = tsdSavePDF.Enabled = tsdSaveImages.Enabled = - tsdEmailPDF.Enabled = tsPrint.Enabled = tsClear.Enabled = _imageList.Images.Any(); - - // Context-menu actions - ctxView.Visible = ctxCopy.Visible = ctxDelete.Visible = - ctxSeparator1.Visible = ctxSeparator2.Visible = _imageList.Selection.Any(); - ctxSelectAll.Enabled = _imageList.Images.Any(); - - // Other - btnZoomIn.Enabled = _imageList.Images.Any() && Config.Get(c => c.ThumbnailSize) < ThumbnailSizes.MAX_SIZE; - btnZoomOut.Enabled = _imageList.Images.Any() && Config.Get(c => c.ThumbnailSize) > ThumbnailSizes.MIN_SIZE; - tsNewProfile.Enabled = - !(Config.Get(c => c.NoUserProfiles) && _profileManager.Profiles.Any(x => x.IsLocked)); - - if (PlatformCompat.Runtime.RefreshListViewAfterChange) - { - // TODO: Eventually, once we have a native linux UI, we can get rid of mono hacks - // TODO: But in the meantime we should verify mono behavior - _listView.NativeControl.Size = - new Size(_listView.NativeControl.Width - 1, _listView.NativeControl.Height - 1); - _listView.NativeControl.Size = - new Size(_listView.NativeControl.Width + 1, _listView.NativeControl.Height + 1); - } - } - - private void UpdateScanButton() - { - const int staticButtonCount = 2; - - // Clean up the dropdown - while (tsScan.DropDownItems.Count > staticButtonCount) - { - tsScan.DropDownItems.RemoveAt(0); - } - - // Populate the dropdown - var defaultProfile = _profileManager.DefaultProfile; - int i = 1; - foreach (var profile in _profileManager.Profiles) - { - var item = new ToolStripMenuItem - { - Text = profile.DisplayName.Replace("&", "&&"), - Image = profile == defaultProfile ? Icons.accept_small : null, - ImageScaling = ToolStripItemImageScaling.None - }; - AssignProfileShortcut(i, item); - item.Click += async (_, _) => await _desktopScanController.ScanWithProfile(profile); - tsScan.DropDownItems.Insert(tsScan.DropDownItems.Count - staticButtonCount, item); - - i++; - } - - if (_profileManager.Profiles.Any()) - { - tsScan.DropDownItems.Insert(tsScan.DropDownItems.Count - staticButtonCount, new ToolStripSeparator()); - } - } - - private void SaveToolStripLocation() - { - Config.User.Set(c => c.DesktopToolStripDock, tStrip.Parent.Dock); - } - - private void LoadToolStripLocation() - { - var dock = Config.Get(c => c.DesktopToolStripDock); - if (dock != DockStyle.None) - { - var panel = toolStripContainer1.Controls.OfType().FirstOrDefault(x => x.Dock == dock); - if (panel != null) - { - tStrip.Parent = panel; - } - } - tStrip.Parent.TabStop = true; - } - - #endregion - - #region Keyboard Shortcuts - - private void AssignKeyboardShortcuts() - { - // Defaults - - _ksm.Assign("Ctrl+Enter", tsScan); - _ksm.Assign("Ctrl+B", tsBatchScan); - _ksm.Assign("Ctrl+O", tsImport); - _ksm.Assign("Ctrl+S", tsdSavePDF); - _ksm.Assign("Ctrl+P", tsPrint); - _ksm.Assign("Ctrl+Up", _imageListActions.MoveUp); - _ksm.Assign("Ctrl+Left", _imageListActions.MoveUp); - _ksm.Assign("Ctrl+Down", _imageListActions.MoveDown); - _ksm.Assign("Ctrl+Right", _imageListActions.MoveDown); - _ksm.Assign("Ctrl+Shift+Del", tsClear); - _ksm.Assign("F1", _desktopSubFormController.ShowAboutForm); - _ksm.Assign("Ctrl+OemMinus", btnZoomOut); - _ksm.Assign("Ctrl+Oemplus", btnZoomIn); - _ksm.Assign("Del", ctxDelete); - _ksm.Assign("Ctrl+A", ctxSelectAll); - _ksm.Assign("Ctrl+C", ctxCopy); - _ksm.Assign("Ctrl+V", ctxPaste); - - // Configured - - var ks = Config.Get(c => c.KeyboardShortcuts); - - _ksm.Assign(ks.About, _desktopSubFormController.ShowAboutForm); - _ksm.Assign(ks.BatchScan, tsBatchScan); - _ksm.Assign(ks.Clear, tsClear); - _ksm.Assign(ks.Delete, tsDelete); - _ksm.Assign(ks.EmailPDF, tsdEmailPDF); - _ksm.Assign(ks.EmailPDFAll, tsEmailPDFAll); - _ksm.Assign(ks.EmailPDFSelected, tsEmailPDFSelected); - _ksm.Assign(ks.ImageBlackWhite, tsBlackWhite); - _ksm.Assign(ks.ImageBrightness, tsBrightnessContrast); - _ksm.Assign(ks.ImageContrast, tsBrightnessContrast); - _ksm.Assign(ks.ImageCrop, tsCrop); - _ksm.Assign(ks.ImageHue, tsHueSaturation); - _ksm.Assign(ks.ImageSaturation, tsHueSaturation); - _ksm.Assign(ks.ImageSharpen, tsSharpen); - _ksm.Assign(ks.ImageReset, tsReset); - _ksm.Assign(ks.ImageView, tsView); - _ksm.Assign(ks.Import, tsImport); - _ksm.Assign(ks.MoveDown, _imageListActions.MoveDown); - _ksm.Assign(ks.MoveUp, _imageListActions.MoveUp); - _ksm.Assign(ks.NewProfile, tsNewProfile); - _ksm.Assign(ks.Ocr, tsOcr); - _ksm.Assign(ks.Print, tsPrint); - _ksm.Assign(ks.Profiles, tsProfiles); - - _ksm.Assign(ks.ReorderAltDeinterleave, tsAltDeinterleave); - _ksm.Assign(ks.ReorderAltInterleave, tsAltInterleave); - _ksm.Assign(ks.ReorderDeinterleave, tsDeinterleave); - _ksm.Assign(ks.ReorderInterleave, tsInterleave); - _ksm.Assign(ks.ReorderReverseAll, tsReverseAll); - _ksm.Assign(ks.ReorderReverseSelected, tsReverseSelected); - _ksm.Assign(ks.RotateCustom, tsCustomRotation); - _ksm.Assign(ks.RotateFlip, tsFlip); - _ksm.Assign(ks.RotateLeft, tsRotateLeft); - _ksm.Assign(ks.RotateRight, tsRotateRight); - _ksm.Assign(ks.SaveImages, tsdSaveImages); - _ksm.Assign(ks.SaveImagesAll, tsSaveImagesAll); - _ksm.Assign(ks.SaveImagesSelected, tsSaveImagesSelected); - _ksm.Assign(ks.SavePDF, tsdSavePDF); - _ksm.Assign(ks.SavePDFAll, tsSavePDFAll); - _ksm.Assign(ks.SavePDFSelected, tsSavePDFSelected); - _ksm.Assign(ks.ScanDefault, tsScan); - - _ksm.Assign(ks.ZoomIn, btnZoomIn); - _ksm.Assign(ks.ZoomOut, btnZoomOut); - } - - private void AssignProfileShortcut(int i, ToolStripMenuItem item) - { - var sh = GetProfileShortcut(i); - if (string.IsNullOrWhiteSpace(sh) && i <= 11) - { - sh = "F" + (i + 1); - } - _ksm.Assign(sh, item); - } - - private string? GetProfileShortcut(int i) - { - // TODO: Granular - var ks = Config.Get(c => c.KeyboardShortcuts); - switch (i) - { - case 1: - return ks.ScanProfile1; - case 2: - return ks.ScanProfile2; - case 3: - return ks.ScanProfile3; - case 4: - return ks.ScanProfile4; - case 5: - return ks.ScanProfile5; - case 6: - return ks.ScanProfile6; - case 7: - return ks.ScanProfile7; - case 8: - return ks.ScanProfile8; - case 9: - return ks.ScanProfile9; - case 10: - return ks.ScanProfile10; - case 11: - return ks.ScanProfile11; - case 12: - return ks.ScanProfile12; - } - return null; - } - - private void ListViewKeyDown(object? sender, KeyEventArgs e) - { - e.Handled = _ksm.Perform(e.KeyData); - } - - private void ListViewMouseWheel(object? sender, MouseEventArgs e) - { - if (ModifierKeys.HasFlag(Keys.Control)) - { - StepThumbnailSize(e.Delta / (double) SystemInformation.MouseWheelScrollDelta); - } - } - - #endregion - - #region Event Handlers - Misc - - private void tStrip_ParentChanged(object sender, EventArgs e) => _toolbarFormatter.RelayoutToolbar(tStrip); - - #endregion - - #region Event Handlers - Toolbar - - private async void tsScan_ButtonClick(object sender, EventArgs e) => await _desktopScanController.ScanDefault(); - - private async void tsNewProfile_Click(object sender, EventArgs e) => - await _desktopScanController.ScanWithNewProfile(); - - private void tsBatchScan_Click(object sender, EventArgs e) => _desktopSubFormController.ShowBatchScanForm(); - private void tsProfiles_Click(object sender, EventArgs e) => _desktopSubFormController.ShowProfilesForm(); - - private void tsOcr_Click(object sender, EventArgs e) + else { + FormFactory.Create().ShowDialog(); if (_tesseractLanguageManager.InstalledLanguages.Any()) { FormFactory.Create().ShowDialog(); } - else - { - FormFactory.Create().ShowDialog(); - if (_tesseractLanguageManager.InstalledLanguages.Any()) - { - FormFactory.Create().ShowDialog(); - } - } } - - private void tsImport_Click(object sender, EventArgs e) => _desktopController.Import(); - - private async void tsdSavePDF_ButtonClick(object sender, EventArgs e) - { - var action = Config.Get(c => c.SaveButtonDefaultAction); - - if (action == SaveButtonDefaultAction.AlwaysPrompt - || action == SaveButtonDefaultAction.PromptIfSelected && _imageList.Selection.Any()) - { - tsdSavePDF.ShowDropDown(); - } - else if (action == SaveButtonDefaultAction.SaveSelected && _imageList.Selection.Any()) - { - await _desktopController.SavePDF(_imageList.Selection.ToList()); - } - else - { - await _desktopController.SavePDF(_imageList.Images); - } - } - - private async void tsdSaveImages_ButtonClick(object sender, EventArgs e) - { - var action = Config.Get(c => c.SaveButtonDefaultAction); - - if (action == SaveButtonDefaultAction.AlwaysPrompt - || action == SaveButtonDefaultAction.PromptIfSelected && _imageList.Selection.Any()) - { - tsdSaveImages.ShowDropDown(); - } - else if (action == SaveButtonDefaultAction.SaveSelected && _imageList.Selection.Any()) - { - await _desktopController.SaveImages(_imageList.Selection.ToList()); - } - else - { - await _desktopController.SaveImages(_imageList.Images); - } - } - - private async void tsdEmailPDF_ButtonClick(object sender, EventArgs e) - { - var action = Config.Get(c => c.SaveButtonDefaultAction); - - if (action == SaveButtonDefaultAction.AlwaysPrompt - || action == SaveButtonDefaultAction.PromptIfSelected && _imageList.Selection.Any()) - { - tsdEmailPDF.ShowDropDown(); - } - else if (action == SaveButtonDefaultAction.SaveSelected && _imageList.Selection.Any()) - { - await _desktopController.EmailPDF(_imageList.Selection.ToList()); - } - else - { - await _desktopController.EmailPDF(_imageList.Images); - } - } - - private async void tsPrint_Click(object sender, EventArgs e) - { - var state = _imageList.CurrentState; - using var allImages = _imageList.Images.Select(x => x.GetClonedImage()).ToDisposableList(); - using var selectedImages = _imageList.Selection.Select(x => x.GetClonedImage()).ToDisposableList(); - if (await _scannedImagePrinter.PromptToPrint(allImages.InnerList, selectedImages.InnerList)) - { - _imageList.SavedState = state; - } - } - - private void tsMove_FirstClick(object sender, EventArgs e) => _imageListActions.MoveUp(); - - private void tsMove_SecondClick(object sender, EventArgs e) => _imageListActions.MoveDown(); - - private void tsDelete_Click(object sender, EventArgs e) => _desktopController.Delete(); - - private void tsClear_Click(object sender, EventArgs e) => _desktopController.Clear(); - - private void tsAbout_Click(object sender, EventArgs e) => _desktopSubFormController.ShowAboutForm(); - - private void tsSettings_Click(object sender, EventArgs e) => _desktopSubFormController.ShowSettingsForm(); - - #endregion - - #region Event Handlers - Save/Email Menus - - private async void tsSavePDFAll_Click(object sender, EventArgs e) => - await _desktopController.SavePDF(_imageList.Images); - - private async void tsSavePDFSelected_Click(object sender, EventArgs e) => - await _desktopController.SavePDF(_imageList.Selection.ToList()); - - private async void tsPDFSettings_Click(object sender, EventArgs e) => - FormFactory.Create().ShowDialog(); - - private async void tsSaveImagesAll_Click(object sender, EventArgs e) => - await _desktopController.SaveImages(_imageList.Images); - - private async void tsSaveImagesSelected_Click(object sender, EventArgs e) => - await _desktopController.SaveImages(_imageList.Selection.ToList()); - - private void tsImageSettings_Click(object sender, EventArgs e) => - FormFactory.Create().ShowDialog(); - - private async void tsEmailPDFAll_Click(object sender, EventArgs e) => - await _desktopController.EmailPDF(_imageList.Images); - - private async void tsEmailPDFSelected_Click(object sender, EventArgs e) => - await _desktopController.EmailPDF(_imageList.Selection.ToList()); - - private void tsPdfSettings2_Click(object sender, EventArgs e) => - FormFactory.Create().ShowDialog(); - - private void tsEmailSettings_Click(object sender, EventArgs e) => - FormFactory.Create().ShowDialog(); - - #endregion - - #region Event Handlers - Image Menu - - private void tsView_Click(object sender, EventArgs e) => _desktopSubFormController.ShowViewerForm(); - private void tsCrop_Click(object sender, EventArgs e) => _desktopSubFormController.ShowImageForm(); - - private void tsBrightnessContrast_Click(object sender, EventArgs e) => - _desktopSubFormController.ShowImageForm(); - - private void tsHueSaturation_Click(object sender, EventArgs e) => - _desktopSubFormController.ShowImageForm(); - - private void tsBlackWhite_Click(object sender, EventArgs e) => - _desktopSubFormController.ShowImageForm(); - - private void tsSharpen_Click(object sender, EventArgs e) => _desktopSubFormController.ShowImageForm(); - private void tsReset_Click(object sender, EventArgs e) => _desktopController.ResetImage(); - - #endregion - - #region Event Handlers - Rotate Menu - - private async void tsRotateLeft_Click(object sender, EventArgs e) => await _imageListActions.RotateLeft(); - private async void tsRotateRight_Click(object sender, EventArgs e) => await _imageListActions.RotateRight(); - private async void tsFlip_Click(object sender, EventArgs e) => await _imageListActions.Flip(); - private void tsDeskew_Click(object sender, EventArgs e) => _imageListActions.Deskew(); - private void tsCustomRotation_Click(object sender, EventArgs e) => _desktopSubFormController.ShowImageForm(); - - #endregion - - #region Event Handlers - Reorder Menu - - private void tsInterleave_Click(object sender, EventArgs e) => _imageListActions.Interleave(); - private void tsDeinterleave_Click(object sender, EventArgs e) => _imageListActions.Deinterleave(); - private void tsAltInterleave_Click(object sender, EventArgs e) => _imageListActions.AltInterleave(); - private void tsAltDeinterleave_Click(object sender, EventArgs e) => _imageListActions.AltDeinterleave(); - private void tsReverseAll_Click(object sender, EventArgs e) => _imageListActions.ReverseAll(); - private void tsReverseSelected_Click(object sender, EventArgs e) => _imageListActions.ReverseSelected(); - - #endregion - - #region Context Menu - - private void contextMenuStrip_Opening(object sender, System.ComponentModel.CancelEventArgs e) - { - ctxPaste.Enabled = _imageTransfer.IsInClipboard(); - if (!_imageList.Images.Any() && !ctxPaste.Enabled) - { - e.Cancel = true; - } - } - - private void ctxSelectAll_Click(object sender, EventArgs e) => _imageListActions.SelectAll(); - private void ctxView_Click(object sender, EventArgs e) => _desktopSubFormController.ShowViewerForm(); - private void ctxDelete_Click(object sender, EventArgs e) => _desktopController.Delete(); - - private async void ctxCopy_Click(object sender, EventArgs e) => await _desktopController.Copy(); - - private void ctxPaste_Click(object sender, EventArgs e) => _desktopController.Paste(); - - #endregion - - #region Thumbnail Resizing - - private void StepThumbnailSize(double step) - { - int thumbnailSize = Config.Get(c => c.ThumbnailSize); - thumbnailSize = - (int) ThumbnailSizes.StepNumberToSize(ThumbnailSizes.SizeToStepNumber(thumbnailSize) + step); - thumbnailSize = Math.Max(Math.Min(thumbnailSize, ThumbnailSizes.MAX_SIZE), ThumbnailSizes.MIN_SIZE); - Config.User.Set(c => c.ThumbnailSize, thumbnailSize); - ResizeThumbnails(thumbnailSize); - } - - private void ResizeThumbnails(int thumbnailSize) - { - if (!_imageList.Images.Any()) - { - // Can't show visual feedback so don't do anything - // TODO: This is wrong? - return; - } - if (_listView.ImageSize == thumbnailSize) - { - // Same size so no resizing needed - return; - } - - // Adjust the visible thumbnail display with the new size - _listView.ImageSize = thumbnailSize; - _listView.RegenerateImages(); - - SetThumbnailSpacing(thumbnailSize); - UpdateToolbar(); // TODO: Do we need this? - - // Render high-quality thumbnails at the new size in a background task - // The existing (poorly scaled) thumbnails are used in the meantime - _thumbnailRenderQueue.SetThumbnailSize(thumbnailSize); - } - - private void SetThumbnailSpacing(int thumbnailSize) - { - _listView.NativeControl.Padding = new Padding(0, 20, 0, 0); - const int MIN_PADDING = 6; - const int MAX_PADDING = 66; - // Linearly scale the padding with the thumbnail size - int padding = MIN_PADDING + (MAX_PADDING - MIN_PADDING) * (thumbnailSize - ThumbnailSizes.MIN_SIZE) / - (ThumbnailSizes.MAX_SIZE - ThumbnailSizes.MIN_SIZE); - int spacing = thumbnailSize + padding * 2; - WinFormsHacks.SetListSpacing(_listView.NativeControl, spacing, spacing); - } - - private void btnZoomOut_Click(object sender, EventArgs e) => StepThumbnailSize(-1); - private void btnZoomIn_Click(object sender, EventArgs e) => StepThumbnailSize(1); - - #endregion - - #region Drag/Drop - - private void ListViewItemClicked(object? sender, EventArgs e) => _desktopSubFormController.ShowViewerForm(); - - private void ListViewSelectionChanged(object? sender, EventArgs e) - { - _imageList.UpdateSelection(_listView.Selection); - UpdateToolbar(); - } - - private void ListViewDrop(object? sender, DropEventArgs args) - { - if (_imageTransfer.IsIn(args.Data.ToEto())) - { - var data = _imageTransfer.GetFrom(args.Data.ToEto()); - if (data.ProcessId == Process.GetCurrentProcess().Id) - { - DragMoveImages(args.Position); - } - else - { - _desktopController.ImportDirect(data, false); - } - } - else if (args.Data.GetDataPresent(DataFormats.FileDrop)) - { - var data = (string[]) args.Data.GetData(DataFormats.FileDrop); - _desktopController.ImportFiles(data); - } - } - - private void DragMoveImages(int position) - { - if (!_imageList.Selection.Any()) - { - return; - } - if (position != -1) - { - _imageListActions.MoveTo(position); - } - } - - #endregion } + + private void tsImport_Click(object sender, EventArgs e) => _desktopController.Import(); + + private async void tsdSavePDF_ButtonClick(object sender, EventArgs e) + { + var action = Config.Get(c => c.SaveButtonDefaultAction); + + if (action == SaveButtonDefaultAction.AlwaysPrompt + || action == SaveButtonDefaultAction.PromptIfSelected && _imageList.Selection.Any()) + { + tsdSavePDF.ShowDropDown(); + } + else if (action == SaveButtonDefaultAction.SaveSelected && _imageList.Selection.Any()) + { + await _desktopController.SavePDF(_imageList.Selection.ToList()); + } + else + { + await _desktopController.SavePDF(_imageList.Images); + } + } + + private async void tsdSaveImages_ButtonClick(object sender, EventArgs e) + { + var action = Config.Get(c => c.SaveButtonDefaultAction); + + if (action == SaveButtonDefaultAction.AlwaysPrompt + || action == SaveButtonDefaultAction.PromptIfSelected && _imageList.Selection.Any()) + { + tsdSaveImages.ShowDropDown(); + } + else if (action == SaveButtonDefaultAction.SaveSelected && _imageList.Selection.Any()) + { + await _desktopController.SaveImages(_imageList.Selection.ToList()); + } + else + { + await _desktopController.SaveImages(_imageList.Images); + } + } + + private async void tsdEmailPDF_ButtonClick(object sender, EventArgs e) + { + var action = Config.Get(c => c.SaveButtonDefaultAction); + + if (action == SaveButtonDefaultAction.AlwaysPrompt + || action == SaveButtonDefaultAction.PromptIfSelected && _imageList.Selection.Any()) + { + tsdEmailPDF.ShowDropDown(); + } + else if (action == SaveButtonDefaultAction.SaveSelected && _imageList.Selection.Any()) + { + await _desktopController.EmailPDF(_imageList.Selection.ToList()); + } + else + { + await _desktopController.EmailPDF(_imageList.Images); + } + } + + private async void tsPrint_Click(object sender, EventArgs e) + { + var state = _imageList.CurrentState; + using var allImages = _imageList.Images.Select(x => x.GetClonedImage()).ToDisposableList(); + using var selectedImages = _imageList.Selection.Select(x => x.GetClonedImage()).ToDisposableList(); + if (await _scannedImagePrinter.PromptToPrint(allImages.InnerList, selectedImages.InnerList)) + { + _imageList.SavedState = state; + } + } + + private void tsMove_FirstClick(object sender, EventArgs e) => _imageListActions.MoveUp(); + + private void tsMove_SecondClick(object sender, EventArgs e) => _imageListActions.MoveDown(); + + private void tsDelete_Click(object sender, EventArgs e) => _desktopController.Delete(); + + private void tsClear_Click(object sender, EventArgs e) => _desktopController.Clear(); + + private void tsAbout_Click(object sender, EventArgs e) => _desktopSubFormController.ShowAboutForm(); + + private void tsSettings_Click(object sender, EventArgs e) => _desktopSubFormController.ShowSettingsForm(); + + #endregion + + #region Event Handlers - Save/Email Menus + + private async void tsSavePDFAll_Click(object sender, EventArgs e) => + await _desktopController.SavePDF(_imageList.Images); + + private async void tsSavePDFSelected_Click(object sender, EventArgs e) => + await _desktopController.SavePDF(_imageList.Selection.ToList()); + + private async void tsPDFSettings_Click(object sender, EventArgs e) => + FormFactory.Create().ShowDialog(); + + private async void tsSaveImagesAll_Click(object sender, EventArgs e) => + await _desktopController.SaveImages(_imageList.Images); + + private async void tsSaveImagesSelected_Click(object sender, EventArgs e) => + await _desktopController.SaveImages(_imageList.Selection.ToList()); + + private void tsImageSettings_Click(object sender, EventArgs e) => + FormFactory.Create().ShowDialog(); + + private async void tsEmailPDFAll_Click(object sender, EventArgs e) => + await _desktopController.EmailPDF(_imageList.Images); + + private async void tsEmailPDFSelected_Click(object sender, EventArgs e) => + await _desktopController.EmailPDF(_imageList.Selection.ToList()); + + private void tsPdfSettings2_Click(object sender, EventArgs e) => + FormFactory.Create().ShowDialog(); + + private void tsEmailSettings_Click(object sender, EventArgs e) => + FormFactory.Create().ShowDialog(); + + #endregion + + #region Event Handlers - Image Menu + + private void tsView_Click(object sender, EventArgs e) => _desktopSubFormController.ShowViewerForm(); + private void tsCrop_Click(object sender, EventArgs e) => _desktopSubFormController.ShowImageForm(); + + private void tsBrightnessContrast_Click(object sender, EventArgs e) => + _desktopSubFormController.ShowImageForm(); + + private void tsHueSaturation_Click(object sender, EventArgs e) => + _desktopSubFormController.ShowImageForm(); + + private void tsBlackWhite_Click(object sender, EventArgs e) => + _desktopSubFormController.ShowImageForm(); + + private void tsSharpen_Click(object sender, EventArgs e) => _desktopSubFormController.ShowImageForm(); + private void tsReset_Click(object sender, EventArgs e) => _desktopController.ResetImage(); + + #endregion + + #region Event Handlers - Rotate Menu + + private async void tsRotateLeft_Click(object sender, EventArgs e) => await _imageListActions.RotateLeft(); + private async void tsRotateRight_Click(object sender, EventArgs e) => await _imageListActions.RotateRight(); + private async void tsFlip_Click(object sender, EventArgs e) => await _imageListActions.Flip(); + private void tsDeskew_Click(object sender, EventArgs e) => _imageListActions.Deskew(); + private void tsCustomRotation_Click(object sender, EventArgs e) => _desktopSubFormController.ShowImageForm(); + + #endregion + + #region Event Handlers - Reorder Menu + + private void tsInterleave_Click(object sender, EventArgs e) => _imageListActions.Interleave(); + private void tsDeinterleave_Click(object sender, EventArgs e) => _imageListActions.Deinterleave(); + private void tsAltInterleave_Click(object sender, EventArgs e) => _imageListActions.AltInterleave(); + private void tsAltDeinterleave_Click(object sender, EventArgs e) => _imageListActions.AltDeinterleave(); + private void tsReverseAll_Click(object sender, EventArgs e) => _imageListActions.ReverseAll(); + private void tsReverseSelected_Click(object sender, EventArgs e) => _imageListActions.ReverseSelected(); + + #endregion + + #region Context Menu + + private void contextMenuStrip_Opening(object sender, System.ComponentModel.CancelEventArgs e) + { + ctxPaste.Enabled = _imageTransfer.IsInClipboard(); + if (!_imageList.Images.Any() && !ctxPaste.Enabled) + { + e.Cancel = true; + } + } + + private void ctxSelectAll_Click(object sender, EventArgs e) => _imageListActions.SelectAll(); + private void ctxView_Click(object sender, EventArgs e) => _desktopSubFormController.ShowViewerForm(); + private void ctxDelete_Click(object sender, EventArgs e) => _desktopController.Delete(); + + private async void ctxCopy_Click(object sender, EventArgs e) => await _desktopController.Copy(); + + private void ctxPaste_Click(object sender, EventArgs e) => _desktopController.Paste(); + + #endregion + + #region Thumbnail Resizing + + private void StepThumbnailSize(double step) + { + int thumbnailSize = Config.Get(c => c.ThumbnailSize); + thumbnailSize = + (int) ThumbnailSizes.StepNumberToSize(ThumbnailSizes.SizeToStepNumber(thumbnailSize) + step); + thumbnailSize = Math.Max(Math.Min(thumbnailSize, ThumbnailSizes.MAX_SIZE), ThumbnailSizes.MIN_SIZE); + Config.User.Set(c => c.ThumbnailSize, thumbnailSize); + ResizeThumbnails(thumbnailSize); + } + + private void ResizeThumbnails(int thumbnailSize) + { + if (!_imageList.Images.Any()) + { + // Can't show visual feedback so don't do anything + // TODO: This is wrong? + return; + } + if (_listView.ImageSize == thumbnailSize) + { + // Same size so no resizing needed + return; + } + + // Adjust the visible thumbnail display with the new size + _listView.ImageSize = thumbnailSize; + _listView.RegenerateImages(); + + SetThumbnailSpacing(thumbnailSize); + UpdateToolbar(); // TODO: Do we need this? + + // Render high-quality thumbnails at the new size in a background task + // The existing (poorly scaled) thumbnails are used in the meantime + _thumbnailRenderQueue.SetThumbnailSize(thumbnailSize); + } + + private void SetThumbnailSpacing(int thumbnailSize) + { + _listView.NativeControl.Padding = new Padding(0, 20, 0, 0); + const int MIN_PADDING = 6; + const int MAX_PADDING = 66; + // Linearly scale the padding with the thumbnail size + int padding = MIN_PADDING + (MAX_PADDING - MIN_PADDING) * (thumbnailSize - ThumbnailSizes.MIN_SIZE) / + (ThumbnailSizes.MAX_SIZE - ThumbnailSizes.MIN_SIZE); + int spacing = thumbnailSize + padding * 2; + WinFormsHacks.SetListSpacing(_listView.NativeControl, spacing, spacing); + } + + private void btnZoomOut_Click(object sender, EventArgs e) => StepThumbnailSize(-1); + private void btnZoomIn_Click(object sender, EventArgs e) => StepThumbnailSize(1); + + #endregion + + #region Drag/Drop + + private void ListViewItemClicked(object? sender, EventArgs e) => _desktopSubFormController.ShowViewerForm(); + + private void ListViewSelectionChanged(object? sender, EventArgs e) + { + _imageList.UpdateSelection(_listView.Selection); + UpdateToolbar(); + } + + private void ListViewDrop(object? sender, DropEventArgs args) + { + if (_imageTransfer.IsIn(args.Data.ToEto())) + { + var data = _imageTransfer.GetFrom(args.Data.ToEto()); + if (data.ProcessId == Process.GetCurrentProcess().Id) + { + DragMoveImages(args.Position); + } + else + { + _desktopController.ImportDirect(data, false); + } + } + else if (args.Data.GetDataPresent(DataFormats.FileDrop)) + { + var data = (string[]) args.Data.GetData(DataFormats.FileDrop); + _desktopController.ImportFiles(data); + } + } + + private void DragMoveImages(int position) + { + if (!_imageList.Selection.Any()) + { + return; + } + if (position != -1) + { + _imageListActions.MoveTo(position); + } + } + + #endregion } \ No newline at end of file diff --git a/NAPS2.Lib.WinForms/WinForms/FEmailProvider.cs b/NAPS2.Lib.WinForms/WinForms/FEmailProvider.cs index beac2e112..09b1ab95d 100644 --- a/NAPS2.Lib.WinForms/WinForms/FEmailProvider.cs +++ b/NAPS2.Lib.WinForms/WinForms/FEmailProvider.cs @@ -5,147 +5,146 @@ using NAPS2.ImportExport.Email.Mapi; using NAPS2.ImportExport.Email.Oauth; using NAPS2.Scan; -namespace NAPS2.WinForms +namespace NAPS2.WinForms; + +public partial class FEmailProvider : FormBase { - public partial class FEmailProvider : FormBase + private readonly GmailOauthProvider _gmailOauthProvider; + private readonly OutlookWebOauthProvider _outlookWebOauthProvider; + private readonly SystemEmailClients _systemEmailClients; + + private List _providerWidgets; + private string[] _systemClientNames; + private string _defaultSystemClientName; + + public FEmailProvider(GmailOauthProvider gmailOauthProvider, OutlookWebOauthProvider outlookWebOauthProvider, SystemEmailClients systemEmailClients) { - private readonly GmailOauthProvider _gmailOauthProvider; - private readonly OutlookWebOauthProvider _outlookWebOauthProvider; - private readonly SystemEmailClients _systemEmailClients; + _gmailOauthProvider = gmailOauthProvider; + _outlookWebOauthProvider = outlookWebOauthProvider; + _systemEmailClients = systemEmailClients; - private List _providerWidgets; - private string[] _systemClientNames; - private string _defaultSystemClientName; + InitializeComponent(); + } - public FEmailProvider(GmailOauthProvider gmailOauthProvider, OutlookWebOauthProvider outlookWebOauthProvider, SystemEmailClients systemEmailClients) + private void FEmailProvider_Load(object sender, EventArgs e) + { + _providerWidgets = new List(); + _systemClientNames = _systemEmailClients.GetNames(); + _defaultSystemClientName = _systemEmailClients.GetDefaultName(); + + foreach (var clientName in _systemClientNames.OrderBy(x => x == _defaultSystemClientName ? 0 : 1)) { - _gmailOauthProvider = gmailOauthProvider; - _outlookWebOauthProvider = outlookWebOauthProvider; - _systemEmailClients = systemEmailClients; - - InitializeComponent(); + _providerWidgets.Add(new EmailProviderWidget + { + ProviderType = EmailProviderType.System, + ProviderIcon = _systemEmailClients.GetIcon(clientName) ?? Icons.mail_yellow, + ProviderName = clientName, + ClickAction = () => ChooseSystem(clientName) + }); } - private void FEmailProvider_Load(object sender, EventArgs e) + if (_gmailOauthProvider.HasClientCreds) { - _providerWidgets = new List(); - _systemClientNames = _systemEmailClients.GetNames(); - _defaultSystemClientName = _systemEmailClients.GetDefaultName(); - - foreach (var clientName in _systemClientNames.OrderBy(x => x == _defaultSystemClientName ? 0 : 1)) + _providerWidgets.Add(new EmailProviderWidget { - _providerWidgets.Add(new EmailProviderWidget - { - ProviderType = EmailProviderType.System, - ProviderIcon = _systemEmailClients.GetIcon(clientName) ?? Icons.mail_yellow, - ProviderName = clientName, - ClickAction = () => ChooseSystem(clientName) - }); - } - - if (_gmailOauthProvider.HasClientCreds) - { - _providerWidgets.Add(new EmailProviderWidget - { - ProviderType = EmailProviderType.Gmail, - ProviderIcon = Icons.gmail, - ProviderName = EmailProviderType.Gmail.Description(), - ClickAction = () => ChooseOauth(_gmailOauthProvider) - }); - } - - if (_outlookWebOauthProvider.HasClientCreds) - { - _providerWidgets.Add(new EmailProviderWidget - { - ProviderType = EmailProviderType.OutlookWeb, - ProviderIcon = Icons.outlookweb, - ProviderName = EmailProviderType.OutlookWeb.Description(), - ClickAction = () => ChooseOauth(_outlookWebOauthProvider) - }); - } - - //providerWidgets.Add(new EmailProviderWidget - //{ - // ProviderType = EmailProviderType.CustomSmtp, - // ProviderIcon = Icons.email_setting, - // ProviderName = EmailProviderType.CustomSmtp.Description(), - // ClickAction = ChooseCustomSmtp - //}); - - // Put the configured provider at the top - var defaultWidget = GetDefaultWidget(); - if (defaultWidget != null) - { - _providerWidgets.Remove(defaultWidget); - _providerWidgets.Insert(0, defaultWidget); - } - - ShowWidgets(); + ProviderType = EmailProviderType.Gmail, + ProviderIcon = Icons.gmail, + ProviderName = EmailProviderType.Gmail.Description(), + ClickAction = () => ChooseOauth(_gmailOauthProvider) + }); } - private void ChooseSystem(string clientName) + if (_outlookWebOauthProvider.HasClientCreds) + { + _providerWidgets.Add(new EmailProviderWidget + { + ProviderType = EmailProviderType.OutlookWeb, + ProviderIcon = Icons.outlookweb, + ProviderName = EmailProviderType.OutlookWeb.Description(), + ClickAction = () => ChooseOauth(_outlookWebOauthProvider) + }); + } + + //providerWidgets.Add(new EmailProviderWidget + //{ + // ProviderType = EmailProviderType.CustomSmtp, + // ProviderIcon = Icons.email_setting, + // ProviderName = EmailProviderType.CustomSmtp.Description(), + // ClickAction = ChooseCustomSmtp + //}); + + // Put the configured provider at the top + var defaultWidget = GetDefaultWidget(); + if (defaultWidget != null) + { + _providerWidgets.Remove(defaultWidget); + _providerWidgets.Insert(0, defaultWidget); + } + + ShowWidgets(); + } + + private void ChooseSystem(string clientName) + { + var emailSetup = Config.Get(c => c.EmailSetup); + emailSetup.SystemProviderName = clientName == _defaultSystemClientName ? null : clientName; + emailSetup.ProviderType = EmailProviderType.System; + Config.User.Set(c => c.EmailSetup, emailSetup); + DialogResult = DialogResult.OK; + Close(); + } + + private void ChooseOauth(OauthProvider provider) + { + var authForm = FormFactory.Create(); + authForm.OauthProvider = provider; + if (authForm.ShowDialog() == DialogResult.OK) { - var emailSetup = Config.Get(c => c.EmailSetup); - emailSetup.SystemProviderName = clientName == _defaultSystemClientName ? null : clientName; - emailSetup.ProviderType = EmailProviderType.System; - Config.User.Set(c => c.EmailSetup, emailSetup); DialogResult = DialogResult.OK; Close(); } + } - private void ChooseOauth(OauthProvider provider) + private void ShowWidgets() + { + int heightDiff = Height - panel1.Height; + panel1.Height = 0; + foreach (var widget in _providerWidgets) { - var authForm = FormFactory.Create(); - authForm.OauthProvider = provider; - if (authForm.ShowDialog() == DialogResult.OK) - { - DialogResult = DialogResult.OK; - Close(); - } + panel1.Controls.Add(widget); + widget.Top = panel1.Height; + widget.Width = panel1.Width; + widget.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right; + panel1.Height += widget.Height - 1; } - private void ShowWidgets() - { - int heightDiff = Height - panel1.Height; - panel1.Height = 0; - foreach (var widget in _providerWidgets) - { - panel1.Controls.Add(widget); - widget.Top = panel1.Height; - widget.Width = panel1.Width; - widget.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right; - panel1.Height += widget.Height - 1; - } + panel1.Height += 1; + MaximumSize = new Size(MaximumSize.Width, panel1.Height + heightDiff); + MinimumSize = new Size(MinimumSize.Width, panel1.Height + heightDiff); + } - panel1.Height += 1; - MaximumSize = new Size(MaximumSize.Width, panel1.Height + heightDiff); - MinimumSize = new Size(MinimumSize.Width, panel1.Height + heightDiff); - } - - private EmailProviderWidget GetDefaultWidget() + private EmailProviderWidget GetDefaultWidget() + { + var emailSetup = Config.Get(c => c.EmailSetup); + foreach (var widget in _providerWidgets) { - var emailSetup = Config.Get(c => c.EmailSetup); - foreach (var widget in _providerWidgets) + if (widget.ProviderType == emailSetup.ProviderType) { - if (widget.ProviderType == emailSetup.ProviderType) + if (widget.ProviderType == EmailProviderType.System) { - if (widget.ProviderType == EmailProviderType.System) - { - // System providers need additional logic since there may be more than one - if (widget.ProviderName == emailSetup.SystemProviderName - || string.IsNullOrEmpty(emailSetup.SystemProviderName) && widget.ProviderName == _defaultSystemClientName) - { - return widget; - } - } - else + // System providers need additional logic since there may be more than one + if (widget.ProviderName == emailSetup.SystemProviderName + || string.IsNullOrEmpty(emailSetup.SystemProviderName) && widget.ProviderName == _defaultSystemClientName) { return widget; } } + else + { + return widget; + } } - return null; } + return null; } -} +} \ No newline at end of file diff --git a/NAPS2.Lib.WinForms/WinForms/FHueSaturation.cs b/NAPS2.Lib.WinForms/WinForms/FHueSaturation.cs index 277cd0395..8aedad1de 100644 --- a/NAPS2.Lib.WinForms/WinForms/FHueSaturation.cs +++ b/NAPS2.Lib.WinForms/WinForms/FHueSaturation.cs @@ -1,75 +1,74 @@ using System.Windows.Forms; -namespace NAPS2.WinForms +namespace NAPS2.WinForms; + +partial class FHueSaturation : ImageForm { - partial class FHueSaturation : ImageForm + public FHueSaturation(ImageContext imageContext) + : base(imageContext) { - public FHueSaturation(ImageContext imageContext) - : base(imageContext) - { - InitializeComponent(); - ActiveControl = txtHue; - } - - public HueTransform HueTransform { get; private set; } = new HueTransform(); - - public SaturationTransform SaturationTransform { get; private set; } = new SaturationTransform(); - - protected override IEnumerable Transforms => new Transform[] { HueTransform, SaturationTransform }; - - protected override PictureBox PictureBox => pictureBox; - - protected override void ResetTransform() - { - HueTransform = new HueTransform(); - SaturationTransform = new SaturationTransform(); - tbHue.Value = 0; - tbSaturation.Value = 0; - txtHue.Text = tbHue.Value.ToString("G"); - txtSaturation.Text = tbSaturation.Value.ToString("G"); - } - - private void UpdateTransform() - { - HueTransform = new HueTransform(tbHue.Value); - SaturationTransform = new SaturationTransform(tbSaturation.Value); - UpdatePreviewBox(); - } - - private void txtHue_TextChanged(object sender, EventArgs e) - { - if (int.TryParse(txtHue.Text, out int value)) - { - if (value >= tbHue.Minimum && value <= tbHue.Maximum) - { - tbHue.Value = value; - } - } - UpdateTransform(); - } - - private void tbHue_Scroll(object sender, EventArgs e) - { - txtHue.Text = tbHue.Value.ToString("G"); - UpdateTransform(); - } - - private void txtSaturation_TextChanged(object sender, EventArgs e) - { - if (int.TryParse(txtSaturation.Text, out int value)) - { - if (value >= tbSaturation.Minimum && value <= tbSaturation.Maximum) - { - tbSaturation.Value = value; - } - } - UpdateTransform(); - } - - private void tbSaturation_Scroll(object sender, EventArgs e) - { - txtSaturation.Text = tbSaturation.Value.ToString("G"); - UpdateTransform(); - } + InitializeComponent(); + ActiveControl = txtHue; } -} + + public HueTransform HueTransform { get; private set; } = new HueTransform(); + + public SaturationTransform SaturationTransform { get; private set; } = new SaturationTransform(); + + protected override IEnumerable Transforms => new Transform[] { HueTransform, SaturationTransform }; + + protected override PictureBox PictureBox => pictureBox; + + protected override void ResetTransform() + { + HueTransform = new HueTransform(); + SaturationTransform = new SaturationTransform(); + tbHue.Value = 0; + tbSaturation.Value = 0; + txtHue.Text = tbHue.Value.ToString("G"); + txtSaturation.Text = tbSaturation.Value.ToString("G"); + } + + private void UpdateTransform() + { + HueTransform = new HueTransform(tbHue.Value); + SaturationTransform = new SaturationTransform(tbSaturation.Value); + UpdatePreviewBox(); + } + + private void txtHue_TextChanged(object sender, EventArgs e) + { + if (int.TryParse(txtHue.Text, out int value)) + { + if (value >= tbHue.Minimum && value <= tbHue.Maximum) + { + tbHue.Value = value; + } + } + UpdateTransform(); + } + + private void tbHue_Scroll(object sender, EventArgs e) + { + txtHue.Text = tbHue.Value.ToString("G"); + UpdateTransform(); + } + + private void txtSaturation_TextChanged(object sender, EventArgs e) + { + if (int.TryParse(txtSaturation.Text, out int value)) + { + if (value >= tbSaturation.Minimum && value <= tbSaturation.Maximum) + { + tbSaturation.Value = value; + } + } + UpdateTransform(); + } + + private void tbSaturation_Scroll(object sender, EventArgs e) + { + txtSaturation.Text = tbSaturation.Value.ToString("G"); + UpdateTransform(); + } +} \ No newline at end of file diff --git a/NAPS2.Lib.WinForms/WinForms/FOcrLanguageDownload.cs b/NAPS2.Lib.WinForms/WinForms/FOcrLanguageDownload.cs index ea3b5e551..2fc80267c 100644 --- a/NAPS2.Lib.WinForms/WinForms/FOcrLanguageDownload.cs +++ b/NAPS2.Lib.WinForms/WinForms/FOcrLanguageDownload.cs @@ -1,92 +1,91 @@ using System.Windows.Forms; using NAPS2.Ocr; -namespace NAPS2.WinForms +namespace NAPS2.WinForms; + +public partial class FOcrLanguageDownload : FormBase { - public partial class FOcrLanguageDownload : FormBase + private readonly TesseractLanguageManager _tesseractLanguageManager; + + public FOcrLanguageDownload(TesseractLanguageManager tesseractLanguageManager) { - private readonly TesseractLanguageManager _tesseractLanguageManager; + _tesseractLanguageManager = tesseractLanguageManager; + InitializeComponent(); - public FOcrLanguageDownload(TesseractLanguageManager tesseractLanguageManager) + var initialSelection = new HashSet(); + // TODO: We used to select old installed languages here, maybe we could do it again if we get new lang data + if (!_tesseractLanguageManager.InstalledLanguages.Any()) { - _tesseractLanguageManager = tesseractLanguageManager; - InitializeComponent(); + // Fresh install, so pre-select English as a sensible default + initialSelection.Add("ocr-eng"); + } - var initialSelection = new HashSet(); - // TODO: We used to select old installed languages here, maybe we could do it again if we get new lang data - if (!_tesseractLanguageManager.InstalledLanguages.Any()) + // Populate the list of language options + // Special case for English: sorted to the top of the list + var languageOptions = _tesseractLanguageManager.NotInstalledLanguages + .OrderBy(x => x.Code == "eng" ? "AAA" : x.Name); + foreach (var languageOption in languageOptions) + { + var item = new ListViewItem { Text = languageOption.Name, Tag = languageOption.Code }; + if (initialSelection.Contains($"ocr-{languageOption.Code}")) { - // Fresh install, so pre-select English as a sensible default - initialSelection.Add("ocr-eng"); + item.Checked = true; } - - // Populate the list of language options - // Special case for English: sorted to the top of the list - var languageOptions = _tesseractLanguageManager.NotInstalledLanguages - .OrderBy(x => x.Code == "eng" ? "AAA" : x.Name); - foreach (var languageOption in languageOptions) - { - var item = new ListViewItem { Text = languageOption.Name, Tag = languageOption.Code }; - if (initialSelection.Contains($"ocr-{languageOption.Code}")) - { - item.Checked = true; - } - lvLanguages.Items.Add(item); - } - - UpdateView(); + lvLanguages.Items.Add(item); } - protected override void OnLoad(object sender, EventArgs eventArgs) - { - new LayoutManager(this) - .Bind(lvLanguages) - .WidthToForm() - .HeightToForm() - .Bind(labelSizeEstimate, btnCancel, btnDownload) - .BottomToForm() - .Bind(btnCancel, btnDownload) - .RightToForm() - .Activate(); - } - - private void UpdateView() - { - var selectedLanguages = SelectedLanguages; - double downloadSize = _tesseractLanguageManager.LanguageComponents.Where(x => selectedLanguages.Contains(x.Id)).Select(x => x.DownloadInfo.Size).Sum(); - - labelSizeEstimate.Text = string.Format(MiscResources.EstimatedDownloadSize, downloadSize.ToString("f1")); - - btnDownload.Enabled = lvLanguages.Items.Cast().Any(x => x.Checked); - } - - private HashSet SelectedLanguages - { - get { return new HashSet(lvLanguages.Items.Cast().Where(x => x.Checked).Select(x => $"ocr-{x.Tag}")); } - } - - private void lvLanguages_ItemChecked(object sender, ItemCheckedEventArgs e) - { - UpdateView(); - } - - private void btnCancel_Click(object sender, EventArgs e) - { - Close(); - } - - private void btnDownload_Click(object sender, EventArgs e) - { - var progressForm = FormFactory.Create(); - - var selectedLanguages = SelectedLanguages; - foreach (var langComponent in _tesseractLanguageManager.LanguageComponents.Where(x => selectedLanguages.Contains(x.Id))) - { - progressForm.QueueFile(langComponent); - } - - Close(); - progressForm.ShowDialog(); - } + UpdateView(); } -} + + protected override void OnLoad(object sender, EventArgs eventArgs) + { + new LayoutManager(this) + .Bind(lvLanguages) + .WidthToForm() + .HeightToForm() + .Bind(labelSizeEstimate, btnCancel, btnDownload) + .BottomToForm() + .Bind(btnCancel, btnDownload) + .RightToForm() + .Activate(); + } + + private void UpdateView() + { + var selectedLanguages = SelectedLanguages; + double downloadSize = _tesseractLanguageManager.LanguageComponents.Where(x => selectedLanguages.Contains(x.Id)).Select(x => x.DownloadInfo.Size).Sum(); + + labelSizeEstimate.Text = string.Format(MiscResources.EstimatedDownloadSize, downloadSize.ToString("f1")); + + btnDownload.Enabled = lvLanguages.Items.Cast().Any(x => x.Checked); + } + + private HashSet SelectedLanguages + { + get { return new HashSet(lvLanguages.Items.Cast().Where(x => x.Checked).Select(x => $"ocr-{x.Tag}")); } + } + + private void lvLanguages_ItemChecked(object sender, ItemCheckedEventArgs e) + { + UpdateView(); + } + + private void btnCancel_Click(object sender, EventArgs e) + { + Close(); + } + + private void btnDownload_Click(object sender, EventArgs e) + { + var progressForm = FormFactory.Create(); + + var selectedLanguages = SelectedLanguages; + foreach (var langComponent in _tesseractLanguageManager.LanguageComponents.Where(x => selectedLanguages.Contains(x.Id))) + { + progressForm.QueueFile(langComponent); + } + + Close(); + progressForm.ShowDialog(); + } +} \ No newline at end of file diff --git a/NAPS2.Lib.WinForms/WinForms/FPdfPassword.cs b/NAPS2.Lib.WinForms/WinForms/FPdfPassword.cs index 8b659b4ec..f8bcc093d 100644 --- a/NAPS2.Lib.WinForms/WinForms/FPdfPassword.cs +++ b/NAPS2.Lib.WinForms/WinForms/FPdfPassword.cs @@ -1,37 +1,36 @@ using System.Windows.Forms; -namespace NAPS2.WinForms +namespace NAPS2.WinForms; + +public partial class FPdfPassword : FormBase { - public partial class FPdfPassword : FormBase + public FPdfPassword() { - public FPdfPassword() - { - RestoreFormState = false; - InitializeComponent(); - AcceptButton = btnOK; - CancelButton = btnCancel; - } - - public string FileName { get; set; } - - public string Password { get; private set; } - - protected override void OnLoad(object sender, EventArgs eventArgs) - { - lblPrompt.Text = string.Format(lblPrompt.Text, FileName); - } - - private void btnOK_Click(object sender, EventArgs e) - { - Password = txtPassword.Text; - DialogResult = DialogResult.OK; - Close(); - } - - private void btnCancel_Click(object sender, EventArgs e) - { - DialogResult = DialogResult.Cancel; - Close(); - } + RestoreFormState = false; + InitializeComponent(); + AcceptButton = btnOK; + CancelButton = btnCancel; } -} + + public string FileName { get; set; } + + public string Password { get; private set; } + + protected override void OnLoad(object sender, EventArgs eventArgs) + { + lblPrompt.Text = string.Format(lblPrompt.Text, FileName); + } + + private void btnOK_Click(object sender, EventArgs e) + { + Password = txtPassword.Text; + DialogResult = DialogResult.OK; + Close(); + } + + private void btnCancel_Click(object sender, EventArgs e) + { + DialogResult = DialogResult.Cancel; + Close(); + } +} \ No newline at end of file diff --git a/NAPS2.Lib.WinForms/WinForms/FProgress.cs b/NAPS2.Lib.WinForms/WinForms/FProgress.cs index f3abc80b5..4d7100e65 100644 --- a/NAPS2.Lib.WinForms/WinForms/FProgress.cs +++ b/NAPS2.Lib.WinForms/WinForms/FProgress.cs @@ -1,98 +1,97 @@ using System.Windows.Forms; -namespace NAPS2.WinForms +namespace NAPS2.WinForms; + +public partial class FProgress : FormBase { - public partial class FProgress : FormBase + private readonly ErrorOutput _errorOutput; + private readonly OperationProgress _operationProgress; + + private volatile bool _loaded; + private volatile bool _background; + private IOperation _operation; + + public FProgress(ErrorOutput errorOutput, OperationProgress operationProgress) { - private readonly ErrorOutput _errorOutput; - private readonly OperationProgress _operationProgress; + _errorOutput = errorOutput; + _operationProgress = operationProgress; + InitializeComponent(); - private volatile bool _loaded; - private volatile bool _background; - private IOperation _operation; + RestoreFormState = false; + } - public FProgress(ErrorOutput errorOutput, OperationProgress operationProgress) + public IOperation Operation + { + get => _operation; + set { - _errorOutput = errorOutput; - _operationProgress = operationProgress; - InitializeComponent(); - - RestoreFormState = false; + _operation = value; + _operation.StatusChanged += operation_StatusChanged; + _operation.Finished += operation_Finished; + btnCancel.Visible = _operation.AllowCancel; } + } - public IOperation Operation + void operation_StatusChanged(object sender, EventArgs e) + { + if (_loaded && !_background) { - get => _operation; - set - { - _operation = value; - _operation.StatusChanged += operation_StatusChanged; - _operation.Finished += operation_Finished; - btnCancel.Visible = _operation.AllowCancel; - } + SafeInvoke(DisplayProgress); } + } - void operation_StatusChanged(object sender, EventArgs e) + void operation_Finished(object sender, EventArgs e) + { + if (_loaded && !_background) { - if (_loaded && !_background) - { - SafeInvoke(DisplayProgress); - } + SafeInvoke(Close); } + } - void operation_Finished(object sender, EventArgs e) + protected override void OnLoad(object sender, EventArgs eventArgs) + { + _loaded = true; + Text = _operation.ProgressTitle; + btnRunInBG.Visible = _operation.AllowBackground; + + DisplayProgress(); + if (_operation.IsFinished) { - if (_loaded && !_background) - { - SafeInvoke(Close); - } - } - - protected override void OnLoad(object sender, EventArgs eventArgs) - { - _loaded = true; - Text = _operation.ProgressTitle; - btnRunInBG.Visible = _operation.AllowBackground; - - DisplayProgress(); - if (_operation.IsFinished) - { - Close(); - } - } - - private void DisplayProgress() - { - WinFormsOperationProgress.RenderStatus(Operation, labelStatus, labelNumber, progressBar); - } - - private void btnCancel_Click(object sender, EventArgs e) - { - TryCancelOp(); - } - - private void FDownloadProgress_FormClosing(object sender, FormClosingEventArgs e) - { - if (!_operation.IsFinished && !_background) - { - TryCancelOp(); - e.Cancel = true; - } - } - - private void TryCancelOp() - { - if (Operation.AllowCancel) - { - Operation.Cancel(); - btnCancel.Enabled = false; - } - } - - private void btnRunInBG_Click(object sender, EventArgs e) - { - _background = true; Close(); } } -} + + private void DisplayProgress() + { + WinFormsOperationProgress.RenderStatus(Operation, labelStatus, labelNumber, progressBar); + } + + private void btnCancel_Click(object sender, EventArgs e) + { + TryCancelOp(); + } + + private void FDownloadProgress_FormClosing(object sender, FormClosingEventArgs e) + { + if (!_operation.IsFinished && !_background) + { + TryCancelOp(); + e.Cancel = true; + } + } + + private void TryCancelOp() + { + if (Operation.AllowCancel) + { + Operation.Cancel(); + btnCancel.Enabled = false; + } + } + + private void btnRunInBG_Click(object sender, EventArgs e) + { + _background = true; + Close(); + } +} \ No newline at end of file diff --git a/NAPS2.Lib.WinForms/WinForms/FRecover.cs b/NAPS2.Lib.WinForms/WinForms/FRecover.cs index 3ec3eac98..2c761a104 100644 --- a/NAPS2.Lib.WinForms/WinForms/FRecover.cs +++ b/NAPS2.Lib.WinForms/WinForms/FRecover.cs @@ -1,38 +1,37 @@ using System.Windows.Forms; -namespace NAPS2.WinForms +namespace NAPS2.WinForms; + +public partial class FRecover : FormBase { - public partial class FRecover : FormBase + public FRecover() { - public FRecover() - { - RestoreFormState = false; - InitializeComponent(); - AcceptButton = btnRecover; - CancelButton = btnCancel; - } - - public void SetData(int imageCount, DateTime scannedDateTime) - { - lblPrompt.Text = string.Format(lblPrompt.Text, imageCount, scannedDateTime.ToShortDateString(), scannedDateTime.ToShortTimeString()); - } - - private void btnDelete_Click(object sender, EventArgs e) - { - DialogResult = DialogResult.No; - Close(); - } - - private void btnRecover_Click(object sender, EventArgs e) - { - DialogResult = DialogResult.Yes; - Close(); - } - - private void btnCancel_Click(object sender, EventArgs e) - { - DialogResult = DialogResult.Cancel; - Close(); - } + RestoreFormState = false; + InitializeComponent(); + AcceptButton = btnRecover; + CancelButton = btnCancel; } -} + + public void SetData(int imageCount, DateTime scannedDateTime) + { + lblPrompt.Text = string.Format(lblPrompt.Text, imageCount, scannedDateTime.ToShortDateString(), scannedDateTime.ToShortTimeString()); + } + + private void btnDelete_Click(object sender, EventArgs e) + { + DialogResult = DialogResult.No; + Close(); + } + + private void btnRecover_Click(object sender, EventArgs e) + { + DialogResult = DialogResult.Yes; + Close(); + } + + private void btnCancel_Click(object sender, EventArgs e) + { + DialogResult = DialogResult.Cancel; + Close(); + } +} \ No newline at end of file diff --git a/NAPS2.Lib.WinForms/WinForms/FSelectDevice.cs b/NAPS2.Lib.WinForms/WinForms/FSelectDevice.cs index 60dd46b07..a60ca5bf5 100644 --- a/NAPS2.Lib.WinForms/WinForms/FSelectDevice.cs +++ b/NAPS2.Lib.WinForms/WinForms/FSelectDevice.cs @@ -1,64 +1,63 @@ using System.Windows.Forms; using NAPS2.Scan; -namespace NAPS2.WinForms +namespace NAPS2.WinForms; + +public partial class FSelectDevice : FormBase { - public partial class FSelectDevice : FormBase + public FSelectDevice() { - public FSelectDevice() + SaveFormState = false; + RestoreFormState = false; + InitializeComponent(); + AcceptButton = btnSelect; + CancelButton = btnCancel; + } + + public List DeviceList { get; set; } + + public ScanDevice? SelectedDevice { get; private set; } + + protected override void OnLoad(object sender, EventArgs eventArgs) + { + new LayoutManager(this) + .Bind(listboxDevices) + .WidthToForm() + .HeightToForm() + .Bind(btnSelect, btnCancel) + .RightToForm() + .Activate(); + + foreach (var device in DeviceList) { - SaveFormState = false; - RestoreFormState = false; - InitializeComponent(); - AcceptButton = btnSelect; - CancelButton = btnCancel; + listboxDevices.Items.Add(device); } - - public List DeviceList { get; set; } - - public ScanDevice? SelectedDevice { get; private set; } - - protected override void OnLoad(object sender, EventArgs eventArgs) + if (listboxDevices.Items.Count > 0) { - new LayoutManager(this) - .Bind(listboxDevices) - .WidthToForm() - .HeightToForm() - .Bind(btnSelect, btnCancel) - .RightToForm() - .Activate(); - - foreach (var device in DeviceList) - { - listboxDevices.Items.Add(device); - } - if (listboxDevices.Items.Count > 0) - { - listboxDevices.SelectedIndex = 0; - } - } - - private void listboxDevices_Format(object sender, ListControlConvertEventArgs e) - { - e.Value = ((ScanDevice)e.ListItem).Name; - } - - private void btnSelect_Click(object sender, EventArgs e) - { - if (listboxDevices.SelectedItem == null) - { - listboxDevices.Focus(); - return; - } - DialogResult = DialogResult.OK; - SelectedDevice = ((ScanDevice) listboxDevices.SelectedItem); - Close(); - } - - private void btnCancel_Click(object sender, EventArgs e) - { - DialogResult = DialogResult.Cancel; - Close(); + listboxDevices.SelectedIndex = 0; } } -} + + private void listboxDevices_Format(object sender, ListControlConvertEventArgs e) + { + e.Value = ((ScanDevice)e.ListItem).Name; + } + + private void btnSelect_Click(object sender, EventArgs e) + { + if (listboxDevices.SelectedItem == null) + { + listboxDevices.Focus(); + return; + } + DialogResult = DialogResult.OK; + SelectedDevice = ((ScanDevice) listboxDevices.SelectedItem); + Close(); + } + + private void btnCancel_Click(object sender, EventArgs e) + { + DialogResult = DialogResult.Cancel; + Close(); + } +} \ No newline at end of file diff --git a/NAPS2.Lib.WinForms/WinForms/FViewer.cs b/NAPS2.Lib.WinForms/WinForms/FViewer.cs index 7afa9c06a..fadf60eab 100644 --- a/NAPS2.Lib.WinForms/WinForms/FViewer.cs +++ b/NAPS2.Lib.WinForms/WinForms/FViewer.cs @@ -3,640 +3,639 @@ using System.Globalization; using System.Windows.Forms; using NAPS2.Images.Gdi; -namespace NAPS2.WinForms +namespace NAPS2.WinForms; + +public class FViewer : FormBase { - public class FViewer : FormBase + private readonly Container _components = null; + private ToolStripContainer _toolStripContainer1; + private ToolStrip _toolStrip1; + private ToolStripTextBox _tbPageCurrent; + private ToolStripLabel _lblPageTotal; + private ToolStripButton _tsPrev; + private ToolStripButton _tsNext; + private ToolStripSeparator _toolStripSeparator1; + private ToolStripDropDownButton _tsdRotate; + private ToolStripMenuItem _tsRotateLeft; + private ToolStripMenuItem _tsRotateRight; + private ToolStripMenuItem _tsFlip; + private ToolStripMenuItem _tsCustomRotation; + private ToolStripButton _tsCrop; + private ToolStripButton _tsBrightnessContrast; + private ToolStripButton _tsDelete; + private TiffViewerCtl _tiffViewer1; + private ToolStripMenuItem _tsDeskew; + private ToolStripSeparator _toolStripSeparator3; + private ToolStripButton _tsSavePdf; + private ToolStripSeparator _toolStripSeparator2; + private ToolStripButton _tsSaveImage; + private readonly IOperationFactory _operationFactory; + private readonly IWinFormsExportHelper _exportHelper; + private ToolStripButton _tsHueSaturation; + private ToolStripButton _tsBlackWhite; + private ToolStripButton _tsSharpen; + private readonly KeyboardShortcutManager _ksm; + private readonly OperationProgress _operationProgress; + private readonly GdiImageContext _imageContext; + private readonly UiImageList _imageList; + private readonly INotificationManager _notificationManager; + + private UiImage? _currentImage; + + public FViewer(IOperationFactory operationFactory, IWinFormsExportHelper exportHelper, + KeyboardShortcutManager ksm, OperationProgress operationProgress, GdiImageContext imageContext, + UiImageList imageList, INotificationManager notificationManager) { - private readonly Container _components = null; - private ToolStripContainer _toolStripContainer1; - private ToolStrip _toolStrip1; - private ToolStripTextBox _tbPageCurrent; - private ToolStripLabel _lblPageTotal; - private ToolStripButton _tsPrev; - private ToolStripButton _tsNext; - private ToolStripSeparator _toolStripSeparator1; - private ToolStripDropDownButton _tsdRotate; - private ToolStripMenuItem _tsRotateLeft; - private ToolStripMenuItem _tsRotateRight; - private ToolStripMenuItem _tsFlip; - private ToolStripMenuItem _tsCustomRotation; - private ToolStripButton _tsCrop; - private ToolStripButton _tsBrightnessContrast; - private ToolStripButton _tsDelete; - private TiffViewerCtl _tiffViewer1; - private ToolStripMenuItem _tsDeskew; - private ToolStripSeparator _toolStripSeparator3; - private ToolStripButton _tsSavePdf; - private ToolStripSeparator _toolStripSeparator2; - private ToolStripButton _tsSaveImage; - private readonly IOperationFactory _operationFactory; - private readonly IWinFormsExportHelper _exportHelper; - private ToolStripButton _tsHueSaturation; - private ToolStripButton _tsBlackWhite; - private ToolStripButton _tsSharpen; - private readonly KeyboardShortcutManager _ksm; - private readonly OperationProgress _operationProgress; - private readonly GdiImageContext _imageContext; - private readonly UiImageList _imageList; - private readonly INotificationManager _notificationManager; + _operationFactory = operationFactory; + _exportHelper = exportHelper; + _ksm = ksm; + _operationProgress = operationProgress; + _imageContext = imageContext; + _imageList = imageList; + _notificationManager = notificationManager; + InitializeComponent(); + } - private UiImage? _currentImage; - - public FViewer(IOperationFactory operationFactory, IWinFormsExportHelper exportHelper, - KeyboardShortcutManager ksm, OperationProgress operationProgress, GdiImageContext imageContext, - UiImageList imageList, INotificationManager notificationManager) + public UiImage CurrentImage + { + get => _currentImage ?? throw new InvalidOperationException(); + set { - _operationFactory = operationFactory; - _exportHelper = exportHelper; - _ksm = ksm; - _operationProgress = operationProgress; - _imageContext = imageContext; - _imageList = imageList; - _notificationManager = notificationManager; - InitializeComponent(); + if (_currentImage != null) + { + _currentImage.ThumbnailInvalidated -= ImageThumbnailInvalidated; + } + _currentImage = value; + _currentImage.ThumbnailInvalidated += ImageThumbnailInvalidated; + } + } + + private void ImageThumbnailInvalidated(object? sender, EventArgs e) + { + SafeInvokeAsync(() => UpdateImage().AssertNoAwait()); + } + + private int ImageIndex + { + get + { + var index = _imageList.Images.IndexOf(CurrentImage); + if (index == -1) + { + index = 0; + } + return index; + } + } + + protected override async void OnLoad(object sender, EventArgs e) + { + _tbPageCurrent.Visible = PlatformCompat.Runtime.IsToolbarTextboxSupported; + if (Config.Get(c => c.HiddenButtons).HasFlag(ToolbarButtons.SavePdf)) + { + _toolStrip1.Items.Remove(_tsSavePdf); + } + if (Config.Get(c => c.HiddenButtons).HasFlag(ToolbarButtons.SaveImages)) + { + _toolStrip1.Items.Remove(_tsSaveImage); } - public UiImage CurrentImage + AssignKeyboardShortcuts(); + UpdatePage(); + await UpdateImage(); + } + + private async Task GoTo(int index) + { + lock (_imageList) { - get => _currentImage ?? throw new InvalidOperationException(); - set + if (index == ImageIndex || index < 0 || index >= _imageList.Images.Count) { - if (_currentImage != null) - { - _currentImage.ThumbnailInvalidated -= ImageThumbnailInvalidated; - } - _currentImage = value; - _currentImage.ThumbnailInvalidated += ImageThumbnailInvalidated; + return; + } + CurrentImage = _imageList.Images[index]; + _imageList.UpdateSelection(ListSelection.Of(CurrentImage)); + } + UpdatePage(); + await UpdateImage(); + } + + private void UpdatePage() + { + _tbPageCurrent.Text = (ImageIndex + 1).ToString(CultureInfo.CurrentCulture); + _lblPageTotal.Text = string.Format(MiscResources.OfN, _imageList.Images.Count); + if (!PlatformCompat.Runtime.IsToolbarTextboxSupported) + { + _lblPageTotal.Text = _tbPageCurrent.Text + ' ' + _lblPageTotal.Text; + } + } + + private async Task UpdateImage() + { + _tiffViewer1.Image?.Dispose(); + _tiffViewer1.Image = null; + using var imageToRender = CurrentImage.GetClonedImage(); + var rendered = _imageContext.RenderToBitmap(imageToRender); + _tiffViewer1.Image = rendered; + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _components?.Dispose(); + _tiffViewer1?.Image?.Dispose(); + _tiffViewer1?.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = + new System.ComponentModel.ComponentResourceManager(typeof(FViewer)); + this._toolStripContainer1 = new System.Windows.Forms.ToolStripContainer(); + this._tiffViewer1 = new NAPS2.WinForms.TiffViewerCtl(); + this._toolStrip1 = new System.Windows.Forms.ToolStrip(); + this._tbPageCurrent = new System.Windows.Forms.ToolStripTextBox(); + this._lblPageTotal = new System.Windows.Forms.ToolStripLabel(); + this._tsPrev = new System.Windows.Forms.ToolStripButton(); + this._tsNext = new System.Windows.Forms.ToolStripButton(); + this._toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); + this._tsdRotate = new System.Windows.Forms.ToolStripDropDownButton(); + this._tsRotateLeft = new System.Windows.Forms.ToolStripMenuItem(); + this._tsRotateRight = new System.Windows.Forms.ToolStripMenuItem(); + this._tsFlip = new System.Windows.Forms.ToolStripMenuItem(); + this._tsDeskew = new System.Windows.Forms.ToolStripMenuItem(); + this._tsCustomRotation = new System.Windows.Forms.ToolStripMenuItem(); + this._tsCrop = new System.Windows.Forms.ToolStripButton(); + this._tsBrightnessContrast = new System.Windows.Forms.ToolStripButton(); + this._tsHueSaturation = new System.Windows.Forms.ToolStripButton(); + this._tsBlackWhite = new System.Windows.Forms.ToolStripButton(); + this._tsSharpen = new System.Windows.Forms.ToolStripButton(); + this._toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator(); + this._tsSavePdf = new System.Windows.Forms.ToolStripButton(); + this._tsSaveImage = new System.Windows.Forms.ToolStripButton(); + this._toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator(); + this._tsDelete = new System.Windows.Forms.ToolStripButton(); + this._toolStripContainer1.ContentPanel.SuspendLayout(); + this._toolStripContainer1.TopToolStripPanel.SuspendLayout(); + this._toolStripContainer1.SuspendLayout(); + this._toolStrip1.SuspendLayout(); + this.SuspendLayout(); + // + // toolStripContainer1 + // + // + // toolStripContainer1.ContentPanel + // + this._toolStripContainer1.ContentPanel.Controls.Add(this._tiffViewer1); + resources.ApplyResources(this._toolStripContainer1.ContentPanel, "toolStripContainer1.ContentPanel"); + resources.ApplyResources(this._toolStripContainer1, "_toolStripContainer1"); + this._toolStripContainer1.Name = "_toolStripContainer1"; + // + // toolStripContainer1.TopToolStripPanel + // + this._toolStripContainer1.TopToolStripPanel.Controls.Add(this._toolStrip1); + // + // tiffViewer1 + // + resources.ApplyResources(this._tiffViewer1, "_tiffViewer1"); + this._tiffViewer1.Image = null; + this._tiffViewer1.Name = "_tiffViewer1"; + this._tiffViewer1.KeyDown += new System.Windows.Forms.KeyEventHandler(this.tiffViewer1_KeyDown); + // + // toolStrip1 + // + resources.ApplyResources(this._toolStrip1, "_toolStrip1"); + this._toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] + { + this._tbPageCurrent, + this._lblPageTotal, + this._tsPrev, + this._tsNext, + this._toolStripSeparator1, + this._tsdRotate, + this._tsCrop, + this._tsBrightnessContrast, + this._tsHueSaturation, + this._tsBlackWhite, + this._tsSharpen, + this._toolStripSeparator3, + this._tsSavePdf, + this._tsSaveImage, + this._toolStripSeparator2, + this._tsDelete + }); + this._toolStrip1.Name = "_toolStrip1"; + // + // tbPageCurrent + // + this._tbPageCurrent.Name = "_tbPageCurrent"; + resources.ApplyResources(this._tbPageCurrent, "_tbPageCurrent"); + this._tbPageCurrent.KeyDown += new System.Windows.Forms.KeyEventHandler(this.tbPageCurrent_KeyDown); + this._tbPageCurrent.TextChanged += new System.EventHandler(this.tbPageCurrent_TextChanged); + // + // lblPageTotal + // + this._lblPageTotal.Name = "_lblPageTotal"; + resources.ApplyResources(this._lblPageTotal, "_lblPageTotal"); + // + // tsPrev + // + this._tsPrev.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this._tsPrev.Image = global::NAPS2.Icons.arrow_left; + resources.ApplyResources(this._tsPrev, "_tsPrev"); + this._tsPrev.Name = "_tsPrev"; + this._tsPrev.Click += new System.EventHandler(this.tsPrev_Click); + // + // tsNext + // + this._tsNext.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this._tsNext.Image = global::NAPS2.Icons.arrow_right; + resources.ApplyResources(this._tsNext, "_tsNext"); + this._tsNext.Name = "_tsNext"; + this._tsNext.Click += new System.EventHandler(this.tsNext_Click); + // + // toolStripSeparator1 + // + this._toolStripSeparator1.Name = "_toolStripSeparator1"; + resources.ApplyResources(this._toolStripSeparator1, "_toolStripSeparator1"); + // + // tsdRotate + // + this._tsdRotate.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this._tsdRotate.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] + { + this._tsRotateLeft, + this._tsRotateRight, + this._tsFlip, + this._tsDeskew, + this._tsCustomRotation + }); + this._tsdRotate.Image = global::NAPS2.Icons.arrow_rotate_anticlockwise_small; + resources.ApplyResources(this._tsdRotate, "_tsdRotate"); + this._tsdRotate.Name = "_tsdRotate"; + this._tsdRotate.ShowDropDownArrow = false; + // + // tsRotateLeft + // + this._tsRotateLeft.Image = global::NAPS2.Icons.arrow_rotate_anticlockwise_small; + this._tsRotateLeft.Name = "_tsRotateLeft"; + resources.ApplyResources(this._tsRotateLeft, "_tsRotateLeft"); + this._tsRotateLeft.Click += new System.EventHandler(this.tsRotateLeft_Click); + // + // tsRotateRight + // + this._tsRotateRight.Image = global::NAPS2.Icons.arrow_rotate_clockwise_small; + this._tsRotateRight.Name = "_tsRotateRight"; + resources.ApplyResources(this._tsRotateRight, "_tsRotateRight"); + this._tsRotateRight.Click += new System.EventHandler(this.tsRotateRight_Click); + // + // tsFlip + // + this._tsFlip.Image = global::NAPS2.Icons.arrow_switch_small; + this._tsFlip.Name = "_tsFlip"; + resources.ApplyResources(this._tsFlip, "_tsFlip"); + this._tsFlip.Click += new System.EventHandler(this.tsFlip_Click); + // + // tsDeskew + // + this._tsDeskew.Name = "_tsDeskew"; + resources.ApplyResources(this._tsDeskew, "_tsDeskew"); + this._tsDeskew.Click += new System.EventHandler(this.tsDeskew_Click); + // + // tsCustomRotation + // + this._tsCustomRotation.Name = "_tsCustomRotation"; + resources.ApplyResources(this._tsCustomRotation, "_tsCustomRotation"); + this._tsCustomRotation.Click += new System.EventHandler(this.tsCustomRotation_Click); + // + // tsCrop + // + this._tsCrop.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this._tsCrop.Image = global::NAPS2.Icons.transform_crop; + resources.ApplyResources(this._tsCrop, "_tsCrop"); + this._tsCrop.Name = "_tsCrop"; + this._tsCrop.Click += new System.EventHandler(this.tsCrop_Click); + // + // tsBrightnessContrast + // + this._tsBrightnessContrast.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this._tsBrightnessContrast.Image = global::NAPS2.Icons.contrast_with_sun; + resources.ApplyResources(this._tsBrightnessContrast, "_tsBrightnessContrast"); + this._tsBrightnessContrast.Name = "_tsBrightnessContrast"; + this._tsBrightnessContrast.Click += new System.EventHandler(this.tsBrightnessContrast_Click); + // + // tsHueSaturation + // + this._tsHueSaturation.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this._tsHueSaturation.Image = global::NAPS2.Icons.color_management; + resources.ApplyResources(this._tsHueSaturation, "_tsHueSaturation"); + this._tsHueSaturation.Name = "_tsHueSaturation"; + this._tsHueSaturation.Click += new System.EventHandler(this.tsHueSaturation_Click); + // + // tsBlackWhite + // + this._tsBlackWhite.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this._tsBlackWhite.Image = global::NAPS2.Icons.contrast_high; + resources.ApplyResources(this._tsBlackWhite, "_tsBlackWhite"); + this._tsBlackWhite.Name = "_tsBlackWhite"; + this._tsBlackWhite.Click += new System.EventHandler(this.tsBlackWhite_Click); + // + // tsSharpen + // + this._tsSharpen.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this._tsSharpen.Image = global::NAPS2.Icons.sharpen; + resources.ApplyResources(this._tsSharpen, "_tsSharpen"); + this._tsSharpen.Name = "_tsSharpen"; + this._tsSharpen.Click += new System.EventHandler(this.tsSharpen_Click); + // + // toolStripSeparator3 + // + this._toolStripSeparator3.Name = "_toolStripSeparator3"; + resources.ApplyResources(this._toolStripSeparator3, "_toolStripSeparator3"); + // + // tsSavePDF + // + this._tsSavePdf.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this._tsSavePdf.Image = global::NAPS2.Icons.file_extension_pdf_small; + resources.ApplyResources(this._tsSavePdf, "_tsSavePdf"); + this._tsSavePdf.Name = "_tsSavePdf"; + this._tsSavePdf.Click += new System.EventHandler(this.tsSavePDF_Click); + // + // tsSaveImage + // + this._tsSaveImage.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this._tsSaveImage.Image = global::NAPS2.Icons.picture_small; + resources.ApplyResources(this._tsSaveImage, "_tsSaveImage"); + this._tsSaveImage.Name = "_tsSaveImage"; + this._tsSaveImage.Click += new System.EventHandler(this.tsSaveImage_Click); + // + // toolStripSeparator2 + // + this._toolStripSeparator2.Name = "_toolStripSeparator2"; + resources.ApplyResources(this._toolStripSeparator2, "_toolStripSeparator2"); + // + // tsDelete + // + this._tsDelete.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this._tsDelete.Image = global::NAPS2.Icons.cross_small; + resources.ApplyResources(this._tsDelete, "_tsDelete"); + this._tsDelete.Name = "_tsDelete"; + this._tsDelete.Click += new System.EventHandler(this.tsDelete_Click); + // + // FViewer + // + resources.ApplyResources(this, "$this"); + this.Controls.Add(this._toolStripContainer1); + this.Name = "FViewer"; + this.ShowInTaskbar = false; + this._toolStripContainer1.ContentPanel.ResumeLayout(false); + this._toolStripContainer1.TopToolStripPanel.ResumeLayout(false); + this._toolStripContainer1.TopToolStripPanel.PerformLayout(); + this._toolStripContainer1.ResumeLayout(false); + this._toolStripContainer1.PerformLayout(); + this._toolStrip1.ResumeLayout(false); + this._toolStrip1.PerformLayout(); + this.ResumeLayout(false); + } + + #endregion + + private async void tbPageCurrent_TextChanged(object sender, EventArgs e) + { + if (int.TryParse(_tbPageCurrent.Text, out int indexOffBy1)) + { + await GoTo(indexOffBy1 - 1); + } + } + + private async void tsNext_Click(object sender, EventArgs e) + { + await GoTo(ImageIndex + 1); + } + + private async void tsPrev_Click(object sender, EventArgs e) + { + await GoTo(ImageIndex - 1); + } + + private async void tsRotateLeft_Click(object sender, EventArgs e) + { + await _imageList.MutateAsync(new ImageListMutation.RotateFlip(_imageContext, 270), + ListSelection.Of(CurrentImage)); + } + + private async void tsRotateRight_Click(object sender, EventArgs e) + { + await _imageList.MutateAsync(new ImageListMutation.RotateFlip(_imageContext, 90), + ListSelection.Of(CurrentImage)); + } + + private async void tsFlip_Click(object sender, EventArgs e) + { + await _imageList.MutateAsync(new ImageListMutation.RotateFlip(_imageContext, 180), + ListSelection.Of(CurrentImage)); + } + + private async void tsDeskew_Click(object sender, EventArgs e) + { + var op = _operationFactory.Create(); + if (op.Start(new[] { CurrentImage }, new DeskewParams { ThumbnailSize = Config.Get(c => c.ThumbnailSize) })) + { + _operationProgress.ShowProgress(op); + } + } + + private async void tsCustomRotation_Click(object sender, EventArgs e) + { + var form = FormFactory.Create(); + form.Image = CurrentImage; + } + + private async void tsCrop_Click(object sender, EventArgs e) + { + var form = FormFactory.Create(); + form.Image = CurrentImage; + form.ShowDialog(); + } + + private async void tsBrightnessContrast_Click(object sender, EventArgs e) + { + var form = FormFactory.Create(); + form.Image = CurrentImage; + form.ShowDialog(); + } + + private async void tsHueSaturation_Click(object sender, EventArgs e) + { + var form = FormFactory.Create(); + form.Image = CurrentImage; + form.ShowDialog(); + } + + private async void tsBlackWhite_Click(object sender, EventArgs e) + { + var form = FormFactory.Create(); + form.Image = CurrentImage; + form.ShowDialog(); + } + + private async void tsSharpen_Click(object sender, EventArgs e) + { + var form = FormFactory.Create(); + form.Image = CurrentImage; + form.ShowDialog(); + } + + private async void tsDelete_Click(object sender, EventArgs e) + { + if (MessageBox.Show(string.Format(MiscResources.ConfirmDeleteItems, 1), MiscResources.Delete, + MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == DialogResult.OK) + { + await DeleteCurrentImage(); + } + } + + private async Task DeleteCurrentImage() + { + // TODO: Are the file access issues still a thing? + // Need to dispose the bitmap first to avoid file access issues + _tiffViewer1.Image?.Dispose(); + + var lastIndex = ImageIndex; + await _imageList.MutateAsync(new ImageListMutation.DeleteSelected(), + ListSelection.Of(CurrentImage)); + + bool shouldClose = false; + lock (_imageList) + { + if (_imageList.Images.Any()) + { + // Update the GUI for the newly displayed image + var nextIndex = lastIndex >= _imageList.Images.Count ? _imageList.Images.Count - 1 : lastIndex; + CurrentImage = _imageList.Images[nextIndex]; + } + else + { + shouldClose = true; } } - - private void ImageThumbnailInvalidated(object? sender, EventArgs e) + if (shouldClose) { - SafeInvokeAsync(() => UpdateImage().AssertNoAwait()); + // No images left to display, so no point keeping the form open + Close(); } - - private int ImageIndex + else { - get - { - var index = _imageList.Images.IndexOf(CurrentImage); - if (index == -1) - { - index = 0; - } - return index; - } - } - - protected override async void OnLoad(object sender, EventArgs e) - { - _tbPageCurrent.Visible = PlatformCompat.Runtime.IsToolbarTextboxSupported; - if (Config.Get(c => c.HiddenButtons).HasFlag(ToolbarButtons.SavePdf)) - { - _toolStrip1.Items.Remove(_tsSavePdf); - } - if (Config.Get(c => c.HiddenButtons).HasFlag(ToolbarButtons.SaveImages)) - { - _toolStrip1.Items.Remove(_tsSaveImage); - } - - AssignKeyboardShortcuts(); UpdatePage(); await UpdateImage(); } + } - private async Task GoTo(int index) + private async void tsSavePDF_Click(object sender, EventArgs e) + { + using var imageToSave = CurrentImage.GetClonedImage(); + if (await _exportHelper.SavePDF(new List { imageToSave }, _notificationManager)) { - lock (_imageList) - { - if (index == ImageIndex || index < 0 || index >= _imageList.Images.Count) - { - return; - } - CurrentImage = _imageList.Images[index]; - _imageList.UpdateSelection(ListSelection.Of(CurrentImage)); - } - UpdatePage(); - await UpdateImage(); - } - - private void UpdatePage() - { - _tbPageCurrent.Text = (ImageIndex + 1).ToString(CultureInfo.CurrentCulture); - _lblPageTotal.Text = string.Format(MiscResources.OfN, _imageList.Images.Count); - if (!PlatformCompat.Runtime.IsToolbarTextboxSupported) - { - _lblPageTotal.Text = _tbPageCurrent.Text + ' ' + _lblPageTotal.Text; - } - } - - private async Task UpdateImage() - { - _tiffViewer1.Image?.Dispose(); - _tiffViewer1.Image = null; - using var imageToRender = CurrentImage.GetClonedImage(); - var rendered = _imageContext.RenderToBitmap(imageToRender); - _tiffViewer1.Image = rendered; - } - - protected override void Dispose(bool disposing) - { - if (disposing) - { - _components?.Dispose(); - _tiffViewer1?.Image?.Dispose(); - _tiffViewer1?.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - System.ComponentModel.ComponentResourceManager resources = - new System.ComponentModel.ComponentResourceManager(typeof(FViewer)); - this._toolStripContainer1 = new System.Windows.Forms.ToolStripContainer(); - this._tiffViewer1 = new NAPS2.WinForms.TiffViewerCtl(); - this._toolStrip1 = new System.Windows.Forms.ToolStrip(); - this._tbPageCurrent = new System.Windows.Forms.ToolStripTextBox(); - this._lblPageTotal = new System.Windows.Forms.ToolStripLabel(); - this._tsPrev = new System.Windows.Forms.ToolStripButton(); - this._tsNext = new System.Windows.Forms.ToolStripButton(); - this._toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); - this._tsdRotate = new System.Windows.Forms.ToolStripDropDownButton(); - this._tsRotateLeft = new System.Windows.Forms.ToolStripMenuItem(); - this._tsRotateRight = new System.Windows.Forms.ToolStripMenuItem(); - this._tsFlip = new System.Windows.Forms.ToolStripMenuItem(); - this._tsDeskew = new System.Windows.Forms.ToolStripMenuItem(); - this._tsCustomRotation = new System.Windows.Forms.ToolStripMenuItem(); - this._tsCrop = new System.Windows.Forms.ToolStripButton(); - this._tsBrightnessContrast = new System.Windows.Forms.ToolStripButton(); - this._tsHueSaturation = new System.Windows.Forms.ToolStripButton(); - this._tsBlackWhite = new System.Windows.Forms.ToolStripButton(); - this._tsSharpen = new System.Windows.Forms.ToolStripButton(); - this._toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator(); - this._tsSavePdf = new System.Windows.Forms.ToolStripButton(); - this._tsSaveImage = new System.Windows.Forms.ToolStripButton(); - this._toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator(); - this._tsDelete = new System.Windows.Forms.ToolStripButton(); - this._toolStripContainer1.ContentPanel.SuspendLayout(); - this._toolStripContainer1.TopToolStripPanel.SuspendLayout(); - this._toolStripContainer1.SuspendLayout(); - this._toolStrip1.SuspendLayout(); - this.SuspendLayout(); - // - // toolStripContainer1 - // - // - // toolStripContainer1.ContentPanel - // - this._toolStripContainer1.ContentPanel.Controls.Add(this._tiffViewer1); - resources.ApplyResources(this._toolStripContainer1.ContentPanel, "toolStripContainer1.ContentPanel"); - resources.ApplyResources(this._toolStripContainer1, "_toolStripContainer1"); - this._toolStripContainer1.Name = "_toolStripContainer1"; - // - // toolStripContainer1.TopToolStripPanel - // - this._toolStripContainer1.TopToolStripPanel.Controls.Add(this._toolStrip1); - // - // tiffViewer1 - // - resources.ApplyResources(this._tiffViewer1, "_tiffViewer1"); - this._tiffViewer1.Image = null; - this._tiffViewer1.Name = "_tiffViewer1"; - this._tiffViewer1.KeyDown += new System.Windows.Forms.KeyEventHandler(this.tiffViewer1_KeyDown); - // - // toolStrip1 - // - resources.ApplyResources(this._toolStrip1, "_toolStrip1"); - this._toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] - { - this._tbPageCurrent, - this._lblPageTotal, - this._tsPrev, - this._tsNext, - this._toolStripSeparator1, - this._tsdRotate, - this._tsCrop, - this._tsBrightnessContrast, - this._tsHueSaturation, - this._tsBlackWhite, - this._tsSharpen, - this._toolStripSeparator3, - this._tsSavePdf, - this._tsSaveImage, - this._toolStripSeparator2, - this._tsDelete - }); - this._toolStrip1.Name = "_toolStrip1"; - // - // tbPageCurrent - // - this._tbPageCurrent.Name = "_tbPageCurrent"; - resources.ApplyResources(this._tbPageCurrent, "_tbPageCurrent"); - this._tbPageCurrent.KeyDown += new System.Windows.Forms.KeyEventHandler(this.tbPageCurrent_KeyDown); - this._tbPageCurrent.TextChanged += new System.EventHandler(this.tbPageCurrent_TextChanged); - // - // lblPageTotal - // - this._lblPageTotal.Name = "_lblPageTotal"; - resources.ApplyResources(this._lblPageTotal, "_lblPageTotal"); - // - // tsPrev - // - this._tsPrev.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; - this._tsPrev.Image = global::NAPS2.Icons.arrow_left; - resources.ApplyResources(this._tsPrev, "_tsPrev"); - this._tsPrev.Name = "_tsPrev"; - this._tsPrev.Click += new System.EventHandler(this.tsPrev_Click); - // - // tsNext - // - this._tsNext.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; - this._tsNext.Image = global::NAPS2.Icons.arrow_right; - resources.ApplyResources(this._tsNext, "_tsNext"); - this._tsNext.Name = "_tsNext"; - this._tsNext.Click += new System.EventHandler(this.tsNext_Click); - // - // toolStripSeparator1 - // - this._toolStripSeparator1.Name = "_toolStripSeparator1"; - resources.ApplyResources(this._toolStripSeparator1, "_toolStripSeparator1"); - // - // tsdRotate - // - this._tsdRotate.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; - this._tsdRotate.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] - { - this._tsRotateLeft, - this._tsRotateRight, - this._tsFlip, - this._tsDeskew, - this._tsCustomRotation - }); - this._tsdRotate.Image = global::NAPS2.Icons.arrow_rotate_anticlockwise_small; - resources.ApplyResources(this._tsdRotate, "_tsdRotate"); - this._tsdRotate.Name = "_tsdRotate"; - this._tsdRotate.ShowDropDownArrow = false; - // - // tsRotateLeft - // - this._tsRotateLeft.Image = global::NAPS2.Icons.arrow_rotate_anticlockwise_small; - this._tsRotateLeft.Name = "_tsRotateLeft"; - resources.ApplyResources(this._tsRotateLeft, "_tsRotateLeft"); - this._tsRotateLeft.Click += new System.EventHandler(this.tsRotateLeft_Click); - // - // tsRotateRight - // - this._tsRotateRight.Image = global::NAPS2.Icons.arrow_rotate_clockwise_small; - this._tsRotateRight.Name = "_tsRotateRight"; - resources.ApplyResources(this._tsRotateRight, "_tsRotateRight"); - this._tsRotateRight.Click += new System.EventHandler(this.tsRotateRight_Click); - // - // tsFlip - // - this._tsFlip.Image = global::NAPS2.Icons.arrow_switch_small; - this._tsFlip.Name = "_tsFlip"; - resources.ApplyResources(this._tsFlip, "_tsFlip"); - this._tsFlip.Click += new System.EventHandler(this.tsFlip_Click); - // - // tsDeskew - // - this._tsDeskew.Name = "_tsDeskew"; - resources.ApplyResources(this._tsDeskew, "_tsDeskew"); - this._tsDeskew.Click += new System.EventHandler(this.tsDeskew_Click); - // - // tsCustomRotation - // - this._tsCustomRotation.Name = "_tsCustomRotation"; - resources.ApplyResources(this._tsCustomRotation, "_tsCustomRotation"); - this._tsCustomRotation.Click += new System.EventHandler(this.tsCustomRotation_Click); - // - // tsCrop - // - this._tsCrop.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; - this._tsCrop.Image = global::NAPS2.Icons.transform_crop; - resources.ApplyResources(this._tsCrop, "_tsCrop"); - this._tsCrop.Name = "_tsCrop"; - this._tsCrop.Click += new System.EventHandler(this.tsCrop_Click); - // - // tsBrightnessContrast - // - this._tsBrightnessContrast.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; - this._tsBrightnessContrast.Image = global::NAPS2.Icons.contrast_with_sun; - resources.ApplyResources(this._tsBrightnessContrast, "_tsBrightnessContrast"); - this._tsBrightnessContrast.Name = "_tsBrightnessContrast"; - this._tsBrightnessContrast.Click += new System.EventHandler(this.tsBrightnessContrast_Click); - // - // tsHueSaturation - // - this._tsHueSaturation.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; - this._tsHueSaturation.Image = global::NAPS2.Icons.color_management; - resources.ApplyResources(this._tsHueSaturation, "_tsHueSaturation"); - this._tsHueSaturation.Name = "_tsHueSaturation"; - this._tsHueSaturation.Click += new System.EventHandler(this.tsHueSaturation_Click); - // - // tsBlackWhite - // - this._tsBlackWhite.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; - this._tsBlackWhite.Image = global::NAPS2.Icons.contrast_high; - resources.ApplyResources(this._tsBlackWhite, "_tsBlackWhite"); - this._tsBlackWhite.Name = "_tsBlackWhite"; - this._tsBlackWhite.Click += new System.EventHandler(this.tsBlackWhite_Click); - // - // tsSharpen - // - this._tsSharpen.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; - this._tsSharpen.Image = global::NAPS2.Icons.sharpen; - resources.ApplyResources(this._tsSharpen, "_tsSharpen"); - this._tsSharpen.Name = "_tsSharpen"; - this._tsSharpen.Click += new System.EventHandler(this.tsSharpen_Click); - // - // toolStripSeparator3 - // - this._toolStripSeparator3.Name = "_toolStripSeparator3"; - resources.ApplyResources(this._toolStripSeparator3, "_toolStripSeparator3"); - // - // tsSavePDF - // - this._tsSavePdf.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; - this._tsSavePdf.Image = global::NAPS2.Icons.file_extension_pdf_small; - resources.ApplyResources(this._tsSavePdf, "_tsSavePdf"); - this._tsSavePdf.Name = "_tsSavePdf"; - this._tsSavePdf.Click += new System.EventHandler(this.tsSavePDF_Click); - // - // tsSaveImage - // - this._tsSaveImage.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; - this._tsSaveImage.Image = global::NAPS2.Icons.picture_small; - resources.ApplyResources(this._tsSaveImage, "_tsSaveImage"); - this._tsSaveImage.Name = "_tsSaveImage"; - this._tsSaveImage.Click += new System.EventHandler(this.tsSaveImage_Click); - // - // toolStripSeparator2 - // - this._toolStripSeparator2.Name = "_toolStripSeparator2"; - resources.ApplyResources(this._toolStripSeparator2, "_toolStripSeparator2"); - // - // tsDelete - // - this._tsDelete.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; - this._tsDelete.Image = global::NAPS2.Icons.cross_small; - resources.ApplyResources(this._tsDelete, "_tsDelete"); - this._tsDelete.Name = "_tsDelete"; - this._tsDelete.Click += new System.EventHandler(this.tsDelete_Click); - // - // FViewer - // - resources.ApplyResources(this, "$this"); - this.Controls.Add(this._toolStripContainer1); - this.Name = "FViewer"; - this.ShowInTaskbar = false; - this._toolStripContainer1.ContentPanel.ResumeLayout(false); - this._toolStripContainer1.TopToolStripPanel.ResumeLayout(false); - this._toolStripContainer1.TopToolStripPanel.PerformLayout(); - this._toolStripContainer1.ResumeLayout(false); - this._toolStripContainer1.PerformLayout(); - this._toolStrip1.ResumeLayout(false); - this._toolStrip1.PerformLayout(); - this.ResumeLayout(false); - } - - #endregion - - private async void tbPageCurrent_TextChanged(object sender, EventArgs e) - { - if (int.TryParse(_tbPageCurrent.Text, out int indexOffBy1)) - { - await GoTo(indexOffBy1 - 1); - } - } - - private async void tsNext_Click(object sender, EventArgs e) - { - await GoTo(ImageIndex + 1); - } - - private async void tsPrev_Click(object sender, EventArgs e) - { - await GoTo(ImageIndex - 1); - } - - private async void tsRotateLeft_Click(object sender, EventArgs e) - { - await _imageList.MutateAsync(new ImageListMutation.RotateFlip(_imageContext, 270), - ListSelection.Of(CurrentImage)); - } - - private async void tsRotateRight_Click(object sender, EventArgs e) - { - await _imageList.MutateAsync(new ImageListMutation.RotateFlip(_imageContext, 90), - ListSelection.Of(CurrentImage)); - } - - private async void tsFlip_Click(object sender, EventArgs e) - { - await _imageList.MutateAsync(new ImageListMutation.RotateFlip(_imageContext, 180), - ListSelection.Of(CurrentImage)); - } - - private async void tsDeskew_Click(object sender, EventArgs e) - { - var op = _operationFactory.Create(); - if (op.Start(new[] { CurrentImage }, new DeskewParams { ThumbnailSize = Config.Get(c => c.ThumbnailSize) })) - { - _operationProgress.ShowProgress(op); - } - } - - private async void tsCustomRotation_Click(object sender, EventArgs e) - { - var form = FormFactory.Create(); - form.Image = CurrentImage; - } - - private async void tsCrop_Click(object sender, EventArgs e) - { - var form = FormFactory.Create(); - form.Image = CurrentImage; - form.ShowDialog(); - } - - private async void tsBrightnessContrast_Click(object sender, EventArgs e) - { - var form = FormFactory.Create(); - form.Image = CurrentImage; - form.ShowDialog(); - } - - private async void tsHueSaturation_Click(object sender, EventArgs e) - { - var form = FormFactory.Create(); - form.Image = CurrentImage; - form.ShowDialog(); - } - - private async void tsBlackWhite_Click(object sender, EventArgs e) - { - var form = FormFactory.Create(); - form.Image = CurrentImage; - form.ShowDialog(); - } - - private async void tsSharpen_Click(object sender, EventArgs e) - { - var form = FormFactory.Create(); - form.Image = CurrentImage; - form.ShowDialog(); - } - - private async void tsDelete_Click(object sender, EventArgs e) - { - if (MessageBox.Show(string.Format(MiscResources.ConfirmDeleteItems, 1), MiscResources.Delete, - MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == DialogResult.OK) + if (Config.Get(c => c.DeleteAfterSaving)) { await DeleteCurrentImage(); } } + } - private async Task DeleteCurrentImage() + private async void tsSaveImage_Click(object sender, EventArgs e) + { + using var imageToSave = CurrentImage.GetClonedImage(); + if (await _exportHelper.SaveImages(new List { imageToSave }, _notificationManager)) { - // TODO: Are the file access issues still a thing? - // Need to dispose the bitmap first to avoid file access issues - _tiffViewer1.Image?.Dispose(); - - var lastIndex = ImageIndex; - await _imageList.MutateAsync(new ImageListMutation.DeleteSelected(), - ListSelection.Of(CurrentImage)); - - bool shouldClose = false; - lock (_imageList) + if (Config.Get(c => c.DeleteAfterSaving)) { - if (_imageList.Images.Any()) - { - // Update the GUI for the newly displayed image - var nextIndex = lastIndex >= _imageList.Images.Count ? _imageList.Images.Count - 1 : lastIndex; - CurrentImage = _imageList.Images[nextIndex]; - } - else - { - shouldClose = true; - } + await DeleteCurrentImage(); } - if (shouldClose) - { - // No images left to display, so no point keeping the form open - Close(); - } - else - { - UpdatePage(); - await UpdateImage(); - } - } - - private async void tsSavePDF_Click(object sender, EventArgs e) - { - using var imageToSave = CurrentImage.GetClonedImage(); - if (await _exportHelper.SavePDF(new List { imageToSave }, _notificationManager)) - { - if (Config.Get(c => c.DeleteAfterSaving)) - { - await DeleteCurrentImage(); - } - } - } - - private async void tsSaveImage_Click(object sender, EventArgs e) - { - using var imageToSave = CurrentImage.GetClonedImage(); - if (await _exportHelper.SaveImages(new List { imageToSave }, _notificationManager)) - { - if (Config.Get(c => c.DeleteAfterSaving)) - { - await DeleteCurrentImage(); - } - } - } - - private async void tiffViewer1_KeyDown(object sender, KeyEventArgs e) - { - if (!(e.Control || e.Shift || e.Alt)) - { - switch (e.KeyCode) - { - case Keys.Escape: - Close(); - return; - case Keys.PageDown: - case Keys.Right: - case Keys.Down: - await GoTo(ImageIndex + 1); - return; - case Keys.PageUp: - case Keys.Left: - case Keys.Up: - await GoTo(ImageIndex - 1); - return; - } - } - - e.Handled = _ksm.Perform(e.KeyData); - } - - private async void tbPageCurrent_KeyDown(object sender, KeyEventArgs e) - { - if (!(e.Control || e.Shift || e.Alt)) - { - switch (e.KeyCode) - { - case Keys.PageDown: - case Keys.Right: - case Keys.Down: - await GoTo(ImageIndex + 1); - return; - case Keys.PageUp: - case Keys.Left: - case Keys.Up: - await GoTo(ImageIndex - 1); - return; - } - } - - e.Handled = _ksm.Perform(e.KeyData); - } - - private void AssignKeyboardShortcuts() - { - // Defaults - - _ksm.Assign("Del", _tsDelete); - - // Configured - - // TODO: Granular - var ks = Config.Get(c => c.KeyboardShortcuts); - - _ksm.Assign(ks.Delete, _tsDelete); - _ksm.Assign(ks.ImageBlackWhite, _tsBlackWhite); - _ksm.Assign(ks.ImageBrightness, _tsBrightnessContrast); - _ksm.Assign(ks.ImageContrast, _tsBrightnessContrast); - _ksm.Assign(ks.ImageCrop, _tsCrop); - _ksm.Assign(ks.ImageHue, _tsHueSaturation); - _ksm.Assign(ks.ImageSaturation, _tsHueSaturation); - _ksm.Assign(ks.ImageSharpen, _tsSharpen); - - _ksm.Assign(ks.RotateCustom, _tsCustomRotation); - _ksm.Assign(ks.RotateFlip, _tsFlip); - _ksm.Assign(ks.RotateLeft, _tsRotateLeft); - _ksm.Assign(ks.RotateRight, _tsRotateRight); - _ksm.Assign(ks.SaveImages, _tsSaveImage); - _ksm.Assign(ks.SavePDF, _tsSavePdf); } } + + private async void tiffViewer1_KeyDown(object sender, KeyEventArgs e) + { + if (!(e.Control || e.Shift || e.Alt)) + { + switch (e.KeyCode) + { + case Keys.Escape: + Close(); + return; + case Keys.PageDown: + case Keys.Right: + case Keys.Down: + await GoTo(ImageIndex + 1); + return; + case Keys.PageUp: + case Keys.Left: + case Keys.Up: + await GoTo(ImageIndex - 1); + return; + } + } + + e.Handled = _ksm.Perform(e.KeyData); + } + + private async void tbPageCurrent_KeyDown(object sender, KeyEventArgs e) + { + if (!(e.Control || e.Shift || e.Alt)) + { + switch (e.KeyCode) + { + case Keys.PageDown: + case Keys.Right: + case Keys.Down: + await GoTo(ImageIndex + 1); + return; + case Keys.PageUp: + case Keys.Left: + case Keys.Up: + await GoTo(ImageIndex - 1); + return; + } + } + + e.Handled = _ksm.Perform(e.KeyData); + } + + private void AssignKeyboardShortcuts() + { + // Defaults + + _ksm.Assign("Del", _tsDelete); + + // Configured + + // TODO: Granular + var ks = Config.Get(c => c.KeyboardShortcuts); + + _ksm.Assign(ks.Delete, _tsDelete); + _ksm.Assign(ks.ImageBlackWhite, _tsBlackWhite); + _ksm.Assign(ks.ImageBrightness, _tsBrightnessContrast); + _ksm.Assign(ks.ImageContrast, _tsBrightnessContrast); + _ksm.Assign(ks.ImageCrop, _tsCrop); + _ksm.Assign(ks.ImageHue, _tsHueSaturation); + _ksm.Assign(ks.ImageSaturation, _tsHueSaturation); + _ksm.Assign(ks.ImageSharpen, _tsSharpen); + + _ksm.Assign(ks.RotateCustom, _tsCustomRotation); + _ksm.Assign(ks.RotateFlip, _tsFlip); + _ksm.Assign(ks.RotateLeft, _tsRotateLeft); + _ksm.Assign(ks.RotateRight, _tsRotateRight); + _ksm.Assign(ks.SaveImages, _tsSaveImage); + _ksm.Assign(ks.SavePDF, _tsSavePdf); + } } \ No newline at end of file diff --git a/NAPS2.Lib.WinForms/WinForms/ILProfileIcons.cs b/NAPS2.Lib.WinForms/WinForms/ILProfileIcons.cs index e5d0fc969..b23897050 100644 --- a/NAPS2.Lib.WinForms/WinForms/ILProfileIcons.cs +++ b/NAPS2.Lib.WinForms/WinForms/ILProfileIcons.cs @@ -1,19 +1,18 @@ using System.ComponentModel; -namespace NAPS2.WinForms +namespace NAPS2.WinForms; + +public partial class ILProfileIcons : Component { - public partial class ILProfileIcons : Component + public ILProfileIcons() { - public ILProfileIcons() - { - InitializeComponent(); - } - - public ILProfileIcons(IContainer container) - { - container.Add(this); - - InitializeComponent(); - } + InitializeComponent(); } -} + + public ILProfileIcons(IContainer container) + { + container.Add(this); + + InitializeComponent(); + } +} \ No newline at end of file diff --git a/NAPS2.Lib.WinForms/WinForms/ImageForm.cs b/NAPS2.Lib.WinForms/WinForms/ImageForm.cs index c00acdded..8b0f284fc 100644 --- a/NAPS2.Lib.WinForms/WinForms/ImageForm.cs +++ b/NAPS2.Lib.WinForms/WinForms/ImageForm.cs @@ -3,180 +3,179 @@ using System.Windows.Forms; using NAPS2.Images.Gdi; using Timer = System.Threading.Timer; -namespace NAPS2.WinForms +namespace NAPS2.WinForms; + +public partial class ImageForm : FormBase { - public partial class ImageForm : FormBase + private readonly ImageContext _imageContext; + + protected Bitmap workingImage, workingImage2; + private bool _initComplete; + private bool _previewOutOfDate; + private bool _working; + private Timer _previewTimer; + private bool _closed; + + private ImageForm() { - private readonly ImageContext _imageContext; + // For the designer only + InitializeComponent(); + } - protected Bitmap workingImage, workingImage2; - private bool _initComplete; - private bool _previewOutOfDate; - private bool _working; - private Timer _previewTimer; - private bool _closed; + protected ImageForm(ImageContext imageContext) + { + _imageContext = imageContext; + InitializeComponent(); + } - private ImageForm() + public UiImage Image { get; set; } + + public List SelectedImages { get; set; } + + protected virtual IEnumerable Transforms => throw new NotImplementedException(); + + protected virtual PictureBox PictureBox => throw new NotImplementedException(); + + private bool TransformMultiple => SelectedImages != null && checkboxApplyToSelected.Checked; + + private IEnumerable ImagesToTransform => TransformMultiple ? SelectedImages : Enumerable.Repeat(Image, 1); + + protected virtual Bitmap RenderPreview() + { + var result = (Bitmap)workingImage.Clone(); + foreach (var transform in Transforms) { - // For the designer only - InitializeComponent(); - } - - protected ImageForm(ImageContext imageContext) - { - _imageContext = imageContext; - InitializeComponent(); - } - - public UiImage Image { get; set; } - - public List SelectedImages { get; set; } - - protected virtual IEnumerable Transforms => throw new NotImplementedException(); - - protected virtual PictureBox PictureBox => throw new NotImplementedException(); - - private bool TransformMultiple => SelectedImages != null && checkboxApplyToSelected.Checked; - - private IEnumerable ImagesToTransform => TransformMultiple ? SelectedImages : Enumerable.Repeat(Image, 1); - - protected virtual Bitmap RenderPreview() - { - var result = (Bitmap)workingImage.Clone(); - foreach (var transform in Transforms) + if (!transform.IsNull) { - if (!transform.IsNull) - { - // TODO: Maybe the working images etc. should be storage - result = ((GdiImage)_imageContext.PerformTransform(new GdiImage(result), transform)).Bitmap; - } - } - return result; - } - - protected virtual void InitTransform() - { - } - - protected virtual void ResetTransform() - { - } - - protected virtual void TransformSaved() - { - } - - private async void ImageForm_Load(object sender, EventArgs e) - { - checkboxApplyToSelected.BringToFront(); - btnRevert.BringToFront(); - btnCancel.BringToFront(); - btnOK.BringToFront(); - if (SelectedImages != null && SelectedImages.Count > 1) - { - checkboxApplyToSelected.Text = string.Format(checkboxApplyToSelected.Text, SelectedImages.Count); - } - else - { - ConditionalControls.Hide(checkboxApplyToSelected, 6); - } - - Size = new Size(600, 600); - - var maxDimen = Screen.AllScreens.Max(s => Math.Max(s.WorkingArea.Height, s.WorkingArea.Width)); - // TODO: Limit to maxDimen * 2 - using var imageToRender = Image.GetClonedImage(); - // TODO: More generic or avoid the cast somehow? In general how do we integrate with eto? - workingImage = ((GdiImageContext)_imageContext).RenderToBitmap(imageToRender); - if (_closed) - { - workingImage?.Dispose(); - return; - } - workingImage2 = (Bitmap)workingImage.Clone(); - - InitTransform(); - lock (this) - { - _initComplete = true; - } - - UpdatePreviewBox(); - } - - protected void UpdatePreviewBox() - { - if (_previewTimer == null) - { - _previewTimer = new Timer(_ => - { - lock (this) - { - if (!_initComplete || !IsHandleCreated || !_previewOutOfDate || _working) return; - _working = true; - _previewOutOfDate = false; - } - var bitmap = RenderPreview(); - SafeInvoke(() => - { - PictureBox.Image?.Dispose(); - PictureBox.Image = bitmap; - }); - lock (this) - { - _working = false; - } - }, null, 0, 100); - } - lock (this) - { - _previewOutOfDate = true; + // TODO: Maybe the working images etc. should be storage + result = ((GdiImage)_imageContext.PerformTransform(new GdiImage(result), transform)).Bitmap; } } + return result; + } - private void btnCancel_Click(object sender, EventArgs e) + protected virtual void InitTransform() + { + } + + protected virtual void ResetTransform() + { + } + + protected virtual void TransformSaved() + { + } + + private async void ImageForm_Load(object sender, EventArgs e) + { + checkboxApplyToSelected.BringToFront(); + btnRevert.BringToFront(); + btnCancel.BringToFront(); + btnOK.BringToFront(); + if (SelectedImages != null && SelectedImages.Count > 1) { - Close(); + checkboxApplyToSelected.Text = string.Format(checkboxApplyToSelected.Text, SelectedImages.Count); + } + else + { + ConditionalControls.Hide(checkboxApplyToSelected, 6); } - private void btnOK_Click(object sender, EventArgs e) - { - if (Transforms.Any(x => !x.IsNull)) - { - foreach (var img in ImagesToTransform) - { - lock (img) - { - foreach (var t in Transforms) - { - img.AddTransform(t); - } - // Optimize thumbnail rendering for the first (or only) image since we already have it loaded into memory - if (img == Image) - { - var transformed = _imageContext.PerformAllTransforms(new GdiImage(workingImage).Clone(), Transforms); - img.SetThumbnail(_imageContext.PerformTransform(transformed, new ThumbnailTransform(Config.Get(c => c.ThumbnailSize)))); - } - } - } - } - TransformSaved(); - Close(); - } + Size = new Size(600, 600); - private void btnRevert_Click(object sender, EventArgs e) - { - ResetTransform(); - UpdatePreviewBox(); - } - - private void ImageForm_FormClosed(object sender, FormClosedEventArgs e) + var maxDimen = Screen.AllScreens.Max(s => Math.Max(s.WorkingArea.Height, s.WorkingArea.Width)); + // TODO: Limit to maxDimen * 2 + using var imageToRender = Image.GetClonedImage(); + // TODO: More generic or avoid the cast somehow? In general how do we integrate with eto? + workingImage = ((GdiImageContext)_imageContext).RenderToBitmap(imageToRender); + if (_closed) { workingImage?.Dispose(); - workingImage2?.Dispose(); - PictureBox.Image?.Dispose(); - _previewTimer?.Dispose(); - _closed = true; + return; + } + workingImage2 = (Bitmap)workingImage.Clone(); + + InitTransform(); + lock (this) + { + _initComplete = true; + } + + UpdatePreviewBox(); + } + + protected void UpdatePreviewBox() + { + if (_previewTimer == null) + { + _previewTimer = new Timer(_ => + { + lock (this) + { + if (!_initComplete || !IsHandleCreated || !_previewOutOfDate || _working) return; + _working = true; + _previewOutOfDate = false; + } + var bitmap = RenderPreview(); + SafeInvoke(() => + { + PictureBox.Image?.Dispose(); + PictureBox.Image = bitmap; + }); + lock (this) + { + _working = false; + } + }, null, 0, 100); + } + lock (this) + { + _previewOutOfDate = true; } } -} + + private void btnCancel_Click(object sender, EventArgs e) + { + Close(); + } + + private void btnOK_Click(object sender, EventArgs e) + { + if (Transforms.Any(x => !x.IsNull)) + { + foreach (var img in ImagesToTransform) + { + lock (img) + { + foreach (var t in Transforms) + { + img.AddTransform(t); + } + // Optimize thumbnail rendering for the first (or only) image since we already have it loaded into memory + if (img == Image) + { + var transformed = _imageContext.PerformAllTransforms(new GdiImage(workingImage).Clone(), Transforms); + img.SetThumbnail(_imageContext.PerformTransform(transformed, new ThumbnailTransform(Config.Get(c => c.ThumbnailSize)))); + } + } + } + } + TransformSaved(); + Close(); + } + + private void btnRevert_Click(object sender, EventArgs e) + { + ResetTransform(); + UpdatePreviewBox(); + } + + private void ImageForm_FormClosed(object sender, FormClosedEventArgs e) + { + workingImage?.Dispose(); + workingImage2?.Dispose(); + PictureBox.Image?.Dispose(); + _previewTimer?.Dispose(); + _closed = true; + } +} \ No newline at end of file diff --git a/NAPS2.Lib.WinForms/WinForms/OperationProgressNotifyWidget.cs b/NAPS2.Lib.WinForms/WinForms/OperationProgressNotifyWidget.cs index 2f798b657..6c853b03e 100644 --- a/NAPS2.Lib.WinForms/WinForms/OperationProgressNotifyWidget.cs +++ b/NAPS2.Lib.WinForms/WinForms/OperationProgressNotifyWidget.cs @@ -1,74 +1,73 @@ using System.Windows.Forms; -namespace NAPS2.WinForms +namespace NAPS2.WinForms; + +public partial class OperationProgressNotifyWidget : NotifyWidgetBase { - public partial class OperationProgressNotifyWidget : NotifyWidgetBase + private readonly OperationProgress _operationProgress; + private readonly IOperation _op; + + public OperationProgressNotifyWidget(OperationProgress operationProgress, IOperation op) { - private readonly OperationProgress _operationProgress; - private readonly IOperation _op; + InitializeComponent(); - public OperationProgressNotifyWidget(OperationProgress operationProgress, IOperation op) + _operationProgress = operationProgress; + _op = op; + + cancelToolStripMenuItem.Visible = op.AllowCancel; + op.StatusChanged += Op_StatusChanged; + op.Finished += Op_Finished; + } + + public override void ShowNotify() => DisplayProgress(); + + public override NotifyWidgetBase Clone() => new OperationProgressNotifyWidget(_operationProgress, _op); + + private void DisplayProgress() + { + var lblNumberRight = lblNumber.Right; + WinFormsOperationProgress.RenderStatus(_op, lblTitle, lblNumber, progressBar); + if (_op.Status?.IndeterminateProgress != true) { - InitializeComponent(); - - _operationProgress = operationProgress; - _op = op; - - cancelToolStripMenuItem.Visible = op.AllowCancel; - op.StatusChanged += Op_StatusChanged; - op.Finished += Op_Finished; + // Don't display the number if the progress bar is precise + // Otherwise, the widget will be too cluttered + // The number is only shown for OcrOperation at the moment + lblNumber.Text = ""; } + lblNumber.Left = lblNumberRight - lblNumber.Width; + Width = Math.Max(Width, lblTitle.Width + lblNumber.Width + 22); + Height = Math.Max(Height, lblTitle.Height + 35); + } - public override void ShowNotify() => DisplayProgress(); + private void DoHideNotify() + { + _op.StatusChanged -= Op_StatusChanged; + _op.Finished -= Op_Finished; + InvokeHideNotify(); + } - public override NotifyWidgetBase Clone() => new OperationProgressNotifyWidget(_operationProgress, _op); + private void Op_StatusChanged(object sender, EventArgs e) + { + SafeInvoke(DisplayProgress); + } - private void DisplayProgress() - { - var lblNumberRight = lblNumber.Right; - WinFormsOperationProgress.RenderStatus(_op, lblTitle, lblNumber, progressBar); - if (_op.Status?.IndeterminateProgress != true) - { - // Don't display the number if the progress bar is precise - // Otherwise, the widget will be too cluttered - // The number is only shown for OcrOperation at the moment - lblNumber.Text = ""; - } - lblNumber.Left = lblNumberRight - lblNumber.Width; - Width = Math.Max(Width, lblTitle.Width + lblNumber.Width + 22); - Height = Math.Max(Height, lblTitle.Height + 35); - } + private void Op_Finished(object sender, EventArgs e) + { + DoHideNotify(); + } - private void DoHideNotify() - { - _op.StatusChanged -= Op_StatusChanged; - _op.Finished -= Op_Finished; - InvokeHideNotify(); - } + private void cancelToolStripMenuItem_Click(object sender, EventArgs e) + { + _op.Cancel(); + cancelToolStripMenuItem.Enabled = false; + } - private void Op_StatusChanged(object sender, EventArgs e) - { - SafeInvoke(DisplayProgress); - } - - private void Op_Finished(object sender, EventArgs e) + private void OperationProgressNotifyWidget_Click(object sender, MouseEventArgs e) + { + if (e.Button == MouseButtons.Left) { DoHideNotify(); - } - - private void cancelToolStripMenuItem_Click(object sender, EventArgs e) - { - _op.Cancel(); - cancelToolStripMenuItem.Enabled = false; - } - - private void OperationProgressNotifyWidget_Click(object sender, MouseEventArgs e) - { - if (e.Button == MouseButtons.Left) - { - DoHideNotify(); - _operationProgress.ShowModalProgress(_op); - } + _operationProgress.ShowModalProgress(_op); } } -} +} \ No newline at end of file diff --git a/NAPS2.Lib.WinForms/WinForms/TiffViewer.cs b/NAPS2.Lib.WinForms/WinForms/TiffViewer.cs index afe073118..3cc88ee17 100644 --- a/NAPS2.Lib.WinForms/WinForms/TiffViewer.cs +++ b/NAPS2.Lib.WinForms/WinForms/TiffViewer.cs @@ -2,173 +2,172 @@ using System.ComponentModel; using System.Drawing; using System.Windows.Forms; -namespace NAPS2.WinForms +namespace NAPS2.WinForms; + +public class TiffViewer : UserControl { - public class TiffViewer : UserControl + private readonly Container _components = null; + + private Image _image; + private PictureBox _pbox; + private double _xzoom; + + public TiffViewer() { - private readonly Container _components = null; + InitializeComponent(); + } - private Image _image; - private PictureBox _pbox; - private double _xzoom; - - public TiffViewer() + public Image Image + { + set { - InitializeComponent(); - } - - public Image Image - { - set + if (value != null) { - if (value != null) - { - _image = value; - Zoom = 100; - } - else - { - ClearImage(); - _image = null; - } - } - } - - public int ImageWidth => _image?.Width ?? 0; - - public int ImageHeight => _image?.Height ?? 0; - - public double Zoom - { - set - { - if (_image != null) - { - double maxZoom = Math.Sqrt(1e8 / (_image.Width * (double) _image.Height)) * 100; - _xzoom = Math.Max(Math.Min(value, maxZoom), 10); - double displayWidth = _image.Width * (_xzoom / 100); - double displayHeight = _image.Height * (_xzoom / 100); - if (_image.HorizontalResolution > 0 && _image.VerticalResolution > 0) - { - displayHeight *= _image.HorizontalResolution / (double)_image.VerticalResolution; - } - _pbox.Image = _image; - _pbox.BorderStyle = BorderStyle.FixedSingle; - _pbox.Width = (int)displayWidth; - _pbox.Height = (int)displayHeight; - if (ZoomChanged != null) - { - _pbox.Cursor = HorizontalScroll.Visible || VerticalScroll.Visible ? Cursors.Hand : Cursors.Default; - ZoomChanged.Invoke(this, new EventArgs()); - } - } - } - get => _xzoom; - } - - public event EventHandler ZoomChanged; - - private void ClearImage() - { - _pbox.Image = Icons.hourglass_grey; - _pbox.BorderStyle = BorderStyle.None; - _pbox.Width = 32; - _pbox.Height = 32; - } - - protected override void Dispose(bool disposing) - { - if (disposing) - { - _components?.Dispose(); - } - base.Dispose(disposing); - } - - protected override void OnMouseWheel(MouseEventArgs e) - { - if (ModifierKeys.HasFlag(Keys.Control)) - { - StepZoom(e.Delta / (double)SystemInformation.MouseWheelScrollDelta); + _image = value; + Zoom = 100; } else { - base.OnMouseWheel(e); + ClearImage(); + _image = null; } } - - protected override void OnKeyDown(KeyEventArgs e) - { - base.OnKeyDown(e); - switch (e.KeyCode) - { - case Keys.OemMinus: - if (e.Control) - { - StepZoom(-1); - } - break; - case Keys.Oemplus: - if (e.Control) - { - StepZoom(1); - } - break; - } - } - - public void StepZoom(double steps) - { - Zoom = Math.Round(Zoom * Math.Pow(1.2, steps)); - } - - private Point _mousePos; - - private void pbox_MouseDown(object sender, MouseEventArgs e) - { - _mousePos = e.Location; - } - - private void pbox_MouseMove(object sender, MouseEventArgs e) - { - if (e.Button == MouseButtons.Left) - { - AutoScrollPosition = new Point(-AutoScrollPosition.X + _mousePos.X - e.X, -AutoScrollPosition.Y + _mousePos.Y - e.Y); - } - } - - #region Component Designer generated code - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(TiffViewer)); - this._pbox = new System.Windows.Forms.PictureBox(); - ((System.ComponentModel.ISupportInitialize)(this._pbox)).BeginInit(); - this.SuspendLayout(); - // - // pbox - // - resources.ApplyResources(this._pbox, "_pbox"); - this._pbox.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; - this._pbox.Name = "_pbox"; - this._pbox.TabStop = false; - this._pbox.SizeMode = PictureBoxSizeMode.Zoom; - this._pbox.MouseDown += new System.Windows.Forms.MouseEventHandler(this.pbox_MouseDown); - this._pbox.MouseMove += new System.Windows.Forms.MouseEventHandler(this.pbox_MouseMove); - // - // TiffViewer - // - resources.ApplyResources(this, "$this"); - this.BackColor = System.Drawing.Color.LightGray; - this.Controls.Add(this._pbox); - this.Name = "TiffViewer"; - ((System.ComponentModel.ISupportInitialize)(this._pbox)).EndInit(); - this.ResumeLayout(false); - - } - #endregion } -} + + public int ImageWidth => _image?.Width ?? 0; + + public int ImageHeight => _image?.Height ?? 0; + + public double Zoom + { + set + { + if (_image != null) + { + double maxZoom = Math.Sqrt(1e8 / (_image.Width * (double) _image.Height)) * 100; + _xzoom = Math.Max(Math.Min(value, maxZoom), 10); + double displayWidth = _image.Width * (_xzoom / 100); + double displayHeight = _image.Height * (_xzoom / 100); + if (_image.HorizontalResolution > 0 && _image.VerticalResolution > 0) + { + displayHeight *= _image.HorizontalResolution / (double)_image.VerticalResolution; + } + _pbox.Image = _image; + _pbox.BorderStyle = BorderStyle.FixedSingle; + _pbox.Width = (int)displayWidth; + _pbox.Height = (int)displayHeight; + if (ZoomChanged != null) + { + _pbox.Cursor = HorizontalScroll.Visible || VerticalScroll.Visible ? Cursors.Hand : Cursors.Default; + ZoomChanged.Invoke(this, new EventArgs()); + } + } + } + get => _xzoom; + } + + public event EventHandler ZoomChanged; + + private void ClearImage() + { + _pbox.Image = Icons.hourglass_grey; + _pbox.BorderStyle = BorderStyle.None; + _pbox.Width = 32; + _pbox.Height = 32; + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _components?.Dispose(); + } + base.Dispose(disposing); + } + + protected override void OnMouseWheel(MouseEventArgs e) + { + if (ModifierKeys.HasFlag(Keys.Control)) + { + StepZoom(e.Delta / (double)SystemInformation.MouseWheelScrollDelta); + } + else + { + base.OnMouseWheel(e); + } + } + + protected override void OnKeyDown(KeyEventArgs e) + { + base.OnKeyDown(e); + switch (e.KeyCode) + { + case Keys.OemMinus: + if (e.Control) + { + StepZoom(-1); + } + break; + case Keys.Oemplus: + if (e.Control) + { + StepZoom(1); + } + break; + } + } + + public void StepZoom(double steps) + { + Zoom = Math.Round(Zoom * Math.Pow(1.2, steps)); + } + + private Point _mousePos; + + private void pbox_MouseDown(object sender, MouseEventArgs e) + { + _mousePos = e.Location; + } + + private void pbox_MouseMove(object sender, MouseEventArgs e) + { + if (e.Button == MouseButtons.Left) + { + AutoScrollPosition = new Point(-AutoScrollPosition.X + _mousePos.X - e.X, -AutoScrollPosition.Y + _mousePos.Y - e.Y); + } + } + + #region Component Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(TiffViewer)); + this._pbox = new System.Windows.Forms.PictureBox(); + ((System.ComponentModel.ISupportInitialize)(this._pbox)).BeginInit(); + this.SuspendLayout(); + // + // pbox + // + resources.ApplyResources(this._pbox, "_pbox"); + this._pbox.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this._pbox.Name = "_pbox"; + this._pbox.TabStop = false; + this._pbox.SizeMode = PictureBoxSizeMode.Zoom; + this._pbox.MouseDown += new System.Windows.Forms.MouseEventHandler(this.pbox_MouseDown); + this._pbox.MouseMove += new System.Windows.Forms.MouseEventHandler(this.pbox_MouseMove); + // + // TiffViewer + // + resources.ApplyResources(this, "$this"); + this.BackColor = System.Drawing.Color.LightGray; + this.Controls.Add(this._pbox); + this.Name = "TiffViewer"; + ((System.ComponentModel.ISupportInitialize)(this._pbox)).EndInit(); + this.ResumeLayout(false); + + } + #endregion +} \ No newline at end of file