Refactor transfer helpers

This commit is contained in:
Ben Olden-Cooligan 2021-04-16 16:44:01 -07:00
parent d7f03c824f
commit 1c542fa4ee
12 changed files with 125 additions and 124 deletions

View File

@ -10,7 +10,7 @@ namespace NAPS2.Config
ImmutableList<ScanProfile> Profiles { get; }
void Mutate(ListMutation<ScanProfile> mutation, ISelectable<ScanProfile> selectable);
void Mutate(ListMutation<ScanProfile> mutation, ListSelection<ScanProfile> selection);
ScanProfile DefaultProfile { get; set; }
ScanProfile? DefaultProfile { get; set; }
void Load();
void Save();

View File

@ -9,6 +9,7 @@ using NAPS2.Serialization;
namespace NAPS2.Config
{
// TODO: Fix cross-instance contention
public class ProfileManager : IProfileManager
{
private readonly ISerializer<ProfileConfig> _serializer = new ProfileSerializer();

View File

@ -13,8 +13,11 @@ namespace NAPS2.EtoForms
{
public class ProfileListViewBehavior : ListViewBehavior<ScanProfile>
{
public ProfileListViewBehavior()
private ProfileTransfer _profileTransfer;
public ProfileListViewBehavior(ProfileTransfer profileTransfer)
{
_profileTransfer = profileTransfer;
MultiSelect = false;
ShowLabels = true;
}
@ -42,7 +45,7 @@ namespace NAPS2.EtoForms
{
if (selection.Count > 0)
{
TransferHelper.SaveProfileToDataObject(selection.First(), dataObject);
_profileTransfer.AddTo(dataObject, selection.Single());
}
}
@ -51,9 +54,9 @@ namespace NAPS2.EtoForms
// Determine if drop data is compatible
try
{
if (TransferHelper.HasProfile(dataObject))
if (_profileTransfer.IsIn(dataObject))
{
var data = TransferHelper.GetProfileFromDataObject(dataObject);
var data = _profileTransfer.GetFrom(dataObject);
return data.ProcessId == Process.GetCurrentProcess().Id
? data.Locked
? DragEffects.None

View File

@ -21,6 +21,7 @@ namespace NAPS2.EtoForms.Ui
private readonly IScanPerformer _scanPerformer;
private readonly ProfileNameTracker _profileNameTracker;
private readonly IProfileManager _profileManager;
private readonly ProfileTransfer _profileTransfer;
private readonly IListView<ScanProfile> _listView;
@ -32,12 +33,13 @@ namespace NAPS2.EtoForms.Ui
private readonly Command _copyCommand;
private readonly Command _pasteCommand;
public ProfilesForm(ConfigProvider<CommonConfig> configProvider, IScanPerformer scanPerformer, ProfileNameTracker profileNameTracker, IProfileManager profileManager, IEtoPlatform etoPlatform, ProfileListViewBehavior profileListViewBehavior)
public ProfilesForm(ConfigProvider<CommonConfig> configProvider, IScanPerformer scanPerformer, ProfileNameTracker profileNameTracker, IProfileManager profileManager, IEtoPlatform etoPlatform, ProfileListViewBehavior profileListViewBehavior, ProfileTransfer profileTransfer)
{
_configProvider = configProvider;
_scanPerformer = scanPerformer;
_profileNameTracker = profileNameTracker;
_profileManager = profileManager;
_profileTransfer = profileTransfer;
Title = UiStrings.ProfilesFormTitle;
Icon = Icons.blueprints_small.ToEtoIcon();
@ -146,19 +148,9 @@ namespace NAPS2.EtoForms.Ui
).Aligned());
}
public Action<ScannedImage> ImageCallback { get; set; }
public Action<ScannedImage>? ImageCallback { get; set; }
private ScanProfile? SelectedProfile
{
get
{
if (_listView.Selection.Count == 1)
{
return _listView.Selection.Single();
}
return null;
}
}
private ScanProfile? SelectedProfile => _listView.Selection.SingleOrDefault();
private bool SelectionLocked
{
@ -201,9 +193,9 @@ namespace NAPS2.EtoForms.Ui
private void Drop(object sender, DropEventArgs e)
{
// Receive drop data
if (e.Data.GetDataPresent(TransferHelper.ProfileTypeName))
if (_profileTransfer.IsIn(e.Data.ToEto()))
{
var data = DirectProfileTransfer.Parser.ParseFrom((byte[])e.Data.GetData(TransferHelper.ProfileTypeName));
var data = _profileTransfer.GetFrom(e.Data.ToEto());
if (data.ProcessId == Process.GetCurrentProcess().Id)
{
if (data.Locked)
@ -241,11 +233,15 @@ namespace NAPS2.EtoForms.Ui
_editCommand.Enabled = SelectedProfile != null;
_deleteCommand.Enabled = SelectedProfile != null && !SelectedProfile.IsLocked;
_copyCommand.Enabled = SelectedProfile != null;
_pasteCommand.Enabled = TransferHelper.ClipboardHasProfile();
_pasteCommand.Enabled = _profileTransfer.IsInClipboard();
}
private async void DoScan()
{
if (ImageCallback == null)
{
throw new InvalidOperationException("Image callback not specified");
}
if (_profileManager.Profiles.Count == 0)
{
var editSettingsForm = FormFactory.Create<FEditProfile>();
@ -303,12 +299,9 @@ namespace NAPS2.EtoForms.Ui
private void DoDelete()
{
int selectedCount = _listView.Selection.Count;
if (selectedCount > 0 && !SelectionLocked)
if (SelectedProfile != null && !SelectionLocked)
{
string message = selectedCount == 1
? string.Format(MiscResources.ConfirmDeleteSingleProfile, SelectedProfile.DisplayName)
: string.Format(MiscResources.ConfirmDeleteMultipleProfiles, selectedCount);
string message = string.Format(MiscResources.ConfirmDeleteSingleProfile, SelectedProfile.DisplayName);
if (MessageBox.Show(message, MiscResources.Delete, MessageBoxButtons.YesNo, MessageBoxType.Warning) == DialogResult.Yes)
{
foreach (var profile in _listView.Selection)
@ -330,7 +323,10 @@ namespace NAPS2.EtoForms.Ui
private void DoCopy()
{
TransferHelper.SaveProfileToClipboard(SelectedProfile);
if (SelectedProfile != null)
{
_profileTransfer.SetClipboard(SelectedProfile);
}
}
private void DoPaste()
@ -339,10 +335,10 @@ namespace NAPS2.EtoForms.Ui
{
return;
}
if (TransferHelper.ClipboardHasProfile())
if (_profileTransfer.IsInClipboard())
{
var transfer = TransferHelper.GetProfileFromClipboard();
var profile = transfer.ScanProfileXml.FromXml<ScanProfile>();
var data = _profileTransfer.GetFromClipboard();
var profile = data.ScanProfileXml.FromXml<ScanProfile>();
_profileManager.Mutate(new ListMutation<ScanProfile>.Append(profile), _listView);
}
}

View File

@ -24,7 +24,7 @@ namespace NAPS2.ImportExport
AllowBackground = true;
}
public bool Start(DirectImageTransfer data, bool copy, Action<ScannedImage> imageCallback, DirectImportParams importParams)
public bool Start(ImageTransferData data, bool copy, Action<ScannedImage> imageCallback, DirectImportParams importParams)
{
ProgressTitle = copy ? MiscResources.CopyProgress : MiscResources.ImportProgress;
Status = new OperationStatus

View File

@ -0,0 +1,30 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using NAPS2.Images;
using NAPS2.Images.Storage;
using NAPS2.Serialization;
namespace NAPS2.ImportExport
{
public class ImageTransfer : TransferHelper<IEnumerable<ScannedImage>, ImageTransferData>
{
private readonly ImageContext _imageContext;
public ImageTransfer(ImageContext imageContext)
{
_imageContext = imageContext;
}
protected override ImageTransferData AsData(IEnumerable<ScannedImage> images)
{
var transfer = new ImageTransferData
{
ProcessId = Process.GetCurrentProcess().Id
};
var serializedImages = images.Select(x => SerializedImageHelper.Serialize(_imageContext, (ScannedImage) x, new SerializedImageHelper.SerializeOptions()));
transfer.SerializedImages.AddRange(serializedImages);
return transfer;
}
}
}

View File

@ -0,0 +1,23 @@
using System.Diagnostics;
using NAPS2.Scan;
using NAPS2.Serialization;
namespace NAPS2.ImportExport
{
public class ProfileTransfer : TransferHelper<ScanProfile, ProfileTransferData>
{
protected override ProfileTransferData AsData(ScanProfile profile)
{
var profileCopy = profile.Clone();
profileCopy.IsDefault = false;
profileCopy.IsLocked = false;
profileCopy.IsDeviceLocked = false;
return new ProfileTransferData
{
ProcessId = Process.GetCurrentProcess().Id,
ScanProfileXml = profileCopy.ToXml(),
Locked = profile.IsLocked
};
}
}
}

View File

@ -4,12 +4,12 @@ package NAPS2.ImportExport;
import "Serialization/SerializedImage.proto";
message DirectImageTransfer {
message ImageTransferData {
int32 processId = 1;
repeated NAPS2.Serialization.SerializedImage serializedImages = 2;
}
message DirectProfileTransfer {
message ProfileTransferData {
int32 processId = 1;
string scanProfileXml = 2;
bool locked = 3;

View File

@ -1,92 +1,39 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Eto.Forms;
using Google.Protobuf;
using NAPS2.Images;
using NAPS2.Images.Storage;
using NAPS2.Scan;
using NAPS2.Serialization;
namespace NAPS2.ImportExport
{
// TODO: Split
// ProfileTransfer, ImageTransfer (injected helpers); ProfileTransferData, ImageTransferData (protos)
// .SetClipboard(X); AddTo(DataObject); IsIn(DataObject); IsInClipboard(); GetFrom(DataObject); GetFromClipboard()
// We can have a generic base class (TransferHelper) that implements the clipboard methods.
// ImageContext is injected for imagetransfer
public static class TransferHelper
public abstract class TransferHelper<TInput, TData> where TData : IMessage<TData>, new()
{
public static string ImageTypeName { get; } = "NAPS2.Serialization.DirectImageTransferProto";
public static string ProfileTypeName { get; } = "NAPS2.Serialization.DirectProfileTransferProto";
private readonly string _typeName = typeof(TData).FullName;
public static DirectImageTransfer Images(ImageContext imageContext, IEnumerable<ScannedImage> images)
{
var transfer = new DirectImageTransfer
{
ProcessId = Process.GetCurrentProcess().Id
};
var serializedImages = images.Select(x => SerializedImageHelper.Serialize(imageContext, x, new SerializedImageHelper.SerializeOptions()));
transfer.SerializedImages.AddRange(serializedImages);
return transfer;
}
public static DirectProfileTransfer Profile(ScanProfile profile)
{
var profileCopy = profile.Clone();
profileCopy.IsDefault = false;
profileCopy.IsLocked = false;
profileCopy.IsDeviceLocked = false;
return new DirectProfileTransfer
{
ProcessId = Process.GetCurrentProcess().Id,
ScanProfileXml = profileCopy.ToXml(),
Locked = profile.IsLocked
};
}
public static bool ClipboardHasImages() =>
Clipboard.Instance.Contains(ImageTypeName);
public static DirectImageTransfer GetImagesFromClipboard() => GetImagesFromDataObject(Clipboard.Instance);
public static DirectImageTransfer GetImagesFromDataObject(IDataObject dataObject)
{
var data = dataObject.GetData(ImageTypeName);
return DirectImageTransfer.Parser.ParseFrom(data);
}
public static void SaveImagesToClipboard(ImageContext imageContext, IEnumerable<ScannedImage> images)
public void SetClipboard(TInput input)
{
Clipboard.Instance.Clear();
SaveImagesToDataObject(imageContext, images, Clipboard.Instance);
}
public static void SaveImagesToDataObject(ImageContext imageContext, IEnumerable<ScannedImage> images, IDataObject dataObject) {
dataObject.SetData(Images(imageContext, images).ToByteArray(), ImageTypeName);
AddToClipboard(input);
}
public static bool ClipboardHasProfile() =>
Clipboard.Instance.Contains(ProfileTypeName);
public static bool HasProfile(IDataObject dataObject) =>
dataObject.Contains(ProfileTypeName);
public void AddToClipboard(TInput input) => AddTo(Clipboard.Instance, input);
public bool IsInClipboard() => IsIn(Clipboard.Instance);
public TData GetFromClipboard() => GetFrom(Clipboard.Instance);
public static DirectProfileTransfer GetProfileFromClipboard() => GetProfileFromDataObject(Clipboard.Instance);
public static DirectProfileTransfer GetProfileFromDataObject(IDataObject dataObject)
public void AddTo(IDataObject dataObject, TInput input)
{
var data = dataObject.GetData(ProfileTypeName);
return DirectProfileTransfer.Parser.ParseFrom(data);
dataObject.SetData(AsData(input).ToByteArray(), _typeName);
}
public static void SaveProfileToClipboard(ScanProfile profile) {
Clipboard.Instance.Clear();
SaveProfileToDataObject(profile, Clipboard.Instance);
public bool IsIn(IDataObject dataObject)
{
return dataObject.Contains(_typeName);
}
public static void SaveProfileToDataObject(ScanProfile profile, IDataObject dataObject) {
dataObject.SetData(Profile(profile).ToByteArray(), ProfileTypeName);
public TData GetFrom(IDataObject dataObject)
{
var data = new TData();
data.MergeFrom(dataObject.GetData(_typeName));
return data;
}
protected abstract TData AsData(TInput input);
}
}

View File

@ -30,7 +30,7 @@ namespace NAPS2.Scan
}
}
public void DeletingProfile(string name)
public void DeletingProfile(string? name)
{
if (string.IsNullOrEmpty(name))
{

View File

@ -12,7 +12,7 @@ using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Google.Protobuf;
using Eto.WinForms;
using NAPS2.Config;
using NAPS2.EtoForms.Ui;
using NAPS2.ImportExport;
@ -61,6 +61,7 @@ namespace NAPS2.WinForms
private readonly UpdateChecker _updateChecker;
private readonly IProfileManager _profileManager;
private readonly ScannedImageList _imageList;
private readonly ImageTransfer _imageTransfer;
#endregion
@ -78,7 +79,7 @@ namespace NAPS2.WinForms
#region Initialization and Culture
public FDesktop(ImageContext imageContext, StringWrapper stringWrapper, RecoveryManager recoveryManager, OcrEngineManager ocrEngineManager, IScanPerformer scanPerformer, IScannedImagePrinter scannedImagePrinter, StillImage stillImage, IOperationFactory operationFactory, KeyboardShortcutManager ksm, ThumbnailRenderer thumbnailRenderer, WinFormsExportHelper exportHelper, ImageClipboard imageClipboard, ImageRenderer imageRenderer, NotificationManager notify, CultureInitializer cultureInitializer, IWorkerFactory workerFactory, OperationProgress operationProgress, UpdateChecker updateChecker, IProfileManager profileManager, ScannedImageList imageList)
public FDesktop(ImageContext imageContext, StringWrapper stringWrapper, RecoveryManager recoveryManager, OcrEngineManager ocrEngineManager, IScanPerformer scanPerformer, IScannedImagePrinter scannedImagePrinter, StillImage stillImage, IOperationFactory operationFactory, KeyboardShortcutManager ksm, ThumbnailRenderer thumbnailRenderer, WinFormsExportHelper exportHelper, ImageClipboard imageClipboard, ImageRenderer imageRenderer, NotificationManager notify, CultureInitializer cultureInitializer, IWorkerFactory workerFactory, OperationProgress operationProgress, UpdateChecker updateChecker, IProfileManager profileManager, ScannedImageList imageList, ImageTransfer imageTransfer)
{
_imageContext = imageContext;
_stringWrapper = stringWrapper;
@ -100,6 +101,7 @@ namespace NAPS2.WinForms
_updateChecker = updateChecker;
_profileManager = profileManager;
_imageList = imageList;
_imageTransfer = imageTransfer;
_userActions = new UserActions(imageContext, imageList);
InitializeComponent();
@ -952,7 +954,7 @@ namespace NAPS2.WinForms
return filesList;
}
private void ImportDirect(DirectImageTransfer data, bool copy)
private void ImportDirect(ImageTransferData data, bool copy)
{
var op = _operationFactory.Create<DirectImportOperation>();
if (op.Start(data, copy, ReceiveScannedImage(), new DirectImportParams { ThumbnailSize = ConfigProvider.Get(c => c.ThumbnailSize) }))
@ -1436,7 +1438,7 @@ namespace NAPS2.WinForms
private void contextMenuStrip_Opening(object sender, System.ComponentModel.CancelEventArgs e)
{
ctxPaste.Enabled = TransferHelper.ClipboardHasImages();
ctxPaste.Enabled = _imageTransfer.IsInClipboard();
if (!_imageList.Images.Any() && !ctxPaste.Enabled)
{
e.Cancel = true;
@ -1457,9 +1459,9 @@ namespace NAPS2.WinForms
private void ctxPaste_Click(object sender, EventArgs e)
{
if (TransferHelper.ClipboardHasImages())
if (_imageTransfer.IsInClipboard())
{
ImportDirect(TransferHelper.GetImagesFromClipboard(), true);
ImportDirect(_imageTransfer.GetFromClipboard(), true);
}
}
@ -1637,7 +1639,7 @@ namespace NAPS2.WinForms
if (SelectedIndices.Any())
{
var ido = new DataObject();
ido.SetData(TransferHelper.ImageTypeName, TransferHelper.Images(_imageContext, SelectedImages).ToByteArray());
_imageTransfer.AddTo(ido.ToEto(), SelectedImages);
DoDragDrop(ido, DragDropEffects.Move | DragDropEffects.Copy);
}
}
@ -1647,9 +1649,9 @@ namespace NAPS2.WinForms
// Determine if drop data is compatible
try
{
if (e.Data.GetDataPresent(TransferHelper.ImageTypeName))
if (_imageTransfer.IsIn(e.Data.ToEto()))
{
var data = DirectImageTransfer.Parser.ParseFrom((byte[]) e.Data.GetData(TransferHelper.ImageTypeName));
var data = _imageTransfer.GetFrom(e.Data.ToEto());
e.Effect = data.ProcessId == Process.GetCurrentProcess().Id
? DragDropEffects.Move
: DragDropEffects.Copy;
@ -1668,9 +1670,9 @@ namespace NAPS2.WinForms
private void thumbnailList1_DragDrop(object sender, DragEventArgs e)
{
// Receive drop data
if (e.Data.GetDataPresent(TransferHelper.ImageTypeName))
if (_imageTransfer.IsIn(e.Data.ToEto()))
{
var data = DirectImageTransfer.Parser.ParseFrom((byte[]) e.Data.GetData(TransferHelper.ImageTypeName));
var data = _imageTransfer.GetFrom(e.Data.ToEto());
if (data.ProcessId == Process.GetCurrentProcess().Id)
{
DragMoveImages(e);

View File

@ -15,22 +15,21 @@ namespace NAPS2.WinForms
{
public class ImageClipboard
{
private readonly ImageContext _imageContext;
private readonly BitmapRenderer _bitmapRenderer;
private readonly ImageTransfer _imageTransfer;
public ImageClipboard()
{
_imageContext = ImageContext.Default;
_bitmapRenderer = new BitmapRenderer(ImageContext.Default);
_imageTransfer = new ImageTransfer(ImageContext.Default);
}
public ImageClipboard(ImageContext imageContext, BitmapRenderer bitmapRenderer)
public ImageClipboard(BitmapRenderer bitmapRenderer, ImageTransfer imageTransfer)
{
_imageContext = imageContext;
_bitmapRenderer = bitmapRenderer;
_imageTransfer = imageTransfer;
}
public async Task Write(IEnumerable<ScannedImage> images, bool includeBitmap)
{
var imageList = images.ToList();
@ -40,7 +39,7 @@ namespace NAPS2.WinForms
}
// Fast path for copying within NAPS2
TransferHelper.SaveImagesToClipboard(_imageContext, imageList);
_imageTransfer.SetClipboard(imageList);
// Slow path for more full-featured copying
if (includeBitmap)