* (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
This commit is contained in:
Tiago Conceição
2020-11-05 18:27:44 +00:00
parent 50ce7d8f12
commit 5da3c7e173
55 changed files with 1342 additions and 119 deletions
+13
View File
@@ -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
+1 -1
View File
@@ -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;*/
+2
View File
@@ -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; }
+58 -3
View File
@@ -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 { get; set; } = true;
public virtual bool CanROI => true;
/// <summary>
/// Gets if this operation can store profiles
/// </summary>
public virtual bool CanHaveProfiles => true;
/// <summary>
/// Gets if this operation supports cancellation
@@ -105,9 +124,19 @@ 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}]";
}
}
}
}
+25 -1
View File
@@ -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);
}
}
}
+42
View File
@@ -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
}
}
@@ -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; }
@@ -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
}
@@ -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();
+47 -3
View File
@@ -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; set; }
public bool MakeCopy
{
get => _makeCopy;
set => RaiseAndSetIfChanged(ref _makeCopy, value);
}
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
}
}
@@ -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();
@@ -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>();
@@ -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();
@@ -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
+6
View File
@@ -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);
+50
View File
@@ -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
}
}
+3
View File
@@ -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();
@@ -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();
@@ -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
}
}
@@ -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; }
}
+40 -1
View File
@@ -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
}
}
@@ -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
}
}
@@ -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
}
}
@@ -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
}
}
+1 -1
View File
@@ -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>
+13 -11
View File
@@ -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)
@@ -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))
@@ -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");
}
@@ -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(
@@ -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()
@@ -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>
@@ -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)
{
@@ -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()
@@ -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));
@@ -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
{
@@ -24,7 +22,7 @@ namespace UVtools.WPF.Controls.Tools
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");
@@ -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)
{
@@ -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));
@@ -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()
@@ -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");
}
@@ -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) =>
@@ -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)
{
@@ -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");
}
@@ -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,
@@ -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()
@@ -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}"
@@ -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()
@@ -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()
+43 -1
View File
@@ -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
+20
View File
@@ -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>()
+183 -5
View File
@@ -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)
@@ -63,6 +143,104 @@ namespace UVtools.WPF.Structures
Debug.WriteLine(e.ToString());
}
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;
}
}
+270
View File
@@ -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
}
}
+1 -2
View File
@@ -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" />
+10 -2
View File
@@ -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;
}
}
+100
View File
@@ -203,6 +203,106 @@
</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
IsVisible="{Binding IsContentVisible}"
+163 -26
View File
@@ -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";
@@ -43,6 +55,8 @@ namespace UVtools.WPF.Windows
private bool _buttonOkVisible = true;
private double _scrollViewerMaxHeight=double.PositiveInfinity;
public double ScrollViewerMaxHeight
{
get => _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(() =>