diff --git a/.gitignore b/.gitignore index b6d999b..61815dc 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ *.user *.userosscache *.sln.docstates +*.backup # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs diff --git a/Config/AnalysisData/a32nx/a32nx_engine_display.png b/Config/AnalysisData/a32nx/a32nx_engine_display.png deleted file mode 100644 index 6e6cae6..0000000 Binary files a/Config/AnalysisData/a32nx/a32nx_engine_display.png and /dev/null differ diff --git a/Config/AnalysisData/a32nx/a32nx_message_panel.png b/Config/AnalysisData/a32nx/a32nx_message_panel.png deleted file mode 100644 index 01134ff..0000000 Binary files a/Config/AnalysisData/a32nx/a32nx_message_panel.png and /dev/null differ diff --git a/Config/AnalysisData/a32nx/a32nx_multipurpose_control.png b/Config/AnalysisData/a32nx/a32nx_multipurpose_control.png deleted file mode 100644 index ce2fc02..0000000 Binary files a/Config/AnalysisData/a32nx/a32nx_multipurpose_control.png and /dev/null differ diff --git a/Config/AnalysisData/a32nx/a32nx_nav_display.png b/Config/AnalysisData/a32nx/a32nx_nav_display.png deleted file mode 100644 index d665dae..0000000 Binary files a/Config/AnalysisData/a32nx/a32nx_nav_display.png and /dev/null differ diff --git a/Config/AnalysisData/a32nx/a32nx_pfd.png b/Config/AnalysisData/a32nx/a32nx_pfd.png deleted file mode 100644 index 2926329..0000000 Binary files a/Config/AnalysisData/a32nx/a32nx_pfd.png and /dev/null differ diff --git a/Config/AnalysisData/a32nx/a32nx_standby_altitude_indicator.png b/Config/AnalysisData/a32nx/a32nx_standby_altitude_indicator.png deleted file mode 100644 index 54857e1..0000000 Binary files a/Config/AnalysisData/a32nx/a32nx_standby_altitude_indicator.png and /dev/null differ diff --git a/Config/AnalysisData/a32nx/a32nx_system_display.png b/Config/AnalysisData/a32nx/a32nx_system_display.png deleted file mode 100644 index e6eab77..0000000 Binary files a/Config/AnalysisData/a32nx/a32nx_system_display.png and /dev/null differ diff --git a/Config/AnalysisData/g1000/g1000_mfd.png b/Config/AnalysisData/g1000/g1000_mfd.png deleted file mode 100644 index e71fb9a..0000000 Binary files a/Config/AnalysisData/g1000/g1000_mfd.png and /dev/null differ diff --git a/Config/AnalysisData/g1000/g1000_mfd2.png b/Config/AnalysisData/g1000/g1000_mfd2.png deleted file mode 100644 index 34aebdd..0000000 Binary files a/Config/AnalysisData/g1000/g1000_mfd2.png and /dev/null differ diff --git a/Config/AnalysisData/g1000/g1000_pfd.png b/Config/AnalysisData/g1000/g1000_pfd.png deleted file mode 100644 index aa43b56..0000000 Binary files a/Config/AnalysisData/g1000/g1000_pfd.png and /dev/null differ diff --git a/Config/AnalysisData/g3000-kingair/g3000kingair_pfd2.png b/Config/AnalysisData/g3000-kingair/g3000kingair_pfd2.png deleted file mode 100644 index 9b8c4ff..0000000 Binary files a/Config/AnalysisData/g3000-kingair/g3000kingair_pfd2.png and /dev/null differ diff --git a/Config/AnalysisData/pms50-gtn750/pms50_gtn750_mfd.png b/Config/AnalysisData/pms50-gtn750/pms50_gtn750_mfd.png deleted file mode 100644 index a18760c..0000000 Binary files a/Config/AnalysisData/pms50-gtn750/pms50_gtn750_mfd.png and /dev/null differ diff --git a/Config/AnalysisData/pms50-gtn750/pms50_gtn750_mfd2.png b/Config/AnalysisData/pms50-gtn750/pms50_gtn750_mfd2.png deleted file mode 100644 index 73674a3..0000000 Binary files a/Config/AnalysisData/pms50-gtn750/pms50_gtn750_mfd2.png and /dev/null differ diff --git a/Config/AnalysisData/pms50-gtn750/pms50_gtn750_pfd.png b/Config/AnalysisData/pms50-gtn750/pms50_gtn750_pfd.png deleted file mode 100644 index b844641..0000000 Binary files a/Config/AnalysisData/pms50-gtn750/pms50_gtn750_pfd.png and /dev/null differ diff --git a/Config/preprocessingdata/separation_button.png b/Config/preprocessingdata/separation_button.png deleted file mode 100644 index 21b142f..0000000 Binary files a/Config/preprocessingdata/separation_button.png and /dev/null differ diff --git a/MSFSPopoutPanelManager.csproj b/MSFSPopoutPanelManager.csproj index 7cc90d4..3bea90a 100644 --- a/MSFSPopoutPanelManager.csproj +++ b/MSFSPopoutPanelManager.csproj @@ -5,7 +5,7 @@ net5.0-windows true x64;AnyCPU - 2.2.0.0 + 3.0 MSFSPopoutPanelManager MSFSPopoutPanelManager WindowManager.ico @@ -13,67 +13,46 @@ MSFS 2020 Popout Panel Manager MSFS 2020 Popout Panel Manager true - 2.2.0.0 - 2.2.0.0 - None - false + 3.0.0.0 + 3.0.0.0 + + + + + + Stanley Kwok 2021 + https://github.com/hawkeye-stan/msfs-popout-panel-manager + + + + false + embedded + true - - - + - - ..\..\..\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\UIAutomationClient.dll - - - ..\..\..\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\UIAutomationTypes.dll - - - - - - - - - - - - - - - - - - - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + - + - - - - - - - - - - Always - - - Always - Always + + Always + Always @@ -82,181 +61,8 @@ - - - - - - - - - - - - - - - - - - - - - - - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - PreserveNewest - - - Always - - - PreserveNewest - - - Always - - - PreserveNewest - - - Always - - - PreserveNewest - - - Always - - - Always - - - PreserveNewest - - - Always - - - Always - - - PreserveNewest - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - Always @@ -264,208 +70,9 @@ Always - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - True - True - Resources.resx + + Form - - - - ResXFileCodeGenerator - Resources.Designer.cs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Modules/ChildWindow.cs b/Modules/ChildWindow.cs deleted file mode 100644 index 6c392f2..0000000 --- a/Modules/ChildWindow.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; - -namespace MSFSPopoutPanelManager -{ - public class ChildWindow - { - public ChildWindow() - { - WindowType = WindowType.Undetermined; - } - - public IntPtr Handle { get; set; } - - public int PanelId { get; set; } - - public string Title { get; set; } - - public string ClassName { get; set; } - - public WindowType WindowType { get; set; } - } -} diff --git a/Modules/Enums.cs b/Modules/Enums.cs deleted file mode 100644 index 257e82a..0000000 --- a/Modules/Enums.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace MSFSPopoutPanelManager -{ - public enum FlightSimResolution - { - HD, - QHD, - WQHD, - UHD - } - - public enum WindowType - { - FlightSimMainWindow, - BuiltIn_Popout, - Custom_Popout, - Undetermined - } -} diff --git a/Modules/FileManager.cs b/Modules/FileManager.cs deleted file mode 100644 index df4c5ce..0000000 --- a/Modules/FileManager.cs +++ /dev/null @@ -1,301 +0,0 @@ -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.IO; -using System.Windows.Forms; - -namespace MSFSPopoutPanelManager -{ - public class FileManager - { - private static string StartupPath; - - static FileManager() - { - FileManager.StartupPath = Application.StartupPath; - } - - public static List ReadAllPlaneProfileData() - { - List allProfiles = new List(); - allProfiles.AddRange(FileManager.ReadBuiltInPlaneProfileData()); - allProfiles.AddRange(FileManager.ReadCustomPlaneProfileData()); - - return allProfiles; - } - - public static List ReadBuiltInPlaneProfileData() - { - try - { - using (StreamReader reader = new StreamReader(GetFilePathByType(FilePathType.ProfileData) + "planeprofile.json")) - { - return JsonConvert.DeserializeObject>(reader.ReadToEnd()); - } - } - catch - { - return new List(); - } - } - - public static List ReadCustomPlaneProfileData() - { - try - { - using (StreamReader reader = new StreamReader(GetFilePathByType(FilePathType.ProfileData) + "customplaneprofile.json")) - { - return JsonConvert.DeserializeObject>(reader.ReadToEnd()); - } - } - catch - { - return new List(); - } - } - - public static bool WriteBuiltInPlaneProfileData(List profiles) - { - try - { - using (StreamWriter file = File.CreateText(GetFilePathByType(FilePathType.ProfileData) + "planeprofile.json")) - { - JsonSerializer serializer = new JsonSerializer(); - serializer.Serialize(file, profiles); - } - - return true; - } - catch - { - return false; - } - } - - public static bool WriteCustomPlaneProfileData(List profiles) - { - try - { - using (StreamWriter file = File.CreateText(GetFilePathByType(FilePathType.ProfileData) + "customplaneprofile.json")) - { - JsonSerializer serializer = new JsonSerializer(); - serializer.Serialize(file, profiles); - } - - return true; - } - catch - { - return false; - } - } - - public static UserData ReadUserData() - { - try - { - using (StreamReader reader = new StreamReader(GetFilePathByType(FilePathType.UserData) + "userdata.json")) - { - return JsonConvert.DeserializeObject(reader.ReadToEnd()); - } - } - catch - { - var userData = new UserData(); - return userData; - } - } - - public static UserPlaneProfile GetUserPlaneProfile(int profileId) - { - var userData = ReadUserData(); - - var userPlaneProfile = userData.Profiles.Find(x => x.ProfileId == profileId); - - if (userPlaneProfile == null) - { - userPlaneProfile = new UserPlaneProfile(); - userPlaneProfile.ProfileId = profileId; - userPlaneProfile.PanelSettings.PanelDestinationList = new List(); - userPlaneProfile.PanelSourceCoordinates = new List(); - } - - return userPlaneProfile; - } - - public static void WriteUserData(UserData userData) - { - using (StreamWriter file = File.CreateText(GetFilePathByType(FilePathType.UserData) + "userdata.json")) - { - JsonSerializer serializer = new JsonSerializer(); - serializer.Serialize(file, userData); - } - } - - public static List ReadAllAnalysisTemplateData() - { - List allTemplates = new List(); - allTemplates.AddRange(FileManager.ReadBuiltInAnalysisTemplateData()); - allTemplates.AddRange(FileManager.ReadCustomAnalysisTemplateData()); - - return allTemplates; - } - - public static List ReadBuiltInAnalysisTemplateData() - { - try - { - using (StreamReader reader = new StreamReader(GetFilePathByType(FilePathType.AnalysisData) + "analysisconfig.json")) - { - try - { - return JsonConvert.DeserializeObject>(reader.ReadToEnd()); - } - catch(Exception ex) - { - throw new Exception("The file analysisconfig.json is invalid."); - } - } - } - catch - { - return new List(); - } - } - - public static List ReadCustomAnalysisTemplateData() - { - try - { - using (StreamReader reader = new StreamReader(GetFilePathByType(FilePathType.AnalysisData) + "customanalysisconfig.json")) - { - try - { - return JsonConvert.DeserializeObject>(reader.ReadToEnd()); - } - catch (Exception ex) - { - throw new Exception("The file customanalysisconfig.json is invalid."); - } - } - } - catch - { - return new List(); - } - - } - - public static void WriteCustomAnalysisTemplateData(List analysisDataList) - { - using (StreamWriter file = File.CreateText(GetFilePathByType(FilePathType.AnalysisData) + "customanalysisconfig.json")) - { - JsonSerializer serializer = new JsonSerializer(); - serializer.Serialize(file, analysisDataList); - } - } - - public static void RemoveCustomAnalysisTemplate(string analysisTemplateName) - { - try - { - var templates = ReadCustomAnalysisTemplateData(); - var template = templates.Find(x => x.TemplateName == analysisTemplateName); - - if (template != null) - { - var fullFilePath = GetFilePathByType(FilePathType.AnalysisData) + template.TemplateImagePath; - Directory.Delete(fullFilePath, true); - } - } - catch - { - - } - } - - public static Stream LoadAsStream(FilePathType filePathType, string fileName) - { - try - { - var fullFilePath = GetFilePathByType(filePathType) + fileName; - return new MemoryStream(File.ReadAllBytes(fullFilePath)); - } - catch - { - Logger.LogStatus($"Unable to load file {fileName}"); - return null; - } - } - - public static Stream LoadAsStream(string fullFilePath) - { - return new MemoryStream(File.ReadAllBytes(fullFilePath)); - } - - public static void SaveFile(FilePathType filePathType, string subFolder, string fileName, MemoryStream memoryStream) - { - subFolder = String.IsNullOrEmpty(subFolder) ? String.Empty : subFolder + @"\"; - - var folderPath = GetFilePathByType(filePathType) + subFolder; - var fullFilePath = folderPath + fileName; - - Directory.CreateDirectory(folderPath); - - using (var file = new FileStream(fullFilePath, FileMode.Create, FileAccess.Write)) - { - memoryStream.WriteTo(file); - } - } - - public static List GetFileNames(FilePathType filePathType, string subFolder, string filePrefix) - { - List files = new List(); - - var folderPath = GetFilePathByType(filePathType); - if (!String.IsNullOrEmpty(subFolder)) - folderPath += subFolder + @"\"; - - string[] fileEntries = Directory.GetFiles(folderPath); - foreach (string fileEntry in fileEntries) - { - var fileName = Path.GetFileName(fileEntry); - - if (!String.IsNullOrEmpty(filePrefix)) - { - if(fileName.StartsWith(filePrefix)) - files.Add(fileEntry); - } - else - files.Add(fileEntry); - } - - return files; - } - - public static string GetFilePathByType(FilePathType filePathType) - { - switch (filePathType) - { - case FilePathType.PreprocessingData: - return StartupPath + @"\Config\PreprocessingData\"; - case FilePathType.AnalysisData: - return StartupPath + @"\Config\AnalysisData\"; - case FilePathType.ProfileData: - case FilePathType.UserData: - return StartupPath + @"\Config\"; - default: - return StartupPath; - } - } - } - - public enum FilePathType - { - PreprocessingData, - AnalysisData, - ProfileData, - UserData, - Default - } -} diff --git a/Modules/ImageAnalysis.cs b/Modules/ImageAnalysis.cs deleted file mode 100644 index 9979b07..0000000 --- a/Modules/ImageAnalysis.cs +++ /dev/null @@ -1,61 +0,0 @@ -using AForge.Imaging; -using System; -using System.Drawing; -using System.Linq; - -namespace MSFSPopoutPanelManager -{ - public class ImageAnalysis - { - public static Point ExhaustiveTemplateMatchAnalysisAsync(Bitmap sourceImage, Bitmap templateImage, float similarityThreshHold, int panelStartingTop, int panelStartingLeft) - { - var x = panelStartingLeft - Convert.ToInt32(templateImage.Width * 1.5); - var y = 0; - var width = Convert.ToInt32(templateImage.Width * 1.5); - var height = sourceImage.Height; - - var searchZone = new Rectangle(x, y, width, height); - - var point = AnalyzeExpandImageBitmap(sourceImage, templateImage, similarityThreshHold, searchZone); - - if (point != Point.Empty) - point.Y += panelStartingTop; - - return point; - } - - public static Point AnalyzeExpandImageBitmap(Bitmap sourceImage, Bitmap templateImage, float similarityThreshHold, Rectangle searchZone) - { - // Full image pixel to pixel matching algorithm - ExhaustiveTemplateMatching etm = new ExhaustiveTemplateMatching(similarityThreshHold); - TemplateMatch[] templateMatches = etm.ProcessImage(sourceImage, templateImage, searchZone); - - if (templateMatches != null && templateMatches.Length > 0) - { - var match = templateMatches.OrderByDescending(x => x.Similarity).First(); // Just look at the first match - - var xCoor = match.Rectangle.X + templateImage.Width / 12; - var yCoor = match.Rectangle.Y + templateImage.Height / 4; - - return new Point(Convert.ToInt32(xCoor), Convert.ToInt32(yCoor)); - } - - return Point.Empty; - } - - public static float ExhaustiveTemplateMatchAnalysisScore(Bitmap sourceImage, Bitmap templateImage, float similarityThreshHold) - { - // SUSAN corner block matching algorithm - SusanCornersDetector scd = new SusanCornersDetector(50, 8); - var points = scd.ProcessImage(sourceImage); - - // process images searching for block matchings - ExhaustiveBlockMatching bm = new ExhaustiveBlockMatching(4, 8); - sourceImage = ImageOperation.ResizeImage(sourceImage, 800, 600); - templateImage = ImageOperation.ResizeImage(templateImage, 800, 600); - var templateMatches = bm.ProcessImage(sourceImage, points, templateImage); - - return templateMatches.Count; - } - } -} diff --git a/Modules/ImageOperation.cs b/Modules/ImageOperation.cs deleted file mode 100644 index 7e7ea63..0000000 --- a/Modules/ImageOperation.cs +++ /dev/null @@ -1,110 +0,0 @@ -using AForge.Imaging; -using AForge.Imaging.Filters; -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Imaging; -using System.IO; -using System.Threading; - -namespace MSFSPopoutPanelManager -{ - public class ImageOperation - { - public static byte[] ImageToByte(Bitmap image) - { - ImageConverter converter = new ImageConverter(); - return (byte[])converter.ConvertTo(image, typeof(byte[])); - } - - public static Bitmap ByteToImage(byte[] bytes) - { - return new Bitmap(new MemoryStream(bytes)); - } - - public static Bitmap ResizeImage(Bitmap sourceImage, double width, double height) - { - var bmp = new ResizeBilinear(Convert.ToInt32(width), Convert.ToInt32(height)).Apply(sourceImage); - return ImageOperation.ConvertToFormat(bmp, PixelFormat.Format24bppRgb); - } - - public static Bitmap CropImage(Bitmap sourceImage, int x, int y, int width, int height) - { - Rectangle crop = new Rectangle(x, y, width, height); - - var bmp = new Bitmap(crop.Width, crop.Height); - using (var gr = Graphics.FromImage(bmp)) - { - gr.DrawImage(sourceImage, new Rectangle(0, 0, bmp.Width, bmp.Height), crop, GraphicsUnit.Pixel); - } - - return ImageOperation.ConvertToFormat(bmp, PixelFormat.Format24bppRgb); - } - - public static Bitmap ConvertToFormat(Bitmap image, PixelFormat format) - { - var copy = new Bitmap(image.Width, image.Height, format); - using (Graphics gr = Graphics.FromImage(copy)) - { - gr.DrawImage(image, new Rectangle(0, 0, copy.Width, copy.Height)); - } - - return copy; - } - - public static Bitmap TakeScreenShot(IntPtr windowHandle, bool maximized) - { - if (maximized) - { - const int SW_MAXIMIZE = 3; - PInvoke.ShowWindow(windowHandle, SW_MAXIMIZE); - } - - // Set window to foreground so nothing can hide the window - PInvoke.SetForegroundWindow(windowHandle); - Thread.Sleep(500); - - var rect = new Rect(); - PInvoke.GetWindowRect(windowHandle, out rect); - - var left = rect.Left; - var top = rect.Top; - var right = rect.Right; - var bottom = rect.Bottom; - - var bounds = new Rectangle(left, top, right - left, bottom - top); - var bmp = new Bitmap(bounds.Width, bounds.Height); - - using (Graphics g = Graphics.FromImage(bmp)) - { - g.CopyFromScreen(new Point(bounds.Left, bounds.Top), Point.Empty, bounds.Size); - } - - return ImageOperation.ConvertToFormat(bmp, PixelFormat.Format24bppRgb); - } - - public static Bitmap HighLightMatchedPattern(Bitmap sourceImage, List rectBoxes) - { - // Highlight the match in the source image - var data = sourceImage.LockBits(new Rectangle(0, 0, sourceImage.Width, sourceImage.Height), ImageLockMode.ReadWrite, sourceImage.PixelFormat); - - foreach (Rect rectBox in rectBoxes) - { - Rectangle rect = new Rectangle(rectBox.Left, rectBox.Top, rectBox.Width, rectBox.Height); - Drawing.Rectangle(data, rect, Color.Red); - } - - sourceImage.UnlockBits(data); - sourceImage.Save(@".\debug.png"); - - return sourceImage; - } - public static Bitmap GetExpandButtonImage(int windowHeight) - { - var image = new Bitmap(FileManager.LoadAsStream(FilePathType.PreprocessingData, "separation_button.png")); - double template_image_ratio = Convert.ToDouble(windowHeight) / 1440; // expand button image was created on 1440p resolution - - return ImageOperation.ResizeImage(image, Convert.ToInt32(image.Width * template_image_ratio), Convert.ToInt32(image.Height * template_image_ratio)); - } - } -} diff --git a/Modules/Logger.cs b/Modules/Logger.cs deleted file mode 100644 index 7cc93a7..0000000 --- a/Modules/Logger.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; - -namespace MSFSPopoutPanelManager -{ - public class Logger - { - public static event EventHandler> OnStatusLogged; - - public static void LogStatus(string message) - { - var statusMessage = new StatusMessage() { Message = message, Priority = StatusPriority.Low }; - OnStatusLogged?.Invoke(null, new EventArgs(statusMessage)); - } - - public static void LogStatus(string message, StatusPriority priority) - { - var statusMessage = new StatusMessage() { Message = message, Priority = priority }; - OnStatusLogged?.Invoke(null, new EventArgs(statusMessage)); - } - } - - public class EventArgs : EventArgs - { - public T Value { get; private set; } - - public EventArgs(T val) - { - Value = val; - } - } - - public class StatusMessage - { - public string Message { get; set; } - - public StatusPriority Priority { get; set; } - } - - public enum StatusPriority - { - High, - Low - } -} diff --git a/Modules/PanelAnalysisModule.cs b/Modules/PanelAnalysisModule.cs deleted file mode 100644 index c9e3a5e..0000000 --- a/Modules/PanelAnalysisModule.cs +++ /dev/null @@ -1,506 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Diagnostics; -using System.Drawing; -using System.Threading; -using System.Windows.Forms; -using System.Runtime.InteropServices; -using System.Text; -using System.Drawing.Imaging; -using System.Threading.Tasks; -using System.IO; - -namespace MSFSPopoutPanelManager -{ - public class PanelAnalysisModule - { - private Form _form; - - public PanelAnalysisModule(Form form) - { - _form = form; - } - - public void Analyze(WindowProcess simulatorProcess, int profileId, int panelsCount) - { - var panelsToBeIdentified = panelsCount; - // Get all child windows - var processZero = GetProcessZero(); - - // Move process zero childs back into simulator process - MoveChildWindowsIntoSimulatorProcess(simulatorProcess, processZero); - - if (simulatorProcess.ChildWindows.Count > 0) - { - foreach(var customPopout in simulatorProcess.ChildWindows.FindAll(x => x.WindowType == WindowType.Undetermined)) - { - while (panelsToBeIdentified > 1) // Do not have to separate the last panel - { - var coordinate = AnalyzeMergedPopoutWindows(customPopout.Handle); - if (!coordinate.IsEmpty) - SeparateUntitledPanel(customPopout.Handle, coordinate.X, coordinate.Y); - - panelsToBeIdentified--; - } - - panelsToBeIdentified = panelsCount; - } - } - - // Now all newly pop out windows are in process zero, move them into flight simulator process - processZero = GetProcessZero(); - MoveChildWindowsIntoSimulatorProcess(simulatorProcess, processZero); - - // Analyze the content of the pop out panels - AnalyzePopoutWindows(simulatorProcess, profileId); - } - - private WindowProcess GetProcessZero() - { - // Get process with PID of zero (PID zero launches all the popout windows for MSFS) - var process = Process.GetProcesses().ToList().Find(x => x.Id == 0); - var processZero = new WindowProcess() - { - ProcessId = process.Id, - ProcessName = process.ProcessName, - Handle = process.MainWindowHandle - }; - - GetChildWindows(processZero); - - return processZero; - } - - private void GetChildWindows(WindowProcess process) - { - int classNameLength = 256; - - var childHandles = GetAllChildHandles(process.Handle); - - childHandles.ForEach(childHandle => - { - StringBuilder className = new StringBuilder(classNameLength); - PInvoke.GetClassName(childHandle, className, classNameLength); - - if (className.ToString() == "AceApp") - { - process.ChildWindows.Add(new ChildWindow - { - ClassName = "AceApp", - Handle = childHandle, - Title = GetWindowTitle(childHandle) - }); - } - }); - } - - private List GetAllChildHandles(IntPtr parent) - { - var childHandles = new List(); - - GCHandle gcChildhandlesList = GCHandle.Alloc(childHandles); - IntPtr pointerChildHandlesList = GCHandle.ToIntPtr(gcChildhandlesList); - - try - { - PInvoke.EnumWindowProc childProc = new PInvoke.EnumWindowProc(EnumWindow); - PInvoke.EnumChildWindows(parent, childProc, pointerChildHandlesList); - } - finally - { - gcChildhandlesList.Free(); - } - - return childHandles; - } - - private bool EnumWindow(IntPtr hWnd, IntPtr lParam) - { - var gcChildhandlesList = GCHandle.FromIntPtr(lParam); - - if (gcChildhandlesList.Target == null) - return false; - - var childHandles = gcChildhandlesList.Target as List; - childHandles.Add(hWnd); - - return true; - } - - private string GetWindowTitle(IntPtr hWnd) - { - StringBuilder title = new StringBuilder(1024); - PInvoke.GetWindowText(hWnd, title, title.Capacity); - - return String.IsNullOrEmpty(title.ToString()) ? null : title.ToString(); - } - - private void MoveChildWindowsIntoSimulatorProcess(WindowProcess simulatorProcess, WindowProcess processZero) - { - // The popout windows such as PFD and MFD attached itself to main window for Process ID zero instead of the MSFS process. - // Moving these windows back into MSFS main window - if (processZero != null) - { - // Clean up all existing simulator process child window data - simulatorProcess.ChildWindows.RemoveAll(x => x.WindowType == WindowType.Custom_Popout || x.WindowType == WindowType.BuiltIn_Popout); - - foreach (var child in processZero.ChildWindows) - { - int parentProcessId; - PInvoke.GetWindowThreadProcessId(child.Handle, out parentProcessId); - - if (simulatorProcess != null && parentProcessId == simulatorProcess.ProcessId && !simulatorProcess.ChildWindows.Exists(x => x.Handle == child.Handle)) - { - if (String.IsNullOrEmpty(child.Title)) - child.WindowType = WindowType.Undetermined; - else if (child.Title.Contains("(Custom)")) - child.WindowType = WindowType.Custom_Popout; - else if (child.Title.Contains("Microsoft Flight Simulator")) - child.WindowType = WindowType.FlightSimMainWindow; - else if (!String.IsNullOrEmpty(child.Title)) - child.WindowType = WindowType.BuiltIn_Popout; - else - child.WindowType = WindowType.Undetermined; - - simulatorProcess.ChildWindows.Add(child); - } - } - } - } - - public Point AnalyzeMergedPopoutWindows(IntPtr windowHandle) - { - float EXHAUSTIVE_TEMPLATE_MATCHING_SIMILARITY_THRESHOLD = 0.86f; - - var sourceImage = ImageOperation.TakeScreenShot(windowHandle, true); - var templateImage = ImageOperation.GetExpandButtonImage(sourceImage.Height); - - var panelsStartingTop = GetPanelsStartingTop(windowHandle, sourceImage); - - if (panelsStartingTop > sourceImage.Height / 2) // if usually the last panel occupied the entire window with no white menubar - return Point.Empty; - - var panelsStartingLeft = GetPanelsStartingLeft(windowHandle, sourceImage, panelsStartingTop + 5); - - var templateImageRatios = GetExpandButtonHeightRatio(windowHandle, sourceImage, 1); - - var resizedSource = ImageOperation.CropImage(sourceImage, 0, panelsStartingTop, sourceImage.Width, sourceImage.Height / 12); // add around 100px per 1440p resolution - - resizedSource.Save(FileManager.GetFilePathByType(FilePathType.PreprocessingData) + "source.png"); - - var resizedTemplate = ImageOperation.ResizeImage(templateImage, templateImage.Width * templateImageRatios[0], templateImage.Height * templateImageRatios[0]); - var point = ImageAnalysis.ExhaustiveTemplateMatchAnalysisAsync(resizedSource, resizedTemplate, EXHAUSTIVE_TEMPLATE_MATCHING_SIMILARITY_THRESHOLD, panelsStartingTop, panelsStartingLeft); - - if (point.IsEmpty) - { - resizedTemplate = ImageOperation.ResizeImage(templateImage, templateImage.Width * templateImageRatios[1], templateImage.Height * templateImageRatios[1]); - point = ImageAnalysis.ExhaustiveTemplateMatchAnalysisAsync(resizedSource, resizedTemplate, EXHAUSTIVE_TEMPLATE_MATCHING_SIMILARITY_THRESHOLD, panelsStartingTop, panelsStartingLeft); - } - - return point; - } - - private List GetExpandButtonHeightRatio(IntPtr windowHandle, Bitmap sourceImage, int numberOfRows, double percentFromLeft = 0.48) - { - var ratios = new List(); - - const int SW_MAXIMIZE = 3; - PInvoke.ShowWindow(windowHandle, SW_MAXIMIZE); - PInvoke.SetForegroundWindow(windowHandle); - Thread.Sleep(200); - - Rect rect = new Rect(); - PInvoke.GetClientRect(windowHandle, out rect); - - // Get a snippet of 1 pixel wide vertical strip of windows. We will choose the strip left of center. - // This is to determine when the actual panel's vertical pixel starts in the window. This will allow accurate sizing of the expand button image - var clientWindowHeight = rect.Bottom - rect.Top; - var left = Convert.ToInt32((rect.Right - rect.Left) * percentFromLeft); // look at around 48% from the left - var top = sourceImage.Height - clientWindowHeight; - - // Using much faster image LockBits instead of GetPixel method - unsafe - { - var stripData = sourceImage.LockBits(new Rectangle(left, top, 1, clientWindowHeight), ImageLockMode.ReadWrite, sourceImage.PixelFormat); - - int bytesPerPixel = Bitmap.GetPixelFormatSize(stripData.PixelFormat) / 8; - int heightInPixels = stripData.Height; - int widthInBytes = stripData.Width * bytesPerPixel; - byte* ptrFirstPixel = (byte*)stripData.Scan0; - - // Find the first white pixel (the panel title bar) - for (int y = 0; y < heightInPixels; y++) - { - byte* currentLine = ptrFirstPixel + (y * stripData.Stride); - for (int x = 0; x < widthInBytes; x = x + bytesPerPixel) - { - int red = currentLine[x + 2]; - int green = currentLine[x + 1]; - int blue = currentLine[x]; - - if (red == 255 && green == 255 && blue == 255) - { - sourceImage.UnlockBits(stripData); - - var unpopPanelSize = (clientWindowHeight - (y * 2)) / Convert.ToDouble(numberOfRows); - - ratios.Add(unpopPanelSize / Convert.ToDouble(clientWindowHeight)); // 1 row of panel - ratios.Add(unpopPanelSize / 2 / Convert.ToDouble(clientWindowHeight)); // 2 rows of panel - return ratios; - } - } - } - - sourceImage.UnlockBits(stripData); - } - - return GetExpandButtonHeightRatio(windowHandle, sourceImage, numberOfRows, percentFromLeft - 0.01); - } - - private int GetPanelsStartingTop(IntPtr windowHandle, Bitmap sourceImage, double percentFromLeft = 0.49) - { - const int SW_MAXIMIZE = 3; - PInvoke.ShowWindow(windowHandle, SW_MAXIMIZE); - PInvoke.SetForegroundWindow(windowHandle); - Thread.Sleep(250); - - Rect rect = new Rect(); - PInvoke.GetClientRect(windowHandle, out rect); - - // Get a snippet of 1 pixel wide vertical strip of windows. We will choose the strip left of center. - // This is to determine when the actual panel's vertical pixel starts in the window. This will allow accurate sizing of the template image - var clientWindowHeight = rect.Bottom - rect.Top; - var left = Convert.ToInt32((rect.Right - rect.Left) * percentFromLeft); // look at around 49% from the left - var top = sourceImage.Height - clientWindowHeight; - - if (top < 0 || left < 0) - return -1; - - unsafe - { - var stripData = sourceImage.LockBits(new Rectangle(left, top, 1, clientWindowHeight), ImageLockMode.ReadWrite, sourceImage.PixelFormat); - - int bytesPerPixel = Bitmap.GetPixelFormatSize(stripData.PixelFormat) / 8; - int heightInPixels = stripData.Height; - int widthInBytes = stripData.Width * bytesPerPixel; - byte* ptrFirstPixel = (byte*)stripData.Scan0; - - for (int y = 0; y < heightInPixels; y++) - { - byte* currentLine = ptrFirstPixel + (y * stripData.Stride); - for (int x = 0; x < widthInBytes; x = x + bytesPerPixel) - { - int red = currentLine[x + 2]; - int green = currentLine[x + 1]; - int blue = currentLine[x]; - - if (red == 255 && green == 255 && blue == 255) - { - sourceImage.UnlockBits(stripData); - return y + top; - } - } - } - - sourceImage.UnlockBits(stripData); - } - - return GetPanelsStartingTop(windowHandle, sourceImage, percentFromLeft - 0.01); - } - - private int GetPanelsStartingLeft(IntPtr windowHandle, Bitmap sourceImage, int top) - { - Rect rect = new Rect(); - PInvoke.GetClientRect(windowHandle, out rect); - - // Get a snippet of 1 pixel wide horizontal strip of windows - var clientWindowWidth = rect.Right - rect.Left; - - unsafe - { - var stripData = sourceImage.LockBits(new Rectangle(0, top, clientWindowWidth, 1), ImageLockMode.ReadWrite, sourceImage.PixelFormat); - - int bytesPerPixel = Bitmap.GetPixelFormatSize(stripData.PixelFormat) / 8; - int widthInPixels = stripData.Width; - int heightInBytes = stripData.Height * bytesPerPixel; - byte* ptrFirstPixel = (byte*)stripData.Scan0; - - for (int x = 0; x < widthInPixels; x++) - { - byte* currentLine = ptrFirstPixel - (x * bytesPerPixel); - for (int y = 0; y < heightInBytes; y = y + bytesPerPixel) - { - int red = currentLine[y + 2]; - int green = currentLine[y + 1]; - int blue = currentLine[y]; - - if (red == 255 && green == 255 && blue == 255) - { - sourceImage.UnlockBits(stripData); - return sourceImage.Width - x; - } - } - } - - sourceImage.UnlockBits(stripData); - } - - return -1; - } - - private void SeparateUntitledPanel(IntPtr windowHandle, int x, int y) - { - const uint MOUSEEVENTF_LEFTDOWN = 0x02; - const uint MOUSEEVENTF_LEFTUP = 0x04; - - var point = new Point { X = x, Y = y }; - Cursor.Position = new Point(point.X, point.Y); - - // Wait for mouse to get into position - Thread.Sleep(500); - - PInvoke.mouse_event(MOUSEEVENTF_LEFTDOWN, point.X, point.Y, 0, 0); - Thread.Sleep(200); - PInvoke.mouse_event(MOUSEEVENTF_LEFTUP, point.X, point.Y, 0, 0); - - Cursor.Position = new Point(point.X + 50, point.Y + 50); - } - - private void AnalyzePopoutWindows(WindowProcess simulatorProcess, int profileId) - { - var panelScores = new List(); - - // Get analysis template data for the profile - var planeProfile = FileManager.ReadAllPlaneProfileData().Find(x => x.ProfileId == profileId); - var templateData = FileManager.ReadAllAnalysisTemplateData().Find(x => x.TemplateName == planeProfile.AnalysisTemplateName); - - if(templateData == null) - { - CreateNewAnalysisTemplate(simulatorProcess, profileId); - return; - } - - // Load the template images for the selected profile - var templates = new List>(); - foreach (var template in templateData.Templates) - { - foreach (var imagePath in template.ImagePaths) - { - templates.Add(new KeyValuePair(template.PopoutName, ImageOperation.ConvertToFormat(new Bitmap(FileManager.LoadAsStream(FilePathType.AnalysisData, imagePath)), PixelFormat.Format24bppRgb))); - } - } - - var popouts = simulatorProcess.ChildWindows.FindAll(x => x.WindowType == WindowType.Undetermined); - - foreach (var popout in popouts) - { - popout.WindowType = WindowType.Custom_Popout; - - // Resize all untitled pop out panels to 800x600 and set it to foreground - PInvoke.MoveWindow(popout.Handle, 0, 0, 800, 600, true); - PInvoke.SetForegroundWindow(popout.Handle); - - Thread.Sleep(300); // ** this delay is important to allow the window to go into focus before screenshot is taken - - var srcImage = ImageOperation.TakeScreenShot(popout.Handle, false); - var srcImageBytes = ImageOperation.ImageToByte(srcImage); - - Parallel.ForEach(templates, new ParallelOptions { MaxDegreeOfParallelism = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * 0.75) * 1.0)) }, template => - { - var src = ImageOperation.ByteToImage(srcImageBytes); - panelScores.Add(new PanelScore - { - WindowHandle = popout.Handle, - PanelName = template.Key, - Score = ImageAnalysis.ExhaustiveTemplateMatchAnalysisScore(src, template.Value, 0.85f) - }); - }); - } - - // Gets the highest matching score for template matches for each panel - var panels = (from s in panelScores - group s by s.WindowHandle into g - select g.OrderByDescending(z => z.Score).FirstOrDefault()).ToList(); - - // Set the pop out panel title bar text to identify it - foreach (var panel in panels) - { - var title = $"{panel.PanelName} (Custom)"; - simulatorProcess.ChildWindows.Find(x => x.Handle == panel.WindowHandle).Title = title; - PInvoke.SetWindowText(panel.WindowHandle, title); - } - } - - private void CreateNewAnalysisTemplate(WindowProcess simulatorProcess, int profileId) - { - var planeProfile = FileManager.ReadAllPlaneProfileData().Find(x => x.ProfileId == profileId); - - var popouts = simulatorProcess.ChildWindows.FindAll(x => x.WindowType == WindowType.Undetermined); - var images = new List(); - - - foreach (var popout in popouts) - { - popout.WindowType = WindowType.Custom_Popout; - - // Resize all untitled pop out panels to 800x600 and set it to foreground - PInvoke.MoveWindow(popout.Handle, 0, 0, 800, 600, true); - PInvoke.SetForegroundWindow(popout.Handle); - - Thread.Sleep(300); // ** this delay is important to allow the window to go into focus before screenshot is taken - - var screenshot = ImageOperation.TakeScreenShot(popout.Handle, false); - images.Add(screenshot); - } - - var customAnalysisDataList = FileManager.ReadCustomAnalysisTemplateData(); - - AnalysisData analysisData = new AnalysisData(); - analysisData.TemplateName = planeProfile.ProfileName; - analysisData.IsUserTemplate = true; - - for (var i = 0; i < popouts.Count; i++) - { - var panelName = "Panel" + (i + 1); - var imageName = @$"{panelName}.png"; - var imagePath = analysisData.TemplateImagePath; - - using (var memoryStream = new MemoryStream()) - { - images[i].Save(memoryStream, ImageFormat.Png); - FileManager.SaveFile(FilePathType.AnalysisData, imagePath, imageName, memoryStream); - } - - analysisData.Templates.Add(new Template() - { - PopoutId = i + 1, - PopoutName = panelName, - ImagePaths = new List() { @$"{imagePath}/{imageName}" } - }); - - var panelTitle = $"{panelName} (Custom)"; - simulatorProcess.ChildWindows.Find(x => x.Handle == popouts[i].Handle).Title = panelTitle; - PInvoke.SetWindowText(popouts[i].Handle, panelTitle); - } - - customAnalysisDataList.Add(analysisData); - FileManager.WriteCustomAnalysisTemplateData(customAnalysisDataList); - } - - private Bitmap CloneImage(Bitmap srcImage) - { - return srcImage.Clone(new Rectangle(0, 0, srcImage.Width, srcImage.Height), PixelFormat.Format24bppRgb); - } - } - - - public class PanelScore - { - public IntPtr WindowHandle { get; set; } - - public string PanelName { get; set; } - - public float Score { get; set; } - } -} diff --git a/Modules/PanelManager.cs b/Modules/PanelManager.cs deleted file mode 100644 index b28549b..0000000 --- a/Modules/PanelManager.cs +++ /dev/null @@ -1,348 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace MSFSPopoutPanelManager -{ - public class PanelManager - { - private Form _appForm; - private const int MSFS_CONNECTION_RETRY_TIMEOUT = 2000; - private System.Timers.Timer _timer; - private WindowProcess _simulatorProcess; - - private PanelLocationSelectionModule _panelLocationSelectionModule; - private PanelAnalysisModule _panelAnalysisModule; - private UserPlaneProfile _currentPlaneProfile; - - public PanelManager(Form form) - { - _appForm = form; - - _panelLocationSelectionModule = new PanelLocationSelectionModule(form); - _panelAnalysisModule = new PanelAnalysisModule(form); - - _panelLocationSelectionModule.OnSelectionCompleted += (source, e) => { SavePanelSelectionLocation(); }; - } - - public event EventHandler OnSimulatorStarted; - public event EventHandler OnPanelSettingsChanged; - public event EventHandler OnAnalysisCompleted; - - public PanelLocationSelectionModule PanelLocationSelection - { - get { return _panelLocationSelectionModule; } - } - - public UserPlaneProfile CurrentPanelProfile - { - get { return _currentPlaneProfile; } - } - - public void CheckSimulatorStarted() - { - // Autoconnect to flight simulator - _timer = new System.Timers.Timer(); - _timer.Interval = MSFS_CONNECTION_RETRY_TIMEOUT; - _timer.Enabled = true; - _timer.Elapsed += (source, e) => - { - foreach (var process in Process.GetProcesses()) - { - if (process.ProcessName == "FlightSimulator" && _simulatorProcess == null) - { - _simulatorProcess = new WindowProcess() - { - ProcessId = process.Id, - ProcessName = process.ProcessName, - Handle = process.MainWindowHandle - }; - - _timer.Enabled = false; - OnSimulatorStarted?.Invoke(this, null); - } - } - }; - } - - public void PlaneProfileChanged(int? profileId, bool showCoordinateOverlay) - { - Logger.LogStatus(String.Empty); - - if (profileId != null) - { - _currentPlaneProfile = FileManager.GetUserPlaneProfile((int) profileId); - _panelLocationSelectionModule.PlaneProfile = _currentPlaneProfile; - _panelLocationSelectionModule.ShowPanelLocationOverlay(showCoordinateOverlay); - _panelLocationSelectionModule.UpdatePanelLocationUI(); - } - else - { - _panelLocationSelectionModule.PlaneProfile = null; - _panelLocationSelectionModule.ShowPanelLocationOverlay(showCoordinateOverlay); - _panelLocationSelectionModule.UpdatePanelLocationUI(); - } - } - - public void SetDefaultProfile() - { - var userData = FileManager.ReadUserData(); - userData.DefaultProfileId = _currentPlaneProfile.ProfileId; - - FileManager.WriteUserData(userData); - - var profileName = FileManager.ReadAllPlaneProfileData().Find(x => x.ProfileId == _currentPlaneProfile.ProfileId).ProfileName; - - Logger.LogStatus($"Profile '{profileName}' has been set as default."); - } - - public void SavePanelSelectionLocation() - { - var profileId = _currentPlaneProfile.ProfileId; - - var userData = FileManager.ReadUserData(); - - if (userData == null) - userData = new UserData(); - - if (!userData.Profiles.Exists(x => x.ProfileId == profileId)) - { - userData.Profiles.Add(_currentPlaneProfile); - } - else - { - var profileIndex = userData.Profiles.FindIndex(x => x.ProfileId == profileId); - userData.Profiles[profileIndex] = _currentPlaneProfile; - } - - FileManager.WriteUserData(userData); - - Logger.LogStatus("Panel location coordinates have been saved."); - } - - public void Analyze() - { - if (PanelLocationSelection.PanelCoordinates == null || PanelLocationSelection.PanelCoordinates.Count == 0) - { - Logger.LogStatus("No panel locations to be analyze. Please select at least one panel first."); - return; - } - - Logger.LogStatus("Panel analysis in progress. Please wait..."); - Thread.Sleep(1000); // allow time for the mouse to be stopped moving by the user - - PanelLocationSelection.ShowPanelLocationOverlay(false); - - WindowManager.ExecutePopout(_simulatorProcess.Handle, PanelLocationSelection.PanelCoordinates); - _panelAnalysisModule.Analyze(_simulatorProcess, _currentPlaneProfile.ProfileId, PanelLocationSelection.PanelCoordinates.Count); - - PInvoke.SetForegroundWindow(_appForm.Handle); - - // Get the identified panel windows and previously saved panel destination location - List panelDestinationList = new List(); - var panels = _simulatorProcess.ChildWindows.FindAll(x => x.WindowType == WindowType.Custom_Popout || x.WindowType == WindowType.BuiltIn_Popout); - - var hasExistingData = _currentPlaneProfile.PanelSettings.PanelDestinationList.Count > 0; - - _currentPlaneProfile.PanelSettings.PanelDestinationList.ForEach(x => x.IsOpened = false); - - foreach (var panel in panels) - { - var index = _currentPlaneProfile.PanelSettings.PanelDestinationList.FindIndex(x => x.PanelName == panel.Title); - - if (index != -1) - { - _currentPlaneProfile.PanelSettings.PanelDestinationList[index].PanelHandle = panel.Handle; - _currentPlaneProfile.PanelSettings.PanelDestinationList[index].IsOpened = true; - _currentPlaneProfile.PanelSettings.PanelDestinationList[index].PanelType = panel.WindowType; - } - else - { - Rect rect = new Rect(); - var window = PInvoke.GetWindowRect(panel.Handle, out rect); - - PanelDestinationInfo panelDestinationInfo = new PanelDestinationInfo - { - PanelName = panel.Title, - PanelHandle = panel.Handle, - Left = rect.Left, - Top = rect.Top, - Width = rect.Right - rect.Left, - Height = rect.Bottom - rect.Top, - IsOpened = true, - PanelType = panel.WindowType - }; - - _currentPlaneProfile.PanelSettings.PanelDestinationList.Add(panelDestinationInfo); - } - } - - OnAnalysisCompleted?.Invoke(this, null); - - if (panelDestinationList.Count > 0) - Logger.LogStatus("Analysis has been completed. You may now drag the panels to their desire locations."); - else - Logger.LogStatus("No panel has been identified."); - - if(hasExistingData) - ApplyPanelSettings(); - - PInvoke.SetForegroundWindow(_appForm.Handle); - } - - public void ApplyPanelSettings() - { - var panels = _simulatorProcess.ChildWindows.FindAll(x => x.WindowType == WindowType.Custom_Popout || x.WindowType == WindowType.BuiltIn_Popout); - - int applyCount = 0; - - Parallel.ForEach(panels, panel => - { - var panelDestinationInfo = _currentPlaneProfile.PanelSettings.PanelDestinationList.Find(x => x.PanelName == panel.Title); - if (panelDestinationInfo != null && panelDestinationInfo.Width != 0 && panelDestinationInfo.Height != 0) - { - if (panelDestinationInfo.Left != 0 && panelDestinationInfo.Top != 0) - { - // Apply locations - PInvoke.MoveWindow(panel.Handle, panelDestinationInfo.Left, panelDestinationInfo.Top, panelDestinationInfo.Width, panelDestinationInfo.Height, true); - applyCount++; - } - - // Apply always on top - Thread.Sleep(300); - WindowManager.ApplyAlwaysOnTop(panel.Handle, _currentPlaneProfile.PanelSettings.AlwaysOnTop); - - // Apply hide title bar - Thread.Sleep(300); - WindowManager.ApplyHidePanelTitleBar(panel.Handle, _currentPlaneProfile.PanelSettings.HidePanelTitleBar); - } - }); - - if(applyCount > 0) - Logger.LogStatus("Previously saved panel settings have been applied."); - else if (panels.Count > 0 && applyCount == 0) - Logger.LogStatus("Please move the newly identified panels to their desire locations. Once everything is perfect, click 'Save Settings' and these settings will be used in future flights."); - else - Logger.LogStatus("No panel has been found."); - - PInvoke.SetForegroundWindow(_appForm.Handle); - } - - public void SavePanelSettings() - { - var profileId = _currentPlaneProfile.ProfileId; - - // Get latest panel destination locations from screen - var panels = _simulatorProcess.ChildWindows.FindAll(x => x.WindowType == WindowType.Custom_Popout || x.WindowType == WindowType.BuiltIn_Popout); - foreach(var panel in panels) - { - Rect rect = new Rect(); - var window = PInvoke.GetWindowRect(panel.Handle, out rect); - - var panelDestinationInfo = _currentPlaneProfile.PanelSettings.PanelDestinationList.Find(x => x.PanelName == panel.Title); - - if (panelDestinationInfo == null) - { - panelDestinationInfo = new PanelDestinationInfo(); - _currentPlaneProfile.PanelSettings.PanelDestinationList.Add(panelDestinationInfo); - } - - panelDestinationInfo.PanelName = panel.Title; - panelDestinationInfo.Left = rect.Left; - panelDestinationInfo.Top = rect.Top; - panelDestinationInfo.Width = rect.Right - rect.Left; - panelDestinationInfo.Height = rect.Bottom - rect.Top; - } - - var userData = FileManager.ReadUserData(); - - if (!userData.Profiles.Exists(x => x.ProfileId == _currentPlaneProfile.ProfileId)) - userData.Profiles.Add(_currentPlaneProfile); - else - { - var profileIndex = userData.Profiles.FindIndex(x => x.ProfileId == _currentPlaneProfile.ProfileId); - userData.Profiles[profileIndex] = _currentPlaneProfile; - } - - FileManager.WriteUserData(userData); - - OnPanelSettingsChanged?.Invoke(this, null); - - Logger.LogStatus("Panel settings have been saved."); - } - - public void UpdatePanelLocationUI() - { - _panelLocationSelectionModule.UpdatePanelLocationUI(); - } - - public int AddUserProfile(string profileName, string analysisTemplateName) - { - var userPlaneProfiles = FileManager.ReadCustomPlaneProfileData(); - - int nextProfileId = 1000; - - if (userPlaneProfiles.Count > 0) - nextProfileId = userPlaneProfiles.Max(x => x.ProfileId) + 1; - - profileName = $"User - {profileName}"; - - var newPlaneProfile = new PlaneProfile() - { - ProfileId = nextProfileId, - ProfileName = profileName, - AnalysisTemplateName = analysisTemplateName == "New" ? profileName : analysisTemplateName, - IsUserProfile = true - }; - - userPlaneProfiles.Add(newPlaneProfile); - - FileManager.WriteCustomPlaneProfileData(userPlaneProfiles); - - return nextProfileId; - } - - public void DeleteUserProfile(PlaneProfile planeProfile) - { - if (planeProfile.IsUserProfile) - { - // Remove custom plane profile data - var profiles = FileManager.ReadCustomPlaneProfileData(); - profiles.RemoveAll(x => x.ProfileId == planeProfile.ProfileId); - FileManager.WriteCustomPlaneProfileData(profiles); - - // Remove analysis template data - if (!profiles.Exists(x => x.AnalysisTemplateName == planeProfile.AnalysisTemplateName)) - { - FileManager.RemoveCustomAnalysisTemplate(planeProfile.AnalysisTemplateName); - var templates = FileManager.ReadCustomAnalysisTemplateData(); - templates.RemoveAll(x => x.TemplateName == planeProfile.AnalysisTemplateName); - FileManager.WriteCustomAnalysisTemplateData(templates); - } - - // Remove profile from user data - var userData = FileManager.ReadUserData(); - userData.Profiles.RemoveAll(x => x.ProfileId == planeProfile.ProfileId); - FileManager.WriteUserData(userData); - } - else - { - // Remove plane profile data - var profiles = FileManager.ReadBuiltInPlaneProfileData(); - profiles.RemoveAll(x => x.ProfileId == planeProfile.ProfileId); - FileManager.WriteBuiltInPlaneProfileData(profiles); - - // Remove profile from user data - var userData = FileManager.ReadUserData(); - userData.Profiles.RemoveAll(x => x.ProfileId == planeProfile.ProfileId); - FileManager.WriteUserData(userData); - } - - _currentPlaneProfile = null; - } - } -} diff --git a/Modules/PlaneProfile.cs b/Modules/PlaneProfile.cs deleted file mode 100644 index 2731691..0000000 --- a/Modules/PlaneProfile.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System.Collections.Generic; - -namespace MSFSPopoutPanelManager -{ - public class PlaneProfile - { - public PlaneProfile() - { - ProfileId = 1000; - IsUserProfile = false; - AnalysisTemplateName = "none"; - } - - public int ProfileId { get; set; } - - public string ProfileName { get; set; } - - public string AnalysisTemplateName { get; set; } - - public bool IsUserProfile { get; set; } - } - - public class AnalysisData - { - public AnalysisData() - { - Templates = new List