diff --git a/CarManager3Setup/CarManager3Setup.vdproj b/CarManager3Setup/CarManager3Setup.vdproj index f1976d6..8e2ab6d 100644 --- a/CarManager3Setup/CarManager3Setup.vdproj +++ b/CarManager3Setup/CarManager3Setup.vdproj @@ -332,15 +332,15 @@ { "Name" = "8:Microsoft Visual Studio" "ProductName" = "8:Car Manager 3" - "ProductCode" = "8:{79688954-DAEC-4255-BF3C-8FD89EEC28AA}" - "PackageCode" = "8:{6ED782FF-C62F-45E9-B6A8-756604C01717}" + "ProductCode" = "8:{8FDFF7ED-D464-4F87-BA8F-BDC1000520E4}" + "PackageCode" = "8:{83DA7553-805F-4A70-921B-9A6FB0787780}" "UpgradeCode" = "8:{6FF57925-465E-4DB9-85DA-CE933191A3DD}" "AspNetVersion" = "8:2.0.50727.0" "RestartWWWService" = "11:FALSE" "RemovePreviousVersions" = "11:TRUE" "DetectNewerInstalledVersion" = "11:TRUE" "InstallAllUsers" = "11:FALSE" - "ProductVersion" = "8:1.3.0" + "ProductVersion" = "8:1.4.0" "Manufacturer" = "8:Jaro Digital" "ARPHELPTELEPHONE" = "8:" "ARPHELPLINK" = "8:" diff --git a/CarManagerV3/App.config b/CarManagerV3/App.config index 25f046c..4a09ba7 100644 --- a/CarManagerV3/App.config +++ b/CarManagerV3/App.config @@ -13,6 +13,9 @@ + + False + \ No newline at end of file diff --git a/CarManagerV3/CarManagerV3.csproj b/CarManagerV3/CarManagerV3.csproj index 56d59a4..4f45086 100644 --- a/CarManagerV3/CarManagerV3.csproj +++ b/CarManagerV3/CarManagerV3.csproj @@ -15,19 +15,18 @@ false true 0 - 1.0.0.%2a + 1.4.0 false false true - false + true true CarMgm_Icon.ico Car Manager 3 - 1.3 - 1.3.0 - 1.3.0 Car Manager 3 + 1.4.0 + @@ -39,6 +38,7 @@ Settings.settings + diff --git a/CarManagerV3/Forms/CarDetailsForm.Designer.cs b/CarManagerV3/Forms/CarDetailsForm.Designer.cs index 424e58a..3b3bdf1 100644 --- a/CarManagerV3/Forms/CarDetailsForm.Designer.cs +++ b/CarManagerV3/Forms/CarDetailsForm.Designer.cs @@ -64,7 +64,7 @@ tableLayoutPanel1.AutoSize = true; tableLayoutPanel1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; tableLayoutPanel1.ColumnCount = 2; - tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 70F)); + tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 80F)); tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); tableLayoutPanel1.Controls.Add(pbxCarImage, 0, 0); tableLayoutPanel1.Controls.Add(label1, 0, 1); @@ -82,18 +82,18 @@ tableLayoutPanel1.Controls.Add(tableLayoutPanel2, 1, 3); tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); - tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(3, 3, 26, 3); + tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(3, 4, 30, 4); tableLayoutPanel1.Name = "tableLayoutPanel1"; tableLayoutPanel1.RowCount = 8; - tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 188F)); - tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28F)); - tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28F)); - tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28F)); - tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28F)); - tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28F)); - tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28F)); - tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 38F)); - tableLayoutPanel1.Size = new System.Drawing.Size(469, 422); + tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 251F)); + tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); + tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); + tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); + tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); + tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); + tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); + tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 51F)); + tableLayoutPanel1.Size = new System.Drawing.Size(536, 563); tableLayoutPanel1.TabIndex = 0; // // pbxCarImage @@ -101,9 +101,10 @@ tableLayoutPanel1.SetColumnSpan(pbxCarImage, 2); pbxCarImage.Dock = System.Windows.Forms.DockStyle.Fill; pbxCarImage.Image = (System.Drawing.Image)resources.GetObject("pbxCarImage.Image"); - pbxCarImage.Location = new System.Drawing.Point(3, 3); + pbxCarImage.Location = new System.Drawing.Point(3, 4); + pbxCarImage.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); pbxCarImage.Name = "pbxCarImage"; - pbxCarImage.Size = new System.Drawing.Size(463, 182); + pbxCarImage.Size = new System.Drawing.Size(530, 243); pbxCarImage.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom; pbxCarImage.TabIndex = 0; pbxCarImage.TabStop = false; @@ -112,9 +113,9 @@ // label1.AutoSize = true; label1.Dock = System.Windows.Forms.DockStyle.Fill; - label1.Location = new System.Drawing.Point(3, 188); + label1.Location = new System.Drawing.Point(3, 251); label1.Name = "label1"; - label1.Size = new System.Drawing.Size(64, 28); + label1.Size = new System.Drawing.Size(74, 37); label1.TabIndex = 1; label1.Text = "Make:"; label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; @@ -122,9 +123,10 @@ // tbxMake // tbxMake.Dock = System.Windows.Forms.DockStyle.Fill; - tbxMake.Location = new System.Drawing.Point(73, 191); + tbxMake.Location = new System.Drawing.Point(83, 255); + tbxMake.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); tbxMake.Name = "tbxMake"; - tbxMake.Size = new System.Drawing.Size(393, 23); + tbxMake.Size = new System.Drawing.Size(450, 27); tbxMake.TabIndex = 1; tbxMake.TextChanged += tbxMake_TextChanged; tbxMake.Leave += tbxMake_Leave; @@ -133,9 +135,9 @@ // label2.AutoSize = true; label2.Dock = System.Windows.Forms.DockStyle.Fill; - label2.Location = new System.Drawing.Point(3, 216); + label2.Location = new System.Drawing.Point(3, 288); label2.Name = "label2"; - label2.Size = new System.Drawing.Size(64, 28); + label2.Size = new System.Drawing.Size(74, 37); label2.TabIndex = 3; label2.Text = "Model:"; label2.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; @@ -143,9 +145,10 @@ // tbxModel // tbxModel.Dock = System.Windows.Forms.DockStyle.Fill; - tbxModel.Location = new System.Drawing.Point(73, 219); + tbxModel.Location = new System.Drawing.Point(83, 292); + tbxModel.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); tbxModel.Name = "tbxModel"; - tbxModel.Size = new System.Drawing.Size(393, 23); + tbxModel.Size = new System.Drawing.Size(450, 27); tbxModel.TabIndex = 2; tbxModel.TextChanged += tbxModel_TextChanged; tbxModel.Leave += tbxModel_Leave; @@ -154,9 +157,9 @@ // label3.AutoSize = true; label3.Dock = System.Windows.Forms.DockStyle.Fill; - label3.Location = new System.Drawing.Point(3, 244); + label3.Location = new System.Drawing.Point(3, 325); label3.Name = "label3"; - label3.Size = new System.Drawing.Size(64, 28); + label3.Size = new System.Drawing.Size(74, 37); label3.TabIndex = 5; label3.Text = "Year:"; label3.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; @@ -165,9 +168,9 @@ // label4.AutoSize = true; label4.Dock = System.Windows.Forms.DockStyle.Fill; - label4.Location = new System.Drawing.Point(3, 272); + label4.Location = new System.Drawing.Point(3, 362); label4.Name = "label4"; - label4.Size = new System.Drawing.Size(64, 28); + label4.Size = new System.Drawing.Size(74, 37); label4.TabIndex = 6; label4.Text = "Color:"; label4.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; @@ -176,9 +179,9 @@ // label5.AutoSize = true; label5.Dock = System.Windows.Forms.DockStyle.Fill; - label5.Location = new System.Drawing.Point(3, 300); + label5.Location = new System.Drawing.Point(3, 399); label5.Name = "label5"; - label5.Size = new System.Drawing.Size(64, 28); + label5.Size = new System.Drawing.Size(74, 37); label5.TabIndex = 7; label5.Text = "Mileage:"; label5.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; @@ -187,9 +190,9 @@ // label6.AutoSize = true; label6.Dock = System.Windows.Forms.DockStyle.Fill; - label6.Location = new System.Drawing.Point(3, 328); + label6.Location = new System.Drawing.Point(3, 436); label6.Name = "label6"; - label6.Size = new System.Drawing.Size(64, 28); + label6.Size = new System.Drawing.Size(74, 37); label6.TabIndex = 8; label6.Text = "Price:"; label6.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; @@ -202,17 +205,19 @@ flowLayoutPanel1.Controls.Add(lblID); flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; flowLayoutPanel1.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft; - flowLayoutPanel1.Location = new System.Drawing.Point(3, 359); + flowLayoutPanel1.Location = new System.Drawing.Point(3, 477); + flowLayoutPanel1.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); flowLayoutPanel1.Name = "flowLayoutPanel1"; - flowLayoutPanel1.Padding = new System.Windows.Forms.Padding(4); - flowLayoutPanel1.Size = new System.Drawing.Size(463, 60); + flowLayoutPanel1.Padding = new System.Windows.Forms.Padding(5); + flowLayoutPanel1.Size = new System.Drawing.Size(530, 82); flowLayoutPanel1.TabIndex = 9; // // btnSave // - btnSave.Location = new System.Drawing.Point(386, 7); + btnSave.Location = new System.Drawing.Point(442, 9); + btnSave.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); btnSave.Name = "btnSave"; - btnSave.Size = new System.Drawing.Size(66, 22); + btnSave.Size = new System.Drawing.Size(75, 29); btnSave.TabIndex = 7; btnSave.Text = "Save"; btnSave.UseVisualStyleBackColor = true; @@ -220,9 +225,10 @@ // // btnDelete // - btnDelete.Location = new System.Drawing.Point(314, 7); + btnDelete.Location = new System.Drawing.Point(361, 9); + btnDelete.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); btnDelete.Name = "btnDelete"; - btnDelete.Size = new System.Drawing.Size(66, 22); + btnDelete.Size = new System.Drawing.Size(75, 29); btnDelete.TabIndex = 8; btnDelete.Text = "Delete"; btnDelete.UseVisualStyleBackColor = true; @@ -231,28 +237,30 @@ // lblID // lblID.AutoSize = true; - lblID.Location = new System.Drawing.Point(290, 4); + lblID.Location = new System.Drawing.Point(331, 5); lblID.Name = "lblID"; - lblID.Size = new System.Drawing.Size(18, 15); + lblID.Size = new System.Drawing.Size(24, 20); lblID.TabIndex = 2; lblID.Text = "ID"; // // tbxColor // tbxColor.Dock = System.Windows.Forms.DockStyle.Fill; - tbxColor.Location = new System.Drawing.Point(73, 275); + tbxColor.Location = new System.Drawing.Point(83, 366); + tbxColor.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); tbxColor.Name = "tbxColor"; - tbxColor.Size = new System.Drawing.Size(393, 23); + tbxColor.Size = new System.Drawing.Size(450, 27); tbxColor.TabIndex = 4; tbxColor.TextChanged += tbxColor_TextChanged; tbxColor.Leave += tbxColor_Leave; // // nudMileage // - nudMileage.Location = new System.Drawing.Point(73, 303); + nudMileage.Location = new System.Drawing.Point(83, 403); + nudMileage.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); nudMileage.Maximum = new decimal(new int[] { 999999, 0, 0, 0 }); nudMileage.Name = "nudMileage"; - nudMileage.Size = new System.Drawing.Size(105, 23); + nudMileage.Size = new System.Drawing.Size(120, 27); nudMileage.TabIndex = 5; nudMileage.ThousandsSeparator = true; nudMileage.ValueChanged += nudMileage_ValueChanged; @@ -260,10 +268,11 @@ // nudPrice // nudPrice.DecimalPlaces = 2; - nudPrice.Location = new System.Drawing.Point(73, 331); + nudPrice.Location = new System.Drawing.Point(83, 440); + nudPrice.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); nudPrice.Maximum = new decimal(new int[] { 999999, 0, 0, 0 }); nudPrice.Name = "nudPrice"; - nudPrice.Size = new System.Drawing.Size(105, 23); + nudPrice.Size = new System.Drawing.Size(120, 27); nudPrice.TabIndex = 6; nudPrice.ThousandsSeparator = true; nudPrice.ValueChanged += nudPrice_ValueChanged; @@ -278,19 +287,21 @@ tableLayoutPanel2.Controls.Add(lblAge, 1, 0); tableLayoutPanel2.Controls.Add(tbxAge, 2, 0); tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill; - tableLayoutPanel2.Location = new System.Drawing.Point(73, 247); + tableLayoutPanel2.Location = new System.Drawing.Point(83, 329); + tableLayoutPanel2.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); tableLayoutPanel2.Name = "tableLayoutPanel2"; tableLayoutPanel2.RowCount = 1; tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); - tableLayoutPanel2.Size = new System.Drawing.Size(393, 22); + tableLayoutPanel2.Size = new System.Drawing.Size(450, 29); tableLayoutPanel2.TabIndex = 10; // // nudYear // - nudYear.Location = new System.Drawing.Point(3, 3); + nudYear.Location = new System.Drawing.Point(3, 4); + nudYear.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); nudYear.Maximum = new decimal(new int[] { 3000, 0, 0, 0 }); nudYear.Name = "nudYear"; - nudYear.Size = new System.Drawing.Size(105, 23); + nudYear.Size = new System.Drawing.Size(120, 27); nudYear.TabIndex = 3; nudYear.ValueChanged += nudYear_ValueChanged; // @@ -298,10 +309,10 @@ // lblAge.AutoSize = true; lblAge.Dock = System.Windows.Forms.DockStyle.Fill; - lblAge.Location = new System.Drawing.Point(134, 0); + lblAge.Location = new System.Drawing.Point(153, 0); lblAge.Name = "lblAge"; lblAge.RightToLeft = System.Windows.Forms.RightToLeft.No; - lblAge.Size = new System.Drawing.Size(125, 22); + lblAge.Size = new System.Drawing.Size(144, 29); lblAge.TabIndex = 4; lblAge.Text = "Age"; lblAge.TextAlign = System.Drawing.ContentAlignment.MiddleRight; @@ -310,19 +321,21 @@ // tbxAge.Dock = System.Windows.Forms.DockStyle.Fill; tbxAge.Enabled = false; - tbxAge.Location = new System.Drawing.Point(265, 3); + tbxAge.Location = new System.Drawing.Point(303, 4); + tbxAge.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); tbxAge.Name = "tbxAge"; tbxAge.ReadOnly = true; - tbxAge.Size = new System.Drawing.Size(125, 23); + tbxAge.Size = new System.Drawing.Size(144, 27); tbxAge.TabIndex = 5; // // CarDetailsForm // - AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + AutoScaleDimensions = new System.Drawing.SizeF(8F, 20F); AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - ClientSize = new System.Drawing.Size(469, 422); + ClientSize = new System.Drawing.Size(536, 563); Controls.Add(tableLayoutPanel1); FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); MaximizeBox = false; Name = "CarDetailsForm"; Text = "Details"; diff --git a/CarManagerV3/Forms/Components/AnimatedProgressBar.cs b/CarManagerV3/Forms/Components/AnimatedProgressBar.cs new file mode 100644 index 0000000..f1b1ce6 --- /dev/null +++ b/CarManagerV3/Forms/Components/AnimatedProgressBar.cs @@ -0,0 +1,180 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace CarManagerV3 +{ + public class AnimatedProgressBar : UserControl + { + private Timer animationTimer; + private float animationPosition = 0f; + private Color primaryColor = Color.FromArgb(0, 120, 215); + private Color secondaryColor = Color.FromArgb(100, 180, 255); + private Color backgroundColor = Color.FromArgb(240, 240, 240); + + public AnimatedProgressBar() + { + this.SetStyle( + ControlStyles.AllPaintingInWmPaint | + ControlStyles.UserPaint | + ControlStyles.OptimizedDoubleBuffer | + ControlStyles.ResizeRedraw, + true); + + this.Height = 8; + + // Initialize animation timer + animationTimer = new Timer(); + animationTimer.Interval = 20; // 50 FPS + animationTimer.Tick += AnimationTimer_Tick; + animationTimer.Start(); + } + + private void AnimationTimer_Tick(object sender, EventArgs e) + { + animationPosition += 2f; + if (animationPosition > this.Width + 100) + { + animationPosition = -100; + } + this.Invalidate(); + } + + protected override void OnPaint(PaintEventArgs e) + { + base.OnPaint(e); + + Graphics g = e.Graphics; + g.SmoothingMode = SmoothingMode.AntiAlias; + g.PixelOffsetMode = PixelOffsetMode.HighQuality; + + // Draw background with rounded corners + using (GraphicsPath path = GetRoundedRect(ClientRectangle, 4)) + { + using (SolidBrush bgBrush = new SolidBrush(backgroundColor)) + { + g.FillPath(bgBrush, path); + } + } + + // Create animated gradient bar + float barWidth = 100f; + RectangleF barRect = new RectangleF(animationPosition, 0, barWidth, this.Height); + + if (barRect.Right > 0 && barRect.Left < this.Width) + { + using (GraphicsPath barPath = GetRoundedRect(barRect, 4)) + { + // Create gradient brush + using (LinearGradientBrush gradientBrush = new LinearGradientBrush( + barRect, + Color.Transparent, + Color.Transparent, + LinearGradientMode.Horizontal)) + { + ColorBlend colorBlend = new ColorBlend(); + colorBlend.Colors = new Color[] { + Color.FromArgb(0, primaryColor), + primaryColor, + secondaryColor, + primaryColor, + Color.FromArgb(0, primaryColor) + }; + colorBlend.Positions = new float[] { 0f, 0.2f, 0.5f, 0.8f, 1f }; + gradientBrush.InterpolationColors = colorBlend; + + // Clip to control bounds + Region oldClip = g.Clip; + using (GraphicsPath clipPath = GetRoundedRect(ClientRectangle, 4)) + { + g.SetClip(clipPath); + g.FillPath(gradientBrush, barPath); + g.Clip = oldClip; + } + } + } + } + + // Draw subtle border + using (GraphicsPath borderPath = GetRoundedRect(ClientRectangle, 4)) + { + using (Pen borderPen = new Pen(Color.FromArgb(220, 220, 220), 1)) + { + g.DrawPath(borderPen, borderPath); + } + } + } + + private GraphicsPath GetRoundedRect(RectangleF rect, float radius) + { + GraphicsPath path = new GraphicsPath(); + float diameter = radius * 2; + + path.AddArc(rect.X, rect.Y, diameter, diameter, 180, 90); + path.AddArc(rect.Right - diameter, rect.Y, diameter, diameter, 270, 90); + path.AddArc(rect.Right - diameter, rect.Bottom - diameter, diameter, diameter, 0, 90); + path.AddArc(rect.X, rect.Bottom - diameter, diameter, diameter, 90, 90); + path.CloseFigure(); + + return path; + } + + public void StartAnimation() + { + animationTimer.Start(); + } + + public void StopAnimation() + { + animationTimer.Stop(); + } + + //protected override void Dispose(bool disposing) + //{ + // if (disposing) + // { + // animationTimer?.Stop(); + // animationTimer?.Dispose(); + // } + // base.Dispose(disposing); + //} + + // Properties for customization + public Color PrimaryColor + { + get => primaryColor; + set + { + primaryColor = value; + this.Invalidate(); + } + } + + public Color SecondaryColor + { + get => secondaryColor; + set + { + secondaryColor = value; + this.Invalidate(); + } + } + + public Color ProgressBackColor + { + get => backgroundColor; + set + { + backgroundColor = value; + this.Invalidate(); + } + } + } +} diff --git a/CarManagerV3/Forms/Components/AnimatedProgressBar.resx b/CarManagerV3/Forms/Components/AnimatedProgressBar.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/CarManagerV3/Forms/Components/AnimatedProgressBar.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/CarManagerV3/Forms/MainForm.Designer.cs b/CarManagerV3/Forms/MainForm.Designer.cs index ba77bb9..b02d733 100644 --- a/CarManagerV3/Forms/MainForm.Designer.cs +++ b/CarManagerV3/Forms/MainForm.Designer.cs @@ -49,11 +49,14 @@ addCarToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); importToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); clearSearchToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + settingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); toolsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); openWelcomeScreenToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); clearRecentFilesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); imageList1 = new System.Windows.Forms.ImageList(components); - settingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + aboutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + checkForUpdatesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + gitRepositoryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); tableLayoutPanel1.SuspendLayout(); tlpControls.SuspendLayout(); tlpSearch.SuspendLayout(); @@ -171,7 +174,7 @@ // menuStrip1.BackColor = System.Drawing.SystemColors.ButtonFace; menuStrip1.ImageScalingSize = new System.Drawing.Size(20, 20); - menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { fileToolStripMenuItem, editToolStripMenuItem, toolsToolStripMenuItem }); + menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { fileToolStripMenuItem, editToolStripMenuItem, toolsToolStripMenuItem, aboutToolStripMenuItem }); menuStrip1.Location = new System.Drawing.Point(0, 0); menuStrip1.Name = "menuStrip1"; menuStrip1.Size = new System.Drawing.Size(902, 28); @@ -240,24 +243,31 @@ // addCarToolStripMenuItem // addCarToolStripMenuItem.Name = "addCarToolStripMenuItem"; - addCarToolStripMenuItem.Size = new System.Drawing.Size(224, 26); + addCarToolStripMenuItem.Size = new System.Drawing.Size(174, 26); addCarToolStripMenuItem.Text = "Add Car"; addCarToolStripMenuItem.Click += addCarToolStripMenuItem_Click; // // importToolStripMenuItem // importToolStripMenuItem.Name = "importToolStripMenuItem"; - importToolStripMenuItem.Size = new System.Drawing.Size(224, 26); + importToolStripMenuItem.Size = new System.Drawing.Size(174, 26); importToolStripMenuItem.Text = "Import"; importToolStripMenuItem.Click += importToolStripMenuItem_Click; // // clearSearchToolStripMenuItem // clearSearchToolStripMenuItem.Name = "clearSearchToolStripMenuItem"; - clearSearchToolStripMenuItem.Size = new System.Drawing.Size(224, 26); + clearSearchToolStripMenuItem.Size = new System.Drawing.Size(174, 26); clearSearchToolStripMenuItem.Text = "Clear Search"; clearSearchToolStripMenuItem.Click += clearSearchToolStripMenuItem_Click; // + // settingsToolStripMenuItem + // + settingsToolStripMenuItem.Name = "settingsToolStripMenuItem"; + settingsToolStripMenuItem.Size = new System.Drawing.Size(174, 26); + settingsToolStripMenuItem.Text = "Settings"; + settingsToolStripMenuItem.Click += settingsToolStripMenuItem_Click; + // // toolsToolStripMenuItem // toolsToolStripMenuItem.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text; @@ -290,12 +300,26 @@ imageList1.Images.SetKeyName(0, "Icon_Search.png"); imageList1.Images.SetKeyName(1, "Icon_Add.png"); // - // settingsToolStripMenuItem + // aboutToolStripMenuItem // - settingsToolStripMenuItem.Name = "settingsToolStripMenuItem"; - settingsToolStripMenuItem.Size = new System.Drawing.Size(224, 26); - settingsToolStripMenuItem.Text = "Settings"; - settingsToolStripMenuItem.Click += settingsToolStripMenuItem_Click; + aboutToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { checkForUpdatesToolStripMenuItem, gitRepositoryToolStripMenuItem }); + aboutToolStripMenuItem.Name = "aboutToolStripMenuItem"; + aboutToolStripMenuItem.Size = new System.Drawing.Size(64, 24); + aboutToolStripMenuItem.Text = "About"; + // + // checkForUpdatesToolStripMenuItem + // + checkForUpdatesToolStripMenuItem.Name = "checkForUpdatesToolStripMenuItem"; + checkForUpdatesToolStripMenuItem.Size = new System.Drawing.Size(224, 26); + checkForUpdatesToolStripMenuItem.Text = "Check for Updates"; + checkForUpdatesToolStripMenuItem.Click += checkForUpdatesToolStripMenuItem_Click; + // + // gitRepositoryToolStripMenuItem + // + gitRepositoryToolStripMenuItem.Name = "gitRepositoryToolStripMenuItem"; + gitRepositoryToolStripMenuItem.Size = new System.Drawing.Size(224, 26); + gitRepositoryToolStripMenuItem.Text = "Git Repository"; + gitRepositoryToolStripMenuItem.Click += gitRepositoryToolStripMenuItem_Click; // // MainForm // @@ -348,5 +372,8 @@ private System.Windows.Forms.ToolStripMenuItem exitToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem clearRecentFilesToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem settingsToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem aboutToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem checkForUpdatesToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem gitRepositoryToolStripMenuItem; } } \ No newline at end of file diff --git a/CarManagerV3/Forms/MainForm.cs b/CarManagerV3/Forms/MainForm.cs index 466be3c..45f23d5 100644 --- a/CarManagerV3/Forms/MainForm.cs +++ b/CarManagerV3/Forms/MainForm.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Threading.Tasks; using System.Windows.Forms; using CarManagerV3.Forms; +using CarManagerV3.Manager; namespace CarManagerV3 { @@ -60,6 +61,19 @@ namespace CarManagerV3 } refreshRecents(); + try + { + if (Updater.IsUpdateAvailable(Properties.Settings.Default.AllowPrerelease)) + { + UpdatePromptForm updatePrompt = new UpdatePromptForm(Updater.GetCurrentVersion(), Updater.GetLatestVersion()); + updatePrompt.ShowDialog(); + } + } + catch (Exception ex) + { + Console.Error.WriteLine("Error checking for updates: " + ex.Message); + } + } @@ -142,7 +156,7 @@ namespace CarManagerV3 Console.WriteLine($"[L] Updating car: {car.Id}"); // changes card = existing; - if(force) card.LoadImage(); // reload image if forced refresh + if (force) card.LoadImage(); // reload image if forced refresh } else { @@ -550,5 +564,35 @@ namespace CarManagerV3 settingsForm.ShowDialog(); } + + private void checkForUpdatesToolStripMenuItem_Click(object sender, EventArgs e) + { + try + { + if (Updater.IsUpdateAvailable(Properties.Settings.Default.AllowPrerelease)) + { + UpdatePromptForm updatePrompt = new UpdatePromptForm(Updater.GetCurrentVersion(), Updater.GetLatestVersion()); + updatePrompt.ShowDialog(); + } + else + { + MessageBox.Show($"You are already using the latest version. ({Updater.GetCurrentVersion()})", "No Updates Available", MessageBoxButtons.OK, MessageBoxIcon.Information); + } + } + catch (Exception ex) + { + MessageBox.Show("Error checking for updates: " + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + private void gitRepositoryToolStripMenuItem_Click(object sender, EventArgs e) + { + // Open the Git repository in the user's default browser + System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo + { + FileName = "https://git.jaro.digital/frozd/carmanager-3", + UseShellExecute = true + }); + } } } diff --git a/CarManagerV3/Forms/MainForm.resx b/CarManagerV3/Forms/MainForm.resx index 2e63906..ca05122 100644 --- a/CarManagerV3/Forms/MainForm.resx +++ b/CarManagerV3/Forms/MainForm.resx @@ -117,6 +117,9 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 17, 17 + @@ -139,9 +142,6 @@ VXs21p8MAVc2KdQAvwAAAABJRU5ErkJggg== - - 17, 17 - 153, 17 @@ -150,15 +150,15 @@ AAEAAAD/////AQAAAAAAAAAMAgAAAEZTeXN0ZW0uV2luZG93cy5Gb3JtcywgQ3VsdHVyZT1uZXV0cmFs LCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5BQEAAAAmU3lzdGVtLldpbmRvd3MuRm9ybXMu SW1hZ2VMaXN0U3RyZWFtZXIBAAAABERhdGEHAgIAAAAJAwAAAA8DAAAAYAQAAAJNU0Z0AUkBTAIBAQIB - AAGIAQABiAEAARQBAAEUAQAE/wEhAQAI/wFCAU0BNgcAATYDAAEoAwABUAMAARQDAAEBAQABIAYAARn/ + AAGYAQABmAEAARQBAAEUAQAE/wEhAQAI/wFCAU0BNgcAATYDAAEoAwABUAMAARQDAAEBAQABIAYAARn/ AP8AzAADCAEKAyMBMgMuAUYDFgEe/wAtAAMIAQoDIwEyAk8BTgGXAlYBVAGrAy4BRv8ALQADIwEyAk8B TgGXAf8BmQEzAf8DTgGWAyMBMiwAA10BzAT/9AADKAE8Ak8BTgGXAl8BWwHTA04BlgMjATIDCAEKLAAD XQHMBP/UAAMDAQQDDwETA0QBewJPAU4BlwNNAZIDQAFuAwYBBwMpAT0DTgGYAlsBWQHAAk8BTgGXAyMB MgMIAQowAANdAcwE/9AAAxMBGgMxAU0DRAF5AlwBWQHBAmEBXQHPAl0BWwHKAlcBVQG0Az8BbQNSAaMC WwFZAcADTgGYAygBPDgAA10BzAT/zAADAwEEAzEBTQJZAVcBvAJgAV0BzgNLAYwDQAFvA0ABbwNLAYwC - YAFdAc4BggFxAVMB9AJTAVEBogMpAT08AANdAcwE/8wAAw8BEwNEAXkCYAFdAc4DBgEIBAIIAAQCAwYB + YAFdAc4BfAFtAVMB9AJTAVEBogMpAT08AANdAcwE/8wAAw8BEwNEAXkCYAFdAc4DBgEIBAIIAAQCAwYB CAJgAV0BzgM+AWsDBAEFKAADXQHMLP+4AANEAXkDWgG/A0sBjAQCEAAEAgNLAYwCWgFYAbcDQQFxKAAD - UgGjA10BzANdAcwDXQHMA10BzAOJAfUE/wNdAcwDXQHMA10BzANdAcwDXQHMuAACTwFOAZcCYAFdAc4D + UgGjA10BzANdAcwDXQHMA10BzAOFAfUE/wNdAcwDXQHMA10BzANdAcwDXQHMuAACTwFOAZcCYAFdAc4D QAFvGAADQAFvAl8BXAHLA04BlDwAA10BzAT/zAACTwFOAZcCYAFdAc4DQAFvGAADQAFvAl4BWwHNA04B ljwAA10BzAT/zAADRAF6AlsBWQHAA0sBjAQCEAAEAgNLAYwCWgFYAb0CRAFDAXc8AANdAcwE/8wAAw4B EgNEAXgCYAFdAc4DBgEIBAIIAAQCAwYBCAJgAV0BzgJDAUIBdQMMAQ88AANdAcwE/8wAAwMBBAMxAU0C diff --git a/CarManagerV3/Forms/SettingsForm.Designer.cs b/CarManagerV3/Forms/SettingsForm.Designer.cs index dcae6ca..2e76ba3 100644 --- a/CarManagerV3/Forms/SettingsForm.Designer.cs +++ b/CarManagerV3/Forms/SettingsForm.Designer.cs @@ -40,6 +40,7 @@ tbxDataLocation = new System.Windows.Forms.TextBox(); tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); btnReset = new System.Windows.Forms.Button(); + cbxPreRelease = new System.Windows.Forms.CheckBox(); flowLayoutPanel1.SuspendLayout(); flowLayoutPanel2.SuspendLayout(); tableLayoutPanel1.SuspendLayout(); @@ -119,13 +120,15 @@ tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); tableLayoutPanel1.Controls.Add(lblDataLocation, 0, 0); tableLayoutPanel1.Controls.Add(tbxDataLocation, 1, 0); + tableLayoutPanel1.Controls.Add(cbxPreRelease, 1, 1); tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; tableLayoutPanel1.Location = new System.Drawing.Point(0, 86); tableLayoutPanel1.Name = "tableLayoutPanel1"; tableLayoutPanel1.Padding = new System.Windows.Forms.Padding(10, 0, 10, 0); - tableLayoutPanel1.RowCount = 2; + tableLayoutPanel1.RowCount = 3; + tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); + tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); - tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); tableLayoutPanel1.Size = new System.Drawing.Size(499, 531); tableLayoutPanel1.TabIndex = 3; // @@ -175,6 +178,18 @@ btnReset.UseVisualStyleBackColor = true; btnReset.Click += btnReset_Click; // + // cbxPreRelease + // + cbxPreRelease.AutoSize = true; + tableLayoutPanel1.SetColumnSpan(cbxPreRelease, 2); + cbxPreRelease.Location = new System.Drawing.Point(13, 36); + cbxPreRelease.Name = "cbxPreRelease"; + cbxPreRelease.RightToLeft = System.Windows.Forms.RightToLeft.Yes; + cbxPreRelease.Size = new System.Drawing.Size(164, 24); + cbxPreRelease.TabIndex = 3; + cbxPreRelease.Text = "Pre-Release channel"; + cbxPreRelease.UseVisualStyleBackColor = true; + // // SettingsForm // AutoScaleDimensions = new System.Drawing.SizeF(8F, 20F); @@ -214,5 +229,6 @@ private System.Windows.Forms.TextBox tbxDataLocation; private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; private System.Windows.Forms.Button btnReset; + private System.Windows.Forms.CheckBox cbxPreRelease; } } \ No newline at end of file diff --git a/CarManagerV3/Forms/SettingsForm.cs b/CarManagerV3/Forms/SettingsForm.cs index 6ada62f..dc5bdcc 100644 --- a/CarManagerV3/Forms/SettingsForm.cs +++ b/CarManagerV3/Forms/SettingsForm.cs @@ -13,7 +13,7 @@ namespace CarManagerV3.Forms public partial class SettingsForm : Form { // Settings map (Maps settings to controls and default values + optional change event handler function that takes previous and new value) - private Dictionary onChange)> settingsMap = new Dictionary onChange)>(); + private readonly Dictionary settingsMap = new(); public SettingsForm() { @@ -24,11 +24,23 @@ namespace CarManagerV3.Forms private void initializeSettingsMap() { // Initialize the settings map with setting keys, associated controls, default values, and optional change event handlers - settingsMap["DataLocation"] = (tbxDataLocation, Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\CarManagerV3", (string before, string after) => - { - // TODO - } - ); + settingsMap["DataLocation"] = + new SettingBinding( + control: tbxDataLocation, + defaultValue: Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\CarManagerV3", + read: () => Properties.Settings.Default.DataLocation, // strongly typed + write: v => Properties.Settings.Default.DataLocation = v, // strongly typed + onChange: (before, after) => + { + // TODO: handle path change if needed + }); + + settingsMap["AllowPrerelease"] = + new SettingBinding( + control: cbxPreRelease, + defaultValue: false, + read: () => Properties.Settings.Default.AllowPrerelease, + write: v => Properties.Settings.Default.AllowPrerelease = v); } @@ -39,51 +51,32 @@ namespace CarManagerV3.Forms private void loadSettings() { - // with settings map - foreach (var setting in settingsMap) - { - string key = setting.Key; - Control control = setting.Value.control; - string value = Properties.Settings.Default[key]?.ToString() ?? string.Empty; - control.Text = value; - } + foreach (var kvp in settingsMap) + kvp.Value.Load(); } private void saveSettings() { - // Save settings using the settings map - foreach (var setting in settingsMap) - { - string key = setting.Key; - Control control = setting.Value.control; - string value = control.Text; - string oldValue = Properties.Settings.Default[key]?.ToString() ?? string.Empty; - // Save the value to application settings - Properties.Settings.Default[key] = value; - // Invoke the change event handler if it exists and the value has changed - if (setting.Value.onChange != null && oldValue != value) - { - setting.Value.onChange(oldValue, value); - } - } + foreach (var kvp in settingsMap) + kvp.Value.Save(); + Properties.Settings.Default.Save(); } private void resetSettings() { - DialogResult confirmReset = MessageBox.Show("Are you sure you want to reset all settings to their default values? This action cannot be undone.", "Confirm Reset", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); - if(confirmReset != DialogResult.Yes) - { + DialogResult confirmReset = MessageBox.Show( + "Are you sure you want to reset all settings to their default values? This action cannot be undone.", + "Confirm Reset", + MessageBoxButtons.YesNo, + MessageBoxIcon.Warning); + + if (confirmReset != DialogResult.Yes) return; - } - // Reset settings to default values using the settings map - foreach (var setting in settingsMap) - { - string key = setting.Key; - string defaultValue = setting.Value.defaultValue; - // Reset the value to default in application settings - Properties.Settings.Default[key] = defaultValue; - } + + foreach (var kvp in settingsMap) + kvp.Value.Reset(); + Properties.Settings.Default.Save(); loadSettings(); } @@ -104,4 +97,77 @@ namespace CarManagerV3.Forms resetSettings(); } } + + internal interface ISettingBinding + { + void Load(); + void Save(); + void Reset(); + } + + internal sealed class SettingBinding : ISettingBinding + { + private readonly Control control; + private readonly T defaultValue; + private readonly Func read; + private readonly Action write; + private readonly Action? onChange; + + public SettingBinding(Control control, T defaultValue, Func read, Action write, Action? onChange = null) + { + this.control = control; + this.defaultValue = defaultValue; + this.read = read; + this.write = write; + this.onChange = onChange; + } + + public void Load() + { + T value = read(); + ApplyToControl(value); + } + + public void Save() + { + T before = read(); + T after = ReadFromControl(); + + write(after); + + if (onChange != null && !EqualityComparer.Default.Equals(before, after)) + onChange(before, after); + } + + public void Reset() => write(defaultValue); + + private void ApplyToControl(T value) + { + switch (control) + { + case TextBox tb: + tb.Text = value?.ToString() ?? string.Empty; + break; + + case CheckBox cb: + cb.Checked = value is bool b ? b : Convert.ToBoolean(value); + break; + + default: + throw new NotSupportedException($"Control type '{control.GetType().Name}' not supported for {typeof(T).Name}."); + } + } + + private T ReadFromControl() + { + object result = control switch + { + TextBox tb when typeof(T) == typeof(string) => tb.Text, + CheckBox cb when typeof(T) == typeof(bool) => cb.Checked, + _ => throw new NotSupportedException($"Cannot read {typeof(T).Name} from control type '{control.GetType().Name}'.") + }; + + return (T)result; + } + } } diff --git a/CarManagerV3/Forms/UpdatePromptForm.Designer.cs b/CarManagerV3/Forms/UpdatePromptForm.Designer.cs new file mode 100644 index 0000000..9a52e17 --- /dev/null +++ b/CarManagerV3/Forms/UpdatePromptForm.Designer.cs @@ -0,0 +1,191 @@ +namespace CarManagerV3.Forms +{ + partial class UpdatePromptForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); + lblUpdateTitleStatic = new System.Windows.Forms.Label(); + label2 = new System.Windows.Forms.Label(); + lblInstalledVersion = new System.Windows.Forms.Label(); + lblLatestVersion = new System.Windows.Forms.Label(); + tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + flowLayoutPanel2 = new System.Windows.Forms.FlowLayoutPanel(); + btnInstallUpdate = new System.Windows.Forms.Button(); + btnDismissUpdate = new System.Windows.Forms.Button(); + btnReadChangelog = new System.Windows.Forms.Button(); + flowLayoutPanel1.SuspendLayout(); + tableLayoutPanel1.SuspendLayout(); + flowLayoutPanel2.SuspendLayout(); + SuspendLayout(); + // + // flowLayoutPanel1 + // + flowLayoutPanel1.Controls.Add(lblUpdateTitleStatic); + flowLayoutPanel1.Controls.Add(label2); + flowLayoutPanel1.Controls.Add(lblInstalledVersion); + flowLayoutPanel1.Controls.Add(lblLatestVersion); + flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Top; + flowLayoutPanel1.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; + flowLayoutPanel1.Location = new System.Drawing.Point(10, 20); + flowLayoutPanel1.Name = "flowLayoutPanel1"; + flowLayoutPanel1.Size = new System.Drawing.Size(846, 125); + flowLayoutPanel1.TabIndex = 0; + // + // lblUpdateTitleStatic + // + lblUpdateTitleStatic.AutoSize = true; + lblUpdateTitleStatic.Font = new System.Drawing.Font("Segoe UI", 14F); + lblUpdateTitleStatic.Location = new System.Drawing.Point(3, 0); + lblUpdateTitleStatic.Name = "lblUpdateTitleStatic"; + lblUpdateTitleStatic.Size = new System.Drawing.Size(298, 32); + lblUpdateTitleStatic.TabIndex = 4; + lblUpdateTitleStatic.Text = "A new Version is Available!"; + // + // label2 + // + label2.AutoSize = true; + label2.Location = new System.Drawing.Point(3, 32); + label2.Name = "label2"; + label2.Size = new System.Drawing.Size(600, 20); + label2.TabIndex = 5; + label2.Text = "A new version of Car Manager 3 has been released. Update now to get the latest features."; + // + // lblInstalledVersion + // + lblInstalledVersion.AutoSize = true; + lblInstalledVersion.Location = new System.Drawing.Point(3, 57); + lblInstalledVersion.Margin = new System.Windows.Forms.Padding(3, 5, 3, 0); + lblInstalledVersion.Name = "lblInstalledVersion"; + lblInstalledVersion.Size = new System.Drawing.Size(123, 20); + lblInstalledVersion.TabIndex = 6; + lblInstalledVersion.Text = "Your version: ?.?.?"; + // + // lblLatestVersion + // + lblLatestVersion.AutoSize = true; + lblLatestVersion.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold); + lblLatestVersion.Location = new System.Drawing.Point(3, 77); + lblLatestVersion.Name = "lblLatestVersion"; + lblLatestVersion.Size = new System.Drawing.Size(144, 20); + lblLatestVersion.TabIndex = 7; + lblLatestVersion.Text = "Latest version: ?.?.?"; + // + // tableLayoutPanel1 + // + tableLayoutPanel1.ColumnCount = 2; + tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + tableLayoutPanel1.Controls.Add(flowLayoutPanel2, 1, 0); + tableLayoutPanel1.Controls.Add(btnReadChangelog, 0, 0); + tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Bottom; + tableLayoutPanel1.Location = new System.Drawing.Point(10, 393); + tableLayoutPanel1.Name = "tableLayoutPanel1"; + tableLayoutPanel1.RowCount = 1; + tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); + tableLayoutPanel1.Size = new System.Drawing.Size(846, 47); + tableLayoutPanel1.TabIndex = 1; + // + // flowLayoutPanel2 + // + flowLayoutPanel2.Controls.Add(btnInstallUpdate); + flowLayoutPanel2.Controls.Add(btnDismissUpdate); + flowLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill; + flowLayoutPanel2.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft; + flowLayoutPanel2.Location = new System.Drawing.Point(423, 0); + flowLayoutPanel2.Margin = new System.Windows.Forms.Padding(0); + flowLayoutPanel2.Name = "flowLayoutPanel2"; + flowLayoutPanel2.Size = new System.Drawing.Size(423, 47); + flowLayoutPanel2.TabIndex = 0; + // + // btnInstallUpdate + // + btnInstallUpdate.AutoSize = true; + btnInstallUpdate.Location = new System.Drawing.Point(320, 3); + btnInstallUpdate.Name = "btnInstallUpdate"; + btnInstallUpdate.Size = new System.Drawing.Size(100, 30); + btnInstallUpdate.TabIndex = 0; + btnInstallUpdate.Text = "Update now"; + btnInstallUpdate.UseVisualStyleBackColor = true; + btnInstallUpdate.Click += btnInstallUpdate_Click; + // + // btnDismissUpdate + // + btnDismissUpdate.Location = new System.Drawing.Point(220, 3); + btnDismissUpdate.Name = "btnDismissUpdate"; + btnDismissUpdate.Size = new System.Drawing.Size(94, 29); + btnDismissUpdate.TabIndex = 1; + btnDismissUpdate.Text = "Dismiss"; + btnDismissUpdate.UseVisualStyleBackColor = true; + btnDismissUpdate.Click += btnDismissUpdate_Click; + // + // btnReadChangelog + // + btnReadChangelog.Location = new System.Drawing.Point(3, 3); + btnReadChangelog.Name = "btnReadChangelog"; + btnReadChangelog.Size = new System.Drawing.Size(94, 29); + btnReadChangelog.TabIndex = 1; + btnReadChangelog.Text = "Changelog"; + btnReadChangelog.UseVisualStyleBackColor = true; + btnReadChangelog.Click += btnReadChangelog_Click; + // + // UpdatePromptForm + // + AutoScaleDimensions = new System.Drawing.SizeF(8F, 20F); + AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + ClientSize = new System.Drawing.Size(866, 450); + Controls.Add(tableLayoutPanel1); + Controls.Add(flowLayoutPanel1); + FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + MaximizeBox = false; + MinimizeBox = false; + Name = "UpdatePromptForm"; + Padding = new System.Windows.Forms.Padding(10, 20, 10, 10); + Text = "Update Car Manager 3"; + TopMost = true; + flowLayoutPanel1.ResumeLayout(false); + flowLayoutPanel1.PerformLayout(); + tableLayoutPanel1.ResumeLayout(false); + flowLayoutPanel2.ResumeLayout(false); + flowLayoutPanel2.PerformLayout(); + ResumeLayout(false); + } + + #endregion + + private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; + private System.Windows.Forms.Label lblUpdateTitleStatic; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label lblInstalledVersion; + private System.Windows.Forms.Label lblLatestVersion; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel2; + private System.Windows.Forms.Button btnInstallUpdate; + private System.Windows.Forms.Button btnDismissUpdate; + private System.Windows.Forms.Button btnReadChangelog; + } +} \ No newline at end of file diff --git a/CarManagerV3/Forms/UpdatePromptForm.cs b/CarManagerV3/Forms/UpdatePromptForm.cs new file mode 100644 index 0000000..cf95a73 --- /dev/null +++ b/CarManagerV3/Forms/UpdatePromptForm.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using CarManagerV3.Manager; + +namespace CarManagerV3.Forms +{ + public partial class UpdatePromptForm : Form + { + public UpdatePromptForm(string currentVersion, string latestVersion) + { + InitializeComponent(); + lblInstalledVersion.Text = lblInstalledVersion.Text.Replace("?.?.?", currentVersion); + lblLatestVersion.Text = lblLatestVersion.Text.Replace("?.?.?", latestVersion); + + } + + private void btnDismissUpdate_Click(object sender, EventArgs e) + { + this.Close(); + } + + private void btnInstallUpdate_Click(object sender, EventArgs e) + { + /* + var msgbox = new PleaseWait(); + msgbox.Show(); + Application.DoEvents(); + StateManager.UpdateCar(car); + Image fooimg = ImageManager.GetImage(car); + msgbox.Close(); + this.Close(); + */ + PleaseWait loadForm = new PleaseWait("Downloading the newest version..."); + try + { + this.Enabled = false; + loadForm.Show(); + Application.DoEvents(); + //return; + Updater.DownloadNewestInstaller(); + } + catch (Exception ex) + { + MessageBox.Show("An error occurred while trying to download the update: " + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + this.Enabled = true; + loadForm.Close(); + this.Close(); + + } + + private void btnReadChangelog_Click(object sender, EventArgs e) + { + Updater.openReleasePage(); + } + } +} diff --git a/CarManagerV3/Forms/UpdatePromptForm.resx b/CarManagerV3/Forms/UpdatePromptForm.resx new file mode 100644 index 0000000..8b2ff64 --- /dev/null +++ b/CarManagerV3/Forms/UpdatePromptForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/CarManagerV3/Forms/Util/PleaseWait.Designer.cs b/CarManagerV3/Forms/Util/PleaseWait.Designer.cs index 21a4f7e..4b5d3ad 100644 --- a/CarManagerV3/Forms/Util/PleaseWait.Designer.cs +++ b/CarManagerV3/Forms/Util/PleaseWait.Designer.cs @@ -28,65 +28,90 @@ /// private void InitializeComponent() { - this.progressBar1 = new System.Windows.Forms.ProgressBar(); - this.label1 = new System.Windows.Forms.Label(); - this.label2 = new System.Windows.Forms.Label(); - this.SuspendLayout(); - // - // progressBar1 - // - this.progressBar1.Location = new System.Drawing.Point(12, 62); - this.progressBar1.Name = "progressBar1"; - this.progressBar1.Size = new System.Drawing.Size(422, 23); - this.progressBar1.Style = System.Windows.Forms.ProgressBarStyle.Marquee; - this.progressBar1.TabIndex = 0; - this.progressBar1.Click += new System.EventHandler(this.progressBar1_Click); + label1 = new System.Windows.Forms.Label(); + lblContent = new System.Windows.Forms.Label(); + animatedProgressBar1 = new AnimatedProgressBar(); + flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); + flowLayoutPanel1.SuspendLayout(); + SuspendLayout(); // // label1 // - this.label1.AutoSize = true; - this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 10.2F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label1.Location = new System.Drawing.Point(12, 13); - this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(121, 20); - this.label1.TabIndex = 1; - this.label1.Text = "Please wait..."; + label1.AutoSize = true; + label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, 0); + label1.Location = new System.Drawing.Point(13, 10); + label1.Name = "label1"; + label1.Size = new System.Drawing.Size(140, 25); + label1.TabIndex = 1; + label1.Text = "Please wait..."; + label1.UseWaitCursor = true; // - // label2 + // lblContent // - this.label2.AutoSize = true; - this.label2.Location = new System.Drawing.Point(13, 33); - this.label2.Name = "label2"; - this.label2.Size = new System.Drawing.Size(133, 16); - this.label2.TabIndex = 2; - this.label2.Text = "Saving your changes"; + lblContent.AutoSize = true; + lblContent.Location = new System.Drawing.Point(13, 40); + lblContent.Margin = new System.Windows.Forms.Padding(3, 5, 3, 10); + lblContent.Name = "lblContent"; + lblContent.Size = new System.Drawing.Size(144, 20); + lblContent.TabIndex = 2; + lblContent.Text = "Saving your changes"; + lblContent.UseWaitCursor = true; + // + // animatedProgressBar1 + // + animatedProgressBar1.Location = new System.Drawing.Point(13, 73); + animatedProgressBar1.Name = "animatedProgressBar1"; + animatedProgressBar1.PrimaryColor = System.Drawing.Color.FromArgb(0, 120, 215); + animatedProgressBar1.ProgressBackColor = System.Drawing.Color.FromArgb(240, 240, 240); + animatedProgressBar1.SecondaryColor = System.Drawing.Color.FromArgb(100, 180, 255); + animatedProgressBar1.Size = new System.Drawing.Size(408, 22); + animatedProgressBar1.TabIndex = 3; + animatedProgressBar1.UseWaitCursor = true; + // + // flowLayoutPanel1 + // + flowLayoutPanel1.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom; + flowLayoutPanel1.Controls.Add(label1); + flowLayoutPanel1.Controls.Add(lblContent); + flowLayoutPanel1.Controls.Add(animatedProgressBar1); + flowLayoutPanel1.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; + flowLayoutPanel1.Location = new System.Drawing.Point(0, 0); + flowLayoutPanel1.Name = "flowLayoutPanel1"; + flowLayoutPanel1.Padding = new System.Windows.Forms.Padding(10); + flowLayoutPanel1.Size = new System.Drawing.Size(437, 153); + flowLayoutPanel1.TabIndex = 4; + flowLayoutPanel1.UseWaitCursor = true; + flowLayoutPanel1.WrapContents = false; // // PleaseWait // - this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.AutoSize = true; - this.ClientSize = new System.Drawing.Size(446, 97); - this.ControlBox = false; - this.Controls.Add(this.label2); - this.Controls.Add(this.label1); - this.Controls.Add(this.progressBar1); - this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; - this.MaximizeBox = false; - this.MinimizeBox = false; - this.Name = "PleaseWait"; - this.ShowIcon = false; - this.ShowInTaskbar = false; - this.Text = "Please Wait"; - this.ResumeLayout(false); - this.PerformLayout(); + AutoScaleDimensions = new System.Drawing.SizeF(8F, 20F); + AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + AutoSize = true; + ClientSize = new System.Drawing.Size(437, 153); + ControlBox = false; + Controls.Add(flowLayoutPanel1); + FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + MaximizeBox = false; + MinimizeBox = false; + Name = "PleaseWait"; + ShowIcon = false; + ShowInTaskbar = false; + StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + Text = "Please Wait"; + TopMost = true; + UseWaitCursor = true; + flowLayoutPanel1.ResumeLayout(false); + flowLayoutPanel1.PerformLayout(); + ResumeLayout(false); } #endregion - - private System.Windows.Forms.ProgressBar progressBar1; private System.Windows.Forms.Label label1; - private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label lblContent; + private AnimatedProgressBar animatedProgressBar1; + private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; } } \ No newline at end of file diff --git a/CarManagerV3/Forms/Util/PleaseWait.cs b/CarManagerV3/Forms/Util/PleaseWait.cs index 87993bc..6e41c72 100644 --- a/CarManagerV3/Forms/Util/PleaseWait.cs +++ b/CarManagerV3/Forms/Util/PleaseWait.cs @@ -5,13 +5,19 @@ namespace CarManagerV3 { public partial class PleaseWait : Form { - public PleaseWait() + public PleaseWait(string content = "Saving your changes...") { InitializeComponent(); + + this.SetStyle(ControlStyles.OptimizedDoubleBuffer | + ControlStyles.AllPaintingInWmPaint | + ControlStyles.UserPaint, true); + // loading animation - progressBar1.Style = ProgressBarStyle.Marquee; - progressBar1.MarqueeAnimationSpeed = 30; - + animatedProgressBar1.StartAnimation(); + lblContent.Text = content; + + } private void progressBar1_Click(object sender, EventArgs e) diff --git a/CarManagerV3/Forms/Util/PleaseWait.resx b/CarManagerV3/Forms/Util/PleaseWait.resx index 1af7de1..8b2ff64 100644 --- a/CarManagerV3/Forms/Util/PleaseWait.resx +++ b/CarManagerV3/Forms/Util/PleaseWait.resx @@ -1,17 +1,17 @@  - diff --git a/CarManagerV3/Manager/Updater.cs b/CarManagerV3/Manager/Updater.cs new file mode 100644 index 0000000..019744c --- /dev/null +++ b/CarManagerV3/Manager/Updater.cs @@ -0,0 +1,197 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace CarManagerV3.Manager +{ + internal class Updater + { + + private static readonly string GitApiUrl = "https://git.jaro.digital/api/v1"; + private static readonly string GitRepoOwner = "frozd"; + private static readonly string GitRepoName = "carmanager-3"; + private static readonly string GitApiRepoUrl = $"{GitApiUrl}/repos/{GitRepoOwner}/{GitRepoName}"; + private static readonly string LatestReleaseEndpoint = $"{GitApiRepoUrl}/releases/latest"; + private static readonly string PreReleaseEndpoint = $"{GitApiRepoUrl}/releases?limit=1&pre-release=true"; + private static string latestVersionCache = null; + private static DateTime lastChecked = DateTime.MinValue; + private static readonly int CacheDurationMinutes = 60; + private static bool cacheIncludesPrerelease = false; + private static readonly string debugVersion = null;//"1.2.0"; + + public static string GetCurrentVersion() + { + //DEBUG:: + if(debugVersion != null) + { + return debugVersion; + } + + var asm = Assembly.GetEntryAssembly()!; + Version v = asm.GetName().Version ?? new Version(0, 0, 0, 0); + return v.ToString(); + } + + private static bool IsCacheValid(bool includePreRelease = false) + { + return includePreRelease == cacheIncludesPrerelease && latestVersionCache != null && (DateTime.Now - lastChecked).TotalMinutes < CacheDurationMinutes; + } + + private static void InvalidateCache() + { + latestVersionCache = null; + lastChecked = DateTime.MinValue; + } + + private static void SetCache(string version, bool includePreRelease = false) + { + latestVersionCache = version; + cacheIncludesPrerelease = includePreRelease; + lastChecked = DateTime.Now; + } + + + public static string GetLatestVersion(bool includePreRelease = false) + { + if (IsCacheValid(includePreRelease)) + { + System.Diagnostics.Debug.WriteLine("Using cached latest version: " + latestVersionCache); + return latestVersionCache; + } + string latestVersion = null; + // Get the latest stable version first + using (var client = new System.Net.Http.HttpClient()) + { + var response = client.GetAsync(LatestReleaseEndpoint).Result; + if (response.IsSuccessStatusCode) + { + var content = response.Content.ReadAsStringAsync().Result; + dynamic release = Newtonsoft.Json.JsonConvert.DeserializeObject(content); + latestVersion = release.tag_name; + } + } + // If pre-release is requested, check for the latest pre-release version + if (includePreRelease) + { + using (var client = new System.Net.Http.HttpClient()) + { + var response = client.GetAsync(PreReleaseEndpoint).Result; + if (response.IsSuccessStatusCode) + { + var content = response.Content.ReadAsStringAsync().Result; + dynamic releases = Newtonsoft.Json.JsonConvert.DeserializeObject(content); + if (releases.Count > 0) + { + var preReleaseVersion = releases[0].tag_name; + // Compare versions and return the newer one + if (IsNewerVersion(preReleaseVersion, latestVersion)) + { + latestVersion = preReleaseVersion; + } + } + } + } + } + SetCache(latestVersion, includePreRelease); + return latestVersion; + + } + + public static bool IsNewerVersion(string versionA, string versionB) + { + if (versionA == null) return false; + if (versionB == null) return true; + Version vA = new Version(versionA.TrimStart('v')); + Version vB = new Version(versionB.TrimStart('v')); + return vA > vB; + } + + + public static void DownloadNewestInstaller(bool includePreRelease = false) + { + string latestVersion = GetLatestVersion(includePreRelease); + if (latestVersion == null) + { + throw new Exception("Could not fetch latest version from Git API."); + } + + string releaseUrl = $"{GitApiRepoUrl}/releases/tags/{latestVersion}"; + using (var client = new System.Net.Http.HttpClient()) + { + var response = client.GetAsync(releaseUrl).Result; + if (response.IsSuccessStatusCode) + { + var content = response.Content.ReadAsStringAsync().Result; + dynamic release = Newtonsoft.Json.JsonConvert.DeserializeObject(content); + string downloadUrl = null; + foreach (var asset in release.assets) + { + // file that ends with .msi + if (asset.name.ToString().EndsWith(".msi")) + { + downloadUrl = asset.browser_download_url; + break; + } + } + + if (downloadUrl != null) + { + + + // Download the installer to the users set Data dir, run it, and then exit the application. + string tempFilePath = System.IO.Path.Combine(System.IO.Path.GetTempPath(), $"CarManagerInstaller_{latestVersion}.msi"); + using (var downloadClient = new System.Net.WebClient()) + { + downloadClient.DownloadFile(downloadUrl, tempFilePath); + } + // Use ProcessStartInfo with UseShellExecute to launch the MSI file + var processStartInfo = new System.Diagnostics.ProcessStartInfo + { + FileName = tempFilePath, + UseShellExecute = true + }; + System.Diagnostics.Process.Start(processStartInfo); + + + Application.Exit(); + } + else + { + throw new Exception("Could not find installer asset in the latest release."); + } + } + else + { + throw new Exception("Could not fetch release information from Git API."); + + } + + } + } + + public static bool IsUpdateAvailable(bool includePreRelease = false) + { + string currentVersion = GetCurrentVersion(); + string latestVersion = GetLatestVersion(includePreRelease); + return IsNewerVersion(latestVersion, currentVersion); + } + + public static void openReleasePage(string version = null) + { + if(version == null) + { + version = GetLatestVersion(true); + } + string releaseUrl = $"https://git.jaro.digital/{GitRepoOwner}/{GitRepoName}/releases/tag/{version}"; + System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo + { + FileName = releaseUrl, + UseShellExecute = true + }); + } + } +} diff --git a/CarManagerV3/Properties/Settings.Designer.cs b/CarManagerV3/Properties/Settings.Designer.cs index 1b3e9fc..9975d8f 100644 --- a/CarManagerV3/Properties/Settings.Designer.cs +++ b/CarManagerV3/Properties/Settings.Designer.cs @@ -34,5 +34,17 @@ namespace CarManagerV3.Properties { this["DataLocation"] = value; } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool AllowPrerelease { + get { + return ((bool)(this["AllowPrerelease"])); + } + set { + this["AllowPrerelease"] = value; + } + } } } diff --git a/CarManagerV3/Properties/Settings.settings b/CarManagerV3/Properties/Settings.settings index ee34d15..cc257af 100644 --- a/CarManagerV3/Properties/Settings.settings +++ b/CarManagerV3/Properties/Settings.settings @@ -5,5 +5,8 @@ + + False + \ No newline at end of file