diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e782de..fd0ab96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ * (Add) PrusaSlicer Printer: Nova Bene4 Mono * (Add) CWS: Support the GRAY2RGB and RBG2GRAY encoding for Bene Mono * (Add) Layer issue Z map paired with layer navigation tracker bar +* (Add) Setting: Pixel editor can be configured to exit after each apply operation (#45) * (Improvement) When zoom into issue or drawing now it checks bounds of zoom rectangle and only performs ZoomToFit is it will be larger then the viewPort after zoom. Otherwise, it will zoom to the fixed zoom level (Auto zoom to region setting dropped as merged into this) (#42) * (Improvement) Layer and Issues Repair: Detailed description and warning text in this dialog has been moved from main form into tooltips. It's useful information for new users, but not needed to be visible each time repair is run. Less frequently used settings for gap and noise removal iterations have been moved to an advanced settings group that is hidden by default, and can be shown if changes in those settings is desired. For many users, those advanced settings can be left on default and never adjusted. (#43) diff --git a/PrusaSlicer/printer/Kelant S400.ini b/PrusaSlicer/printer/Kelant S400.ini index a9f237a..53b25a5 100644 --- a/PrusaSlicer/printer/Kelant S400.ini +++ b/PrusaSlicer/printer/Kelant S400.ini @@ -1,4 +1,4 @@ -# generated by PrusaSlicer 2.2.0+win64 on 2020-06-29 at 20:32:09 UTC +# generated by PrusaSlicer 2.2.0+win64 on 2020-09-05 at 19:03:56 UTC absolute_correction = 0 area_fill = 50 bed_custom_model = @@ -9,9 +9,9 @@ default_sla_print_profile = 0.05 Normal display_height = 120 display_mirror_x = 0 display_mirror_y = 1 -display_orientation = portrait +display_orientation = landscape display_pixels_x = 2560 -display_pixels_y = 1440 +display_pixels_y = 1600 display_width = 192 elefant_foot_compensation = 0.2 elefant_foot_min_width = 0.2 @@ -25,7 +25,7 @@ min_exposure_time = 1 min_initial_exposure_time = 1 print_host = printer_model = SL1 -printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_SL1\nPRINTER_VENDOR_KELANT\nPRINTER_MODEL_S400\n\nSTART_CUSTOM_VALUES\nFLIP_XY\nLayerOffTime_0\nBottomLightOffDelay_0\nBottomLiftHeight_5\nLiftHeight_5\nBottomLiftSpeed_100\nLiftSpeed_100\nRetractSpeed_100\nBottomLightPWM_255\nLightPWM_255\nAntiAliasing_4 ; Use 0 or 1 for disable AntiAliasing with "printer gamma correction" set to 0, otherwise use multiples of 2 and "gamma correction" set to 1 for enable\nEND_CUSTOM_VALUES +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_SL1\nPRINTER_VENDOR_KELANT\nPRINTER_MODEL_S400\n\nSTART_CUSTOM_VALUES\nLayerOffTime_0\nBottomLightOffDelay_0\nBottomLiftHeight_15\nLiftHeight_15\nBottomLiftSpeed_30\nLiftSpeed_30\nRetractSpeed_300\nBottomLightPWM_255\nLightPWM_255\nAntiAliasing_4 ; Use 0 or 1 for disable AntiAliasing with "printer gamma correction" set to 0, otherwise use multiples of 2 and "gamma correction" set to 1 for enable\nEND_CUSTOM_VALUES printer_settings_id = printer_technology = SLA printer_variant = default diff --git a/PrusaSlicer/sla_print/Universal 0.1 Fast - Medium Supports.ini b/PrusaSlicer/sla_print/Universal 0.1 Fast - Medium Supports.ini index 3edcb20..c43832c 100644 --- a/PrusaSlicer/sla_print/Universal 0.1 Fast - Medium Supports.ini +++ b/PrusaSlicer/sla_print/Universal 0.1 Fast - Medium Supports.ini @@ -1,4 +1,4 @@ -# generated by PrusaSlicer 2.2.0+win64 on 2020-06-11 at 02:28:40 UTC +# generated by PrusaSlicer 2.2.0+win64 on 2020-09-05 at 15:51:14 UTC compatible_printers = compatible_printers_condition = default_sla_print_profile = diff --git a/UVtools.Core/FileFormats/CWSFile.cs b/UVtools.Core/FileFormats/CWSFile.cs index 11ce9c3..62a0787 100644 --- a/UVtools.Core/FileFormats/CWSFile.cs +++ b/UVtools.Core/FileFormats/CWSFile.cs @@ -373,7 +373,7 @@ namespace UVtools.Core.FileFormats progress.ItemCount = OutputSettings.LayersNum; var gcode = GCode.ToString(); - float currentHeight = 0; + //float currentHeight = 0; int layerSize = OutputSettings.LayersNum.ToString().Length; diff --git a/UVtools.Core/Obects/BindableBase.cs b/UVtools.Core/Obects/BindableBase.cs new file mode 100644 index 0000000..fd920f2 --- /dev/null +++ b/UVtools.Core/Obects/BindableBase.cs @@ -0,0 +1,68 @@ +/* + * GNU AFFERO GENERAL PUBLIC LICENSE + * Version 3, 19 November 2007 + * Copyright (C) 2007 Free Software Foundation, Inc. + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace UVtools.Core.Obects +{ + /// + /// Implementation of to simplify models. + /// + public abstract class BindableBase : INotifyPropertyChanged + { + /// + /// Multicast event for property change notifications. + /// + public event PropertyChangedEventHandler PropertyChanged; + + /// + /// Checks if a property already matches a desired value. Sets the property and + /// notifies listeners only when necessary. + /// + /// Type of the property. + /// Reference to a property with both getter and setter. + /// Desired value for the property. + /// + /// Name of the property used to notify listeners. This + /// value is optional and can be provided automatically when invoked from compilers that + /// support CallerMemberName. + /// + /// + /// True if the value was changed, false if the existing value matched the + /// desired value. + /// + protected bool SetProperty(ref T storage, T value, [CallerMemberName] string propertyName = null) + { + if (Equals(storage, value)) + { + return false; + } + + storage = value; + OnPropertyChanged(propertyName); + return true; + } + + /// + /// Notifies listeners that a property value has changed. + /// + /// + /// Name of the property used to notify listeners. This + /// value is optional and can be provided automatically when invoked from compilers + /// that support . + /// + protected void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + var eventHandler = PropertyChanged; + if (!ReferenceEquals(eventHandler, null)) + { + eventHandler(this, new PropertyChangedEventArgs(propertyName)); + } + } + } +} diff --git a/UVtools.Core/Obects/StringTag.cs b/UVtools.Core/Obects/StringTag.cs new file mode 100644 index 0000000..889c54d --- /dev/null +++ b/UVtools.Core/Obects/StringTag.cs @@ -0,0 +1,58 @@ +/* + * GNU AFFERO GENERAL PUBLIC LICENSE + * Version 3, 19 November 2007 + * Copyright (C) 2007 Free Software Foundation, Inc. + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ + +using System; +using System.Collections.Generic; + +namespace UVtools.Core.Obects +{ + public class StringTag : IComparable + { + public string Content { get; set; } + + public object Tag { get; set; } + public string TagString => Tag.ToString(); + + public StringTag(string content, object tag = null) + { + Content = content; + Tag = tag; + } + + private sealed class ContentEqualityComparer : IEqualityComparer + { + public bool Equals(StringTag x, StringTag y) + { + if (ReferenceEquals(x, y)) return true; + if (ReferenceEquals(x, null)) return false; + if (ReferenceEquals(y, null)) return false; + if (x.GetType() != y.GetType()) return false; + return x.Content == y.Content; + } + + public int GetHashCode(StringTag obj) + { + return (obj.Content != null ? obj.Content.GetHashCode() : 0); + } + } + + public static IEqualityComparer ContentComparer { get; } = new ContentEqualityComparer(); + + public int CompareTo(StringTag other) + { + if (ReferenceEquals(this, other)) return 0; + if (ReferenceEquals(null, other)) return 1; + return string.Compare(Content, other.Content, StringComparison.Ordinal); + } + + public override string ToString() + { + return Content; + } + } +} diff --git a/UVtools.Core/Operations/OperationLayerImport.cs b/UVtools.Core/Operations/OperationLayerImport.cs new file mode 100644 index 0000000..591e42c --- /dev/null +++ b/UVtools.Core/Operations/OperationLayerImport.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Threading.Tasks; +using Emgu.CV; +using Emgu.CV.CvEnum; + +namespace UVtools.Core.Operations +{ + public sealed class OperationLayerImport + { + public uint InsertAfterLayerIndex { get; set; } + public bool ReplaceStartLayer { get; set; } + public bool ReplaceSubsequentLayers { get; set; } + public bool DiscardRemainingLayers { get; set; } + + public List Files { get; } = new List(); + + public void Sort() + { + Files.Sort((file1, file2) => string.Compare(Path.GetFileNameWithoutExtension(file1), Path.GetFileNameWithoutExtension(file2), StringComparison.Ordinal)); + } + + public ConcurrentBag Validate(Size resolution) + { + var result = new ConcurrentBag(); + Parallel.ForEach(Files, file => + { + using (Mat mat = CvInvoke.Imread(file, ImreadModes.AnyColor)) + { + if (mat.Size != resolution) + { + result.Add(file); + } + } + }); + return result; + } + + public uint CalculateTotalLayers(uint totalLayers) + { + if (DiscardRemainingLayers) + { + return (uint) (1 + InsertAfterLayerIndex + Files.Count - (ReplaceStartLayer ? 1u : 0u)); + } + if (!ReplaceSubsequentLayers) + { + return (uint)(totalLayers + Files.Count - (ReplaceStartLayer ? 1u : 0u)); + } + + // Need to calculate the total layer count after subsequent replacing, taking in account that layer count can grow + + + return 0; + } + } +} diff --git a/UVtools.GUI/Controls/CtrlDescriptionPanel.Designer.cs b/UVtools.GUI/Controls/CtrlDescriptionPanel.Designer.cs deleted file mode 100644 index 47f6f85..0000000 --- a/UVtools.GUI/Controls/CtrlDescriptionPanel.Designer.cs +++ /dev/null @@ -1,65 +0,0 @@ -namespace UVtools.GUI.Controls -{ - partial class CtrlDescriptionPanel - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Component Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.lbDescription = new System.Windows.Forms.Label(); - this.SuspendLayout(); - // - // lbDescription - // - this.lbDescription.AutoSize = true; - this.lbDescription.Dock = System.Windows.Forms.DockStyle.Fill; - this.lbDescription.Location = new System.Drawing.Point(0, 0); - this.lbDescription.Name = "lbDescription"; - this.lbDescription.Padding = new System.Windows.Forms.Padding(20); - this.lbDescription.Size = new System.Drawing.Size(129, 60); - this.lbDescription.TabIndex = 0; - this.lbDescription.Text = "Description"; - // - // CtrlDescriptionPanel - // - this.AutoSize = true; - this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; - this.BackColor = System.Drawing.Color.WhiteSmoke; - this.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; - this.Controls.Add(this.lbDescription); - this.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); - this.Name = "CtrlDescriptionPanel"; - this.Size = new System.Drawing.Size(129, 60); - this.ResumeLayout(false); - this.PerformLayout(); - - } - - #endregion - - private System.Windows.Forms.Label lbDescription; - } -} diff --git a/UVtools.GUI/Controls/CtrlDescriptionPanel.cs b/UVtools.GUI/Controls/CtrlDescriptionPanel.cs deleted file mode 100644 index 457eefd..0000000 --- a/UVtools.GUI/Controls/CtrlDescriptionPanel.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.ComponentModel; -using System.Windows.Forms; - -namespace UVtools.GUI.Controls -{ - public partial class CtrlDescriptionPanel : UserControl - { - public string Description - { - get => lbDescription.Text; - set => lbDescription.Text = value; - } - - public CtrlDescriptionPanel() - { - InitializeComponent(); - } - } -} diff --git a/UVtools.GUI/Controls/CtrlDescriptionPanel.resx b/UVtools.GUI/Controls/CtrlDescriptionPanel.resx deleted file mode 100644 index 1af7de1..0000000 --- a/UVtools.GUI/Controls/CtrlDescriptionPanel.resx +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file diff --git a/UVtools.GUI/Controls/CtrlToolWindowContent.Designer.cs b/UVtools.GUI/Controls/CtrlToolWindowContent.Designer.cs index 9eb3a35..905dca7 100644 --- a/UVtools.GUI/Controls/CtrlToolWindowContent.Designer.cs +++ b/UVtools.GUI/Controls/CtrlToolWindowContent.Designer.cs @@ -28,20 +28,32 @@ /// private void InitializeComponent() { + this.components = new System.ComponentModel.Container(); + this.toolTip = new System.Windows.Forms.ToolTip(this.components); this.SuspendLayout(); // + // toolTip + // + this.toolTip.AutoPopDelay = 32767; + this.toolTip.InitialDelay = 500; + this.toolTip.ReshowDelay = 100; + this.toolTip.ToolTipIcon = System.Windows.Forms.ToolTipIcon.Info; + this.toolTip.ToolTipTitle = "Information"; + // // CtrlToolWindowContent // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.BackColor = System.Drawing.Color.White; - this.MinimumSize = new System.Drawing.Size(563, 0); + this.MinimumSize = new System.Drawing.Size(540, 0); this.Name = "CtrlToolWindowContent"; - this.Size = new System.Drawing.Size(563, 261); + this.Size = new System.Drawing.Size(540, 261); this.ResumeLayout(false); } #endregion + + public System.Windows.Forms.ToolTip toolTip; } } diff --git a/UVtools.GUI/Controls/CtrlToolWindowContent.cs b/UVtools.GUI/Controls/CtrlToolWindowContent.cs index 99263a7..cfbc47f 100644 --- a/UVtools.GUI/Controls/CtrlToolWindowContent.cs +++ b/UVtools.GUI/Controls/CtrlToolWindowContent.cs @@ -7,18 +7,84 @@ */ using System.ComponentModel; using System.Drawing.Design; +using System.Runtime.CompilerServices; using System.Windows.Forms; +using UVtools.Core.Extensions; +using UVtools.GUI.Annotations; namespace UVtools.GUI.Controls { - public partial class CtrlToolWindowContent : UserControl + public partial class CtrlToolWindowContent : UserControl, INotifyPropertyChanged { + #region BindableBase + /// + /// Multicast event for property change notifications. + /// + public event PropertyChangedEventHandler PropertyChanged; + + /// + /// Checks if a property already matches a desired value. Sets the property and + /// notifies listeners only when necessary. + /// + /// Type of the property. + /// Reference to a property with both getter and setter. + /// Desired value for the property. + /// + /// Name of the property used to notify listeners. This + /// value is optional and can be provided automatically when invoked from compilers that + /// support CallerMemberName. + /// + /// + /// True if the value was changed, false if the existing value matched the + /// desired value. + /// + [NotifyPropertyChangedInvocator] + protected bool SetProperty(ref T storage, T value, [CallerMemberName] string propertyName = null) + { + if (Equals(storage, value)) + { + return false; + } + + storage = value; + OnPropertyChanged(propertyName); + return true; + } + + /// + /// Notifies listeners that a property value has changed. + /// + /// + /// Name of the property used to notify listeners. This + /// value is optional and can be provided automatically when invoked from compilers + /// that support . + /// + [NotifyPropertyChangedInvocator] + protected void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + var eventHandler = PropertyChanged; + if (!ReferenceEquals(eventHandler, null)) + { + eventHandler(this, new PropertyChangedEventArgs(propertyName)); + } + } + #endregion + + #region Properties [Editor("System.ComponentModel.Design.MultilineStringEditor", typeof(UITypeEditor))] [SettingsBindable(true)] public string Description { get; set; } + private bool _buttonOkEnabled; + [SettingsBindable(true)] + public bool ButtonOkEnabled + { + get => _buttonOkEnabled; + set => SetProperty(ref _buttonOkEnabled, value); + } + [Editor("System.ComponentModel.Design.MultilineStringEditor", typeof(UITypeEditor))] [SettingsBindable(true)] public string ButtonOkText { get; set; } = "Ok"; @@ -37,8 +103,8 @@ namespace UVtools.GUI.Controls [SettingsBindable(true)] public uint LayerRangeEnd { get; set; }*/ - [Editor("System.ComponentModel.Design.MultilineStringEditor", typeof(UITypeEditor))] - [SettingsBindable(true)] + [ReadOnly(true)] + [Browsable(false)] public virtual string ConfirmationText { get; } = "do this action?"; #endregion @@ -54,6 +120,11 @@ namespace UVtools.GUI.Controls public virtual bool ValidateForm() => true; + public DialogResult MessageErrorBox(string message, MessageBoxButtons buttons = MessageBoxButtons.OK) => GUIExtensions.MessageErrorBox($"{Text} Error", message, buttons); + public DialogResult MessageQuestionBox(string message, string title = null, MessageBoxButtons buttons = MessageBoxButtons.YesNo) => GUIExtensions.MessageQuestionBox($"{title ?? Text}", message, buttons); + #endregion + + } } diff --git a/UVtools.GUI/Controls/CtrlToolWindowContent.resx b/UVtools.GUI/Controls/CtrlToolWindowContent.resx index 1af7de1..8766f29 100644 --- a/UVtools.GUI/Controls/CtrlToolWindowContent.resx +++ b/UVtools.GUI/Controls/CtrlToolWindowContent.resx @@ -117,4 +117,7 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 17, 17 + \ No newline at end of file diff --git a/UVtools.GUI/Controls/Tools/CtrlToolLayerImport.Designer.cs b/UVtools.GUI/Controls/Tools/CtrlToolLayerImport.Designer.cs index ef7517b..ff76c89 100644 --- a/UVtools.GUI/Controls/Tools/CtrlToolLayerImport.Designer.cs +++ b/UVtools.GUI/Controls/Tools/CtrlToolLayerImport.Designer.cs @@ -28,28 +28,51 @@ /// private void InitializeComponent() { - this.checkBox1 = new System.Windows.Forms.CheckBox(); + this.cbReplaceStartLayer = new System.Windows.Forms.CheckBox(); this.lbHeight = new System.Windows.Forms.Label(); this.nmInsertAfterLayer = new System.Windows.Forms.NumericUpDown(); this.label1 = new System.Windows.Forms.Label(); + this.tsBar = new System.Windows.Forms.ToolStrip(); + this.btnAdd = new System.Windows.Forms.ToolStripButton(); + this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); + this.btnRemove = new System.Windows.Forms.ToolStripButton(); + this.lbCount = new System.Windows.Forms.ToolStripLabel(); + this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator(); + this.btnClear = new System.Windows.Forms.ToolStripButton(); + this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator(); + this.btnSort = new System.Windows.Forms.ToolStripButton(); + this.cbAutoSort = new System.Windows.Forms.CheckBox(); + this.lbResult = new System.Windows.Forms.Label(); + this.splitContainer = new System.Windows.Forms.SplitContainer(); + this.lbFiles = new System.Windows.Forms.ListBox(); + this.pbSelectedImage = new System.Windows.Forms.PictureBox(); + this.cbReplaceSubsequentLayers = new System.Windows.Forms.CheckBox(); + this.cbDiscardRemainingLayers = new System.Windows.Forms.CheckBox(); ((System.ComponentModel.ISupportInitialize)(this.nmInsertAfterLayer)).BeginInit(); + this.tsBar.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit(); + this.splitContainer.Panel1.SuspendLayout(); + this.splitContainer.Panel2.SuspendLayout(); + this.splitContainer.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pbSelectedImage)).BeginInit(); this.SuspendLayout(); // - // checkBox1 + // cbReplaceStartLayer // - this.checkBox1.AutoSize = true; - this.checkBox1.Location = new System.Drawing.Point(212, 74); - this.checkBox1.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); - this.checkBox1.Name = "checkBox1"; - this.checkBox1.Size = new System.Drawing.Size(153, 24); - this.checkBox1.TabIndex = 33; - this.checkBox1.Text = "Replace this layer"; - this.checkBox1.UseVisualStyleBackColor = true; + this.cbReplaceStartLayer.AutoSize = true; + this.cbReplaceStartLayer.Location = new System.Drawing.Point(10, 49); + this.cbReplaceStartLayer.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.cbReplaceStartLayer.Name = "cbReplaceStartLayer"; + this.cbReplaceStartLayer.Size = new System.Drawing.Size(153, 24); + this.cbReplaceStartLayer.TabIndex = 33; + this.cbReplaceStartLayer.Text = "Replace this layer"; + this.cbReplaceStartLayer.UseVisualStyleBackColor = true; + this.cbReplaceStartLayer.CheckedChanged += new System.EventHandler(this.EventClick); // // lbHeight // this.lbHeight.AutoSize = true; - this.lbHeight.Location = new System.Drawing.Point(376, 116); + this.lbHeight.Location = new System.Drawing.Point(216, 13); this.lbHeight.Margin = new System.Windows.Forms.Padding(6, 0, 6, 0); this.lbHeight.Name = "lbHeight"; this.lbHeight.Size = new System.Drawing.Size(85, 20); @@ -58,7 +81,7 @@ // // nmInsertAfterLayer // - this.nmInsertAfterLayer.Location = new System.Drawing.Point(230, 111); + this.nmInsertAfterLayer.Location = new System.Drawing.Point(138, 10); this.nmInsertAfterLayer.Margin = new System.Windows.Forms.Padding(6, 8, 6, 8); this.nmInsertAfterLayer.Maximum = new decimal(new int[] { 100000, @@ -66,34 +89,223 @@ 0, 0}); this.nmInsertAfterLayer.Name = "nmInsertAfterLayer"; - this.nmInsertAfterLayer.Size = new System.Drawing.Size(135, 26); + this.nmInsertAfterLayer.Size = new System.Drawing.Size(74, 26); this.nmInsertAfterLayer.TabIndex = 30; + this.nmInsertAfterLayer.ValueChanged += new System.EventHandler(this.nmInsertAfterLayer_ValueChanged); // // label1 // this.label1.AutoSize = true; - this.label1.Location = new System.Drawing.Point(26, 116); + this.label1.Location = new System.Drawing.Point(6, 13); this.label1.Margin = new System.Windows.Forms.Padding(6, 0, 6, 0); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(128, 20); this.label1.TabIndex = 31; this.label1.Text = "Insert after layer:"; // + // tsBar + // + this.tsBar.Dock = System.Windows.Forms.DockStyle.Bottom; + this.tsBar.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.tsBar.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.btnAdd, + this.toolStripSeparator1, + this.btnRemove, + this.lbCount, + this.toolStripSeparator2, + this.btnClear, + this.toolStripSeparator3, + this.btnSort}); + this.tsBar.Location = new System.Drawing.Point(0, 177); + this.tsBar.Name = "tsBar"; + this.tsBar.Size = new System.Drawing.Size(707, 25); + this.tsBar.TabIndex = 35; + // + // btnAdd + // + this.btnAdd.Image = global::UVtools.GUI.Properties.Resources.plus_16x16; + this.btnAdd.ImageTransparentColor = System.Drawing.Color.Magenta; + this.btnAdd.Name = "btnAdd"; + this.btnAdd.Size = new System.Drawing.Size(49, 22); + this.btnAdd.Text = "&Add"; + this.btnAdd.Click += new System.EventHandler(this.EventClick); + // + // toolStripSeparator1 + // + this.toolStripSeparator1.Name = "toolStripSeparator1"; + this.toolStripSeparator1.Size = new System.Drawing.Size(6, 25); + // + // btnRemove + // + this.btnRemove.Enabled = false; + this.btnRemove.Image = global::UVtools.GUI.Properties.Resources.minus_16x16; + this.btnRemove.ImageTransparentColor = System.Drawing.Color.Magenta; + this.btnRemove.Name = "btnRemove"; + this.btnRemove.Size = new System.Drawing.Size(70, 22); + this.btnRemove.Text = "&Remove"; + this.btnRemove.Click += new System.EventHandler(this.EventClick); + // + // lbCount + // + this.lbCount.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right; + this.lbCount.Name = "lbCount"; + this.lbCount.Size = new System.Drawing.Size(52, 22); + this.lbCount.Text = "Layers: 0"; + // + // toolStripSeparator2 + // + this.toolStripSeparator2.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right; + this.toolStripSeparator2.Name = "toolStripSeparator2"; + this.toolStripSeparator2.Size = new System.Drawing.Size(6, 25); + // + // btnClear + // + this.btnClear.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right; + this.btnClear.Enabled = false; + this.btnClear.Image = global::UVtools.GUI.Properties.Resources.delete_16x16; + this.btnClear.ImageTransparentColor = System.Drawing.Color.Magenta; + this.btnClear.Name = "btnClear"; + this.btnClear.Size = new System.Drawing.Size(54, 22); + this.btnClear.Text = "&Clear"; + this.btnClear.Click += new System.EventHandler(this.EventClick); + // + // toolStripSeparator3 + // + this.toolStripSeparator3.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right; + this.toolStripSeparator3.Name = "toolStripSeparator3"; + this.toolStripSeparator3.Size = new System.Drawing.Size(6, 25); + // + // btnSort + // + this.btnSort.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right; + this.btnSort.Enabled = false; + this.btnSort.Image = global::UVtools.GUI.Properties.Resources.sort_alpha_up_16x16; + this.btnSort.ImageTransparentColor = System.Drawing.Color.Magenta; + this.btnSort.Name = "btnSort"; + this.btnSort.Size = new System.Drawing.Size(116, 22); + this.btnSort.Text = "&Sort by file name"; + this.btnSort.Click += new System.EventHandler(this.EventClick); + // + // cbAutoSort + // + this.cbAutoSort.AutoSize = true; + this.cbAutoSort.Checked = true; + this.cbAutoSort.CheckState = System.Windows.Forms.CheckState.Checked; + this.cbAutoSort.Location = new System.Drawing.Point(10, 83); + this.cbAutoSort.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.cbAutoSort.Name = "cbAutoSort"; + this.cbAutoSort.Size = new System.Drawing.Size(226, 24); + this.cbAutoSort.TabIndex = 36; + this.cbAutoSort.Text = "Auto sort layers by file name"; + this.cbAutoSort.UseVisualStyleBackColor = true; + // + // lbResult + // + this.lbResult.AutoSize = true; + this.lbResult.Location = new System.Drawing.Point(6, 117); + this.lbResult.Margin = new System.Windows.Forms.Padding(6, 0, 6, 0); + this.lbResult.Name = "lbResult"; + this.lbResult.Size = new System.Drawing.Size(61, 20); + this.lbResult.TabIndex = 37; + this.lbResult.Text = " "; + // + // splitContainer + // + this.splitContainer.Dock = System.Windows.Forms.DockStyle.Bottom; + this.splitContainer.Location = new System.Drawing.Point(0, 202); + this.splitContainer.Name = "splitContainer"; + // + // splitContainer.Panel1 + // + this.splitContainer.Panel1.Controls.Add(this.lbFiles); + // + // splitContainer.Panel2 + // + this.splitContainer.Panel2.Controls.Add(this.pbSelectedImage); + this.splitContainer.Size = new System.Drawing.Size(707, 376); + this.splitContainer.SplitterDistance = 415; + this.splitContainer.TabIndex = 38; + // + // lbFiles + // + this.lbFiles.Dock = System.Windows.Forms.DockStyle.Fill; + this.lbFiles.FormattingEnabled = true; + this.lbFiles.HorizontalScrollbar = true; + this.lbFiles.ItemHeight = 20; + this.lbFiles.Location = new System.Drawing.Point(0, 0); + this.lbFiles.Name = "lbFiles"; + this.lbFiles.SelectionMode = System.Windows.Forms.SelectionMode.MultiExtended; + this.lbFiles.Size = new System.Drawing.Size(415, 376); + this.lbFiles.TabIndex = 35; + this.lbFiles.SelectedIndexChanged += new System.EventHandler(this.lbFiles_SelectedIndexChanged); + this.lbFiles.KeyUp += new System.Windows.Forms.KeyEventHandler(this.lbFiles_KeyUp); + this.lbFiles.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.lbFiles_MouseDoubleClick); + // + // pbSelectedImage + // + this.pbSelectedImage.Dock = System.Windows.Forms.DockStyle.Fill; + this.pbSelectedImage.Location = new System.Drawing.Point(0, 0); + this.pbSelectedImage.Name = "pbSelectedImage"; + this.pbSelectedImage.Size = new System.Drawing.Size(288, 376); + this.pbSelectedImage.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom; + this.pbSelectedImage.TabIndex = 0; + this.pbSelectedImage.TabStop = false; + // + // cbReplaceSubsequentLayers + // + this.cbReplaceSubsequentLayers.AutoSize = true; + this.cbReplaceSubsequentLayers.Location = new System.Drawing.Point(171, 49); + this.cbReplaceSubsequentLayers.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.cbReplaceSubsequentLayers.Name = "cbReplaceSubsequentLayers"; + this.cbReplaceSubsequentLayers.Size = new System.Drawing.Size(220, 24); + this.cbReplaceSubsequentLayers.TabIndex = 39; + this.cbReplaceSubsequentLayers.Text = "Replace subsequent layers"; + this.cbReplaceSubsequentLayers.UseVisualStyleBackColor = true; + this.cbReplaceSubsequentLayers.CheckedChanged += new System.EventHandler(this.EventClick); + // + // cbDiscardRemainingLayers + // + this.cbDiscardRemainingLayers.AutoSize = true; + this.cbDiscardRemainingLayers.Enabled = false; + this.cbDiscardRemainingLayers.Location = new System.Drawing.Point(399, 49); + this.cbDiscardRemainingLayers.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.cbDiscardRemainingLayers.Name = "cbDiscardRemainingLayers"; + this.cbDiscardRemainingLayers.Size = new System.Drawing.Size(200, 24); + this.cbDiscardRemainingLayers.TabIndex = 40; + this.cbDiscardRemainingLayers.Text = "Discard remaining layers"; + this.cbDiscardRemainingLayers.UseVisualStyleBackColor = true; + this.cbDiscardRemainingLayers.CheckedChanged += new System.EventHandler(this.EventClick); + // // CtrlToolLayerImport // this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.Controls.Add(this.checkBox1); + this.ButtonOkText = "Import"; + this.Controls.Add(this.cbDiscardRemainingLayers); + this.Controls.Add(this.cbReplaceSubsequentLayers); + this.Controls.Add(this.lbResult); + this.Controls.Add(this.cbAutoSort); + this.Controls.Add(this.tsBar); + this.Controls.Add(this.cbReplaceStartLayer); this.Controls.Add(this.lbHeight); this.Controls.Add(this.nmInsertAfterLayer); this.Controls.Add(this.label1); - this.Description = "Import layer(s) from local files into a selected height.\r\nNOTE: Images must respe" + - "ct file resolution and greyscale color."; + this.Controls.Add(this.splitContainer); + this.Description = "Import layer(s) from local file(s) into the model at a selected height.\r\nNOTE: Im" + + "ages must respect file resolution and in greyscale color."; this.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.LayerRangeVisible = false; this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.Name = "CtrlToolLayerImport"; - this.Size = new System.Drawing.Size(563, 263); + this.Size = new System.Drawing.Size(707, 578); ((System.ComponentModel.ISupportInitialize)(this.nmInsertAfterLayer)).EndInit(); + this.tsBar.ResumeLayout(false); + this.tsBar.PerformLayout(); + this.splitContainer.Panel1.ResumeLayout(false); + this.splitContainer.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).EndInit(); + this.splitContainer.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.pbSelectedImage)).EndInit(); this.ResumeLayout(false); this.PerformLayout(); @@ -101,9 +313,25 @@ #endregion - private System.Windows.Forms.CheckBox checkBox1; + private System.Windows.Forms.CheckBox cbReplaceStartLayer; private System.Windows.Forms.Label lbHeight; private System.Windows.Forms.NumericUpDown nmInsertAfterLayer; private System.Windows.Forms.Label label1; + private System.Windows.Forms.ToolStrip tsBar; + private System.Windows.Forms.ToolStripButton btnAdd; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator1; + private System.Windows.Forms.ToolStripButton btnRemove; + private System.Windows.Forms.ToolStripButton btnClear; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator2; + private System.Windows.Forms.ToolStripButton btnSort; + private System.Windows.Forms.CheckBox cbAutoSort; + private System.Windows.Forms.ToolStripLabel lbCount; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator3; + private System.Windows.Forms.Label lbResult; + private System.Windows.Forms.SplitContainer splitContainer; + private System.Windows.Forms.ListBox lbFiles; + private System.Windows.Forms.PictureBox pbSelectedImage; + private System.Windows.Forms.CheckBox cbReplaceSubsequentLayers; + private System.Windows.Forms.CheckBox cbDiscardRemainingLayers; } } diff --git a/UVtools.GUI/Controls/Tools/CtrlToolLayerImport.cs b/UVtools.GUI/Controls/Tools/CtrlToolLayerImport.cs index c61d536..1662019 100644 --- a/UVtools.GUI/Controls/Tools/CtrlToolLayerImport.cs +++ b/UVtools.GUI/Controls/Tools/CtrlToolLayerImport.cs @@ -6,18 +6,248 @@ * of this license document, but changing it is not allowed. */ +using System; +using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Text; +using System.Windows.Forms; +using UVtools.Core.Obects; +using UVtools.Core.Operations; + namespace UVtools.GUI.Controls.Tools { public partial class CtrlToolLayerImport : CtrlToolWindowContent { - public CtrlToolLayerImport() + public OperationLayerImport Operation { get; } = new OperationLayerImport(); + + public CtrlToolLayerImport(uint currentLayer = 0) { InitializeComponent(); + Text = "Import Layer(s)"; + + nmInsertAfterLayer.Maximum = Program.SlicerFile.LayerCount; + + nmInsertAfterLayer.Value = currentLayer; + nmInsertAfterLayer_ValueChanged(nmInsertAfterLayer, EventArgs.Empty); } + public void UpdateOperation() + { + Operation.InsertAfterLayerIndex = (uint)nmInsertAfterLayer.Value; + Operation.ReplaceStartLayer = cbReplaceStartLayer.Checked; + Operation.ReplaceSubsequentLayers = cbReplaceSubsequentLayers.Checked; + Operation.DiscardRemainingLayers = cbDiscardRemainingLayers.Checked; + } + + public override bool ValidateForm() { - return true; + UpdateOperation(); + var result = Operation.Validate(Program.FrmMain.ActualLayerImage.Size); + if (result.Count == 0) return true; + + var message = new StringBuilder(); + message.AppendLine($"The following {result.Count} files mismatched the slice resolution of {Program.FrmMain.ActualLayerImage.Size.Width}x{Program.FrmMain.ActualLayerImage.Size.Height}:"); + message.AppendLine(); + uint count = 0; + foreach (var file in result) + { + count++; + if (count == 20) + { + message.AppendLine("... To many to show ..."); + break; + } + message.AppendLine(Path.GetFileNameWithoutExtension(file)); + + } + + message.AppendLine(); + message.AppendLine("Do you want to remove all invalid files from list?"); + if (MessageErrorBox(message.ToString(), MessageBoxButtons.YesNo) == DialogResult.Yes) + { + foreach (var file in result) + { + Operation.Files.Remove(file); + } + UpdateListBox(); + } + + return false; + } + + private void nmInsertAfterLayer_ValueChanged(object sender, EventArgs e) + { + lbHeight.Text = $"({Program.SlicerFile.GetHeightFromLayer((uint) nmInsertAfterLayer.Value)}mm)"; + UpdateResultText(); + } + + private void EventClick(object sender, EventArgs e) + { + if (ReferenceEquals(sender, cbReplaceStartLayer) || + ReferenceEquals(sender, cbReplaceSubsequentLayers) || + ReferenceEquals(sender, cbDiscardRemainingLayers)) + { + if (ReferenceEquals(sender, cbReplaceSubsequentLayers)) + { + cbDiscardRemainingLayers.Enabled = cbReplaceSubsequentLayers.Checked; + if (!cbReplaceSubsequentLayers.Checked) + { + cbDiscardRemainingLayers.Checked = false; + } + } + + UpdateResultText(); + return; + } + + if (ReferenceEquals(sender, btnAdd)) + { + using (var fileOpen = new OpenFileDialog + { + Multiselect = true, + CheckFileExists = true, + Filter = "Image Files(*.PNG;*.BMP;*.JPEG;*.JPG;*.GIF)|*.PNG;*.BMP;*.JPEG;*.JPG;*.GIF" + }) + { + if (fileOpen.ShowDialog() != DialogResult.OK) return; + + Operation.Files.AddRange(fileOpen.FileNames); + + if (cbAutoSort.Checked) + { + Operation.Sort(); + } + + UpdateListBox(); + } + + return; + } + + if (ReferenceEquals(sender, btnRemove)) + { + foreach (StringTag selectedItem in lbFiles.SelectedItems) + { + Operation.Files.Remove(selectedItem.TagString); + } + UpdateListBox(); + + return; + } + + if (ReferenceEquals(sender, btnSort)) + { + Operation.Sort(); + UpdateListBox(); + return; + } + + if (ReferenceEquals(sender, btnClear)) + { + Operation.Files.Clear(); + UpdateListBox(); + return; + } + } + + private void lbFiles_SelectedIndexChanged(object sender, EventArgs e) + { + if (btnRemove.Enabled = lbFiles.SelectedIndex >= 0) + { + var file = lbFiles.SelectedItem as StringTag; + pbSelectedImage.Image = new Bitmap(file.TagString); + } + else + { + pbSelectedImage.Image = null; + } + } + + private void lbFiles_KeyUp(object sender, KeyEventArgs e) + { + if (e.KeyCode == Keys.Escape) + { + lbFiles.SelectedIndices.Clear(); + e.Handled = true; + return; + } + + if (e.KeyCode == Keys.Delete) + { + btnRemove.PerformClick(); + e.Handled = true; + return; + } + + if (e.KeyCode == Keys.A && (ModifierKeys & Keys.Control) != 0) + { + lbFiles.BeginUpdate(); + for (int i = 0; i < lbFiles.Items.Count; i++) + lbFiles.SetSelected(i, true); + lbFiles.EndUpdate(); + e.Handled = true; + return; + } + } + + public void UpdateListBox() + { + lbFiles.Items.Clear(); + + foreach (var file in Operation.Files) + { + var stringTag = new StringTag(Path.GetFileNameWithoutExtension(file), file); + lbFiles.Items.Add(stringTag); + } + + ButtonOkEnabled = btnRemove.Enabled = btnSort.Enabled = btnClear.Enabled = Operation.Files.Count > 0; + lbCount.Text = $"Layers: {Operation.Files.Count}"; + UpdateResultText(); + } + + private void UpdateResultText() + { + if (Operation.Files.Count > 0) + { + UpdateOperation(); + uint modelTotalLayers = Operation.CalculateTotalLayers(Program.SlicerFile.LayerCount); + string textFactor = "grow"; + if (modelTotalLayers < Program.SlicerFile.LayerCount) + { + textFactor = "shrink"; + } + else if (modelTotalLayers == Program.SlicerFile.LayerCount) + { + textFactor = "keep"; + } + lbResult.Text = + $"{Operation.Files.Count} layers will be imported into model starting from layer {nmInsertAfterLayer.Value} {lbHeight.Text}.\n" + + $"Model will {textFactor} from layers {Program.SlicerFile.LayerCount} ({Program.SlicerFile.TotalHeight}mm) to {modelTotalLayers} ({Program.SlicerFile.GetHeightFromLayer(modelTotalLayers, false)}mm)"; + } + else + { + lbResult.Text = string.Empty; + } + } + + private void lbFiles_MouseDoubleClick(object sender, MouseEventArgs e) + { + if (lbFiles.SelectedItem is StringTag file) + { + try + { + using (Process.Start(file.TagString)) + { } + } + catch (Exception exception) + { + Debug.WriteLine(exception); + } + + return; + } } } } diff --git a/UVtools.GUI/Controls/Tools/CtrlToolLayerImport.resx b/UVtools.GUI/Controls/Tools/CtrlToolLayerImport.resx index 1af7de1..b664397 100644 --- a/UVtools.GUI/Controls/Tools/CtrlToolLayerImport.resx +++ b/UVtools.GUI/Controls/Tools/CtrlToolLayerImport.resx @@ -117,4 +117,10 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 17, 17 + + + 107, 17 + \ No newline at end of file diff --git a/UVtools.GUI/Extensions/GUIExtensions.cs b/UVtools.GUI/Extensions/GUIExtensions.cs new file mode 100644 index 0000000..852b80b --- /dev/null +++ b/UVtools.GUI/Extensions/GUIExtensions.cs @@ -0,0 +1,21 @@ +/* + * GNU AFFERO GENERAL PUBLIC LICENSE + * Version 3, 19 November 2007 + * Copyright (C) 2007 Free Software Foundation, Inc. + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ + +using System.Windows.Forms; + +namespace UVtools.Core.Extensions +{ + public static class GUIExtensions + { + public static DialogResult MessageErrorBox(string title, string message, MessageBoxButtons buttons = MessageBoxButtons.OK) => + MessageBox.Show(message, title, buttons, MessageBoxIcon.Error); + + public static DialogResult MessageQuestionBox(string title, string message, MessageBoxButtons buttons = MessageBoxButtons.YesNo) => + MessageBox.Show(message, title, buttons, MessageBoxIcon.Question); + } +} diff --git a/UVtools.GUI/Forms/FrmToolWindow.Designer.cs b/UVtools.GUI/Forms/FrmToolWindow.Designer.cs index c569ed3..c73dcd5 100644 --- a/UVtools.GUI/Forms/FrmToolWindow.Designer.cs +++ b/UVtools.GUI/Forms/FrmToolWindow.Designer.cs @@ -34,9 +34,9 @@ namespace UVtools.GUI.Forms System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FrmToolWindow)); this.pnDescription = new System.Windows.Forms.Panel(); this.lbDescription = new System.Windows.Forms.Label(); + this.pnActions = new System.Windows.Forms.Panel(); this.btnCancel = new System.Windows.Forms.Button(); this.btnOk = new System.Windows.Forms.Button(); - this.pnActions = new System.Windows.Forms.Panel(); this.pnContent = new System.Windows.Forms.Panel(); this.pnLayerRange = new System.Windows.Forms.Panel(); this.gbLayerRange = new System.Windows.Forms.GroupBox(); @@ -86,6 +86,17 @@ namespace UVtools.GUI.Forms this.lbDescription.Text = "Description"; this.lbDescription.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // + // pnActions + // + this.pnActions.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.pnActions.Controls.Add(this.btnCancel); + this.pnActions.Controls.Add(this.btnOk); + this.pnActions.Dock = System.Windows.Forms.DockStyle.Bottom; + this.pnActions.Location = new System.Drawing.Point(0, 182); + this.pnActions.Name = "pnActions"; + this.pnActions.Size = new System.Drawing.Size(547, 78); + this.pnActions.TabIndex = 8; + // // btnCancel // this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); @@ -119,21 +130,11 @@ namespace UVtools.GUI.Forms this.btnOk.UseVisualStyleBackColor = true; this.btnOk.Click += new System.EventHandler(this.EventClick); // - // pnActions - // - this.pnActions.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; - this.pnActions.Controls.Add(this.btnCancel); - this.pnActions.Controls.Add(this.btnOk); - this.pnActions.Dock = System.Windows.Forms.DockStyle.Bottom; - this.pnActions.Location = new System.Drawing.Point(0, 182); - this.pnActions.Name = "pnActions"; - this.pnActions.Size = new System.Drawing.Size(547, 78); - this.pnActions.TabIndex = 8; - // // pnContent // this.pnContent.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; this.pnContent.BackColor = System.Drawing.Color.White; + this.pnContent.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; this.pnContent.Dock = System.Windows.Forms.DockStyle.Fill; this.pnContent.Location = new System.Drawing.Point(0, 170); this.pnContent.Name = "pnContent"; diff --git a/UVtools.GUI/Forms/FrmToolWindow.cs b/UVtools.GUI/Forms/FrmToolWindow.cs index f73f9e1..08fc7d1 100644 --- a/UVtools.GUI/Forms/FrmToolWindow.cs +++ b/UVtools.GUI/Forms/FrmToolWindow.cs @@ -11,6 +11,7 @@ using System.ComponentModel; using System.Drawing; using System.Drawing.Design; using System.Windows.Forms; +using UVtools.Core.Extensions; using UVtools.GUI.Controls; namespace UVtools.GUI.Forms @@ -79,8 +80,8 @@ namespace UVtools.GUI.Forms set => nmLayerRangeEnd.Value = value; } - [Editor("System.ComponentModel.Design.MultilineStringEditor", typeof(UITypeEditor))] - [SettingsBindable(true)] + [ReadOnly(true)] + [Browsable(false)] public virtual string ConfirmationText { get; } = "do this action?"; @@ -93,11 +94,17 @@ namespace UVtools.GUI.Forms InitializeComponent(); } - public FrmToolWindow(string description, string buttonOkText, bool layerRangeVisible = true) : this() + public FrmToolWindow(string description, string buttonOkText, bool layerRangeVisible = true, bool layerRangeEndVisible = true) : this() { + if (!layerRangeVisible) + { + Height -= pnLayerRange.Height; + } + Description = description; ButtonOkText = buttonOkText; LayerRangeVisible = layerRangeVisible; + LayerRangeEndVisible = layerRangeEndVisible; LayerRangeEnd = Program.SlicerFile.LayerCount - 1; EventValueChanged(nmLayerRangeStart, EventArgs.Empty); @@ -109,13 +116,26 @@ namespace UVtools.GUI.Forms LayerRangeStart = LayerRangeEnd = layerIndex; } - public FrmToolWindow(CtrlToolWindowContent content, bool layerRangeVisible = true) : this(content.Description, content.ButtonOkText, layerRangeVisible) + public FrmToolWindow(CtrlToolWindowContent content) : this(content.Description, content.ButtonOkText, content.LayerRangeVisible, content.LayerRangeEndVisible) { + Text = content.Text; pnContent.Controls.Add(content); Width = Math.Max(MinimumSize.Width, content.Width); Height += content.Height; content.Dock = DockStyle.Fill; + btnOk.Enabled = content.ButtonOkEnabled; Content = content; + + content.PropertyChanged += ContentOnPropertyChanged; + } + + private void ContentOnPropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(Content.ButtonOkEnabled)) + { + btnOk.Enabled = Content.ButtonOkEnabled; + return; + } } public FrmToolWindow(CtrlToolWindowContent content, uint layerIndex) : this(content) @@ -217,16 +237,15 @@ namespace UVtools.GUI.Forms if (LayerRangeStart > LayerRangeEnd) { - MessageBox.Show("Layer range start can't be higher than layer end.\nPlease fix and try again.", Text, MessageBoxButtons.OK, MessageBoxIcon.Error); + MessageErrorBox("Layer range start can't be higher than layer end.\nPlease fix and try again."); nmLayerRangeStart.Select(); return; } if (!ValidateForm()) return; - if (!ReferenceEquals(Content, null) && !ValidateForm()) return; + if (!ReferenceEquals(Content, null) && !Content.ValidateForm()) return; - if (MessageBox.Show($"Are you sure you want to {Content?.ConfirmationText ?? ConfirmationText}", Text, MessageBoxButtons.YesNo, - MessageBoxIcon.Question) == DialogResult.Yes) + if (MessageQuestionBox($"Are you sure you want to {Content?.ConfirmationText ?? ConfirmationText}") == DialogResult.Yes) { DialogResult = DialogResult.OK; Close(); @@ -250,7 +269,8 @@ namespace UVtools.GUI.Forms if (layerIndex >= Program.SlicerFile.LayerCount) return; var layer = Program.SlicerFile[layerIndex]; var text = $"({layer.PositionZ}mm)"; - uint layerCount = (uint) Math.Max(0, nmLayerRangeEnd.Value - nmLayerRangeStart.Value + 1); + + uint layerCount = LayerRangeEndVisible ? (uint) Math.Max(0, nmLayerRangeEnd.Value - nmLayerRangeStart.Value + 1) : 1; lbLayerRangeCount.Text = $"({layerCount} layer / {Program.SlicerFile.LayerHeight * layerCount}mm)"; if (layerCount == 0) @@ -285,6 +305,9 @@ namespace UVtools.GUI.Forms public virtual bool ValidateForm() => true; + public DialogResult MessageErrorBox(string message) => GUIExtensions.MessageErrorBox($"{Text} Error", message); + public DialogResult MessageQuestionBox(string message, string title = null) => GUIExtensions.MessageQuestionBox($"{title ?? Text}", message); + #endregion } diff --git a/UVtools.GUI/Forms/FrmToolWindow.resx b/UVtools.GUI/Forms/FrmToolWindow.resx index 69243ad..95100b0 100644 --- a/UVtools.GUI/Forms/FrmToolWindow.resx +++ b/UVtools.GUI/Forms/FrmToolWindow.resx @@ -123,15 +123,15 @@ True + + True + True True - - True - True diff --git a/UVtools.GUI/FrmMain.cs b/UVtools.GUI/FrmMain.cs index b32301f..a5a6fa6 100644 --- a/UVtools.GUI/FrmMain.cs +++ b/UVtools.GUI/FrmMain.cs @@ -1160,7 +1160,7 @@ namespace UVtools.GUI if (ReferenceEquals(sender, btnLayerImageActionImport)) { - using (var frm = new FrmToolWindow(new CtrlToolLayerImport(), ActualLayer)) + using (var frm = new FrmToolWindow(new CtrlToolLayerImport(ActualLayer))) { frm.ShowDialog(); } diff --git a/UVtools.GUI/Images/minus_16x16.png b/UVtools.GUI/Images/minus_16x16.png new file mode 100644 index 0000000..4fd33f9 Binary files /dev/null and b/UVtools.GUI/Images/minus_16x16.png differ diff --git a/UVtools.GUI/Images/plus-16x16.png b/UVtools.GUI/Images/plus-16x16.png new file mode 100644 index 0000000..7614e37 Binary files /dev/null and b/UVtools.GUI/Images/plus-16x16.png differ diff --git a/UVtools.GUI/Properties/Annotations.cs b/UVtools.GUI/Properties/Annotations.cs new file mode 100644 index 0000000..f0c2603 --- /dev/null +++ b/UVtools.GUI/Properties/Annotations.cs @@ -0,0 +1,1236 @@ +/* MIT License + +Copyright (c) 2016 JetBrains http://www.jetbrains.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. */ + +using System; +// ReSharper disable InheritdocConsiderUsage + +#pragma warning disable 1591 +// ReSharper disable UnusedMember.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable IntroduceOptionalParameters.Global +// ReSharper disable MemberCanBeProtected.Global +// ReSharper disable InconsistentNaming + +namespace UVtools.GUI.Annotations +{ + /// + /// Indicates that the value of the marked element could be null sometimes, + /// so checking for null is required before its usage. + /// + /// + /// [CanBeNull] object Test() => null; + /// + /// void UseTest() { + /// var p = Test(); + /// var s = p.ToString(); // Warning: Possible 'System.NullReferenceException' + /// } + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event | + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)] + public sealed class CanBeNullAttribute : Attribute { } + + /// + /// Indicates that the value of the marked element can never be null. + /// + /// + /// [NotNull] object Foo() { + /// return null; // Warning: Possible 'null' assignment + /// } + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event | + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)] + public sealed class NotNullAttribute : Attribute { } + + /// + /// Can be applied to symbols of types derived from IEnumerable as well as to symbols of Task + /// and Lazy classes to indicate that the value of a collection item, of the Task.Result property + /// or of the Lazy.Value property can never be null. + /// + /// + /// public void Foo([ItemNotNull]List<string> books) + /// { + /// foreach (var book in books) { + /// if (book != null) // Warning: Expression is always true + /// Console.WriteLine(book.ToUpper()); + /// } + /// } + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field)] + public sealed class ItemNotNullAttribute : Attribute { } + + /// + /// Can be applied to symbols of types derived from IEnumerable as well as to symbols of Task + /// and Lazy classes to indicate that the value of a collection item, of the Task.Result property + /// or of the Lazy.Value property can be null. + /// + /// + /// public void Foo([ItemCanBeNull]List<string> books) + /// { + /// foreach (var book in books) + /// { + /// // Warning: Possible 'System.NullReferenceException' + /// Console.WriteLine(book.ToUpper()); + /// } + /// } + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field)] + public sealed class ItemCanBeNullAttribute : Attribute { } + + /// + /// Indicates that the marked method builds string by the format pattern and (optional) arguments. + /// The parameter, which contains the format string, should be given in constructor. The format string + /// should be in -like form. + /// + /// + /// [StringFormatMethod("message")] + /// void ShowError(string message, params object[] args) { /* do something */ } + /// + /// void Foo() { + /// ShowError("Failed: {0}"); // Warning: Non-existing argument in format string + /// } + /// + [AttributeUsage( + AttributeTargets.Constructor | AttributeTargets.Method | + AttributeTargets.Property | AttributeTargets.Delegate)] + public sealed class StringFormatMethodAttribute : Attribute + { + /// + /// Specifies which parameter of an annotated method should be treated as the format string + /// + public StringFormatMethodAttribute([NotNull] string formatParameterName) + { + FormatParameterName = formatParameterName; + } + + [NotNull] public string FormatParameterName { get; } + } + + /// + /// Use this annotation to specify a type that contains static or const fields + /// with values for the annotated property/field/parameter. + /// The specified type will be used to improve completion suggestions. + /// + /// + /// namespace TestNamespace + /// { + /// public class Constants + /// { + /// public static int INT_CONST = 1; + /// public const string STRING_CONST = "1"; + /// } + /// + /// public class Class1 + /// { + /// [ValueProvider("TestNamespace.Constants")] public int myField; + /// public void Foo([ValueProvider("TestNamespace.Constants")] string str) { } + /// + /// public void Test() + /// { + /// Foo(/*try completion here*/);// + /// myField = /*try completion here*/ + /// } + /// } + /// } + /// + [AttributeUsage( + AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field, + AllowMultiple = true)] + public sealed class ValueProviderAttribute : Attribute + { + public ValueProviderAttribute([NotNull] string name) + { + Name = name; + } + + [NotNull] public string Name { get; } + } + + /// + /// Indicates that the integral value falls into the specified interval. + /// It's allowed to specify multiple non-intersecting intervals. + /// Values of interval boundaries are inclusive. + /// + /// + /// void Foo([ValueRange(0, 100)] int value) { + /// if (value == -1) { // Warning: Expression is always 'false' + /// ... + /// } + /// } + /// + [AttributeUsage( + AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property | + AttributeTargets.Method | AttributeTargets.Delegate, + AllowMultiple = true)] + public sealed class ValueRangeAttribute : Attribute + { + public object From { get; } + public object To { get; } + + public ValueRangeAttribute(long from, long to) + { + From = from; + To = to; + } + + public ValueRangeAttribute(ulong from, ulong to) + { + From = from; + To = to; + } + + public ValueRangeAttribute(long value) + { + From = To = value; + } + + public ValueRangeAttribute(ulong value) + { + From = To = value; + } + } + + /// + /// Indicates that the integral value never falls below zero. + /// + /// + /// void Foo([NonNegativeValue] int value) { + /// if (value == -1) { // Warning: Expression is always 'false' + /// ... + /// } + /// } + /// + [AttributeUsage( + AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property | + AttributeTargets.Method | AttributeTargets.Delegate)] + public sealed class NonNegativeValueAttribute : Attribute { } + + /// + /// Indicates that the function argument should be a string literal and match one + /// of the parameters of the caller function. For example, ReSharper annotates + /// the parameter of . + /// + /// + /// void Foo(string param) { + /// if (param == null) + /// throw new ArgumentNullException("par"); // Warning: Cannot resolve symbol + /// } + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class InvokerParameterNameAttribute : Attribute { } + + /// + /// Indicates that the method is contained in a type that implements + /// System.ComponentModel.INotifyPropertyChanged interface and this method + /// is used to notify that some property value changed. + /// + /// + /// The method should be non-static and conform to one of the supported signatures: + /// + /// NotifyChanged(string) + /// NotifyChanged(params string[]) + /// NotifyChanged{T}(Expression{Func{T}}) + /// NotifyChanged{T,U}(Expression{Func{T,U}}) + /// SetProperty{T}(ref T, T, string) + /// + /// + /// + /// public class Foo : INotifyPropertyChanged { + /// public event PropertyChangedEventHandler PropertyChanged; + /// + /// [NotifyPropertyChangedInvocator] + /// protected virtual void NotifyChanged(string propertyName) { ... } + /// + /// string _name; + /// + /// public string Name { + /// get { return _name; } + /// set { _name = value; NotifyChanged("LastName"); /* Warning */ } + /// } + /// } + /// + /// Examples of generated notifications: + /// + /// NotifyChanged("Property") + /// NotifyChanged(() => Property) + /// NotifyChanged((VM x) => x.Property) + /// SetProperty(ref myField, value, "Property") + /// + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class NotifyPropertyChangedInvocatorAttribute : Attribute + { + public NotifyPropertyChangedInvocatorAttribute() { } + public NotifyPropertyChangedInvocatorAttribute([NotNull] string parameterName) + { + ParameterName = parameterName; + } + + [CanBeNull] public string ParameterName { get; } + } + + /// + /// Describes dependency between method input and output. + /// + /// + ///

Function Definition Table syntax:

+ /// + /// FDT ::= FDTRow [;FDTRow]* + /// FDTRow ::= Input => Output | Output <= Input + /// Input ::= ParameterName: Value [, Input]* + /// Output ::= [ParameterName: Value]* {halt|stop|void|nothing|Value} + /// Value ::= true | false | null | notnull | canbenull + /// + /// If the method has a single input parameter, its name could be omitted.
+ /// Using halt (or void/nothing, which is the same) for the method output + /// means that the method doesn't return normally (throws or terminates the process).
+ /// Value canbenull is only applicable for output parameters.
+ /// You can use multiple [ContractAnnotation] for each FDT row, or use single attribute + /// with rows separated by semicolon. There is no notion of order rows, all rows are checked + /// for applicability and applied per each program state tracked by the analysis engine.
+ ///
+ /// + /// + /// [ContractAnnotation("=> halt")] + /// public void TerminationMethod() + /// + /// + /// [ContractAnnotation("null <= param:null")] // reverse condition syntax + /// public string GetName(string surname) + /// + /// + /// [ContractAnnotation("s:null => true")] + /// public bool IsNullOrEmpty(string s) // string.IsNullOrEmpty() + /// + /// + /// // A method that returns null if the parameter is null, + /// // and not null if the parameter is not null + /// [ContractAnnotation("null => null; notnull => notnull")] + /// public object Transform(object data) + /// + /// + /// [ContractAnnotation("=> true, result: notnull; => false, result: null")] + /// public bool TryParse(string s, out Person result) + /// + /// + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + public sealed class ContractAnnotationAttribute : Attribute + { + public ContractAnnotationAttribute([NotNull] string contract) + : this(contract, false) { } + + public ContractAnnotationAttribute([NotNull] string contract, bool forceFullStates) + { + Contract = contract; + ForceFullStates = forceFullStates; + } + + [NotNull] public string Contract { get; } + + public bool ForceFullStates { get; } + } + + /// + /// Indicates whether the marked element should be localized. + /// + /// + /// [LocalizationRequiredAttribute(true)] + /// class Foo { + /// string str = "my string"; // Warning: Localizable string + /// } + /// + [AttributeUsage(AttributeTargets.All)] + public sealed class LocalizationRequiredAttribute : Attribute + { + public LocalizationRequiredAttribute() : this(true) { } + + public LocalizationRequiredAttribute(bool required) + { + Required = required; + } + + public bool Required { get; } + } + + /// + /// Indicates that the value of the marked type (or its derivatives) + /// cannot be compared using '==' or '!=' operators and Equals() + /// should be used instead. However, using '==' or '!=' for comparison + /// with null is always permitted. + /// + /// + /// [CannotApplyEqualityOperator] + /// class NoEquality { } + /// + /// class UsesNoEquality { + /// void Test() { + /// var ca1 = new NoEquality(); + /// var ca2 = new NoEquality(); + /// if (ca1 != null) { // OK + /// bool condition = ca1 == ca2; // Warning + /// } + /// } + /// } + /// + [AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct)] + public sealed class CannotApplyEqualityOperatorAttribute : Attribute { } + + /// + /// When applied to a target attribute, specifies a requirement for any type marked + /// with the target attribute to implement or inherit specific type or types. + /// + /// + /// [BaseTypeRequired(typeof(IComponent)] // Specify requirement + /// class ComponentAttribute : Attribute { } + /// + /// [Component] // ComponentAttribute requires implementing IComponent interface + /// class MyComponent : IComponent { } + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + [BaseTypeRequired(typeof(Attribute))] + public sealed class BaseTypeRequiredAttribute : Attribute + { + public BaseTypeRequiredAttribute([NotNull] Type baseType) + { + BaseType = baseType; + } + + [NotNull] public Type BaseType { get; } + } + + /// + /// Indicates that the marked symbol is used implicitly (e.g. via reflection, in external library), + /// so this symbol will not be reported as unused (as well as by other usage inspections). + /// + [AttributeUsage(AttributeTargets.All)] + public sealed class UsedImplicitlyAttribute : Attribute + { + public UsedImplicitlyAttribute() + : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } + + public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags) + : this(useKindFlags, ImplicitUseTargetFlags.Default) { } + + public UsedImplicitlyAttribute(ImplicitUseTargetFlags targetFlags) + : this(ImplicitUseKindFlags.Default, targetFlags) { } + + public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) + { + UseKindFlags = useKindFlags; + TargetFlags = targetFlags; + } + + public ImplicitUseKindFlags UseKindFlags { get; } + + public ImplicitUseTargetFlags TargetFlags { get; } + } + + /// + /// Can be applied to attributes, type parameters, and parameters of a type assignable from . + /// When applied to an attribute, the decorated attribute behaves the same as . + /// When applied to a type parameter or to a parameter of type , indicates that the corresponding type + /// is used implicitly. + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.GenericParameter | AttributeTargets.Parameter)] + public sealed class MeansImplicitUseAttribute : Attribute + { + public MeansImplicitUseAttribute() + : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } + + public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags) + : this(useKindFlags, ImplicitUseTargetFlags.Default) { } + + public MeansImplicitUseAttribute(ImplicitUseTargetFlags targetFlags) + : this(ImplicitUseKindFlags.Default, targetFlags) { } + + public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) + { + UseKindFlags = useKindFlags; + TargetFlags = targetFlags; + } + + [UsedImplicitly] public ImplicitUseKindFlags UseKindFlags { get; } + + [UsedImplicitly] public ImplicitUseTargetFlags TargetFlags { get; } + } + + /// + /// Specify the details of implicitly used symbol when it is marked + /// with or . + /// + [Flags] + public enum ImplicitUseKindFlags + { + Default = Access | Assign | InstantiatedWithFixedConstructorSignature, + /// Only entity marked with attribute considered used. + Access = 1, + /// Indicates implicit assignment to a member. + Assign = 2, + /// + /// Indicates implicit instantiation of a type with fixed constructor signature. + /// That means any unused constructor parameters won't be reported as such. + /// + InstantiatedWithFixedConstructorSignature = 4, + /// Indicates implicit instantiation of a type. + InstantiatedNoFixedConstructorSignature = 8, + } + + /// + /// Specify what is considered to be used implicitly when marked + /// with or . + /// + [Flags] + public enum ImplicitUseTargetFlags + { + Default = Itself, + Itself = 1, + /// Members of entity marked with attribute are considered used. + Members = 2, + /// Inherited entities are considered used. + WithInheritors = 4, + /// Entity marked with attribute and all its members considered used. + WithMembers = Itself | Members + } + + /// + /// This attribute is intended to mark publicly available API + /// which should not be removed and so is treated as used. + /// + [MeansImplicitUse(ImplicitUseTargetFlags.WithMembers)] + [AttributeUsage(AttributeTargets.All, Inherited = false)] + public sealed class PublicAPIAttribute : Attribute + { + public PublicAPIAttribute() { } + + public PublicAPIAttribute([NotNull] string comment) + { + Comment = comment; + } + + [CanBeNull] public string Comment { get; } + } + + /// + /// Tells code analysis engine if the parameter is completely handled when the invoked method is on stack. + /// If the parameter is a delegate, indicates that delegate is executed while the method is executed. + /// If the parameter is an enumerable, indicates that it is enumerated while the method is executed. + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class InstantHandleAttribute : Attribute { } + + /// + /// Indicates that a method does not make any observable state changes. + /// The same as System.Diagnostics.Contracts.PureAttribute. + /// + /// + /// [Pure] int Multiply(int x, int y) => x * y; + /// + /// void M() { + /// Multiply(123, 42); // Warning: Return value of pure method is not used + /// } + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class PureAttribute : Attribute { } + + /// + /// Indicates that the return value of the method invocation must be used. + /// + /// + /// Methods decorated with this attribute (in contrast to pure methods) might change state, + /// but make no sense without using their return value.
+ /// Similarly to , this attribute + /// will help detecting usages of the method when the return value in not used. + /// Additionally, you can optionally specify a custom message, which will be used when showing warnings, e.g. + /// [MustUseReturnValue("Use the return value to...")]. + ///
+ [AttributeUsage(AttributeTargets.Method)] + public sealed class MustUseReturnValueAttribute : Attribute + { + public MustUseReturnValueAttribute() { } + + public MustUseReturnValueAttribute([NotNull] string justification) + { + Justification = justification; + } + + [CanBeNull] public string Justification { get; } + } + + /// + /// Indicates the type member or parameter of some type, that should be used instead of all other ways + /// to get the value of that type. This annotation is useful when you have some "context" value evaluated + /// and stored somewhere, meaning that all other ways to get this value must be consolidated with existing one. + /// + /// + /// class Foo { + /// [ProvidesContext] IBarService _barService = ...; + /// + /// void ProcessNode(INode node) { + /// DoSomething(node, node.GetGlobalServices().Bar); + /// // ^ Warning: use value of '_barService' field + /// } + /// } + /// + [AttributeUsage( + AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter | AttributeTargets.Method | + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.GenericParameter)] + public sealed class ProvidesContextAttribute : Attribute { } + + /// + /// Indicates that a parameter is a path to a file or a folder within a web project. + /// Path can be relative or absolute, starting from web root (~). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class PathReferenceAttribute : Attribute + { + public PathReferenceAttribute() { } + + public PathReferenceAttribute([NotNull, PathReference] string basePath) + { + BasePath = basePath; + } + + [CanBeNull] public string BasePath { get; } + } + + /// + /// An extension method marked with this attribute is processed by code completion + /// as a 'Source Template'. When the extension method is completed over some expression, its source code + /// is automatically expanded like a template at call site. + /// + /// + /// Template method body can contain valid source code and/or special comments starting with '$'. + /// Text inside these comments is added as source code when the template is applied. Template parameters + /// can be used either as additional method parameters or as identifiers wrapped in two '$' signs. + /// Use the attribute to specify macros for parameters. + /// + /// + /// In this example, the 'forEach' method is a source template available over all values + /// of enumerable types, producing ordinary C# 'foreach' statement and placing caret inside block: + /// + /// [SourceTemplate] + /// public static void forEach<T>(this IEnumerable<T> xs) { + /// foreach (var x in xs) { + /// //$ $END$ + /// } + /// } + /// + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class SourceTemplateAttribute : Attribute { } + + /// + /// Allows specifying a macro for a parameter of a source template. + /// + /// + /// You can apply the attribute on the whole method or on any of its additional parameters. The macro expression + /// is defined in the property. When applied on a method, the target + /// template parameter is defined in the property. To apply the macro silently + /// for the parameter, set the property value = -1. + /// + /// + /// Applying the attribute on a source template method: + /// + /// [SourceTemplate, Macro(Target = "item", Expression = "suggestVariableName()")] + /// public static void forEach<T>(this IEnumerable<T> collection) { + /// foreach (var item in collection) { + /// //$ $END$ + /// } + /// } + /// + /// Applying the attribute on a template method parameter: + /// + /// [SourceTemplate] + /// public static void something(this Entity x, [Macro(Expression = "guid()", Editable = -1)] string newguid) { + /// /*$ var $x$Id = "$newguid$" + x.ToString(); + /// x.DoSomething($x$Id); */ + /// } + /// + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method, AllowMultiple = true)] + public sealed class MacroAttribute : Attribute + { + /// + /// Allows specifying a macro that will be executed for a source template + /// parameter when the template is expanded. + /// + [CanBeNull] public string Expression { get; set; } + + /// + /// Allows specifying which occurrence of the target parameter becomes editable when the template is deployed. + /// + /// + /// If the target parameter is used several times in the template, only one occurrence becomes editable; + /// other occurrences are changed synchronously. To specify the zero-based index of the editable occurrence, + /// use values >= 0. To make the parameter non-editable when the template is expanded, use -1. + /// + public int Editable { get; set; } + + /// + /// Identifies the target parameter of a source template if the + /// is applied on a template method. + /// + [CanBeNull] public string Target { get; set; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public sealed class AspMvcAreaMasterLocationFormatAttribute : Attribute + { + public AspMvcAreaMasterLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public sealed class AspMvcAreaPartialViewLocationFormatAttribute : Attribute + { + public AspMvcAreaPartialViewLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public sealed class AspMvcAreaViewLocationFormatAttribute : Attribute + { + public AspMvcAreaViewLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public sealed class AspMvcMasterLocationFormatAttribute : Attribute + { + public AspMvcMasterLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public sealed class AspMvcPartialViewLocationFormatAttribute : Attribute + { + public AspMvcPartialViewLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public sealed class AspMvcViewLocationFormatAttribute : Attribute + { + public AspMvcViewLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; } + } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter + /// is an MVC action. If applied to a method, the MVC action name is calculated + /// implicitly from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class AspMvcActionAttribute : Attribute + { + public AspMvcActionAttribute() { } + + public AspMvcActionAttribute([NotNull] string anonymousProperty) + { + AnonymousProperty = anonymousProperty; + } + + [CanBeNull] public string AnonymousProperty { get; } + } + + /// + /// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC area. + /// Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class AspMvcAreaAttribute : Attribute + { + public AspMvcAreaAttribute() { } + + public AspMvcAreaAttribute([NotNull] string anonymousProperty) + { + AnonymousProperty = anonymousProperty; + } + + [CanBeNull] public string AnonymousProperty { get; } + } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is + /// an MVC controller. If applied to a method, the MVC controller name is calculated + /// implicitly from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class AspMvcControllerAttribute : Attribute + { + public AspMvcControllerAttribute() { } + + public AspMvcControllerAttribute([NotNull] string anonymousProperty) + { + AnonymousProperty = anonymousProperty; + } + + [CanBeNull] public string AnonymousProperty { get; } + } + + /// + /// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC Master. Use this attribute + /// for custom wrappers similar to System.Web.Mvc.Controller.View(String, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class AspMvcMasterAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC model type. Use this attribute + /// for custom wrappers similar to System.Web.Mvc.Controller.View(String, Object). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcModelTypeAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is an MVC + /// partial view. If applied to a method, the MVC partial view name is calculated implicitly + /// from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class AspMvcPartialViewAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Allows disabling inspections for MVC views within a class or a method. + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] + public sealed class AspMvcSuppressViewErrorAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC display template. + /// Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.DisplayExtensions.DisplayForModel(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class AspMvcDisplayTemplateAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC editor template. + /// Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.EditorExtensions.EditorForModel(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class AspMvcEditorTemplateAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC template. + /// Use this attribute for custom wrappers similar to + /// System.ComponentModel.DataAnnotations.UIHintAttribute(System.String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class AspMvcTemplateAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter + /// is an MVC view component. If applied to a method, the MVC view name is calculated implicitly + /// from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Controller.View(Object). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class AspMvcViewAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter + /// is an MVC view component name. + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class AspMvcViewComponentAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter + /// is an MVC view component view. If applied to a method, the MVC view component view name is default. + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class AspMvcViewComponentViewAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. When applied to a parameter of an attribute, + /// indicates that this parameter is an MVC action name. + /// + /// + /// [ActionName("Foo")] + /// public ActionResult Login(string returnUrl) { + /// ViewBag.ReturnUrl = Url.Action("Foo"); // OK + /// return RedirectToAction("Bar"); // Error: Cannot resolve action + /// } + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property)] + public sealed class AspMvcActionSelectorAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field)] + public sealed class HtmlElementAttributesAttribute : Attribute + { + public HtmlElementAttributesAttribute() { } + + public HtmlElementAttributesAttribute([NotNull] string name) + { + Name = name; + } + + [CanBeNull] public string Name { get; } + } + + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class HtmlAttributeValueAttribute : Attribute + { + public HtmlAttributeValueAttribute([NotNull] string name) + { + Name = name; + } + + [NotNull] public string Name { get; } + } + + /// + /// Razor attribute. Indicates that the marked parameter or method is a Razor section. + /// Use this attribute for custom wrappers similar to + /// System.Web.WebPages.WebPageBase.RenderSection(String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + public sealed class RazorSectionAttribute : Attribute { } + + /// + /// Indicates how method, constructor invocation, or property access + /// over collection type affects the contents of the collection. + /// Use to specify the access type. + /// + /// + /// Using this attribute only makes sense if all collection methods are marked with this attribute. + /// + /// + /// public class MyStringCollection : List<string> + /// { + /// [CollectionAccess(CollectionAccessType.Read)] + /// public string GetFirstString() + /// { + /// return this.ElementAt(0); + /// } + /// } + /// class Test + /// { + /// public void Foo() + /// { + /// // Warning: Contents of the collection is never updated + /// var col = new MyStringCollection(); + /// string x = col.GetFirstString(); + /// } + /// } + /// + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property)] + public sealed class CollectionAccessAttribute : Attribute + { + public CollectionAccessAttribute(CollectionAccessType collectionAccessType) + { + CollectionAccessType = collectionAccessType; + } + + public CollectionAccessType CollectionAccessType { get; } + } + + /// + /// Provides a value for the to define + /// how the collection method invocation affects the contents of the collection. + /// + [Flags] + public enum CollectionAccessType + { + /// Method does not use or modify content of the collection. + None = 0, + /// Method only reads content of the collection but does not modify it. + Read = 1, + /// Method can change content of the collection but does not add new elements. + ModifyExistingContent = 2, + /// Method can add new elements to the collection. + UpdatedContent = ModifyExistingContent | 4 + } + + /// + /// Indicates that the marked method is assertion method, i.e. it halts the control flow if + /// one of the conditions is satisfied. To set the condition, mark one of the parameters with + /// attribute. + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class AssertionMethodAttribute : Attribute { } + + /// + /// Indicates the condition parameter of the assertion method. The method itself should be + /// marked by attribute. The mandatory argument of + /// the attribute is the assertion type. + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AssertionConditionAttribute : Attribute + { + public AssertionConditionAttribute(AssertionConditionType conditionType) + { + ConditionType = conditionType; + } + + public AssertionConditionType ConditionType { get; } + } + + /// + /// Specifies assertion type. If the assertion method argument satisfies the condition, + /// then the execution continues. Otherwise, execution is assumed to be halted. + /// + public enum AssertionConditionType + { + /// Marked parameter should be evaluated to true. + IS_TRUE = 0, + /// Marked parameter should be evaluated to false. + IS_FALSE = 1, + /// Marked parameter should be evaluated to null value. + IS_NULL = 2, + /// Marked parameter should be evaluated to not null value. + IS_NOT_NULL = 3, + } + + /// + /// Indicates that the marked method unconditionally terminates control flow execution. + /// For example, it could unconditionally throw exception. + /// + [Obsolete("Use [ContractAnnotation('=> halt')] instead")] + [AttributeUsage(AttributeTargets.Method)] + public sealed class TerminatesProgramAttribute : Attribute { } + + /// + /// Indicates that method is pure LINQ method, with postponed enumeration (like Enumerable.Select, + /// .Where). This annotation allows inference of [InstantHandle] annotation for parameters + /// of delegate type by analyzing LINQ method chains. + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class LinqTunnelAttribute : Attribute { } + + /// + /// Indicates that IEnumerable passed as a parameter is not enumerated. + /// Use this annotation to suppress the 'Possible multiple enumeration of IEnumerable' inspection. + /// + /// + /// static void ThrowIfNull<T>([NoEnumeration] T v, string n) where T : class + /// { + /// // custom check for null but no enumeration + /// } + /// + /// void Foo(IEnumerable<string> values) + /// { + /// ThrowIfNull(values, nameof(values)); + /// var x = values.ToList(); // No warnings about multiple enumeration + /// } + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class NoEnumerationAttribute : Attribute { } + + /// + /// Indicates that the marked parameter is a regular expression pattern. + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class RegexPatternAttribute : Attribute { } + + /// + /// Prevents the Member Reordering feature from tossing members of the marked class. + /// + /// + /// The attribute must be mentioned in your member reordering patterns. + /// + [AttributeUsage( + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.Enum)] + public sealed class NoReorderAttribute : Attribute { } + + /// + /// XAML attribute. Indicates the type that has ItemsSource property and should be treated + /// as ItemsControl-derived type, to enable inner items DataContext type resolve. + /// + [AttributeUsage(AttributeTargets.Class)] + public sealed class XamlItemsControlAttribute : Attribute { } + + /// + /// XAML attribute. Indicates the property of some BindingBase-derived type, that + /// is used to bind some item of ItemsControl-derived type. This annotation will + /// enable the DataContext type resolve for XAML bindings for such properties. + /// + /// + /// Property should have the tree ancestor of the ItemsControl type or + /// marked with the attribute. + /// + [AttributeUsage(AttributeTargets.Property)] + public sealed class XamlItemBindingOfItemsControlAttribute : Attribute { } + + /// + /// XAML attribute. Indicates the property of some Style-derived type, that + /// is used to style items of ItemsControl-derived type. This annotation will + /// enable the DataContext type resolve for XAML bindings for such properties. + /// + /// + /// Property should have the tree ancestor of the ItemsControl type or + /// marked with the attribute. + /// + [AttributeUsage(AttributeTargets.Property)] + public sealed class XamlItemStyleOfItemsControlAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public sealed class AspChildControlTypeAttribute : Attribute + { + public AspChildControlTypeAttribute([NotNull] string tagName, [NotNull] Type controlType) + { + TagName = tagName; + ControlType = controlType; + } + + [NotNull] public string TagName { get; } + + [NotNull] public Type ControlType { get; } + } + + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] + public sealed class AspDataFieldAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] + public sealed class AspDataFieldsAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Property)] + public sealed class AspMethodPropertyAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public sealed class AspRequiredAttributeAttribute : Attribute + { + public AspRequiredAttributeAttribute([NotNull] string attribute) + { + Attribute = attribute; + } + + [NotNull] public string Attribute { get; } + } + + [AttributeUsage(AttributeTargets.Property)] + public sealed class AspTypePropertyAttribute : Attribute + { + public bool CreateConstructorReferences { get; } + + public AspTypePropertyAttribute(bool createConstructorReferences) + { + CreateConstructorReferences = createConstructorReferences; + } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class RazorImportNamespaceAttribute : Attribute + { + public RazorImportNamespaceAttribute([NotNull] string name) + { + Name = name; + } + + [NotNull] public string Name { get; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class RazorInjectionAttribute : Attribute + { + public RazorInjectionAttribute([NotNull] string type, [NotNull] string fieldName) + { + Type = type; + FieldName = fieldName; + } + + [NotNull] public string Type { get; } + + [NotNull] public string FieldName { get; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class RazorDirectiveAttribute : Attribute + { + public RazorDirectiveAttribute([NotNull] string directive) + { + Directive = directive; + } + + [NotNull] public string Directive { get; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class RazorPageBaseTypeAttribute : Attribute + { + public RazorPageBaseTypeAttribute([NotNull] string baseType) + { + BaseType = baseType; + } + public RazorPageBaseTypeAttribute([NotNull] string baseType, string pageName) + { + BaseType = baseType; + PageName = pageName; + } + + [NotNull] public string BaseType { get; } + [CanBeNull] public string PageName { get; } + } + + [AttributeUsage(AttributeTargets.Method)] + public sealed class RazorHelperCommonAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Property)] + public sealed class RazorLayoutAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Method)] + public sealed class RazorWriteLiteralMethodAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Method)] + public sealed class RazorWriteMethodAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class RazorWriteMethodParameterAttribute : Attribute { } +} \ No newline at end of file diff --git a/UVtools.GUI/Properties/Resources.Designer.cs b/UVtools.GUI/Properties/Resources.Designer.cs index b921395..006371c 100644 --- a/UVtools.GUI/Properties/Resources.Designer.cs +++ b/UVtools.GUI/Properties/Resources.Designer.cs @@ -590,6 +590,16 @@ namespace UVtools.GUI.Properties { } } + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap minus_16x16 { + get { + object obj = ResourceManager.GetObject("minus_16x16", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// @@ -750,6 +760,16 @@ namespace UVtools.GUI.Properties { } } + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap plus_16x16 { + get { + object obj = ResourceManager.GetObject("plus-16x16", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// diff --git a/UVtools.GUI/Properties/Resources.resx b/UVtools.GUI/Properties/Resources.resx index 400d008..ff8ece6 100644 --- a/UVtools.GUI/Properties/Resources.resx +++ b/UVtools.GUI/Properties/Resources.resx @@ -310,9 +310,15 @@ ..\Images\Button-Info-16x16.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + ..\Images\plus-16x16.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\Global-Network-icon-16x16.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + ..\Images\file-import-16x16.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\photo-16x16.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a @@ -370,7 +376,7 @@ ..\Images\expand-alt-16x16.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - ..\Images\file-import-16x16.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + ..\Images\minus_16x16.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a \ No newline at end of file diff --git a/UVtools.GUI/UVtools.GUI.csproj b/UVtools.GUI/UVtools.GUI.csproj index e286b5b..c548440 100644 --- a/UVtools.GUI/UVtools.GUI.csproj +++ b/UVtools.GUI/UVtools.GUI.csproj @@ -152,12 +152,6 @@ - - UserControl - - - CtrlDescriptionPanel.cs - UserControl @@ -185,6 +179,7 @@ Component + Form @@ -326,10 +321,8 @@ + - - CtrlDescriptionPanel.cs - CtrlKernel.cs @@ -482,6 +475,8 @@ + + diff --git a/UVtools.sln.DotSettings b/UVtools.sln.DotSettings new file mode 100644 index 0000000..4e7f5dc --- /dev/null +++ b/UVtools.sln.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file