diff --git a/CSE/GUI/F_Preferences.designer.vb b/CSE/GUI/F_Preferences.designer.vb index a6e7f44340f34c2c98cf40fe688896cb185fc99f..0e35faece8490aca91be489be294d83026dc18f7 100644 --- a/CSE/GUI/F_Preferences.designer.vb +++ b/CSE/GUI/F_Preferences.designer.vb @@ -41,13 +41,14 @@ Partial Class F_Preferences Me.ButtonSelectNotepad = New System.Windows.Forms.Button() Me.editor = New System.Windows.Forms.TextBox() Me.GroupBox1 = New System.Windows.Forms.GroupBox() + Me.hideUsername = New System.Windows.Forms.CheckBox() Me.strictBodies = New System.Windows.Forms.CheckBox() Me.includeSchemas = New System.Windows.Forms.CheckBox() Me.TextBox1 = New System.Windows.Forms.TextBox() Me.Label3 = New System.Windows.Forms.Label() Me.CheckBox1 = New System.Windows.Forms.CheckBox() Me.ButtonReload = New System.Windows.Forms.Button() - Me.hideUsername = New System.Windows.Forms.CheckBox() + Me.ButtonSave = New System.Windows.Forms.Button() Me.GroupBoxWorDir.SuspendLayout() Me.GroupBoxInterface.SuspendLayout() Me.TabControl1.SuspendLayout() @@ -92,14 +93,14 @@ Partial Class F_Preferences Me.ButtonOK.Name = "ButtonOK" Me.ButtonOK.Size = New System.Drawing.Size(75, 23) Me.ButtonOK.TabIndex = 0 - Me.ButtonOK.Text = "Save" + Me.ButtonOK.Text = "OK" Me.ButtonOK.UseVisualStyleBackColor = True ' 'ButtonCancel ' Me.ButtonCancel.Anchor = CType((System.Windows.Forms.AnchorStyles.Bottom Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles) Me.ButtonCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel - Me.ButtonCancel.Location = New System.Drawing.Point(99, 248) + Me.ButtonCancel.Location = New System.Drawing.Point(355, 248) Me.ButtonCancel.Name = "ButtonCancel" Me.ButtonCancel.Size = New System.Drawing.Size(75, 23) Me.ButtonCancel.TabIndex = 1 @@ -241,6 +242,16 @@ Partial Class F_Preferences Me.GroupBox1.TabStop = False Me.GroupBox1.Text = "JSON" ' + 'hideUsername + ' + Me.hideUsername.AutoSize = True + Me.hideUsername.Location = New System.Drawing.Point(6, 67) + Me.hideUsername.Name = "hideUsername" + Me.hideUsername.Size = New System.Drawing.Size(94, 17) + Me.hideUsername.TabIndex = 12 + Me.hideUsername.Text = "hideUsername" + Me.hideUsername.UseVisualStyleBackColor = True + ' 'strictBodies ' Me.strictBodies.AutoSize = True @@ -297,15 +308,16 @@ Partial Class F_Preferences Me.ButtonReload.Text = "Reload" Me.ButtonReload.UseVisualStyleBackColor = True ' - 'hideUsername + 'ButtonSave ' - Me.hideUsername.AutoSize = True - Me.hideUsername.Location = New System.Drawing.Point(6, 67) - Me.hideUsername.Name = "hideUsername" - Me.hideUsername.Size = New System.Drawing.Size(94, 17) - Me.hideUsername.TabIndex = 12 - Me.hideUsername.Text = "hideUsername" - Me.hideUsername.UseVisualStyleBackColor = True + Me.ButtonSave.Anchor = CType((System.Windows.Forms.AnchorStyles.Bottom Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles) + Me.ButtonSave.DialogResult = System.Windows.Forms.DialogResult.OK + Me.ButtonSave.Location = New System.Drawing.Point(99, 248) + Me.ButtonSave.Name = "ButtonSave" + Me.ButtonSave.Size = New System.Drawing.Size(75, 23) + Me.ButtonSave.TabIndex = 13 + Me.ButtonSave.Text = "Save" + Me.ButtonSave.UseVisualStyleBackColor = True ' 'F_Preferences ' @@ -314,6 +326,7 @@ Partial Class F_Preferences Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font Me.CancelButton = Me.ButtonCancel Me.ClientSize = New System.Drawing.Size(515, 283) + Me.Controls.Add(Me.ButtonSave) Me.Controls.Add(Me.TabControl1) Me.Controls.Add(Me.ButtonCancel) Me.Controls.Add(Me.ButtonReload) @@ -363,4 +376,5 @@ Partial Class F_Preferences Friend WithEvents strictBodies As System.Windows.Forms.CheckBox Friend WithEvents includeSchemas As System.Windows.Forms.CheckBox Friend WithEvents hideUsername As System.Windows.Forms.CheckBox + Friend WithEvents ButtonSave As System.Windows.Forms.Button End Class diff --git a/CSE/GUI/F_Preferences.vb b/CSE/GUI/F_Preferences.vb index c2a6f717d2b69bdbacdae0e1230055e3ea138edf..cdae6935a95adb26fd033311594e0859d4b51f35 100644 --- a/CSE/GUI/F_Preferences.vb +++ b/CSE/GUI/F_Preferences.vb @@ -2,8 +2,7 @@ Imports Newtonsoft.Json.Linq Public Class F_Preferences - ' Load confic - Private Sub F03_Options_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load + Private Sub FormLoadHandler(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load Dim controlPairs As IList(Of Control()) = New List(Of Control()) '' CONTROL LABEL controlPairs.Add({Me.workingDir, Me.GroupBoxWorDir}) @@ -15,37 +14,82 @@ Public Class F_Preferences controlPairs.Add({Me.strictBodies, Nothing}) controlPairs.Add({Me.hideUsername, Nothing}) - '' Add help-tooltips from Json-Schema. + '' Add help-tooltips from Json-Schema and + '' dirty-check them. '' Dim schema = JObject.Parse(cPreferences.JSchemaStr) For Each row In controlPairs Dim ctrl = row(0) Dim Label = row(1) updateControlsFromSchema(schema, ctrl, Label) + If TypeOf ctrl Is CheckBox Then + AddHandler DirectCast(ctrl, CheckBox).CheckedChanged, AddressOf DirtyHandler + Else + AddHandler ctrl.TextChanged, AddressOf DirtyHandler + End If Next UI_PopulateFrom(Prefs) End Sub + Private Sub FormClosingHandler(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing + If Me.Dirty Then + Dim res = MsgBox("Save changes?", MsgBoxStyle.YesNoCancel, "Preferences Changed") + Select Case res + Case MsgBoxResult.No + Case MsgBoxResult.Yes + Try + StorePrefs() + Catch ex As Exception + e.Cancel = True + logme(9, True, format("Failed storing Preferences({0}) due to: {1} \n Preferences left unmodified!", _ + PrefsPath, ex.Message), ex) + End Try + Case Else + e.Cancel = True + End Select + End If + End Sub + + Private _Dirty + Private Property Dirty As Boolean + Get + Return _Dirty + End Get + Set(ByVal value As Boolean) + If _Dirty Xor value Then + Me.Text = "Preferences" & IIf(value, "*", "") + End If + _Dirty = value + End Set + End Property + + + Private Sub DirtyHandler(ByVal sender As Object, ByVal e As System.EventArgs) + Dirty = True + End Sub + Private Sub UI_PopulateFrom(ByVal value As cPreferences) ' Allocate the data from the confic file (Only by the start) Me.workingDir.Text = value.workingDir - Me.editor.Text = value.Editor - Me.writeLog.Checked = value.WriteLog - Me.logLevel.Text = value.LogLevel - Me.logSize.Text = value.LogSize - Me.includeSchemas.Checked = value.IncludeSchemas + Me.editor.Text = value.editor + Me.writeLog.Checked = value.writeLog + Me.logLevel.Text = value.logLevel + Me.logSize.Text = value.logSize + Me.includeSchemas.Checked = value.includeSchemas Me.strictBodies.Checked = value.strictBodies Me.hideUsername.Checked = value.hideUsername + + Me.Dirty = False End Sub Private Sub UI_PopulateTo(ByVal value As cPreferences) value.workingDir = Me.workingDir.Text - value.Editor = Me.editor.Text - value.WriteLog = Me.writeLog.Checked - value.LogLevel = Me.logLevel.Text - value.LogSize = Me.logSize.Text - value.IncludeSchemas = Me.includeSchemas.Checked + value.editor = Me.editor.Text + value.writeLog = Me.writeLog.Checked + value.logLevel = Me.logLevel.Text + value.logSize = Me.logSize.Text + value.includeSchemas = Me.includeSchemas.Checked value.strictBodies = Me.strictBodies.Checked value.hideUsername = Me.hideUsername.Checked End Sub @@ -57,28 +101,34 @@ Public Class F_Preferences End If End Sub - ' Ok button - Private Sub StorePrefs(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonOK.Click + Private Sub SaveHandler(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonOK.Click, ButtonSave.Click Try - Dim newPrefs As cPreferences = Prefs.Clone() - UI_PopulateTo(newPrefs) - - ' Write the config file - newPrefs.Store(PrefsPath, newPrefs) - Prefs = newPrefs ' Replace active prefs if successful. - - ' Message for the restart of VECTO - RestartN = True - logme(7, True, format("Stored Preferences({0}). \n\nDo you want to restart VECTO now?", PrefsPath)) - - ' Close the window - Me.Close() + '' OK-btn save when dirty, always closes-form. + '' Save-btn: always saves, burt not closes-form. + '' + If sender IsNot ButtonOK OrElse Me.Dirty Then + StorePrefs() + End If + + If sender Is ButtonOK Then Me.Close() Catch ex As Exception logme(9, True, format("Failed storing Preferences({0}) due to: {1} \n Preferences left unmodified!", _ PrefsPath, ex.Message), ex) End Try End Sub + Private Sub StorePrefs() + Dim newPrefs As cPreferences = Prefs.Clone() + UI_PopulateTo(newPrefs) + + ' Write the config file + newPrefs.Store(PrefsPath, newPrefs) + Prefs = newPrefs ' Replace active prefs if successful. + Me.Dirty = False + + ' Message for the restart of VECTO + logme(7, False, format("Stored Preferences({0}).", PrefsPath)) + End Sub ' Ok button Private Sub ReloadPrefs(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonReload.Click @@ -150,5 +200,6 @@ Public Class F_Preferences Private Sub TextBoxLogSize_Leave(ByVal sender As Object, ByVal e As System.EventArgs) Handles logSize.Leave, TextBox1.Leave If Me.logSize.Text = Nothing Then Me.logSize.Text = Prefs.PropDefault("logSize") End Sub + End Class diff --git a/CSE/IO/cJsonFile.vb b/CSE/IO/cJsonFile.vb index 1c70ee89a664b9967193e30f524f1983c3d7a9a6..cc69b79e7e3419f0db67cc2960d45dcb0ba03e6a 100644 --- a/CSE/IO/cJsonFile.vb +++ b/CSE/IO/cJsonFile.vb @@ -10,8 +10,17 @@ Imports System.Globalization ''' and delegates the Body-building and its validation almost entirely to sub-classes. ''' </summary> ''' <remarks> -''' The /Header/Strict boolean controls whether to allow additional-properties in Body, -''' so it can be used to debug input-files by manually setting it to 'true' with a text-editor. +''' JSON files do not support comments, but help is provided by their accompanying json-schemas (see below). +''' They are splitted in two sections, Header and Body. The Header contains administrational fields or fields +''' related to the actual parsing of the file. +''' +''' Of interest are the following 2 of Header’s properties: +''' • /Header/StrictBody: Controls whether the application will accept any unknown body-properties +''' while reading the file. Set it to true to debug malformed input-files, ie to detect accidentally +''' renamed properties. +''' • /Header/BodySchema: The JSON-schema of the body will be placed HERE, for documenting file. +''' When true, it is always replaced by the Body's schema on the next save. When false, it overrides +''' application's choice and is not replaced ever. ''' </remarks> Public MustInherit Class cJsonFile Implements ICloneable @@ -28,7 +37,7 @@ Public MustInherit Class cJsonFile "AppVersion": null, "ModifiedDate": null, "CreatedBy": null, - "StrictBody": false, + "StrictBody": null, "BodySchema": null, }, "Body": null @@ -70,17 +79,19 @@ Public MustInherit Class cJsonFile }, "StrictBody": { "title": "Validate body strictly", - "type": "boolean", - "description": "When True, the 'Body' does not accept unknown properties.", - "default": false, + "type": ["boolean", "null"], + "description": "If set to true, the application will not accept any unknown body-properties while reading this file. +When null/property missing, the application decides what to do. +It is useful for debugging malformed input-files, ie to detect accidentally renamed properties.", + "default": null, }, "BodySchema": { "title": "Body schema", "type": ["boolean", "object", "null"], - "description": "Body schema is included HERE, for documenting file. \n _ - When null/property missing, application decides what to do. \n _ - When True, it is always replaced by the Body's schema on the next save.\n _ - When False, it overrides Application's choice and is not replaced ever.", + "description": "Body schema is included HERE, for documenting file. +When null/property missing, the application decides what to do. +When True, it is always replaced by the Body's schema on the next save. +When False, it overrides Application's choice and is not replaced ever.", "default": null, }, } @@ -177,8 +188,11 @@ Public MustInherit Class cJsonFile h("CreatedBy") = format("{0}{1}(lic: {2})", username, Lic.LicString, Lic.GUID) h("AppVersion") = AppVers + + '' Ensure StrictBody element always there. + If h("StrictBody") Is Nothing Then - h("StrictBody") = False + h("StrictBody") = Nothing End If '' Decide whether to include body-schema in header (for documenting file), @@ -210,8 +224,8 @@ Public MustInherit Class cJsonFile End Sub ''' <exception cref="FormatException">includes all validation errors</exception> - ''' <param name="prefs">It is there to be used when storing cPreferences themselfs.</param> ''' <param name="strictHeader">when false, relaxes Header's schema (used on Loading to be more accepting)</param> + ''' <param name="prefs">It is there just to be used when storing cPreferences themselfs.</param> Friend Sub Validate(Optional ByVal strictHeader As Boolean = False, Optional ByVal prefs As cPreferences = Nothing) If prefs Is Nothing Then prefs = CSE.Prefs Dim validateMsgs As IList(Of String) = New List(Of String) @@ -227,9 +241,7 @@ Public MustInherit Class cJsonFile Dim dummy = New cSemanticVersion(Me.FileVersion) '' Just to ensure its syntax. '' Validate Body by subclass - Dim hsb = Me.Header("StrictBody") - Dim strictBody As Boolean = IIf(hsb Is Nothing, Prefs.strictBodies, hsb) - Me.ValidateBody(strictBody, validateMsgs) + Me.ValidateBody(Me.StrictBody, validateMsgs) If (validateMsgs.Any()) Then Throw New FormatException(format("Validating /Body failed due to: {0}", String.Join(vbCrLf, validateMsgs))) @@ -249,7 +261,17 @@ Public MustInherit Class cJsonFile If obj Is Nothing OrElse Not Me.GetType().Equals(obj.GetType()) Then Return False Else - Return JToken.DeepEquals(Me.Content, DirectCast(obj, cJsonFile).Content) + Dim oj As cJsonFile = DirectCast(obj, cJsonFile) + If Not JToken.DeepEquals(Me.Body, oj.Body) Then Return False + + '' Compare Headers without the 'ModifiedDate' field + '' which would undoublfully different each time. + Dim mh As New JObject(Me.Header) + Dim oh As New JObject(oj.Header) + + mh.Remove("ModifiedDate") + oh.Remove("ModifiedDate") + Return JToken.DeepEquals(mh, oh) End If End Function @@ -331,12 +353,18 @@ Public MustInherit Class cJsonFile Public ReadOnly Property StrictBody As Boolean Get - Dim value = Me.Body("StrictBody") - Return IIf(value Is Nothing OrElse value.Type = JTokenType.Null, False, value) + Dim value = False + Dim jt = Me.Header("StrictBody") + If jt IsNot Nothing AndAlso jt.Type <> JTokenType.Null Then + value = jt + ElseIf Prefs IsNot Nothing Then + value = Prefs.strictBodies + End If + Return value End Get End Property - '' NO, logic behind it more complex, see UpdateHeader() instead. + '' NO, logic behind it too complex, see UpdateHeader() instead. 'Public ReadOnly Property BodySchema As Boolean ' Get ' Dim value = Me.Body("BodySchema") diff --git a/CSE/IO/cPreferences.vb b/CSE/IO/cPreferences.vb index 22e37c4adba0dc6a9e90c296f77677c865412757..3b5b24a666e41c54288166b164fbd69f7357661e 100644 --- a/CSE/IO/cPreferences.vb +++ b/CSE/IO/cPreferences.vb @@ -60,7 +60,7 @@ Public Class cPreferences "minimum": 0, "maximum": 10, "exclusiveMaximum": true, "default": 2, - "description": "Sets the threshold(Level) above (inclusive) from which log-messages are shown in the log-window. + "description": "Sets the threshold(Level) above (inclusive) from which log-messages are shown in the log-window. 0 : All 3-7 : No infos 8 : No warnings @@ -76,17 +76,17 @@ Public Class cPreferences "title": "Strict Bodies", "type": "boolean", "default": false, - "description": "When set to true, any unknown body-properties are not accepted when reading JSON-files. -It is useful for debugging malformed input-files, ie to detect -accidentally renamed properties. + "description": "If set to true, the application will not accept any unknown body-properties when reading JSON-files. +It is useful for debugging malformed input-files, ie to detect accidentally renamed properties. Each file can override it by setting its `/Header/StrictBody` property.", }, "includeSchemas": { "title": "Include Schemas", "type": "boolean", "default": false, - "description": "When set to true the JSON-files are self-documented by populating their `/Header/BodySchema` property. -Each file can override it by setting its `/Header/BodySchema` property to false/true.", + "description": "When set to true the JSON-files are self-documented by +populating their `/Header/BodySchema` property. Each file can override it by +setting its `/Header/BodySchema` property to false/true.", }, "hideUsername": { "title": "Hide Username", diff --git a/CSE/utils.vb b/CSE/utils.vb index 788473ff6a91d2d20d38e0354c9cae26b5371e5f..5c372720ad50737e0febc9ef7aec80f789c6f6ba 100644 --- a/CSE/utils.vb +++ b/CSE/utils.vb @@ -260,9 +260,11 @@ Module utils Private Sub updateLogWindow(ByVal logFileLevel As Integer, ByVal text As String, ByVal tabLabel As String, ByVal ex As Exception) ' Established the text wit the symbol from the style + Dim printEx = False If (ex IsNot Nothing) Then - text = text & " (Check log-file for details)" + printEx = (logFileLevel > 1 AndAlso Prefs.logLevel <= 2) + text = format("{0} (Check {1} for details)", text, IIf(printEx, "error/warn tab", "log-file")) End If ' Write to Log-windows @@ -271,8 +273,14 @@ Module utils F_Main.ListBoxMSG.Items.Add(text) F_Main.ListBoxWar.Items.Add(text) F_Main.TabPageWar.Text = format("Warnings({0})", F_Main.ListBoxWar.Items.Count) + If printEx Then + F_Main.ListBoxWar.Items.Add(format("\i{0}", ex)) + End If Case 3 ' Error F_Main.ListBoxErr.Items.Add(text) + If printEx Then + F_Main.ListBoxErr.Items.Add(format("\i{0}", ex)) + End If F_Main.TabPageErr.Text = format("Errors({0})", F_Main.ListBoxErr.Items.Count) Case Else '' ignored