diff --git a/GBM/Classes/XML Serialize Classes/Game.vb b/GBM/Classes/XML Serialize Classes/Game.vb index 9ecadc8..1bc63d4 100644 --- a/GBM/Classes/XML Serialize Classes/Game.vb +++ b/GBM/Classes/XML Serialize Classes/Game.vb @@ -7,6 +7,7 @@ Private bFolderSave As Boolean Private sFileType As String Private sExcludeList As String + Private bMonitorOnly As Boolean Private oTags As List(Of Tag) Property Name As String @@ -81,6 +82,15 @@ End Get End Property + Property MonitorOnly As Boolean + Set(value As Boolean) + bMonitorOnly = value + End Set + Get + Return bMonitorOnly + End Get + End Property + Property Tags As List(Of Tag) Get Return oTags diff --git a/GBM/Classes/XML Serialize Classes/Tag.vb b/GBM/Classes/XML Serialize Classes/Tag.vb index 8d726f9..c12a1e0 100644 --- a/GBM/Classes/XML Serialize Classes/Tag.vb +++ b/GBM/Classes/XML Serialize Classes/Tag.vb @@ -1,4 +1,5 @@ -Public Class Tag + +Public Class Tag Private sTagName As String Property Name As String diff --git a/GBM/Classes/clsGame.vb b/GBM/Classes/clsGame.vb index a7ad9b9..fb7ce2e 100644 --- a/GBM/Classes/clsGame.vb +++ b/GBM/Classes/clsGame.vb @@ -1,4 +1,5 @@ -Public Class clsGame + +Public Class clsGame Private sGameID As String = Guid.NewGuid.ToString Private sGameName As String = String.Empty Private sProcessName As String = String.Empty @@ -201,7 +202,7 @@ End Set End Property - Property Enabled As Boolean + Property Enabled As Boolean Get Return bEnabled End Get @@ -210,7 +211,7 @@ End Set End Property - Property MonitorOnly As Boolean + Property MonitorOnly As Boolean Get Return bMonitorOnly End Get @@ -405,4 +406,12 @@ Return sProcessName End Function + Public Shared Function SetSyncField(ByVal eSyncFields As eOptionalSyncFields, ByVal eSyncField As eOptionalSyncFields) As eOptionalSyncFields + Return eSyncFields Or eSyncField + End Function + + Public Shared Function RemoveSyncField(ByVal eSyncFields As eOptionalSyncFields, ByVal eSyncField As eOptionalSyncFields) As eOptionalSyncFields + Return eSyncFields And (Not eSyncField) + End Function + End Class diff --git a/GBM/Classes/clsGameFilter.vb b/GBM/Classes/clsGameFilter.vb new file mode 100644 index 0000000..b1395d3 --- /dev/null +++ b/GBM/Classes/clsGameFilter.vb @@ -0,0 +1,71 @@ +Public Class clsGameFilter + + Private sID As String + Private oField As clsGameFilterField + Private oData As Object + Private eNumericOperator As eNumericOperators = eNumericOperators.Equals + + Public Enum eNumericOperators + Equals = 1 + Greater = 2 + Lesser = 3 + GreaterEquals = 4 + LesserEquals = 5 + End Enum + + Public Property ID As String + Get + Return sID + End Get + Set(value As String) + sID = value + End Set + End Property + + Public Property Field As clsGameFilterField + Get + Return oField + End Get + Set(value As clsGameFilterField) + oField = value + End Set + End Property + + Public Property Data As Object + Get + Return oData + End Get + Set(value As Object) + oData = value + End Set + End Property + + Public Property NumericOperator As eNumericOperators + Get + Return eNumericOperator + End Get + Set(value As eNumericOperators) + eNumericOperator = value + End Set + End Property + + Public ReadOnly Property NumericOperatorAsString As String + Get + Select Case eNumericOperator + Case eNumericOperators.Equals + Return "=" + Case eNumericOperators.Greater + Return ">" + Case eNumericOperators.GreaterEquals + Return ">=" + Case eNumericOperators.Lesser + Return "<" + Case eNumericOperators.LesserEquals + Return "<=" + Case Else + Return String.Empty + End Select + End Get + End Property + +End Class diff --git a/GBM/Classes/clsGameFilterField.vb b/GBM/Classes/clsGameFilterField.vb new file mode 100644 index 0000000..7e97302 --- /dev/null +++ b/GBM/Classes/clsGameFilterField.vb @@ -0,0 +1,77 @@ +Public Class clsGameFilterField + + Public Enum eDataType As Integer + fString = 1 + fNumeric = 2 + fBool = 3 + End Enum + + Public Enum eFieldStatus + None = 0 + ValidFilter = 1 + ValidSort = 2 + End Enum + + Private sFieldName As String + Private sFriendlyFieldName As String + Private eType As eDataType + Private eStatus As eFieldStatus + + Public Property FieldName As String + Get + Return sFieldName + End Get + Set(value As String) + sFieldName = value + End Set + End Property + + Public Property FriendlyFieldName As String + Get + Return sFriendlyFieldName + End Get + Set(value As String) + sFriendlyFieldName = value + End Set + End Property + + Public Property Type As eDataType + Get + Return eType + End Get + Set(value As eDataType) + eType = value + End Set + End Property + + 'This is a flag property - Setting a value will toggle that flag on and off. + Public Property Status As eFieldStatus + Get + Return eStatus + End Get + Set(value As eFieldStatus) + If (eStatus And value) = value Then + eStatus = RemoveFieldStatus(value) + Else + eStatus = SetFieldStatus(value) + End If + End Set + End Property + + Private Function SetFieldStatus(ByVal eFlag As eFieldStatus) As eFieldStatus + Return eStatus Or eFlag + End Function + + Private Function RemoveFieldStatus(ByVal eFlag As eFieldStatus) As eFieldStatus + Return eStatus And (Not eFlag) + End Function + + Public Function CheckStatus(ByVal eFlag As eFieldStatus) As Boolean + If (eStatus And eFlag) = eFlag Then + Return True + Else + Return False + End If + End Function + +End Class diff --git a/GBM/Forms/frmAdvancedImport.vb b/GBM/Forms/frmAdvancedImport.vb index ee374f9..70ef96b 100644 --- a/GBM/Forms/frmAdvancedImport.vb +++ b/GBM/Forms/frmAdvancedImport.vb @@ -1,10 +1,11 @@ Imports GBM.My.Resources +Imports System.IO Public Class frmAdvancedImport Private hshImportData As Hashtable Private hshFinalData As New Hashtable - Private bSelectAll As Boolean = False + Private bSelectAll As Boolean = True Private bIsLoading As Boolean = False Private iCurrentSort As Integer = 0 Private WithEvents tmFilterTimer As Timer @@ -44,7 +45,7 @@ Public Class frmAdvancedImport End If End Sub - Private Sub LoadData(Optional ByVal sFilter As String = "") + Private Sub LoadData(Optional ByVal sFilter As String = "", Optional ByVal bAutoDetect As Boolean = False) Dim oApp As clsGame Dim oListViewItem As ListViewItem Dim sTags As String @@ -79,6 +80,19 @@ Public Class frmAdvancedImport oListViewItem.Checked = False End If + If bAutoDetect Then + If oApp.AbsolutePath Then + If Directory.Exists(oApp.Path) Then + oListViewItem.Checked = True + SaveChecked(oListViewItem) + Else + oListViewItem.Checked = False + End If + Else + oListViewItem.Checked = False + End If + End If + If sFilter = String.Empty Then bAddItem = True Else @@ -142,8 +156,7 @@ Public Class frmAdvancedImport Private Sub frmAdvancedImport_Load(sender As Object, e As EventArgs) Handles MyBase.Load bIsLoading = True SetForm() - LoadData() - SelectToggle() + LoadData(String.Empty, True) bIsLoading = False End Sub @@ -184,6 +197,7 @@ Public Class frmAdvancedImport tmFilterTimer.Stop() tmFilterTimer.Enabled = False End Sub + End Class ' Column Sorter diff --git a/GBM/Forms/frmFilter.Designer.vb b/GBM/Forms/frmFilter.Designer.vb index 833d5b8..71f4469 100644 --- a/GBM/Forms/frmFilter.Designer.vb +++ b/GBM/Forms/frmFilter.Designer.vb @@ -22,8 +22,6 @@ Partial Class frmFilter 'Do not modify it using the code editor. _ Private Sub InitializeComponent() - Me.optGameInfo = New System.Windows.Forms.RadioButton() - Me.optTag = New System.Windows.Forms.RadioButton() Me.grpTagFilter = New System.Windows.Forms.GroupBox() Me.grpTagOptions = New System.Windows.Forms.GroupBox() Me.optAll = New System.Windows.Forms.RadioButton() @@ -32,45 +30,42 @@ Partial Class frmFilter Me.lblTags = New System.Windows.Forms.Label() Me.btnRemove = New System.Windows.Forms.Button() Me.btnAdd = New System.Windows.Forms.Button() - Me.lstFilter = New System.Windows.Forms.ListBox() + Me.lstTagFilter = New System.Windows.Forms.ListBox() Me.lstTags = New System.Windows.Forms.ListBox() Me.btnOK = New System.Windows.Forms.Button() Me.grpGameFilter = New System.Windows.Forms.GroupBox() - Me.grpGameInfoOptions = New System.Windows.Forms.GroupBox() + Me.cboBoolFilter = New System.Windows.Forms.ComboBox() + Me.numFilter = New System.Windows.Forms.NumericUpDown() + Me.cboNumericOps = New System.Windows.Forms.ComboBox() + Me.lblCurrentFilters = New System.Windows.Forms.Label() + Me.lblFilterData = New System.Windows.Forms.Label() + Me.lblFields = New System.Windows.Forms.Label() + Me.btnRemoveFilter = New System.Windows.Forms.Button() + Me.lstFilter = New System.Windows.Forms.ListBox() + Me.btnAddFilter = New System.Windows.Forms.Button() + Me.cboFilterField = New System.Windows.Forms.ComboBox() + Me.grpFilterType = New System.Windows.Forms.GroupBox() Me.optOr = New System.Windows.Forms.RadioButton() Me.optAnd = New System.Windows.Forms.RadioButton() - Me.txtCompany = New System.Windows.Forms.TextBox() - Me.lblCompany = New System.Windows.Forms.Label() - Me.txtProcess = New System.Windows.Forms.TextBox() - Me.lblProcess = New System.Windows.Forms.Label() - Me.lblName = New System.Windows.Forms.Label() - Me.txtName = New System.Windows.Forms.TextBox() + Me.txtStringFilter = New System.Windows.Forms.TextBox() + Me.grpSorting = New System.Windows.Forms.GroupBox() + Me.grpSortOptions = New System.Windows.Forms.GroupBox() + Me.optSortAsc = New System.Windows.Forms.RadioButton() + Me.optSortDesc = New System.Windows.Forms.RadioButton() + Me.lblSortFields = New System.Windows.Forms.Label() + Me.cboSortField = New System.Windows.Forms.ComboBox() + Me.chkTag = New System.Windows.Forms.CheckBox() + Me.chkGameInfo = New System.Windows.Forms.CheckBox() + Me.Label1 = New System.Windows.Forms.Label() Me.grpTagFilter.SuspendLayout() Me.grpTagOptions.SuspendLayout() Me.grpGameFilter.SuspendLayout() - Me.grpGameInfoOptions.SuspendLayout() + CType(Me.numFilter, System.ComponentModel.ISupportInitialize).BeginInit() + Me.grpFilterType.SuspendLayout() + Me.grpSorting.SuspendLayout() + Me.grpSortOptions.SuspendLayout() Me.SuspendLayout() ' - 'optGameInfo - ' - Me.optGameInfo.AutoSize = True - Me.optGameInfo.Location = New System.Drawing.Point(12, 12) - Me.optGameInfo.Name = "optGameInfo" - Me.optGameInfo.Size = New System.Drawing.Size(108, 17) - Me.optGameInfo.TabIndex = 0 - Me.optGameInfo.Text = "Game Information" - Me.optGameInfo.UseVisualStyleBackColor = True - ' - 'optTag - ' - Me.optTag.AutoSize = True - Me.optTag.Location = New System.Drawing.Point(12, 190) - Me.optTag.Name = "optTag" - Me.optTag.Size = New System.Drawing.Size(44, 17) - Me.optTag.TabIndex = 2 - Me.optTag.Text = "Tag" - Me.optTag.UseVisualStyleBackColor = True - ' 'grpTagFilter ' Me.grpTagFilter.Controls.Add(Me.grpTagOptions) @@ -78,11 +73,11 @@ Partial Class frmFilter Me.grpTagFilter.Controls.Add(Me.lblTags) Me.grpTagFilter.Controls.Add(Me.btnRemove) Me.grpTagFilter.Controls.Add(Me.btnAdd) - Me.grpTagFilter.Controls.Add(Me.lstFilter) + Me.grpTagFilter.Controls.Add(Me.lstTagFilter) Me.grpTagFilter.Controls.Add(Me.lstTags) - Me.grpTagFilter.Location = New System.Drawing.Point(12, 213) + Me.grpTagFilter.Location = New System.Drawing.Point(12, 236) Me.grpTagFilter.Name = "grpTagFilter" - Me.grpTagFilter.Size = New System.Drawing.Size(385, 265) + Me.grpTagFilter.Size = New System.Drawing.Size(385, 198) Me.grpTagFilter.TabIndex = 3 Me.grpTagFilter.TabStop = False ' @@ -90,7 +85,7 @@ Partial Class frmFilter ' Me.grpTagOptions.Controls.Add(Me.optAll) Me.grpTagOptions.Controls.Add(Me.optAny) - Me.grpTagOptions.Location = New System.Drawing.Point(6, 211) + Me.grpTagOptions.Location = New System.Drawing.Point(6, 146) Me.grpTagOptions.Name = "grpTagOptions" Me.grpTagOptions.Size = New System.Drawing.Size(150, 46) Me.grpTagOptions.TabIndex = 6 @@ -138,7 +133,7 @@ Partial Class frmFilter ' 'btnRemove ' - Me.btnRemove.Location = New System.Drawing.Point(162, 122) + Me.btnRemove.Location = New System.Drawing.Point(162, 88) Me.btnRemove.Name = "btnRemove" Me.btnRemove.Size = New System.Drawing.Size(61, 23) Me.btnRemove.TabIndex = 3 @@ -147,22 +142,22 @@ Partial Class frmFilter ' 'btnAdd ' - Me.btnAdd.Location = New System.Drawing.Point(162, 93) + Me.btnAdd.Location = New System.Drawing.Point(162, 59) Me.btnAdd.Name = "btnAdd" Me.btnAdd.Size = New System.Drawing.Size(61, 23) Me.btnAdd.TabIndex = 2 Me.btnAdd.Text = ">" Me.btnAdd.UseVisualStyleBackColor = True ' - 'lstFilter + 'lstTagFilter ' - Me.lstFilter.FormattingEnabled = True - Me.lstFilter.Location = New System.Drawing.Point(229, 32) - Me.lstFilter.Name = "lstFilter" - Me.lstFilter.SelectionMode = System.Windows.Forms.SelectionMode.MultiExtended - Me.lstFilter.Size = New System.Drawing.Size(150, 173) - Me.lstFilter.Sorted = True - Me.lstFilter.TabIndex = 5 + Me.lstTagFilter.FormattingEnabled = True + Me.lstTagFilter.Location = New System.Drawing.Point(229, 32) + Me.lstTagFilter.Name = "lstTagFilter" + Me.lstTagFilter.SelectionMode = System.Windows.Forms.SelectionMode.MultiExtended + Me.lstTagFilter.Size = New System.Drawing.Size(150, 108) + Me.lstTagFilter.Sorted = True + Me.lstTagFilter.TabIndex = 5 ' 'lstTags ' @@ -170,124 +165,270 @@ Partial Class frmFilter Me.lstTags.Location = New System.Drawing.Point(6, 32) Me.lstTags.Name = "lstTags" Me.lstTags.SelectionMode = System.Windows.Forms.SelectionMode.MultiExtended - Me.lstTags.Size = New System.Drawing.Size(150, 173) + Me.lstTags.Size = New System.Drawing.Size(150, 108) Me.lstTags.Sorted = True Me.lstTags.TabIndex = 1 ' 'btnOK ' - Me.btnOK.Location = New System.Drawing.Point(322, 484) + Me.btnOK.Location = New System.Drawing.Point(322, 526) Me.btnOK.Name = "btnOK" Me.btnOK.Size = New System.Drawing.Size(75, 23) - Me.btnOK.TabIndex = 4 + Me.btnOK.TabIndex = 6 Me.btnOK.Text = "&OK" Me.btnOK.UseVisualStyleBackColor = True ' 'grpGameFilter ' - Me.grpGameFilter.Controls.Add(Me.grpGameInfoOptions) - Me.grpGameFilter.Controls.Add(Me.txtCompany) - Me.grpGameFilter.Controls.Add(Me.lblCompany) - Me.grpGameFilter.Controls.Add(Me.txtProcess) - Me.grpGameFilter.Controls.Add(Me.lblProcess) - Me.grpGameFilter.Controls.Add(Me.lblName) - Me.grpGameFilter.Controls.Add(Me.txtName) + Me.grpGameFilter.Controls.Add(Me.cboBoolFilter) + Me.grpGameFilter.Controls.Add(Me.numFilter) + Me.grpGameFilter.Controls.Add(Me.cboNumericOps) + Me.grpGameFilter.Controls.Add(Me.lblCurrentFilters) + Me.grpGameFilter.Controls.Add(Me.lblFilterData) + Me.grpGameFilter.Controls.Add(Me.lblFields) + Me.grpGameFilter.Controls.Add(Me.btnRemoveFilter) + Me.grpGameFilter.Controls.Add(Me.lstFilter) + Me.grpGameFilter.Controls.Add(Me.btnAddFilter) + Me.grpGameFilter.Controls.Add(Me.cboFilterField) + Me.grpGameFilter.Controls.Add(Me.grpFilterType) + Me.grpGameFilter.Controls.Add(Me.txtStringFilter) Me.grpGameFilter.Location = New System.Drawing.Point(12, 35) Me.grpGameFilter.Name = "grpGameFilter" - Me.grpGameFilter.Size = New System.Drawing.Size(385, 150) + Me.grpGameFilter.Size = New System.Drawing.Size(385, 172) Me.grpGameFilter.TabIndex = 1 Me.grpGameFilter.TabStop = False ' - 'grpGameInfoOptions + 'cboBoolFilter ' - Me.grpGameInfoOptions.Controls.Add(Me.optOr) - Me.grpGameInfoOptions.Controls.Add(Me.optAnd) - Me.grpGameInfoOptions.Location = New System.Drawing.Point(14, 97) - Me.grpGameInfoOptions.Name = "grpGameInfoOptions" - Me.grpGameInfoOptions.Size = New System.Drawing.Size(106, 46) - Me.grpGameInfoOptions.TabIndex = 6 - Me.grpGameInfoOptions.TabStop = False - Me.grpGameInfoOptions.Text = "Options" + Me.cboBoolFilter.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList + Me.cboBoolFilter.FormattingEnabled = True + Me.cboBoolFilter.Location = New System.Drawing.Point(162, 36) + Me.cboBoolFilter.Name = "cboBoolFilter" + Me.cboBoolFilter.Size = New System.Drawing.Size(136, 21) + Me.cboBoolFilter.TabIndex = 3 + ' + 'numFilter + ' + Me.numFilter.DecimalPlaces = 1 + Me.numFilter.Location = New System.Drawing.Point(233, 37) + Me.numFilter.Maximum = New Decimal(New Integer() {1000000, 0, 0, 0}) + Me.numFilter.Name = "numFilter" + Me.numFilter.Size = New System.Drawing.Size(65, 20) + Me.numFilter.TabIndex = 4 + ' + 'cboNumericOps + ' + Me.cboNumericOps.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList + Me.cboNumericOps.FormattingEnabled = True + Me.cboNumericOps.Location = New System.Drawing.Point(162, 36) + Me.cboNumericOps.Name = "cboNumericOps" + Me.cboNumericOps.Size = New System.Drawing.Size(65, 21) + Me.cboNumericOps.TabIndex = 3 + ' + 'lblCurrentFilters + ' + Me.lblCurrentFilters.AutoSize = True + Me.lblCurrentFilters.Location = New System.Drawing.Point(94, 65) + Me.lblCurrentFilters.Name = "lblCurrentFilters" + Me.lblCurrentFilters.Size = New System.Drawing.Size(71, 13) + Me.lblCurrentFilters.TabIndex = 6 + Me.lblCurrentFilters.Text = "Current Filters" + ' + 'lblFilterData + ' + Me.lblFilterData.AutoSize = True + Me.lblFilterData.Location = New System.Drawing.Point(214, 20) + Me.lblFilterData.Name = "lblFilterData" + Me.lblFilterData.Size = New System.Drawing.Size(32, 13) + Me.lblFilterData.TabIndex = 2 + Me.lblFilterData.Text = "Filter " + ' + 'lblFields + ' + Me.lblFields.AutoSize = True + Me.lblFields.Location = New System.Drawing.Point(41, 20) + Me.lblFields.Name = "lblFields" + Me.lblFields.Size = New System.Drawing.Size(80, 13) + Me.lblFields.TabIndex = 0 + Me.lblFields.Text = "Available Fields" + ' + 'btnRemoveFilter + ' + Me.btnRemoveFilter.Location = New System.Drawing.Point(259, 140) + Me.btnRemoveFilter.Name = "btnRemoveFilter" + Me.btnRemoveFilter.Size = New System.Drawing.Size(75, 23) + Me.btnRemoveFilter.TabIndex = 9 + Me.btnRemoveFilter.Text = "Remove" + Me.btnRemoveFilter.UseVisualStyleBackColor = True + ' + 'lstFilter + ' + Me.lstFilter.FormattingEnabled = True + Me.lstFilter.Location = New System.Drawing.Point(6, 81) + Me.lstFilter.Name = "lstFilter" + Me.lstFilter.Size = New System.Drawing.Size(247, 82) + Me.lstFilter.TabIndex = 7 + ' + 'btnAddFilter + ' + Me.btnAddFilter.Location = New System.Drawing.Point(304, 34) + Me.btnAddFilter.Name = "btnAddFilter" + Me.btnAddFilter.Size = New System.Drawing.Size(75, 23) + Me.btnAddFilter.TabIndex = 5 + Me.btnAddFilter.Text = "Add" + Me.btnAddFilter.UseVisualStyleBackColor = True + ' + 'cboFilterField + ' + Me.cboFilterField.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList + Me.cboFilterField.FormattingEnabled = True + Me.cboFilterField.Location = New System.Drawing.Point(6, 36) + Me.cboFilterField.Name = "cboFilterField" + Me.cboFilterField.Size = New System.Drawing.Size(150, 21) + Me.cboFilterField.TabIndex = 1 + ' + 'grpFilterType + ' + Me.grpFilterType.Controls.Add(Me.optOr) + Me.grpFilterType.Controls.Add(Me.optAnd) + Me.grpFilterType.Location = New System.Drawing.Point(259, 81) + Me.grpFilterType.Name = "grpFilterType" + Me.grpFilterType.Size = New System.Drawing.Size(105, 46) + Me.grpFilterType.TabIndex = 8 + Me.grpFilterType.TabStop = False + Me.grpFilterType.Text = "Filter Type" ' 'optOr ' - Me.optOr.Location = New System.Drawing.Point(56, 19) + Me.optOr.Checked = True + Me.optOr.Location = New System.Drawing.Point(6, 19) Me.optOr.Name = "optOr" - Me.optOr.Size = New System.Drawing.Size(36, 17) - Me.optOr.TabIndex = 1 + Me.optOr.Size = New System.Drawing.Size(44, 17) + Me.optOr.TabIndex = 0 Me.optOr.TabStop = True - Me.optOr.Text = "Or" + Me.optOr.Text = "Any" Me.optOr.UseVisualStyleBackColor = True ' 'optAnd ' - Me.optAnd.Checked = True - Me.optAnd.Location = New System.Drawing.Point(6, 19) + Me.optAnd.Location = New System.Drawing.Point(56, 19) Me.optAnd.Name = "optAnd" Me.optAnd.Size = New System.Drawing.Size(44, 17) - Me.optAnd.TabIndex = 0 - Me.optAnd.TabStop = True - Me.optAnd.Text = "And" + Me.optAnd.TabIndex = 1 + Me.optAnd.Text = "All" Me.optAnd.UseVisualStyleBackColor = True ' - 'txtCompany + 'txtStringFilter ' - Me.txtCompany.Location = New System.Drawing.Point(70, 71) - Me.txtCompany.Name = "txtCompany" - Me.txtCompany.Size = New System.Drawing.Size(309, 20) - Me.txtCompany.TabIndex = 5 + Me.txtStringFilter.Location = New System.Drawing.Point(162, 36) + Me.txtStringFilter.Name = "txtStringFilter" + Me.txtStringFilter.Size = New System.Drawing.Size(136, 20) + Me.txtStringFilter.TabIndex = 3 ' - 'lblCompany + 'grpSorting ' - Me.lblCompany.AutoSize = True - Me.lblCompany.Location = New System.Drawing.Point(11, 74) - Me.lblCompany.Name = "lblCompany" - Me.lblCompany.Size = New System.Drawing.Size(54, 13) - Me.lblCompany.TabIndex = 4 - Me.lblCompany.Text = "Company:" + Me.grpSorting.Controls.Add(Me.grpSortOptions) + Me.grpSorting.Controls.Add(Me.lblSortFields) + Me.grpSorting.Controls.Add(Me.cboSortField) + Me.grpSorting.Location = New System.Drawing.Point(12, 440) + Me.grpSorting.Name = "grpSorting" + Me.grpSorting.Size = New System.Drawing.Size(385, 80) + Me.grpSorting.TabIndex = 4 + Me.grpSorting.TabStop = False + Me.grpSorting.Text = "Sorting" ' - 'txtProcess + 'grpSortOptions ' - Me.txtProcess.Location = New System.Drawing.Point(70, 45) - Me.txtProcess.Name = "txtProcess" - Me.txtProcess.Size = New System.Drawing.Size(309, 20) - Me.txtProcess.TabIndex = 3 + Me.grpSortOptions.Controls.Add(Me.optSortAsc) + Me.grpSortOptions.Controls.Add(Me.optSortDesc) + Me.grpSortOptions.Location = New System.Drawing.Point(162, 19) + Me.grpSortOptions.Name = "grpSortOptions" + Me.grpSortOptions.Size = New System.Drawing.Size(189, 43) + Me.grpSortOptions.TabIndex = 3 + Me.grpSortOptions.TabStop = False + Me.grpSortOptions.Text = "Sort Options" ' - 'lblProcess + 'optSortAsc ' - Me.lblProcess.AutoSize = True - Me.lblProcess.Location = New System.Drawing.Point(11, 48) - Me.lblProcess.Name = "lblProcess" - Me.lblProcess.Size = New System.Drawing.Size(48, 13) - Me.lblProcess.TabIndex = 2 - Me.lblProcess.Text = "Process:" + Me.optSortAsc.AutoSize = True + Me.optSortAsc.Location = New System.Drawing.Point(6, 17) + Me.optSortAsc.Name = "optSortAsc" + Me.optSortAsc.Size = New System.Drawing.Size(75, 17) + Me.optSortAsc.TabIndex = 0 + Me.optSortAsc.TabStop = True + Me.optSortAsc.Text = "Ascending" + Me.optSortAsc.UseVisualStyleBackColor = True ' - 'lblName + 'optSortDesc ' - Me.lblName.AutoSize = True - Me.lblName.Location = New System.Drawing.Point(11, 22) - Me.lblName.Name = "lblName" - Me.lblName.Size = New System.Drawing.Size(38, 13) - Me.lblName.TabIndex = 0 - Me.lblName.Text = "Name:" + Me.optSortDesc.AutoSize = True + Me.optSortDesc.Location = New System.Drawing.Point(90, 17) + Me.optSortDesc.Name = "optSortDesc" + Me.optSortDesc.Size = New System.Drawing.Size(82, 17) + Me.optSortDesc.TabIndex = 1 + Me.optSortDesc.TabStop = True + Me.optSortDesc.Text = "Descending" + Me.optSortDesc.UseVisualStyleBackColor = True ' - 'txtName + 'lblSortFields ' - Me.txtName.Location = New System.Drawing.Point(70, 19) - Me.txtName.Name = "txtName" - Me.txtName.Size = New System.Drawing.Size(309, 20) - Me.txtName.TabIndex = 1 + Me.lblSortFields.AutoSize = True + Me.lblSortFields.Location = New System.Drawing.Point(41, 19) + Me.lblSortFields.Name = "lblSortFields" + Me.lblSortFields.Size = New System.Drawing.Size(80, 13) + Me.lblSortFields.TabIndex = 1 + Me.lblSortFields.Text = "Available Fields" + ' + 'cboSortField + ' + Me.cboSortField.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList + Me.cboSortField.FormattingEnabled = True + Me.cboSortField.Location = New System.Drawing.Point(6, 35) + Me.cboSortField.Name = "cboSortField" + Me.cboSortField.Size = New System.Drawing.Size(150, 21) + Me.cboSortField.TabIndex = 2 + ' + 'chkTag + ' + Me.chkTag.AutoSize = True + Me.chkTag.Location = New System.Drawing.Point(12, 213) + Me.chkTag.Name = "chkTag" + Me.chkTag.Size = New System.Drawing.Size(45, 17) + Me.chkTag.TabIndex = 2 + Me.chkTag.Text = "Tag" + Me.chkTag.UseVisualStyleBackColor = True + ' + 'chkGameInfo + ' + Me.chkGameInfo.AutoSize = True + Me.chkGameInfo.Location = New System.Drawing.Point(12, 12) + Me.chkGameInfo.Name = "chkGameInfo" + Me.chkGameInfo.Size = New System.Drawing.Size(109, 17) + Me.chkGameInfo.TabIndex = 0 + Me.chkGameInfo.Text = "Game Information" + Me.chkGameInfo.UseVisualStyleBackColor = True + ' + 'Label1 + ' + Me.Label1.AutoSize = True + Me.Label1.Location = New System.Drawing.Point(12, 531) + Me.Label1.Name = "Label1" + Me.Label1.Size = New System.Drawing.Size(249, 13) + Me.Label1.TabIndex = 5 + Me.Label1.Text = "* Indicates a field that may give unexpected results." ' 'frmFilter ' Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!) Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font - Me.ClientSize = New System.Drawing.Size(409, 516) + Me.ClientSize = New System.Drawing.Size(409, 561) + Me.Controls.Add(Me.Label1) + Me.Controls.Add(Me.grpSorting) + Me.Controls.Add(Me.chkTag) Me.Controls.Add(Me.grpGameFilter) + Me.Controls.Add(Me.chkGameInfo) Me.Controls.Add(Me.grpTagFilter) Me.Controls.Add(Me.btnOK) - Me.Controls.Add(Me.optTag) - Me.Controls.Add(Me.optGameInfo) Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle Me.MaximizeBox = False Me.MinimizeBox = False @@ -300,13 +441,16 @@ Partial Class frmFilter Me.grpTagOptions.ResumeLayout(False) Me.grpGameFilter.ResumeLayout(False) Me.grpGameFilter.PerformLayout() - Me.grpGameInfoOptions.ResumeLayout(False) + CType(Me.numFilter, System.ComponentModel.ISupportInitialize).EndInit() + Me.grpFilterType.ResumeLayout(False) + Me.grpSorting.ResumeLayout(False) + Me.grpSorting.PerformLayout() + Me.grpSortOptions.ResumeLayout(False) + Me.grpSortOptions.PerformLayout() Me.ResumeLayout(False) Me.PerformLayout() End Sub - Friend WithEvents optGameInfo As System.Windows.Forms.RadioButton - Friend WithEvents optTag As System.Windows.Forms.RadioButton Friend WithEvents grpTagFilter As System.Windows.Forms.GroupBox Friend WithEvents grpTagOptions As System.Windows.Forms.GroupBox Friend WithEvents optAll As System.Windows.Forms.RadioButton @@ -315,17 +459,31 @@ Partial Class frmFilter Friend WithEvents lblTags As System.Windows.Forms.Label Friend WithEvents btnRemove As System.Windows.Forms.Button Friend WithEvents btnAdd As System.Windows.Forms.Button - Friend WithEvents lstFilter As System.Windows.Forms.ListBox + Friend WithEvents lstTagFilter As System.Windows.Forms.ListBox Friend WithEvents lstTags As System.Windows.Forms.ListBox Friend WithEvents btnOK As System.Windows.Forms.Button Friend WithEvents grpGameFilter As System.Windows.Forms.GroupBox - Friend WithEvents txtProcess As System.Windows.Forms.TextBox - Friend WithEvents lblProcess As System.Windows.Forms.Label - Friend WithEvents lblName As System.Windows.Forms.Label - Friend WithEvents txtName As System.Windows.Forms.TextBox - Friend WithEvents txtCompany As System.Windows.Forms.TextBox - Friend WithEvents lblCompany As System.Windows.Forms.Label - Friend WithEvents grpGameInfoOptions As System.Windows.Forms.GroupBox + Friend WithEvents txtStringFilter As System.Windows.Forms.TextBox + Friend WithEvents grpFilterType As System.Windows.Forms.GroupBox Friend WithEvents optOr As System.Windows.Forms.RadioButton Friend WithEvents optAnd As System.Windows.Forms.RadioButton + Friend WithEvents grpSorting As GroupBox + Friend WithEvents optSortDesc As RadioButton + Friend WithEvents optSortAsc As RadioButton + Friend WithEvents cboSortField As ComboBox + Friend WithEvents chkTag As CheckBox + Friend WithEvents chkGameInfo As CheckBox + Friend WithEvents cboFilterField As ComboBox + Friend WithEvents lstFilter As ListBox + Friend WithEvents btnAddFilter As Button + Friend WithEvents btnRemoveFilter As Button + Friend WithEvents lblCurrentFilters As Label + Friend WithEvents lblFilterData As Label + Friend WithEvents lblFields As Label + Friend WithEvents cboNumericOps As ComboBox + Friend WithEvents numFilter As NumericUpDown + Friend WithEvents cboBoolFilter As ComboBox + Friend WithEvents lblSortFields As Label + Friend WithEvents Label1 As Label + Friend WithEvents grpSortOptions As GroupBox End Class diff --git a/GBM/Forms/frmFilter.vb b/GBM/Forms/frmFilter.vb index f7a2c55..0907c56 100644 --- a/GBM/Forms/frmFilter.vb +++ b/GBM/Forms/frmFilter.vb @@ -3,36 +3,74 @@ Public Class frmFilter Public Enum eFilterType As Integer - NoFilter = 1 + BaseFilter = 1 AnyTag = 2 AllTags = 3 NoTags = 4 - FieldAnd = 5 - FieldOr = 6 End Enum Dim oTagFilters As New List(Of clsTag) - Dim hshStringFilters As New Hashtable - Dim eCurrentFilterType As eFilterType = eFilterType.AnyTag + Dim oGameFilters As New List(Of clsGameFilter) + Dim oValidFields As New List(Of clsGameFilterField) + Dim eCurrentFilterType As eFilterType = eFilterType.BaseFilter + Dim bAndOperator As Boolean = False + Dim bSortAsc As Boolean = True + Dim sSortField As String = "Name" Dim hshTags As New Hashtable Dim bShutdown As Boolean = False - Public ReadOnly Property StringFilters As Hashtable + Public Property GameFilters As List(Of clsGameFilter) Get - Return hshStringFilters + Return oGameFilters End Get + Set(value As List(Of clsGameFilter)) + oGameFilters = value + End Set End Property - Public ReadOnly Property TagFilters As List(Of clsTag) + Public Property TagFilters As List(Of clsTag) Get Return oTagFilters End Get + Set(value As List(Of clsTag)) + oTagFilters = value + End Set End Property - Public ReadOnly Property FilterType As eFilterType + Public Property FilterType As eFilterType Get Return eCurrentFilterType End Get + Set(value As eFilterType) + eCurrentFilterType = value + End Set + End Property + + Public Property AndOperator As Boolean + Get + Return bAndOperator + End Get + Set(value As Boolean) + bAndOperator = value + End Set + End Property + + Public Property SortAsc As Boolean + Get + Return bSortAsc + End Get + Set(value As Boolean) + bSortAsc = value + End Set + End Property + + Public Property SortField As String + Get + Return sSortField + End Get + Set(value As String) + sSortField = value + End Set End Property Private Sub AddTag() @@ -41,7 +79,7 @@ Public Class frmFilter If lstTags.SelectedItems.Count = 1 Then oData = lstTags.SelectedItems(0) - lstFilter.Items.Add(oData) + lstTagFilter.Items.Add(oData) lstTags.Items.Remove(oData) ElseIf lstTags.SelectedItems.Count > 1 Then oTags = New List(Of KeyValuePair(Of String, String)) @@ -51,7 +89,7 @@ Public Class frmFilter Next For Each kp As KeyValuePair(Of String, String) In oTags - lstFilter.Items.Add(kp) + lstTagFilter.Items.Add(kp) lstTags.Items.Remove(kp) Next End If @@ -62,26 +100,173 @@ Public Class frmFilter Dim oData As KeyValuePair(Of String, String) Dim oTags As List(Of KeyValuePair(Of String, String)) - If lstFilter.SelectedItems.Count = 1 Then - oData = lstFilter.SelectedItems(0) - lstFilter.Items.Remove(oData) + If lstTagFilter.SelectedItems.Count = 1 Then + oData = lstTagFilter.SelectedItems(0) + lstTagFilter.Items.Remove(oData) lstTags.Items.Add(oData) - ElseIf lstFilter.SelectedItems.Count > 1 Then + ElseIf lstTagFilter.SelectedItems.Count > 1 Then oTags = New List(Of KeyValuePair(Of String, String)) - For Each oData In lstFilter.SelectedItems + For Each oData In lstTagFilter.SelectedItems oTags.Add(oData) Next For Each kp As KeyValuePair(Of String, String) In oTags - lstFilter.Items.Remove(kp) + lstTagFilter.Items.Remove(kp) lstTags.Items.Add(kp) Next End If End Sub - Private Sub LoadData() + Private Sub LoadFilterFields() + Dim oField As clsGameFilterField + + 'Name + oField = New clsGameFilterField + oField.FieldName = "Name" + oField.FriendlyFieldName = frmFilter_FieldName + oField.Type = clsGameFilterField.eDataType.fString + oField.Status = clsGameFilterField.eFieldStatus.ValidSort + oField.Status = clsGameFilterField.eFieldStatus.ValidFilter + oValidFields.Add(oField) + + 'Process + oField = New clsGameFilterField + oField.FieldName = "Process" + oField.FriendlyFieldName = frmFilter_FieldProcess + oField.Type = clsGameFilterField.eDataType.fString + oField.Status = clsGameFilterField.eFieldStatus.ValidSort + oField.Status = clsGameFilterField.eFieldStatus.ValidFilter + oValidFields.Add(oField) + + 'Parameter + oField = New clsGameFilterField + oField.FieldName = "Parameter" + oField.FriendlyFieldName = frmFilter_FieldParameter + oField.Type = clsGameFilterField.eDataType.fString + oField.Status = clsGameFilterField.eFieldStatus.ValidSort + oField.Status = clsGameFilterField.eFieldStatus.ValidFilter + oValidFields.Add(oField) + + 'Save Path + oField = New clsGameFilterField + oField.FieldName = "Path" + oField.FriendlyFieldName = frmFilter_FieldPath + oField.Type = clsGameFilterField.eDataType.fString + oField.Status = clsGameFilterField.eFieldStatus.ValidFilter + oValidFields.Add(oField) + + 'Include Items + oField = New clsGameFilterField + oField.FieldName = "FileType" + oField.FriendlyFieldName = frmFilter_FieldFileType + oField.Type = clsGameFilterField.eDataType.fString + oField.Status = clsGameFilterField.eFieldStatus.ValidFilter + oValidFields.Add(oField) + + 'Exclude Items + oField = New clsGameFilterField + oField.FieldName = "ExcludeList" + oField.FriendlyFieldName = frmFilter_FieldExcludeList + oField.Type = clsGameFilterField.eDataType.fString + oField.Status = clsGameFilterField.eFieldStatus.ValidFilter + oValidFields.Add(oField) + + 'Save Entire Folder + oField = New clsGameFilterField + oField.FieldName = "FolderSave" + oField.FriendlyFieldName = frmFilter_FieldFolderSave + oField.Type = clsGameFilterField.eDataType.fBool + oField.Status = clsGameFilterField.eFieldStatus.ValidFilter + oValidFields.Add(oField) + + 'Delete Folder on Restore + oField = New clsGameFilterField + oField.FieldName = "CleanFolder" + oField.FriendlyFieldName = frmFilter_FieldCleanFolder + oField.Type = clsGameFilterField.eDataType.fBool + oField.Status = clsGameFilterField.eFieldStatus.ValidFilter + oValidFields.Add(oField) + + 'Save Multiple Backups + oField = New clsGameFilterField + oField.FieldName = "TimeStamp" + oField.FriendlyFieldName = frmFilter_FieldTimeStamp + oField.Type = clsGameFilterField.eDataType.fBool + oField.Status = clsGameFilterField.eFieldStatus.ValidFilter + oValidFields.Add(oField) + + 'Backup Limit + oField = New clsGameFilterField + oField.FieldName = "BackupLimit" + oField.FriendlyFieldName = frmFilter_FieldBackupLimit + oField.Type = clsGameFilterField.eDataType.fNumeric + oField.Status = clsGameFilterField.eFieldStatus.ValidFilter + oValidFields.Add(oField) + + 'Game Path + oField = New clsGameFilterField + oField.FieldName = "ProcessPath" + oField.FriendlyFieldName = frmFilter_FieldProcessPath + oField.Type = clsGameFilterField.eDataType.fString + oField.Status = clsGameFilterField.eFieldStatus.ValidFilter + oValidFields.Add(oField) + + 'Company + oField = New clsGameFilterField + oField.FieldName = "Company" + oField.FriendlyFieldName = frmFilter_FieldCompany + oField.Type = clsGameFilterField.eDataType.fString + oField.Status = clsGameFilterField.eFieldStatus.ValidSort + oField.Status = clsGameFilterField.eFieldStatus.ValidFilter + oValidFields.Add(oField) + + 'Version + oField = New clsGameFilterField + oField.FieldName = "Version" + oField.FriendlyFieldName = frmFilter_FieldVersion + oField.Type = clsGameFilterField.eDataType.fString + oField.Status = clsGameFilterField.eFieldStatus.ValidSort + oField.Status = clsGameFilterField.eFieldStatus.ValidFilter + oValidFields.Add(oField) + + 'Icon + oField = New clsGameFilterField + oField.FieldName = "Icon" + oField.FriendlyFieldName = frmFilter_FieldIcon + oField.Type = clsGameFilterField.eDataType.fString + oField.Status = clsGameFilterField.eFieldStatus.ValidFilter + oValidFields.Add(oField) + + 'Hours + oField = New clsGameFilterField + oField.FieldName = "Hours" + oField.FriendlyFieldName = frmFilter_FieldHours + oField.Type = clsGameFilterField.eDataType.fNumeric + oField.Status = clsGameFilterField.eFieldStatus.ValidSort + oField.Status = clsGameFilterField.eFieldStatus.ValidFilter + oValidFields.Add(oField) + + 'Enabled + oField = New clsGameFilterField + oField.FieldName = "Enabled" + oField.FriendlyFieldName = frmFilter_FieldEnabled + oField.Type = clsGameFilterField.eDataType.fBool + oField.Status = clsGameFilterField.eFieldStatus.ValidFilter + oValidFields.Add(oField) + + 'Monitor Only + oField = New clsGameFilterField + oField.FieldName = "MonitorOnly" + oField.FriendlyFieldName = frmFilter_FieldMonitorOnly + oField.Type = clsGameFilterField.eDataType.fBool + oField.Status = clsGameFilterField.eFieldStatus.ValidFilter + oValidFields.Add(oField) + + End Sub + + Private Sub LoadTagData() Dim oTag As clsTag Dim oData As KeyValuePair(Of String, String) @@ -90,12 +275,13 @@ Public Class frmFilter 'Handle Lists lstTags.Items.Clear() - lstFilter.Items.Clear() + lstTagFilter.Items.Clear() lstTags.ValueMember = "Key" lstTags.DisplayMember = "Value" - lstFilter.ValueMember = "Key" - lstFilter.DisplayMember = "Value" + + lstTagFilter.ValueMember = "Key" + lstTagFilter.DisplayMember = "Value" For Each de As DictionaryEntry In hshTags oTag = DirectCast(de.Value, clsTag) @@ -105,32 +291,140 @@ Public Class frmFilter End Sub + Private Sub LoadExistingFilters() + Dim sFilter As String = String.Empty + Dim oListTag As KeyValuePair(Of String, String) + + 'Game Filters + If bAndOperator Then + optAnd.Checked = True + Else + optOr.Checked = True + End If + + If oGameFilters.Count > 0 Then + chkGameInfo.Checked = True + For Each oFilter As clsGameFilter In oGameFilters + Select Case oFilter.Field.Type + Case clsGameFilterField.eDataType.fString + sFilter = oFilter.Field.FriendlyFieldName & " " & frmFilter_lstFilterContains & " """ & oFilter.Data & """" + Case clsGameFilterField.eDataType.fNumeric + sFilter = oFilter.Field.FriendlyFieldName & " " & oFilter.NumericOperatorAsString & " " & oFilter.Data + Case clsGameFilterField.eDataType.fBool + sFilter = oFilter.Field.FriendlyFieldName & " = " & oFilter.Data + End Select + + lstFilter.Items.Add(New KeyValuePair(Of clsGameFilter, String)(oFilter, sFilter)) + Next + End If + + 'Tag Filters + If oTagFilters.Count > 0 Then + chkTag.Checked = True + For Each oTag As clsTag In oTagFilters + oListTag = New KeyValuePair(Of String, String)(oTag.ID, oTag.Name) + lstTagFilter.Items.Add(oListTag) + lstTags.Items.Remove(oListTag) + Next + + If eCurrentFilterType = eFilterType.AllTags Then + optAll.Checked = True + Else + optAny.Checked = True + End If + End If + + 'Sorting + cboSortField.SelectedValue = sSortField + If bSortAsc Then + optSortAsc.Checked = True + Else + optSortDesc.Checked = True + End If + + End Sub + + Private Sub ChangeFilterMode() + Dim oFilterType As clsGameFilterField.eDataType = DirectCast(cboFilterField.SelectedValue, clsGameFilterField).Type + + 'Reset + cboNumericOps.SelectedIndex = 0 + cboBoolFilter.SelectedIndex = 0 + numFilter.Value = 0 + txtStringFilter.Text = String.Empty + + 'Reset Visibilty + cboBoolFilter.Visible = False + cboNumericOps.Visible = False + numFilter.Visible = False + txtStringFilter.Visible = False + + 'Set Visiblity + Select Case oFilterType + Case clsGameFilterField.eDataType.fString + txtStringFilter.Visible = True + Case clsGameFilterField.eDataType.fNumeric + cboNumericOps.Visible = True + numFilter.Visible = True + txtStringFilter.Visible = False + Case clsGameFilterField.eDataType.fBool + cboBoolFilter.Visible = True + End Select + + End Sub + + Private Sub AddFilter() + Dim oFilter As New clsGameFilter + Dim sFilter As String = String.Empty + + 'Build Filter + oFilter.ID = Guid.NewGuid.ToString.Split("-")(0) + oFilter.Field = cboFilterField.SelectedValue + + Select Case oFilter.Field.Type + Case clsGameFilterField.eDataType.fString + oFilter.Data = txtStringFilter.Text + sFilter = oFilter.Field.FriendlyFieldName & " " & frmFilter_lstFilterContains & " """ & oFilter.Data & """" + Case clsGameFilterField.eDataType.fNumeric + oFilter.Data = numFilter.Value + oFilter.NumericOperator = DirectCast(cboNumericOps.SelectedValue, clsGameFilter.eNumericOperators) + sFilter = oFilter.Field.FriendlyFieldName & " " & oFilter.NumericOperatorAsString & " " & oFilter.Data + Case clsGameFilterField.eDataType.fBool + oFilter.Data = cboBoolFilter.SelectedValue + sFilter = oFilter.Field.FriendlyFieldName & " = " & oFilter.Data + End Select + + oGameFilters.Add(oFilter) + lstFilter.Items.Add(New KeyValuePair(Of clsGameFilter, String)(oFilter, sFilter)) + + End Sub + + Private Sub RemoveFilter() + Dim oFilter As Object + + If lstFilter.SelectedIndex <> -1 Then + oFilter = lstFilter.SelectedItem + oGameFilters.Remove(DirectCast(oFilter, KeyValuePair(Of clsGameFilter, String)).Key) + lstFilter.Items.Remove(oFilter) + End If + + End Sub + Private Sub GetFilters() Dim oData As KeyValuePair(Of String, String) Dim oTag As clsTag - If optGameInfo.Checked Then - 'Set Filter Type - If optAnd.Checked Then - eCurrentFilterType = eFilterType.FieldAnd - Else - eCurrentFilterType = eFilterType.FieldOr - End If + If chkGameInfo.Checked Then + 'Set Filter Type(s) + eCurrentFilterType = eFilterType.BaseFilter + bAndOperator = optAnd.Checked + End If - 'Set String Filter - If txtName.Text <> String.Empty Then - hshStringFilters.Add("Name", txtName.Text) - End If - If txtProcess.Text <> String.Empty Then - hshStringFilters.Add("Process", txtProcess.Text) - End If - If txtCompany.Text <> String.Empty Then - hshStringFilters.Add("Company", txtCompany.Text) - End If - Else + If chkTag.Checked Then 'Set Tags - For Each oData In lstFilter.Items + TagFilters.Clear() + For Each oData In lstTagFilter.Items oTag = DirectCast(hshTags(oData.Value), clsTag) TagFilters.Add(oTag) Next @@ -145,6 +439,72 @@ Public Class frmFilter End If End If + 'Sorting + If optSortAsc.Checked Then + bSortAsc = True + Else + bSortAsc = False + End If + + sSortField = cboSortField.SelectedValue + + End Sub + + Private Sub LoadCombos() + Dim oFilterFields As New List(Of KeyValuePair(Of clsGameFilterField, String)) + Dim oSortFields As New List(Of KeyValuePair(Of String, String)) + Dim oNumericOperators As New List(Of KeyValuePair(Of clsGameFilter.eNumericOperators, String)) + Dim oBoolOperators As New List(Of KeyValuePair(Of Boolean, String)) + + 'cboBoolFilter + cboBoolFilter.ValueMember = "Key" + cboBoolFilter.DisplayMember = "Value" + + oBoolOperators.Add(New KeyValuePair(Of Boolean, String)(True, frmFilter_cboBoolFilterEnabled)) + oBoolOperators.Add(New KeyValuePair(Of Boolean, String)(False, frmFilter_cboBoolFilterDisabled)) + + cboBoolFilter.DataSource = oBoolOperators + + 'cboNumericOps + cboNumericOps.ValueMember = "Key" + cboNumericOps.DisplayMember = "Value" + + oNumericOperators.Add(New KeyValuePair(Of clsGameFilter.eNumericOperators, String)(clsGameFilter.eNumericOperators.Equals, "=")) + oNumericOperators.Add(New KeyValuePair(Of clsGameFilter.eNumericOperators, String)(clsGameFilter.eNumericOperators.Greater, ">")) + oNumericOperators.Add(New KeyValuePair(Of clsGameFilter.eNumericOperators, String)(clsGameFilter.eNumericOperators.Lesser, "<")) + oNumericOperators.Add(New KeyValuePair(Of clsGameFilter.eNumericOperators, String)(clsGameFilter.eNumericOperators.GreaterEquals, ">=")) + oNumericOperators.Add(New KeyValuePair(Of clsGameFilter.eNumericOperators, String)(clsGameFilter.eNumericOperators.LesserEquals, "<=")) + + cboNumericOps.DataSource = oNumericOperators + + 'cboFilterField + cboFilterField.ValueMember = "Key" + cboFilterField.DisplayMember = "Value" + + For Each oField As clsGameFilterField In oValidFields + If oField.CheckStatus(clsGameFilterField.eFieldStatus.ValidFilter) Then + oFilterFields.Add(New KeyValuePair(Of clsGameFilterField, String)(oField, oField.FriendlyFieldName)) + End If + Next + + cboFilterField.DataSource = oFilterFields + + 'cboSortField + cboSortField.ValueMember = "Key" + cboSortField.DisplayMember = "Value" + + For Each oField As clsGameFilterField In oValidFields + If oField.CheckStatus(clsGameFilterField.eFieldStatus.ValidSort) Then + oSortFields.Add(New KeyValuePair(Of String, String)(oField.FieldName, oField.FriendlyFieldName)) + End If + Next + + cboSortField.DataSource = oSortFields + + 'Select Defaults + cboNumericOps.SelectedIndex = 0 + cboFilterField.SelectedIndex = 0 + cboSortField.SelectedIndex = 0 End Sub Private Sub SetForm() @@ -154,10 +514,7 @@ Public Class frmFilter 'Set Form Text optOr.Text = frmFilter_optOr optAnd.Text = frmFilter_optAnd - lblCompany.Text = frmFilter_lblCompany - lblProcess.Text = frmFilter_lblProcess - lblName.Text = frmFilter_lblName - grpGameInfoOptions.Text = frmFilter_grpGameInfoOptions + grpFilterType.Text = frmFilter_grpFilterType optAll.Text = frmFilter_optAll optAny.Text = frmFilter_optAny lblGameTags.Text = frmFilter_lblGameTags @@ -166,14 +523,36 @@ Public Class frmFilter btnAdd.Text = frmFilter_btnAdd btnOK.Text = frmFilter_btnOK grpTagOptions.Text = frmFilter_grpTagOptions - optTag.Text = frmFilter_optTag - optGameInfo.Text = frmFilter_optGameInfo + chkTag.Text = frmFilter_chkTag + chkGameInfo.Text = frmFilter_chkGameInfo + grpSorting.Text = frmFilter_grpSorting + lblSortFields.Text = frmFilter_lblSortsFields + optSortAsc.Text = frmFilter_optSortAsc + optSortDesc.Text = frmFilter_optSortDesc + btnAddFilter.Text = frmFilter_btnAddFilter + btnRemoveFilter.Text = frmFilter_btnRemoveFilter + lblCurrentFilters.Text = frmFilter_lblCurrentFilters + lblFields.Text = frmFilter_lblFields + lblFilterData.Text = frmFilter_lblFilterData + grpSortOptions.Text = frmFilter_grpSortOptions + + 'Defaults + optOr.Checked = True + optSortAsc.Checked = True + grpGameFilter.Enabled = False + grpTagFilter.Enabled = False + + 'Init Game Filter + lstFilter.ValueMember = "Key" + lstFilter.DisplayMember = "Value" End Sub Private Sub frmGameTags_Load(sender As Object, e As EventArgs) Handles MyBase.Load SetForm() - optGameInfo.Checked = True - LoadData() + LoadFilterFields() + LoadCombos() + LoadTagData() + LoadExistingFilters() End Sub Private Sub btnOK_Click(sender As Object, e As EventArgs) Handles btnOK.Click @@ -196,13 +575,37 @@ Public Class frmFilter End If End Sub - Private Sub optGameInfo_Click(sender As Object, e As EventArgs) Handles optGameInfo.Click, optTag.Click - If optGameInfo.Checked = True Then + Private Sub chkGameInfo_CheckedChanged(sender As Object, e As EventArgs) Handles chkGameInfo.CheckedChanged + If chkGameInfo.Checked Then grpGameFilter.Enabled = True - grpTagFilter.Enabled = False Else + optOr.Checked = True grpGameFilter.Enabled = False - grpTagFilter.Enabled = True + oGameFilters.Clear() + lstFilter.Items.Clear() End If End Sub + + Private Sub chkTag_CheckedChanged(sender As Object, e As EventArgs) Handles chkTag.CheckedChanged + If chkTag.Checked Then + grpTagFilter.Enabled = True + Else + grpTagFilter.Enabled = False + oTagFilters.Clear() + LoadTagData() + End If + End Sub + + Private Sub btnAddFilter_Click(sender As Object, e As EventArgs) Handles btnAddFilter.Click + AddFilter() + End Sub + + Private Sub btnRemoveFilter_Click(sender As Object, e As EventArgs) Handles btnRemoveFilter.Click + RemoveFilter() + End Sub + + Private Sub cboFilterField_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cboFilterField.SelectedIndexChanged + ChangeFilterMode() + End Sub + End Class \ No newline at end of file diff --git a/GBM/Forms/frmGameManager.vb b/GBM/Forms/frmGameManager.vb index bcfe486..c1daa0e 100644 --- a/GBM/Forms/frmGameManager.vb +++ b/GBM/Forms/frmGameManager.vb @@ -1,4 +1,5 @@ Imports GBM.My.Resources +Imports System.Collections.Specialized Imports System.IO Public Class frmGameManager @@ -13,14 +14,17 @@ Public Class frmGameManager Private bTriggerRestore As Boolean = False Private oBackupList As New List(Of clsGame) Private oRestoreList As New Hashtable - Private oAppData As Hashtable + Private oGameData As OrderedDictionary Private oLocalBackupData As SortedList Private oRemoteBackupData As SortedList Private bIsDirty As Boolean = False Private bIsLoading As Boolean = False Private oCurrentTagFilters As New List(Of clsTag) - Private oCurrentStringFilters As New Hashtable - Private eCurrentFilter As frmFilter.eFilterType = frmFilter.eFilterType.NoFilter + Private oCurrentFilters As New List(Of clsGameFilter) + Private eCurrentFilter As frmFilter.eFilterType = frmFilter.eFilterType.BaseFilter + Private bCurrentAndOperator As Boolean = False + Private bCurrentSortAsc As Boolean = True + Private sCurrentSortField As String = "Name" Private WithEvents tmFilterTimer As Timer Private Enum eModes As Integer @@ -70,12 +74,12 @@ Public Class frmGameManager End Set End Property - Private Property AppData As Hashtable + Private Property GameData As OrderedDictionary Get - Return oAppData + Return oGameData End Get - Set(value As Hashtable) - oAppData = value + Set(value As OrderedDictionary) + oGameData = value End Set End Property @@ -222,41 +226,55 @@ Public Class frmGameManager If optCustom.Checked Then If Not bRetainFilter Then frm = New frmFilter + + frm.TagFilters = oCurrentTagFilters + frm.GameFilters = oCurrentFilters + frm.FilterType = eCurrentFilter + frm.AndOperator = bCurrentAndOperator + frm.SortAsc = bCurrentSortAsc + frm.SortField = sCurrentSortField + frm.ShowDialog() + oCurrentTagFilters = frm.TagFilters - oCurrentStringFilters = frm.StringFilters + oCurrentFilters = frm.GameFilters eCurrentFilter = frm.FilterType + bCurrentAndOperator = frm.AndOperator + bCurrentSortAsc = frm.SortAsc + sCurrentSortField = frm.SortField End If Else oCurrentTagFilters.Clear() - oCurrentStringFilters.Clear() - eCurrentFilter = frmFilter.eFilterType.NoFilter + oCurrentFilters.Clear() + eCurrentFilter = frmFilter.eFilterType.BaseFilter + bCurrentSortAsc = True + sCurrentSortField = "Name" End If - AppData = mgrMonitorList.ReadFilteredList(oCurrentTagFilters, oCurrentStringFilters, eCurrentFilter) + GameData = mgrMonitorList.ReadFilteredList(oCurrentTagFilters, oCurrentFilters, eCurrentFilter, bCurrentAndOperator, bCurrentSortAsc, sCurrentSortField) If optPendingRestores.Checked Then oRestoreData = mgrRestore.CompareManifests 'Only show games with data to restore - Dim oTemporaryList As Hashtable = AppData.Clone + Dim oTemporaryList As OrderedDictionary = mgrCommon.GenericClone(GameData) For Each de As DictionaryEntry In oTemporaryList oGame = DirectCast(de.Value, clsGame) If Not oRestoreData.ContainsKey(oGame.Name) Then - AppData.Remove(de.Key) + GameData.Remove(de.Key) Else oRestoreData.Remove(oGame.Name) End If Next ElseIf optBackupData.Checked Then 'Only show games with backup data - Dim oTemporaryList As Hashtable = AppData.Clone + Dim oTemporaryList As OrderedDictionary = mgrCommon.GenericClone(GameData) oRestoreData = oRemoteBackupData.Clone For Each de As DictionaryEntry In oTemporaryList oGame = DirectCast(de.Value, clsGame) If Not oRemoteBackupData.ContainsKey(oGame.Name) Then - AppData.Remove(de.Key) + GameData.Remove(de.Key) Else oRestoreData.Remove(oGame.Name) End If @@ -269,7 +287,7 @@ Public Class frmGameManager oGame = New clsGame oGame.Name = oBackup.Name oGame.Temporary = True - AppData.Add(oGame.ID, oGame) + GameData.Add(oGame.ID, oGame) Next End If @@ -387,7 +405,7 @@ Public Class frmGameManager Dim oList As New List(Of KeyValuePair(Of String, String)) Dim sFilter As String = txtQuickFilter.Text - For Each de As DictionaryEntry In AppData + For Each de As DictionaryEntry In GameData oApp = DirectCast(de.Value, clsGame) oData = New KeyValuePair(Of String, String)(oApp.ID, oApp.Name) 'Apply the quick filter if applicable @@ -400,8 +418,6 @@ Public Class frmGameManager End If Next - oList.Sort(AddressOf mgrCommon.CompareByListBoxItemByValue) - lstGames.BeginUpdate() lstGames.ValueMember = "Key" lstGames.DisplayMember = "Value" @@ -545,7 +561,7 @@ Public Class frmGameManager frm.TagList = oTagsToSave Else For Each oData In lstGames.SelectedItems - oApp = DirectCast(AppData(oData.Key), clsGame) + oApp = DirectCast(GameData(oData.Key), clsGame) sMonitorIDs.Add(oApp.ID) Next frm.GameName = CurrentGame.Name @@ -731,7 +747,7 @@ Public Class frmGameManager IsLoading = True Dim oData As KeyValuePair(Of String, String) = lstGames.SelectedItems(0) - Dim oApp As clsGame = DirectCast(AppData(oData.Key), clsGame) + Dim oApp As clsGame = DirectCast(GameData(oData.Key), clsGame) 'Core txtID.Text = oApp.ID @@ -1028,6 +1044,27 @@ Public Class frmGameManager VerifyCleanFolder() End Sub + Private Sub MonitorOnlyModeChange() + If chkMonitorOnly.Checked Then + chkFolderSave.Enabled = False + chkTimeStamp.Enabled = False + lblSavePath.Enabled = False + txtSavePath.Enabled = False + btnSavePathBrowse.Enabled = False + btnInclude.Enabled = False + btnExclude.Enabled = False + Else + chkFolderSave.Enabled = True + chkTimeStamp.Enabled = True + lblSavePath.Enabled = True + txtSavePath.Enabled = True + btnSavePathBrowse.Enabled = True + btnInclude.Enabled = True + btnExclude.Enabled = True + End If + VerifyCleanFolder() + End Sub + Private Sub TimeStampModeChange() If chkTimeStamp.Checked Then nudLimit.Visible = True @@ -1042,7 +1079,7 @@ Public Class frmGameManager Private Sub VerifyCleanFolder() If Not bIsLoading Then - If chkFolderSave.Checked = True And txtExclude.Text = String.Empty And txtSavePath.Text <> String.Empty Then + If (chkFolderSave.Checked = True And txtExclude.Text = String.Empty And txtSavePath.Text <> String.Empty) And Not chkMonitorOnly.Checked Then chkCleanFolder.Enabled = True Else chkCleanFolder.Checked = False @@ -1195,7 +1232,7 @@ Public Class frmGameManager If lstGames.SelectedItems.Count = 1 Then oData = lstGames.SelectedItems(0) - oApp = DirectCast(AppData(oData.Key), clsGame) + oApp = DirectCast(GameData(oData.Key), clsGame) If mgrCommon.ShowMessage(frmGameManager_ConfirmGameDelete, oApp.Name, MsgBoxStyle.YesNo) = MsgBoxResult.Yes Then mgrMonitorList.DoListDelete(oApp.ID) @@ -1207,7 +1244,7 @@ Public Class frmGameManager Dim sMonitorIDs As New List(Of String) For Each oData In lstGames.SelectedItems - oApp = DirectCast(AppData(oData.Key), clsGame) + oApp = DirectCast(GameData(oData.Key), clsGame) sMonitorIDs.Add(oApp.ID) Next @@ -1247,7 +1284,7 @@ Public Class frmGameManager Return False End If - If chkFolderSave.Checked = False And txtFileType.Text = String.Empty Then + If (chkFolderSave.Checked = False And txtFileType.Text = String.Empty) And Not chkMonitorOnly.Checked Then mgrCommon.ShowMessage(frmGameManager_ErrorNoItems, MsgBoxStyle.Exclamation) btnInclude.Focus() Return False @@ -1334,8 +1371,8 @@ Public Class frmGameManager BackupList.Clear() For Each oData In lstGames.SelectedItems - oGame = DirectCast(AppData(oData.Key), clsGame) - BackupList.Add(oGame) + oGame = DirectCast(GameData(oData.Key), clsGame) + If Not oGame.MonitorOnly Then BackupList.Add(oGame) Next If BackupList.Count = 1 Then @@ -1372,17 +1409,14 @@ Public Class frmGameManager If lstGames.SelectedItems.Count > 0 Then RestoreList.Clear() - If lstGames.SelectedItems.Count = 1 Then - RestoreList.Add(CurrentGame, CurrentBackupItem) - Else - For Each oData In lstGames.SelectedItems - If oRemoteBackupData.Contains(oData.Value) Then - oGame = DirectCast(AppData(oData.Key), clsGame) - oBackup = DirectCast(oRemoteBackupData(oData.Value), clsBackup) - RestoreList.Add(oGame, oBackup) - End If - Next - End If + + For Each oData In lstGames.SelectedItems + If oRemoteBackupData.Contains(oData.Value) Then + oGame = DirectCast(GameData(oData.Key), clsGame) + oBackup = DirectCast(oRemoteBackupData(oData.Value), clsBackup) + If Not oGame.MonitorOnly Then RestoreList.Add(oGame, oBackup) + End If + Next If RestoreList.Count = 1 Then bDoRestore = True @@ -1713,4 +1747,8 @@ Public Class frmGameManager Private Sub frmGameManager_Shown(sender As Object, e As EventArgs) Handles MyBase.Shown txtQuickFilter.Focus() End Sub + + Private Sub chkMonitorOnly_CheckedChanged(sender As Object, e As EventArgs) Handles chkMonitorOnly.CheckedChanged + MonitorOnlyModeChange() + End Sub End Class diff --git a/GBM/Forms/frmSyncFields.vb b/GBM/Forms/frmSyncFields.vb index 7200cc9..cea9a57 100644 --- a/GBM/Forms/frmSyncFields.vb +++ b/GBM/Forms/frmSyncFields.vb @@ -67,49 +67,49 @@ Public Class frmSyncFields Private Sub chkTimeStamp_CheckedChanged(sender As Object, e As EventArgs) Handles chkTimeStamp.CheckedChanged If chkTimeStamp.Checked Then - SyncFields = mgrCommon.SetSyncField(SyncFields, clsGame.eOptionalSyncFields.TimeStamp) + SyncFields = clsGame.SetSyncField(SyncFields, clsGame.eOptionalSyncFields.TimeStamp) Else - SyncFields = mgrCommon.RemoveSyncField(SyncFields, clsGame.eOptionalSyncFields.TimeStamp) + SyncFields = clsGame.RemoveSyncField(SyncFields, clsGame.eOptionalSyncFields.TimeStamp) End If End Sub Private Sub chkGamePath_CheckedChanged(sender As Object, e As EventArgs) Handles chkGamePath.CheckedChanged If chkGamePath.Checked Then - SyncFields = mgrCommon.SetSyncField(SyncFields, clsGame.eOptionalSyncFields.GamePath) + SyncFields = clsGame.SetSyncField(SyncFields, clsGame.eOptionalSyncFields.GamePath) Else - SyncFields = mgrCommon.RemoveSyncField(SyncFields, clsGame.eOptionalSyncFields.GamePath) + SyncFields = clsGame.RemoveSyncField(SyncFields, clsGame.eOptionalSyncFields.GamePath) End If End Sub Private Sub chkCompany_CheckedChanged(sender As Object, e As EventArgs) Handles chkCompany.CheckedChanged If chkCompany.Checked Then - SyncFields = mgrCommon.SetSyncField(SyncFields, clsGame.eOptionalSyncFields.Company) + SyncFields = clsGame.SetSyncField(SyncFields, clsGame.eOptionalSyncFields.Company) Else - SyncFields = mgrCommon.RemoveSyncField(SyncFields, clsGame.eOptionalSyncFields.Company) + SyncFields = clsGame.RemoveSyncField(SyncFields, clsGame.eOptionalSyncFields.Company) End If End Sub Private Sub chkVersion_CheckedChanged(sender As Object, e As EventArgs) Handles chkVersion.CheckedChanged If chkVersion.Checked Then - SyncFields = mgrCommon.SetSyncField(SyncFields, clsGame.eOptionalSyncFields.Version) + SyncFields = clsGame.SetSyncField(SyncFields, clsGame.eOptionalSyncFields.Version) Else - SyncFields = mgrCommon.RemoveSyncField(SyncFields, clsGame.eOptionalSyncFields.Version) + SyncFields = clsGame.RemoveSyncField(SyncFields, clsGame.eOptionalSyncFields.Version) End If End Sub Private Sub chkIcon_CheckedChanged(sender As Object, e As EventArgs) Handles chkIcon.CheckedChanged If chkIcon.Checked Then - SyncFields = mgrCommon.SetSyncField(SyncFields, clsGame.eOptionalSyncFields.Icon) + SyncFields = clsGame.SetSyncField(SyncFields, clsGame.eOptionalSyncFields.Icon) Else - SyncFields = mgrCommon.RemoveSyncField(SyncFields, clsGame.eOptionalSyncFields.Icon) + SyncFields = clsGame.RemoveSyncField(SyncFields, clsGame.eOptionalSyncFields.Icon) End If End Sub Private Sub chkMonitorGame_CheckedChanged(sender As Object, e As EventArgs) Handles chkMonitorGame.CheckedChanged If chkMonitorGame.Checked Then - SyncFields = mgrCommon.SetSyncField(SyncFields, clsGame.eOptionalSyncFields.MonitorGame) + SyncFields = clsGame.SetSyncField(SyncFields, clsGame.eOptionalSyncFields.MonitorGame) Else - SyncFields = mgrCommon.RemoveSyncField(SyncFields, clsGame.eOptionalSyncFields.MonitorGame) + SyncFields = clsGame.RemoveSyncField(SyncFields, clsGame.eOptionalSyncFields.MonitorGame) End If End Sub End Class \ No newline at end of file diff --git a/GBM/Game Backup Monitor.vbproj b/GBM/Game Backup Monitor.vbproj index 0267ad9..d4826e5 100644 --- a/GBM/Game Backup Monitor.vbproj +++ b/GBM/Game Backup Monitor.vbproj @@ -122,6 +122,8 @@ + + diff --git a/GBM/Managers/mgrCommon.vb b/GBM/Managers/mgrCommon.vb index e9c8bc9..839f170 100644 --- a/GBM/Managers/mgrCommon.vb +++ b/GBM/Managers/mgrCommon.vb @@ -3,6 +3,8 @@ Imports System.Net Imports System.IO Imports System.Security.Principal Imports System.Text.RegularExpressions +Imports System.Runtime.Serialization +Imports System.Runtime.Serialization.Formatters.Binary Public Class mgrCommon @@ -35,6 +37,28 @@ Public Class mgrCommon End Get End Property + 'Source - https://stackoverflow.com/questions/18873152/deep-copy-of-ordereddictionary + Public Shared Function GenericClone(ByVal oOriginal As Object) As Object + 'Construct a temporary memory stream + Dim oStream As MemoryStream = New MemoryStream() + + 'Construct a serialization formatter that does all the hard work + Dim oFormatter As BinaryFormatter = New BinaryFormatter() + + 'This line Is explained in the "Streaming Contexts" section + oFormatter.Context = New StreamingContext(StreamingContextStates.Clone) + + 'Serialize the object graph into the memory stream + oFormatter.Serialize(oStream, oOriginal) + + 'Seek back to the start of the memory stream before deserializing + oStream.Position = 0 + + 'Deserialize the graph into a New set of objects + 'Return the root of the graph (deep copy) to the caller + Return oFormatter.Deserialize(oStream) + End Function + Public Shared Function CheckAddress(ByVal URL As String) As Boolean Try Dim request As WebRequest = WebRequest.Create(URL) @@ -213,14 +237,6 @@ Public Class mgrCommon oProcess.Start() End Sub - Public Shared Function SetSyncField(ByVal eSyncFields As clsGame.eOptionalSyncFields, ByVal eSyncField As clsGame.eOptionalSyncFields) As clsGame.eOptionalSyncFields - Return eSyncFields Or eSyncField - End Function - - Public Shared Function RemoveSyncField(ByVal eSyncFields As clsGame.eOptionalSyncFields, ByVal eSyncField As clsGame.eOptionalSyncFields) As clsGame.eOptionalSyncFields - Return eSyncFields And (Not eSyncField) - End Function - 'Get a file size Public Shared Function GetFileSize(ByVal sFile As String) As Long Dim oFileInfo As FileInfo @@ -253,13 +269,15 @@ Public Class mgrCommon End Function 'Calculate the current size of a folder - Public Shared Function GetFolderSize(ByVal sPath As String, ByVal sInclude As String(), ByVal sExclude As String()) + Public Shared Function GetFolderSize(ByVal sPath As String, ByVal sInclude As String(), ByVal sExclude As String()) As Long Dim oFolder As DirectoryInfo Dim bInclude As Boolean Dim bExclude As Boolean Dim lSize As Long = 0 Try + If Not Directory.Exists(sPath) Then Return lSize + oFolder = New DirectoryInfo(sPath) 'Files diff --git a/GBM/Managers/mgrMonitorList.vb b/GBM/Managers/mgrMonitorList.vb index c4b1682..5aa9d1d 100644 --- a/GBM/Managers/mgrMonitorList.vb +++ b/GBM/Managers/mgrMonitorList.vb @@ -1,4 +1,5 @@ Imports GBM.My.Resources +Imports System.Collections.Specialized Imports System.IO Public Class mgrMonitorList @@ -496,36 +497,22 @@ Public Class mgrMonitorList End Sub 'Filter Functions - Private Shared Function BuildFilterQuery(ByVal oTagFilters As List(Of clsTag), ByVal hshStringFilters As Hashtable, eFilterType As frmFilter.eFilterType, ByRef hshParams As Hashtable) As String + Private Shared Function BuildFilterQuery(ByVal oTagFilters As List(Of clsTag), ByVal oFilters As List(Of clsGameFilter), ByVal eFilterType As frmFilter.eFilterType, ByVal bAndOperator As Boolean, + ByVal bSortAsc As Boolean, ByVal sSortField As String, ByRef hshParams As Hashtable) As String Dim sSQL As String = String.Empty Dim iCounter As Integer = 0 Dim sBaseSelect As String = "MonitorID, Name, Process, Path, AbsolutePath, FolderSave, FileType, TimeStamp, ExcludeList, ProcessPath, Icon, Hours, Version, Company, Enabled, MonitorOnly, BackupLimit, CleanFolder, Parameter FROM monitorlist" + Dim sSort As String = " ORDER BY " & sSortField + + If bSortAsc Then + sSort &= " ASC" + Else + sSort &= " DESC" + End If Select Case eFilterType - Case frmFilter.eFilterType.NoFilter - sSQL = "SELECT " & sBaseSelect & " ORDER BY Name Asc" - Case frmFilter.eFilterType.FieldAnd, frmFilter.eFilterType.FieldOr + Case frmFilter.eFilterType.BaseFilter sSQL = "SELECT " & sBaseSelect - - If hshStringFilters.Count > 0 Then - sSQL &= " WHERE (" - For Each de As DictionaryEntry In hshStringFilters - sSQL &= de.Key & " LIKE @" & de.Key - hshParams.Add(de.Key, "%" & de.Value.ToString & "%") - iCounter += 1 - If iCounter <> hshStringFilters.Count Then - Select Case eFilterType - Case frmFilter.eFilterType.FieldAnd - sSQL &= " AND " - Case frmFilter.eFilterType.FieldOr - sSQL &= " OR " - End Select - End If - - Next - sSQL &= ")" - End If - sSQL &= " ORDER BY Name Asc" Case frmFilter.eFilterType.AnyTag sSQL = "SELECT DISTINCT " & sBaseSelect sSQL &= " NATURAL JOIN gametags WHERE gametags.TagID IN (" @@ -537,7 +524,7 @@ Public Class mgrMonitorList Next sSQL = sSQL.TrimEnd(",") - sSQL &= ") ORDER BY Name Asc" + sSQL &= ")" Case frmFilter.eFilterType.AllTags sSQL = "SELECT " & sBaseSelect & " WHERE MonitorID IN " @@ -549,41 +536,78 @@ Public Class mgrMonitorList hshParams.Add("TagID" & iCounter, oTag.ID) iCounter += 1 Next - - sSQL &= " ORDER BY Name Asc" Case frmFilter.eFilterType.NoTags - sSQL = "SELECT " & sBaseSelect & " WHERE MonitorID NOT IN (SELECT MonitorID FROM gametags) ORDER BY Name Asc" + sSQL = "SELECT " & sBaseSelect & " WHERE MonitorID NOT IN (SELECT MonitorID FROM gametags)" End Select + 'Handle Other Filters + If oFilters.Count > 0 Then + If eFilterType = frmFilter.eFilterType.BaseFilter Then + sSQL &= " WHERE (" + Else + sSQL &= " AND (" + End If + + iCounter = 0 + For Each oFilter As clsGameFilter In oFilters + Select Case oFilter.Field.Type + Case clsGameFilterField.eDataType.fString + sSQL &= oFilter.Field.FieldName & " LIKE @" & oFilter.ID + hshParams.Add(oFilter.ID, "%" & oFilter.Data & "%") + Case clsGameFilterField.eDataType.fNumeric + sSQL &= oFilter.Field.FieldName & " " & oFilter.NumericOperatorAsString & " @" & oFilter.ID + hshParams.Add(oFilter.ID, oFilter.Data) + Case clsGameFilterField.eDataType.fBool + sSQL &= oFilter.Field.FieldName & " = @" & oFilter.ID + hshParams.Add(oFilter.ID, oFilter.Data) + End Select + + iCounter += 1 + If iCounter <> oFilters.Count Then + If bAndOperator Then + sSQL &= " AND " + Else + sSQL &= " OR " + End If + End If + Next + sSQL &= ")" + End If + + 'Handle Sorting + sSQL &= sSort + Return sSQL End Function - Public Shared Function ReadFilteredList(ByVal oTagFilters As List(Of clsTag), ByVal hshStringFilters As Hashtable, eFilterType As frmFilter.eFilterType, Optional ByVal iSelectDB As mgrSQLite.Database = mgrSQLite.Database.Local) As Hashtable + Public Shared Function ReadFilteredList(ByVal oTagFilters As List(Of clsTag), ByVal oFilters As List(Of clsGameFilter), ByVal eFilterType As frmFilter.eFilterType, ByVal bAndOperator As Boolean, + ByVal bSortAsc As Boolean, ByVal sSortField As String, Optional ByVal iSelectDB As mgrSQLite.Database = mgrSQLite.Database.Local) As OrderedDictionary Dim oDatabase As New mgrSQLite(iSelectDB) Dim oData As DataSet Dim sSQL As String = String.Empty - Dim hshList As New Hashtable + Dim oList As New OrderedDictionary Dim oGame As clsGame Dim hshParams As New Hashtable Dim iCounter As Integer = 0 - sSQL = BuildFilterQuery(oTagFilters, hshStringFilters, eFilterType, hshParams) + sSQL = BuildFilterQuery(oTagFilters, oFilters, eFilterType, bAndOperator, bSortAsc, sSortField, hshParams) oData = oDatabase.ReadParamData(sSQL, hshParams) For Each dr As DataRow In oData.Tables(0).Rows oGame = MapToObject(dr) - hshList.Add(oGame.ID, oGame) + oList.Add(oGame.ID, oGame) Next - Return hshList + Return oList End Function 'Import / Export Functions - Public Shared Function ReadListForExport(ByVal oTagFilters As List(Of clsTag), ByVal hshStringFilters As Hashtable, ByVal eFilterType As frmFilter.eFilterType, Optional ByVal iSelectDB As mgrSQLite.Database = mgrSQLite.Database.Local) As List(Of Game) + Public Shared Function ReadListForExport(ByVal oTagFilters As List(Of clsTag), ByVal oFilters As List(Of clsGameFilter), ByVal eFilterType As frmFilter.eFilterType, ByVal bAndOperator As Boolean, + ByVal bSortAsc As Boolean, ByVal sSortField As String, Optional ByVal iSelectDB As mgrSQLite.Database = mgrSQLite.Database.Local) As List(Of Game) Dim oDatabase As New mgrSQLite(iSelectDB) Dim oData As DataSet Dim sSQL As String = String.Empty @@ -592,7 +616,7 @@ Public Class mgrMonitorList Dim oGame As Game Dim hshParams As New Hashtable - sSQL = BuildFilterQuery(oTagFilters, hshStringFilters, eFilterType, hshParams) + sSQL = BuildFilterQuery(oTagFilters, oFilters, eFilterType, bAndOperator, bSortAsc, sSortField, hshParams) oData = oDatabase.ReadParamData(sSQL, hshParams) @@ -606,6 +630,7 @@ Public Class mgrMonitorList oGame.FolderSave = CBool(dr("FolderSave")) If Not IsDBNull(dr("FileType")) Then oGame.FileType = CStr(dr("FileType")) If Not IsDBNull(dr("ExcludeList")) Then oGame.ExcludeList = CStr(dr("ExcludeList")) + oGame.MonitorOnly = CBool(dr("MonitorOnly")) If Not IsDBNull(dr("Parameter")) Then oGame.Parameter = CStr(dr("Parameter")) oGame.Tags = mgrGameTags.GetTagsByGameForExport(sID) oList.Add(oGame) @@ -685,18 +710,24 @@ Public Class mgrMonitorList Dim oList As List(Of Game) Dim bSuccess As Boolean = False Dim oTagFilters As New List(Of clsTag) - Dim oStringFilters As New Hashtable - Dim eCurrentFilter As frmFilter.eFilterType = frmFilter.eFilterType.NoFilter + Dim oFilters As New List(Of clsGameFilter) + Dim eCurrentFilter As frmFilter.eFilterType = frmFilter.eFilterType.BaseFilter + Dim bAndOperator As Boolean = True + Dim bSortAsc As Boolean = True + Dim sSortField As String = "Name" If mgrCommon.ShowMessage(mgrMonitorList_ConfirmApplyFilter, MsgBoxStyle.YesNo) = MsgBoxResult.Yes Then Dim frm As New frmFilter frm.ShowDialog() oTagFilters = frm.TagFilters - oStringFilters = frm.StringFilters + oFilters = frm.GameFilters eCurrentFilter = frm.FilterType + bAndOperator = frm.AndOperator + bSortAsc = frm.SortAsc + sSortField = frm.SortField End If - oList = ReadListForExport(oTagFilters, oStringFilters, eCurrentFilter) + oList = ReadListForExport(oTagFilters, oFilters, eCurrentFilter, bAndOperator, bSortAsc, sSortField) bSuccess = mgrXML.SerializeAndExport(oList, sLocation) diff --git a/GBM/Managers/mgrXML.vb b/GBM/Managers/mgrXML.vb index a25c35d..b432246 100644 --- a/GBM/Managers/mgrXML.vb +++ b/GBM/Managers/mgrXML.vb @@ -28,6 +28,7 @@ Public Class mgrXML oGame.FolderSave = g.FolderSave oGame.FileType = g.FileType oGame.ExcludeList = g.ExcludeList + oGame.MonitorOnly = g.MonitorOnly oGame.Parameter = g.Parameter For Each t As Tag In g.Tags oGame.ImportTags.Add(t) diff --git a/GBM/My Project/AssemblyInfo.vb b/GBM/My Project/AssemblyInfo.vb index f79cf5e..66ef53c 100644 --- a/GBM/My Project/AssemblyInfo.vb +++ b/GBM/My Project/AssemblyInfo.vb @@ -33,7 +33,7 @@ Imports System.Runtime.InteropServices ' by using the '*' as shown below: ' - - + + \ No newline at end of file diff --git a/GBM/My Project/Resources.Designer.vb b/GBM/My Project/Resources.Designer.vb index 7eae80c..ef39147 100644 --- a/GBM/My Project/Resources.Designer.vb +++ b/GBM/My Project/Resources.Designer.vb @@ -924,6 +924,15 @@ Namespace My.Resources End Get End Property + ''' + ''' Looks up a localized string similar to Add. + ''' + Friend ReadOnly Property frmFilter_btnAddFilter() As String + Get + Return ResourceManager.GetString("frmFilter_btnAddFilter", resourceCulture) + End Get + End Property + ''' ''' Looks up a localized string similar to &OK. ''' @@ -942,6 +951,204 @@ Namespace My.Resources End Get End Property + ''' + ''' Looks up a localized string similar to Remove. + ''' + Friend ReadOnly Property frmFilter_btnRemoveFilter() As String + Get + Return ResourceManager.GetString("frmFilter_btnRemoveFilter", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to Disabled. + ''' + Friend ReadOnly Property frmFilter_cboBoolFilterDisabled() As String + Get + Return ResourceManager.GetString("frmFilter_cboBoolFilterDisabled", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to Enabled. + ''' + Friend ReadOnly Property frmFilter_cboBoolFilterEnabled() As String + Get + Return ResourceManager.GetString("frmFilter_cboBoolFilterEnabled", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to Game Information. + ''' + Friend ReadOnly Property frmFilter_chkGameInfo() As String + Get + Return ResourceManager.GetString("frmFilter_chkGameInfo", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to Tag. + ''' + Friend ReadOnly Property frmFilter_chkTag() As String + Get + Return ResourceManager.GetString("frmFilter_chkTag", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to Backup Limit *. + ''' + Friend ReadOnly Property frmFilter_FieldBackupLimit() As String + Get + Return ResourceManager.GetString("frmFilter_FieldBackupLimit", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to Delete Folder on Restore. + ''' + Friend ReadOnly Property frmFilter_FieldCleanFolder() As String + Get + Return ResourceManager.GetString("frmFilter_FieldCleanFolder", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to Company. + ''' + Friend ReadOnly Property frmFilter_FieldCompany() As String + Get + Return ResourceManager.GetString("frmFilter_FieldCompany", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to Monitor Game. + ''' + Friend ReadOnly Property frmFilter_FieldEnabled() As String + Get + Return ResourceManager.GetString("frmFilter_FieldEnabled", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to Exclude Items. + ''' + Friend ReadOnly Property frmFilter_FieldExcludeList() As String + Get + Return ResourceManager.GetString("frmFilter_FieldExcludeList", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to Include Items. + ''' + Friend ReadOnly Property frmFilter_FieldFileType() As String + Get + Return ResourceManager.GetString("frmFilter_FieldFileType", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to Save Entire Folder. + ''' + Friend ReadOnly Property frmFilter_FieldFolderSave() As String + Get + Return ResourceManager.GetString("frmFilter_FieldFolderSave", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to Hours. + ''' + Friend ReadOnly Property frmFilter_FieldHours() As String + Get + Return ResourceManager.GetString("frmFilter_FieldHours", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to Icon. + ''' + Friend ReadOnly Property frmFilter_FieldIcon() As String + Get + Return ResourceManager.GetString("frmFilter_FieldIcon", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to Monitor Only. + ''' + Friend ReadOnly Property frmFilter_FieldMonitorOnly() As String + Get + Return ResourceManager.GetString("frmFilter_FieldMonitorOnly", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to Name. + ''' + Friend ReadOnly Property frmFilter_FieldName() As String + Get + Return ResourceManager.GetString("frmFilter_FieldName", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to Parameter. + ''' + Friend ReadOnly Property frmFilter_FieldParameter() As String + Get + Return ResourceManager.GetString("frmFilter_FieldParameter", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to Save Path *. + ''' + Friend ReadOnly Property frmFilter_FieldPath() As String + Get + Return ResourceManager.GetString("frmFilter_FieldPath", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to Process. + ''' + Friend ReadOnly Property frmFilter_FieldProcess() As String + Get + Return ResourceManager.GetString("frmFilter_FieldProcess", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to Game Path. + ''' + Friend ReadOnly Property frmFilter_FieldProcessPath() As String + Get + Return ResourceManager.GetString("frmFilter_FieldProcessPath", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to Save Multiple Backups. + ''' + Friend ReadOnly Property frmFilter_FieldTimeStamp() As String + Get + Return ResourceManager.GetString("frmFilter_FieldTimeStamp", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to Version. + ''' + Friend ReadOnly Property frmFilter_FieldVersion() As String + Get + Return ResourceManager.GetString("frmFilter_FieldVersion", resourceCulture) + End Get + End Property + ''' ''' Looks up a localized string similar to Custom Filter. ''' @@ -952,11 +1159,29 @@ Namespace My.Resources End Property ''' - ''' Looks up a localized string similar to Options. + ''' Looks up a localized string similar to Filter Type. ''' - Friend ReadOnly Property frmFilter_grpGameInfoOptions() As String + Friend ReadOnly Property frmFilter_grpFilterType() As String Get - Return ResourceManager.GetString("frmFilter_grpGameInfoOptions", resourceCulture) + Return ResourceManager.GetString("frmFilter_grpFilterType", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to Sorting. + ''' + Friend ReadOnly Property frmFilter_grpSorting() As String + Get + Return ResourceManager.GetString("frmFilter_grpSorting", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to Sort Options. + ''' + Friend ReadOnly Property frmFilter_grpSortOptions() As String + Get + Return ResourceManager.GetString("frmFilter_grpSortOptions", resourceCulture) End Get End Property @@ -970,11 +1195,29 @@ Namespace My.Resources End Property ''' - ''' Looks up a localized string similar to Company:. + ''' Looks up a localized string similar to Current Filters. ''' - Friend ReadOnly Property frmFilter_lblCompany() As String + Friend ReadOnly Property frmFilter_lblCurrentFilters() As String Get - Return ResourceManager.GetString("frmFilter_lblCompany", resourceCulture) + Return ResourceManager.GetString("frmFilter_lblCurrentFilters", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to Available Fields. + ''' + Friend ReadOnly Property frmFilter_lblFields() As String + Get + Return ResourceManager.GetString("frmFilter_lblFields", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to Filter. + ''' + Friend ReadOnly Property frmFilter_lblFilterData() As String + Get + Return ResourceManager.GetString("frmFilter_lblFilterData", resourceCulture) End Get End Property @@ -988,20 +1231,11 @@ Namespace My.Resources End Property ''' - ''' Looks up a localized string similar to Name:. + ''' Looks up a localized string similar to Available Fields. ''' - Friend ReadOnly Property frmFilter_lblName() As String + Friend ReadOnly Property frmFilter_lblSortsFields() As String Get - Return ResourceManager.GetString("frmFilter_lblName", resourceCulture) - End Get - End Property - - ''' - ''' Looks up a localized string similar to Process:. - ''' - Friend ReadOnly Property frmFilter_lblProcess() As String - Get - Return ResourceManager.GetString("frmFilter_lblProcess", resourceCulture) + Return ResourceManager.GetString("frmFilter_lblSortsFields", resourceCulture) End Get End Property @@ -1014,6 +1248,24 @@ Namespace My.Resources End Get End Property + ''' + ''' Looks up a localized string similar to * Indicates a field that may give unexpected results.. + ''' + Friend ReadOnly Property frmFilter_lblWarning() As String + Get + Return ResourceManager.GetString("frmFilter_lblWarning", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to contains. + ''' + Friend ReadOnly Property frmFilter_lstFilterContains() As String + Get + Return ResourceManager.GetString("frmFilter_lstFilterContains", resourceCulture) + End Get + End Property + ''' ''' Looks up a localized string similar to All Tags. ''' @@ -1024,7 +1276,7 @@ Namespace My.Resources End Property ''' - ''' Looks up a localized string similar to And. + ''' Looks up a localized string similar to All. ''' Friend ReadOnly Property frmFilter_optAnd() As String Get @@ -1042,16 +1294,7 @@ Namespace My.Resources End Property ''' - ''' Looks up a localized string similar to Game Information. - ''' - Friend ReadOnly Property frmFilter_optGameInfo() As String - Get - Return ResourceManager.GetString("frmFilter_optGameInfo", resourceCulture) - End Get - End Property - - ''' - ''' Looks up a localized string similar to Or. + ''' Looks up a localized string similar to Any. ''' Friend ReadOnly Property frmFilter_optOr() As String Get @@ -1060,11 +1303,20 @@ Namespace My.Resources End Property ''' - ''' Looks up a localized string similar to Tag. + ''' Looks up a localized string similar to Ascending. ''' - Friend ReadOnly Property frmFilter_optTag() As String + Friend ReadOnly Property frmFilter_optSortAsc() As String Get - Return ResourceManager.GetString("frmFilter_optTag", resourceCulture) + Return ResourceManager.GetString("frmFilter_optSortAsc", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to Descending. + ''' + Friend ReadOnly Property frmFilter_optSortDesc() As String + Get + Return ResourceManager.GetString("frmFilter_optSortDesc", resourceCulture) End Get End Property diff --git a/GBM/My Project/Resources.resx b/GBM/My Project/Resources.resx index be8cac5..468bfb0 100644 --- a/GBM/My Project/Resources.resx +++ b/GBM/My Project/Resources.resx @@ -895,18 +895,9 @@ Custom Filter - - Company: - Current Filter - - Name: - - - Process: - Available Tags @@ -914,18 +905,18 @@ All Tags - And + All Any Tag - + Game Information - Or + Any - + Tag @@ -1015,8 +1006,8 @@ Saved Game Explorer - - Options + + Filter Type Options @@ -1801,4 +1792,97 @@ GBM is busy with an open window on your desktop. + + Sorting + + + Ascending + + + Descending + + + Company + + + Hours + + + Name + + + Process + + + Add + + + Remove + + + Parameter + + + Version + + + Current Filters + + + Available Fields + + + Filter + + + Disabled + + + Enabled + + + Backup Limit * + + + Delete Folder on Restore + + + Monitor Game + + + Exclude Items + + + Include Items + + + Save Entire Folder + + + Icon + + + Monitor Only + + + Save Path * + + + Game Path + + + Save Multiple Backups + + + Available Fields + + + * Indicates a field that may give unexpected results. + + + contains + + + Sort Options + \ No newline at end of file diff --git a/GBM/readme.txt b/GBM/readme.txt index 9b96e04..77c1e33 100644 --- a/GBM/readme.txt +++ b/GBM/readme.txt @@ -1,14 +1,20 @@ -Game Backup Monitor v1.03 Readme +Game Backup Monitor v1.04 Readme http://mikemaximus.github.io/gbm-web/ gamebackupmonitor@gmail.com -August 2, 2017 +September 26, 2017 -New in 1.03 - -- (Windows) You can no longer Alt-Tab to GBM while it's minimized to the system tray. This was an unintentional change in v1.02 and caused various bugs. -- (Windows) Fixed various issues and inconsistent behavior when using the system tray and menu. -- (Linux) Free drive space is now checked correctly when performing a backup. GBM now requires "df" (Coreutils) on Linux. -- (All) Error messages related to SQLite will now be displayed correctly, instead of forcing the application to exit. +New in 1.04 +- (All) The import feature now detects most saved games currently on your PC and automatically selects configurations for you. +- (All) The Game Manager's "Custom Filter" feature has received a major overhaul: + - More fields are now available for use. + - Added the ability to combine different filters. + - Added the ability to set a sort field and order. + - The current filter is now saved when the Custom Filter window is closed and re-opened. +- (All) The "Monitor Only" feature has been updated. This feature allows tracking play time for games that do not require a backup, such as MMOs or CCGs. + - The Game Manager now disables and ignores validation on fields that aren't needed for a Monitor Only configuration. + - Monitor Only is now included in the XML Import / Export. + - Monitor Only configurations may now be included in the official game lists. + The entire version history of GBM releases is available at http://mikemaximus.github.io/gbm-web/versionhistory.html \ No newline at end of file