mirror of
https://github.com/cyanfish/naps2.git
synced 2024-09-11 15:26:55 +03:00
Move eto forms and most other eto code to Lib.Common
This commit is contained in:
parent
fdd1ff839e
commit
3159ede121
@ -1,4 +1,4 @@
|
||||
using System.Windows.Forms;
|
||||
using Eto.Forms;
|
||||
|
||||
namespace NAPS2.EtoForms;
|
||||
|
@ -31,6 +31,27 @@ public static class EtoExtensions
|
||||
public static Bitmap ToEtoImage(this byte[] bytes) => new(bytes);
|
||||
public static Bitmap ToEtoImage(this IMemoryImage image) => EtoPlatform.Current.ToBitmap(image);
|
||||
|
||||
public static MessageBoxType ToEto(this MessageBoxIcon icon)
|
||||
{
|
||||
return icon switch
|
||||
{
|
||||
MessageBoxIcon.Information => MessageBoxType.Information,
|
||||
MessageBoxIcon.Warning => MessageBoxType.Warning,
|
||||
_ => MessageBoxType.Information // TODO: Default type with no icon?
|
||||
};
|
||||
}
|
||||
|
||||
public static DockPosition ToEto(this DockStyle dock)
|
||||
{
|
||||
return dock switch
|
||||
{
|
||||
DockStyle.Bottom => DockPosition.Bottom,
|
||||
DockStyle.Left => DockPosition.Left,
|
||||
DockStyle.Right => DockPosition.Right,
|
||||
_ => DockPosition.Top
|
||||
};
|
||||
}
|
||||
|
||||
public static ControlWithLayoutAttributes Width(this Control control, int width) =>
|
||||
new ControlWithLayoutAttributes(control, width: width);
|
||||
public static ControlWithLayoutAttributes Height(this Control control, int height) =>
|
@ -16,4 +16,5 @@ public abstract class EtoPlatform
|
||||
public abstract IListView<T> CreateListView<T>(ListViewBehavior<T> behavior) where T : notnull;
|
||||
public abstract void ConfigureImageButton(Button button);
|
||||
public abstract Bitmap ToBitmap(IMemoryImage image);
|
||||
public abstract IMemoryImage DrawHourglass(IMemoryImage thumb);
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
using Eto.Drawing;
|
||||
using Eto.Forms;
|
||||
using Eto.WinForms;
|
||||
using NAPS2.ImportExport.Images;
|
||||
|
||||
namespace NAPS2.EtoForms;
|
||||
@ -22,7 +21,7 @@ public class ImageListViewBehavior : ListViewBehavior<UiImage>
|
||||
|
||||
public override Image GetImage(UiImage item, int imageSize)
|
||||
{
|
||||
return _thumbnailProvider.GetThumbnail(item, imageSize).ToEto();
|
||||
return _thumbnailProvider.GetThumbnail(item, imageSize).ToEtoImage();
|
||||
}
|
||||
|
||||
public override void SetDragData(ListSelection<UiImage> selection, IDataObject dataObject)
|
@ -1,6 +1,5 @@
|
||||
using Eto.Drawing;
|
||||
using Eto.Forms;
|
||||
using Eto.WinForms;
|
||||
using NAPS2.ImportExport.Profiles;
|
||||
using NAPS2.Scan;
|
||||
|
@ -1,5 +1,5 @@
|
||||
using Eto.Drawing;
|
||||
using Eto.Forms;
|
||||
using Eto.WinForms;
|
||||
using NAPS2.Update;
|
||||
|
||||
namespace NAPS2.EtoForms.Ui;
|
||||
@ -25,7 +25,7 @@ public class AboutForm : EtoDialogBase
|
||||
_updateChecker = updateChecker;
|
||||
|
||||
Title = UiStrings.AboutFormTitle;
|
||||
Icon = Icons.information_small.ToEtoIcon();
|
||||
Icon = new Icon(1f, Icons.information_small.ToEtoImage());
|
||||
Resizable = false;
|
||||
FormStateController.RestoreFormState = false;
|
||||
|
@ -1,6 +1,5 @@
|
||||
using System.Collections.Immutable;
|
||||
using Eto.Forms;
|
||||
using Eto.WinForms;
|
||||
using NAPS2.ImportExport.Images;
|
||||
using NAPS2.WinForms;
|
||||
|
||||
@ -8,7 +7,7 @@ namespace NAPS2.EtoForms.Ui;
|
||||
|
||||
public abstract class DesktopForm : EtoFormBase
|
||||
{
|
||||
private readonly KeyboardShortcutManager _ksm;
|
||||
// private readonly KeyboardShortcutManager _ksm;
|
||||
private readonly INotificationManager _notify;
|
||||
private readonly CultureHelper _cultureHelper;
|
||||
private readonly IProfileManager _profileManager;
|
||||
@ -20,7 +19,7 @@ public abstract class DesktopForm : EtoFormBase
|
||||
private readonly IDesktopScanController _desktopScanController;
|
||||
private readonly ImageListActions _imageListActions;
|
||||
private readonly DesktopFormProvider _desktopFormProvider;
|
||||
private readonly DesktopSubFormController _desktopSubFormController;
|
||||
private readonly IDesktopSubFormController _desktopSubFormController;
|
||||
|
||||
private readonly Command _scanCommand;
|
||||
private readonly Command _newProfileCommand;
|
||||
@ -79,7 +78,7 @@ public abstract class DesktopForm : EtoFormBase
|
||||
|
||||
public DesktopForm(
|
||||
Naps2Config config,
|
||||
KeyboardShortcutManager ksm,
|
||||
// KeyboardShortcutManager ksm,
|
||||
INotificationManager notify,
|
||||
CultureHelper cultureHelper,
|
||||
IProfileManager profileManager,
|
||||
@ -91,9 +90,9 @@ public abstract class DesktopForm : EtoFormBase
|
||||
IDesktopScanController desktopScanController,
|
||||
ImageListActions imageListActions,
|
||||
DesktopFormProvider desktopFormProvider,
|
||||
DesktopSubFormController desktopSubFormController) : base(config)
|
||||
IDesktopSubFormController desktopSubFormController) : base(config)
|
||||
{
|
||||
_ksm = ksm;
|
||||
// _ksm = ksm;
|
||||
_notify = notify;
|
||||
_cultureHelper = cultureHelper;
|
||||
_profileManager = profileManager;
|
||||
@ -132,7 +131,7 @@ public abstract class DesktopForm : EtoFormBase
|
||||
ToolBarText = UiStrings.Ocr,
|
||||
Image = Icons.text.ToEtoImage()
|
||||
};
|
||||
_importCommand = new ActionCommand(_desktopController.Import)
|
||||
_importCommand = new ActionCommand(() => _desktopController.Import(this))
|
||||
{
|
||||
ToolBarText = UiStrings.Import,
|
||||
Image = Icons.folder_picture.ToEtoImage()
|
||||
@ -185,27 +184,27 @@ public abstract class DesktopForm : EtoFormBase
|
||||
{
|
||||
MenuText = UiStrings.View
|
||||
};
|
||||
_cropCommand = new ActionCommand(_desktopSubFormController.ShowImageForm<FCrop>)
|
||||
_cropCommand = new ActionCommand(_desktopSubFormController.ShowCropForm)
|
||||
{
|
||||
MenuText = UiStrings.Crop,
|
||||
Image = Icons.transform_crop.ToEtoImage()
|
||||
};
|
||||
_brightContCommand = new ActionCommand(_desktopSubFormController.ShowImageForm<FBrightnessContrast>)
|
||||
_brightContCommand = new ActionCommand(_desktopSubFormController.ShowBrightnessContrastForm)
|
||||
{
|
||||
MenuText = UiStrings.BrightnessContrast,
|
||||
Image = Icons.contrast_with_sun.ToEtoImage()
|
||||
};
|
||||
_hueSatCommand = new ActionCommand(_desktopSubFormController.ShowImageForm<FHueSaturation>)
|
||||
_hueSatCommand = new ActionCommand(_desktopSubFormController.ShowHueSaturationForm)
|
||||
{
|
||||
MenuText = UiStrings.HueSaturation,
|
||||
Image = Icons.color_management.ToEtoImage()
|
||||
};
|
||||
_blackWhiteCommand = new ActionCommand(_desktopSubFormController.ShowImageForm<FBlackWhite>)
|
||||
_blackWhiteCommand = new ActionCommand(_desktopSubFormController.ShowBlackWhiteForm)
|
||||
{
|
||||
MenuText = UiStrings.BlackAndWhite,
|
||||
Image = Icons.contrast_high.ToEtoImage()
|
||||
};
|
||||
_sharpenCommand = new ActionCommand(_desktopSubFormController.ShowImageForm<FSharpen>)
|
||||
_sharpenCommand = new ActionCommand(_desktopSubFormController.ShowSharpenForm)
|
||||
{
|
||||
MenuText = UiStrings.Sharpen,
|
||||
Image = Icons.sharpen.ToEtoImage()
|
||||
@ -238,7 +237,7 @@ public abstract class DesktopForm : EtoFormBase
|
||||
{
|
||||
MenuText = UiStrings.Deskew
|
||||
};
|
||||
_customRotateCommand = new ActionCommand(_desktopSubFormController.ShowImageForm<FRotate>)
|
||||
_customRotateCommand = new ActionCommand(_desktopSubFormController.ShowRotateForm)
|
||||
{
|
||||
MenuText = UiStrings.CustomRotation
|
||||
};
|
||||
@ -872,9 +871,9 @@ public abstract class DesktopForm : EtoFormBase
|
||||
|
||||
private void ListViewDrop(object? sender, DropEventArgs args)
|
||||
{
|
||||
if (_imageTransfer.IsIn(args.Data.ToEto()))
|
||||
if (_imageTransfer.IsIn(args.Data))
|
||||
{
|
||||
var data = _imageTransfer.GetFrom(args.Data.ToEto());
|
||||
var data = _imageTransfer.GetFrom(args.Data);
|
||||
if (data.ProcessId == Process.GetCurrentProcess().Id)
|
||||
{
|
||||
DragMoveImages(args.Position);
|
||||
@ -884,10 +883,10 @@ public abstract class DesktopForm : EtoFormBase
|
||||
_desktopController.ImportDirect(data, false);
|
||||
}
|
||||
}
|
||||
else if (args.Data.GetDataPresent("FileDrop"))
|
||||
else if (args.Data.Contains("FileDrop"))
|
||||
{
|
||||
// TODO: Is this xplat-compatible?
|
||||
var data = (string[]) args.Data.GetData("FileDrop");
|
||||
var data = args.Data.GetObject<string[]>("FileDrop");
|
||||
_desktopController.ImportFiles(data);
|
||||
}
|
||||
}
|
@ -1,11 +1,8 @@
|
||||
using Eto.Drawing;
|
||||
using Eto.Forms;
|
||||
using Eto.WinForms;
|
||||
using Eto.WinForms.Forms;
|
||||
using NAPS2.ImportExport.Profiles;
|
||||
using NAPS2.Scan;
|
||||
using NAPS2.Serialization;
|
||||
using NAPS2.WinForms;
|
||||
|
||||
namespace NAPS2.EtoForms.Ui;
|
||||
|
||||
@ -35,17 +32,18 @@ public class ProfilesForm : EtoDialogBase
|
||||
_profileTransfer = profileTransfer;
|
||||
|
||||
Title = UiStrings.ProfilesFormTitle;
|
||||
Icon = Icons.blueprints_small.ToEtoIcon();
|
||||
Icon = new Icon(1f, Icons.blueprints_small.ToEtoImage());
|
||||
Size = new Size(700, 200);
|
||||
MinimumSize = new Size(600, 180);
|
||||
Resizable = true;
|
||||
|
||||
switch (Handler)
|
||||
{
|
||||
case IWindowsControl windowsControl:
|
||||
windowsControl.UseShellDropManager = false;
|
||||
break;
|
||||
}
|
||||
// TODO: Do this only in WinForms (?)
|
||||
// switch (Handler)
|
||||
// {
|
||||
// case IWindowsControl windowsControl:
|
||||
// windowsControl.UseShellDropManager = false;
|
||||
// break;
|
||||
// }
|
||||
|
||||
_listView = EtoPlatform.Current.CreateListView(profileListViewBehavior);
|
||||
_scanCommand = new ActionCommand(DoScan)
|
||||
@ -188,9 +186,9 @@ public class ProfilesForm : EtoDialogBase
|
||||
private void Drop(object? sender, DropEventArgs e)
|
||||
{
|
||||
// Receive drop data
|
||||
if (_profileTransfer.IsIn(e.Data.ToEto()))
|
||||
if (_profileTransfer.IsIn(e.Data))
|
||||
{
|
||||
var data = _profileTransfer.GetFrom(e.Data.ToEto());
|
||||
var data = _profileTransfer.GetFrom(e.Data);
|
||||
if (data.ProcessId == Process.GetCurrentProcess().Id)
|
||||
{
|
||||
if (data.Locked)
|
||||
@ -233,63 +231,64 @@ public class ProfilesForm : EtoDialogBase
|
||||
|
||||
private async void DoScan()
|
||||
{
|
||||
if (ImageCallback == null)
|
||||
{
|
||||
throw new InvalidOperationException("Image callback not specified");
|
||||
}
|
||||
if (_profileManager.Profiles.Count == 0)
|
||||
{
|
||||
var editSettingsForm = FormFactory.Create<FEditProfile>();
|
||||
editSettingsForm.ScanProfile = new ScanProfile
|
||||
{
|
||||
Version = ScanProfile.CURRENT_VERSION
|
||||
};
|
||||
editSettingsForm.ShowDialog();
|
||||
if (!editSettingsForm.Result)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_profileManager.Mutate(new ListMutation<ScanProfile>.Append(editSettingsForm.ScanProfile), ListSelection.Empty<ScanProfile>());
|
||||
_profileManager.DefaultProfile = editSettingsForm.ScanProfile;
|
||||
}
|
||||
if (SelectedProfile == null)
|
||||
{
|
||||
MessageBox.Show(MiscResources.SelectProfileBeforeScan, MiscResources.ChooseProfile, MessageBoxButtons.OK, MessageBoxType.Warning);
|
||||
return;
|
||||
}
|
||||
if (_profileManager.DefaultProfile == null)
|
||||
{
|
||||
_profileManager.DefaultProfile = SelectedProfile;
|
||||
}
|
||||
var source = await _scanPerformer.PerformScan(SelectedProfile, DefaultScanParams(), this.ToNative().Handle);
|
||||
await source.ForEach(ImageCallback);
|
||||
this.ToNative().Activate();
|
||||
// TODO: Migrate FEditProfile to eto
|
||||
// if (ImageCallback == null)
|
||||
// {
|
||||
// throw new InvalidOperationException("Image callback not specified");
|
||||
// }
|
||||
// if (_profileManager.Profiles.Count == 0)
|
||||
// {
|
||||
// var editSettingsForm = FormFactory.Create<FEditProfile>();
|
||||
// editSettingsForm.ScanProfile = new ScanProfile
|
||||
// {
|
||||
// Version = ScanProfile.CURRENT_VERSION
|
||||
// };
|
||||
// editSettingsForm.ShowDialog();
|
||||
// if (!editSettingsForm.Result)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
// _profileManager.Mutate(new ListMutation<ScanProfile>.Append(editSettingsForm.ScanProfile), ListSelection.Empty<ScanProfile>());
|
||||
// _profileManager.DefaultProfile = editSettingsForm.ScanProfile;
|
||||
// }
|
||||
// if (SelectedProfile == null)
|
||||
// {
|
||||
// MessageBox.Show(MiscResources.SelectProfileBeforeScan, MiscResources.ChooseProfile, MessageBoxButtons.OK, MessageBoxType.Warning);
|
||||
// return;
|
||||
// }
|
||||
// if (_profileManager.DefaultProfile == null)
|
||||
// {
|
||||
// _profileManager.DefaultProfile = SelectedProfile;
|
||||
// }
|
||||
// var source = await _scanPerformer.PerformScan(SelectedProfile, DefaultScanParams(), this.ToNative().Handle);
|
||||
// await source.ForEach(ImageCallback);
|
||||
// this.ToNative().Activate();
|
||||
}
|
||||
|
||||
private void DoAdd()
|
||||
{
|
||||
var fedit = FormFactory.Create<FEditProfile>();
|
||||
fedit.ScanProfile = Config.DefaultProfileSettings();
|
||||
fedit.ShowDialog();
|
||||
if (fedit.Result)
|
||||
{
|
||||
_profileManager.Mutate(new ListMutation<ScanProfile>.Append(fedit.ScanProfile), _listView);
|
||||
}
|
||||
// var fedit = FormFactory.Create<FEditProfile>();
|
||||
// fedit.ScanProfile = Config.DefaultProfileSettings();
|
||||
// fedit.ShowDialog();
|
||||
// if (fedit.Result)
|
||||
// {
|
||||
// _profileManager.Mutate(new ListMutation<ScanProfile>.Append(fedit.ScanProfile), _listView);
|
||||
// }
|
||||
}
|
||||
|
||||
private void DoEdit()
|
||||
{
|
||||
var originalProfile = SelectedProfile;
|
||||
if (originalProfile != null)
|
||||
{
|
||||
var fedit = FormFactory.Create<FEditProfile>();
|
||||
fedit.ScanProfile = originalProfile;
|
||||
fedit.ShowDialog();
|
||||
if (fedit.Result)
|
||||
{
|
||||
_profileManager.Mutate(new ListMutation<ScanProfile>.ReplaceWith(fedit.ScanProfile), _listView);
|
||||
}
|
||||
}
|
||||
// var originalProfile = SelectedProfile;
|
||||
// if (originalProfile != null)
|
||||
// {
|
||||
// var fedit = FormFactory.Create<FEditProfile>();
|
||||
// fedit.ScanProfile = originalProfile;
|
||||
// fedit.ShowDialog();
|
||||
// if (fedit.Result)
|
||||
// {
|
||||
// _profileManager.Mutate(new ListMutation<ScanProfile>.ReplaceWith(fedit.ScanProfile), _listView);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
private void DoDelete()
|
@ -1,5 +1,6 @@
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using NAPS2.EtoForms;
|
||||
using NAPS2.Images.Gdi;
|
||||
|
||||
namespace NAPS2.Images;
|
||||
@ -10,61 +11,43 @@ namespace NAPS2.Images;
|
||||
/// </summary>
|
||||
public class UiThumbnailProvider
|
||||
{
|
||||
private Bitmap? _placeholder;
|
||||
private readonly ImageContext _imageContext;
|
||||
private IMemoryImage? _placeholder;
|
||||
|
||||
public Bitmap GetThumbnail(UiImage img, int thumbnailSize)
|
||||
public UiThumbnailProvider(ImageContext imageContext)
|
||||
{
|
||||
_imageContext = imageContext;
|
||||
}
|
||||
|
||||
public IMemoryImage GetThumbnail(UiImage img, int thumbnailSize)
|
||||
{
|
||||
lock (img)
|
||||
{
|
||||
var thumb = ((GdiImage?) img.GetThumbnailClone())?.Bitmap;
|
||||
var thumb = img.GetThumbnailClone();
|
||||
if (thumb == null)
|
||||
{
|
||||
return RenderPlaceholder(thumbnailSize);
|
||||
}
|
||||
if (img.IsThumbnailDirty)
|
||||
{
|
||||
thumb = DrawHourglass(thumb);
|
||||
thumb = EtoPlatform.Current.DrawHourglass(thumb);
|
||||
}
|
||||
return thumb;
|
||||
}
|
||||
}
|
||||
|
||||
private Bitmap RenderPlaceholder(int thumbnailSize)
|
||||
private IMemoryImage RenderPlaceholder(int thumbnailSize)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (_placeholder?.Size.Width == thumbnailSize)
|
||||
if (_placeholder?.Width == thumbnailSize)
|
||||
{
|
||||
return _placeholder;
|
||||
}
|
||||
_placeholder?.Dispose();
|
||||
_placeholder = new Bitmap(thumbnailSize, thumbnailSize);
|
||||
_placeholder = DrawHourglass(_placeholder);
|
||||
_placeholder = _imageContext.Create(thumbnailSize, thumbnailSize, ImagePixelFormat.RGB24);
|
||||
_placeholder = EtoPlatform.Current.DrawHourglass(_placeholder);
|
||||
return _placeholder;
|
||||
}
|
||||
}
|
||||
|
||||
private Bitmap DrawHourglass(Image image)
|
||||
{
|
||||
var bitmap = new Bitmap(image.Width, image.Height);
|
||||
using (var g = Graphics.FromImage(bitmap))
|
||||
{
|
||||
var attrs = new ImageAttributes();
|
||||
attrs.SetColorMatrix(new ColorMatrix
|
||||
{
|
||||
Matrix33 = 0.3f
|
||||
});
|
||||
g.DrawImage(image,
|
||||
new Rectangle(0, 0, bitmap.Width, bitmap.Height),
|
||||
0,
|
||||
0,
|
||||
image.Width,
|
||||
image.Height,
|
||||
GraphicsUnit.Pixel,
|
||||
attrs);
|
||||
g.DrawImage(new Bitmap(new MemoryStream(Icons.hourglass_grey)), new Rectangle((bitmap.Width - 32) / 2, (bitmap.Height - 32) / 2, 32, 32));
|
||||
}
|
||||
image.Dispose();
|
||||
return bitmap;
|
||||
}
|
||||
}
|
12
NAPS2.Lib.Common/ImportExport/IScannedImagePrinter.cs
Normal file
12
NAPS2.Lib.Common/ImportExport/IScannedImagePrinter.cs
Normal file
@ -0,0 +1,12 @@
|
||||
namespace NAPS2.ImportExport;
|
||||
|
||||
public interface IScannedImagePrinter
|
||||
{
|
||||
/// <summary>
|
||||
/// Prints the provided images, prompting the user for the printer settings.
|
||||
/// </summary>
|
||||
/// <param name="images">The full list of images to print.</param>
|
||||
/// <param name="selectedImages">The list of selected images. If non-empty, the user will be presented an option to print selected.</param>
|
||||
/// <returns>True if the print completed, false if there was nothing to print or the user cancelled.</returns>
|
||||
Task<bool> PromptToPrint(IList<ProcessedImage> images, IList<ProcessedImage> selectedImages);
|
||||
}
|
@ -20,6 +20,7 @@
|
||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
||||
<PackageReference Include="Ninject" Version="3.3.6" />
|
||||
<PackageReference Include="NLog" Version="4.5.8" />
|
||||
<PackageReference Include="Eto.Forms" Version="2.7.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -1,7 +1,6 @@
|
||||
using System.ComponentModel;
|
||||
using System.IO.Compression;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
@ -1,5 +1,6 @@
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
using Eto.Forms;
|
||||
using NAPS2.EtoForms;
|
||||
using NAPS2.ImportExport;
|
||||
using NAPS2.ImportExport.Images;
|
||||
using NAPS2.Platform.Windows;
|
||||
@ -7,7 +8,6 @@ using NAPS2.Recovery;
|
||||
using NAPS2.Remoting;
|
||||
using NAPS2.Scan;
|
||||
using NAPS2.Update;
|
||||
using MessageBoxIcon = System.Windows.Forms.MessageBoxIcon;
|
||||
|
||||
namespace NAPS2.WinForms;
|
||||
|
||||
@ -181,9 +181,10 @@ public class DesktopController
|
||||
{
|
||||
if (_operationProgress.ActiveOperations.Any(x => !x.SkipExitPrompt))
|
||||
{
|
||||
var result = MessageBox.Show(MiscResources.ExitWithActiveOperations,
|
||||
var result = MessageBox.Show(_desktopFormProvider.DesktopForm,
|
||||
MiscResources.ExitWithActiveOperations,
|
||||
MiscResources.ActiveOperations,
|
||||
MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2);
|
||||
MessageBoxButtons.YesNo, MessageBoxType.Warning, MessageBoxDefaultButton.No);
|
||||
if (result != DialogResult.Yes)
|
||||
{
|
||||
return false;
|
||||
@ -199,8 +200,9 @@ public class DesktopController
|
||||
{
|
||||
if (userClosing && !SkipRecoveryCleanup)
|
||||
{
|
||||
var result = MessageBox.Show(MiscResources.ExitWithUnsavedChanges, MiscResources.UnsavedChanges,
|
||||
MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2);
|
||||
var result = MessageBox.Show(_desktopFormProvider.DesktopForm, MiscResources.ExitWithUnsavedChanges,
|
||||
MiscResources.UnsavedChanges,
|
||||
MessageBoxButtons.YesNo, MessageBoxType.Warning, MessageBoxDefaultButton.No);
|
||||
if (result != DialogResult.Yes)
|
||||
{
|
||||
return false;
|
||||
@ -252,12 +254,13 @@ public class DesktopController
|
||||
{
|
||||
Invoker.Current.SafeInvoke(() =>
|
||||
{
|
||||
var formOnTop = Application.OpenForms.Cast<Form>().Last();
|
||||
if (formOnTop.WindowState == FormWindowState.Minimized)
|
||||
// TODO: xplat
|
||||
var formOnTop = Application.Instance.Windows.Last();
|
||||
if (formOnTop.WindowState == WindowState.Minimized)
|
||||
{
|
||||
Win32.ShowWindow(formOnTop.Handle, Win32.ShowWindowCommands.Restore);
|
||||
Win32.ShowWindow(formOnTop.NativeHandle, Win32.ShowWindowCommands.Restore);
|
||||
}
|
||||
formOnTop.Activate();
|
||||
formOnTop.BringToFront();
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -270,19 +273,20 @@ public class DesktopController
|
||||
{
|
||||
MessageBox.Show(_config.Get(c => c.StartupMessageText), _config.Get(c => c.StartupMessageTitle),
|
||||
MessageBoxButtons.OK,
|
||||
_config.Get(c => c.StartupMessageIcon).ToWinForms());
|
||||
_config.Get(c => c.StartupMessageIcon).ToEto());
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowRecoveryPrompt()
|
||||
{
|
||||
// Allow scanned images to be recovered in case of an unexpected close
|
||||
var op = _operationFactory.Create<RecoveryOperation>();
|
||||
if (op.Start(_desktopImagesController.ReceiveScannedImage(),
|
||||
new RecoveryParams { ThumbnailSize = _config.ThumbnailSize() }))
|
||||
{
|
||||
_operationProgress.ShowProgress(op);
|
||||
}
|
||||
// TODO: Eto implementation
|
||||
// var op = _operationFactory.Create<RecoveryOperation>();
|
||||
// if (op.Start(_desktopImagesController.ReceiveScannedImage(),
|
||||
// new RecoveryParams { ThumbnailSize = _config.ThumbnailSize() }))
|
||||
// {
|
||||
// _operationProgress.ShowProgress(op);
|
||||
// }
|
||||
}
|
||||
|
||||
private void InitThumbnailRendering()
|
||||
@ -338,9 +342,10 @@ public class DesktopController
|
||||
{
|
||||
if (_imageList.Images.Count > 0)
|
||||
{
|
||||
if (MessageBox.Show(string.Format(MiscResources.ConfirmClearItems, _imageList.Images.Count),
|
||||
if (MessageBox.Show(_desktopFormProvider.DesktopForm,
|
||||
string.Format(MiscResources.ConfirmClearItems, _imageList.Images.Count),
|
||||
MiscResources.Clear, MessageBoxButtons.OKCancel,
|
||||
MessageBoxIcon.Question) == DialogResult.OK)
|
||||
MessageBoxType.Question) == DialogResult.Ok)
|
||||
{
|
||||
_imageListActions.DeleteAll();
|
||||
}
|
||||
@ -351,9 +356,10 @@ public class DesktopController
|
||||
{
|
||||
if (_imageList.Selection.Any())
|
||||
{
|
||||
if (MessageBox.Show(string.Format(MiscResources.ConfirmDeleteItems, _imageList.Selection.Count),
|
||||
if (MessageBox.Show(_desktopFormProvider.DesktopForm,
|
||||
string.Format(MiscResources.ConfirmDeleteItems, _imageList.Selection.Count),
|
||||
MiscResources.Delete, MessageBoxButtons.OKCancel,
|
||||
MessageBoxIcon.Question) == DialogResult.OK)
|
||||
MessageBoxType.Question) == DialogResult.Ok)
|
||||
{
|
||||
_imageListActions.DeleteSelected();
|
||||
}
|
||||
@ -364,9 +370,10 @@ public class DesktopController
|
||||
{
|
||||
if (_imageList.Selection.Any())
|
||||
{
|
||||
if (MessageBox.Show(string.Format(MiscResources.ConfirmResetImages, _imageList.Selection.Count),
|
||||
if (MessageBox.Show(_desktopFormProvider.DesktopForm,
|
||||
string.Format(MiscResources.ConfirmResetImages, _imageList.Selection.Count),
|
||||
MiscResources.ResetImage,
|
||||
MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == DialogResult.OK)
|
||||
MessageBoxButtons.OKCancel, MessageBoxType.Question) == DialogResult.Ok)
|
||||
{
|
||||
_imageListActions.ResetTransforms();
|
||||
}
|
||||
@ -402,7 +409,7 @@ public class DesktopController
|
||||
using var imagesToEmail = images.Select(x => x.GetClonedImage()).ToDisposableList();
|
||||
await _exportHelper.EmailPDF(imagesToEmail.InnerList);
|
||||
}
|
||||
|
||||
|
||||
public async Task Print()
|
||||
{
|
||||
var state = _imageList.CurrentState;
|
||||
@ -414,34 +421,37 @@ public class DesktopController
|
||||
}
|
||||
}
|
||||
|
||||
public void Import()
|
||||
public void Import(Control parentForm)
|
||||
{
|
||||
// TODO: Merge this into exporthelper/dialoghelper?
|
||||
var ofd = new OpenFileDialog
|
||||
{
|
||||
Multiselect = true,
|
||||
MultiSelect = true,
|
||||
CheckFileExists = true,
|
||||
// TODO: Move filter logic somewhere common
|
||||
Filter = MiscResources.FileTypeAllFiles + @"|*.*|" +
|
||||
MiscResources.FileTypePdf + @"|*.pdf|" +
|
||||
MiscResources.FileTypeImageFiles +
|
||||
@"|*.bmp;*.emf;*.exif;*.gif;*.jpg;*.jpeg;*.png;*.tiff;*.tif|" +
|
||||
MiscResources.FileTypeBmp + @"|*.bmp|" +
|
||||
MiscResources.FileTypeEmf + @"|*.emf|" +
|
||||
MiscResources.FileTypeExif + @"|*.exif|" +
|
||||
MiscResources.FileTypeGif + @"|*.gif|" +
|
||||
MiscResources.FileTypeJpeg + @"|*.jpg;*.jpeg|" +
|
||||
MiscResources.FileTypePng + @"|*.png|" +
|
||||
MiscResources.FileTypeTiff + @"|*.tiff;*.tif"
|
||||
Filters =
|
||||
{
|
||||
// TODO: Move filter logic somewhere common
|
||||
new FileFilter(MiscResources.FileTypeAllFiles, ".*"),
|
||||
new FileFilter(MiscResources.FileTypePdf, ".pdf"),
|
||||
new FileFilter(MiscResources.FileTypeImageFiles,
|
||||
".bmp", ".emf", ".exif", ".gif", "jpg", ".jpeg", ".png", ".tiff", ".tif"),
|
||||
new FileFilter(MiscResources.FileTypeBmp, ".bmp"),
|
||||
new FileFilter(MiscResources.FileTypeEmf, ".emf"),
|
||||
new FileFilter(MiscResources.FileTypeExif, ".exif"),
|
||||
new FileFilter(MiscResources.FileTypeGif, ".gif"),
|
||||
new FileFilter(MiscResources.FileTypeJpeg, ".jpg", ".jpeg"),
|
||||
new FileFilter(MiscResources.FileTypePng, ".png"),
|
||||
new FileFilter(MiscResources.FileTypeTiff, ".tiff", ".tif"),
|
||||
}
|
||||
};
|
||||
if (Paths.IsTestAppDataPath)
|
||||
{
|
||||
// For UI test automation we choose the appdata folder to find the prepared files to import
|
||||
ofd.InitialDirectory = Paths.AppData;
|
||||
ofd.Directory = new Uri(Path.GetFullPath(Paths.AppData));
|
||||
}
|
||||
if (ofd.ShowDialog() == DialogResult.OK)
|
||||
if (ofd.ShowDialog(parentForm) == DialogResult.Ok)
|
||||
{
|
||||
ImportFiles(ofd.FileNames);
|
||||
ImportFiles(ofd.Filenames);
|
||||
}
|
||||
}
|
||||
}
|
20
NAPS2.Lib.Common/WinForms/IDesktopSubFormController.cs
Normal file
20
NAPS2.Lib.Common/WinForms/IDesktopSubFormController.cs
Normal file
@ -0,0 +1,20 @@
|
||||
namespace NAPS2.WinForms;
|
||||
|
||||
public interface IDesktopSubFormController
|
||||
{
|
||||
void ShowCropForm();
|
||||
void ShowBrightnessContrastForm();
|
||||
void ShowHueSaturationForm();
|
||||
void ShowBlackWhiteForm();
|
||||
void ShowSharpenForm();
|
||||
void ShowRotateForm();
|
||||
void ShowProfilesForm();
|
||||
void ShowOcrForm();
|
||||
void ShowBatchScanForm();
|
||||
void ShowViewerForm();
|
||||
void ShowPdfSettingsForm();
|
||||
void ShowImageSettingsForm();
|
||||
void ShowEmailSettingsForm();
|
||||
void ShowAboutForm();
|
||||
void ShowSettingsForm();
|
||||
}
|
@ -18,7 +18,7 @@ public class WinFormsDesktopForm : DesktopForm
|
||||
|
||||
public WinFormsDesktopForm(
|
||||
Naps2Config config,
|
||||
KeyboardShortcutManager ksm,
|
||||
// KeyboardShortcutManager ksm,
|
||||
INotificationManager notify,
|
||||
CultureHelper cultureHelper,
|
||||
IProfileManager profileManager,
|
||||
@ -31,7 +31,7 @@ public class WinFormsDesktopForm : DesktopForm
|
||||
ImageListActions imageListActions,
|
||||
DesktopFormProvider desktopFormProvider,
|
||||
DesktopSubFormController desktopSubFormController)
|
||||
: base(config, ksm, notify, cultureHelper, profileManager,
|
||||
: base(config, /*ksm,*/ notify, cultureHelper, profileManager,
|
||||
imageList, imageTransfer, thumbnailRenderQueue, thumbnailProvider, desktopController, desktopScanController,
|
||||
imageListActions, desktopFormProvider, desktopSubFormController)
|
||||
{
|
||||
|
@ -1,10 +1,11 @@
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Windows.Forms;
|
||||
using Eto.Drawing;
|
||||
using Eto.Forms;
|
||||
using Eto.WinForms;
|
||||
using Eto.WinForms.Forms.Controls;
|
||||
using NAPS2.Images.Gdi;
|
||||
using Bitmap = Eto.Drawing.Bitmap;
|
||||
using sd = System.Drawing;
|
||||
|
||||
namespace NAPS2.EtoForms.WinForms;
|
||||
|
||||
@ -13,7 +14,7 @@ public class WinFormsEtoPlatform : EtoPlatform
|
||||
private const int MIN_BUTTON_WIDTH = 75;
|
||||
private const int MIN_BUTTON_HEIGHT = 32;
|
||||
private const int IMAGE_PADDING = 5;
|
||||
|
||||
|
||||
static WinFormsEtoPlatform()
|
||||
{
|
||||
ButtonHandler.DefaultMinimumSize = new Eto.Drawing.Size(MIN_BUTTON_WIDTH, MIN_BUTTON_HEIGHT);
|
||||
@ -28,15 +29,15 @@ public class WinFormsEtoPlatform : EtoPlatform
|
||||
{
|
||||
var native = (System.Windows.Forms.Button) button.ToNative();
|
||||
native.TextImageRelation = TextImageRelation.Overlay;
|
||||
native.ImageAlign = ContentAlignment.MiddleLeft;
|
||||
native.TextAlign = ContentAlignment.MiddleRight;
|
||||
native.ImageAlign = sd.ContentAlignment.MiddleLeft;
|
||||
native.TextAlign = sd.ContentAlignment.MiddleRight;
|
||||
|
||||
var imageWidth = native.Image.Width;
|
||||
using var g = native.CreateGraphics();
|
||||
var textWidth = (int) g.MeasureString(native.Text, native.Font).Width;
|
||||
native.AutoSize = false;
|
||||
|
||||
var widthWithoutRightPadding = imageWidth + textWidth + IMAGE_PADDING + 15;
|
||||
var widthWithoutRightPadding = imageWidth + textWidth + IMAGE_PADDING + 15;
|
||||
native.Width = Math.Max(widthWithoutRightPadding + IMAGE_PADDING, ButtonHandler.DefaultMinimumSize.Width);
|
||||
var rightPadding = IMAGE_PADDING + (native.Width - widthWithoutRightPadding - IMAGE_PADDING) / 2;
|
||||
native.Padding = native.Padding with { Left = IMAGE_PADDING, Right = rightPadding };
|
||||
@ -47,4 +48,29 @@ public class WinFormsEtoPlatform : EtoPlatform
|
||||
{
|
||||
return ((GdiImage) image).Bitmap.ToEto();
|
||||
}
|
||||
|
||||
public override IMemoryImage DrawHourglass(IMemoryImage image)
|
||||
{
|
||||
var bitmap = new System.Drawing.Bitmap(image.Width, image.Height);
|
||||
using (var g = sd.Graphics.FromImage(bitmap))
|
||||
{
|
||||
var attrs = new ImageAttributes();
|
||||
attrs.SetColorMatrix(new ColorMatrix
|
||||
{
|
||||
Matrix33 = 0.3f
|
||||
});
|
||||
g.DrawImage(image.AsBitmap(),
|
||||
new sd.Rectangle(0, 0, bitmap.Width, bitmap.Height),
|
||||
0,
|
||||
0,
|
||||
image.Width,
|
||||
image.Height,
|
||||
sd.GraphicsUnit.Pixel,
|
||||
attrs);
|
||||
using var hourglass = new sd.Bitmap(new MemoryStream(Icons.hourglass_grey));
|
||||
g.DrawImage(hourglass, new sd.Rectangle((bitmap.Width - 32) / 2, (bitmap.Height - 32) / 2, 32, 32));
|
||||
}
|
||||
image.Dispose();
|
||||
return new GdiImage(bitmap);
|
||||
}
|
||||
}
|
@ -263,7 +263,7 @@ public class WinFormsListView<T> : IListView<T> where T : notnull
|
||||
var index = GetDragIndex(e);
|
||||
if (index != -1)
|
||||
{
|
||||
Drop?.Invoke(this, new DropEventArgs(index, e.Data));
|
||||
Drop?.Invoke(this, new DropEventArgs(index, e.Data.ToEto()));
|
||||
}
|
||||
_view.InsertionMark.Index = -1;
|
||||
}
|
||||
|
@ -1,23 +0,0 @@
|
||||
using System.Drawing.Printing;
|
||||
|
||||
namespace NAPS2.ImportExport;
|
||||
|
||||
public interface IScannedImagePrinter
|
||||
{
|
||||
/// <summary>
|
||||
/// Prints the provided images, prompting the user for the printer settings.
|
||||
/// </summary>
|
||||
/// <param name="images">The full list of images to print.</param>
|
||||
/// <param name="selectedImages">The list of selected images. If non-empty, the user will be presented an option to print selected.</param>
|
||||
/// <returns>True if the print completed, false if there was nothing to print or the user cancelled.</returns>
|
||||
Task<bool> PromptToPrint(IList<ProcessedImage> images, IList<ProcessedImage> selectedImages);
|
||||
|
||||
/// <summary>
|
||||
/// Prints the provided images with the specified printer settings.
|
||||
/// </summary>
|
||||
/// <param name="printerSettings">The printer settings.</param>
|
||||
/// <param name="images">The full list of images to print.</param>
|
||||
/// <param name="selectedImages">The list of selected images, to be used if the printer settings specify to print selected.</param>
|
||||
/// <returns>True if the print completed, false if there was nothing to print.</returns>
|
||||
Task<bool> Print(PrinterSettings printerSettings, IList<ProcessedImage> images, IList<ProcessedImage> selectedImages);
|
||||
}
|
@ -30,6 +30,7 @@ public class WinFormsModule : NinjectModule
|
||||
Bind<IUpdateChecker>().To<UpdateChecker>();
|
||||
Bind<IWinFormsExportHelper>().To<WinFormsExportHelper>();
|
||||
Bind<IDesktopScanController>().To<DesktopScanController>();
|
||||
Bind<IDesktopSubFormController>().To<DesktopSubFormController>();
|
||||
Bind<DesktopFormProvider>().ToSelf().InSingletonScope();
|
||||
|
||||
Bind<DesktopForm>().To<WinFormsDesktopForm>();
|
||||
|
@ -362,6 +362,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="ImportExport\Images" />
|
||||
<Folder Include="Scan\Internal" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -10,12 +10,12 @@ public class DesktopScanController : IDesktopScanController
|
||||
private readonly IFormFactory _formFactory;
|
||||
private readonly IScanPerformer _scanPerformer;
|
||||
private readonly DesktopImagesController _desktopImagesController;
|
||||
private readonly DesktopSubFormController _desktopSubFormController;
|
||||
private readonly IDesktopSubFormController _desktopSubFormController;
|
||||
private readonly DesktopFormProvider _desktopFormProvider;
|
||||
|
||||
public DesktopScanController(Naps2Config config, IProfileManager profileManager, IFormFactory formFactory,
|
||||
IScanPerformer scanPerformer, DesktopImagesController desktopImagesController,
|
||||
DesktopSubFormController desktopSubFormController, DesktopFormProvider desktopFormProvider)
|
||||
IDesktopSubFormController desktopSubFormController, DesktopFormProvider desktopFormProvider)
|
||||
{
|
||||
_config = config;
|
||||
_profileManager = profileManager;
|
||||
|
@ -3,7 +3,7 @@ using NAPS2.Ocr;
|
||||
|
||||
namespace NAPS2.WinForms;
|
||||
|
||||
public class DesktopSubFormController
|
||||
public class DesktopSubFormController : IDesktopSubFormController
|
||||
{
|
||||
private readonly IFormFactory _formFactory;
|
||||
private readonly UiImageList _imageList;
|
||||
@ -19,7 +19,14 @@ public class DesktopSubFormController
|
||||
_tesseractLanguageManager = tesseractLanguageManager;
|
||||
}
|
||||
|
||||
public void ShowImageForm<T>() where T : ImageForm
|
||||
public void ShowCropForm() => ShowImageForm<FCrop>();
|
||||
public void ShowBrightnessContrastForm() => ShowImageForm<FBrightnessContrast>();
|
||||
public void ShowHueSaturationForm() => ShowImageForm<FHueSaturation>();
|
||||
public void ShowBlackWhiteForm() => ShowImageForm<FBlackWhite>();
|
||||
public void ShowSharpenForm() => ShowImageForm<FSharpen>();
|
||||
public void ShowRotateForm() => ShowImageForm<FRotate>();
|
||||
|
||||
private void ShowImageForm<T>() where T : ImageForm
|
||||
{
|
||||
var selection = _imageList.Selection.ToList();
|
||||
if (selection.Any())
|
||||
|
@ -7,7 +7,11 @@ using NAPS2.EtoForms.WinForms;
|
||||
using NAPS2.ImportExport;
|
||||
using NAPS2.Ocr;
|
||||
using NAPS2.ImportExport.Images;
|
||||
using DataFormats = System.Windows.Forms.DataFormats;
|
||||
using DockStyle = System.Windows.Forms.DockStyle;
|
||||
using KeyEventArgs = System.Windows.Forms.KeyEventArgs;
|
||||
using Keys = System.Windows.Forms.Keys;
|
||||
using MouseEventArgs = System.Windows.Forms.MouseEventArgs;
|
||||
|
||||
namespace NAPS2.WinForms;
|
||||
|
||||
@ -504,7 +508,7 @@ public partial class FDesktop : FormBase
|
||||
}
|
||||
}
|
||||
|
||||
private void tsImport_Click(object sender, EventArgs e) => _desktopController.Import();
|
||||
private void tsImport_Click(object sender, EventArgs e) => _desktopController.Import(Eto.Forms.WinFormsHelpers.ToEto(this));
|
||||
|
||||
private async void tsdSavePDF_ButtonClick(object sender, EventArgs e)
|
||||
{
|
||||
@ -625,18 +629,18 @@ public partial class FDesktop : FormBase
|
||||
#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<FCrop>();
|
||||
private void tsCrop_Click(object sender, EventArgs e) => _desktopSubFormController.ShowCropForm();
|
||||
|
||||
private void tsBrightnessContrast_Click(object sender, EventArgs e) =>
|
||||
_desktopSubFormController.ShowImageForm<FBrightnessContrast>();
|
||||
_desktopSubFormController.ShowBrightnessContrastForm();
|
||||
|
||||
private void tsHueSaturation_Click(object sender, EventArgs e) =>
|
||||
_desktopSubFormController.ShowImageForm<FHueSaturation>();
|
||||
_desktopSubFormController.ShowHueSaturationForm();
|
||||
|
||||
private void tsBlackWhite_Click(object sender, EventArgs e) =>
|
||||
_desktopSubFormController.ShowImageForm<FBlackWhite>();
|
||||
_desktopSubFormController.ShowBlackWhiteForm();
|
||||
|
||||
private void tsSharpen_Click(object sender, EventArgs e) => _desktopSubFormController.ShowImageForm<FSharpen>();
|
||||
private void tsSharpen_Click(object sender, EventArgs e) => _desktopSubFormController.ShowSharpenForm();
|
||||
private void tsReset_Click(object sender, EventArgs e) => _desktopController.ResetImage();
|
||||
|
||||
#endregion
|
||||
@ -647,7 +651,7 @@ public partial class FDesktop : FormBase
|
||||
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<FRotate>();
|
||||
private void tsCustomRotation_Click(object sender, EventArgs e) => _desktopSubFormController.ShowRotateForm();
|
||||
|
||||
#endregion
|
||||
|
||||
@ -750,9 +754,9 @@ public partial class FDesktop : FormBase
|
||||
|
||||
private void ListViewDrop(object? sender, DropEventArgs args)
|
||||
{
|
||||
if (_imageTransfer.IsIn(args.Data.ToEto()))
|
||||
if (_imageTransfer.IsIn(args.Data))
|
||||
{
|
||||
var data = _imageTransfer.GetFrom(args.Data.ToEto());
|
||||
var data = _imageTransfer.GetFrom(args.Data);
|
||||
if (data.ProcessId == Process.GetCurrentProcess().Id)
|
||||
{
|
||||
DragMoveImages(args.Position);
|
||||
@ -762,9 +766,9 @@ public partial class FDesktop : FormBase
|
||||
_desktopController.ImportDirect(data, false);
|
||||
}
|
||||
}
|
||||
else if (args.Data.GetDataPresent(DataFormats.FileDrop))
|
||||
else if (args.Data.Contains(DataFormats.FileDrop))
|
||||
{
|
||||
var data = (string[]) args.Data.GetData(DataFormats.FileDrop);
|
||||
var data = args.Data.GetObject<string[]>(DataFormats.FileDrop);
|
||||
_desktopController.ImportFiles(data);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user