Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/sn4k3/UVtools.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md13
-rw-r--r--UVtools.Core/Layer/Layer.cs2
-rw-r--r--UVtools.Core/Objects/Kernel.cs2
-rw-r--r--UVtools.Core/Operations/Operation.cs61
-rw-r--r--UVtools.Core/Operations/OperationArithmetic.cs28
-rw-r--r--UVtools.Core/Operations/OperationBlur.cs44
-rw-r--r--UVtools.Core/Operations/OperationCalculator.cs5
-rw-r--r--UVtools.Core/Operations/OperationChangeResolution.cs34
-rw-r--r--UVtools.Core/Operations/OperationEditParameters.cs8
-rw-r--r--UVtools.Core/Operations/OperationFlip.cs50
-rw-r--r--UVtools.Core/Operations/OperationLayerClone.cs9
-rw-r--r--UVtools.Core/Operations/OperationLayerImport.cs7
-rw-r--r--UVtools.Core/Operations/OperationLayerReHeight.cs7
-rw-r--r--UVtools.Core/Operations/OperationLayerRemove.cs9
-rw-r--r--UVtools.Core/Operations/OperationMask.cs6
-rw-r--r--UVtools.Core/Operations/OperationMorph.cs50
-rw-r--r--UVtools.Core/Operations/OperationMove.cs3
-rw-r--r--UVtools.Core/Operations/OperationPattern.cs5
-rw-r--r--UVtools.Core/Operations/OperationPixelDimming.cs37
-rw-r--r--UVtools.Core/Operations/OperationRepairLayers.cs9
-rw-r--r--UVtools.Core/Operations/OperationResize.cs41
-rw-r--r--UVtools.Core/Operations/OperationRotate.cs30
-rw-r--r--UVtools.Core/Operations/OperationSolidify.cs5
-rw-r--r--UVtools.Core/Operations/OperationThreshold.cs36
-rw-r--r--UVtools.Core/UVtools.Core.csproj2
-rw-r--r--UVtools.WPF/App.axaml.cs24
-rw-r--r--UVtools.WPF/Controls/Tools/ToolArithmeticControl.axaml.cs4
-rw-r--r--UVtools.WPF/Controls/Tools/ToolBlurControl.axaml.cs4
-rw-r--r--UVtools.WPF/Controls/Tools/ToolCalculatorControl.axaml.cs4
-rw-r--r--UVtools.WPF/Controls/Tools/ToolChangeResolutionControl.axaml.cs4
-rw-r--r--UVtools.WPF/Controls/Tools/ToolControl.axaml.cs20
-rw-r--r--UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml.cs4
-rw-r--r--UVtools.WPF/Controls/Tools/ToolFlipControl.axaml.cs4
-rw-r--r--UVtools.WPF/Controls/Tools/ToolLayerCloneControl.axaml.cs4
-rw-r--r--UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml.cs8
-rw-r--r--UVtools.WPF/Controls/Tools/ToolLayerReHeightControl.axaml.cs4
-rw-r--r--UVtools.WPF/Controls/Tools/ToolLayerRemoveControl.axaml.cs4
-rw-r--r--UVtools.WPF/Controls/Tools/ToolMaskControl.axaml.cs4
-rw-r--r--UVtools.WPF/Controls/Tools/ToolMorphControl.axaml.cs5
-rw-r--r--UVtools.WPF/Controls/Tools/ToolMoveControl.axaml.cs4
-rw-r--r--UVtools.WPF/Controls/Tools/ToolPatternControl.axaml.cs4
-rw-r--r--UVtools.WPF/Controls/Tools/ToolPixelDimmingControl.axaml.cs4
-rw-r--r--UVtools.WPF/Controls/Tools/ToolRepairLayersControl.axaml.cs4
-rw-r--r--UVtools.WPF/Controls/Tools/ToolResizeControl.axaml.cs7
-rw-r--r--UVtools.WPF/Controls/Tools/ToolRotateControl.axaml2
-rw-r--r--UVtools.WPF/Controls/Tools/ToolRotateControl.axaml.cs4
-rw-r--r--UVtools.WPF/Controls/Tools/ToolThresholdControl.axaml.cs4
-rw-r--r--UVtools.WPF/MainWindow.axaml.cs44
-rw-r--r--UVtools.WPF/Program.cs20
-rw-r--r--UVtools.WPF/Structures/AppVersionChecker.cs188
-rw-r--r--UVtools.WPF/Structures/OperationProfiles.cs270
-rw-r--r--UVtools.WPF/UVtools.WPF.csproj3
-rw-r--r--UVtools.WPF/UserSettings.cs12
-rw-r--r--UVtools.WPF/Windows/ToolWindow.axaml100
-rw-r--r--UVtools.WPF/Windows/ToolWindow.axaml.cs191
55 files changed, 1342 insertions, 119 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 21a5ea9..fa94ac5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,18 @@
# Changelog
+## 05/11/2020 - v1.1.3
+
+* (Add) Auto-updater: When a new version is detected UVtools still show the same green button at top,
+on click, it will prompt for auto or manual update.
+On Linux and Mac the script will kill all UVtools instances and auto-upgrade.
+On Windows the user must close all instances and continue with the shown MSI installation
+* (Add) Tool profiles: Create and remove named presets for some tools
+* (Add) Event handler for handling non-UI thread exceptions
+* (Fix) Mac: File - Open in a new window was not working
+* (Fix) Tool - Rotate: Allow negative angles
+* (Fix) Tool - Rotate: The operation was inverting the angle
+* (Fix) Tools: Select normal layers can crash the program with small files with low layer count, eg: 3 layers total
+
## 02/11/2020 - v1.1.2
* (Add) Program start elapsed seconds on Log
diff --git a/UVtools.Core/Layer/Layer.cs b/UVtools.Core/Layer/Layer.cs
index 342f119..e60d856 100644
--- a/UVtools.Core/Layer/Layer.cs
+++ b/UVtools.Core/Layer/Layer.cs
@@ -685,7 +685,7 @@ namespace UVtools.Core
var halfHeight = target.Height / 2.0f;
using (var translateTransform = new Matrix<double>(2, 3))
{
- CvInvoke.GetRotationMatrix2D(new PointF(halfWidth, halfHeight), (double) operation.AngleDegrees, 1.0, translateTransform);
+ CvInvoke.GetRotationMatrix2D(new PointF(halfWidth, halfHeight), (double) -operation.AngleDegrees, 1.0, translateTransform);
/*var rect = new RotatedRect(PointF.Empty, mat.Size, (float) angle).MinAreaRect();
translateTransform[0, 2] += rect.Width / 2.0 - mat.Cols / 2.0;
translateTransform[0, 2] += rect.Height / 2.0 - mat.Rows / 2.0;*/
diff --git a/UVtools.Core/Objects/Kernel.cs b/UVtools.Core/Objects/Kernel.cs
index f57c93c..b73a0a5 100644
--- a/UVtools.Core/Objects/Kernel.cs
+++ b/UVtools.Core/Objects/Kernel.cs
@@ -6,11 +6,13 @@
* of this license document, but changing it is not allowed.
*/
+using System;
using System.Drawing;
using Emgu.CV;
namespace UVtools.Core.Objects
{
+ [Serializable]
public sealed class Kernel
{
public Matrix<byte> Matrix { get; set; }
diff --git a/UVtools.Core/Operations/Operation.cs b/UVtools.Core/Operations/Operation.cs
index b9d374c..385b6a9 100644
--- a/UVtools.Core/Operations/Operation.cs
+++ b/UVtools.Core/Operations/Operation.cs
@@ -6,17 +6,22 @@
* of this license document, but changing it is not allowed.
*/
+using System;
using System.Drawing;
+using System.Xml.Serialization;
using Emgu.CV;
using UVtools.Core.Objects;
namespace UVtools.Core.Operations
{
+ [Serializable]
public abstract class Operation : BindableBase
{
private Rectangle _roi = Rectangle.Empty;
private uint _layerIndexEnd;
private uint _layerIndexStart;
+ private string _profileName;
+ private Enumerations.LayerRangeSelection _layerRangeSelection = Enumerations.LayerRangeSelection.All;
public const byte ClassNameLength = 9;
/// <summary>
@@ -24,7 +29,16 @@ namespace UVtools.Core.Operations
/// </summary>
public string Id => GetType().Name.Remove(0, ClassNameLength);
- public virtual Enumerations.LayerRangeSelection LayerRangeSelection => Enumerations.LayerRangeSelection.All;
+ public virtual Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.All;
+
+ /// <summary>
+ /// Gets the last used layer range selection, returns none if custom
+ /// </summary>
+ public Enumerations.LayerRangeSelection LayerRangeSelection
+ {
+ get => _layerRangeSelection;
+ set => RaiseAndSetIfChanged(ref _layerRangeSelection, value);
+ }
/// <summary>
/// Gets if this operation should set layer range to the actual layer index on layer preview
@@ -32,9 +46,14 @@ namespace UVtools.Core.Operations
public virtual bool PassActualLayerIndex => false;
/// <summary>
- /// Gets if this control can make use of ROI
+ /// Gets if this operation can make use of ROI
+ /// </summary>
+ public virtual bool CanROI => true;
+
+ /// <summary>
+ /// Gets if this operation can store profiles
/// </summary>
- public virtual bool CanROI { get; set; } = true;
+ public virtual bool CanHaveProfiles => true;
/// <summary>
/// Gets if this operation supports cancellation
@@ -106,8 +125,18 @@ namespace UVtools.Core.Operations
public uint LayerRangeCount => LayerIndexEnd - LayerIndexStart + 1;
/// <summary>
+ /// Gets the name for this profile
+ /// </summary>
+ public string ProfileName
+ {
+ get => _profileName;
+ set => RaiseAndSetIfChanged(ref _profileName, value);
+ }
+
+ /// <summary>
/// Gets or sets an ROI to process this operation
/// </summary>
+ [XmlIgnore]
public Rectangle ROI
{
get => _roi;
@@ -120,5 +149,31 @@ namespace UVtools.Core.Operations
{
return HaveROI ? new Mat(defaultMat, ROI) : defaultMat;
}
+
+ public virtual Operation Clone()
+ {
+ return MemberwiseClone() as Operation;
+ }
+
+ public override string ToString()
+ {
+ if (!string.IsNullOrEmpty(ProfileName)) return ProfileName;
+
+ var result = $"{Title}: {LayerRangeString}";
+ return result;
+ }
+
+ public virtual string LayerRangeString
+ {
+ get
+ {
+ if (LayerRangeSelection == Enumerations.LayerRangeSelection.None)
+ {
+ return $" [Layers: {LayerIndexStart}-{LayerIndexEnd}]";
+ }
+
+ return $" [Layers: {LayerRangeSelection}]";
+ }
+ }
}
}
diff --git a/UVtools.Core/Operations/OperationArithmetic.cs b/UVtools.Core/Operations/OperationArithmetic.cs
index bb55b8a..00f9f61 100644
--- a/UVtools.Core/Operations/OperationArithmetic.cs
+++ b/UVtools.Core/Operations/OperationArithmetic.cs
@@ -9,11 +9,13 @@
using System;
using System.Collections.Generic;
using System.Text;
+using System.Xml.Serialization;
using Emgu.CV;
using UVtools.Core.Objects;
namespace UVtools.Core.Operations
{
+ [Serializable]
public class OperationArithmetic : Operation
{
private string _sentence;
@@ -85,8 +87,10 @@ namespace UVtools.Core.Operations
set => RaiseAndSetIfChanged(ref _sentence, value);
}
+ [XmlIgnore]
public List<ArithmeticOperation> Operations { get; } = new List<ArithmeticOperation>();
+ [XmlIgnore]
public List<uint> SetLayers { get; } = new List<uint>();
public bool IsValid => SetLayers.Count > 0 & Operations.Count > 0;
@@ -181,9 +185,29 @@ namespace UVtools.Core.Operations
return true;
}
- public OperationArithmetic()
+ public override string ToString()
{
-
+ var result = $"{_sentence}" + LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
+
+ protected bool Equals(OperationArithmetic other)
+ {
+ return _sentence == other._sentence;
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (obj is null) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != GetType()) return false;
+ return Equals((OperationArithmetic) obj);
+ }
+
+ public override int GetHashCode()
+ {
+ return (_sentence != null ? _sentence.GetHashCode() : 0);
}
}
}
diff --git a/UVtools.Core/Operations/OperationBlur.cs b/UVtools.Core/Operations/OperationBlur.cs
index e3042d1..f8c1e58 100644
--- a/UVtools.Core/Operations/OperationBlur.cs
+++ b/UVtools.Core/Operations/OperationBlur.cs
@@ -6,11 +6,14 @@
* of this license document, but changing it is not allowed.
*/
+using System;
using System.Text;
+using System.Xml.Serialization;
using UVtools.Core.Objects;
namespace UVtools.Core.Operations
{
+ [Serializable]
public sealed class OperationBlur : Operation
{
private BlurAlgorithm _blurOperation = BlurAlgorithm.Blur;
@@ -80,6 +83,19 @@ namespace UVtools.Core.Operations
new StringTag("Filter 2D: Applies an arbitrary linear filter to an image", BlurAlgorithm.Filter2D),
};
+ public byte BlurTypeIndex
+ {
+ get
+ {
+ for (byte i = 0; i < BlurTypes.Length; i++)
+ {
+ if ((BlurAlgorithm)BlurTypes[i].Tag == BlurOperation) return i;
+ }
+
+ return 0;
+ }
+ }
+
public BlurAlgorithm BlurOperation
{
get => _blurOperation;
@@ -92,10 +108,36 @@ namespace UVtools.Core.Operations
set => RaiseAndSetIfChanged(ref _size, value);
}
+ [XmlIgnore]
public Kernel Kernel { get; set; } = new Kernel();
+ public override string ToString()
+ {
+ var result = $"[{_blurOperation}] [Size: {_size}]" + LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
+
#endregion
-
+ #region Equality
+ private bool Equals(OperationBlur other)
+ {
+ return _blurOperation == other._blurOperation && _size == other._size;
+ }
+
+ public override bool Equals(object obj)
+ {
+ return ReferenceEquals(this, obj) || obj is OperationBlur other && Equals(other);
+ }
+
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ return ((int) _blurOperation * 397) ^ (int) _size;
+ }
+ }
+ #endregion
}
}
diff --git a/UVtools.Core/Operations/OperationCalculator.cs b/UVtools.Core/Operations/OperationCalculator.cs
index 06f0687..b88274c 100644
--- a/UVtools.Core/Operations/OperationCalculator.cs
+++ b/UVtools.Core/Operations/OperationCalculator.cs
@@ -12,6 +12,7 @@ using UVtools.Core.Objects;
namespace UVtools.Core.Operations
{
+ [Serializable]
public class OperationCalculator : Operation
{
public override string Title => "Calculator";
@@ -23,9 +24,11 @@ namespace UVtools.Core.Operations
public override string ProgressAction => null;
- public override Enumerations.LayerRangeSelection LayerRangeSelection => Enumerations.LayerRangeSelection.None;
+ public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
public override bool CanROI => false;
+ public override bool CanHaveProfiles => false;
+
public MillimetersToPixels CalcMillimetersToPixels { get; set; }
public LightOffDelayC CalcLightOffDelay { get; set; }
diff --git a/UVtools.Core/Operations/OperationChangeResolution.cs b/UVtools.Core/Operations/OperationChangeResolution.cs
index 9fa8eb1..da9160c 100644
--- a/UVtools.Core/Operations/OperationChangeResolution.cs
+++ b/UVtools.Core/Operations/OperationChangeResolution.cs
@@ -13,6 +13,7 @@ using UVtools.Core.Objects;
namespace UVtools.Core.Operations
{
+ [Serializable]
public sealed class OperationChangeResolution : Operation
{
private uint _newResolutionX;
@@ -48,8 +49,8 @@ namespace UVtools.Core.Operations
#region Overrides
- public override Enumerations.LayerRangeSelection LayerRangeSelection => Enumerations.LayerRangeSelection.None;
- public override bool CanROI { get; set; } = false;
+ public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
+ public override bool CanROI => false;
public override string Title => "Change print resolution";
public override string Description =>
"Crops or resizes all layer images to fit an alternate print area\n\n" +
@@ -145,6 +146,35 @@ namespace UVtools.Core.Operations
public static Resolution[] Presets => GetResolutions();
+ public override string ToString()
+ {
+ var result = $"{_newResolutionX} x {_newResolutionY}";
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
+
+ #endregion
+
+ #region Equality
+
+ private bool Equals(OperationChangeResolution other)
+ {
+ return _newResolutionX == other._newResolutionX && _newResolutionY == other._newResolutionY;
+ }
+
+ public override bool Equals(object obj)
+ {
+ return ReferenceEquals(this, obj) || obj is OperationChangeResolution other && Equals(other);
+ }
+
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ return ((int) _newResolutionX * 397) ^ (int) _newResolutionY;
+ }
+ }
+
#endregion
}
diff --git a/UVtools.Core/Operations/OperationEditParameters.cs b/UVtools.Core/Operations/OperationEditParameters.cs
index 0c4dd3f..7f774a6 100644
--- a/UVtools.Core/Operations/OperationEditParameters.cs
+++ b/UVtools.Core/Operations/OperationEditParameters.cs
@@ -6,6 +6,7 @@
* of this license document, but changing it is not allowed.
*/
+using System;
using System.Linq;
using System.Text;
using UVtools.Core.FileFormats;
@@ -13,13 +14,14 @@ using UVtools.Core.Objects;
namespace UVtools.Core.Operations
{
+ [Serializable]
public class OperationEditParameters : Operation
{
private bool _perLayerOverride;
- public override Enumerations.LayerRangeSelection LayerRangeSelection => Enumerations.LayerRangeSelection.None;
+ public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
- public override bool CanROI { get; set; } = false;
+ public override bool CanROI => false;
public override string Title => "Edit print parameters";
@@ -58,6 +60,8 @@ namespace UVtools.Core.Operations
public override string ProgressAction => "Changing print parameters";
+ public override bool CanHaveProfiles => false;
+
public override StringTag Validate(params object[] parameters)
{
var sb = new StringBuilder();
diff --git a/UVtools.Core/Operations/OperationFlip.cs b/UVtools.Core/Operations/OperationFlip.cs
index fad6768..aeadc67 100644
--- a/UVtools.Core/Operations/OperationFlip.cs
+++ b/UVtools.Core/Operations/OperationFlip.cs
@@ -11,8 +11,12 @@ using Emgu.CV.CvEnum;
namespace UVtools.Core.Operations
{
+ [Serializable]
public class OperationFlip : Operation
{
+ private bool _makeCopy;
+ private Enumerations.FlipDirection _flipDirection = Enumerations.FlipDirection.Horizontally;
+
public override string Title => "Flip";
public override string Description =>
"Flip the layers of the model vertically and/or horizontally.";
@@ -29,9 +33,19 @@ namespace UVtools.Core.Operations
public override string ProgressAction => "Flipped layers";
- public Enumerations.FlipDirection FlipDirection { get; set; } = Enumerations.FlipDirection.Horizontally;
+ public Enumerations.FlipDirection FlipDirection
+ {
+ get => _flipDirection;
+ set => RaiseAndSetIfChanged(ref _flipDirection, value);
+ }
+
+ public bool MakeCopy
+ {
+ get => _makeCopy;
+ set => RaiseAndSetIfChanged(ref _makeCopy, value);
+ }
- public bool MakeCopy { get; set; }
+ public static Array FlipDirections => Enum.GetValues(typeof(Enumerations.FlipDirection));
public FlipType FlipTypeOpenCV
{
@@ -55,6 +69,36 @@ namespace UVtools.Core.Operations
}
}
- public static Array FlipDirections => Enum.GetValues(typeof(Enumerations.FlipDirection));
+ public override string ToString()
+ {
+ var result = $"[{_flipDirection}] [Blend: {_makeCopy}]" + LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
+
+ #region Equality
+
+ protected bool Equals(OperationFlip other)
+ {
+ return _makeCopy == other._makeCopy && _flipDirection == other._flipDirection;
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (obj is null) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != GetType()) return false;
+ return Equals((OperationFlip) obj);
+ }
+
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ return (_makeCopy.GetHashCode() * 397) ^ (int) _flipDirection;
+ }
+ }
+
+ #endregion
}
}
diff --git a/UVtools.Core/Operations/OperationLayerClone.cs b/UVtools.Core/Operations/OperationLayerClone.cs
index e1e896c..2271135 100644
--- a/UVtools.Core/Operations/OperationLayerClone.cs
+++ b/UVtools.Core/Operations/OperationLayerClone.cs
@@ -5,19 +5,22 @@
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
+
+using System;
using System.Text;
using UVtools.Core.Objects;
namespace UVtools.Core.Operations
{
+ [Serializable]
public sealed class OperationLayerClone : Operation
{
private uint _clones = 1;
#region Overrides
- public override Enumerations.LayerRangeSelection LayerRangeSelection => Enumerations.LayerRangeSelection.Current;
- public override bool CanROI { get; set; } = false;
+ public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.Current;
+ public override bool CanROI => false;
public override bool PassActualLayerIndex => true;
public override string Title => "Clone layers";
@@ -34,6 +37,8 @@ namespace UVtools.Core.Operations
public override bool CanCancel => false;
+ public override bool CanHaveProfiles => false;
+
public override StringTag Validate(params object[] parameters)
{
var sb = new StringBuilder();
diff --git a/UVtools.Core/Operations/OperationLayerImport.cs b/UVtools.Core/Operations/OperationLayerImport.cs
index 07099f0..df45ad4 100644
--- a/UVtools.Core/Operations/OperationLayerImport.cs
+++ b/UVtools.Core/Operations/OperationLayerImport.cs
@@ -20,6 +20,7 @@ using UVtools.Core.Objects;
namespace UVtools.Core.Operations
{
+ [Serializable]
public sealed class OperationLayerImport : Operation
{
private uint _insertAfterLayerIndex;
@@ -31,8 +32,8 @@ namespace UVtools.Core.Operations
#region Overrides
- public override Enumerations.LayerRangeSelection LayerRangeSelection => Enumerations.LayerRangeSelection.None;
- public override bool CanROI { get; set; } = false;
+ public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
+ public override bool CanROI => false;
public override string Title => "Import Layers";
public override string Description =>
@@ -51,6 +52,8 @@ namespace UVtools.Core.Operations
public override uint LayerIndexStart => InsertAfterLayerIndex + (ReplaceStartLayer ? 0u : 1);
public override uint LayerIndexEnd => (uint)(LayerIndexStart + Count - 1);
+ public override bool CanHaveProfiles => false;
+
public override StringTag Validate(params object[] parameters)
{
var result = new ConcurrentBag<StringTag>();
diff --git a/UVtools.Core/Operations/OperationLayerReHeight.cs b/UVtools.Core/Operations/OperationLayerReHeight.cs
index f5550c1..32bf0bc 100644
--- a/UVtools.Core/Operations/OperationLayerReHeight.cs
+++ b/UVtools.Core/Operations/OperationLayerReHeight.cs
@@ -14,14 +14,15 @@ using UVtools.Core.Objects;
namespace UVtools.Core.Operations
{
+ [Serializable]
public sealed class OperationLayerReHeight : Operation
{
private OperationLayerReHeightItem _item;
#region Overrides
- public override Enumerations.LayerRangeSelection LayerRangeSelection => Enumerations.LayerRangeSelection.None;
- public override bool CanROI { get; set; } = false;
+ public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
+ public override bool CanROI => false;
public override string Title => "Adjust layer height";
public override string Description =>
"Adjust the layer height of the model\n\n" +
@@ -38,6 +39,8 @@ namespace UVtools.Core.Operations
public override bool CanCancel => false;
+ public override bool CanHaveProfiles => false;
+
public override StringTag Validate(params object[] parameters)
{
var sb = new StringBuilder();
diff --git a/UVtools.Core/Operations/OperationLayerRemove.cs b/UVtools.Core/Operations/OperationLayerRemove.cs
index b2fb9ca..25dd621 100644
--- a/UVtools.Core/Operations/OperationLayerRemove.cs
+++ b/UVtools.Core/Operations/OperationLayerRemove.cs
@@ -5,16 +5,19 @@
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
+
+using System;
using System.Text;
namespace UVtools.Core.Operations
{
+ [Serializable]
public sealed class OperationLayerRemove : Operation
{
#region Overrides
- public override Enumerations.LayerRangeSelection LayerRangeSelection => Enumerations.LayerRangeSelection.Current;
- public override bool CanROI { get; set; } = false;
+ public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.Current;
+ public override bool CanROI => false;
public override bool PassActualLayerIndex => true;
public override string Title => "Remove layers";
@@ -32,6 +35,8 @@ namespace UVtools.Core.Operations
public override bool CanCancel => false;
+ public override bool CanHaveProfiles => false;
+
#endregion
#region Properties
diff --git a/UVtools.Core/Operations/OperationMask.cs b/UVtools.Core/Operations/OperationMask.cs
index ed1c3c3..c702371 100644
--- a/UVtools.Core/Operations/OperationMask.cs
+++ b/UVtools.Core/Operations/OperationMask.cs
@@ -6,13 +6,16 @@
* of this license document, but changing it is not allowed.
*/
+using System;
using System.Text;
+using System.Xml.Serialization;
using Emgu.CV;
using Emgu.CV.CvEnum;
using UVtools.Core.Objects;
namespace UVtools.Core.Operations
{
+ [Serializable]
public class OperationMask : Operation
{
public override string Title => "Mask";
@@ -30,6 +33,8 @@ namespace UVtools.Core.Operations
public override string ProgressAction => "Masked layers";
+ public override bool CanHaveProfiles => false;
+
public override StringTag Validate(params object[] parameters)
{
var sb = new StringBuilder();
@@ -41,6 +46,7 @@ namespace UVtools.Core.Operations
return new StringTag(sb.ToString());
}
+ [XmlIgnore]
public Mat Mask { get; set; }
public bool HaveMask => !(Mask is null);
diff --git a/UVtools.Core/Operations/OperationMorph.cs b/UVtools.Core/Operations/OperationMorph.cs
index 9be71f7..a16d92c 100644
--- a/UVtools.Core/Operations/OperationMorph.cs
+++ b/UVtools.Core/Operations/OperationMorph.cs
@@ -6,11 +6,14 @@
* of this license document, but changing it is not allowed.
*/
+using System;
+using System.Xml.Serialization;
using Emgu.CV.CvEnum;
using UVtools.Core.Objects;
namespace UVtools.Core.Operations
{
+ [Serializable]
public sealed class OperationMorph : Operation
{
private MorphOp _morphOperation = MorphOp.Erode;
@@ -46,6 +49,19 @@ namespace UVtools.Core.Operations
new StringTag("Gradient - Removes the interior areas of objects", MorphOp.Gradient),
};
+ public byte MorphOperationIndex
+ {
+ get
+ {
+ for (byte i = 0; i < MorphOperations.Length; i++)
+ {
+ if ((MorphOp) MorphOperations[i].Tag == MorphOperation) return i;
+ }
+
+ return 0;
+ }
+ }
+
public MorphOp MorphOperation
{
get => _morphOperation;
@@ -70,8 +86,42 @@ namespace UVtools.Core.Operations
set => RaiseAndSetIfChanged(ref _fadeInOut, value);
}
+ [XmlIgnore]
public Kernel Kernel { get; set; } = new Kernel();
+ public override string ToString()
+ {
+ var result = $"[{_morphOperation}] [Iterations: {_iterationsStart}/{_iterationsEnd}] [Fade: {_fadeInOut}]" + LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
+
+ #endregion
+
+ #region Equality
+
+ private bool Equals(OperationMorph other)
+ {
+ return _morphOperation == other._morphOperation && _iterationsStart == other._iterationsStart && _iterationsEnd == other._iterationsEnd && _fadeInOut == other._fadeInOut;
+ }
+
+ public override bool Equals(object obj)
+ {
+ return ReferenceEquals(this, obj) || obj is OperationMorph other && Equals(other);
+ }
+
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ var hashCode = (int) _morphOperation;
+ hashCode = (hashCode * 397) ^ (int) _iterationsStart;
+ hashCode = (hashCode * 397) ^ (int) _iterationsEnd;
+ hashCode = (hashCode * 397) ^ _fadeInOut.GetHashCode();
+ return hashCode;
+ }
+ }
+
#endregion
}
}
diff --git a/UVtools.Core/Operations/OperationMove.cs b/UVtools.Core/Operations/OperationMove.cs
index 60c9840..b25b611 100644
--- a/UVtools.Core/Operations/OperationMove.cs
+++ b/UVtools.Core/Operations/OperationMove.cs
@@ -13,6 +13,7 @@ using UVtools.Core.Objects;
namespace UVtools.Core.Operations
{
+ [Serializable]
public class OperationMove : Operation
{
public override string Title => "Move";
@@ -29,6 +30,8 @@ namespace UVtools.Core.Operations
public override string ProgressAction => (IsCutMove ? "Moved" : "Copied")+" layers";
+ public override bool CanHaveProfiles => false;
+
public override StringTag Validate(params object[] parameters)
{
var sb = new StringBuilder();
diff --git a/UVtools.Core/Operations/OperationPattern.cs b/UVtools.Core/Operations/OperationPattern.cs
index 4297f42..7d66f7d 100644
--- a/UVtools.Core/Operations/OperationPattern.cs
+++ b/UVtools.Core/Operations/OperationPattern.cs
@@ -5,12 +5,15 @@
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
+
+using System;
using System.Drawing;
using System.Text;
using UVtools.Core.Objects;
namespace UVtools.Core.Operations
{
+ [Serializable]
public class OperationPattern : Operation
{
private Enumerations.Anchor _anchor = Enumerations.Anchor.None;
@@ -37,6 +40,8 @@ namespace UVtools.Core.Operations
public override string ProgressAction => "Patterned layers";
+ public override bool CanHaveProfiles => false;
+
public override StringTag Validate(params object[] parameters)
{
var sb = new StringBuilder();
diff --git a/UVtools.Core/Operations/OperationPixelDimming.cs b/UVtools.Core/Operations/OperationPixelDimming.cs
index 22941d6..24bbeaa 100644
--- a/UVtools.Core/Operations/OperationPixelDimming.cs
+++ b/UVtools.Core/Operations/OperationPixelDimming.cs
@@ -6,12 +6,15 @@
* of this license document, but changing it is not allowed.
*/
+using System;
using System.Text;
+using System.Xml.Serialization;
using Emgu.CV;
using UVtools.Core.Objects;
namespace UVtools.Core.Operations
{
+ [Serializable]
public class OperationPixelDimming : Operation
{
private uint _borderSize = 5;
@@ -68,12 +71,14 @@ namespace UVtools.Core.Operations
set => RaiseAndSetIfChanged(ref _bordersOnly, value);
}
+ [XmlIgnore]
public Matrix<byte> EvenPattern
{
get => _evenPattern;
set => RaiseAndSetIfChanged(ref _evenPattern, value);
}
+ [XmlIgnore]
public Matrix<byte> OddPattern
{
get => _oddPattern;
@@ -81,5 +86,37 @@ namespace UVtools.Core.Operations
}
#endregion
+
+ public override string ToString()
+ {
+ var result = $"[Border: {_borderSize}px] [Only borders: {_bordersOnly}]" + LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
+
+ #region Equality
+
+ protected bool Equals(OperationPixelDimming other)
+ {
+ return _borderSize == other._borderSize && _bordersOnly == other._bordersOnly;
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (obj is null) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != GetType()) return false;
+ return Equals((OperationPixelDimming) obj);
+ }
+
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ return ((int) _borderSize * 397) ^ _bordersOnly.GetHashCode();
+ }
+ }
+
+ #endregion
}
}
diff --git a/UVtools.Core/Operations/OperationRepairLayers.cs b/UVtools.Core/Operations/OperationRepairLayers.cs
index e004682..d54054e 100644
--- a/UVtools.Core/Operations/OperationRepairLayers.cs
+++ b/UVtools.Core/Operations/OperationRepairLayers.cs
@@ -6,12 +6,15 @@
* of this license document, but changing it is not allowed.
*/
+using System;
using System.Collections.Generic;
using System.Text;
+using System.Xml.Serialization;
using UVtools.Core.Objects;
namespace UVtools.Core.Operations
{
+ [Serializable]
public class OperationRepairLayers : Operation
{
private bool _repairIslands = true;
@@ -21,7 +24,7 @@ namespace UVtools.Core.Operations
private ushort _removeIslandsRecursiveIterations = 4;
private uint _gapClosingIterations = 1;
private uint _noiseRemovalIterations = 0;
- public override bool CanROI { get; set; } = false;
+ public override bool CanROI => false;
public override string Title => "Repair layers and issues";
public override string Description => null;
@@ -32,6 +35,8 @@ namespace UVtools.Core.Operations
public override string ProgressAction => "Repaired layers";
+ public override bool CanHaveProfiles => false;
+
public override StringTag Validate(params object[] parameters)
{
var sb = new StringBuilder();
@@ -86,8 +91,10 @@ namespace UVtools.Core.Operations
set => RaiseAndSetIfChanged(ref _noiseRemovalIterations, value);
}
+ [XmlIgnore]
public IslandDetectionConfiguration IslandDetectionConfig { get; set; }
+ [XmlIgnore]
public List<LayerIssue> Issues { get; set; }
}
diff --git a/UVtools.Core/Operations/OperationResize.cs b/UVtools.Core/Operations/OperationResize.cs
index 2225067..fa20389 100644
--- a/UVtools.Core/Operations/OperationResize.cs
+++ b/UVtools.Core/Operations/OperationResize.cs
@@ -5,11 +5,14 @@
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
+
+using System;
using System.Text;
using UVtools.Core.Objects;
namespace UVtools.Core.Operations
{
+ [Serializable]
public class OperationResize : Operation
{
private decimal _x = 100;
@@ -31,7 +34,7 @@ namespace UVtools.Core.Operations
public override string ProgressTitle =>
$"Resizing from layers {LayerIndexStart} to {LayerIndexEnd} at X:{X}% Y:{Y}% ";
- public override string ProgressAction => "Reiszed layers";
+ public override string ProgressAction => "Resized layers";
public override StringTag Validate(params object[] parameters)
{
@@ -86,5 +89,41 @@ namespace UVtools.Core.Operations
public OperationResize()
{
}
+
+ public override string ToString()
+ {
+ var result = $"[X: {_x}%, Y: {_y}%] [Fade: {_isFade}] [Constrain: {_constrainXy}]"+ LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
+
+ #region Equality
+
+ protected bool Equals(OperationResize other)
+ {
+ return _x == other._x && _y == other._y && _constrainXy == other._constrainXy && _isFade == other._isFade;
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (obj is null) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != GetType()) return false;
+ return Equals((OperationResize) obj);
+ }
+
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ var hashCode = _x.GetHashCode();
+ hashCode = (hashCode * 397) ^ _y.GetHashCode();
+ hashCode = (hashCode * 397) ^ _constrainXy.GetHashCode();
+ hashCode = (hashCode * 397) ^ _isFade.GetHashCode();
+ return hashCode;
+ }
+ }
+
+ #endregion
}
}
diff --git a/UVtools.Core/Operations/OperationRotate.cs b/UVtools.Core/Operations/OperationRotate.cs
index e95991f..4d32762 100644
--- a/UVtools.Core/Operations/OperationRotate.cs
+++ b/UVtools.Core/Operations/OperationRotate.cs
@@ -10,6 +10,7 @@ using System;
namespace UVtools.Core.Operations
{
+ [Serializable]
public class OperationRotate : Operation
{
private decimal _angleDegrees = 90;
@@ -30,5 +31,34 @@ namespace UVtools.Core.Operations
get => _angleDegrees;
set => RaiseAndSetIfChanged(ref _angleDegrees, value);
}
+
+ public override string ToString()
+ {
+ var result = $"[Angle: {Math.Abs(_angleDegrees)}º {(AngleDegrees < 0 ? "CCW" : "CW")}]" + LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
+
+ #region Equality
+
+ protected bool Equals(OperationRotate other)
+ {
+ return _angleDegrees == other._angleDegrees;
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (obj is null) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != GetType()) return false;
+ return Equals((OperationRotate) obj);
+ }
+
+ public override int GetHashCode()
+ {
+ return _angleDegrees.GetHashCode();
+ }
+
+ #endregion
}
}
diff --git a/UVtools.Core/Operations/OperationSolidify.cs b/UVtools.Core/Operations/OperationSolidify.cs
index fc6aa96..af9474c 100644
--- a/UVtools.Core/Operations/OperationSolidify.cs
+++ b/UVtools.Core/Operations/OperationSolidify.cs
@@ -6,8 +6,11 @@
* of this license document, but changing it is not allowed.
*/
+using System;
+
namespace UVtools.Core.Operations
{
+ [Serializable]
public sealed class OperationSolidify : Operation
{
#region Overrides
@@ -26,6 +29,8 @@ namespace UVtools.Core.Operations
public override string ProgressAction => "Solidified layers";
+ public override bool CanHaveProfiles => false;
+
#endregion
}
}
diff --git a/UVtools.Core/Operations/OperationThreshold.cs b/UVtools.Core/Operations/OperationThreshold.cs
index 8854ac7..143f173 100644
--- a/UVtools.Core/Operations/OperationThreshold.cs
+++ b/UVtools.Core/Operations/OperationThreshold.cs
@@ -11,6 +11,7 @@ using Emgu.CV.CvEnum;
namespace UVtools.Core.Operations
{
+ [Serializable]
public class OperationThreshold : Operation
{
private byte _threshold = 127;
@@ -51,5 +52,40 @@ namespace UVtools.Core.Operations
}
public static Array ThresholdTypes => Enum.GetValues(typeof(ThresholdType));
+
+ public override string ToString()
+ {
+ var result = $"[{_type} = {_threshold} / {_maximum}]" + LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
+
+ #region Equality
+
+ protected bool Equals(OperationThreshold other)
+ {
+ return _threshold == other._threshold && _maximum == other._maximum && _type == other._type;
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != this.GetType()) return false;
+ return Equals((OperationThreshold) obj);
+ }
+
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ var hashCode = _threshold.GetHashCode();
+ hashCode = (hashCode * 397) ^ _maximum.GetHashCode();
+ hashCode = (hashCode * 397) ^ (int) _type;
+ return hashCode;
+ }
+ }
+
+ #endregion
}
}
diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj
index 4486203..1966dac 100644
--- a/UVtools.Core/UVtools.Core.csproj
+++ b/UVtools.Core/UVtools.Core.csproj
@@ -10,7 +10,7 @@
<RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl>
<PackageProjectUrl>https://github.com/sn4k3/UVtools</PackageProjectUrl>
<Description>MSLA/DLP, file analysis, repair, conversion and manipulation</Description>
- <Version>1.1.2</Version>
+ <Version>1.1.3</Version>
<Copyright>Copyright © 2020 PTRTECH</Copyright>
<PackageIcon>UVtools.png</PackageIcon>
<Platforms>AnyCPU;x64</Platforms>
diff --git a/UVtools.WPF/App.axaml.cs b/UVtools.WPF/App.axaml.cs
index b946e10..8df3fa8 100644
--- a/UVtools.WPF/App.axaml.cs
+++ b/UVtools.WPF/App.axaml.cs
@@ -20,7 +20,9 @@ using Avalonia.Media.Imaging;
using Avalonia.Platform;
using Avalonia.ThemeManager;
using Emgu.CV;
+using UVtools.Core;
using UVtools.Core.FileFormats;
+using UVtools.Core.Operations;
using UVtools.WPF.Extensions;
using UVtools.WPF.Structures;
@@ -49,6 +51,8 @@ namespace UVtools.WPF
UserSettings.Load();
UserSettings.SetVersion();
+ OperationProfiles.Load();
+
ThemeSelector = Avalonia.ThemeManager.ThemeSelector.Create(Path.Combine(ApplicationPath, "Assets", "Themes"));
ThemeSelector.LoadSelectedTheme(Path.Combine(UserSettings.SettingsFolder, "selected.theme"));
if (ThemeSelector.SelectedTheme.Name == "UVtoolsDark" || ThemeSelector.SelectedTheme.Name == "Light")
@@ -87,25 +91,24 @@ namespace UVtools.WPF
}
#region Utilities
+
+ public static string AppExecutable = Path.Combine(ApplicationPath, About.Software);
+ public static string AppExecutableQuoted = $"\"{AppExecutable}\"";
public static void NewInstance(string filePath)
{
try
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
- var info = new ProcessStartInfo("UVtools.exe", $"\"{filePath}\"")
- {
- UseShellExecute = true
- };
- Process.Start(info).Dispose();
+ StartProcess($"{AppExecutable}.exe", $"\"{filePath}\"");
}
- else if(File.Exists("UVtools"))
+ else if(File.Exists(AppExecutable)) // Direct execute
{
- Process.Start("UVtools", $"\"{filePath}\"").Dispose();
+ StartProcess(AppExecutable, $"\"{filePath}\"");
}
else
{
- Process.Start("dotnet", $"UVtools.dll \"{filePath}\"").Dispose();
+ StartProcess("dotnet", $"UVtools.dll \"{filePath}\"");
}
}
catch (Exception e)
@@ -142,11 +145,11 @@ namespace UVtools.WPF
}
- public static void StartProcess(string name)
+ public static void StartProcess(string name, string arguments = null)
{
try
{
- using (Process.Start(new ProcessStartInfo(name)
+ using (Process.Start(new ProcessStartInfo(name, arguments)
{
UseShellExecute = true
})) { }
@@ -155,7 +158,6 @@ namespace UVtools.WPF
{
Debug.WriteLine(e);
}
-
}
public static Stream GetAsset(string url)
diff --git a/UVtools.WPF/Controls/Tools/ToolArithmeticControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolArithmeticControl.axaml.cs
index 73fef89..623633e 100644
--- a/UVtools.WPF/Controls/Tools/ToolArithmeticControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolArithmeticControl.axaml.cs
@@ -6,12 +6,12 @@ namespace UVtools.WPF.Controls.Tools
{
public class ToolArithmeticControl : ToolControl
{
- public OperationArithmetic Operation { get; }
+ public OperationArithmetic Operation => BaseOperation as OperationArithmetic;
public ToolArithmeticControl()
{
InitializeComponent();
- BaseOperation = Operation = new OperationArithmetic();
+ BaseOperation = new OperationArithmetic();
Operation.PropertyChanged += (sender, e) =>
{
if (e.PropertyName == nameof(Operation.Sentence))
diff --git a/UVtools.WPF/Controls/Tools/ToolBlurControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolBlurControl.axaml.cs
index 4a03116..88058ec 100644
--- a/UVtools.WPF/Controls/Tools/ToolBlurControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolBlurControl.axaml.cs
@@ -10,7 +10,7 @@ namespace UVtools.WPF.Controls.Tools
private bool _isSizeEnabled = true;
private bool _isKernelVisible;
private KernelControl _kernelCtrl;
- public OperationBlur Operation { get; }
+ public OperationBlur Operation => BaseOperation as OperationBlur;
public int SelectedAlgorithmIndex
{
@@ -40,7 +40,7 @@ namespace UVtools.WPF.Controls.Tools
public ToolBlurControl()
{
InitializeComponent();
- BaseOperation = Operation = new OperationBlur();
+ BaseOperation = new OperationBlur();
_kernelCtrl = this.Find<KernelControl>("KernelCtrl");
}
diff --git a/UVtools.WPF/Controls/Tools/ToolCalculatorControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolCalculatorControl.axaml.cs
index f8acf19..c342418 100644
--- a/UVtools.WPF/Controls/Tools/ToolCalculatorControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolCalculatorControl.axaml.cs
@@ -6,14 +6,14 @@ namespace UVtools.WPF.Controls.Tools
{
public class ToolCalculatorControl : ToolControl
{
- public OperationCalculator Operation { get; }
+ public OperationCalculator Operation => BaseOperation as OperationCalculator;
public FileFormat SlicerFile => App.SlicerFile;
public ToolCalculatorControl()
{
InitializeComponent();
- BaseOperation = Operation = new OperationCalculator
+ BaseOperation = new OperationCalculator
{
CalcMillimetersToPixels = new OperationCalculator.MillimetersToPixels(SlicerFile.Resolution, SlicerFile.Display),
CalcLightOffDelay = new OperationCalculator.LightOffDelayC(
diff --git a/UVtools.WPF/Controls/Tools/ToolChangeResolutionControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolChangeResolutionControl.axaml.cs
index 5f47e72..2762a3c 100644
--- a/UVtools.WPF/Controls/Tools/ToolChangeResolutionControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolChangeResolutionControl.axaml.cs
@@ -8,7 +8,7 @@ namespace UVtools.WPF.Controls.Tools
public class ToolChangeResolutionControl : ToolControl
{
private OperationChangeResolution.Resolution _selectedPresetItem;
- public OperationChangeResolution Operation { get; }
+ public OperationChangeResolution Operation => BaseOperation as OperationChangeResolution;
public OperationChangeResolution.Resolution SelectedPresetItem
{
@@ -34,7 +34,7 @@ namespace UVtools.WPF.Controls.Tools
public ToolChangeResolutionControl()
{
InitializeComponent();
- BaseOperation = Operation = new OperationChangeResolution(App.SlicerFile.Resolution, App.SlicerFile.LayerManager.BoundingRectangle);
+ BaseOperation = new OperationChangeResolution(App.SlicerFile.Resolution, App.SlicerFile.LayerManager.BoundingRectangle);
}
private void InitializeComponent()
diff --git a/UVtools.WPF/Controls/Tools/ToolControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolControl.axaml.cs
index e91f2fd..8ec6d5b 100644
--- a/UVtools.WPF/Controls/Tools/ToolControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolControl.axaml.cs
@@ -9,7 +9,19 @@ namespace UVtools.WPF.Controls.Tools
{
public class ToolControl : UserControlEx
{
- public Operation BaseOperation = null;
+ private Operation _baseOperation;
+
+ public Operation BaseOperation
+ {
+ get => _baseOperation;
+ set
+ {
+ if(!RaiseAndSetIfChanged(ref _baseOperation, value)) return;
+ if (DataContext is null) return;
+ ResetDataContext();
+ }
+ }
+
public ToolWindow ParentWindow { get; set; } = null;
public bool CanRun { get; set; } = true;
@@ -33,6 +45,12 @@ namespace UVtools.WPF.Controls.Tools
public virtual bool UpdateOperation() => true;
+ /*public virtual void SetOperation(Operation operation)
+ {
+ BaseOperation = operation;
+ ResetDataContext();
+ }*/
+
/// <summary>
/// Validates if is safe to continue with operation
/// </summary>
diff --git a/UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml.cs
index d1e761b..ba83fec 100644
--- a/UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml.cs
@@ -16,7 +16,7 @@ namespace UVtools.WPF.Controls.Tools
{
public class ToolEditParametersControl : ToolControl
{
- public OperationEditParameters Operation { get; }
+ public OperationEditParameters Operation => BaseOperation as OperationEditParameters;
public bool SupportPerLayerSettings => App.SlicerFile.SupportPerLayerSettings;
public RowControl[] RowControls;
@@ -110,7 +110,7 @@ namespace UVtools.WPF.Controls.Tools
InitializeComponent();
App.SlicerFile.RefreshPrintParametersModifiersValues();
- BaseOperation = Operation = new OperationEditParameters(App.SlicerFile.PrintParameterModifiers);
+ BaseOperation = new OperationEditParameters(App.SlicerFile.PrintParameterModifiers);
if (Operation.Modifiers is null || Operation.Modifiers.Length == 0)
{
diff --git a/UVtools.WPF/Controls/Tools/ToolFlipControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolFlipControl.axaml.cs
index c2d973b..7899b4d 100644
--- a/UVtools.WPF/Controls/Tools/ToolFlipControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolFlipControl.axaml.cs
@@ -5,12 +5,12 @@ namespace UVtools.WPF.Controls.Tools
{
public class ToolFlipControl : ToolControl
{
- public OperationFlip Operation { get; }
+ public OperationFlip Operation => BaseOperation as OperationFlip;
public ToolFlipControl()
{
InitializeComponent();
- BaseOperation = Operation = new OperationFlip();
+ BaseOperation = new OperationFlip();
}
private void InitializeComponent()
diff --git a/UVtools.WPF/Controls/Tools/ToolLayerCloneControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolLayerCloneControl.axaml.cs
index a394fa5..bbc7776 100644
--- a/UVtools.WPF/Controls/Tools/ToolLayerCloneControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolLayerCloneControl.axaml.cs
@@ -6,7 +6,7 @@ namespace UVtools.WPF.Controls.Tools
{
public class ToolLayerCloneControl : ToolControl
{
- public OperationLayerClone Operation { get; }
+ public OperationLayerClone Operation => BaseOperation as OperationLayerClone;
public uint ExtraLayers => (uint)Math.Max(0, ((int)Operation.LayerIndexEnd - Operation.LayerIndexStart + 1) * Operation.Clones);
@@ -31,7 +31,7 @@ namespace UVtools.WPF.Controls.Tools
public ToolLayerCloneControl()
{
InitializeComponent();
- BaseOperation = Operation = new OperationLayerClone();
+ BaseOperation = new OperationLayerClone();
Operation.PropertyChanged += (sender, args) =>
{
RaisePropertyChanged(nameof(InfoLayersStr));
diff --git a/UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml.cs
index 7e08757..f96b5f9 100644
--- a/UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml.cs
@@ -2,7 +2,6 @@
using System.Linq;
using System.Threading.Tasks;
using Avalonia.Controls;
-using Avalonia.Controls.Shapes;
using Avalonia.Input;
using Avalonia.Markup.Xaml;
using Avalonia.Media.Imaging;
@@ -12,7 +11,6 @@ using UVtools.Core.Objects;
using UVtools.Core.Operations;
using UVtools.WPF.Extensions;
using UVtools.WPF.Windows;
-using Path = System.IO.Path;
namespace UVtools.WPF.Controls.Tools
{
@@ -23,8 +21,8 @@ namespace UVtools.WPF.Controls.Tools
private Bitmap _previewImage;
private ListBox FilesListBox;
-
- public OperationLayerImport Operation { get; }
+
+ public OperationLayerImport Operation => BaseOperation as OperationLayerImport;
public uint MaximumLayer => App.SlicerFile.LastLayerIndex;
@@ -83,7 +81,7 @@ namespace UVtools.WPF.Controls.Tools
public ToolLayerImportControl()
{
InitializeComponent();
- BaseOperation = Operation = new OperationLayerImport(App.SlicerFile.Resolution);
+ BaseOperation = new OperationLayerImport(App.SlicerFile.Resolution);
Operation.Files.CollectionChanged += (sender, args) => RefreshGUI();
Operation.PropertyChanged += (sender, args) => RefreshGUI();
FilesListBox = this.Find<ListBox>("FilesListBox");
diff --git a/UVtools.WPF/Controls/Tools/ToolLayerReHeightControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolLayerReHeightControl.axaml.cs
index 88c0d0a..feb5e56 100644
--- a/UVtools.WPF/Controls/Tools/ToolLayerReHeightControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolLayerReHeightControl.axaml.cs
@@ -6,7 +6,7 @@ namespace UVtools.WPF.Controls.Tools
{
public class ToolLayerReHeightControl : ToolControl
{
- public OperationLayerReHeight Operation { get; }
+ public OperationLayerReHeight Operation => BaseOperation as OperationLayerReHeight;
public OperationLayerReHeight.OperationLayerReHeightItem[] Presets { get; } = OperationLayerReHeight.GetItems(
App.SlicerFile.LayerCount,
@@ -17,7 +17,7 @@ namespace UVtools.WPF.Controls.Tools
public ToolLayerReHeightControl()
{
InitializeComponent();
- BaseOperation = Operation = new OperationLayerReHeight();
+ BaseOperation = new OperationLayerReHeight();
if (Presets.Length == 0)
{
diff --git a/UVtools.WPF/Controls/Tools/ToolLayerRemoveControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolLayerRemoveControl.axaml.cs
index e025811..a2ada0e 100644
--- a/UVtools.WPF/Controls/Tools/ToolLayerRemoveControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolLayerRemoveControl.axaml.cs
@@ -6,7 +6,7 @@ namespace UVtools.WPF.Controls.Tools
{
public class ToolLayerRemoveControl : ToolControl
{
- public OperationLayerRemove Operation { get; }
+ public OperationLayerRemove Operation => BaseOperation as OperationLayerRemove;
public uint ExtraLayers => (uint)Math.Max(0, (int)Operation.LayerIndexEnd - Operation.LayerIndexStart + 1);
@@ -31,7 +31,7 @@ namespace UVtools.WPF.Controls.Tools
public ToolLayerRemoveControl()
{
InitializeComponent();
- BaseOperation = Operation = new OperationLayerRemove();
+ BaseOperation = new OperationLayerRemove();
Operation.PropertyChanged += (sender, args) =>
{
RaisePropertyChanged(nameof(InfoLayersStr));
diff --git a/UVtools.WPF/Controls/Tools/ToolMaskControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolMaskControl.axaml.cs
index 62486b7..bad26b4 100644
--- a/UVtools.WPF/Controls/Tools/ToolMaskControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolMaskControl.axaml.cs
@@ -20,7 +20,7 @@ namespace UVtools.WPF.Controls.Tools
private byte _genMaximumBrightness = byte.MaxValue;
private uint _genDiameter;
private Bitmap _maskImage;
- public OperationMask Operation { get; }
+ public OperationMask Operation => BaseOperation as OperationMask;
public bool IsMaskInverted
{
@@ -69,7 +69,7 @@ namespace UVtools.WPF.Controls.Tools
public ToolMaskControl()
{
InitializeComponent();
- BaseOperation = Operation = new OperationMask();
+ BaseOperation = new OperationMask();
}
private void InitializeComponent()
diff --git a/UVtools.WPF/Controls/Tools/ToolMorphControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolMorphControl.axaml.cs
index 5384461..7dac93e 100644
--- a/UVtools.WPF/Controls/Tools/ToolMorphControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolMorphControl.axaml.cs
@@ -1,4 +1,5 @@
using System;
+using System.Diagnostics;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Emgu.CV.CvEnum;
@@ -10,7 +11,7 @@ namespace UVtools.WPF.Controls.Tools
public class ToolMorphControl : ToolControl
{
private byte _morphSelectedIndex;
- public OperationMorph Operation { get; }
+ public OperationMorph Operation => BaseOperation as OperationMorph;
private KernelControl _kernelCtrl;
@@ -27,7 +28,7 @@ namespace UVtools.WPF.Controls.Tools
public ToolMorphControl()
{
InitializeComponent();
- BaseOperation = Operation = new OperationMorph();
+ BaseOperation = new OperationMorph();
_kernelCtrl = this.Find<KernelControl>("KernelCtrl");
}
diff --git a/UVtools.WPF/Controls/Tools/ToolMoveControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolMoveControl.axaml.cs
index 03a604e..a18fc5f 100644
--- a/UVtools.WPF/Controls/Tools/ToolMoveControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolMoveControl.axaml.cs
@@ -7,7 +7,7 @@ namespace UVtools.WPF.Controls.Tools
public class ToolMoveControl : ToolControl
{
private bool _isMiddleCenterChecked = true;
- public OperationMove Operation { get; }
+ public OperationMove Operation => BaseOperation as OperationMove;
public bool IsMiddleCenterChecked
{
@@ -19,7 +19,7 @@ namespace UVtools.WPF.Controls.Tools
{
InitializeComponent();
var roi = App.MainWindow.ROI;
- BaseOperation = Operation = new OperationMove(roi.IsEmpty ? App.SlicerFile.LayerManager.BoundingRectangle : roi, (uint)App.MainWindow.LayerCache.Image.Width,
+ BaseOperation = new OperationMove(roi.IsEmpty ? App.SlicerFile.LayerManager.BoundingRectangle : roi, (uint)App.MainWindow.LayerCache.Image.Width,
(uint)App.MainWindow.LayerCache.Image.Height);
Operation.PropertyChanged += (sender, e) =>
diff --git a/UVtools.WPF/Controls/Tools/ToolPatternControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolPatternControl.axaml.cs
index 1b2bbc9..cb29ad4 100644
--- a/UVtools.WPF/Controls/Tools/ToolPatternControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolPatternControl.axaml.cs
@@ -8,7 +8,7 @@ namespace UVtools.WPF.Controls.Tools
{
public class ToolPatternControl : ToolControl
{
- public OperationPattern Operation { get; }
+ public OperationPattern Operation => BaseOperation as OperationPattern;
private bool _isDefaultAnchorChecked = true;
public bool IsDefaultAnchorChecked
@@ -21,7 +21,7 @@ namespace UVtools.WPF.Controls.Tools
{
InitializeComponent();
var roi = App.MainWindow.ROI;
- BaseOperation = Operation = new OperationPattern(roi.IsEmpty ? App.SlicerFile.LayerManager.BoundingRectangle : roi, App.SlicerFile.Resolution);
+ BaseOperation = new OperationPattern(roi.IsEmpty ? App.SlicerFile.LayerManager.BoundingRectangle : roi, App.SlicerFile.Resolution);
if (Operation.MaxRows < 2 && Operation.MaxCols < 2)
{
diff --git a/UVtools.WPF/Controls/Tools/ToolPixelDimmingControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolPixelDimmingControl.axaml.cs
index 24bdd26..36499a2 100644
--- a/UVtools.WPF/Controls/Tools/ToolPixelDimmingControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolPixelDimmingControl.axaml.cs
@@ -28,7 +28,7 @@ namespace UVtools.WPF.Controls.Tools
private byte _dimGenBrightness = 127;
private ushort _infillGenThickness = 10;
private ushort _infillGenSpacing = 20;
- public OperationPixelDimming Operation { get; }
+ public OperationPixelDimming Operation => BaseOperation as OperationPixelDimming;
public string PatternText
{
@@ -63,7 +63,7 @@ namespace UVtools.WPF.Controls.Tools
public ToolPixelDimmingControl()
{
InitializeComponent();
- BaseOperation = Operation = new OperationPixelDimming();
+ BaseOperation = new OperationPixelDimming();
GeneratePixelDimming("Chessboard");
}
diff --git a/UVtools.WPF/Controls/Tools/ToolRepairLayersControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolRepairLayersControl.axaml.cs
index 843c096..0c317d8 100644
--- a/UVtools.WPF/Controls/Tools/ToolRepairLayersControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolRepairLayersControl.axaml.cs
@@ -6,12 +6,12 @@ namespace UVtools.WPF.Controls.Tools
{
public class ToolRepairLayersControl : ToolControl
{
- public OperationRepairLayers Operation { get; }
+ public OperationRepairLayers Operation => BaseOperation as OperationRepairLayers;
public ToolRepairLayersControl()
{
InitializeComponent();
- BaseOperation = Operation = new OperationRepairLayers
+ BaseOperation = new OperationRepairLayers
{
RepairIslands = UserSettings.Instance.LayerRepair.RepairIslands,
RepairResinTraps = UserSettings.Instance.LayerRepair.RepairResinTraps,
diff --git a/UVtools.WPF/Controls/Tools/ToolResizeControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolResizeControl.axaml.cs
index 2f66254..c88b965 100644
--- a/UVtools.WPF/Controls/Tools/ToolResizeControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolResizeControl.axaml.cs
@@ -1,17 +1,16 @@
-using Avalonia.Controls;
-using Avalonia.Markup.Xaml;
+using Avalonia.Markup.Xaml;
using UVtools.Core.Operations;
namespace UVtools.WPF.Controls.Tools
{
public class ToolResizeControl : ToolControl
{
- public OperationResize Operation { get; }
+ public OperationResize Operation => BaseOperation as OperationResize;
public ToolResizeControl()
{
InitializeComponent();
- BaseOperation = Operation = new OperationResize();
+ BaseOperation = new OperationResize();
}
private void InitializeComponent()
diff --git a/UVtools.WPF/Controls/Tools/ToolRotateControl.axaml b/UVtools.WPF/Controls/Tools/ToolRotateControl.axaml
index cf5d180..832c0e6 100644
--- a/UVtools.WPF/Controls/Tools/ToolRotateControl.axaml
+++ b/UVtools.WPF/Controls/Tools/ToolRotateControl.axaml
@@ -9,7 +9,7 @@
<NumericUpDown
MinWidth="100"
MaxWidth="150"
- Minimum="0.01"
+ Minimum="-359.99"
Maximum="359.99"
Increment="0.01"
FormatString="{}{0:#,0.00}"
diff --git a/UVtools.WPF/Controls/Tools/ToolRotateControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolRotateControl.axaml.cs
index 14bcec7..c09b075 100644
--- a/UVtools.WPF/Controls/Tools/ToolRotateControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolRotateControl.axaml.cs
@@ -5,12 +5,12 @@ namespace UVtools.WPF.Controls.Tools
{
public class ToolRotateControl : ToolControl
{
- public OperationRotate Operation { get; }
+ public OperationRotate Operation => BaseOperation as OperationRotate;
public ToolRotateControl()
{
InitializeComponent();
- BaseOperation = Operation = new OperationRotate();
+ BaseOperation = new OperationRotate();
}
private void InitializeComponent()
diff --git a/UVtools.WPF/Controls/Tools/ToolThresholdControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolThresholdControl.axaml.cs
index 5809c8b..648bbbf 100644
--- a/UVtools.WPF/Controls/Tools/ToolThresholdControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolThresholdControl.axaml.cs
@@ -10,7 +10,7 @@ namespace UVtools.WPF.Controls.Tools
private bool _isThresholdEnabled = true;
private bool _isMaximumEnabled = true;
private bool _isTypeEnabled = true;
- public OperationThreshold Operation { get; }
+ public OperationThreshold Operation => BaseOperation as OperationThreshold;
public string[] Presets => new[]
{
@@ -74,7 +74,7 @@ namespace UVtools.WPF.Controls.Tools
public ToolThresholdControl()
{
InitializeComponent();
- BaseOperation = Operation = new OperationThreshold();
+ BaseOperation = new OperationThreshold();
}
private void InitializeComponent()
diff --git a/UVtools.WPF/MainWindow.axaml.cs b/UVtools.WPF/MainWindow.axaml.cs
index fa2de86..1cff077 100644
--- a/UVtools.WPF/MainWindow.axaml.cs
+++ b/UVtools.WPF/MainWindow.axaml.cs
@@ -688,7 +688,49 @@ namespace UVtools.WPF
await new PrusaSlicerManager().ShowDialog(this);
}
- public void MenuNewVersionClicked() => App.OpenBrowser(VersionChecker.Url);
+ public async void MenuNewVersionClicked()
+ {
+ var result =
+ await this.MessageBoxQuestion(
+ $"Do you like to auto-update {About.Software} v{AppSettings.Version} to v{VersionChecker.Version}?\n\n" +
+ "Yes: Auto update\n" +
+ "No: Manual update\n" +
+ "Cancel: No action", "Update UVtools?", ButtonEnum.YesNoCancel);
+
+ if (result == ButtonResult.Yes)
+ {
+ IsGUIEnabled = false;
+
+ var task = await Task.Factory.StartNew(async () =>
+ {
+ ShowProgressWindow($"Downloading: {VersionChecker.Filename}");
+ try
+ {
+ VersionChecker.AutoUpgrade(ProgressWindow.RestartProgress(false));
+ return true;
+ }
+ catch (OperationCanceledException)
+ {
+ }
+ catch (Exception exception)
+ {
+ Dispatcher.UIThread.InvokeAsync(async () =>
+ await this.MessageBoxError(exception.ToString(), "Error opening the file"));
+ }
+
+ return false;
+ });
+
+ IsGUIEnabled = true;
+
+ return;
+ }
+ if (result == ButtonResult.No)
+ {
+ App.OpenBrowser(VersionChecker.UrlLatestRelease);
+ return;
+ }
+ }
#endregion
diff --git a/UVtools.WPF/Program.cs b/UVtools.WPF/Program.cs
index 1d7fa50..efd7797 100644
--- a/UVtools.WPF/Program.cs
+++ b/UVtools.WPF/Program.cs
@@ -1,6 +1,8 @@
using System;
using System.Diagnostics;
using Avalonia;
+using Avalonia.Threading;
+using UVtools.WPF.Extensions;
namespace UVtools.WPF
{
@@ -18,10 +20,28 @@ namespace UVtools.WPF
ProgramStartupTime = Stopwatch.StartNew();
Args = args;
+ // Add the event handler for handling non-UI thread exceptions to the event.
+ AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException;
+
BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args);
}
+ private static async void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs e)
+ {
+ try
+ {
+ Exception ex = (Exception)e.ExceptionObject;
+ string errorMsg = "An application error occurred. Please contact the administrator with the following information:\n\n" +
+ $"{ex}";
+
+ await App.MainWindow.MessageBoxError(errorMsg, "Fatal Non-UI Error");
+ }
+ catch (Exception exc)
+ {
+ }
+ }
+
// Avalonia configuration, don't remove; also used by visual designer.
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>()
diff --git a/UVtools.WPF/Structures/AppVersionChecker.cs b/UVtools.WPF/Structures/AppVersionChecker.cs
index 3d843f1..0d9f333 100644
--- a/UVtools.WPF/Structures/AppVersionChecker.cs
+++ b/UVtools.WPF/Structures/AppVersionChecker.cs
@@ -6,19 +6,72 @@
* of this license document, but changing it is not allowed.
*/
using System;
+using System.Collections.Specialized;
using System.Diagnostics;
+using System.IO;
+using System.IO.Compression;
using System.Net;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
+using ABI.Windows.Data.Json;
using Avalonia.Threading;
+using Newtonsoft.Json;
using UVtools.Core;
using UVtools.Core.Objects;
+using UVtools.Core.Operations;
+using UVtools.WPF.Extensions;
namespace UVtools.WPF.Structures
{
public class AppVersionChecker : BindableBase
{
+ public const string GitHubReleaseApi = "https://api.github.com/repos/sn4k3/UVtools/releases/latest";
private string _version;
private string _url;
+ public string Filename
+ {
+ get
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ return $"{About.Software}_win-x64_v{_version}.msi";
+ }
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ {
+ return $"{About.Software}_linux-x64_v{_version}.zip";
+ }
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+ {
+ return $"{About.Software}_osx-x64_v{_version}.zip";
+ }
+
+ return $"{About.Software}_universal-x86-x64_v{_version}.zip";
+ }
+ }
+
+ public string Runtime
+ {
+ get
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ return "win-x64";
+ }
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ {
+ return "linux-x64";
+ }
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+ {
+ return "osx-x64";
+ }
+
+ return "universal-x86-x64";
+ }
+ }
+
public string Version
{
get => _version;
@@ -30,19 +83,46 @@ namespace UVtools.WPF.Structures
}
}
- public string VersionAnnouncementText => $"New version {_version} is available!";
+ public string VersionAnnouncementText => $"New version v{_version} is available!";
- public string Url => $"{About.Website}/releases/tag/{_version}";
+ public string UrlLatestRelease => $"{About.Website}/releases/latest";
+
+ public string DownloadLink => string.IsNullOrEmpty(Filename) ? null : $"{About.Website}/releases/download/v{_version}/{Filename}";
public bool HaveNewVersion => !string.IsNullOrEmpty(Version);
+ public string DownloadedFile { get; private set; }
+
public bool Check()
{
try
{
- using (WebClient client = new WebClient())
+ using (WebClient client = new WebClient
{
- string htmlCode = client.DownloadString($"{About.Website}/releases");
+ Headers = new WebHeaderCollection
+ {
+ {HttpRequestHeader.Accept, "application/json"},
+ {HttpRequestHeader.UserAgent, "Request"}
+ }
+ })
+ {
+ var response = client.DownloadString(GitHubReleaseApi);
+ dynamic json = JsonConvert.DeserializeObject(response);
+ string tag_name = json.tag_name;
+ if (string.IsNullOrEmpty(tag_name)) return false;
+ tag_name = tag_name.Trim(' ', 'v', 'V');
+ Debug.WriteLine($"Version checker: v{AppSettings.VersionStr} <=> v{tag_name}");
+
+ if (string.Compare(tag_name, AppSettings.VersionStr, StringComparison.OrdinalIgnoreCase) > 0)
+ {
+ Dispatcher.UIThread.InvokeAsync(() =>
+ {
+ Version = tag_name;
+ Debug.WriteLine($"New version detected: {DownloadLink}");
+ });
+ return true;
+ }
+ /*string htmlCode = client.DownloadString($"{About.Website}/releases");
const string searchFor = "/releases/tag/";
var startIndex = htmlCode.IndexOf(searchFor, StringComparison.InvariantCultureIgnoreCase) +
searchFor.Length;
@@ -55,7 +135,7 @@ namespace UVtools.WPF.Structures
Version = version;;
});
return true;
- }
+ }*/
}
}
catch (Exception e)
@@ -65,5 +145,103 @@ namespace UVtools.WPF.Structures
return false;
}
+
+ public bool AutoUpgrade(OperationProgress progress)
+ {
+ if (!HaveNewVersion) return false;
+ progress.ItemName = "Megabytes";
+ try
+ {
+ using (var client = new WebClient())
+ {
+ var path = Path.GetTempPath();
+ DownloadedFile = Path.Combine(path, Filename);
+ Debug.WriteLine($"Downloading to: {DownloadedFile}");
+
+
+ client.DownloadProgressChanged += (sender, e) =>
+ {
+ progress.Reset("Megabytes", (uint)e.TotalBytesToReceive / 1048576, (uint)e.BytesReceived / 1048576);
+ };
+ client.DownloadFileCompleted += (sender, e) =>
+ {
+ progress.Reset("Extracting");
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ App.StartProcess(DownloadedFile);
+ Environment.Exit(0);
+ }
+ else
+ {
+ string upgradeFolder = "UPDATED_VERSION";
+ var targetDir = Path.Combine(App.ApplicationPath, upgradeFolder);
+ using (var stream = File.Open(DownloadedFile, FileMode.Open))
+ {
+ using (ZipArchive zip = new ZipArchive(stream, ZipArchiveMode.Read))
+ {
+ zip.ExtractToDirectory(targetDir, true);
+ }
+ }
+
+ File.Delete(DownloadedFile);
+
+ string upgradeFileName = $"{About.Software}_upgrade.sh";
+ var upgradeFile = Path.Combine(App.ApplicationPath, upgradeFileName);
+ using (var stream = File.CreateText(upgradeFile))
+ {
+ stream.WriteLine("#!/bin/bash");
+ stream.WriteLine($"echo {About.Software} v{AppSettings.Version} updater script");
+ stream.WriteLine($"cd '{App.ApplicationPath}'");
+ stream.WriteLine($"killall {About.Software}");
+ stream.WriteLine("sleep 0.5");
+ stream.WriteLine($"cp -fR {upgradeFolder}/* .");
+ stream.WriteLine($"rm -fr {upgradeFolder}");
+ stream.WriteLine("sleep 0.5");
+ //stream.WriteLine($"[ -f {About.Software} ] && {App.AppExecutableQuoted} & || dotnet {About.Software}.dll &");
+ stream.WriteLine($"if [ -f '{About.Software}' ]; then");
+ stream.WriteLine($" ./{About.Software} &");
+ stream.WriteLine("else");
+ stream.WriteLine($" dotnet {About.Software}.dll &");
+ stream.WriteLine("fi");
+ stream.WriteLine($"rm -f {upgradeFileName}");
+ //stream.WriteLine("exit");
+ stream.Close();
+ }
+
+ App.StartProcess("bash", $"\"{upgradeFile}\"");
+
+ //App.NewInstance(App.MainWindow.SlicerFile?.FileFullPath);
+ Environment.Exit(0);
+ }
+
+ lock (e.UserState)
+ {
+ //releases blocked thread
+ Monitor.Pulse(e.UserState);
+ }
+ };
+
+
+ var syncObject = new object();
+
+ lock (syncObject)
+ {
+ client.DownloadFileAsync(new Uri(DownloadLink), DownloadedFile, syncObject);
+ //This would block the thread until download completes
+ Monitor.Wait(syncObject);
+ }
+
+ }
+ }
+ catch (Exception e)
+ {
+ Dispatcher.UIThread.InvokeAsync(async () =>
+ await App.MainWindow.MessageBoxError(e.ToString(), "Error downloading the file"));
+ return false;
+ }
+
+
+ return false;
+ }
}
}
diff --git a/UVtools.WPF/Structures/OperationProfiles.cs b/UVtools.WPF/Structures/OperationProfiles.cs
new file mode 100644
index 0000000..c351776
--- /dev/null
+++ b/UVtools.WPF/Structures/OperationProfiles.cs
@@ -0,0 +1,270 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Xml.Serialization;
+using UVtools.Core.Operations;
+
+namespace UVtools.WPF.Structures
+{
+ [Serializable]
+ public class OperationProfiles //: IList<Operation>
+ {
+ #region Properties
+ /// <summary>
+ /// Default filepath for store <see cref="UserSettings"/>
+ /// </summary>
+ private static string FilePath => Path.Combine(UserSettings.SettingsFolder, "operation_profiles.xml");
+
+ [XmlElement(typeof(OperationArithmetic))]
+ [XmlElement(typeof(OperationBlur))]
+ //[XmlElement(typeof(OperationCalculator))]
+ [XmlElement(typeof(OperationChangeResolution))]
+ //[XmlElement(typeof(OperationEditParameters))]
+ [XmlElement(typeof(OperationFlip))]
+ //[XmlElement(typeof(OperationLayerClone))]
+ //[XmlElement(typeof(OperationLayerImport))]
+ //[XmlElement(typeof(OperationLayerReHeight))]
+ //[XmlElement(typeof(OperationLayerRemove))]
+ //[XmlElement(typeof(OperationMask))]
+ [XmlElement(typeof(OperationMorph))]
+ //[XmlElement(typeof(OperationMove))]
+ //[XmlElement(typeof(OperationPattern))]
+ [XmlElement(typeof(OperationPixelDimming))]
+ //[XmlElement(typeof(OperationRepairLayers))]
+ [XmlElement(typeof(OperationResize))]
+ [XmlElement(typeof(OperationRotate))]
+ [XmlElement(typeof(OperationThreshold))]
+ public List<Operation> Operations { get; internal set; } = new List<Operation>();
+
+ [XmlIgnore]
+ public static List<Operation> Profiles
+ {
+ get => Instance.Operations;
+ internal set => Instance.Operations = value;
+ }
+
+ #endregion
+
+ #region Singleton
+ private static OperationProfiles _instance;
+ /// <summary>
+ /// Instance of <see cref="UserSettings"/> (singleton)
+ /// </summary>
+ public static OperationProfiles Instance
+ {
+ get => _instance ??= new OperationProfiles();
+ internal set => _instance = value;
+ }
+
+ //public static List<Operation> Operations => _instance.Operations;
+ #endregion
+
+ #region Constructor
+
+ private OperationProfiles()
+ { }
+
+ #endregion
+
+ #region Indexers
+
+ public Operation this[uint index]
+ {
+ get => Operations[(int) index];
+ set => Operations[(int) index] = value;
+ }
+
+ public Operation this[int index]
+ {
+ get => Operations[index];
+ set => Operations[index] = value;
+ }
+
+ #endregion
+
+ #region Enumerable
+
+ public IEnumerator<Operation> GetEnumerator()
+ {
+ return Operations.GetEnumerator();
+ }
+
+ /*IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }*/
+ #endregion
+
+ #region List Implementation
+ public void Add(Operation item)
+ {
+ Operations.Add(item);
+ }
+
+ public int IndexOf(Operation item)
+ {
+ return Operations.IndexOf(item);
+ }
+
+ public void Insert(int index, Operation item)
+ {
+ Operations.Insert(index, item);
+ }
+
+ public bool Remove(Operation item)
+ {
+ return Operations.Remove(item);
+ }
+
+ public void RemoveAt(int index)
+ {
+ Operations.RemoveAt(index);
+ }
+
+ public void Clear()
+ {
+ Operations.Clear();
+ }
+
+ public bool Contains(Operation item)
+ {
+ return Operations.Contains(item);
+ }
+
+ public void CopyTo(Operation[] array, int arrayIndex)
+ {
+ Operations.CopyTo(array, arrayIndex);
+ }
+
+ public int Count => Operations.Count;
+ public bool IsReadOnly => false;
+ #endregion
+
+ #region Static Methods
+ /// <summary>
+ /// Clear all profiles
+ /// </summary>
+ /// <param name="save">True to save settings on file, otherwise false</param>
+ public static void ClearProfiles(bool save = true)
+ {
+ Instance.Clear();
+ if (save) Save();
+ }
+
+ /// <summary>
+ /// Clear all profiles given a type
+ /// </summary>
+ /// <param name="type">Type profiles to clear</param>
+ /// <param name="save">True to save settings on file, otherwise false</param>
+ public static void ClearProfiles(Type type, bool save = true)
+ {
+ Profiles.RemoveAll(operation => operation.GetType() == type);
+ if (save) Save();
+ }
+
+ /// <summary>
+ /// Load settings from file
+ /// </summary>
+ public static void Load()
+ {
+ if (!File.Exists(FilePath))
+ {
+ return;
+ }
+
+ var serializer = new XmlSerializer(Instance.GetType());
+ try
+ {
+ using var myXmlReader = new StreamReader(FilePath);
+ _instance = (OperationProfiles)serializer.Deserialize(myXmlReader);
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine(e.ToString());
+ ClearProfiles();
+ }
+ }
+
+ /// <summary>
+ /// Save settings to file
+ /// </summary>
+ public static void Save()
+ {
+ var serializer = new XmlSerializer(Instance.GetType());
+ try
+ {
+ using var myXmlWriter = new StreamWriter(FilePath);
+ serializer.Serialize(myXmlWriter, Instance);
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine(e.ToString());
+ }
+ }
+
+ public static Operation FindByName(Operation baseOperation, string profileName)
+ {
+ return Profiles.Find(operation =>
+ operation.GetType() == baseOperation.GetType() &&
+ (
+ !string.IsNullOrWhiteSpace(profileName) && operation.ProfileName == profileName
+ || operation.Equals(baseOperation)
+ ));
+ }
+
+
+ /// <summary>
+ /// Adds a profile
+ /// </summary>
+ /// <param name="operation"></param>
+ /// <param name="save"></param>
+ public static void AddProfile(Operation operation, bool save = true)
+ {
+ Profiles.Insert(0, operation);
+ if(save) Save();
+ }
+
+ /// <summary>
+ /// Removes a profile
+ /// </summary>
+ /// <param name="operation"></param>
+ /// <param name="save"></param>
+ public static void RemoveProfile(Operation operation, bool save = true)
+ {
+ Instance.Remove(operation);
+ if (save) Save();
+ }
+
+ /// <summary>
+ /// Get all profiles within a type
+ /// </summary>
+ /// <param name="type"></param>
+ /// <returns></returns>
+ public static List<Operation> GetOperations(Type type)
+ => Profiles.Where(operation => operation.GetType() == type).ToList();
+
+ /// <summary>
+ /// Get all profiles within a type
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <returns></returns>
+ public static List<T> GetOperations<T>()
+ {
+ var result = new List<T>();
+ foreach (var operation in _instance)
+ {
+ if (operation is T operationCast)
+ {
+ result.Add(operationCast);
+ }
+ }
+
+ return result;
+ }
+
+ #endregion
+ }
+}
diff --git a/UVtools.WPF/UVtools.WPF.csproj b/UVtools.WPF/UVtools.WPF.csproj
index b0ddb53..005df41 100644
--- a/UVtools.WPF/UVtools.WPF.csproj
+++ b/UVtools.WPF/UVtools.WPF.csproj
@@ -12,7 +12,7 @@
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
- <Version>1.1.2</Version>
+ <Version>1.1.3</Version>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
@@ -29,7 +29,6 @@
<PackageReference Include="Avalonia.Controls.DataGrid" Version="0.10.0-preview6" />
<PackageReference Include="Avalonia.Desktop" Version="0.10.0-preview6" />
<PackageReference Include="Avalonia.ThemeManager" Version="0.10.0-preview6" />
- <PackageReference Include="Emgu.CV.runtime.raspbian" Version="4.4.0.4099" />
<PackageReference Include="Emgu.CV.runtime.ubuntu" Version="4.4.0.4099" />
<PackageReference Include="Emgu.CV.runtime.windows" Version="4.4.0.4099" />
<PackageReference Include="MessageBox.Avalonia" Version="0.10.0-prev2" />
diff --git a/UVtools.WPF/UserSettings.cs b/UVtools.WPF/UserSettings.cs
index c6dc4bb..28fbe6e 100644
--- a/UVtools.WPF/UserSettings.cs
+++ b/UVtools.WPF/UserSettings.cs
@@ -9,7 +9,6 @@
using System;
using System.Diagnostics;
using System.IO;
-using System.Runtime.InteropServices;
using System.Xml.Serialization;
using Avalonia.Media;
using JetBrains.Annotations;
@@ -951,7 +950,16 @@ namespace UVtools.WPF
get
{
var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), About.Software);
- Directory.CreateDirectory(path);
+ try
+ {
+ if (!Directory.Exists(path))
+ Directory.CreateDirectory(path);
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine(e);
+ }
+
return path;
}
}
diff --git a/UVtools.WPF/Windows/ToolWindow.axaml b/UVtools.WPF/Windows/ToolWindow.axaml
index 0fba631..4c37dda 100644
--- a/UVtools.WPF/Windows/ToolWindow.axaml
+++ b/UVtools.WPF/Windows/ToolWindow.axaml
@@ -202,6 +202,106 @@
</StackPanel>
</Border>
+
+ <!-- Profiles -->
+ <Border
+ Background="WhiteSmoke"
+ Margin="5"
+ BorderBrush="LightBlue"
+ BorderThickness="4"
+ IsVisible="{Binding IsProfilesVisible}"
+ >
+
+ <StackPanel Orientation="Vertical">
+ <Grid
+ ColumnDefinitions="Auto,*" Background="LightBlue">
+ <TextBlock
+ Padding="10" FontWeight="Bold"
+ VerticalAlignment="Center"
+ Text="{Binding Profiles.Count, StringFormat=Profiles: \{0\}}" />
+
+ <StackPanel Orientation="Horizontal"
+ Grid.Column="1"
+ HorizontalAlignment="Right"
+ Spacing="2">
+ <Button
+ VerticalAlignment="Center"
+ ToolTip.Tip="Clear all profiles"
+ IsEnabled="{Binding Profiles.Count}"
+ Command="{Binding ClearProfiles}"
+ >
+ <Image Width="16" Height="16" Source="/Assets/Icons/delete-16x16.png" />
+ </Button>
+
+
+ </StackPanel>
+
+ </Grid>
+
+ <Grid
+ RowDefinitions="Auto,Auto"
+ ColumnDefinitions="*,5,Auto"
+ Margin="15"
+ >
+
+ <ComboBox
+ Margin="0,0,0,10"
+ IsEnabled="{Binding Profiles.Count}"
+ IsVisible="{Binding Profiles.Count}"
+ SelectedItem="{Binding SelectedProfileItem}"
+ Items="{Binding Profiles}" />
+
+ <Button
+ Grid.Row="0"
+ Grid.Column="2"
+ Margin="0,0,0,10"
+ VerticalAlignment="Center"
+ ToolTip.Tip="Remove the selected profile"
+ IsEnabled="{Binding SelectedProfileItem, Converter={x:Static ObjectConverters.IsNotNull}}"
+ IsVisible="{Binding Profiles.Count}"
+ Command="{Binding RemoveSelectedProfile}"
+ >
+ <Image Source="/Assets/Icons/trash-16x16.png" />
+ </Button>
+
+ <TextBox
+ Grid.Row="1"
+ Grid.Column="0"
+ IsEnabled="{Binding ButtonOkEnabled}"
+ Text="{Binding ProfileText}"
+ Watermark="Profile name or leave empty for auto name"
+ />
+
+ <Button
+ Grid.Row="1"
+ Grid.Column="2"
+ VerticalAlignment="Center"
+ ToolTip.Tip="Add a new profile with the current set values"
+ IsEnabled="{Binding ButtonOkEnabled}"
+ Command="{Binding AddProfile}"
+ >
+ <Image Source="/Assets/Icons/plus-16x16.png" />
+ </Button>
+
+ </Grid>
+
+
+ <!--
+ <StackPanel Spacing="20" Margin="15,0,15,15" Orientation="Horizontal">
+ <CheckBox
+ Content="Clear ROI after perform the operation"
+ IsChecked="{Binding ClearROIAfterOperation}"
+ />
+ <Button
+ Padding="5"
+ Content="Clear ROI"
+ Command="{Binding ClearROI}"/>
+ </StackPanel>
+ !-->
+
+ </StackPanel>
+
+ </Border>
<!-- Content -->
<Border
diff --git a/UVtools.WPF/Windows/ToolWindow.axaml.cs b/UVtools.WPF/Windows/ToolWindow.axaml.cs
index 4757e0d..a767cf5 100644
--- a/UVtools.WPF/Windows/ToolWindow.axaml.cs
+++ b/UVtools.WPF/Windows/ToolWindow.axaml.cs
@@ -1,14 +1,20 @@
using System;
+using System.Collections.ObjectModel;
+using System.Diagnostics;
using System.Drawing;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.Threading;
+using DynamicData;
using MessageBox.Avalonia.Enums;
using UVtools.Core;
+using UVtools.Core.Extensions;
+using UVtools.Core.Operations;
using UVtools.WPF.Controls;
using UVtools.WPF.Controls.Tools;
using UVtools.WPF.Extensions;
+using UVtools.WPF.Structures;
namespace UVtools.WPF.Windows
{
@@ -29,8 +35,14 @@ namespace UVtools.WPF.Windows
private uint _layerIndexStart;
private uint _layerIndexEnd;
private bool _isROIVisible;
+
private bool _clearRoiAfterOperation;
+ private bool _isProfilesVisible;
+ private ObservableCollection<Operation> _profiles = new ObservableCollection<Operation>();
+ private Operation _selectedProfileItem;
+ private string _profileText;
+
private IControl _contentControl;
private bool _isButton1Visible;
private string _button1Text = "Reset to defaults";
@@ -42,6 +54,8 @@ namespace UVtools.WPF.Windows
private string _buttonOkText = "Ok";
private bool _buttonOkVisible = true;
private double _scrollViewerMaxHeight=double.PositiveInfinity;
+
+
public double ScrollViewerMaxHeight
{
@@ -92,8 +106,12 @@ namespace UVtools.WPF.Windows
set
{
if (!(ToolControl?.BaseOperation is null))
+ {
+ ToolControl.BaseOperation.LayerRangeSelection = Enumerations.LayerRangeSelection.None;
ToolControl.BaseOperation.LayerIndexStart = value;
-
+ }
+
+ value = value.Clamp(0, App.SlicerFile.LastLayerIndex);
if (!RaiseAndSetIfChanged(ref _layerIndexStart, value)) return;
RaisePropertyChanged(nameof(LayerStartMM));
RaisePropertyChanged(nameof(LayerRangeCountStr));
@@ -115,8 +133,12 @@ namespace UVtools.WPF.Windows
set
{
if (!(ToolControl?.BaseOperation is null))
+ {
+ ToolControl.BaseOperation.LayerRangeSelection = Enumerations.LayerRangeSelection.None;
ToolControl.BaseOperation.LayerIndexEnd = value;
+ }
+ value = value.Clamp(0, App.SlicerFile.LastLayerIndex);
if (!RaiseAndSetIfChanged(ref _layerIndexEnd, value)) return;
RaisePropertyChanged(nameof(LayerEndMM));
RaisePropertyChanged(nameof(LayerRangeCountStr));
@@ -141,33 +163,74 @@ namespace UVtools.WPF.Windows
{
LayerIndexStart = 0;
LayerIndexEnd = MaximumLayerIndex;
+ if(!(ToolControl is null))
+ ToolControl.BaseOperation.LayerRangeSelection = Enumerations.LayerRangeSelection.All;
}
public void SelectCurrentLayer()
{
LayerIndexStart = LayerIndexEnd = App.MainWindow.ActualLayer;
+ if (!(ToolControl is null))
+ ToolControl.BaseOperation.LayerRangeSelection = Enumerations.LayerRangeSelection.Current;
}
public void SelectBottomLayers()
{
LayerIndexStart = 0;
LayerIndexEnd = App.SlicerFile.BottomLayerCount-1u;
+ if (!(ToolControl is null))
+ ToolControl.BaseOperation.LayerRangeSelection = Enumerations.LayerRangeSelection.Bottom;
}
public void SelectNormalLayers()
{
LayerIndexStart = App.SlicerFile.BottomLayerCount;
LayerIndexEnd = MaximumLayerIndex;
+ if (!(ToolControl is null))
+ ToolControl.BaseOperation.LayerRangeSelection = Enumerations.LayerRangeSelection.Normal;
}
public void SelectFirstLayer()
{
LayerIndexStart = LayerIndexEnd = 0;
+ if (!(ToolControl is null))
+ ToolControl.BaseOperation.LayerRangeSelection = Enumerations.LayerRangeSelection.First;
}
public void SelectLastLayer()
{
LayerIndexStart = LayerIndexEnd = MaximumLayerIndex;
+ if (!(ToolControl is null))
+ ToolControl.BaseOperation.LayerRangeSelection = Enumerations.LayerRangeSelection.Last;
+ }
+
+ public void SelectLayers(Enumerations.LayerRangeSelection range)
+ {
+ switch (range)
+ {
+ case Enumerations.LayerRangeSelection.None:
+ break;
+ case Enumerations.LayerRangeSelection.All:
+ SelectAllLayers();
+ break;
+ case Enumerations.LayerRangeSelection.Current:
+ SelectCurrentLayer();
+ break;
+ case Enumerations.LayerRangeSelection.Bottom:
+ SelectBottomLayers();
+ break;
+ case Enumerations.LayerRangeSelection.Normal:
+ SelectNormalLayers();
+ break;
+ case Enumerations.LayerRangeSelection.First:
+ SelectFirstLayer();
+ break;
+ case Enumerations.LayerRangeSelection.Last:
+ SelectLastLayer();
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
}
#endregion
@@ -203,6 +266,98 @@ namespace UVtools.WPF.Windows
#endregion
+ #region Profiles
+ public bool IsProfilesVisible
+ {
+ get => _isProfilesVisible;
+ set => RaiseAndSetIfChanged(ref _isProfilesVisible, value);
+ }
+
+ public ObservableCollection<Operation> Profiles
+ {
+ get => _profiles;
+ set => RaiseAndSetIfChanged(ref _profiles, value);
+ }
+
+ public Operation SelectedProfileItem
+ {
+ get => _selectedProfileItem;
+ set
+ {
+ if(!RaiseAndSetIfChanged(ref _selectedProfileItem, value) || value is null) return;
+ var operation = _selectedProfileItem.Clone();
+ operation.ProfileName = null;
+ ToolControl.BaseOperation = operation;
+ SelectLayers(operation.LayerRangeSelection);
+
+ if (operation is OperationMorph operationMorph && ToolControl is ToolMorphControl toolMorphControl)
+ {
+ toolMorphControl.MorphSelectedIndex = operationMorph.MorphOperationIndex;
+ }
+ else if (operation is OperationBlur operationBlur && ToolControl is ToolBlurControl toolBlurControl)
+ {
+ toolBlurControl.SelectedAlgorithmIndex = operationBlur.BlurTypeIndex;
+ }
+ }
+ }
+
+ public string ProfileText
+ {
+ get => _profileText;
+ set => RaiseAndSetIfChanged(ref _profileText, value);
+ }
+
+ public async void AddProfile()
+ {
+ var name = string.IsNullOrWhiteSpace(_profileText) ? null : _profileText.Trim();
+ var operation = OperationProfiles.FindByName(ToolControl.BaseOperation, name);
+ if (!(operation is null))
+ {
+ if (await this.MessageBoxQuestion(
+ $"A profile with same name or settings already exists.\nDo you want to overwrite:\n{operation}",
+ "Overwrite profile?") != ButtonResult.Yes) return;
+ /*var index = OperationProfiles.Instance.IndexOf(operation);
+ OperationProfiles.Profiles[index] = ToolControl.BaseOperation;
+ index = Profiles.IndexOf(operation);
+ Profiles[index] = ToolControl.BaseOperation;*/
+
+ OperationProfiles.RemoveProfile(operation, false);
+ Profiles.Remove(operation);
+ }
+
+ var toAdd = ToolControl.BaseOperation.Clone();
+ toAdd.ProfileName = string.IsNullOrWhiteSpace(_profileText) ? null : _profileText.Trim();
+ OperationProfiles.AddProfile(toAdd);
+ Profiles.Insert(0, toAdd);
+
+ ProfileText = null;
+ }
+
+ public async void RemoveSelectedProfile()
+ {
+ if (_selectedProfileItem is null) return;
+ if (await this.MessageBoxQuestion(
+ $"Are you sure you want to remove the selected profile?\n{_selectedProfileItem}",
+ "Remove selected profile?") != ButtonResult.Yes) return;
+
+ OperationProfiles.RemoveProfile(_selectedProfileItem);
+ Profiles.Remove(_selectedProfileItem);
+ SelectedProfileItem = null;
+ }
+
+ public async void ClearProfiles()
+ {
+ if (Profiles.Count == 0) return;
+ if (await this.MessageBoxQuestion(
+ $"Are you sure you want to clear all the {Profiles.Count} profiles?",
+ "Clear all profiles?") != ButtonResult.Yes) return;
+
+ OperationProfiles.ClearProfiles(Profiles[0].GetType());
+ Profiles.Clear();
+ }
+
+ #endregion
+
#region Content
public bool IsContentVisible => ContentControl is null || ContentControl.IsVisible;
@@ -300,41 +455,23 @@ namespace UVtools.WPF.Windows
toolControl.Margin = new Thickness(15);
Title = toolControl.BaseOperation.Title;
- LayerRangeVisible = toolControl.BaseOperation.LayerRangeSelection != Enumerations.LayerRangeSelection.None;
+ LayerRangeVisible = toolControl.BaseOperation.StartLayerRangeSelection != Enumerations.LayerRangeSelection.None;
//IsROIVisible = toolControl.BaseOperation.CanROI;
ContentControl = toolControl;
ButtonOkText = toolControl.BaseOperation.ButtonOkText;
ButtonOkVisible = ButtonOkEnabled = toolControl.BaseOperation.HaveAction;
- switch (toolControl.BaseOperation.LayerRangeSelection)
- {
- case Enumerations.LayerRangeSelection.None:
- break;
- case Enumerations.LayerRangeSelection.All:
- SelectAllLayers();
- break;
- case Enumerations.LayerRangeSelection.Current:
- SelectCurrentLayer();
- break;
- case Enumerations.LayerRangeSelection.Bottom:
- SelectBottomLayers();
- break;
- case Enumerations.LayerRangeSelection.Normal:
- SelectNormalLayers();
- break;
- case Enumerations.LayerRangeSelection.First:
- SelectFirstLayer();
- break;
- case Enumerations.LayerRangeSelection.Last:
- SelectLastLayer();
- break;
- default:
- throw new ArgumentOutOfRangeException();
- }
+ SelectLayers(toolControl.BaseOperation.StartLayerRangeSelection);
//RaisePropertyChanged(nameof(IsContentVisible));
//RaisePropertyChanged(nameof(IsROIVisible));
+ if (ToolControl.BaseOperation.CanHaveProfiles)
+ {
+ var profiles = OperationProfiles.GetOperations(ToolControl.BaseOperation.GetType());
+ Profiles.AddRange(profiles);
+ IsProfilesVisible = true;
+ }
// Ensure the description don't stretch window
DispatcherTimer.Run(() =>