From 328f74b03c59651c48307cd493d68c2c91d3795e Mon Sep 17 00:00:00 2001 From: Miepee <38186597+Miepee@users.noreply.github.com> Date: Sun, 22 May 2022 11:31:22 +0200 Subject: [PATCH] convert launcher to file scoped namespaces --- AM2RLauncher/AM2RLauncher/LauncherUpdater.cs | 335 ++-- .../MainForm/CustomControls/ColorButton.cs | 109 +- .../MainForm/CustomControls/CustomButton.cs | 297 ++-- .../MainForm/CustomControls/ImageButton.cs | 156 +- .../AM2RLauncher/MainForm/MainForm.Events.cs | 1502 ++++++++-------- .../AM2RLauncher/MainForm/MainForm.Methods.cs | 377 ++-- .../MainForm/MainForm.StateMachine.cs | 619 ++++--- .../AM2RLauncher/MainForm/MainForm.UI.cs | 1541 ++++++++--------- 8 files changed, 2463 insertions(+), 2473 deletions(-) diff --git a/AM2RLauncher/AM2RLauncher/LauncherUpdater.cs b/AM2RLauncher/AM2RLauncher/LauncherUpdater.cs index 40a4dd8..22d6281 100644 --- a/AM2RLauncher/AM2RLauncher/LauncherUpdater.cs +++ b/AM2RLauncher/AM2RLauncher/LauncherUpdater.cs @@ -6,223 +6,222 @@ using System.IO; using System.IO.Compression; using System.Net; -namespace AM2RLauncher +namespace AM2RLauncher; + +/// +/// Class that checks for Updates and then Updates the Launcher. +/// +//TODO: Mac support for auto updater in general +public static class LauncherUpdater { - /// - /// Class that checks for Updates and then Updates the Launcher. - /// - //TODO: Mac support for auto updater in general - public static class LauncherUpdater - { - // How often this was broken count: 7 - // Auto updating is fun! + // How often this was broken count: 7 + // Auto updating is fun! - /// The Version that identifies this current release. - public const string VERSION = Core.Version; + /// The Version that identifies this current release. + public const string VERSION = Core.Version; - /// The Path of the oldConfig. Only gets used Windows-only - private static readonly string oldConfigPath = CrossPlatformOperations.CurrentPath + "/" + CrossPlatformOperations.LauncherName + ".oldCfg"; + /// The Path of the oldConfig. Only gets used Windows-only + private static readonly string oldConfigPath = CrossPlatformOperations.CurrentPath + "/" + CrossPlatformOperations.LauncherName + ".oldCfg"; - /// The actual Path where the executable is stored, only used for updating. - private static readonly string updatePath = OS.IsWindows ? CrossPlatformOperations.CurrentPath - : (OS.IsLinux ? Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory) : Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory + "../../../")); + /// The actual Path where the executable is stored, only used for updating. + private static readonly string updatePath = OS.IsWindows ? CrossPlatformOperations.CurrentPath + : (OS.IsLinux ? Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory) : Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory + "../../../")); - /// - /// Our log object, that handles logging the current execution to a file. - /// - private static readonly ILog log = LogManager.GetLogger(typeof(LauncherUpdater)); + /// + /// Our log object, that handles logging the current execution to a file. + /// + private static readonly ILog log = LogManager.GetLogger(typeof(LauncherUpdater)); - /// - /// Performs the entire AM2RLauncher update procedure. - /// - public static void Main() - { - log.Info("Running update check..."); + /// + /// Performs the entire AM2RLauncher update procedure. + /// + public static void Main() + { + log.Info("Running update check..."); - // Update section + // Update section - // Clean old files that have been left - if (File.Exists(CrossPlatformOperations.CurrentPath + "/AM2RLauncher.bak")) - { - log.Info("AM2RLauncher.bak detected. Removing file."); - File.Delete(CrossPlatformOperations.CurrentPath + "/AM2RLauncher.bak"); - } - if (OS.IsWindows && File.Exists(oldConfigPath)) - { - log.Info(CrossPlatformOperations.LauncherName + ".oldCfg detected. Removing file."); - File.Delete(oldConfigPath); - } - if (OS.IsWindows && Directory.Exists(CrossPlatformOperations.CurrentPath + "/oldLib")) + // Clean old files that have been left + if (File.Exists(CrossPlatformOperations.CurrentPath + "/AM2RLauncher.bak")) + { + log.Info("AM2RLauncher.bak detected. Removing file."); + File.Delete(CrossPlatformOperations.CurrentPath + "/AM2RLauncher.bak"); + } + if (OS.IsWindows && File.Exists(oldConfigPath)) + { + log.Info(CrossPlatformOperations.LauncherName + ".oldCfg detected. Removing file."); + File.Delete(oldConfigPath); + } + if (OS.IsWindows && Directory.Exists(CrossPlatformOperations.CurrentPath + "/oldLib")) + { + log.Info("Old lib folder detected, removing folder."); + Directory.Delete(CrossPlatformOperations.CurrentPath + "/oldLib", true); + } + + // Clean up old update libs + if (OS.IsWindows && Directory.Exists(CrossPlatformOperations.CurrentPath + "/lib")) + { + foreach (FileInfo file in new DirectoryInfo(CrossPlatformOperations.CurrentPath + "/lib").GetFiles()) { - log.Info("Old lib folder detected, removing folder."); - Directory.Delete(CrossPlatformOperations.CurrentPath + "/oldLib", true); + if (file.Name.EndsWith(".bak")) + file.Delete(); } - // Clean up old update libs - if (OS.IsWindows && Directory.Exists(CrossPlatformOperations.CurrentPath + "/lib")) + // Do the same for each subdirectory + foreach (DirectoryInfo dir in new DirectoryInfo(CrossPlatformOperations.CurrentPath + "/lib").GetDirectories()) { - foreach (FileInfo file in new DirectoryInfo(CrossPlatformOperations.CurrentPath + "/lib").GetFiles()) + foreach (FileInfo file in dir.GetFiles()) { if (file.Name.EndsWith(".bak")) file.Delete(); } - - // Do the same for each subdirectory - foreach (DirectoryInfo dir in new DirectoryInfo(CrossPlatformOperations.CurrentPath + "/lib").GetDirectories()) - { - foreach (FileInfo file in dir.GetFiles()) - { - if (file.Name.EndsWith(".bak")) - file.Delete(); - } - } } + } - // Check settings if autoUpdateLauncher is set to true - bool autoUpdate = Boolean.Parse(MainForm.ReadFromConfig("AutoUpdateLauncher")); + // Check settings if autoUpdateLauncher is set to true + bool autoUpdate = Boolean.Parse(MainForm.ReadFromConfig("AutoUpdateLauncher")); - if (autoUpdate) - { - log.Info("AutoUpdate Launcher set to true!"); + if (autoUpdate) + { + log.Info("AutoUpdate Launcher set to true!"); - // This is supposed to fix the updater throwing an exception on windows 7 and earlier(?) - // See this for information: https://stackoverflow.com/q/2859790 and https://stackoverflow.com/a/50977774 - if (OS.IsWindows) - { - ServicePointManager.Expect100Continue = true; - ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; - } + // This is supposed to fix the updater throwing an exception on windows 7 and earlier(?) + // See this for information: https://stackoverflow.com/q/2859790 and https://stackoverflow.com/a/50977774 + if (OS.IsWindows) + { + ServicePointManager.Expect100Continue = true; + ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; + } - HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://github.com/AM2R-Community-Developers/AM2RLauncher/releases/latest"); - HttpWebResponse response; - try - { - response = (HttpWebResponse)request.GetResponse(); - } - catch (WebException) - { - log.Error("WebException caught! Displaying MessageBox."); - MessageBox.Show(Language.Text.NoInternetConnection); - return; - } + HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://github.com/AM2R-Community-Developers/AM2RLauncher/releases/latest"); + HttpWebResponse response; + try + { + response = (HttpWebResponse)request.GetResponse(); + } + catch (WebException) + { + log.Error("WebException caught! Displaying MessageBox."); + MessageBox.Show(Language.Text.NoInternetConnection); + return; + } - Uri realUri = response.ResponseUri; - string onlineVersion = realUri.AbsoluteUri.Substring(realUri.AbsoluteUri.LastIndexOf('/') + 1); - bool isCurrentVersionOutdated = false; + Uri realUri = response.ResponseUri; + string onlineVersion = realUri.AbsoluteUri.Substring(realUri.AbsoluteUri.LastIndexOf('/') + 1); + bool isCurrentVersionOutdated = false; - string[] localVersionArray = VERSION.Split('.'); - string[] onlineVersionArray = onlineVersion.Split('.'); + string[] localVersionArray = VERSION.Split('.'); + string[] onlineVersionArray = onlineVersion.Split('.'); - for (int i = 0; i < localVersionArray.Length; i++) + for (int i = 0; i < localVersionArray.Length; i++) + { + int onlineNum = Int32.Parse(onlineVersionArray[i]); + int localNum = Int32.Parse(localVersionArray[i]); + if (onlineNum > localNum) { - int onlineNum = Int32.Parse(onlineVersionArray[i]); - int localNum = Int32.Parse(localVersionArray[i]); - if (onlineNum > localNum) - { - isCurrentVersionOutdated = true; - break; - } - if (localNum > onlineNum) - break; + isCurrentVersionOutdated = true; + break; } + if (localNum > onlineNum) + break; + } - log.Info((isCurrentVersionOutdated ? "Updating" : "Not Updating") + " from " + VERSION + " to " + onlineVersion); + log.Info((isCurrentVersionOutdated ? "Updating" : "Not Updating") + " from " + VERSION + " to " + onlineVersion); - // No new update, exiting - if (!isCurrentVersionOutdated) - return; + // No new update, exiting + if (!isCurrentVersionOutdated) + return; - // For mac, we just show a message box that a new version is available, because I don't want to support it yet. - // hardcoded string, since also temporarily until it gets supported one day. - if (OS.IsMac) - { - MessageBox.Show("Your current version is outdated! The newest version is " + onlineVersion + "." + - "Please recompile AM2RLauncher again or disable auto-updating"); - return; - } + // For mac, we just show a message box that a new version is available, because I don't want to support it yet. + // hardcoded string, since also temporarily until it gets supported one day. + if (OS.IsMac) + { + MessageBox.Show("Your current version is outdated! The newest version is " + onlineVersion + "." + + "Please recompile AM2RLauncher again or disable auto-updating"); + return; + } - log.Info("Current version (" + VERSION + ") is outdated! Initiating update for version " + onlineVersion + "."); + log.Info("Current version (" + VERSION + ") is outdated! Initiating update for version " + onlineVersion + "."); - string tmpUpdatePath = CrossPlatformOperations.CurrentPath + "/tmpupdate/"; - string zipPath = CrossPlatformOperations.CurrentPath + "/launcher.zip"; + string tmpUpdatePath = CrossPlatformOperations.CurrentPath + "/tmpupdate/"; + string zipPath = CrossPlatformOperations.CurrentPath + "/launcher.zip"; - // Clean tmpupdate - if (Directory.Exists(tmpUpdatePath)) - Directory.Delete(tmpUpdatePath, true); - if (!Directory.Exists(tmpUpdatePath)) - Directory.CreateDirectory(tmpUpdatePath); + // Clean tmpupdate + if (Directory.Exists(tmpUpdatePath)) + Directory.Delete(tmpUpdatePath, true); + if (!Directory.Exists(tmpUpdatePath)) + Directory.CreateDirectory(tmpUpdatePath); - try - { - using var client = new WebClient(); - string platformSuffix = ""; - if (OS.IsWindows) platformSuffix = "_win"; - else if (OS.IsLinux) platformSuffix = "_lin"; + try + { + using var client = new WebClient(); + string platformSuffix = ""; + if (OS.IsWindows) platformSuffix = "_win"; + else if (OS.IsLinux) platformSuffix = "_lin"; - log.Info("Downloading https://github.com/AM2R-Community-Developers/AM2RLauncher/releases/latest/download/AM2RLauncher_" + onlineVersion + platformSuffix + ".zip to " + zipPath + "."); + log.Info("Downloading https://github.com/AM2R-Community-Developers/AM2RLauncher/releases/latest/download/AM2RLauncher_" + onlineVersion + platformSuffix + ".zip to " + zipPath + "."); - client.DownloadFile("https://github.com/AM2R-Community-Developers/AM2RLauncher/releases/latest/download/AM2RLauncher_" + onlineVersion + platformSuffix + ".zip", zipPath); + client.DownloadFile("https://github.com/AM2R-Community-Developers/AM2RLauncher/releases/latest/download/AM2RLauncher_" + onlineVersion + platformSuffix + ".zip", zipPath); - log.Info("File successfully downloaded."); - } - catch (UnauthorizedAccessException) - { - log.Error("UnauthorizedAccessException caught! Displaying MessageBox."); - MessageBox.Show(Language.Text.UnauthorizedAccessMessage); - return; - } + log.Info("File successfully downloaded."); + } + catch (UnauthorizedAccessException) + { + log.Error("UnauthorizedAccessException caught! Displaying MessageBox."); + MessageBox.Show(Language.Text.UnauthorizedAccessMessage); + return; + } + + ZipFile.ExtractToDirectory(zipPath, tmpUpdatePath); + log.Info("Updates successfully extracted to " + tmpUpdatePath); - ZipFile.ExtractToDirectory(zipPath, tmpUpdatePath); - log.Info("Updates successfully extracted to " + tmpUpdatePath); + File.Delete(zipPath); + File.Move(updatePath + "/" + CrossPlatformOperations.LauncherName, CrossPlatformOperations.CurrentPath + "/AM2RLauncher.bak"); + if (OS.IsWindows) File.Move(CrossPlatformOperations.LauncherName + ".config", CrossPlatformOperations.LauncherName + ".oldCfg"); - File.Delete(zipPath); - File.Move(updatePath + "/" + CrossPlatformOperations.LauncherName, CrossPlatformOperations.CurrentPath + "/AM2RLauncher.bak"); - if (OS.IsWindows) File.Move(CrossPlatformOperations.LauncherName + ".config", CrossPlatformOperations.LauncherName + ".oldCfg"); + foreach (var file in new DirectoryInfo(tmpUpdatePath).GetFiles()) + { + log.Info("Moving " + file.FullName + " to " + CrossPlatformOperations.CurrentPath + "/" + file.Name); + File.Copy(file.FullName, updatePath + "/" + file.Name, true); + } + // For windows, the actual application is in "AM2RLauncher.dll". Which means, we need to update the lib folder as well. + if (OS.IsWindows && Directory.Exists(CrossPlatformOperations.CurrentPath + "/lib")) + { + // So, because Windows behavior is dumb... - foreach (var file in new DirectoryInfo(tmpUpdatePath).GetFiles()) + // Rename all files in lib to *.bak + foreach (FileInfo file in new DirectoryInfo(CrossPlatformOperations.CurrentPath + "/lib").GetFiles()) { - log.Info("Moving " + file.FullName + " to " + CrossPlatformOperations.CurrentPath + "/" + file.Name); - File.Copy(file.FullName, updatePath + "/" + file.Name, true); + file.CopyTo(file.Directory + "/" + file.Name + ".bak"); } - // For windows, the actual application is in "AM2RLauncher.dll". Which means, we need to update the lib folder as well. - if (OS.IsWindows && Directory.Exists(CrossPlatformOperations.CurrentPath + "/lib")) - { - // So, because Windows behavior is dumb... - // Rename all files in lib to *.bak - foreach (FileInfo file in new DirectoryInfo(CrossPlatformOperations.CurrentPath + "/lib").GetFiles()) - { - file.CopyTo(file.Directory + "/" + file.Name + ".bak"); - } - - // Do the same for each sub directory - foreach (DirectoryInfo dir in new DirectoryInfo(CrossPlatformOperations.CurrentPath + "/lib").GetDirectories()) + // Do the same for each sub directory + foreach (DirectoryInfo dir in new DirectoryInfo(CrossPlatformOperations.CurrentPath + "/lib").GetDirectories()) + { + foreach (FileInfo file in dir.GetFiles()) { - foreach (FileInfo file in dir.GetFiles()) - { - file.CopyTo(file.Directory + "/" + file.Name + ".bak"); - } + file.CopyTo(file.Directory + "/" + file.Name + ".bak"); } - - // Yes, the above calls could be recursive. No, I can't be bothered to make them as such. - if (Directory.Exists(tmpUpdatePath + "lib")) - HelperMethods.DirectoryCopy(tmpUpdatePath + "lib", CrossPlatformOperations.CurrentPath + "/lib"); } - Directory.Delete(tmpUpdatePath, true); + // Yes, the above calls could be recursive. No, I can't be bothered to make them as such. + if (Directory.Exists(tmpUpdatePath + "lib")) + HelperMethods.DirectoryCopy(tmpUpdatePath + "lib", CrossPlatformOperations.CurrentPath + "/lib"); + } - MainForm.CopyOldConfigToNewConfig(); + Directory.Delete(tmpUpdatePath, true); - log.Info("Files extracted. Preparing to restart executable..."); - if (OS.IsLinux) System.Diagnostics.Process.Start("chmod", "+x " + updatePath + "./AM2RLauncher.Gtk"); + MainForm.CopyOldConfigToNewConfig(); - System.Diagnostics.Process.Start(updatePath + "/" + CrossPlatformOperations.LauncherName); - Environment.Exit(0); - } - else - { - log.Info("AutoUpdate Launcher set to false. Exiting update check."); - } + log.Info("Files extracted. Preparing to restart executable..."); + if (OS.IsLinux) System.Diagnostics.Process.Start("chmod", "+x " + updatePath + "./AM2RLauncher.Gtk"); + + System.Diagnostics.Process.Start(updatePath + "/" + CrossPlatformOperations.LauncherName); + Environment.Exit(0); + } + else + { + log.Info("AutoUpdate Launcher set to false. Exiting update check."); } } } \ No newline at end of file diff --git a/AM2RLauncher/AM2RLauncher/MainForm/CustomControls/ColorButton.cs b/AM2RLauncher/AM2RLauncher/MainForm/CustomControls/ColorButton.cs index 6d03f59..9ce01e7 100644 --- a/AM2RLauncher/AM2RLauncher/MainForm/CustomControls/ColorButton.cs +++ b/AM2RLauncher/AM2RLauncher/MainForm/CustomControls/ColorButton.cs @@ -2,73 +2,72 @@ using Eto.Forms; using Pablo.Controls; -namespace AM2RLauncher +namespace AM2RLauncher; + +/// +/// Extension of that allows for advanced color settings. +/// +class ColorButton : CustomButton { - /// - /// Extension of that allows for advanced color settings. - /// - class ColorButton : CustomButton - { - /// The to draw the background with when is true. - public Color BackgroundColorHover { get; set; } + /// The to draw the background with when is true. + public Color BackgroundColorHover { get; set; } - /// The to use for text drawing. - public Font Font { get; set; } + /// The to use for text drawing. + public Font Font { get; set; } - /// The with which to draw the frame. - public Color FrameColor { get; set; } + /// The with which to draw the frame. + public Color FrameColor { get; set; } - /// The with which to draw the frame when it is disabled. - public Color FrameColorDisabled { get; set; } + /// The with which to draw the frame when it is disabled. + public Color FrameColorDisabled { get; set; } - /// The text to be drawn. - public string Text { get; set; } + /// The text to be drawn. + public string Text { get; set; } - /// The with which to draw the . - public Color TextColor { get; set; } + /// The with which to draw the . + public Color TextColor { get; set; } - /// The with which to draw the when it is disabled. - public Color TextColorDisabled { get; set; } + /// The with which to draw the when it is disabled. + public Color TextColorDisabled { get; set; } - /// - /// Creates a with a default set of attributes. - /// - public ColorButton() - { - BackgroundColorHover = Colors.White; - Font = new Font(SystemFont.Default, 12); - FrameColor = Colors.White; - FrameColorDisabled = Colors.Gray; - Text = ""; - TextColor = Colors.Black; - TextColorDisabled = Colors.Gray; - } + /// + /// Creates a with a default set of attributes. + /// + public ColorButton() + { + BackgroundColorHover = Colors.White; + Font = new Font(SystemFont.Default, 12); + FrameColor = Colors.White; + FrameColorDisabled = Colors.Gray; + Text = ""; + TextColor = Colors.Black; + TextColorDisabled = Colors.Gray; + } - /// - /// Event raised to draw this control. - /// - /// - protected override void OnPaint(PaintEventArgs pe) - { - // Define draw bounds - Rectangle drawBounds = new Rectangle(2, 2, Width - 5, Height - 5); + /// + /// Event raised to draw this control. + /// + /// + protected override void OnPaint(PaintEventArgs pe) + { + // Define draw bounds + Rectangle drawBounds = new Rectangle(2, 2, Width - 5, Height - 5); - // Draw background - pe.Graphics.FillRectangle(Hover ? BackgroundColorHover : BackgroundColor, drawBounds); + // Draw background + pe.Graphics.FillRectangle(Hover ? BackgroundColorHover : BackgroundColor, drawBounds); - // Draw frame - Pen framePen = new Pen(Enabled ? FrameColor : FrameColorDisabled, 1); - pe.Graphics.DrawRectangle(framePen, drawBounds); + // Draw frame + Pen framePen = new Pen(Enabled ? FrameColor : FrameColorDisabled, 1); + pe.Graphics.DrawRectangle(framePen, drawBounds); - SolidBrush brush = new SolidBrush(Enabled ? TextColor : TextColorDisabled); + SolidBrush brush = new SolidBrush(Enabled ? TextColor : TextColorDisabled); - // Get text measurements - SizeF stringSize = pe.Graphics.MeasureString(Font, Text); - int textWidth = (int)stringSize.Width; - int textHeight = (int)stringSize.Height; + // Get text measurements + SizeF stringSize = pe.Graphics.MeasureString(Font, Text); + int textWidth = (int)stringSize.Width; + int textHeight = (int)stringSize.Height; - // Draw text - pe.Graphics.DrawText(Font, brush, new PointF(Width / 2 - textWidth / 2, Height / 2 - textHeight / 2), Text); - } + // Draw text + pe.Graphics.DrawText(Font, brush, new PointF(Width / 2 - textWidth / 2, Height / 2 - textHeight / 2), Text); } -} +} \ No newline at end of file diff --git a/AM2RLauncher/AM2RLauncher/MainForm/CustomControls/CustomButton.cs b/AM2RLauncher/AM2RLauncher/MainForm/CustomControls/CustomButton.cs index 6bf6e1a..bf318a4 100644 --- a/AM2RLauncher/AM2RLauncher/MainForm/CustomControls/CustomButton.cs +++ b/AM2RLauncher/AM2RLauncher/MainForm/CustomControls/CustomButton.cs @@ -2,190 +2,189 @@ using Eto.Forms; using System; -namespace Pablo.Controls +namespace Pablo.Controls; + +/// +/// A custom button implementation for advanced drawing and events. +/// Originally written by cwensley (https://gist.github.com/cwensley/95000998e37acd93e830), +/// modified by Lojemiru and Miepee for use in the AM2RLauncher. +/// +public class CustomButton : Drawable { - /// - /// A custom button implementation for advanced drawing and events. - /// Originally written by cwensley (https://gist.github.com/cwensley/95000998e37acd93e830), - /// modified by Lojemiru and Miepee for use in the AM2RLauncher. - /// - public class CustomButton : Drawable - { - /// Whether or not this is being pressed. - bool pressed; + /// Whether or not this is being pressed. + bool pressed; - /// Whether or not the mouse is hovering over this . - bool hover; + /// Whether or not the mouse is hovering over this . + bool hover; - /// - bool mouseDown; + /// + bool mouseDown; - /// Gets or sets the disabled color of this control. - public static Color DisabledColor = Color.FromGrayscale(0.4f, 0.3f); + /// Gets or sets the disabled color of this control. + public static Color DisabledColor = Color.FromGrayscale(0.4f, 0.3f); - /// Gets or sets the color of this control. - public static Color EnabledColor = Colors.Black; + /// Gets or sets the color of this control. + public static Color EnabledColor = Colors.Black; - /// Gets or sets whether or not this control is enabled. - public override bool Enabled + /// Gets or sets whether or not this control is enabled. + public override bool Enabled + { + get { return base.Enabled; } + set { - get { return base.Enabled; } - set + if (base.Enabled != value) { - if (base.Enabled != value) - { - base.Enabled = value; - if (Loaded) - Invalidate(); - } + base.Enabled = value; + if (Loaded) + Invalidate(); } } + } - /// Gets or sets . - public bool Pressed + /// Gets or sets . + public bool Pressed + { + get { return pressed; } + set { - get { return pressed; } - set + if (pressed != value) { - if (pressed != value) - { - pressed = value; - mouseDown = false; - if (Loaded) - Invalidate(); - } + pressed = value; + mouseDown = false; + if (Loaded) + Invalidate(); } } + } - /// Gets . - public bool Hover - { - get { return hover; } - } + /// Gets . + public bool Hover + { + get { return hover; } + } - /// Gets either the or dependent on . - public Color DrawColor - { - get { return Enabled ? EnabledColor : DisabledColor; } - } + /// Gets either the or dependent on . + public Color DrawColor + { + get { return Enabled ? EnabledColor : DisabledColor; } + } - /// Gets or sets whether or not this control should act as a toggle. - public bool Toggle { get; set; } + /// Gets or sets whether or not this control should act as a toggle. + public bool Toggle { get; set; } - /// Gets or sets whether or not this control is persistent. - public bool Persistent { get; set; } + /// Gets or sets whether or not this control is persistent. + public bool Persistent { get; set; } - public event EventHandler Click; + public event EventHandler Click; - /// - /// Event raised when this control is clicked. - /// - /// - protected virtual void OnClick(EventArgs e) - { - if (Click != null) - Click(this, e); - } + /// + /// Event raised when this control is clicked. + /// + /// + protected virtual void OnClick(EventArgs e) + { + if (Click != null) + Click(this, e); + } - /// Default constructor. Sets to true. - public CustomButton() { Enabled = true; } + /// Default constructor. Sets to true. + public CustomButton() { Enabled = true; } - /// Event raised when this control is resized. - /// - protected override void OnSizeChanged(EventArgs e) + /// Event raised when this control is resized. + /// + protected override void OnSizeChanged(EventArgs e) + { + base.OnSizeChanged(e); + if (Loaded) + Invalidate(); + } + + /// + /// Event raised when the mouse is pressed down over this control's bounding box. + /// + /// + protected override void OnMouseDown(MouseEventArgs e) + { + base.OnMouseDown(e); + if (Enabled) { - base.OnSizeChanged(e); - if (Loaded) - Invalidate(); + mouseDown = true; + Invalidate(); } + } + + /// + /// Event raised when the mouse enters this control's bounding box. + /// + /// + protected override void OnMouseEnter(MouseEventArgs e) + { + Cursor = new Cursor(CursorType.Pointer); + base.OnMouseEnter(e); + hover = true; + Invalidate(); + } - /// - /// Event raised when the mouse is pressed down over this control's bounding box. - /// - /// - protected override void OnMouseDown(MouseEventArgs e) + /// + /// Event raised when the mouse leaves this control's bounding box. + /// + /// + protected override void OnMouseLeave(MouseEventArgs e) + { + Cursor = new Cursor(CursorType.Default); + base.OnMouseLeave(e); + hover = false; + Invalidate(); + } + + /// + /// Event raised when the mouse is released over this control's bounding box. + /// + /// + protected override void OnMouseUp(MouseEventArgs e) + { + base.OnMouseUp(e); + var rect = new Rectangle(this.Size); + if (mouseDown && rect.Contains((Point)e.Location)) { - base.OnMouseDown(e); + if (Toggle) + pressed = !pressed; + else if (Persistent) + pressed = true; + else + pressed = false; + mouseDown = false; + + this.Invalidate(); if (Enabled) - { - mouseDown = true; - Invalidate(); - } + OnClick(EventArgs.Empty); } - - /// - /// Event raised when the mouse enters this control's bounding box. - /// - /// - protected override void OnMouseEnter(MouseEventArgs e) + else { - Cursor = new Cursor(CursorType.Pointer); - base.OnMouseEnter(e); - hover = true; - Invalidate(); + mouseDown = false; + this.Invalidate(); } + } - /// - /// Event raised when the mouse leaves this control's bounding box. - /// - /// - protected override void OnMouseLeave(MouseEventArgs e) + /// + /// Event raised to draw this control. + /// + /// + protected override void OnPaint(PaintEventArgs pe) + { + var rect = new Rectangle(this.Size); + var col = Color.FromGrayscale(hover && Enabled ? 0.95f : 0.8f); + if (Enabled && (pressed || mouseDown)) { - Cursor = new Cursor(CursorType.Default); - base.OnMouseLeave(e); - hover = false; - Invalidate(); + pe.Graphics.FillRectangle(col, rect); + pe.Graphics.DrawInsetRectangle(Colors.Gray, Colors.White, rect); } - - /// - /// Event raised when the mouse is released over this control's bounding box. - /// - /// - protected override void OnMouseUp(MouseEventArgs e) + else if (hover && Enabled) { - base.OnMouseUp(e); - var rect = new Rectangle(this.Size); - if (mouseDown && rect.Contains((Point)e.Location)) - { - if (Toggle) - pressed = !pressed; - else if (Persistent) - pressed = true; - else - pressed = false; - mouseDown = false; - - this.Invalidate(); - if (Enabled) - OnClick(EventArgs.Empty); - } - else - { - mouseDown = false; - this.Invalidate(); - } + pe.Graphics.FillRectangle(col, rect); + pe.Graphics.DrawInsetRectangle(Colors.White, Colors.Gray, rect); } - /// - /// Event raised to draw this control. - /// - /// - protected override void OnPaint(PaintEventArgs pe) - { - var rect = new Rectangle(this.Size); - var col = Color.FromGrayscale(hover && Enabled ? 0.95f : 0.8f); - if (Enabled && (pressed || mouseDown)) - { - pe.Graphics.FillRectangle(col, rect); - pe.Graphics.DrawInsetRectangle(Colors.Gray, Colors.White, rect); - } - else if (hover && Enabled) - { - pe.Graphics.FillRectangle(col, rect); - pe.Graphics.DrawInsetRectangle(Colors.White, Colors.Gray, rect); - } - - base.OnPaint(pe); - } + base.OnPaint(pe); } -} +} \ No newline at end of file diff --git a/AM2RLauncher/AM2RLauncher/MainForm/CustomControls/ImageButton.cs b/AM2RLauncher/AM2RLauncher/MainForm/CustomControls/ImageButton.cs index a8329e2..5b85cbd 100644 --- a/AM2RLauncher/AM2RLauncher/MainForm/CustomControls/ImageButton.cs +++ b/AM2RLauncher/AM2RLauncher/MainForm/CustomControls/ImageButton.cs @@ -1,105 +1,103 @@ using Eto.Drawing; using System; -namespace Pablo.Controls +namespace Pablo.Controls; + +/// +/// Extension of that allows image assignment and drawing. +/// Originally written by cwensley (https://gist.github.com/cwensley/95000998e37acd93e830), +/// modified by Lojemiru and Miepee for use in the AM2RLauncher. +/// +public class ImageButton : CustomButton { /// - /// Extension of that allows image assignment and drawing. - /// Originally written by cwensley (https://gist.github.com/cwensley/95000998e37acd93e830), - /// modified by Lojemiru and Miepee for use in the AM2RLauncher. + /// Whether or not this control's size has been set. /// - public class ImageButton : CustomButton - { - /// - /// Whether or not this control's size has been set. - /// - bool sizeSet; + bool sizeSet; - /// - /// Gets or sets the to draw during . - /// - public Image Image { get; set; } + /// + /// Gets or sets the to draw during . + /// + public Image Image { get; set; } - /// - /// Gets or sets the to draw during if this control is disabled. - /// - public Image DisabledImage { get; set; } + /// + /// Gets or sets the to draw during if this control is disabled. + /// + public Image DisabledImage { get; set; } - /// - /// Gets or sets the of this control. - /// - public override Size Size - { - get { return base.Size; } - set { base.Size = value; sizeSet = true; } - } + /// + /// Gets or sets the of this control. + /// + public override Size Size + { + get { return base.Size; } + set { base.Size = value; sizeSet = true; } + } - /// - /// Default constructor, assigns no special attributes. - /// - public ImageButton() { } + /// + /// Default constructor, assigns no special attributes. + /// + public ImageButton() { } - /// - /// Event raised when this control is loading. - /// - /// - protected override void OnLoad(EventArgs e) + /// + /// Event raised when this control is loading. + /// + /// + protected override void OnLoad(EventArgs e) + { + base.OnLoad(e); + if (Image != null) { - base.OnLoad(e); - if (Image != null) - { - if (!sizeSet) - this.Size = Image.Size + 4; - } + if (!sizeSet) + this.Size = Image.Size + 4; } + } - /// - /// Event raised when this control has finished loading. - /// - /// - protected override void OnLoadComplete(EventArgs e) - { - base.OnLoadComplete(e); + /// + /// Event raised when this control has finished loading. + /// + /// + protected override void OnLoadComplete(EventArgs e) + { + base.OnLoadComplete(e); - if (DisabledImage == null) + if (DisabledImage == null) + { + var image = Image; + if (image != null) { - var image = Image; - if (image != null) + var disabledImage = new Bitmap(image.Size.Width, image.Size.Height, PixelFormat.Format32bppRgba); + using (var graphics = new Graphics(disabledImage)) { - var disabledImage = new Bitmap(image.Size.Width, image.Size.Height, PixelFormat.Format32bppRgba); - using (var graphics = new Graphics(disabledImage)) - { - graphics.DrawImage(image, 0, 0); - } - using (var bd = disabledImage.Lock()) + graphics.DrawImage(image, 0, 0); + } + using (var bd = disabledImage.Lock()) + { + unsafe { - unsafe + var data = (int*)bd.Data; + for (int i = 0; i < bd.ScanWidth * disabledImage.Size.Height; i++) { - var data = (int*)bd.Data; - for (int i = 0; i < bd.ScanWidth * disabledImage.Size.Height; i++) - { - var col = Color.FromArgb(bd.TranslateDataToArgb(*data)); - var gray = (col.R + col.G + col.B) / 3; - *data = bd.TranslateArgbToData(Color.FromGrayscale(gray, 0.8f).ToArgb()); - } + var col = Color.FromArgb(bd.TranslateDataToArgb(*data)); + var gray = (col.R + col.G + col.B) / 3; + *data = bd.TranslateArgbToData(Color.FromGrayscale(gray, 0.8f).ToArgb()); } } } } } - - /// - /// Event raised to draw this control. - /// - /// - protected override void OnPaint(Eto.Forms.PaintEventArgs pe) - { - var image = this.Enabled ? Image : DisabledImage; - var size = image.Size.FitTo(this.Size - 2); - var xoffset = (this.Size.Width - size.Width) / 2; - var yoffset = (this.Size.Height - size.Height) / 2; - pe.Graphics.DrawImage(image, xoffset, yoffset, size.Width, size.Height); - } } -} + /// + /// Event raised to draw this control. + /// + /// + protected override void OnPaint(Eto.Forms.PaintEventArgs pe) + { + var image = this.Enabled ? Image : DisabledImage; + var size = image.Size.FitTo(this.Size - 2); + var xoffset = (this.Size.Width - size.Width) / 2; + var yoffset = (this.Size.Height - size.Height) / 2; + pe.Graphics.DrawImage(image, xoffset, yoffset, size.Width, size.Height); + } +} \ No newline at end of file diff --git a/AM2RLauncher/AM2RLauncher/MainForm/MainForm.Events.cs b/AM2RLauncher/AM2RLauncher/MainForm/MainForm.Events.cs index aba5bdc..3d10463 100644 --- a/AM2RLauncher/AM2RLauncher/MainForm/MainForm.Events.cs +++ b/AM2RLauncher/AM2RLauncher/MainForm/MainForm.Events.cs @@ -12,948 +12,946 @@ using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; -namespace AM2RLauncher +namespace AM2RLauncher; +//TODO: comment a bunch of this stuff for readability + +/// +/// Methods for UI Events that get triggered go in here +/// +public partial class MainForm : Form { - //TODO: comment a bunch of this stuff for readability + /// This is used for to get the current Git Object during cloning. + private static int currentGitObject = 0; /// - /// Methods for UI Events that get triggered go in here + /// This is a static variable, that uses, to check if it should cancel the current git process. /// - public partial class MainForm : Form - { - /// This is used for to get the current Git Object during cloning. - private static int currentGitObject = 0; - - /// - /// This is a static variable, that uses, to check if it should cancel the current git process. - /// - private static bool isGitProcessGettingCancelled = false; + private static bool isGitProcessGettingCancelled = false; - #region Misc events + #region Misc events - /// - /// Fires when the profile layout completes loading. This makes sure that if has nothing in it "on boot", - /// that everything is disabled. - /// - private void ProfileLayoutLoadComplete(object sender, EventArgs e) - { - // Safety check - if ((modSettingsProfileDropDown == null) || (modSettingsProfileDropDown.Items.Count != 0)) return; - addModButton.Enabled = false; - settingsProfileLabel.TextColor = colInactive; - modSettingsProfileDropDown.Enabled = false; - profileButton.Enabled = false; - saveButton.Enabled = false; - updateModButton.Enabled = false; - deleteModButton.Enabled = false; - profileNotesTextArea.TextColor = colInactive; - } + /// + /// Fires when the profile layout completes loading. This makes sure that if has nothing in it "on boot", + /// that everything is disabled. + /// + private void ProfileLayoutLoadComplete(object sender, EventArgs e) + { + // Safety check + if ((modSettingsProfileDropDown == null) || (modSettingsProfileDropDown.Items.Count != 0)) return; + addModButton.Enabled = false; + settingsProfileLabel.TextColor = colInactive; + modSettingsProfileDropDown.Enabled = false; + profileButton.Enabled = false; + saveButton.Enabled = false; + updateModButton.Enabled = false; + deleteModButton.Enabled = false; + profileNotesTextArea.TextColor = colInactive; + } - /// - /// The calls this when you're resizing, in order to resize and scale the application accordingly. - /// - private void DrawablePaintEvent(object sender, PaintEventArgs e) - { - // Exit if sender is not a Drawable - Drawable drawable = sender as Drawable; - if (drawable == null) return; + /// + /// The calls this when you're resizing, in order to resize and scale the application accordingly. + /// + private void DrawablePaintEvent(object sender, PaintEventArgs e) + { + // Exit if sender is not a Drawable + Drawable drawable = sender as Drawable; + if (drawable == null) return; - // Get drawing variables - float height = drawable.Height; - float width = drawable.Width; - //TODO: apparently winforms is the big outlier here. Works normal on wpf, I have *no* idea why, seems related to our image. issue has been submitted at eto - float scaleDivisor = OS.IsWindows ? formBG.Width : formBG.Height; + // Get drawing variables + float height = drawable.Height; + float width = drawable.Width; + //TODO: apparently winforms is the big outlier here. Works normal on wpf, I have *no* idea why, seems related to our image. issue has been submitted at eto + float scaleDivisor = OS.IsWindows ? formBG.Width : formBG.Height; - float scale = height / scaleDivisor; + float scale = height / scaleDivisor; - // Do the actual scaling - e.Graphics.ScaleTransform(scale); + // Do the actual scaling + e.Graphics.ScaleTransform(scale); - // Draw the image, change x offset with some absurd wizardry written at 5 AM - e.Graphics.DrawImage(formBG, ((width / 2) - (height / 1.4745f)) / scale, 0); - } + // Draw the image, change x offset with some absurd wizardry written at 5 AM + e.Graphics.DrawImage(formBG, ((width / 2) - (height / 1.4745f)) / scale, 0); + } - /// - /// Gets called when user tries to close . This does a few things:
- /// 1) Writes the Width, Height, the check if is currently maximized and the ProfileIndex to the Config
- /// 2) Checks if current is . If yes, it creates a Warning to the end user. - ///
- private void MainFormClosing(object sender, CancelEventArgs e) - { - log.Info("Attempting to close MainForm!"); + /// + /// Gets called when user tries to close . This does a few things:
+ /// 1) Writes the Width, Height, the check if is currently maximized and the ProfileIndex to the Config
+ /// 2) Checks if current is . If yes, it creates a Warning to the end user. + ///
+ private void MainFormClosing(object sender, CancelEventArgs e) + { + log.Info("Attempting to close MainForm!"); - WriteToConfig("Width", ClientSize.Width); - WriteToConfig("Height", ClientSize.Height); - WriteToConfig("IsMaximized", WindowState == WindowState.Maximized); - WriteToConfig("ProfileIndex", profileIndex.ToString()); + WriteToConfig("Width", ClientSize.Width); + WriteToConfig("Height", ClientSize.Height); + WriteToConfig("IsMaximized", WindowState == WindowState.Maximized); + WriteToConfig("ProfileIndex", profileIndex.ToString()); - switch (updateState) + switch (updateState) + { + // If we're currently still downloading, ask first if user really wants to close and cancel the event if necessary + case PlayButtonState.Downloading: { - // If we're currently still downloading, ask first if user really wants to close and cancel the event if necessary - case PlayButtonState.Downloading: - { - var result = MessageBox.Show(this, Text.CloseOnCloningText, Text.WarningWindowTitle, MessageBoxButtons.YesNo, - MessageBoxType.Warning, MessageBoxDefaultButton.No); + var result = MessageBox.Show(this, Text.CloseOnCloningText, Text.WarningWindowTitle, MessageBoxButtons.YesNo, + MessageBoxType.Warning, MessageBoxDefaultButton.No); - if (result == DialogResult.No) - e.Cancel = true; - else - isGitProcessGettingCancelled = true; - // We don't need to delete any folders here, the cancelled gitClone will do that automatically for us :) - break; - } - // We can't close during installing, so we cancel the event. - case PlayButtonState.Installing: - { - MessageBox.Show(this, Text.CloseOnInstallingText, Text.WarningWindowTitle, MessageBoxButtons.OK, MessageBoxType.Warning); + if (result == DialogResult.No) e.Cancel = true; - break; - } + else + isGitProcessGettingCancelled = true; + // We don't need to delete any folders here, the cancelled gitClone will do that automatically for us :) + break; + } + // We can't close during installing, so we cancel the event. + case PlayButtonState.Installing: + { + MessageBox.Show(this, Text.CloseOnInstallingText, Text.WarningWindowTitle, MessageBoxButtons.OK, MessageBoxType.Warning); + e.Cancel = true; + break; } + } - // This needs to be made invisible, otherwise a tray indicator will be visible (on linux?) that clicking crashes the application - //TODO: this sounds like an eto issue. check if this can get reproduced. - trayIndicator.Visible = false; + // This needs to be made invisible, otherwise a tray indicator will be visible (on linux?) that clicking crashes the application + //TODO: this sounds like an eto issue. check if this can get reproduced. + trayIndicator.Visible = false; - if (e.Cancel) - log.Info("Cancelled MainForm closing event during UpdateState." + updateState + "."); - else - log.Info("Successfully closed MainForm. Exiting main thread."); - } + if (e.Cancel) + log.Info("Cancelled MainForm closing event during UpdateState." + updateState + "."); + else + log.Info("Successfully closed MainForm. Exiting main thread."); + } - /// Shows the and brings it to the front again. - private void ShowButtonClick(object sender, EventArgs e) - { - log.Info("User has opened the launcher from system tray."); + /// Shows the and brings it to the front again. + private void ShowButtonClick(object sender, EventArgs e) + { + log.Info("User has opened the launcher from system tray."); - Show(); - BringToFront(); - } + Show(); + BringToFront(); + } - #endregion + #endregion - #region MAIN TAB + #region MAIN TAB - /// - /// After the has been loaded, git pull if a repo has been cloned already. - /// - private async void PlayButtonLoadComplete(object sender, EventArgs e) - { - //Only pull if Patchdata is cloned and user wants it updated - LoadProfilesAndAdjustLists(); - if (!Profile.IsPatchDataCloned() || !(bool)autoUpdateAM2RCheck.Checked) - return; + /// + /// After the has been loaded, git pull if a repo has been cloned already. + /// + private async void PlayButtonLoadComplete(object sender, EventArgs e) + { + //Only pull if Patchdata is cloned and user wants it updated + LoadProfilesAndAdjustLists(); + if (!Profile.IsPatchDataCloned() || !(bool)autoUpdateAM2RCheck.Checked) + return; - SetPlayButtonState(PlayButtonState.Downloading); - EnableProgressBarAndLabel(); + SetPlayButtonState(PlayButtonState.Downloading); + EnableProgressBarAndLabel(); - // Try to pull - try + // Try to pull + try + { + log.Info("Attempting to pull repository " + currentMirror + "..."); + await Task.Run(() => Profile.PullPatchData(TransferProgressHandlerMethod)); + } + catch (UserCancelledException ex) + { + // TODO: why do we delete patchdata if user cancels pulling? + log.Info(ex.Message); + MessageBox.Show(this, Text.CorruptPatchData, Text.ErrorWindowTitle, MessageBoxType.Error); + HelperMethods.DeleteDirectory(Core.PatchDataPath); + } + // This is for any exceptions from libgit + catch (LibGit2SharpException ex) + { + string errMessage = ex.Message.ToLower(); + // Libgit2sharp error messages are always in english! + // If internet connection suddenly dropped or site not reachable + if (errMessage.Contains("failed to send request") || errMessage.Contains("connection with the server was terminated") || + errMessage.Contains("failed to resolve address")) { - log.Info("Attempting to pull repository " + currentMirror + "..."); - await Task.Run(() => Profile.PullPatchData(TransferProgressHandlerMethod)); + log.Error("Internet connection failed while attempting to pull repository" + currentMirror + "!"); + MessageBox.Show(this, Text.InternetConnectionDrop, Text.WarningWindowTitle, MessageBoxType.Warning); } - catch (UserCancelledException ex) + // Error message on protected folders. See this for more info: https://docs.microsoft.com/en-us/microsoft-365/security/defender-endpoint/controlled-folders + else if (errMessage.Contains("access is denied")) { - // TODO: why do we delete patchdata if user cancels pulling? - log.Info(ex.Message); - MessageBox.Show(this, Text.CorruptPatchData, Text.ErrorWindowTitle, MessageBoxType.Error); - HelperMethods.DeleteDirectory(Core.PatchDataPath); + // Needs localizable text, logging and message box + // Also, check if this is the right place for it. + throw new NotImplementedException(); } - // This is for any exceptions from libgit - catch (LibGit2SharpException ex) - { - string errMessage = ex.Message.ToLower(); - // Libgit2sharp error messages are always in english! - // If internet connection suddenly dropped or site not reachable - if (errMessage.Contains("failed to send request") || errMessage.Contains("connection with the server was terminated") || - errMessage.Contains("failed to resolve address")) - { - log.Error("Internet connection failed while attempting to pull repository" + currentMirror + "!"); - MessageBox.Show(this, Text.InternetConnectionDrop, Text.WarningWindowTitle, MessageBoxType.Warning); - } - // Error message on protected folders. See this for more info: https://docs.microsoft.com/en-us/microsoft-365/security/defender-endpoint/controlled-folders - else if (errMessage.Contains("access is denied")) - { - // Needs localizable text, logging and message box - // Also, check if this is the right place for it. - throw new NotImplementedException(); - } - else - { - log.Error(ex.Message + "\n*****Stack Trace*****\n\n" + ex.StackTrace); - MessageBox.Show(this, ex.Message + "\n*****Stack Trace*****\n\n" + ex.StackTrace, Text.ErrorWindowTitle, MessageBoxType.Error); - } - } - // This is if somehow any other exception might get thrown as well. - catch (Exception ex) + else { log.Error(ex.Message + "\n*****Stack Trace*****\n\n" + ex.StackTrace); MessageBox.Show(this, ex.Message + "\n*****Stack Trace*****\n\n" + ex.StackTrace, Text.ErrorWindowTitle, MessageBoxType.Error); } - // At the end of everything, reset progressBar controls - finally - { - DisableProgressBarAndProgressLabel(); - LoadProfilesAndAdjustLists(); - } + } + // This is if somehow any other exception might get thrown as well. + catch (Exception ex) + { + log.Error(ex.Message + "\n*****Stack Trace*****\n\n" + ex.StackTrace); + MessageBox.Show(this, ex.Message + "\n*****Stack Trace*****\n\n" + ex.StackTrace, Text.ErrorWindowTitle, MessageBoxType.Error); + } + // At the end of everything, reset progressBar controls + finally + { + DisableProgressBarAndProgressLabel(); + LoadProfilesAndAdjustLists(); + } - // Handling for updates - if current version does not match PatchData version, rename folder so that we attempt to install! - // Also, add a non-installable profile for it so people can access the older version or delete it from the mod manager. - if ((profileList.Count > 0) && Profile.IsProfileInstalled(profileList[0])) - { - ProfileXML installedUpdatesProfile = Serializer.Deserialize(File.ReadAllText(Core.ProfilesPath + "/Community Updates (Latest)/profile.xml")); + // Handling for updates - if current version does not match PatchData version, rename folder so that we attempt to install! + // Also, add a non-installable profile for it so people can access the older version or delete it from the mod manager. + if ((profileList.Count > 0) && Profile.IsProfileInstalled(profileList[0])) + { + ProfileXML installedUpdatesProfile = Serializer.Deserialize(File.ReadAllText(Core.ProfilesPath + "/Community Updates (Latest)/profile.xml")); - if (installedUpdatesProfile.Version != profileList[0].Version) - { - log.Info("New game version (" + profileList[0].Version + ") detected! Beginning archival of version " + installedUpdatesProfile.Version + "..."); - Profile.ArchiveProfile(installedUpdatesProfile); - profileDropDown.SelectedIndex = 0; - LoadProfilesAndAdjustLists(); - } + if (installedUpdatesProfile.Version != profileList[0].Version) + { + log.Info("New game version (" + profileList[0].Version + ") detected! Beginning archival of version " + installedUpdatesProfile.Version + "..."); + Profile.ArchiveProfile(installedUpdatesProfile); + profileDropDown.SelectedIndex = 0; + LoadProfilesAndAdjustLists(); } - - SetPlayButtonState(PlayButtonState.Install); - UpdateStateMachine(); } - /// - /// Does a bunch of stuff, depending on the current state of . - /// - private async void PlayButtonClickEvent(object sender, EventArgs e) - { - // State Check - UpdateStateMachine(); + SetPlayButtonState(PlayButtonState.Install); + UpdateStateMachine(); + } - // Check if 1.1 is installed by forcing invalidation - Profile.Is11Installed(true); + /// + /// Does a bunch of stuff, depending on the current state of . + /// + private async void PlayButtonClickEvent(object sender, EventArgs e) + { + // State Check + UpdateStateMachine(); - switch (updateState) - { - #region Download - case PlayButtonState.Download: + // Check if 1.1 is installed by forcing invalidation + Profile.Is11Installed(true); - log.Info("Attempting to clone repository " + currentMirror + "..."); - bool successful = true; + switch (updateState) + { + #region Download + case PlayButtonState.Download: - // Update playButton states and progress controls - SetPlayButtonState(PlayButtonState.Downloading); - EnableProgressBarAndLabel(); + log.Info("Attempting to clone repository " + currentMirror + "..."); + bool successful = true; - // Set up progressBar update method - CloneOptions cloneOptions = new CloneOptions { OnTransferProgress = TransferProgressHandlerMethod }; + // Update playButton states and progress controls + SetPlayButtonState(PlayButtonState.Downloading); + EnableProgressBarAndLabel(); - // Try to clone - try - { - // Cleanup invalid PatchData directory if it exists - if (Directory.Exists(Core.PatchDataPath)) - { - log.Info("PatchData directory already exists, cleaning up..."); - HelperMethods.DeleteDirectory(Core.PatchDataPath); - } + // Set up progressBar update method + CloneOptions cloneOptions = new CloneOptions { OnTransferProgress = TransferProgressHandlerMethod }; - // Separate thread so launcher doesn't get locked - await Task.Run(() => Repository.Clone(currentMirror, Core.PatchDataPath, cloneOptions)); - } - // We deliberately cancelled this, so no error handling - catch (UserCancelledException) + // Try to clone + try + { + // Cleanup invalid PatchData directory if it exists + if (Directory.Exists(Core.PatchDataPath)) { - successful = false; + log.Info("PatchData directory already exists, cleaning up..."); + HelperMethods.DeleteDirectory(Core.PatchDataPath); } - //TODO: this is currently copy-pasted with the PullPatchData method. put this into a separate method. - // For any exceptions from libgit - catch (LibGit2SharpException ex) + + // Separate thread so launcher doesn't get locked + await Task.Run(() => Repository.Clone(currentMirror, Core.PatchDataPath, cloneOptions)); + } + // We deliberately cancelled this, so no error handling + catch (UserCancelledException) + { + successful = false; + } + //TODO: this is currently copy-pasted with the PullPatchData method. put this into a separate method. + // For any exceptions from libgit + catch (LibGit2SharpException ex) + { + // Libgit2sharp error messages are always in english! + if (ex.Message.ToLower().Contains("failed to send request") || ex.Message.ToLower().Contains("connection with the server was terminated") || + ex.Message.ToLower().Contains("failed to resolve address")) { - // Libgit2sharp error messages are always in english! - if (ex.Message.ToLower().Contains("failed to send request") || ex.Message.ToLower().Contains("connection with the server was terminated") || - ex.Message.ToLower().Contains("failed to resolve address")) - { - log.Error("Internet connection dropped while attempting to clone repository" + currentMirror + "!"); - MessageBox.Show(this, Text.InternetConnectionDrop, Text.WarningWindowTitle, MessageBoxType.Warning); - } - else - { - log.Error("LibGit2SharpException: " + ex.Message + "\n*****Stack Trace*****\n\n" + ex.StackTrace); - MessageBox.Show(this, ex.Message + "\n*****Stack Trace*****\n\n" + ex.StackTrace, Text.ErrorWindowTitle, MessageBoxType.Error); - if (Directory.Exists(Core.PatchDataPath)) - HelperMethods.DeleteDirectory(Core.PatchDataPath); - } - successful = false; + log.Error("Internet connection dropped while attempting to clone repository" + currentMirror + "!"); + MessageBox.Show(this, Text.InternetConnectionDrop, Text.WarningWindowTitle, MessageBoxType.Warning); } - // This is if somehow any other exception might get thrown as well. - catch (Exception ex) + else { - log.Error(ex.Message + "\n*****Stack Trace*****\n\n" + ex.StackTrace); + log.Error("LibGit2SharpException: " + ex.Message + "\n*****Stack Trace*****\n\n" + ex.StackTrace); MessageBox.Show(this, ex.Message + "\n*****Stack Trace*****\n\n" + ex.StackTrace, Text.ErrorWindowTitle, MessageBoxType.Error); - - if (Directory.Exists(CrossPlatformOperations.CurrentPath + " / PatchData")) + if (Directory.Exists(Core.PatchDataPath)) HelperMethods.DeleteDirectory(Core.PatchDataPath); - successful = false; } + successful = false; + } + // This is if somehow any other exception might get thrown as well. + catch (Exception ex) + { + log.Error(ex.Message + "\n*****Stack Trace*****\n\n" + ex.StackTrace); + MessageBox.Show(this, ex.Message + "\n*****Stack Trace*****\n\n" + ex.StackTrace, Text.ErrorWindowTitle, MessageBoxType.Error); - log.Info("Repository clone attempt finished " + (successful ? "successfully." : "unsuccessfully.")); - - currentGitObject = 0; - - // Reset progressBar after clone is finished - DisableProgressBarAndProgressLabel(); + if (Directory.Exists(CrossPlatformOperations.CurrentPath + " / PatchData")) + HelperMethods.DeleteDirectory(Core.PatchDataPath); + successful = false; + } - // Just need to switch this to anything that isn't an "active" state so SetUpdateState() actually does something - SetPlayButtonState(PlayButtonState.Install); + log.Info("Repository clone attempt finished " + (successful ? "successfully." : "unsuccessfully.")); - // This needs to be run BEFORE the state check so that the Mod Settings tab doesn't weird out - LoadProfilesAndAdjustLists(); + currentGitObject = 0; - // Do a state check - UpdateStateMachine(); + // Reset progressBar after clone is finished + DisableProgressBarAndProgressLabel(); - break; - #endregion + // Just need to switch this to anything that isn't an "active" state so SetUpdateState() actually does something + SetPlayButtonState(PlayButtonState.Install); - #region Downloading + // This needs to be run BEFORE the state check so that the Mod Settings tab doesn't weird out + LoadProfilesAndAdjustLists(); - case PlayButtonState.Downloading: - DialogResult result = MessageBox.Show(this, Text.CloseOnCloningText, Text.WarningWindowTitle, MessageBoxButtons.YesNo, - MessageBoxType.Warning, MessageBoxDefaultButton.No); - if (result != DialogResult.Yes) - return; + // Do a state check + UpdateStateMachine(); - log.Info("User cancelled download!"); - isGitProcessGettingCancelled = true; + break; + #endregion - // We don't need to delete any folders here, the cancelled gitClone will do that automatically for us :) - // But we should probably wait a bit before proceeding, since cleanup can take a while - Thread.Sleep(1000); - isGitProcessGettingCancelled = false; - break; + #region Downloading - #endregion + case PlayButtonState.Downloading: + DialogResult result = MessageBox.Show(this, Text.CloseOnCloningText, Text.WarningWindowTitle, MessageBoxButtons.YesNo, + MessageBoxType.Warning, MessageBoxDefaultButton.No); + if (result != DialogResult.Yes) + return; - #region Select11 - case PlayButtonState.Select11: + log.Info("User cancelled download!"); + isGitProcessGettingCancelled = true; - log.Info("Requesting user input for AM2R_11.zip..."); + // We don't need to delete any folders here, the cancelled gitClone will do that automatically for us :) + // But we should probably wait a bit before proceeding, since cleanup can take a while + Thread.Sleep(1000); + isGitProcessGettingCancelled = false; + break; - OpenFileDialog fileFinder = GetSingleZipDialog(Text.Select11FileDialog); - if (fileFinder.ShowDialog(this) != DialogResult.Ok) - { - log.Info("User cancelled the selection."); - return; - } + #endregion - // Default filename is whitespace - if (String.IsNullOrWhiteSpace(fileFinder.FileName)) - { - log.Error("User did not supply valid input. Cancelling import."); - return; - } + #region Select11 + case PlayButtonState.Select11: - // If either a directory was selected or the file somehow went missing, cancel - if (!File.Exists(fileFinder.FileName)) - { - log.Error("Selected AM2R_11.zip file not found! Cancelling import."); - return; - } + log.Info("Requesting user input for AM2R_11.zip..."); - IsZipAM2R11ReturnCodes errorCode = Profile.CheckIfZipIsAM2R11(fileFinder.FileName); - if (errorCode != IsZipAM2R11ReturnCodes.Successful) - { - log.Error("User tried to input invalid AM2R_11.zip file (" + errorCode + "). Cancelling import."); - MessageBox.Show(this, Text.ZipIsNotAM2R11 + "\n\nError Code: " + errorCode, Text.ErrorWindowTitle, MessageBoxType.Error); - return; - } + OpenFileDialog fileFinder = GetSingleZipDialog(Text.Select11FileDialog); + if (fileFinder.ShowDialog(this) != DialogResult.Ok) + { + log.Info("User cancelled the selection."); + return; + } - // We check if it exists first, because someone coughDRUIDcough might've copied it into here while on the showDialog - if (fileFinder.FileName != Core.AM2R11File) - File.Copy(fileFinder.FileName, Core.AM2R11File); + // Default filename is whitespace + if (String.IsNullOrWhiteSpace(fileFinder.FileName)) + { + log.Error("User did not supply valid input. Cancelling import."); + return; + } - log.Info("AM2R_11.zip successfully imported."); - UpdateStateMachine(); - break; - #endregion + // If either a directory was selected or the file somehow went missing, cancel + if (!File.Exists(fileFinder.FileName)) + { + log.Error("Selected AM2R_11.zip file not found! Cancelling import."); + return; + } - #region Install - case PlayButtonState.Install: - EnableProgressBar(); - SetPlayButtonState(PlayButtonState.Installing); + IsZipAM2R11ReturnCodes errorCode = Profile.CheckIfZipIsAM2R11(fileFinder.FileName); + if (errorCode != IsZipAM2R11ReturnCodes.Successful) + { + log.Error("User tried to input invalid AM2R_11.zip file (" + errorCode + "). Cancelling import."); + MessageBox.Show(this, Text.ZipIsNotAM2R11 + "\n\nError Code: " + errorCode, Text.ErrorWindowTitle, MessageBoxType.Error); + return; + } - // Make sure the main interface state machines properly - UpdateApkState(); - UpdateProfileState(); + // We check if it exists first, because someone coughDRUIDcough might've copied it into here while on the showDialog + if (fileFinder.FileName != Core.AM2R11File) + File.Copy(fileFinder.FileName, Core.AM2R11File); - // If the file cannot be launched due to anti-virus shenanigans or any other reason, we try catch here - try - { - // Check if xdelta is installed on unix and exit if not - if (OS.IsUnix && !CrossPlatformOperations.CheckIfXdeltaIsInstalled()) - { - MessageBox.Show(this, Text.XdeltaNotFound, Text.WarningWindowTitle, MessageBoxButtons.OK); - SetPlayButtonState(PlayButtonState.Install); - UpdateStateMachine(); - log.Error("Xdelta not found. Aborting installing a profile..."); - return; - } - Progress progressIndicator = new Progress(UpdateProgressBar); - bool useHqMusic = hqMusicPCCheck.Checked.Value; - await Task.Run(() => Profile.InstallProfile(profileList[profileIndex.Value], useHqMusic, progressIndicator)); - // This is just for visuals because the average windows end user will ask why it doesn't go to the end otherwise. - if (OS.IsWindows) - Thread.Sleep(500); - } - catch (Exception ex) - { - log.Error(ex.Message + "\n*****Stack Trace*****\n\n" + ex.StackTrace); - MessageBox.Show(this, ex.Message + "\n*****Stack Trace*****\n\n" + ex.StackTrace, Text.ErrorWindowTitle, MessageBoxType.Error); - } - DisableProgressBar(); + log.Info("AM2R_11.zip successfully imported."); + UpdateStateMachine(); + break; + #endregion - // Just need to switch this to anything that isn't an "active" state so SetUpdateState() actually does something - SetPlayButtonState(PlayButtonState.Play); - UpdateStateMachine(); - break; - #endregion + #region Install + case PlayButtonState.Install: + EnableProgressBar(); + SetPlayButtonState(PlayButtonState.Installing); - #region Play - case PlayButtonState.Play: + // Make sure the main interface state machines properly + UpdateApkState(); + UpdateProfileState(); - if (!IsProfileIndexValid()) + // If the file cannot be launched due to anti-virus shenanigans or any other reason, we try catch here + try + { + // Check if xdelta is installed on unix and exit if not + if (OS.IsUnix && !CrossPlatformOperations.CheckIfXdeltaIsInstalled()) + { + MessageBox.Show(this, Text.XdeltaNotFound, Text.WarningWindowTitle, MessageBoxButtons.OK); + SetPlayButtonState(PlayButtonState.Install); + UpdateStateMachine(); + log.Error("Xdelta not found. Aborting installing a profile..."); return; + } + Progress progressIndicator = new Progress(UpdateProgressBar); + bool useHqMusic = hqMusicPCCheck.Checked.Value; + await Task.Run(() => Profile.InstallProfile(profileList[profileIndex.Value], useHqMusic, progressIndicator)); + // This is just for visuals because the average windows end user will ask why it doesn't go to the end otherwise. + if (OS.IsWindows) + Thread.Sleep(500); + } + catch (Exception ex) + { + log.Error(ex.Message + "\n*****Stack Trace*****\n\n" + ex.StackTrace); + MessageBox.Show(this, ex.Message + "\n*****Stack Trace*****\n\n" + ex.StackTrace, Text.ErrorWindowTitle, MessageBoxType.Error); + } + DisableProgressBar(); - ProfileXML profile = profileList[profileIndex.Value]; - Visible = false; - SetPlayButtonState(PlayButtonState.Playing); - - // Make sure the main interface state machines properly - UpdateApkState(); - UpdateProfileState(); + // Just need to switch this to anything that isn't an "active" state so SetUpdateState() actually does something + SetPlayButtonState(PlayButtonState.Play); + UpdateStateMachine(); + break; + #endregion - ShowInTaskbar = false; - trayIndicator.Visible = true; - WindowState windowStateBeforeLaunching = WindowState; - Minimize(); + #region Play + case PlayButtonState.Play: - string envVarText = customEnvVarTextBox?.Text; - bool createDebugLogs = profileDebugLogCheck.Checked.Value; + if (!IsProfileIndexValid()) + return; - await Task.Run(() => Profile.RunGame(profile, createDebugLogs, envVarText)); + ProfileXML profile = profileList[profileIndex.Value]; + Visible = false; + SetPlayButtonState(PlayButtonState.Playing); - ShowInTaskbar = true; - trayIndicator.Visible = false; - Show(); - BringToFront(); - Visible = true; - WindowState = windowStateBeforeLaunching; + // Make sure the main interface state machines properly + UpdateApkState(); + UpdateProfileState(); - SetPlayButtonState(PlayButtonState.Play); - UpdateStateMachine(); - break; + ShowInTaskbar = false; + trayIndicator.Visible = true; + WindowState windowStateBeforeLaunching = WindowState; + Minimize(); - #endregion + string envVarText = customEnvVarTextBox?.Text; + bool createDebugLogs = profileDebugLogCheck.Checked.Value; - default: log.Error("Encountered invalid update state: " + updateState + "!"); break; - } - } + await Task.Run(() => Profile.RunGame(profile, createDebugLogs, envVarText)); - /// - /// Does stuff, depending on the current state of . - /// - private async void ApkButtonClickEvent(object sender, EventArgs e) - { - // If we're currently creating something, exit - if (apkButtonState == ApkButtonState.Creating) return; + ShowInTaskbar = true; + trayIndicator.Visible = false; + Show(); + BringToFront(); + Visible = true; + WindowState = windowStateBeforeLaunching; - // Check for java, exit safely with a warning if not found! - if (!CrossPlatformOperations.IsJavaInstalled()) - { - MessageBox.Show(this, Text.JavaNotFound, Text.WarningWindowTitle, MessageBoxButtons.OK); - SetApkButtonState(ApkButtonState.Create); + SetPlayButtonState(PlayButtonState.Play); UpdateStateMachine(); - log.Error("Java not found! Aborting Android APK creation."); - return; - } + break; - // Check if xdelta is installed on unix, exit with a warning if not. - if (OS.IsUnix && !CrossPlatformOperations.CheckIfXdeltaIsInstalled()) - { - MessageBox.Show(this, Text.XdeltaNotFound, Text.WarningWindowTitle, MessageBoxButtons.OK); - SetApkButtonState(ApkButtonState.Create); - UpdateStateMachine(); - log.Error("Xdelta not found. Aborting Android APK creation..."); - return; - } + #endregion - UpdateStateMachine(); + default: log.Error("Encountered invalid update state: " + updateState + "!"); break; + } + } - if (apkButtonState != ApkButtonState.Create) return; + /// + /// Does stuff, depending on the current state of . + /// + private async void ApkButtonClickEvent(object sender, EventArgs e) + { + // If we're currently creating something, exit + if (apkButtonState == ApkButtonState.Creating) return; - SetApkButtonState(ApkButtonState.Creating); + // Check for java, exit safely with a warning if not found! + if (!CrossPlatformOperations.IsJavaInstalled()) + { + MessageBox.Show(this, Text.JavaNotFound, Text.WarningWindowTitle, MessageBoxButtons.OK); + SetApkButtonState(ApkButtonState.Create); UpdateStateMachine(); + log.Error("Java not found! Aborting Android APK creation."); + return; + } - EnableProgressBar(); - bool useHqMusic = hqMusicAndroidCheck.Checked.Value; - - Progress progressIndicator = new Progress(UpdateProgressBar); - await Task.Run(() => Profile.CreateAPK(profileList[profileIndex.Value], useHqMusic, progressIndicator)); - + // Check if xdelta is installed on unix, exit with a warning if not. + if (OS.IsUnix && !CrossPlatformOperations.CheckIfXdeltaIsInstalled()) + { + MessageBox.Show(this, Text.XdeltaNotFound, Text.WarningWindowTitle, MessageBoxButtons.OK); SetApkButtonState(ApkButtonState.Create); - DisableProgressBar(); UpdateStateMachine(); + log.Error("Xdelta not found. Aborting Android APK creation..."); + return; } - /// Gets called when user selects a different item from and changes accordingly. - private void ProfileDropDownSelectedIndexChanged(object sender, EventArgs e) - { - if ((profileDropDown.SelectedIndex == -1) && (profileDropDown.Items.Count == 0)) return; + UpdateStateMachine(); - profileIndex = profileDropDown.SelectedIndex; - log.Debug("profileDropDown.SelectedIndex has been changed to " + profileIndex + "."); + if (apkButtonState != ApkButtonState.Create) return; - profileAuthorLabel.Text = Text.Author + " " + profileList[profileDropDown.SelectedIndex].Author; - profileVersionLabel.Text = Text.VersionLabel + " " + profileList[profileDropDown.SelectedIndex].Version; + SetApkButtonState(ApkButtonState.Creating); + UpdateStateMachine(); - if ((profileDropDown.SelectedIndex != 0) && ((profileList[profileDropDown.SelectedIndex].SaveLocation == "%localappdata%/AM2R") || - (profileList[profileDropDown.SelectedIndex].SaveLocation == "default"))) - saveWarningLabel.Visible = true; - else - saveWarningLabel.Visible = false; + EnableProgressBar(); + bool useHqMusic = hqMusicAndroidCheck.Checked.Value; - UpdateStateMachine(); - } + Progress progressIndicator = new Progress(UpdateProgressBar); + await Task.Run(() => Profile.CreateAPK(profileList[profileIndex.Value], useHqMusic, progressIndicator)); - #endregion + SetApkButtonState(ApkButtonState.Create); + DisableProgressBar(); + UpdateStateMachine(); + } - /// - /// If no internet access is available, this changes the content of to an empty page only displaying . - /// - /// The to change the contents of. - /// The that should be displayed. - private void ChangeToEmptyPageOnNoInternet(TabPage tabPage, Label errorLabel) - { - if (isInternetThere) - return; + /// Gets called when user selects a different item from and changes accordingly. + private void ProfileDropDownSelectedIndexChanged(object sender, EventArgs e) + { + if ((profileDropDown.SelectedIndex == -1) && (profileDropDown.Items.Count == 0)) return; - tabPage.Content = new TableLayout - { - Rows = - { - null, - errorLabel, - null - } - }; - } + profileIndex = profileDropDown.SelectedIndex; + log.Debug("profileDropDown.SelectedIndex has been changed to " + profileIndex + "."); - #region SETTINGS + profileAuthorLabel.Text = Text.Author + " " + profileList[profileDropDown.SelectedIndex].Author; + profileVersionLabel.Text = Text.VersionLabel + " " + profileList[profileDropDown.SelectedIndex].Version; - /// Gets called when user selects a different item from and writes that to the config. - private void LanguageDropDownSelectedIndexChanged(object sender, EventArgs e) - { - log.Info("languageDropDown.SelectedIndex has been changed to " + languageDropDown.SelectedIndex + "."); - WriteToConfig("Language", languageDropDown.SelectedIndex == 0 ? "Default" : languageDropDown.Items[languageDropDown.SelectedIndex].Text); - } + if ((profileDropDown.SelectedIndex != 0) && ((profileList[profileDropDown.SelectedIndex].SaveLocation == "%localappdata%/AM2R") || + (profileList[profileDropDown.SelectedIndex].SaveLocation == "default"))) + saveWarningLabel.Visible = true; + else + saveWarningLabel.Visible = false; - /// Gets called when gets clicked and writes its new value to the config. - private void AutoUpdateAM2RCheckChanged(object sender, EventArgs e) - { - log.Info("Auto Update AM2R has been set to " + autoUpdateAM2RCheck.Checked + "."); - WriteToConfig("AutoUpdateAM2R", (bool)autoUpdateAM2RCheck.Checked); - } + UpdateStateMachine(); + } - /// Gets called when gets clicked and writes its new value to the config. - private void AutoUpdateLauncherCheckChanged(object sender, EventArgs e) - { - log.Info("Auto Update Launcher has been set to " + autoUpdateAM2RCheck.Checked + "."); - WriteToConfig("AutoUpdateLauncher", (bool)autoUpdateAM2RCheck.Checked); - } + #endregion - /// Gets called when gets clicked and writes its new value to the config. - private void HqMusicPCCheckChanged(object sender, EventArgs e) - { - log.Info("PC HQ Music option has been changed to " + hqMusicPCCheck.Checked); - WriteToConfig("MusicHQPC", hqMusicPCCheck.Checked); - } + /// + /// If no internet access is available, this changes the content of to an empty page only displaying . + /// + /// The to change the contents of. + /// The that should be displayed. + private void ChangeToEmptyPageOnNoInternet(TabPage tabPage, Label errorLabel) + { + if (isInternetThere) + return; - /// Gets called when gets clicked and writes its new value to the config. - private void HqMusicAndroidCheckChanged(object sender, EventArgs e) + tabPage.Content = new TableLayout { - log.Info("Android HQ Music option has been changed to " + hqMusicAndroidCheck.Checked); - WriteToConfig("MusicHQAndroid", hqMusicAndroidCheck.Checked); - } + Rows = + { + null, + errorLabel, + null + } + }; + } - /// - /// Gets called when gets clicked, and writes it's new value to the config. - /// - private void ProfileDebugLogCheckedChanged(object sender, EventArgs e) - { - log.Info("Create Game Debug Logs option has been set to " + profileDebugLogCheck.Checked + "."); - WriteToConfig("ProfileDebugLog", profileDebugLogCheck.Checked); - } + #region SETTINGS - /// Gets called when user selects a different item from . - /// It then writes that to the config, and if is not - /// it also overwrites the upstream URL in .git/config. - private void MirrorDropDownSelectedIndexChanged(object sender, EventArgs e) - { - currentMirror = mirrorList[mirrorDropDown.SelectedIndex]; + /// Gets called when user selects a different item from and writes that to the config. + private void LanguageDropDownSelectedIndexChanged(object sender, EventArgs e) + { + log.Info("languageDropDown.SelectedIndex has been changed to " + languageDropDown.SelectedIndex + "."); + WriteToConfig("Language", languageDropDown.SelectedIndex == 0 ? "Default" : languageDropDown.Items[languageDropDown.SelectedIndex].Text); + } + + /// Gets called when gets clicked and writes its new value to the config. + private void AutoUpdateAM2RCheckChanged(object sender, EventArgs e) + { + log.Info("Auto Update AM2R has been set to " + autoUpdateAM2RCheck.Checked + "."); + WriteToConfig("AutoUpdateAM2R", (bool)autoUpdateAM2RCheck.Checked); + } - log.Info("Current mirror has been set to " + currentMirror + "."); + /// Gets called when gets clicked and writes its new value to the config. + private void AutoUpdateLauncherCheckChanged(object sender, EventArgs e) + { + log.Info("Auto Update Launcher has been set to " + autoUpdateAM2RCheck.Checked + "."); + WriteToConfig("AutoUpdateLauncher", (bool)autoUpdateAM2RCheck.Checked); + } - WriteToConfig("MirrorIndex", mirrorDropDown.SelectedIndex); + /// Gets called when gets clicked and writes its new value to the config. + private void HqMusicPCCheckChanged(object sender, EventArgs e) + { + log.Info("PC HQ Music option has been changed to " + hqMusicPCCheck.Checked); + WriteToConfig("MusicHQPC", hqMusicPCCheck.Checked); + } - // Don't overwrite the git config while we download!!! - if (updateState == PlayButtonState.Downloading) return; + /// Gets called when gets clicked and writes its new value to the config. + private void HqMusicAndroidCheckChanged(object sender, EventArgs e) + { + log.Info("Android HQ Music option has been changed to " + hqMusicAndroidCheck.Checked); + WriteToConfig("MusicHQAndroid", hqMusicAndroidCheck.Checked); + } - log.Info("Overwriting mirror in gitconfig."); + /// + /// Gets called when gets clicked, and writes it's new value to the config. + /// + private void ProfileDebugLogCheckedChanged(object sender, EventArgs e) + { + log.Info("Create Game Debug Logs option has been set to " + profileDebugLogCheck.Checked + "."); + WriteToConfig("ProfileDebugLog", profileDebugLogCheck.Checked); + } - // Check if the gitConfig exists, if yes regex the gitURL, and replace it with the new current Mirror. - string gitConfigPath = Core.PatchDataPath + "/.git/config"; - if (!File.Exists(gitConfigPath)) return; + /// Gets called when user selects a different item from . + /// It then writes that to the config, and if is not + /// it also overwrites the upstream URL in .git/config. + private void MirrorDropDownSelectedIndexChanged(object sender, EventArgs e) + { + currentMirror = mirrorList[mirrorDropDown.SelectedIndex]; - string gitConfig = File.ReadAllText(gitConfigPath); - Regex gitURLRegex = new Regex("https://.*\\.git"); - Match match = gitURLRegex.Match(gitConfig); - gitConfig = gitConfig.Replace(match.Value, currentMirror); - File.WriteAllText(gitConfigPath, gitConfig); - } + log.Info("Current mirror has been set to " + currentMirror + "."); - /// Gets called when gets clicked, displays a warning - /// and enables accordingly. - private void CustomMirrorCheckChanged(object sender, EventArgs e) - { - log.Info("Use Custom Mirror option has been set to " + customMirrorCheck.Checked + "."); - WriteToConfig("CustomMirrorEnabled", (bool)customMirrorCheck.Checked); + WriteToConfig("MirrorIndex", mirrorDropDown.SelectedIndex); - EnableMirrorControlsAccordingly(); + // Don't overwrite the git config while we download!!! + if (updateState == PlayButtonState.Downloading) return; - // Create warning dialog when enabling - if ((bool)customMirrorCheck.Checked) - { - MessageBox.Show(this, Text.WarningWindowText, Text.WarningWindowTitle, MessageBoxType.Warning); - currentMirror = customMirrorTextBox.Text; - } - else - { - // Revert mirror to selected index in mirror dropdown - currentMirror = mirrorList[mirrorDropDown.SelectedIndex]; - } - } + log.Info("Overwriting mirror in gitconfig."); - /// - /// If the has lost focus, we set its text as the new . - /// - private void CustomMirrorTextBoxLostFocus(object sender, EventArgs e) - { - // Check first, if the text is a valid git repo - Regex gitURLRegex = new Regex("https://.*\\.git"); - string mirrorText = customMirrorTextBox.Text; - if (!gitURLRegex.IsMatch(mirrorText)) - { - log.Info("User used " + mirrorText + " as a custom Mirror, didn't pass git validation test."); - MessageBox.Show(this, HelperMethods.GetText(Text.InvalidGitURL, mirrorText), Text.ErrorWindowTitle, MessageBoxType.Error); - return; - } + // Check if the gitConfig exists, if yes regex the gitURL, and replace it with the new current Mirror. + string gitConfigPath = Core.PatchDataPath + "/.git/config"; + if (!File.Exists(gitConfigPath)) return; - currentMirror = mirrorText; - WriteToConfig("CustomMirrorText", currentMirror); + string gitConfig = File.ReadAllText(gitConfigPath); + Regex gitURLRegex = new Regex("https://.*\\.git"); + Match match = gitURLRegex.Match(gitConfig); + gitConfig = gitConfig.Replace(match.Value, currentMirror); + File.WriteAllText(gitConfigPath, gitConfig); + } - log.Info("Overwriting mirror in gitconfig."); + /// Gets called when gets clicked, displays a warning + /// and enables accordingly. + private void CustomMirrorCheckChanged(object sender, EventArgs e) + { + log.Info("Use Custom Mirror option has been set to " + customMirrorCheck.Checked + "."); + WriteToConfig("CustomMirrorEnabled", (bool)customMirrorCheck.Checked); - // Check if the gitConfig exists, if yes regex the gitURL, and replace it with the new current Mirror. - string gitConfigPath = Core.PatchDataPath + "/.git/config"; - if (!File.Exists(gitConfigPath)) return; - string gitConfig = File.ReadAllText(gitConfigPath); - Match match = gitURLRegex.Match(gitConfig); - gitConfig = gitConfig.Replace(match.Value, currentMirror); - File.WriteAllText(gitConfigPath, gitConfig); + EnableMirrorControlsAccordingly(); - log.Info("Custom Mirror has been set to " + currentMirror + "."); + // Create warning dialog when enabling + if ((bool)customMirrorCheck.Checked) + { + MessageBox.Show(this, Text.WarningWindowText, Text.WarningWindowTitle, MessageBoxType.Warning); + currentMirror = customMirrorTextBox.Text; + } + else + { + // Revert mirror to selected index in mirror dropdown + currentMirror = mirrorList[mirrorDropDown.SelectedIndex]; } + } - /// - /// If has lost focus, we write its text to the config. - /// - private void CustomEnvVarTextBoxLostFocus(object sender, EventArgs e) + /// + /// If the has lost focus, we set its text as the new . + /// + private void CustomMirrorTextBoxLostFocus(object sender, EventArgs e) + { + // Check first, if the text is a valid git repo + Regex gitURLRegex = new Regex("https://.*\\.git"); + string mirrorText = customMirrorTextBox.Text; + if (!gitURLRegex.IsMatch(mirrorText)) { - log.Info("Custom Environment variables have been set to \"" + customEnvVarTextBox.Text + "\"."); - WriteToConfig("CustomEnvVar", customEnvVarTextBox.Text); + log.Info("User used " + mirrorText + " as a custom Mirror, didn't pass git validation test."); + MessageBox.Show(this, HelperMethods.GetText(Text.InvalidGitURL, mirrorText), Text.ErrorWindowTitle, MessageBoxType.Error); + return; } - #endregion + currentMirror = mirrorText; + WriteToConfig("CustomMirrorText", currentMirror); - #region MOD SETTINGS + log.Info("Overwriting mirror in gitconfig."); - /// - /// Runs when is clicked. Brings up a file select to select a mod, and adds that to the mod directory. - /// - private void AddModButtonClicked(object sender, EventArgs e) - { - log.Info("User requested to add mod. Requesting user input for new mod .zip..."); + // Check if the gitConfig exists, if yes regex the gitURL, and replace it with the new current Mirror. + string gitConfigPath = Core.PatchDataPath + "/.git/config"; + if (!File.Exists(gitConfigPath)) return; + string gitConfig = File.ReadAllText(gitConfigPath); + Match match = gitURLRegex.Match(gitConfig); + gitConfig = gitConfig.Replace(match.Value, currentMirror); + File.WriteAllText(gitConfigPath, gitConfig); - OpenFileDialog fileFinder = GetSingleZipDialog(Text.SelectModFileDialog); + log.Info("Custom Mirror has been set to " + currentMirror + "."); + } - // If user didn't press ok, cancel - if (fileFinder.ShowDialog(this) != DialogResult.Ok) - { - log.Info("User cancelled the Mod selection."); - return; - } + /// + /// If has lost focus, we write its text to the config. + /// + private void CustomEnvVarTextBoxLostFocus(object sender, EventArgs e) + { + log.Info("Custom Environment variables have been set to \"" + customEnvVarTextBox.Text + "\"."); + WriteToConfig("CustomEnvVar", customEnvVarTextBox.Text); + } - log.Info("User selected \"" + fileFinder.FileName + "\""); + #endregion - // If either a directory was selected, user pressed OK without selecting anything or the file somehow went missing, cancel - if (!File.Exists(fileFinder.FileName)) - { - log.Error("Selected mod .zip file not found! Cancelling import."); - return; - } + #region MOD SETTINGS - //TODO: move most of this into AM2RLauncher.Profile? + /// + /// Runs when is clicked. Brings up a file select to select a mod, and adds that to the mod directory. + /// + private void AddModButtonClicked(object sender, EventArgs e) + { + log.Info("User requested to add mod. Requesting user input for new mod .zip..."); - FileInfo modFile = new FileInfo(fileFinder.FileName); - string modFileName = Path.GetFileNameWithoutExtension(modFile.Name); - string extractedModDir = Core.ModsPath + "/" + modFileName; + OpenFileDialog fileFinder = GetSingleZipDialog(Text.SelectModFileDialog); - // Check first, if the directory is already there, if yes, throw error - if (Directory.Exists(extractedModDir)) - { - string existingProfileName = Serializer.Deserialize(File.ReadAllText(extractedModDir + "/profile.xml")).Name; - log.Error("Mod is already imported as " + modFileName + "! Cancelling mod import."); - MessageBox.Show(this, HelperMethods.GetText(Text.ModIsAlreadyInstalledMessage, existingProfileName), Text.WarningWindowTitle, MessageBoxType.Warning); - return; - } + // If user didn't press ok, cancel + if (fileFinder.ShowDialog(this) != DialogResult.Ok) + { + log.Info("User cancelled the Mod selection."); + return; + } - // Directory doesn't exist -> extract! - ZipFile.ExtractToDirectory(modFile.FullName, extractedModDir); - log.Info("Imported and extracted mod .zip as " + modFileName); + log.Info("User selected \"" + fileFinder.FileName + "\""); - // If profile.xml doesn't exist, throw an error and cleanup - if (!File.Exists(extractedModDir + "/profile.xml")) - { - log.Error(modFile.Name + " does not contain profile.xml! Cancelling mod import."); - MessageBox.Show(this, HelperMethods.GetText(Text.ModIsInvalidMessage, modFileName), Text.ErrorWindowTitle, MessageBoxType.Error); - Directory.Delete(extractedModDir, true); - return; - } + // If either a directory was selected, user pressed OK without selecting anything or the file somehow went missing, cancel + if (!File.Exists(fileFinder.FileName)) + { + log.Error("Selected mod .zip file not found! Cancelling import."); + return; + } - ProfileXML profile = Serializer.Deserialize(File.ReadAllText(extractedModDir + "/profile.xml")); + //TODO: move most of this into AM2RLauncher.Profile? - // If OS versions mismatch, throw error and cleanup - if (OS.Name != profile.OperatingSystem) - { - log.Error("Mod is for " + profile.OperatingSystem + " while current OS is " + OS.Name + ". Cancelling mod import."); - MessageBox.Show(this, HelperMethods.GetText(Text.ModIsForWrongOS, profile.Name).Replace("$OS", profile.OperatingSystem).Replace("$CURRENTOS", OS.Name), - Text.ErrorWindowTitle, MessageBoxType.Error); - HelperMethods.DeleteDirectory(extractedModDir); - return; - } + FileInfo modFile = new FileInfo(fileFinder.FileName); + string modFileName = Path.GetFileNameWithoutExtension(modFile.Name); + string extractedModDir = Core.ModsPath + "/" + modFileName; - // If mod was installed/added by *name* already, throw error and cleanup - if (profileList.FirstOrDefault(p => p.Name == profile.Name) != null) - { - log.Error(profile.Name + " is already installed."); - MessageBox.Show(this, HelperMethods.GetText(Text.ModIsAlreadyInstalledMessage, profile.Name), Text.WarningWindowTitle, MessageBoxType.Warning); - HelperMethods.DeleteDirectory(extractedModDir); - return; - } + // Check first, if the directory is already there, if yes, throw error + if (Directory.Exists(extractedModDir)) + { + string existingProfileName = Serializer.Deserialize(File.ReadAllText(extractedModDir + "/profile.xml")).Name; + log.Error("Mod is already imported as " + modFileName + "! Cancelling mod import."); + MessageBox.Show(this, HelperMethods.GetText(Text.ModIsAlreadyInstalledMessage, existingProfileName), Text.WarningWindowTitle, MessageBoxType.Warning); + return; + } - // Reload list so mod gets recognized - LoadProfilesAndAdjustLists(); - // Adjust profileIndex to point to newly added mod. if its not found for whatever reason, we default to first community updates - modSettingsProfileDropDown.SelectedIndex = profileList.FindIndex(p => p.Name == profile.Name); - if (modSettingsProfileDropDown.SelectedIndex == -1) - modSettingsProfileDropDown.SelectedIndex = 0; + // Directory doesn't exist -> extract! + ZipFile.ExtractToDirectory(modFile.FullName, extractedModDir); + log.Info("Imported and extracted mod .zip as " + modFileName); - log.Info(profile.Name + " successfully added."); - MessageBox.Show(this, HelperMethods.GetText(Text.ModSuccessfullyInstalledMessage, profile.Name), Text.SuccessWindowTitle); + // If profile.xml doesn't exist, throw an error and cleanup + if (!File.Exists(extractedModDir + "/profile.xml")) + { + log.Error(modFile.Name + " does not contain profile.xml! Cancelling mod import."); + MessageBox.Show(this, HelperMethods.GetText(Text.ModIsInvalidMessage, modFileName), Text.ErrorWindowTitle, MessageBoxType.Error); + Directory.Delete(extractedModDir, true); + return; } - /// - /// Enabled / disables and accordingly. - /// - private void ModSettingsProfileDropDownSelectedIndexChanged(object sender, EventArgs e) + ProfileXML profile = Serializer.Deserialize(File.ReadAllText(extractedModDir + "/profile.xml")); + + // If OS versions mismatch, throw error and cleanup + if (OS.Name != profile.OperatingSystem) { - if (modSettingsProfileDropDown.SelectedIndex == -1 && modSettingsProfileDropDown.Items.Count == 0) return; + log.Error("Mod is for " + profile.OperatingSystem + " while current OS is " + OS.Name + ". Cancelling mod import."); + MessageBox.Show(this, HelperMethods.GetText(Text.ModIsForWrongOS, profile.Name).Replace("$OS", profile.OperatingSystem).Replace("$CURRENTOS", OS.Name), + Text.ErrorWindowTitle, MessageBoxType.Error); + HelperMethods.DeleteDirectory(extractedModDir); + return; + } - string profileName = modSettingsProfileDropDown.Items[modSettingsProfileDropDown.SelectedIndex].Text; + // If mod was installed/added by *name* already, throw error and cleanup + if (profileList.FirstOrDefault(p => p.Name == profile.Name) != null) + { + log.Error(profile.Name + " is already installed."); + MessageBox.Show(this, HelperMethods.GetText(Text.ModIsAlreadyInstalledMessage, profile.Name), Text.WarningWindowTitle, MessageBoxType.Warning); + HelperMethods.DeleteDirectory(extractedModDir); + return; + } - log.Info("SettingsProfileDropDown.SelectedIndex has been changed to " + modSettingsProfileDropDown.SelectedIndex + "."); - if (modSettingsProfileDropDown.SelectedIndex <= 0 || modSettingsProfileDropDown.Items.Count == 0) - { - deleteModButton.Enabled = false; - deleteModButton.ToolTip = null; - updateModButton.Enabled = false; - updateModButton.ToolTip = null; - profileNotesTextArea.TextColor = colInactive; - } - else - { - deleteModButton.Enabled = true; - deleteModButton.ToolTip = HelperMethods.GetText(Text.DeleteModButtonToolTip, profileName); - // On non-installable profiles we want to disable updating - updateModButton.Enabled = profileList[modSettingsProfileDropDown.SelectedIndex].Installable; - updateModButton.ToolTip = HelperMethods.GetText(Text.UpdateModButtonToolTip, profileName); - } + // Reload list so mod gets recognized + LoadProfilesAndAdjustLists(); + // Adjust profileIndex to point to newly added mod. if its not found for whatever reason, we default to first community updates + modSettingsProfileDropDown.SelectedIndex = profileList.FindIndex(p => p.Name == profile.Name); + if (modSettingsProfileDropDown.SelectedIndex == -1) + modSettingsProfileDropDown.SelectedIndex = 0; - profileButton.Enabled = Directory.Exists(Core.ProfilesPath + "/" + profileName); - profileButton.ToolTip = HelperMethods.GetText(Text.OpenProfileFolderToolTip, profileName); - saveButton.Enabled = true; - saveButton.ToolTip = HelperMethods.GetText(Text.OpenSaveFolderToolTip, profileName); + log.Info(profile.Name + " successfully added."); + MessageBox.Show(this, HelperMethods.GetText(Text.ModSuccessfullyInstalledMessage, profile.Name), Text.SuccessWindowTitle); + } - if (modSettingsProfileDropDown.SelectedIndex < 0 || modSettingsProfileDropDown.Items.Count == 0) - return; - profileNotesTextArea.TextColor = colGreen; - profileNotesTextArea.Text = Text.ProfileNotes + "\n" + profileList[modSettingsProfileDropDown.SelectedIndex].ProfileNotes; + /// + /// Enabled / disables and accordingly. + /// + private void ModSettingsProfileDropDownSelectedIndexChanged(object sender, EventArgs e) + { + if (modSettingsProfileDropDown.SelectedIndex == -1 && modSettingsProfileDropDown.Items.Count == 0) return; - } + string profileName = modSettingsProfileDropDown.Items[modSettingsProfileDropDown.SelectedIndex].Text; - /// - /// This opens the game files directory for the current profile. - /// - private void ProfileDataButtonClickEvent(object sender, EventArgs e) + log.Info("SettingsProfileDropDown.SelectedIndex has been changed to " + modSettingsProfileDropDown.SelectedIndex + "."); + if (modSettingsProfileDropDown.SelectedIndex <= 0 || modSettingsProfileDropDown.Items.Count == 0) { - if (!IsProfileIndexValid()) - return; - ProfileXML profile = profileList[modSettingsProfileDropDown.SelectedIndex]; - log.Info("User opened the profile directory for profile " + profile.Name + ", which is " + profile.SaveLocation); - CrossPlatformOperations.OpenFolder(Core.ProfilesPath + "/" + profile.Name); + deleteModButton.Enabled = false; + deleteModButton.ToolTip = null; + updateModButton.Enabled = false; + updateModButton.ToolTip = null; + profileNotesTextArea.TextColor = colInactive; } - - /// - /// This opens the save directory for the current profile. - /// - private void SaveButtonClickEvent(object sender, EventArgs e) + else { - if (!IsProfileIndexValid()) - return; - ProfileXML profile = profileList[modSettingsProfileDropDown.SelectedIndex]; - log.Info("User opened the save directory for profile " + profile.Name + ", which is " + profile.SaveLocation); - CrossPlatformOperations.OpenFolder(profile.SaveLocation); + deleteModButton.Enabled = true; + deleteModButton.ToolTip = HelperMethods.GetText(Text.DeleteModButtonToolTip, profileName); + // On non-installable profiles we want to disable updating + updateModButton.Enabled = profileList[modSettingsProfileDropDown.SelectedIndex].Installable; + updateModButton.ToolTip = HelperMethods.GetText(Text.UpdateModButtonToolTip, profileName); } - /// - /// Gets called when gets clicked. Deletes the current selected in . - /// - private void DeleteModButtonClicked(object sender, EventArgs e) - { - ProfileXML profile = profileList[modSettingsProfileDropDown.SelectedIndex]; - log.Info("User is attempting to delete profile " + profile.Name + "."); + profileButton.Enabled = Directory.Exists(Core.ProfilesPath + "/" + profileName); + profileButton.ToolTip = HelperMethods.GetText(Text.OpenProfileFolderToolTip, profileName); + saveButton.Enabled = true; + saveButton.ToolTip = HelperMethods.GetText(Text.OpenSaveFolderToolTip, profileName); - DialogResult result = MessageBox.Show(this, HelperMethods.GetText(Text.DeleteModWarning, profile.Name), Text.WarningWindowTitle, - MessageBoxButtons.OKCancel, MessageBoxType.Warning, MessageBoxDefaultButton.Cancel); + if (modSettingsProfileDropDown.SelectedIndex < 0 || modSettingsProfileDropDown.Items.Count == 0) + return; + profileNotesTextArea.TextColor = colGreen; + profileNotesTextArea.Text = Text.ProfileNotes + "\n" + profileList[modSettingsProfileDropDown.SelectedIndex].ProfileNotes; - // if user didn't press ok, cancel - if (result != DialogResult.Ok) - { - log.Info("User has cancelled profile deletion."); - return; - } + } - log.Info("User did not cancel. Proceeding to delete " + profile); - DeleteProfileAndAdjustLists(profile); - log.Info(profile + " has been deleted"); - MessageBox.Show(this, HelperMethods.GetText(Text.DeleteModButtonSuccess, profile.Name), Text.SuccessWindowTitle); - } + /// + /// This opens the game files directory for the current profile. + /// + private void ProfileDataButtonClickEvent(object sender, EventArgs e) + { + if (!IsProfileIndexValid()) + return; + ProfileXML profile = profileList[modSettingsProfileDropDown.SelectedIndex]; + log.Info("User opened the profile directory for profile " + profile.Name + ", which is " + profile.SaveLocation); + CrossPlatformOperations.OpenFolder(Core.ProfilesPath + "/" + profile.Name); + } - /// - /// Gets called, when gets clicked. Opens a window, so the user can select a zip, which will be updated over - /// the current selected in . - /// - private void UpdateModButtonClicked(object sender, EventArgs e) - { - log.Info("User requested to update mod. Requesting user input for new mod .zip..."); + /// + /// This opens the save directory for the current profile. + /// + private void SaveButtonClickEvent(object sender, EventArgs e) + { + if (!IsProfileIndexValid()) + return; + ProfileXML profile = profileList[modSettingsProfileDropDown.SelectedIndex]; + log.Info("User opened the save directory for profile " + profile.Name + ", which is " + profile.SaveLocation); + CrossPlatformOperations.OpenFolder(profile.SaveLocation); + } - ProfileXML currentProfile = profileList[modSettingsProfileDropDown.SelectedIndex]; - OpenFileDialog fileFinder = GetSingleZipDialog(Text.SelectModFileDialog); + /// + /// Gets called when gets clicked. Deletes the current selected in . + /// + private void DeleteModButtonClicked(object sender, EventArgs e) + { + ProfileXML profile = profileList[modSettingsProfileDropDown.SelectedIndex]; + log.Info("User is attempting to delete profile " + profile.Name + "."); - // If user didn't click OK, cancel - if (fileFinder.ShowDialog(this) != DialogResult.Ok) - { - log.Info("User cancelled the Mod selection."); - return; - } + DialogResult result = MessageBox.Show(this, HelperMethods.GetText(Text.DeleteModWarning, profile.Name), Text.WarningWindowTitle, + MessageBoxButtons.OKCancel, MessageBoxType.Warning, MessageBoxDefaultButton.Cancel); - log.Info("User selected \"" + fileFinder.FileName + "\""); + // if user didn't press ok, cancel + if (result != DialogResult.Ok) + { + log.Info("User has cancelled profile deletion."); + return; + } - // If either a directory was selected, no file was selected or the file somehow went missing, cancel - if (!File.Exists(fileFinder.FileName)) - { - log.Error("Selected mod .zip file not found! Cancelling mod update."); - return; - } + log.Info("User did not cancel. Proceeding to delete " + profile); + DeleteProfileAndAdjustLists(profile); + log.Info(profile + " has been deleted"); + MessageBox.Show(this, HelperMethods.GetText(Text.DeleteModButtonSuccess, profile.Name), Text.SuccessWindowTitle); + } - //TODO: move most of this into AM2RLauncher.Profile? + /// + /// Gets called, when gets clicked. Opens a window, so the user can select a zip, which will be updated over + /// the current selected in . + /// + private void UpdateModButtonClicked(object sender, EventArgs e) + { + log.Info("User requested to update mod. Requesting user input for new mod .zip..."); - FileInfo modFile = new FileInfo(fileFinder.FileName); - string extractedName = Path.GetFileNameWithoutExtension(modFile.Name) + "_new"; - string extractedModDir = Core.ModsPath + "/" + extractedName; + ProfileXML currentProfile = profileList[modSettingsProfileDropDown.SelectedIndex]; + OpenFileDialog fileFinder = GetSingleZipDialog(Text.SelectModFileDialog); - // If for some reason old files remain, delete them so that extraction doesn't throw - if (Directory.Exists(extractedModDir)) - Directory.Delete(extractedModDir, true); + // If user didn't click OK, cancel + if (fileFinder.ShowDialog(this) != DialogResult.Ok) + { + log.Info("User cancelled the Mod selection."); + return; + } - // Directory doesn't exist -> extract! - ZipFile.ExtractToDirectory(fileFinder.FileName, extractedModDir); + log.Info("User selected \"" + fileFinder.FileName + "\""); - // If mod doesn't have a profile.xml, throw an error and cleanup - if (!File.Exists(extractedModDir + "/profile.xml")) - { - log.Error(fileFinder.FileName + " does not contain profile.xml! Cancelling mod update."); - MessageBox.Show(this, HelperMethods.GetText(Text.ModIsInvalidMessage, extractedName), Text.ErrorWindowTitle, MessageBoxType.Error); - Directory.Delete(extractedModDir, true); - return; - } + // If either a directory was selected, no file was selected or the file somehow went missing, cancel + if (!File.Exists(fileFinder.FileName)) + { + log.Error("Selected mod .zip file not found! Cancelling mod update."); + return; + } - // Check by *name*, if the mod was installed already - ProfileXML profile = Serializer.Deserialize(File.ReadAllText(extractedModDir + "/profile.xml")); + //TODO: move most of this into AM2RLauncher.Profile? - // If the selected mod is not installed, tell user that they should add it and cleanup - if (profileList.FirstOrDefault(p => p.Name == profile.Name) == null) - { - log.Error("Mod is not installed! Cancelling mod update."); - MessageBox.Show(this, HelperMethods.GetText(Text.UpdateModButtonWrongMod, currentProfile.Name).Replace("$SELECT", profile.Name), - Text.WarningWindowTitle, MessageBoxButtons.OK); - HelperMethods.DeleteDirectory(extractedModDir); - return; - } + FileInfo modFile = new FileInfo(fileFinder.FileName); + string extractedName = Path.GetFileNameWithoutExtension(modFile.Name) + "_new"; + string extractedModDir = Core.ModsPath + "/" + extractedName; - // If user doesn't want to update, cleanup - DialogResult updateResult = MessageBox.Show(this, HelperMethods.GetText(Text.UpdateModWarning, currentProfile.Name), Text.WarningWindowTitle, - MessageBoxButtons.OKCancel, MessageBoxType.Warning, MessageBoxDefaultButton.Cancel); - if (updateResult != DialogResult.Ok) - { - log.Error("User has cancelled mod update!"); - HelperMethods.DeleteDirectory(extractedModDir); - return; - } + // If for some reason old files remain, delete them so that extraction doesn't throw + if (Directory.Exists(extractedModDir)) + Directory.Delete(extractedModDir, true); - // If the profile isn't installed, don't ask about archiving it - if (Profile.IsProfileInstalled(currentProfile)) - { - DialogResult archiveResult = MessageBox.Show(this, HelperMethods.GetText(Text.ArchiveMod, currentProfile.Name + " " + Text.VersionLabel + currentProfile.Version), Text.WarningWindowTitle, MessageBoxButtons.YesNo, MessageBoxType.Warning, MessageBoxDefaultButton.No); + // Directory doesn't exist -> extract! + ZipFile.ExtractToDirectory(fileFinder.FileName, extractedModDir); - // User wants to archive profile - if (archiveResult == DialogResult.Yes) - ArchiveProfileAndAdjustLists(currentProfile); - } + // If mod doesn't have a profile.xml, throw an error and cleanup + if (!File.Exists(extractedModDir + "/profile.xml")) + { + log.Error(fileFinder.FileName + " does not contain profile.xml! Cancelling mod update."); + MessageBox.Show(this, HelperMethods.GetText(Text.ModIsInvalidMessage, extractedName), Text.ErrorWindowTitle, MessageBoxType.Error); + Directory.Delete(extractedModDir, true); + return; + } - DeleteProfileAndAdjustLists(currentProfile); + // Check by *name*, if the mod was installed already + ProfileXML profile = Serializer.Deserialize(File.ReadAllText(extractedModDir + "/profile.xml")); - // Rename directory to take the old one's place - string originalFolder = Core.ModsPath + "/" + Path.GetFileNameWithoutExtension(modFile.Name); - Directory.Move(extractedModDir, originalFolder); + // If the selected mod is not installed, tell user that they should add it and cleanup + if (profileList.FirstOrDefault(p => p.Name == profile.Name) == null) + { + log.Error("Mod is not installed! Cancelling mod update."); + MessageBox.Show(this, HelperMethods.GetText(Text.UpdateModButtonWrongMod, currentProfile.Name).Replace("$SELECT", profile.Name), + Text.WarningWindowTitle, MessageBoxButtons.OK); + HelperMethods.DeleteDirectory(extractedModDir); + return; + } - // Adjust our lists so it gets recognized - LoadProfilesAndAdjustLists(); + // If user doesn't want to update, cleanup + DialogResult updateResult = MessageBox.Show(this, HelperMethods.GetText(Text.UpdateModWarning, currentProfile.Name), Text.WarningWindowTitle, + MessageBoxButtons.OKCancel, MessageBoxType.Warning, MessageBoxDefaultButton.Cancel); + if (updateResult != DialogResult.Ok) + { + log.Error("User has cancelled mod update!"); + HelperMethods.DeleteDirectory(extractedModDir); + return; + } - modSettingsProfileDropDown.SelectedIndex = profileList.FindIndex(p => p.Name == currentProfile.Name); - if (modSettingsProfileDropDown.SelectedIndex == -1) - modSettingsProfileDropDown.SelectedIndex = 0; + // If the profile isn't installed, don't ask about archiving it + if (Profile.IsProfileInstalled(currentProfile)) + { + DialogResult archiveResult = MessageBox.Show(this, HelperMethods.GetText(Text.ArchiveMod, currentProfile.Name + " " + Text.VersionLabel + currentProfile.Version), Text.WarningWindowTitle, MessageBoxButtons.YesNo, MessageBoxType.Warning, MessageBoxDefaultButton.No); - log.Info("Successfully updated mod profile " + profile.Name + "."); - MessageBox.Show(this, HelperMethods.GetText(Text.ModSuccessfullyInstalledMessage, currentProfile.Name), Text.SuccessWindowTitle); + // User wants to archive profile + if (archiveResult == DialogResult.Yes) + ArchiveProfileAndAdjustLists(currentProfile); } - #endregion + DeleteProfileAndAdjustLists(currentProfile); + + // Rename directory to take the old one's place + string originalFolder = Core.ModsPath + "/" + Path.GetFileNameWithoutExtension(modFile.Name); + Directory.Move(extractedModDir, originalFolder); + + // Adjust our lists so it gets recognized + LoadProfilesAndAdjustLists(); + + modSettingsProfileDropDown.SelectedIndex = profileList.FindIndex(p => p.Name == currentProfile.Name); + if (modSettingsProfileDropDown.SelectedIndex == -1) + modSettingsProfileDropDown.SelectedIndex = 0; + + log.Info("Successfully updated mod profile " + profile.Name + "."); + MessageBox.Show(this, HelperMethods.GetText(Text.ModSuccessfullyInstalledMessage, currentProfile.Name), Text.SuccessWindowTitle); } + + #endregion } \ No newline at end of file diff --git a/AM2RLauncher/AM2RLauncher/MainForm/MainForm.Methods.cs b/AM2RLauncher/AM2RLauncher/MainForm/MainForm.Methods.cs index 4e7d3d3..4213f1d 100644 --- a/AM2RLauncher/AM2RLauncher/MainForm/MainForm.Methods.cs +++ b/AM2RLauncher/AM2RLauncher/MainForm/MainForm.Methods.cs @@ -9,231 +9,230 @@ using System.IO; using System.Text.RegularExpressions; using Configuration = System.Configuration.Configuration; -namespace AM2RLauncher +namespace AM2RLauncher; + +public partial class MainForm : Form { - public partial class MainForm : Form + /// + /// Method that updates . + /// + /// The value that should be set to. + /// The min value that should be set to. + /// The max value that should be set to. + private void UpdateProgressBar(int value, int min = 0, int max = 100) { - /// - /// Method that updates . - /// - /// The value that should be set to. - /// The min value that should be set to. - /// The max value that should be set to. - private void UpdateProgressBar(int value, int min = 0, int max = 100) + Application.Instance.Invoke(() => { - Application.Instance.Invoke(() => - { - progressBar.MinValue = min; - progressBar.MaxValue = max; - progressBar.Value = value; - }); - } + progressBar.MinValue = min; + progressBar.MaxValue = max; + progressBar.Value = value; + }); + } - /// - /// Method that updates with a min value of 0 and max value of 100. - /// - /// The value that should be set to. - private void UpdateProgressBar(int value) - { - UpdateProgressBar(value, 0, 100); - } + /// + /// Method that updates with a min value of 0 and max value of 100. + /// + /// The value that should be set to. + private void UpdateProgressBar(int value) + { + UpdateProgressBar(value, 0, 100); + } - /// - /// Safety check function before accessing . - /// - /// if it is valid, if not. - private bool IsProfileIndexValid() + /// + /// Safety check function before accessing . + /// + /// if it is valid, if not. + private bool IsProfileIndexValid() + { + return profileIndex != null; + } + + /// + /// This is just a helper method for the git commands in order to have a progress bar display for them. + /// + private bool TransferProgressHandlerMethod(TransferProgress transferProgress) + { + // Thank you random issue on the gitlib2sharp repo!!!! + // Also tldr; rtfm + if (isGitProcessGettingCancelled) return false; + + // This needs to be in an Invoke, in order to access the variables from the main thread + // Otherwise this will throw a runtime exception + Application.Instance.Invoke(() => { - return profileIndex != null; - } + progressBar.MinValue = 0; + progressBar.MaxValue = transferProgress.TotalObjects; + if (currentGitObject >= transferProgress.ReceivedObjects) + return; + progressLabel.Text = Text.ProgressbarProgress + " " + transferProgress.ReceivedObjects + " (" + ((int)transferProgress.ReceivedBytes / 1000000) + "MB) / " + transferProgress.TotalObjects + " objects"; + currentGitObject = transferProgress.ReceivedObjects; + progressBar.Value = transferProgress.ReceivedObjects; + }); + + return true; + } - /// - /// This is just a helper method for the git commands in order to have a progress bar display for them. - /// - private bool TransferProgressHandlerMethod(TransferProgress transferProgress) + /// + /// Creates a single-file, zip-filtered file dialog. + /// + /// The title of the file dialog. + /// The created file dialog. + private OpenFileDialog GetSingleZipDialog(string title = "") + { + OpenFileDialog fileDialog = new OpenFileDialog { - // Thank you random issue on the gitlib2sharp repo!!!! - // Also tldr; rtfm - if (isGitProcessGettingCancelled) return false; + Directory = new Uri(CrossPlatformOperations.CurrentPath), + MultiSelect = false, + Title = title + }; + fileDialog.Filters.Add(new FileFilter(Text.ZipArchiveText, ".zip")); + return fileDialog; + } - // This needs to be in an Invoke, in order to access the variables from the main thread - // Otherwise this will throw a runtime exception - Application.Instance.Invoke(() => - { - progressBar.MinValue = 0; - progressBar.MaxValue = transferProgress.TotalObjects; - if (currentGitObject >= transferProgress.ReceivedObjects) - return; - progressLabel.Text = Text.ProgressbarProgress + " " + transferProgress.ReceivedObjects + " (" + ((int)transferProgress.ReceivedBytes / 1000000) + "MB) / " + transferProgress.TotalObjects + " objects"; - currentGitObject = transferProgress.ReceivedObjects; - progressBar.Value = transferProgress.ReceivedObjects; - }); - - return true; - } + private void DisableProgressBar() + { + progressBar.Visible = false; + progressBar.Value = 0; + } - /// - /// Creates a single-file, zip-filtered file dialog. - /// - /// The title of the file dialog. - /// The created file dialog. - private OpenFileDialog GetSingleZipDialog(string title = "") - { - OpenFileDialog fileDialog = new OpenFileDialog - { - Directory = new Uri(CrossPlatformOperations.CurrentPath), - MultiSelect = false, - Title = title - }; - fileDialog.Filters.Add(new FileFilter(Text.ZipArchiveText, ".zip")); - return fileDialog; - } + private void EnableProgressBar() + { + progressBar.Visible = true; + progressBar.Value = 0; + } - private void DisableProgressBar() - { - progressBar.Visible = false; - progressBar.Value = 0; - } + private void DisableProgressBarAndProgressLabel() + { + DisableProgressBar(); + progressLabel.Visible = false; + progressLabel.Text = ""; + } - private void EnableProgressBar() - { - progressBar.Visible = true; - progressBar.Value = 0; - } + private void EnableProgressBarAndLabel() + { + EnableProgressBar(); + progressLabel.Visible = true; + progressLabel.Text = ""; + } - private void DisableProgressBarAndProgressLabel() + /// + /// Reads the Launcher config file on the current Platform and returns the value for . + /// + /// The property to get the value from. + /// The value from as a string + public static string ReadFromConfig(string property) + { + log.Info($"Reading {property} from config."); + if (OS.IsWindows) { - DisableProgressBar(); - progressLabel.Visible = false; - progressLabel.Text = ""; + // We use the configuration manager in order to read `property` from the app.config and then return it + ConnectionStringSettings appConfig = ConfigurationManager.ConnectionStrings[property]; + if (appConfig == null) throw new ArgumentException("The property " + property + " could not be found."); + return appConfig.ConnectionString; } - - private void EnableProgressBarAndLabel() + if (OS.IsUnix) { - EnableProgressBar(); - progressLabel.Visible = true; - progressLabel.Text = ""; - } + string launcherConfigFilePath = CrossPlatformOperations.NixLauncherConfigFilePath; + string launcherConfigPath = Path.GetDirectoryName(launcherConfigFilePath); + XML.LauncherConfigXML launcherConfig = new XML.LauncherConfigXML(); - /// - /// Reads the Launcher config file on the current Platform and returns the value for . - /// - /// The property to get the value from. - /// The value from as a string - public static string ReadFromConfig(string property) - { - log.Info($"Reading {property} from config."); - if (OS.IsWindows) + // If folder doesn't exist, create it and the config file + if (!Directory.Exists(launcherConfigPath) || !File.Exists(launcherConfigFilePath)) { - // We use the configuration manager in order to read `property` from the app.config and then return it - ConnectionStringSettings appConfig = ConfigurationManager.ConnectionStrings[property]; - if (appConfig == null) throw new ArgumentException("The property " + property + " could not be found."); - return appConfig.ConnectionString; - } - if (OS.IsUnix) - { - string launcherConfigFilePath = CrossPlatformOperations.NixLauncherConfigFilePath; - string launcherConfigPath = Path.GetDirectoryName(launcherConfigFilePath); - XML.LauncherConfigXML launcherConfig = new XML.LauncherConfigXML(); - - // If folder doesn't exist, create it and the config file - if (!Directory.Exists(launcherConfigPath) || !File.Exists(launcherConfigFilePath)) - { - Directory.CreateDirectory(launcherConfigPath); - File.WriteAllText(launcherConfigFilePath, Serializer.Serialize(launcherConfig)); - } - - // Deserialize the config xml into launcherConfig - launcherConfig = Serializer.Deserialize(File.ReadAllText(launcherConfigFilePath)); - - // This uses the indexer, which means, we can use the variable in order to get the property. Look at LauncherConfigXML for more info - return launcherConfig[property]?.ToString(); + Directory.CreateDirectory(launcherConfigPath); + File.WriteAllText(launcherConfigFilePath, Serializer.Serialize(launcherConfig)); } - log.Error(OS.Name + " has no config to read from!"); - return null; + // Deserialize the config xml into launcherConfig + launcherConfig = Serializer.Deserialize(File.ReadAllText(launcherConfigFilePath)); + + // This uses the indexer, which means, we can use the variable in order to get the property. Look at LauncherConfigXML for more info + return launcherConfig[property]?.ToString(); } - /// - /// Writes to in the Launcher Config file. - /// - /// The property whose value you want to change. - /// The value that will be written. - public static void WriteToConfig(string property, object value) + log.Error(OS.Name + " has no config to read from!"); + return null; + } + + /// + /// Writes to in the Launcher Config file. + /// + /// The property whose value you want to change. + /// The value that will be written. + public static void WriteToConfig(string property, object value) + { + log.Info($"Writing {value} of type {value.GetType()} to {property} to config."); + if (OS.IsWindows) { - log.Info($"Writing {value} of type {value.GetType()} to {property} to config."); - if (OS.IsWindows) - { - // We use the configuration manager in order to read from the app.config, change the value and save it - Configuration appConfig = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); - if (appConfig == null) - throw new NullReferenceException("Could not find the Config file! Please make sure it exists!"); - ConnectionStringsSection connectionStringsSection = (ConnectionStringsSection)appConfig.GetSection("connectionStrings"); - if (connectionStringsSection?.ConnectionStrings[property]?.ConnectionString == null) - throw new ArgumentException("The property " + property + " could not be found."); - connectionStringsSection.ConnectionStrings[property].ConnectionString = value.ToString(); - appConfig.Save(); - ConfigurationManager.RefreshSection("connectionStrings"); - } - else if (OS.IsUnix) + // We use the configuration manager in order to read from the app.config, change the value and save it + Configuration appConfig = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); + if (appConfig == null) + throw new NullReferenceException("Could not find the Config file! Please make sure it exists!"); + ConnectionStringsSection connectionStringsSection = (ConnectionStringsSection)appConfig.GetSection("connectionStrings"); + if (connectionStringsSection?.ConnectionStrings[property]?.ConnectionString == null) + throw new ArgumentException("The property " + property + " could not be found."); + connectionStringsSection.ConnectionStrings[property].ConnectionString = value.ToString(); + appConfig.Save(); + ConfigurationManager.RefreshSection("connectionStrings"); + } + else if (OS.IsUnix) + { + string launcherConfigFilePath = CrossPlatformOperations.NixLauncherConfigFilePath; + string launcherConfigPath = Path.GetDirectoryName(launcherConfigFilePath); + XML.LauncherConfigXML launcherConfig = new XML.LauncherConfigXML(); + + // If folder doesn't exist, create it and the config file + if (!Directory.Exists(launcherConfigPath) || !File.Exists(launcherConfigFilePath)) { - string launcherConfigFilePath = CrossPlatformOperations.NixLauncherConfigFilePath; - string launcherConfigPath = Path.GetDirectoryName(launcherConfigFilePath); - XML.LauncherConfigXML launcherConfig = new XML.LauncherConfigXML(); - - // If folder doesn't exist, create it and the config file - if (!Directory.Exists(launcherConfigPath) || !File.Exists(launcherConfigFilePath)) - { - Directory.CreateDirectory(launcherConfigPath); - File.WriteAllText(launcherConfigFilePath, Serializer.Serialize(launcherConfig)); - } - // Deserialize the config xml into launcherConfig - launcherConfig = Serializer.Deserialize(File.ReadAllText(launcherConfigFilePath)); - - // Uses indexer. Look at LauncherConfigXML for more info - launcherConfig[property] = value; - - // Serialize back into the file + Directory.CreateDirectory(launcherConfigPath); File.WriteAllText(launcherConfigFilePath, Serializer.Serialize(launcherConfig)); } - else - log.Error(OS.Name + " has no config to write to!"); + // Deserialize the config xml into launcherConfig + launcherConfig = Serializer.Deserialize(File.ReadAllText(launcherConfigFilePath)); + + // Uses indexer. Look at LauncherConfigXML for more info + launcherConfig[property] = value; + + // Serialize back into the file + File.WriteAllText(launcherConfigFilePath, Serializer.Serialize(launcherConfig)); } + else + log.Error(OS.Name + " has no config to write to!"); + } - /// - /// When a Launcher update occurs that introduces new config properties, this method ensures that the old user config is copied over as much as possible. - /// - public static void CopyOldConfigToNewConfig() + /// + /// When a Launcher update occurs that introduces new config properties, this method ensures that the old user config is copied over as much as possible. + /// + public static void CopyOldConfigToNewConfig() + { + if (OS.IsWindows) { - if (OS.IsWindows) - { - string oldConfigPath = CrossPlatformOperations.LauncherName + ".oldCfg"; - string newConfigPath = CrossPlatformOperations.LauncherName + ".config"; - string oldConfigText = File.ReadAllText(oldConfigPath); - string newConfigText = File.ReadAllText(newConfigPath); + string oldConfigPath = CrossPlatformOperations.LauncherName + ".oldCfg"; + string newConfigPath = CrossPlatformOperations.LauncherName + ".config"; + string oldConfigText = File.ReadAllText(oldConfigPath); + string newConfigText = File.ReadAllText(newConfigPath); - Regex settingRegex = new Regex(""); + Regex settingRegex = new Regex(""); - MatchCollection oldMatch = settingRegex.Matches(oldConfigText); - MatchCollection newMatch = settingRegex.Matches(newConfigText); + MatchCollection oldMatch = settingRegex.Matches(oldConfigText); + MatchCollection newMatch = settingRegex.Matches(newConfigText); - for (int i = 0; i < oldMatch.Count; i++) - newConfigText = newConfigText.Replace(newMatch[i].Value, oldMatch[i].Value); + for (int i = 0; i < oldMatch.Count; i++) + newConfigText = newConfigText.Replace(newMatch[i].Value, oldMatch[i].Value); - File.WriteAllText(newConfigPath, newConfigText); + File.WriteAllText(newConfigPath, newConfigText); - } - else if (OS.IsUnix) - { - string launcherConfigFilePath = CrossPlatformOperations.NixLauncherConfigFilePath; + } + else if (OS.IsUnix) + { + string launcherConfigFilePath = CrossPlatformOperations.NixLauncherConfigFilePath; - // For some reason deserializing and saving back again works, not exactly sure why, but I'll take it - XML.LauncherConfigXML launcherConfig = Serializer.Deserialize(File.ReadAllText(launcherConfigFilePath)); - File.WriteAllText(launcherConfigFilePath, Serializer.Serialize(launcherConfig)); - } - else - log.Error(OS.Name + " has no config to transfer over!"); + // For some reason deserializing and saving back again works, not exactly sure why, but I'll take it + XML.LauncherConfigXML launcherConfig = Serializer.Deserialize(File.ReadAllText(launcherConfigFilePath)); + File.WriteAllText(launcherConfigFilePath, Serializer.Serialize(launcherConfig)); } + else + log.Error(OS.Name + " has no config to transfer over!"); } } \ No newline at end of file diff --git a/AM2RLauncher/AM2RLauncher/MainForm/MainForm.StateMachine.cs b/AM2RLauncher/AM2RLauncher/MainForm/MainForm.StateMachine.cs index 30c04c1..30fb782 100644 --- a/AM2RLauncher/AM2RLauncher/MainForm/MainForm.StateMachine.cs +++ b/AM2RLauncher/AM2RLauncher/MainForm/MainForm.StateMachine.cs @@ -4,380 +4,379 @@ using AM2RLauncherLib; using AM2RLauncherLib.XML; using AM2RLauncher.Language; -namespace AM2RLauncher +namespace AM2RLauncher; + +/// +/// Everything UI/Form state machine-related goes in here +/// +public partial class MainForm { /// - /// Everything UI/Form state machine-related goes in here + /// Updates , , , and according to the current conditions. + /// + private void UpdateStateMachine() + { + UpdatePlayState(); + UpdateApkState(); + UpdateProfileState(); + UpdateModSettingsState(); + } + + /// + /// Determines current conditions and calls accordingly. /// - public partial class MainForm + private void UpdatePlayState() { - /// - /// Updates , , , and according to the current conditions. - /// - private void UpdateStateMachine() + // If we're downloading or installing, don't change anything + if ((updateState == PlayButtonState.Downloading) || (updateState == PlayButtonState.Installing)) + return; + + // If we're currently creating an APK, we disable the play button + if (apkButtonState == ApkButtonState.Creating) { - UpdatePlayState(); - UpdateApkState(); - UpdateProfileState(); - UpdateModSettingsState(); + playButton.Enabled = false; + return; } - /// - /// Determines current conditions and calls accordingly. - /// - private void UpdatePlayState() + playButton.Enabled = true; + // If PatchData isn't cloned, we still need to download + if (!Profile.IsPatchDataCloned()) { - // If we're downloading or installing, don't change anything - if ((updateState == PlayButtonState.Downloading) || (updateState == PlayButtonState.Installing)) - return; - - // If we're currently creating an APK, we disable the play button - if (apkButtonState == ApkButtonState.Creating) - { - playButton.Enabled = false; - return; - } - - playButton.Enabled = true; - // If PatchData isn't cloned, we still need to download - if (!Profile.IsPatchDataCloned()) - { - SetPlayButtonState(PlayButtonState.Download); - return; - } - - // If 1.1 isn't installed, we still need to select it - if (!Profile.Is11Installed()) - { - SetPlayButtonState(PlayButtonState.Select11); - return; - } - - var isProfileValid = IsProfileIndexValid(); - // If current profile is installed, we're ready to play! - if (isProfileValid && Profile.IsProfileInstalled(profileList[profileIndex.Value])) - { - SetPlayButtonState(PlayButtonState.Play); - return; - } - // Otherwise, if profile is NOT installable, we delete the profile because we can't install it and therefore holds no value! - if (isProfileValid && profileList[profileIndex.Value].Installable == false) - { - DeleteProfileAndAdjustLists(profileList[profileIndex.Value]); - return; - } + SetPlayButtonState(PlayButtonState.Download); + return; + } - // Otherwise, we still need to install. - SetPlayButtonState(PlayButtonState.Install); + // If 1.1 isn't installed, we still need to select it + if (!Profile.Is11Installed()) + { + SetPlayButtonState(PlayButtonState.Select11); + return; } - /// - /// Determines current conditions and enables or disables accordingly. - /// - private void UpdateApkState() + var isProfileValid = IsProfileIndexValid(); + // If current profile is installed, we're ready to play! + if (isProfileValid && Profile.IsProfileInstalled(profileList[profileIndex.Value])) { - // Safety check - if (apkButton == null) - return; + SetPlayButtonState(PlayButtonState.Play); + return; + } + // Otherwise, if profile is NOT installable, we delete the profile because we can't install it and therefore holds no value! + if (isProfileValid && profileList[profileIndex.Value].Installable == false) + { + DeleteProfileAndAdjustLists(profileList[profileIndex.Value]); + return; + } - // Our default values - apkButton.Enabled = false; - apkButton.ToolTip = Text.ApkButtonDisabledToolTip; + // Otherwise, we still need to install. + SetPlayButtonState(PlayButtonState.Install); + } - // If profile supports Android and if we are NOT already creating an APK... - if (!IsProfileIndexValid()) - return; + /// + /// Determines current conditions and enables or disables accordingly. + /// + private void UpdateApkState() + { + // Safety check + if (apkButton == null) + return; - var profile = profileList[profileIndex.Value]; - if (!profile.SupportsAndroid || !profile.Installable || apkButtonState != ApkButtonState.Create) - return; + // Our default values + apkButton.Enabled = false; + apkButton.ToolTip = Text.ApkButtonDisabledToolTip; - // Switch status based on main button's state - switch (updateState) - { - case PlayButtonState.Download: - case PlayButtonState.Downloading: - case PlayButtonState.Select11: - case PlayButtonState.Installing: - case PlayButtonState.Playing: return; - - case PlayButtonState.Install: - case PlayButtonState.Play: apkButton.Enabled = true; apkButton.ToolTip = HelperMethods.GetText(Text.ApkButtonEnabledToolTip, profileDropDown?.Items[profileDropDown.SelectedIndex]?.Text ?? ""); break; - } + // If profile supports Android and if we are NOT already creating an APK... + if (!IsProfileIndexValid()) + return; + + var profile = profileList[profileIndex.Value]; + if (!profile.SupportsAndroid || !profile.Installable || apkButtonState != ApkButtonState.Create) + return; + + // Switch status based on main button's state + switch (updateState) + { + case PlayButtonState.Download: + case PlayButtonState.Downloading: + case PlayButtonState.Select11: + case PlayButtonState.Installing: + case PlayButtonState.Playing: return; + + case PlayButtonState.Install: + case PlayButtonState.Play: apkButton.Enabled = true; apkButton.ToolTip = HelperMethods.GetText(Text.ApkButtonEnabledToolTip, profileDropDown?.Items[profileDropDown.SelectedIndex]?.Text ?? ""); break; } + } - /// - /// Determines current conditions and enables or disables the and related controls accordingly. - /// - private void UpdateProfileState() + /// + /// Determines current conditions and enables or disables the and related controls accordingly. + /// + private void UpdateProfileState() + { + // Safety check + if (profileDropDown == null) + return; + switch (updateState) { - // Safety check - if (profileDropDown == null) - return; - switch (updateState) - { - case PlayButtonState.Download: - case PlayButtonState.Downloading: - case PlayButtonState.Select11: - case PlayButtonState.Installing: - case PlayButtonState.Playing: profileDropDown.Enabled = false; break; + case PlayButtonState.Download: + case PlayButtonState.Downloading: + case PlayButtonState.Select11: + case PlayButtonState.Installing: + case PlayButtonState.Playing: profileDropDown.Enabled = false; break; - case PlayButtonState.Install: - case PlayButtonState.Play: profileDropDown.Enabled = true; break; + case PlayButtonState.Install: + case PlayButtonState.Play: profileDropDown.Enabled = true; break; - } - if (apkButtonState == ApkButtonState.Creating) profileDropDown.Enabled = false; + } + if (apkButtonState == ApkButtonState.Creating) profileDropDown.Enabled = false; - Color col = profileDropDown.Enabled ? colGreen : colInactive; + Color col = profileDropDown.Enabled ? colGreen : colInactive; - if (OS.IsWindows) - profileDropDown.TextColor = col; - profileAuthorLabel.TextColor = col; - profileVersionLabel.TextColor = col; - profileLabel.TextColor = col; - } + if (OS.IsWindows) + profileDropDown.TextColor = col; + profileAuthorLabel.TextColor = col; + profileVersionLabel.TextColor = col; + profileLabel.TextColor = col; + } - /// - /// Determines current conditions and enables or disabled the mainPage controls accordingly. - /// - private void UpdateModSettingsState() - { - // Safety check - if (modSettingsProfileDropDown == null || modSettingsProfileDropDown.Items.Count <= 0) return; + /// + /// Determines current conditions and enables or disabled the mainPage controls accordingly. + /// + private void UpdateModSettingsState() + { + // Safety check + if (modSettingsProfileDropDown == null || modSettingsProfileDropDown.Items.Count <= 0) return; - bool enabled = false; - switch (updateState) - { - case PlayButtonState.Download: - case PlayButtonState.Downloading: - case PlayButtonState.Select11: - case PlayButtonState.Installing: - case PlayButtonState.Playing: enabled = false; break; + bool enabled = false; + switch (updateState) + { + case PlayButtonState.Download: + case PlayButtonState.Downloading: + case PlayButtonState.Select11: + case PlayButtonState.Installing: + case PlayButtonState.Playing: enabled = false; break; - case PlayButtonState.Install: - case PlayButtonState.Play: enabled = true; break; + case PlayButtonState.Install: + case PlayButtonState.Play: enabled = true; break; - } - if (apkButtonState == ApkButtonState.Creating) enabled = false; + } + if (apkButtonState == ApkButtonState.Creating) enabled = false; - string selectedProfileName = modSettingsProfileDropDown.Items[modSettingsProfileDropDown.SelectedIndex].Text; + string selectedProfileName = modSettingsProfileDropDown.Items[modSettingsProfileDropDown.SelectedIndex].Text; - settingsProfileLabel.TextColor = colGreen; - modSettingsProfileDropDown.Enabled = enabled; - profileButton.Enabled = enabled; - profileButton.ToolTip = HelperMethods.GetText(Text.OpenProfileFolderToolTip, selectedProfileName); - saveButton.Enabled = enabled; - saveButton.ToolTip = HelperMethods.GetText(Text.OpenSaveFolderToolTip, selectedProfileName); - addModButton.Enabled = enabled; - addModButton.ToolTip = Text.AddNewModToolTip; + settingsProfileLabel.TextColor = colGreen; + modSettingsProfileDropDown.Enabled = enabled; + profileButton.Enabled = enabled; + profileButton.ToolTip = HelperMethods.GetText(Text.OpenProfileFolderToolTip, selectedProfileName); + saveButton.Enabled = enabled; + saveButton.ToolTip = HelperMethods.GetText(Text.OpenSaveFolderToolTip, selectedProfileName); + addModButton.Enabled = enabled; + addModButton.ToolTip = Text.AddNewModToolTip; - // Only enable these, when we're not on the community updates - if (modSettingsProfileDropDown.SelectedIndex > 0) - { - updateModButton.Enabled = profileList[modSettingsProfileDropDown.SelectedIndex].Installable; - updateModButton.ToolTip = HelperMethods.GetText(Text.UpdateModButtonToolTip, selectedProfileName); - deleteModButton.Enabled = enabled; - deleteModButton.ToolTip = HelperMethods.GetText(Text.DeleteModButtonToolTip, selectedProfileName); - } + // Only enable these, when we're not on the community updates + if (modSettingsProfileDropDown.SelectedIndex > 0) + { + updateModButton.Enabled = profileList[modSettingsProfileDropDown.SelectedIndex].Installable; + updateModButton.ToolTip = HelperMethods.GetText(Text.UpdateModButtonToolTip, selectedProfileName); + deleteModButton.Enabled = enabled; + deleteModButton.ToolTip = HelperMethods.GetText(Text.DeleteModButtonToolTip, selectedProfileName); + } - Color col = enabled ? colGreen : colInactive; + Color col = enabled ? colGreen : colInactive; - if (OS.IsWindows) - modSettingsProfileDropDown.TextColor = col; + if (OS.IsWindows) + modSettingsProfileDropDown.TextColor = col; - settingsProfileLabel.TextColor = col; + settingsProfileLabel.TextColor = col; - if (enabled) - modSettingsProfileDropDown.SelectedIndex = profileDropDown.SelectedIndex; - } + if (enabled) + modSettingsProfileDropDown.SelectedIndex = profileDropDown.SelectedIndex; + } - /// - /// Sets the global and then changes the state of accordingly. - /// - /// The state that should be set to. - private void SetPlayButtonState(PlayButtonState state) + /// + /// Sets the global and then changes the state of accordingly. + /// + /// The state that should be set to. + private void SetPlayButtonState(PlayButtonState state) + { + updateState = state; + switch (updateState) { - updateState = state; - switch (updateState) - { - case PlayButtonState.Download: - case PlayButtonState.Downloading: - case PlayButtonState.Select11: - case PlayButtonState.Install: - case PlayButtonState.Play: playButton.Enabled = true; break; - - case PlayButtonState.Installing: - case PlayButtonState.Playing: playButton.Enabled = false; break; - } - playButton.Text = GetPlayButtonText(); - playButton.ToolTip = GetPlayButtonTooltip(); + case PlayButtonState.Download: + case PlayButtonState.Downloading: + case PlayButtonState.Select11: + case PlayButtonState.Install: + case PlayButtonState.Play: playButton.Enabled = true; break; + + case PlayButtonState.Installing: + case PlayButtonState.Playing: playButton.Enabled = false; break; + } + playButton.Text = GetPlayButtonText(); + playButton.ToolTip = GetPlayButtonTooltip(); - playButton.Invalidate(); + playButton.Invalidate(); - UpdateModSettingsState(); - } + UpdateModSettingsState(); + } - /// - /// Sets the global and then changes the state of accordingly. - /// - /// The state that should be set to. - private void SetApkButtonState(ApkButtonState state) + /// + /// Sets the global and then changes the state of accordingly. + /// + /// The state that should be set to. + private void SetApkButtonState(ApkButtonState state) + { + apkButtonState = state; + switch (apkButtonState) { - apkButtonState = state; - switch (apkButtonState) - { - case ApkButtonState.Create: apkButton.Enabled = true; break; - case ApkButtonState.Creating: apkButton.Enabled = false; break; - } - apkButton.Text = GetApkButtonText(); + case ApkButtonState.Create: apkButton.Enabled = true; break; + case ApkButtonState.Creating: apkButton.Enabled = false; break; } + apkButton.Text = GetApkButtonText(); + } - /// - /// This returns the text that should have depending on the global updateState. - /// - /// The text as a , or if the current State is invalid. - private string GetPlayButtonText() + /// + /// This returns the text that should have depending on the global updateState. + /// + /// The text as a , or if the current State is invalid. + private string GetPlayButtonText() + { + switch (updateState) { - switch (updateState) - { - case PlayButtonState.Download: return Text.Download; - case PlayButtonState.Downloading: return Text.Abort; - case PlayButtonState.Select11: return Text.Select11; - case PlayButtonState.Install: return Text.Install; - case PlayButtonState.Installing: return Text.Installing; - case PlayButtonState.Play: return Text.Play; - case PlayButtonState.Playing: return Text.Playing; - default: return null; - } + case PlayButtonState.Download: return Text.Download; + case PlayButtonState.Downloading: return Text.Abort; + case PlayButtonState.Select11: return Text.Select11; + case PlayButtonState.Install: return Text.Install; + case PlayButtonState.Installing: return Text.Installing; + case PlayButtonState.Play: return Text.Play; + case PlayButtonState.Playing: return Text.Playing; + default: return null; } + } - /// - /// This returns the tooltip that should have depending on the global updateState. - /// - /// The tooltip as a , or if the current State is invalid. - private string GetPlayButtonTooltip() + /// + /// This returns the tooltip that should have depending on the global updateState. + /// + /// The tooltip as a , or if the current State is invalid. + private string GetPlayButtonTooltip() + { + string profileName = ((profileDropDown != null) && (profileDropDown.Items.Count > 0)) ? profileDropDown.Items[profileDropDown.SelectedIndex].Text : ""; + switch (updateState) { - string profileName = ((profileDropDown != null) && (profileDropDown.Items.Count > 0)) ? profileDropDown.Items[profileDropDown.SelectedIndex].Text : ""; - switch (updateState) - { - case PlayButtonState.Download: return Text.PlayButtonDownloadToolTip; - case PlayButtonState.Downloading: return Text.PlayButtonDownloadToolTip; - case PlayButtonState.Select11: return Text.PlayButtonSelect11ToolTip; - case PlayButtonState.Install: return playButton.ToolTip = HelperMethods.GetText(Text.PlayButtonInstallToolTip, profileName); - case PlayButtonState.Installing: return Text.PlayButtonInstallingToolTip; - case PlayButtonState.Play: return HelperMethods.GetText(Text.PlayButtonPlayToolTip, profileName); - case PlayButtonState.Playing: return Text.PlayButtonPlayingToolTip; - default: return null; - } + case PlayButtonState.Download: return Text.PlayButtonDownloadToolTip; + case PlayButtonState.Downloading: return Text.PlayButtonDownloadToolTip; + case PlayButtonState.Select11: return Text.PlayButtonSelect11ToolTip; + case PlayButtonState.Install: return playButton.ToolTip = HelperMethods.GetText(Text.PlayButtonInstallToolTip, profileName); + case PlayButtonState.Installing: return Text.PlayButtonInstallingToolTip; + case PlayButtonState.Play: return HelperMethods.GetText(Text.PlayButtonPlayToolTip, profileName); + case PlayButtonState.Playing: return Text.PlayButtonPlayingToolTip; + default: return null; } + } - /// - /// This returns the text that the apkButton should have depending on the global updateState. - /// - /// The text as a , or if the current State is invalid. - private string GetApkButtonText() + /// + /// This returns the text that the apkButton should have depending on the global updateState. + /// + /// The text as a , or if the current State is invalid. + private string GetApkButtonText() + { + switch (apkButtonState) { - switch (apkButtonState) - { - case ApkButtonState.Create: return Text.CreateAPK; - case ApkButtonState.Creating: return Text.CreatingAPK; - default: return null; - } + case ApkButtonState.Create: return Text.CreateAPK; + case ApkButtonState.Creating: return Text.CreatingAPK; + default: return null; } + } - /// - /// Loads valid profile entries and reloads the necessary UI components. - /// - private void LoadProfilesAndAdjustLists() - { - // Reset loaded profiles - profileDropDown.Items.Clear(); - profileList.Clear(); - profileIndex = null; - - // Load the profileList - profileList = Profile.LoadProfiles(); - - // Add profile names to the profileDropDown - foreach (ProfileXML profile in profileList) - { - // Archive version notes - if (!profile.Installable) - { - if (profile.Name.Contains("Community Updates")) - profile.ProfileNotes = Text.ArchiveNotesCommunityUpdates; - else - profile.ProfileNotes = Text.ArchiveNotesMods + "\n\n" + profile.ProfileNotes; - } - - profileDropDown.Items.Add(profile.Name); - } - - // Read the value from the config - string profIndexString = ReadFromConfig("ProfileIndex"); - - // Check if either no profile was found or the setting says that the last current profile didn't exist - if (profileDropDown.Items.Count == 0) - profileIndex = null; - else - { - // We know that profiles exist at this point, so we're going to point it to 0 instead so the following code doesn't fail - if (profIndexString == "null") - profIndexString = "0"; - - // We parse from the settings, and check if profiles got deleted from the last time the launcher has been selected. if yes, we revert the last selection to 0; - int intParseResult = Int32.Parse(profIndexString); - profileIndex = intParseResult; - if (profileIndex >= profileDropDown.Items.Count) - profileIndex = 0; - profileDropDown.SelectedIndex = profileIndex.Value; - } + /// + /// Loads valid profile entries and reloads the necessary UI components. + /// + private void LoadProfilesAndAdjustLists() + { + // Reset loaded profiles + profileDropDown.Items.Clear(); + profileList.Clear(); + profileIndex = null; - // Update stored profiles in the Profile Settings tab - modSettingsProfileDropDown.Items.Clear(); - modSettingsProfileDropDown.Items.AddRange(profileDropDown.Items); - modSettingsProfileDropDown.SelectedIndex = profileDropDown.Items.Count != 0 ? 0 : -1; + // Load the profileList + profileList = Profile.LoadProfiles(); - // Refresh the author and version label on the main tab - if (profileList.Count > 0) + // Add profile names to the profileDropDown + foreach (ProfileXML profile in profileList) + { + // Archive version notes + if (!profile.Installable) { - profileAuthorLabel.Text = Text.Author + " " + profileList[profileDropDown.SelectedIndex].Author; - profileVersionLabel.Text = Text.VersionLabel + " " + profileList[profileDropDown.SelectedIndex].Version; + if (profile.Name.Contains("Community Updates")) + profile.ProfileNotes = Text.ArchiveNotesCommunityUpdates; + else + profile.ProfileNotes = Text.ArchiveNotesMods + "\n\n" + profile.ProfileNotes; } - log.Info("Reloading UI components after loading successful."); - - UpdateStateMachine(); + profileDropDown.Items.Add(profile.Name); } + // Read the value from the config + string profIndexString = ReadFromConfig("ProfileIndex"); - /// - /// Deletes a profile and reloads the necessary UI components. - /// - /// The profile to delete. - private void DeleteProfileAndAdjustLists(ProfileXML profile) + // Check if either no profile was found or the setting says that the last current profile didn't exist + if (profileDropDown.Items.Count == 0) + profileIndex = null; + else { - Profile.DeleteProfile(profile); - LoadProfilesAndAdjustLists(); + // We know that profiles exist at this point, so we're going to point it to 0 instead so the following code doesn't fail + if (profIndexString == "null") + profIndexString = "0"; + + // We parse from the settings, and check if profiles got deleted from the last time the launcher has been selected. if yes, we revert the last selection to 0; + int intParseResult = Int32.Parse(profIndexString); + profileIndex = intParseResult; + if (profileIndex >= profileDropDown.Items.Count) + profileIndex = 0; + profileDropDown.SelectedIndex = profileIndex.Value; } - private void ArchiveProfileAndAdjustLists(ProfileXML profile) - { - Profile.ArchiveProfile(profile); - LoadProfilesAndAdjustLists(); - } + // Update stored profiles in the Profile Settings tab + modSettingsProfileDropDown.Items.Clear(); + modSettingsProfileDropDown.Items.AddRange(profileDropDown.Items); + modSettingsProfileDropDown.SelectedIndex = profileDropDown.Items.Count != 0 ? 0 : -1; - /// Enables and changes colors for and accordingly. - private void EnableMirrorControlsAccordingly() + // Refresh the author and version label on the main tab + if (profileList.Count > 0) { - bool enabled = (bool)customMirrorCheck.Checked; - customMirrorTextBox.Enabled = enabled; - mirrorDropDown.Enabled = !enabled; - // Not sure why the dropdown menu needs this hack, but the textBox does not. - //TODO: eto feature request - if (OS.IsWindows) - mirrorDropDown.TextColor = mirrorDropDown.Enabled ? colGreen : colInactive; - mirrorLabel.TextColor = !enabled ? colGreen : colInactive; + profileAuthorLabel.Text = Text.Author + " " + profileList[profileDropDown.SelectedIndex].Author; + profileVersionLabel.Text = Text.VersionLabel + " " + profileList[profileDropDown.SelectedIndex].Version; } + + log.Info("Reloading UI components after loading successful."); + + UpdateStateMachine(); + } + + + /// + /// Deletes a profile and reloads the necessary UI components. + /// + /// The profile to delete. + private void DeleteProfileAndAdjustLists(ProfileXML profile) + { + Profile.DeleteProfile(profile); + LoadProfilesAndAdjustLists(); + } + + private void ArchiveProfileAndAdjustLists(ProfileXML profile) + { + Profile.ArchiveProfile(profile); + LoadProfilesAndAdjustLists(); + } + + /// Enables and changes colors for and accordingly. + private void EnableMirrorControlsAccordingly() + { + bool enabled = (bool)customMirrorCheck.Checked; + customMirrorTextBox.Enabled = enabled; + mirrorDropDown.Enabled = !enabled; + // Not sure why the dropdown menu needs this hack, but the textBox does not. + //TODO: eto feature request + if (OS.IsWindows) + mirrorDropDown.TextColor = mirrorDropDown.Enabled ? colGreen : colInactive; + mirrorLabel.TextColor = !enabled ? colGreen : colInactive; } } \ No newline at end of file diff --git a/AM2RLauncher/AM2RLauncher/MainForm/MainForm.UI.cs b/AM2RLauncher/AM2RLauncher/MainForm/MainForm.UI.cs index d56cc78..d018c46 100644 --- a/AM2RLauncher/AM2RLauncher/MainForm/MainForm.UI.cs +++ b/AM2RLauncher/AM2RLauncher/MainForm/MainForm.UI.cs @@ -14,914 +14,913 @@ using System.IO; using System.Linq; using System.Threading; -namespace AM2RLauncher +namespace AM2RLauncher; + +/// +/// Basically our Launcher window. +/// +public partial class MainForm : Form { + // Load reference to logger /// - /// Basically our Launcher window. + /// Our log object, that handles logging the current execution to a file. /// - public partial class MainForm : Form - { - // Load reference to logger - /// - /// Our log object, that handles logging the current execution to a file. - /// - private static readonly ILog log = LogManager.GetLogger(typeof(MainForm)); - - /// - /// The current Launcher version. - /// - private const string VERSION = LauncherUpdater.VERSION; - - - /// - /// An enum, that has possible states for the play button. - /// - public enum PlayButtonState - { - Download, - Downloading, - Select11, - Install, - Installing, - Play, - Playing - } - - /// - /// An enum, that has different states for . - /// - public enum ApkButtonState - { - Create, - Creating - } - - /// - /// This variable has the current global state of the Launcher. - /// - private static PlayButtonState updateState = PlayButtonState.Download; - /// - /// This variable has the current global statue of the . - /// - private static ApkButtonState apkButtonState = ApkButtonState.Create; - - /// - /// Stores the index for . - /// - private static int? profileIndex = null; - /// - /// Stores the index for . - /// - private static int mirrorIndex = 0; - /// - /// Stores the current mirror from either or . - /// - private static string currentMirror; - - private static readonly bool isInternetThere = Core.IsInternetThere; - - private static readonly bool isThisRunningFromWine = OS.IsThisRunningFromWine; - - private static bool singleInstance; - - // This mutex needs to CONTINUE existing for the entire application's lifetime, or else the rest of this won't ever work! - // We're basically using it to key a thread and scan for other instances of that tag. - private Mutex mutex = new Mutex(true, "AM2RLauncher", out singleInstance); - - public MainForm() - { - // Exit if we're already running the AM2RLauncher - // Thanks, StackOverflow! https://stackoverflow.com/questions/184084/how-to-force-c-sharp-net-app-to-run-only-one-instance-in-windows - if (!singleInstance) - { - // If on Windows, set the original app to the foreground window to prevent confusion - if (OS.IsWindows) - { - Process current = Process.GetCurrentProcess(); - Process process = Process.GetProcessesByName(current.ProcessName).First(p => p.Id == current.Id); - if (process != null) - Core.SetForegroundWindow(process.MainWindowHandle); - } - Environment.Exit(0); - } + private static readonly ILog log = LogManager.GetLogger(typeof(MainForm)); - log.Info("Mutex check passed. Entering main thread."); - log.Info("Current Launcher Version: " + VERSION); - log.Info("Current Platform-ID is: " + Platform.ID); - log.Info("Current OS is: " + OS.Name); + /// + /// The current Launcher version. + /// + private const string VERSION = LauncherUpdater.VERSION; - // Set the Current Directory to the path the Launcher is located. Fixes some relative path issues. - Environment.CurrentDirectory = CrossPlatformOperations.CurrentPath; - log.Info("Set Launcher CWD to " + Environment.CurrentDirectory); - // But log actual folder location nonetheless - log.Info("Actual Launcher location: " + Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory)); + /// + /// An enum, that has possible states for the play button. + /// + public enum PlayButtonState + { + Download, + Downloading, + Select11, + Install, + Installing, + Play, + Playing + } - // Set the language to what User wanted or choose local language - string userLanguage = ReadFromConfig("Language").ToLower(); - if (!userLanguage.Equals("default")) - Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultures(CultureTypes.AllCultures).First(c => c.NativeName.ToLower().Contains(userLanguage)); + /// + /// An enum, that has different states for . + /// + public enum ApkButtonState + { + Create, + Creating + } - log.Info("Language has been set to: " + Thread.CurrentThread.CurrentUICulture.EnglishName); + /// + /// This variable has the current global state of the Launcher. + /// + private static PlayButtonState updateState = PlayButtonState.Download; + /// + /// This variable has the current global statue of the . + /// + private static ApkButtonState apkButtonState = ApkButtonState.Create; - #region VARIABLE INITIALIZATION - log.Info("Beginning UI initialization..."); + /// + /// Stores the index for . + /// + private static int? profileIndex = null; + /// + /// Stores the index for . + /// + private static int mirrorIndex = 0; + /// + /// Stores the current mirror from either or . + /// + private static string currentMirror; - Bitmap am2rIcon = new Bitmap(Resources.AM2RIcon); + private static readonly bool isInternetThere = Core.IsInternetThere; - // System tray indicator - ButtonMenuItem showButton = new ButtonMenuItem { Text = Text.TrayButtonShow }; - trayIndicator = new TrayIndicator - { - Menu = new ContextMenu(showButton), - Title = "AM2RLauncher", - Visible = false, - Image = am2rIcon - }; + private static readonly bool isThisRunningFromWine = OS.IsThisRunningFromWine; - // Create MenuBar with defaults for mac - if (OS.IsMac) - Menu = new MenuBar(); + private static bool singleInstance; - // Create array from validCount - profileList = new List(); + // This mutex needs to CONTINUE existing for the entire application's lifetime, or else the rest of this won't ever work! + // We're basically using it to key a thread and scan for other instances of that tag. + private Mutex mutex = new Mutex(true, "AM2RLauncher", out singleInstance); - //TODO: whenever profileDropDown gets rewritten to use a datastore, scrap this - profileNames = new List(); - foreach (var profile in profileList) + public MainForm() + { + // Exit if we're already running the AM2RLauncher + // Thanks, StackOverflow! https://stackoverflow.com/questions/184084/how-to-force-c-sharp-net-app-to-run-only-one-instance-in-windows + if (!singleInstance) + { + // If on Windows, set the original app to the foreground window to prevent confusion + if (OS.IsWindows) { - profileNames.Add(profile.Name); + Process current = Process.GetCurrentProcess(); + Process process = Process.GetProcessesByName(current.ProcessName).First(p => p.Id == current.Id); + if (process != null) + Core.SetForegroundWindow(process.MainWindowHandle); } + Environment.Exit(0); + } - // Custom splash texts - string splash = Splash.GetSplash(); - log.Info("Randomly chosen splash: " + splash); + log.Info("Mutex check passed. Entering main thread."); + log.Info("Current Launcher Version: " + VERSION); + log.Info("Current Platform-ID is: " + Platform.ID); + log.Info("Current OS is: " + OS.Name); - Font smallButtonFont = new Font(SystemFont.Default, 10); + // Set the Current Directory to the path the Launcher is located. Fixes some relative path issues. + Environment.CurrentDirectory = CrossPlatformOperations.CurrentPath; + log.Info("Set Launcher CWD to " + Environment.CurrentDirectory); - // Create mirror list - eventually this should be platform specific! - // We do this as a List so we can add more dynamically on user input... if necessary. - mirrorList = CrossPlatformOperations.GenerateMirrorList(); + // But log actual folder location nonetheless + log.Info("Actual Launcher location: " + Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory)); - // Create mirror list - // We do this as a list for 1) make this dynamic and 2) make ETO happy - mirrorDescriptionList = new List(); - // Add each entry dynamically instead of hard-coding it to two. If we have neither a github or gitlab mirror, we use the mirror itself as text - foreach (var mirror in mirrorList) - { - string text = mirror; - if (text.Contains("github.com")) text = Text.MirrorGithubText; - else if (text.Contains("gitlab.com")) text = Text.MirrorGitlabText; - mirrorDescriptionList.Add(new ListItem { Key = mirror, Text = text }); - } - #endregion - - Icon = new Icon(1f, am2rIcon); - Title = "AM2RLauncher " + VERSION + ": " + splash; - MinimumSize = new Size(500, 400); - // TODO: for some reason, this doesn't work on Linux. Was reported at eto, stays here until its fixed - ClientSize = new Size(Int32.Parse(ReadFromConfig("Width")), Int32.Parse(ReadFromConfig("Height"))); - if (ClientSize.Width < 500) - ClientSize = new Size(500, ClientSize.Height); - if (ClientSize.Height < 400) - ClientSize = new Size(ClientSize.Width, 400); - log.Info("Start the launcher with Size: " + ClientSize.Width + ", " + ClientSize.Height); - if (Boolean.Parse(ReadFromConfig("IsMaximized"))) Maximize(); - - Drawable drawable = new Drawable { BackgroundColor = colBGNoAlpha }; - - // Drawable paint event - drawable.Paint += DrawablePaintEvent; - // Some systems don't call the paintEvent by default and only do so after actual resizing - if (OS.IsMac) - LoadComplete += (_, _) => { Size = new Size(Size.Width + 1, Size.Height); Size = new Size(Size.Width - 1, Size.Height);}; - - #region MAIN WINDOW - - // Center buttons/interface panel - var centerInterface = new DynamicLayout(); - - // PLAY button - playButton = new ColorButton - { - ToolTip = "", - BackgroundColorHover = colBGHover, - Height = 40, - Width = 250, - TextColor = colGreen, - TextColorDisabled = colInactive, - BackgroundColor = colBG, - FrameColor = colGreen, - FrameColorDisabled = colInactive - }; + // Set the language to what User wanted or choose local language + string userLanguage = ReadFromConfig("Language").ToLower(); + if (!userLanguage.Equals("default")) + Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultures(CultureTypes.AllCultures).First(c => c.NativeName.ToLower().Contains(userLanguage)); - UpdateStateMachine(); + log.Info("Language has been set to: " + Thread.CurrentThread.CurrentUICulture.EnglishName); - SetPlayButtonState(updateState); + #region VARIABLE INITIALIZATION + log.Info("Beginning UI initialization..."); - centerInterface.AddRow(playButton); + Bitmap am2rIcon = new Bitmap(Resources.AM2RIcon); - // 2px spacer between playButton and apkButton (Windows only) - if (OS.IsWindows) centerInterface.AddRow(new Label { BackgroundColor = colBG, Height = 2 }); + // System tray indicator + ButtonMenuItem showButton = new ButtonMenuItem { Text = Text.TrayButtonShow }; + trayIndicator = new TrayIndicator + { + Menu = new ContextMenu(showButton), + Title = "AM2RLauncher", + Visible = false, + Image = am2rIcon + }; + + // Create MenuBar with defaults for mac + if (OS.IsMac) + Menu = new MenuBar(); + + // Create array from validCount + profileList = new List(); + + //TODO: whenever profileDropDown gets rewritten to use a datastore, scrap this + profileNames = new List(); + foreach (var profile in profileList) + { + profileNames.Add(profile.Name); + } - // APK button - apkButton = new ColorButton - { - Text = Text.CreateAPK, - Height = 40, - Width = 250, - TextColor = colGreen, - BackgroundColor = colBG, - FrameColor = colGreen, - BackgroundColorHover = colBGHover - }; + // Custom splash texts + string splash = Splash.GetSplash(); + log.Info("Randomly chosen splash: " + splash); - centerInterface.AddRow(apkButton); + Font smallButtonFont = new Font(SystemFont.Default, 10); - progressBar = new ProgressBar - { - Visible = false, - Height = 15 - }; + // Create mirror list - eventually this should be platform specific! + // We do this as a List so we can add more dynamically on user input... if necessary. + mirrorList = CrossPlatformOperations.GenerateMirrorList(); - // 4px spacer between APK button and progressBar (Windows only) - if (OS.IsWindows) centerInterface.AddRow(new Label { BackgroundColor = colBG, Height = 4 }); + // Create mirror list + // We do this as a list for 1) make this dynamic and 2) make ETO happy + mirrorDescriptionList = new List(); + // Add each entry dynamically instead of hard-coding it to two. If we have neither a github or gitlab mirror, we use the mirror itself as text + foreach (var mirror in mirrorList) + { + string text = mirror; + if (text.Contains("github.com")) text = Text.MirrorGithubText; + else if (text.Contains("gitlab.com")) text = Text.MirrorGitlabText; + mirrorDescriptionList.Add(new ListItem { Key = mirror, Text = text }); + } + #endregion - centerInterface.AddRow(progressBar); + Icon = new Icon(1f, am2rIcon); + Title = "AM2RLauncher " + VERSION + ": " + splash; + MinimumSize = new Size(500, 400); + // TODO: for some reason, this doesn't work on Linux. Was reported at eto, stays here until its fixed + ClientSize = new Size(Int32.Parse(ReadFromConfig("Width")), Int32.Parse(ReadFromConfig("Height"))); + if (ClientSize.Width < 500) + ClientSize = new Size(500, ClientSize.Height); + if (ClientSize.Height < 400) + ClientSize = new Size(ClientSize.Width, 400); + log.Info("Start the launcher with Size: " + ClientSize.Width + ", " + ClientSize.Height); + if (Boolean.Parse(ReadFromConfig("IsMaximized"))) Maximize(); + + Drawable drawable = new Drawable { BackgroundColor = colBGNoAlpha }; + + // Drawable paint event + drawable.Paint += DrawablePaintEvent; + // Some systems don't call the paintEvent by default and only do so after actual resizing + if (OS.IsMac) + LoadComplete += (_, _) => { Size = new Size(Size.Width + 1, Size.Height); Size = new Size(Size.Width - 1, Size.Height);}; + + #region MAIN WINDOW + + // Center buttons/interface panel + var centerInterface = new DynamicLayout(); + + // PLAY button + playButton = new ColorButton + { + ToolTip = "", + BackgroundColorHover = colBGHover, + Height = 40, + Width = 250, + TextColor = colGreen, + TextColorDisabled = colInactive, + BackgroundColor = colBG, + FrameColor = colGreen, + FrameColorDisabled = colInactive + }; - progressLabel = new Label - { - BackgroundColor = colBG, - Height = 15, - Text = "", - TextColor = colGreen, - Visible = false - }; + UpdateStateMachine(); - centerInterface.AddRow(progressLabel); + SetPlayButtonState(updateState); - // 3px spacer between progressBar and profile label (Windows only) - if (OS.IsWindows) centerInterface.AddRow(new Label { BackgroundColor = colBG, Height = 3 }); + centerInterface.AddRow(playButton); - profileLabel = new Label - { - BackgroundColor = colBG, - Height = 15, - Text = Text.CurrentProfile, - TextColor = colGreen - }; + // 2px spacer between playButton and apkButton (Windows only) + if (OS.IsWindows) centerInterface.AddRow(new Label { BackgroundColor = colBG, Height = 2 }); - centerInterface.AddRow(profileLabel); + // APK button + apkButton = new ColorButton + { + Text = Text.CreateAPK, + Height = 40, + Width = 250, + TextColor = colGreen, + BackgroundColor = colBG, + FrameColor = colGreen, + BackgroundColorHover = colBGHover + }; + + centerInterface.AddRow(apkButton); + + progressBar = new ProgressBar + { + Visible = false, + Height = 15 + }; - // Profiles dropdown + // 4px spacer between APK button and progressBar (Windows only) + if (OS.IsWindows) centerInterface.AddRow(new Label { BackgroundColor = colBG, Height = 4 }); - // Yes, we know this looks horrific on GTK. Sorry. - // We're not exactly in a position to rewrite the entire DropDown object as a Drawable child, but if you want to, you're more than welcome! - // Mac gets a default BackgroundColor because it looks waaaaaaay better. - profileDropDown = new DropDown - { - TextColor = colGreen, - BackgroundColor = OS.IsWindows ? colBGNoAlpha : new Color() - }; - // In order to not have conflicting theming, we just always respect the users theme for dropdown on GTK. - if (OS.IsLinux) - profileDropDown = new DropDown(); + centerInterface.AddRow(progressBar); - profileDropDown.Items.AddRange(profileNames); // It's actually more comfortable if it's outside, because of GTK shenanigans + progressLabel = new Label + { + BackgroundColor = colBG, + Height = 15, + Text = "", + TextColor = colGreen, + Visible = false + }; - centerInterface.AddRow(profileDropDown); + centerInterface.AddRow(progressLabel); - // Profiles label - profileAuthorLabel = new Label - { - BackgroundColor = colBG, - Height = 16, - Text = Text.Author + " ", - TextColor = colGreen - }; + // 3px spacer between progressBar and profile label (Windows only) + if (OS.IsWindows) centerInterface.AddRow(new Label { BackgroundColor = colBG, Height = 3 }); - centerInterface.AddRow(profileAuthorLabel); + profileLabel = new Label + { + BackgroundColor = colBG, + Height = 15, + Text = Text.CurrentProfile, + TextColor = colGreen + }; - profileVersionLabel = new Label - { - BackgroundColor = colBG, - Height = 16, - Text = Text.VersionLabel + " ", - TextColor = colGreen - }; + centerInterface.AddRow(profileLabel); - centerInterface.AddRow(profileVersionLabel); + // Profiles dropdown - saveWarningLabel = new Label - { - Visible = false, - BackgroundColor = colBG, - Width = 20, - Height = 55, - Text = Text.SaveLocationWarning, - TextColor = colRed - }; + // Yes, we know this looks horrific on GTK. Sorry. + // We're not exactly in a position to rewrite the entire DropDown object as a Drawable child, but if you want to, you're more than welcome! + // Mac gets a default BackgroundColor because it looks waaaaaaay better. + profileDropDown = new DropDown + { + TextColor = colGreen, + BackgroundColor = OS.IsWindows ? colBGNoAlpha : new Color() + }; + // In order to not have conflicting theming, we just always respect the users theme for dropdown on GTK. + if (OS.IsLinux) + profileDropDown = new DropDown(); - centerInterface.AddRow(saveWarningLabel); + profileDropDown.Items.AddRange(profileNames); // It's actually more comfortable if it's outside, because of GTK shenanigans + centerInterface.AddRow(profileDropDown); - // Social buttons - Bitmap redditIcon = new Bitmap(Resources.redditIcon48); - var redditButton = new ImageButton { ToolTip = Text.RedditToolTip, Image = redditIcon }; - redditButton.Click += (_, _) => CrossPlatformOperations.OpenURL("https://www.reddit.com/r/AM2R"); + // Profiles label + profileAuthorLabel = new Label + { + BackgroundColor = colBG, + Height = 16, + Text = Text.Author + " ", + TextColor = colGreen + }; - Bitmap githubIcon = new Bitmap(Resources.githubIcon48); - var githubButton = new ImageButton { ToolTip = Text.GithubToolTip, Image = githubIcon }; - githubButton.Click += (_, _) => CrossPlatformOperations.OpenURL("https://www.github.com/AM2R-Community-Developers"); + centerInterface.AddRow(profileAuthorLabel); - Bitmap youtubeIcon = new Bitmap(Resources.youtubeIcon48); - var youtubeButton = new ImageButton { ToolTip = Text.YoutubeToolTip, Image = youtubeIcon }; - youtubeButton.Click += (_, _) => CrossPlatformOperations.OpenURL("https://www.youtube.com/c/AM2RCommunityUpdates"); + profileVersionLabel = new Label + { + BackgroundColor = colBG, + Height = 16, + Text = Text.VersionLabel + " ", + TextColor = colGreen + }; - Bitmap discordIcon = new Bitmap(Resources.discordIcon48); - var discordButton = new ImageButton { ToolTip = Text.DiscordToolTip, Image = discordIcon }; - discordButton.Click += (_, _) => CrossPlatformOperations.OpenURL("https://discord.gg/nk7UYPbd5u"); + centerInterface.AddRow(profileVersionLabel); + saveWarningLabel = new Label + { + Visible = false, + BackgroundColor = colBG, + Width = 20, + Height = 55, + Text = Text.SaveLocationWarning, + TextColor = colRed + }; - // Social button panel - var socialPanel = new DynamicLayout(); - socialPanel.BeginVertical(); - socialPanel.AddRow(redditButton); - socialPanel.AddRow(githubButton); - socialPanel.AddRow(youtubeButton); - socialPanel.AddRow(discordButton); - socialPanel.EndVertical(); + centerInterface.AddRow(saveWarningLabel); - // Version number label - Label versionLabel = new Label - { - Text = "v" + VERSION + (isThisRunningFromWine ? "-WINE" : ""), - Width = 48, TextAlignment = TextAlignment.Right, TextColor = colGreen, - Font = new Font(SystemFont.Default, 12) - }; + // Social buttons + Bitmap redditIcon = new Bitmap(Resources.redditIcon48); + var redditButton = new ImageButton { ToolTip = Text.RedditToolTip, Image = redditIcon }; + redditButton.Click += (_, _) => CrossPlatformOperations.OpenURL("https://www.reddit.com/r/AM2R"); - // Tie everything together - var mainLayout = new DynamicLayout(); + Bitmap githubIcon = new Bitmap(Resources.githubIcon48); + var githubButton = new ImageButton { ToolTip = Text.GithubToolTip, Image = githubIcon }; + githubButton.Click += (_, _) => CrossPlatformOperations.OpenURL("https://www.github.com/AM2R-Community-Developers"); - mainLayout.BeginHorizontal(); - mainLayout.AddColumn(null, socialPanel); + Bitmap youtubeIcon = new Bitmap(Resources.youtubeIcon48); + var youtubeButton = new ImageButton { ToolTip = Text.YoutubeToolTip, Image = youtubeIcon }; + youtubeButton.Click += (_, _) => CrossPlatformOperations.OpenURL("https://www.youtube.com/c/AM2RCommunityUpdates"); - mainLayout.AddSpace(); - mainLayout.AddColumn(null, centerInterface, null); - mainLayout.AddSpace(); + Bitmap discordIcon = new Bitmap(Resources.discordIcon48); + var discordButton = new ImageButton { ToolTip = Text.DiscordToolTip, Image = discordIcon }; + discordButton.Click += (_, _) => CrossPlatformOperations.OpenURL("https://discord.gg/nk7UYPbd5u"); - // Yes, I'm hard-coding this string. Linux users can english. - mainLayout.AddColumn(versionLabel, isThisRunningFromWine ? new Label { Text = "Unsupported", TextColor = colRed, TextAlignment = TextAlignment.Right } : null); - drawable.Content = mainLayout; + // Social button panel + var socialPanel = new DynamicLayout(); + socialPanel.BeginVertical(); + socialPanel.AddRow(redditButton); + socialPanel.AddRow(githubButton); + socialPanel.AddRow(youtubeButton); + socialPanel.AddRow(discordButton); + socialPanel.EndVertical(); - #endregion - #region TABS - - #region MAIN PAGE - // [MAIN PAGE] - TabPage mainPage = new TabPage - { - BackgroundColor = colBGNoAlpha, - Text = Text.PlayTab, - Content = drawable - }; - #endregion + // Version number label + Label versionLabel = new Label + { + Text = "v" + VERSION + (isThisRunningFromWine ? "-WINE" : ""), + Width = 48, TextAlignment = TextAlignment.Right, TextColor = colGreen, + Font = new Font(SystemFont.Default, 12) + }; - #region CHANGELOG PAGE - // [CHANGELOG] - Uri changelogUri = new Uri("https://am2r-community-developers.github.io/DistributionCenter/changelog.html"); - WebView changelogWebView = new WebView { Url = changelogUri }; + // Tie everything together + var mainLayout = new DynamicLayout(); - if (OS.IsUnix && !isInternetThere) - changelogWebView = new WebView(); + mainLayout.BeginHorizontal(); + mainLayout.AddColumn(null, socialPanel); - //TODO: these clip on gtk, Eto issue - Label changelogNoConnectionLabel = new Label - { - Text = Text.NoInternetConnection, - TextColor = colGreen, - TextAlignment = TextAlignment.Center, - }; + mainLayout.AddSpace(); + mainLayout.AddColumn(null, centerInterface, null); + mainLayout.AddSpace(); - TabPage changelogPage = new TabPage - { - BackgroundColor = colBGNoAlpha, - Text = Text.ChangelogTab, + // Yes, I'm hard-coding this string. Linux users can english. + mainLayout.AddColumn(versionLabel, isThisRunningFromWine ? new Label { Text = "Unsupported", TextColor = colRed, TextAlignment = TextAlignment.Right } : null); - Content = new TableLayout - { - Rows = - { - changelogWebView - } - } - }; + drawable.Content = mainLayout; - #endregion + #endregion - #region NEWS PAGE + #region TABS - // [NEWS] - Uri newsUri = new Uri("https://am2r-community-developers.github.io/DistributionCenter/news.html"); - WebView newsWebView = new WebView { Url = newsUri }; + #region MAIN PAGE + // [MAIN PAGE] + TabPage mainPage = new TabPage + { + BackgroundColor = colBGNoAlpha, + Text = Text.PlayTab, + Content = drawable + }; + #endregion - //TODO: why exactly is this check necessary? - if (OS.IsUnix && !isInternetThere) - newsWebView = new WebView(); + #region CHANGELOG PAGE + // [CHANGELOG] + Uri changelogUri = new Uri("https://am2r-community-developers.github.io/DistributionCenter/changelog.html"); + WebView changelogWebView = new WebView { Url = changelogUri }; - Label newsNoConnectionLabel = new Label - { - Text = Text.NoInternetConnection, - TextColor = colGreen, - TextAlignment = TextAlignment.Center - }; + if (OS.IsUnix && !isInternetThere) + changelogWebView = new WebView(); - TabPage newsPage = new TabPage - { - Text = Text.NewsTab, - BackgroundColor = colBGNoAlpha, + //TODO: these clip on gtk, Eto issue + Label changelogNoConnectionLabel = new Label + { + Text = Text.NoInternetConnection, + TextColor = colGreen, + TextAlignment = TextAlignment.Center, + }; - Content = new TableLayout - { - Rows = - { - newsWebView - } - } - }; + TabPage changelogPage = new TabPage + { + BackgroundColor = colBGNoAlpha, + Text = Text.ChangelogTab, - //TODO: this is hack because on linux / mac the other way doesn't work. eto issue? - if (OS.IsUnix && !isInternetThere) + Content = new TableLayout { - changelogPage.Content = new TableLayout + Rows = { - Rows = - { - null, - changelogNoConnectionLabel, - null - } - }; - newsPage.Content = new TableLayout - { - Rows = - { - null, - newsNoConnectionLabel, - null - } - }; + changelogWebView + } } + }; - #endregion + #endregion - #region SETTINGS PAGE + #region NEWS PAGE - // [LAUNCHER SETTINGS] - DynamicLayout settingsLayout = new DynamicLayout(); + // [NEWS] + Uri newsUri = new Uri("https://am2r-community-developers.github.io/DistributionCenter/news.html"); + WebView newsWebView = new WebView { Url = newsUri }; - // LanguageLabel - Label languageLabel = new Label - { - Text = Text.LanguageNotice, - TextColor = colGreen - }; + //TODO: why exactly is this check necessary? + if (OS.IsUnix && !isInternetThere) + newsWebView = new WebView(); + + Label newsNoConnectionLabel = new Label + { + Text = Text.NoInternetConnection, + TextColor = colGreen, + TextAlignment = TextAlignment.Center + }; + + TabPage newsPage = new TabPage + { + Text = Text.NewsTab, + BackgroundColor = colBGNoAlpha, - // Language DropDown menu + Content = new TableLayout + { + Rows = + { + newsWebView + } + } + }; - List languageList = new List + //TODO: this is hack because on linux / mac the other way doesn't work. eto issue? + if (OS.IsUnix && !isInternetThere) + { + changelogPage.Content = new TableLayout { - Text.SystemLanguage, - "Deutsch", - "English", - "Español", - "Français", - "Italiano", - "Português", - "Русский", - "日本語", - "中文(简体)" + Rows = + { + null, + changelogNoConnectionLabel, + null + } }; - - languageDropDown = new DropDown + newsPage.Content = new TableLayout { - TextColor = colGreen, - BackgroundColor = OS.IsWindows ? colBGNoAlpha : new Color() + Rows = + { + null, + newsNoConnectionLabel, + null + } }; - if (OS.IsLinux) - languageDropDown = new DropDown(); + } - languageDropDown.Items.AddRange(languageList); + #endregion - var tmpLanguage = ReadFromConfig("Language"); - languageDropDown.SelectedIndex = tmpLanguage == "Default" ? 0 : languageDropDown.Items.IndexOf(languageDropDown.Items.FirstOrDefault(x => x.Text.Equals(tmpLanguage))); + #region SETTINGS PAGE - if (languageDropDown.SelectedIndex == -1) - { - log.Info("User has tried to use " + tmpLanguage + " as a Language, but it was not found. Reverting to System Language"); - languageDropDown.SelectedIndex = 0; - } + // [LAUNCHER SETTINGS] + DynamicLayout settingsLayout = new DynamicLayout(); - // autoUpdateAM2R checkbox - autoUpdateAM2RCheck = new CheckBox - { - Checked = Boolean.Parse(ReadFromConfig("AutoUpdateAM2R")), - Text = Text.AutoUpdateAM2R, - TextColor = colGreen - }; + // LanguageLabel + Label languageLabel = new Label + { + Text = Text.LanguageNotice, + TextColor = colGreen + }; + // Language DropDown menu - // autoUpdateLauncher checkbox - autoUpdateLauncherCheck = new CheckBox - { - Checked = Boolean.Parse(ReadFromConfig("AutoUpdateLauncher")), - Text = Text.AutoUpdateLauncher, - TextColor = colGreen - }; + List languageList = new List + { + Text.SystemLanguage, + "Deutsch", + "English", + "Español", + "Français", + "Italiano", + "Português", + "Русский", + "日本語", + "中文(简体)" + }; + + languageDropDown = new DropDown + { + TextColor = colGreen, + BackgroundColor = OS.IsWindows ? colBGNoAlpha : new Color() + }; + if (OS.IsLinux) + languageDropDown = new DropDown(); - // HQ music, PC - hqMusicPCCheck = new CheckBox - { - Checked = Boolean.Parse(ReadFromConfig("MusicHQPC")), - Text = Text.HighQualityPC, - TextColor = colGreen - }; + languageDropDown.Items.AddRange(languageList); - // HQ music, Android - hqMusicAndroidCheck = new CheckBox - { - Checked = Boolean.Parse(ReadFromConfig("MusicHQAndroid")), - Text = Text.HighQualityAndroid, - TextColor = colGreen - }; + var tmpLanguage = ReadFromConfig("Language"); + languageDropDown.SelectedIndex = tmpLanguage == "Default" ? 0 : languageDropDown.Items.IndexOf(languageDropDown.Items.FirstOrDefault(x => x.Text.Equals(tmpLanguage))); - // Create game debug logs - profileDebugLogCheck = new CheckBox - { - Checked = Boolean.Parse(ReadFromConfig("ProfileDebugLog")), - Text = Text.ProfileDebugCheckBox, - TextColor = colGreen - }; - - // Custom environment variables label - Label customEnvVarLabel = new Label(); - if (OS.IsLinux) - { - customEnvVarLabel = new Label - { - Text = Text.CustomEnvVarLabel, - TextColor = colGreen - }; - } + if (languageDropDown.SelectedIndex == -1) + { + log.Info("User has tried to use " + tmpLanguage + " as a Language, but it was not found. Reverting to System Language"); + languageDropDown.SelectedIndex = 0; + } - // Custom environment variables textbox - customEnvVarTextBox = null; - if (OS.IsLinux) - { - customEnvVarTextBox = new TextBox - { - Text = ReadFromConfig("CustomEnvVar"), - BackgroundColor = colBGNoAlpha, - TextColor = colGreen - }; - } + // autoUpdateAM2R checkbox + autoUpdateAM2RCheck = new CheckBox + { + Checked = Boolean.Parse(ReadFromConfig("AutoUpdateAM2R")), + Text = Text.AutoUpdateAM2R, + TextColor = colGreen + }; - // Mirror list - mirrorLabel = new Label - { - Text = Text.DownloadSource, - TextColor = colGreen - }; - mirrorDropDown = new DropDown - { - TextColor = colGreen, - BackgroundColor = OS.IsWindows ? colBGNoAlpha : new Color() - }; - if (OS.IsLinux) - mirrorDropDown = new DropDown(); + // autoUpdateLauncher checkbox + autoUpdateLauncherCheck = new CheckBox + { + Checked = Boolean.Parse(ReadFromConfig("AutoUpdateLauncher")), + Text = Text.AutoUpdateLauncher, + TextColor = colGreen + }; - mirrorDropDown.Items.AddRange(mirrorDescriptionList); // As above, find a way to get this inside the dropDown definition - mirrorIndex = (Int32.Parse(ReadFromConfig("MirrorIndex")) < mirrorDropDown.Items.Count) ? Int32.Parse(ReadFromConfig("MirrorIndex")) - : 0; - mirrorDropDown.SelectedIndex = mirrorIndex; + // HQ music, PC + hqMusicPCCheck = new CheckBox + { + Checked = Boolean.Parse(ReadFromConfig("MusicHQPC")), + Text = Text.HighQualityPC, + TextColor = colGreen + }; - currentMirror = mirrorList[mirrorDropDown.SelectedIndex]; + // HQ music, Android + hqMusicAndroidCheck = new CheckBox + { + Checked = Boolean.Parse(ReadFromConfig("MusicHQAndroid")), + Text = Text.HighQualityAndroid, + TextColor = colGreen + }; - // Custom mirror - customMirrorCheck = new CheckBox + // Create game debug logs + profileDebugLogCheck = new CheckBox + { + Checked = Boolean.Parse(ReadFromConfig("ProfileDebugLog")), + Text = Text.ProfileDebugCheckBox, + TextColor = colGreen + }; + + // Custom environment variables label + Label customEnvVarLabel = new Label(); + if (OS.IsLinux) + { + customEnvVarLabel = new Label { - Checked = Boolean.Parse(ReadFromConfig("CustomMirrorEnabled")), - Text = Text.CustomMirrorCheck, + Text = Text.CustomEnvVarLabel, TextColor = colGreen }; + } - customMirrorTextBox = new TextBox + // Custom environment variables textbox + customEnvVarTextBox = null; + if (OS.IsLinux) + { + customEnvVarTextBox = new TextBox { - Text = ReadFromConfig("CustomMirrorText"), + Text = ReadFromConfig("CustomEnvVar"), BackgroundColor = colBGNoAlpha, TextColor = colGreen }; + } - EnableMirrorControlsAccordingly(); - - settingsLayout.BeginHorizontal(); - settingsLayout.AddSpace(); - settingsLayout.AddColumn(null, languageLabel, languageDropDown, autoUpdateAM2RCheck, autoUpdateLauncherCheck, hqMusicPCCheck, hqMusicAndroidCheck, profileDebugLogCheck, customEnvVarLabel, (Control)customEnvVarTextBox ?? new Label(), mirrorLabel, mirrorDropDown, customMirrorCheck, customMirrorTextBox, null); - settingsLayout.AddSpace(); + // Mirror list + mirrorLabel = new Label + { + Text = Text.DownloadSource, + TextColor = colGreen + }; - TabPage settingsPage = new TabPage - { - BackgroundColor = colBGNoAlpha, - Content = settingsLayout, - Text = Text.LauncherSettingsTab - }; + mirrorDropDown = new DropDown + { + TextColor = colGreen, + BackgroundColor = OS.IsWindows ? colBGNoAlpha : new Color() + }; + if (OS.IsLinux) + mirrorDropDown = new DropDown(); - #endregion + mirrorDropDown.Items.AddRange(mirrorDescriptionList); // As above, find a way to get this inside the dropDown definition + mirrorIndex = (Int32.Parse(ReadFromConfig("MirrorIndex")) < mirrorDropDown.Items.Count) ? Int32.Parse(ReadFromConfig("MirrorIndex")) + : 0; + mirrorDropDown.SelectedIndex = mirrorIndex; - #region MODSETTINGS PAGE + currentMirror = mirrorList[mirrorDropDown.SelectedIndex]; - // [MOD SETTINGS] - DynamicLayout modSettingsLayout = new DynamicLayout(); + // Custom mirror + customMirrorCheck = new CheckBox + { + Checked = Boolean.Parse(ReadFromConfig("CustomMirrorEnabled")), + Text = Text.CustomMirrorCheck, + TextColor = colGreen + }; + customMirrorTextBox = new TextBox + { + Text = ReadFromConfig("CustomMirrorText"), + BackgroundColor = colBGNoAlpha, + TextColor = colGreen + }; - addModButton = new ColorButton - { - ToolTip = null, - Text = Text.AddNewMod, - Font = smallButtonFont, - Height = 30, - Width = 275, - TextColor = colGreen, - BackgroundColor = colBG, - FrameColor = colGreen, - BackgroundColorHover = colBGHover - }; + EnableMirrorControlsAccordingly(); - Label modSpacer = new Label - { - Height = 14 - }; + settingsLayout.BeginHorizontal(); + settingsLayout.AddSpace(); + settingsLayout.AddColumn(null, languageLabel, languageDropDown, autoUpdateAM2RCheck, autoUpdateLauncherCheck, hqMusicPCCheck, hqMusicAndroidCheck, profileDebugLogCheck, customEnvVarLabel, (Control)customEnvVarTextBox ?? new Label(), mirrorLabel, mirrorDropDown, customMirrorCheck, customMirrorTextBox, null); + settingsLayout.AddSpace(); - settingsProfileLabel = new Label - { - Text = Text.CurrentProfile, - TextColor = colGreen, - Width = 275 - }; + TabPage settingsPage = new TabPage + { + BackgroundColor = colBGNoAlpha, + Content = settingsLayout, + Text = Text.LauncherSettingsTab + }; - modSettingsProfileDropDown = new DropDown - { - TextColor = colGreen, - BackgroundColor = OS.IsWindows ? colBGNoAlpha : new Color() - }; + #endregion - // In order to not have conflicting theming, we just always respect the users theme for dropdown on GTK. - if (OS.IsLinux) - modSettingsProfileDropDown = new DropDown(); + #region MODSETTINGS PAGE - modSettingsProfileDropDown.Items.AddRange(profileNames); // It's actually more comfortable if it's outside, because of GTK shenanigans + // [MOD SETTINGS] + DynamicLayout modSettingsLayout = new DynamicLayout(); - profileButton = new ColorButton - { - ToolTip = null, - Text = Text.OpenProfileFolder, - Font = smallButtonFont, - Height = 30, - Width = 275, - TextColor = colGreen, - BackgroundColor = colBG, - FrameColor = colGreen, - BackgroundColorHover = colBGHover - }; - saveButton = new ColorButton - { - ToolTip = null, - Text = Text.OpenSaveFolder, - Font = smallButtonFont, - Height = 30, - Width = 275, - TextColor = colGreen, - BackgroundColor = colBG, - FrameColor = colGreen, - BackgroundColorHover = colBGHover - }; + addModButton = new ColorButton + { + ToolTip = null, + Text = Text.AddNewMod, + Font = smallButtonFont, + Height = 30, + Width = 275, + TextColor = colGreen, + BackgroundColor = colBG, + FrameColor = colGreen, + BackgroundColorHover = colBGHover + }; + + Label modSpacer = new Label + { + Height = 14 + }; - updateModButton = new ColorButton - { - ToolTip = null, - Text = Text.UpdateModButtonText, - Font = smallButtonFont, - Height = 30, - Width = 275, - TextColor = colGreen, - BackgroundColor = colBG, - FrameColor = colGreen, - BackgroundColorHover = colBGHover - }; + settingsProfileLabel = new Label + { + Text = Text.CurrentProfile, + TextColor = colGreen, + Width = 275 + }; - deleteModButton = new ColorButton - { - ToolTip = null, - Text = Text.DeleteModButtonText, - Font = smallButtonFont, - Height = 30, - Width = 275, - TextColor = colGreen, - BackgroundColor = colBG, - FrameColor = colGreen, - BackgroundColorHover = colBGHover - }; + modSettingsProfileDropDown = new DropDown + { + TextColor = colGreen, + BackgroundColor = OS.IsWindows ? colBGNoAlpha : new Color() + }; - profileNotesTextArea = new TextArea - { - ReadOnly = true, - BackgroundColor = colBGNoAlpha, - TextColor = colInactive, - SpellCheck = false, - Width = 275, - Height = 150, - Text = Text.ProfileNotes - }; + // In order to not have conflicting theming, we just always respect the users theme for dropdown on GTK. + if (OS.IsLinux) + modSettingsProfileDropDown = new DropDown(); - modSettingsLayout.BeginHorizontal(); - modSettingsLayout.AddSpace(); - modSettingsLayout.AddColumn(null, addModButton, modSpacer, settingsProfileLabel, modSettingsProfileDropDown, profileButton, saveButton, updateModButton, deleteModButton, profileNotesTextArea, null); - modSettingsLayout.AddSpace(); + modSettingsProfileDropDown.Items.AddRange(profileNames); // It's actually more comfortable if it's outside, because of GTK shenanigans - TabPage modSettingsPage = new TabPage - { - BackgroundColor = colBGNoAlpha, - Content = modSettingsLayout, - Text = Text.ModSettingsTab - }; + profileButton = new ColorButton + { + ToolTip = null, + Text = Text.OpenProfileFolder, + Font = smallButtonFont, + Height = 30, + Width = 275, + TextColor = colGreen, + BackgroundColor = colBG, + FrameColor = colGreen, + BackgroundColorHover = colBGHover + }; + + saveButton = new ColorButton + { + ToolTip = null, + Text = Text.OpenSaveFolder, + Font = smallButtonFont, + Height = 30, + Width = 275, + TextColor = colGreen, + BackgroundColor = colBG, + FrameColor = colGreen, + BackgroundColorHover = colBGHover + }; + + updateModButton = new ColorButton + { + ToolTip = null, + Text = Text.UpdateModButtonText, + Font = smallButtonFont, + Height = 30, + Width = 275, + TextColor = colGreen, + BackgroundColor = colBG, + FrameColor = colGreen, + BackgroundColorHover = colBGHover + }; + + deleteModButton = new ColorButton + { + ToolTip = null, + Text = Text.DeleteModButtonText, + Font = smallButtonFont, + Height = 30, + Width = 275, + TextColor = colGreen, + BackgroundColor = colBG, + FrameColor = colGreen, + BackgroundColorHover = colBGHover + }; + + profileNotesTextArea = new TextArea + { + ReadOnly = true, + BackgroundColor = colBGNoAlpha, + TextColor = colInactive, + SpellCheck = false, + Width = 275, + Height = 150, + Text = Text.ProfileNotes + }; + + modSettingsLayout.BeginHorizontal(); + modSettingsLayout.AddSpace(); + modSettingsLayout.AddColumn(null, addModButton, modSpacer, settingsProfileLabel, modSettingsProfileDropDown, profileButton, saveButton, updateModButton, deleteModButton, profileNotesTextArea, null); + modSettingsLayout.AddSpace(); + + TabPage modSettingsPage = new TabPage + { + BackgroundColor = colBGNoAlpha, + Content = modSettingsLayout, + Text = Text.ModSettingsTab + }; - #endregion + #endregion - #endregion + #endregion - Content = new TabControl + Content = new TabControl + { + Pages = { - Pages = - { - mainPage, + mainPage, - changelogPage, + changelogPage, - newsPage, + newsPage, - settingsPage, + settingsPage, - modSettingsPage - } - }; - - #region EVENTS - log.Info("All UI objects have been initialized, UI has been set up."); - log.Info("Beginning event linkage..."); - - Closing += MainFormClosing; - showButton.Click += ShowButtonClick; - profileDropDown.SelectedIndexChanged += ProfileDropDownSelectedIndexChanged; - languageDropDown.SelectedIndexChanged += LanguageDropDownSelectedIndexChanged; - autoUpdateAM2RCheck.CheckedChanged += AutoUpdateAM2RCheckChanged; - autoUpdateLauncherCheck.CheckedChanged += AutoUpdateLauncherCheckChanged; - hqMusicAndroidCheck.CheckedChanged += HqMusicAndroidCheckChanged; - hqMusicPCCheck.CheckedChanged += HqMusicPCCheckChanged; - customMirrorCheck.CheckedChanged += CustomMirrorCheckChanged; - apkButton.Click += ApkButtonClickEvent; - apkButton.LoadComplete += (_, _) => UpdateApkState(); - profileDropDown.LoadComplete += (_, _) => UpdateProfileState(); - playButton.Click += PlayButtonClickEvent; - playButton.LoadComplete += PlayButtonLoadComplete; - customMirrorTextBox.LostFocus += CustomMirrorTextBoxLostFocus; - mirrorDropDown.SelectedIndexChanged += MirrorDropDownSelectedIndexChanged; - modSettingsLayout.LoadComplete += ProfileLayoutLoadComplete; - addModButton.Click += AddModButtonClicked; - profileButton.Click += ProfileDataButtonClickEvent; - saveButton.Click += SaveButtonClickEvent; - modSettingsProfileDropDown.SelectedIndexChanged += ModSettingsProfileDropDownSelectedIndexChanged; - deleteModButton.Click += DeleteModButtonClicked; - updateModButton.Click += UpdateModButtonClicked; - profileDebugLogCheck.CheckedChanged += ProfileDebugLogCheckedChanged; - if (OS.IsLinux) - customEnvVarTextBox.LostFocus += CustomEnvVarTextBoxLostFocus; - - //TODO: Retest if these now work on mac - newsWebView.DocumentLoaded += (_, _) => ChangeToEmptyPageOnNoInternet(newsPage, newsNoConnectionLabel); - changelogWebView.DocumentLoaded += (_, _) => ChangeToEmptyPageOnNoInternet(changelogPage, changelogNoConnectionLabel); - - log.Info("Events linked successfully."); - - #endregion - } - - #region CONTROL VARIABLES - - // Visual studio does it like this for normal winforms projects, so I just used the same format. - - /// The tray indicator - private TrayIndicator trayIndicator; - - /// of s, used for actually working with profile data. - //TODO: this should be moved into AM2RLauncherLib - private List profileList; - /// of s so that Eto's annoying interface is appeased. Used for profile name display in DropDowns. - private List profileNames; - - /// The planet Background. - private readonly Bitmap formBG = new Bitmap(Resources.bgCentered); - - // Colors - /// The main green color. - private readonly Color colGreen = Color.FromArgb(142, 188, 35); - /// The warning red color. - private readonly Color colRed = Color.FromArgb(188, 10, 35); - /// The main inactive color. - private readonly Color colInactive = Color.FromArgb(109, 109, 109); - /// The black background color without alpha value. - private readonly Color colBGNoAlpha = Color.FromArgb(10, 10, 10); - /// The black background color. - // XORG can't display alpha anyway, and Wayland breaks with it. - // TODO: that sounds like an Eto issue. investigate, try to open eto issue. - private readonly Color colBG = OS.IsLinux ? Color.FromArgb(10, 10, 10) : Color.FromArgb(10, 10, 10, 80); - /// The lighter green color on hover. - private readonly Color colBGHover = Color.FromArgb(17, 28, 13); - - // Mirror lists - /// of mirror s, used for actually working with mirrors. - private List mirrorList; - /// of so that Eto's annoying IListItem interface is appeased. Used for mirror name display in DropDowns. - private List mirrorDescriptionList; - - /// A that acts as the main Button - private ColorButton playButton; - /// A which is only used for creating APK's - private ColorButton apkButton; - /// A that is used to add mods. - private ColorButton addModButton; - /// A that will open the game files directory for the selected mod. - private ColorButton profileButton; - /// A that will open the save directory for the selected mod. - private ColorButton saveButton; - /// A that is used to update a mod - private ColorButton updateModButton; - /// A that is used to delete a mod - private ColorButton deleteModButton; - - /// The that entitles . - private Label profileLabel; - /// The that gives author information for . - private Label profileAuthorLabel; - /// The that gives version information for . - private Label profileVersionLabel; - /// The that gives information for . - private Label mirrorLabel; - /// The that gives information for . - private Label settingsProfileLabel; - /// The that compliments . - private Label progressLabel; - /// The that gives a warning if the current selected shares the same save location has default AM2R. - private Label saveWarningLabel; - - /// A , that indicates whether to automatically update AM2R or not. - private CheckBox autoUpdateAM2RCheck; - /// A , that indicates whether to automatically update the AM2RLauncher or not. - private CheckBox autoUpdateLauncherCheck; - /// A , that indicates whether to use a custom mirror or not. - private CheckBox customMirrorCheck; - /// A , that indicates whether to use HQ Music when patching to PC or not. - private CheckBox hqMusicPCCheck; - /// A , that indicates whether to use HQ Music when patching to Android or not. - private CheckBox hqMusicAndroidCheck; - /// A , that indicates whether to create debug logs for profiles. - private CheckBox profileDebugLogCheck; - - /// A where languages can be chosen. - private DropDown languageDropDown; - /// A where mirrors can be chosen. - private DropDown mirrorDropDown; - /// A where profiles can be chosen. - private DropDown profileDropDown; - /// A where profiles can be chosen (located in Profile Settings). - //TODO: Use MVVM bindings: https://github.com/picoe/Eto/wiki/Data-Binding#mvvm-binding - private DropDown modSettingsProfileDropDown; - - /// A , where the user can input their custom mirror. - private TextBox customMirrorTextBox; - /// A , where the user can input their custom environment variables. - private TextBox customEnvVarTextBox; - - /// A , where the notes from the current selected profile in are displayed. - private TextArea profileNotesTextArea; - - /// A that can be used to show progress for a specific task. - private ProgressBar progressBar; + modSettingsPage + } + }; + + #region EVENTS + log.Info("All UI objects have been initialized, UI has been set up."); + log.Info("Beginning event linkage..."); + + Closing += MainFormClosing; + showButton.Click += ShowButtonClick; + profileDropDown.SelectedIndexChanged += ProfileDropDownSelectedIndexChanged; + languageDropDown.SelectedIndexChanged += LanguageDropDownSelectedIndexChanged; + autoUpdateAM2RCheck.CheckedChanged += AutoUpdateAM2RCheckChanged; + autoUpdateLauncherCheck.CheckedChanged += AutoUpdateLauncherCheckChanged; + hqMusicAndroidCheck.CheckedChanged += HqMusicAndroidCheckChanged; + hqMusicPCCheck.CheckedChanged += HqMusicPCCheckChanged; + customMirrorCheck.CheckedChanged += CustomMirrorCheckChanged; + apkButton.Click += ApkButtonClickEvent; + apkButton.LoadComplete += (_, _) => UpdateApkState(); + profileDropDown.LoadComplete += (_, _) => UpdateProfileState(); + playButton.Click += PlayButtonClickEvent; + playButton.LoadComplete += PlayButtonLoadComplete; + customMirrorTextBox.LostFocus += CustomMirrorTextBoxLostFocus; + mirrorDropDown.SelectedIndexChanged += MirrorDropDownSelectedIndexChanged; + modSettingsLayout.LoadComplete += ProfileLayoutLoadComplete; + addModButton.Click += AddModButtonClicked; + profileButton.Click += ProfileDataButtonClickEvent; + saveButton.Click += SaveButtonClickEvent; + modSettingsProfileDropDown.SelectedIndexChanged += ModSettingsProfileDropDownSelectedIndexChanged; + deleteModButton.Click += DeleteModButtonClicked; + updateModButton.Click += UpdateModButtonClicked; + profileDebugLogCheck.CheckedChanged += ProfileDebugLogCheckedChanged; + if (OS.IsLinux) + customEnvVarTextBox.LostFocus += CustomEnvVarTextBoxLostFocus; + + //TODO: Retest if these now work on mac + newsWebView.DocumentLoaded += (_, _) => ChangeToEmptyPageOnNoInternet(newsPage, newsNoConnectionLabel); + changelogWebView.DocumentLoaded += (_, _) => ChangeToEmptyPageOnNoInternet(changelogPage, changelogNoConnectionLabel); + + log.Info("Events linked successfully."); #endregion } + + #region CONTROL VARIABLES + + // Visual studio does it like this for normal winforms projects, so I just used the same format. + + /// The tray indicator + private TrayIndicator trayIndicator; + + /// of s, used for actually working with profile data. + //TODO: this should be moved into AM2RLauncherLib + private List profileList; + /// of s so that Eto's annoying interface is appeased. Used for profile name display in DropDowns. + private List profileNames; + + /// The planet Background. + private readonly Bitmap formBG = new Bitmap(Resources.bgCentered); + + // Colors + /// The main green color. + private readonly Color colGreen = Color.FromArgb(142, 188, 35); + /// The warning red color. + private readonly Color colRed = Color.FromArgb(188, 10, 35); + /// The main inactive color. + private readonly Color colInactive = Color.FromArgb(109, 109, 109); + /// The black background color without alpha value. + private readonly Color colBGNoAlpha = Color.FromArgb(10, 10, 10); + /// The black background color. + // XORG can't display alpha anyway, and Wayland breaks with it. + // TODO: that sounds like an Eto issue. investigate, try to open eto issue. + private readonly Color colBG = OS.IsLinux ? Color.FromArgb(10, 10, 10) : Color.FromArgb(10, 10, 10, 80); + /// The lighter green color on hover. + private readonly Color colBGHover = Color.FromArgb(17, 28, 13); + + // Mirror lists + /// of mirror s, used for actually working with mirrors. + private List mirrorList; + /// of so that Eto's annoying IListItem interface is appeased. Used for mirror name display in DropDowns. + private List mirrorDescriptionList; + + /// A that acts as the main Button + private ColorButton playButton; + /// A which is only used for creating APK's + private ColorButton apkButton; + /// A that is used to add mods. + private ColorButton addModButton; + /// A that will open the game files directory for the selected mod. + private ColorButton profileButton; + /// A that will open the save directory for the selected mod. + private ColorButton saveButton; + /// A that is used to update a mod + private ColorButton updateModButton; + /// A that is used to delete a mod + private ColorButton deleteModButton; + + /// The that entitles . + private Label profileLabel; + /// The that gives author information for . + private Label profileAuthorLabel; + /// The that gives version information for . + private Label profileVersionLabel; + /// The that gives information for . + private Label mirrorLabel; + /// The that gives information for . + private Label settingsProfileLabel; + /// The that compliments . + private Label progressLabel; + /// The that gives a warning if the current selected shares the same save location has default AM2R. + private Label saveWarningLabel; + + /// A , that indicates whether to automatically update AM2R or not. + private CheckBox autoUpdateAM2RCheck; + /// A , that indicates whether to automatically update the AM2RLauncher or not. + private CheckBox autoUpdateLauncherCheck; + /// A , that indicates whether to use a custom mirror or not. + private CheckBox customMirrorCheck; + /// A , that indicates whether to use HQ Music when patching to PC or not. + private CheckBox hqMusicPCCheck; + /// A , that indicates whether to use HQ Music when patching to Android or not. + private CheckBox hqMusicAndroidCheck; + /// A , that indicates whether to create debug logs for profiles. + private CheckBox profileDebugLogCheck; + + /// A where languages can be chosen. + private DropDown languageDropDown; + /// A where mirrors can be chosen. + private DropDown mirrorDropDown; + /// A where profiles can be chosen. + private DropDown profileDropDown; + /// A where profiles can be chosen (located in Profile Settings). + //TODO: Use MVVM bindings: https://github.com/picoe/Eto/wiki/Data-Binding#mvvm-binding + private DropDown modSettingsProfileDropDown; + + /// A , where the user can input their custom mirror. + private TextBox customMirrorTextBox; + /// A , where the user can input their custom environment variables. + private TextBox customEnvVarTextBox; + + /// A , where the notes from the current selected profile in are displayed. + private TextArea profileNotesTextArea; + + /// A that can be used to show progress for a specific task. + private ProgressBar progressBar; + + #endregion } \ No newline at end of file