1
0
Fork 0
mirror of https://github.com/hawkeye-stan/msfs-popout-panel-manager.git synced 2024-11-25 07:10:10 +00:00

Version 2.1

This commit is contained in:
hawkeye 2021-10-17 12:18:45 -04:00
parent 8cdcaf0d45
commit d2a732b634
25 changed files with 844 additions and 115 deletions

View file

@ -5,12 +5,12 @@
<TargetFramework>net5.0-windows</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
<Platforms>x64</Platforms>
<Version>2.0.2</Version>
<Version>2.1</Version>
<AssemblyName>MSFSPopoutPanelManager</AssemblyName>
<RootNamespace>MSFSPopoutPanelManager</RootNamespace>
<ApplicationIcon>WindowManager.ico</ApplicationIcon>
<Authors>Stanley Kwok</Authors>
<Product>MSFS 2020 Popout Window Manager</Product>
<Product>MSFS 2020 Popout Panel Manager</Product>
<PackageId>MSFS 2020 Popout Panel Manager</PackageId>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

View file

@ -11,6 +11,8 @@ namespace MSFSPopoutPanelManager
public IntPtr Handle { get; set; }
public int PanelId { get; set; }
public string Title { get; set; }
public string ClassName { get; set; }

View file

@ -15,7 +15,16 @@ namespace MSFSPopoutPanelManager
FileManager.StartupPath = Application.StartupPath;
}
public static List<PlaneProfile> ReadPlaneProfileData()
public static List<PlaneProfile> ReadAllPlaneProfileData()
{
List<PlaneProfile> allProfiles = new List<PlaneProfile>();
allProfiles.AddRange(FileManager.ReadBuiltInPlaneProfileData());
allProfiles.AddRange(FileManager.ReadCustomPlaneProfileData());
return allProfiles;
}
public static List<PlaneProfile> ReadBuiltInPlaneProfileData()
{
try
{
@ -30,6 +39,57 @@ namespace MSFSPopoutPanelManager
}
}
public static List<PlaneProfile> ReadCustomPlaneProfileData()
{
try
{
using (StreamReader reader = new StreamReader(GetFilePathByType(FilePathType.ProfileData) + "customplaneprofile.json"))
{
return JsonConvert.DeserializeObject<List<PlaneProfile>>(reader.ReadToEnd());
}
}
catch
{
return new List<PlaneProfile>();
}
}
public static bool WriteBuiltInPlaneProfileData(List<PlaneProfile> 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<PlaneProfile> 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
@ -72,19 +132,86 @@ namespace MSFSPopoutPanelManager
}
}
public static List<AnalysisData> ReadAnalysisTemplateData()
public static List<AnalysisData> ReadAllAnalysisTemplateData()
{
using (StreamReader reader = new StreamReader(GetFilePathByType(FilePathType.AnalysisData) + "analysisconfig.json"))
List<AnalysisData> allTemplates = new List<AnalysisData>();
allTemplates.AddRange(FileManager.ReadBuiltInAnalysisTemplateData());
allTemplates.AddRange(FileManager.ReadCustomAnalysisTemplateData());
return allTemplates;
}
public static List<AnalysisData> ReadBuiltInAnalysisTemplateData()
{
try
{
try
using (StreamReader reader = new StreamReader(GetFilePathByType(FilePathType.AnalysisData) + "analysisconfig.json"))
{
return JsonConvert.DeserializeObject<List<AnalysisData>>(reader.ReadToEnd());
try
{
return JsonConvert.DeserializeObject<List<AnalysisData>>(reader.ReadToEnd());
}
catch(Exception ex)
{
throw new Exception("The file analysisconfig.json is invalid.");
}
}
catch(Exception ex)
}
catch
{
return new List<AnalysisData>();
}
}
public static List<AnalysisData> ReadCustomAnalysisTemplateData()
{
try
{
using (StreamReader reader = new StreamReader(GetFilePathByType(FilePathType.AnalysisData) + "customanalysisconfig.json"))
{
throw new Exception("The file analysisconfig.json is invalid.");
try
{
return JsonConvert.DeserializeObject<List<AnalysisData>>(reader.ReadToEnd());
}
catch (Exception ex)
{
throw new Exception("The file customanalysisconfig.json is invalid.");
}
}
}
catch
{
return new List<AnalysisData>();
}
}
public static void WriteCustomAnalysisTemplateData(List<AnalysisData> 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)
@ -106,10 +233,12 @@ namespace MSFSPopoutPanelManager
return new MemoryStream(File.ReadAllBytes(fullFilePath));
}
public static void SaveFile(FilePathType filePathType, string fileName, MemoryStream memoryStream)
public static void SaveFile(FilePathType filePathType, string subFolder, string fileName, MemoryStream memoryStream)
{
var folderPath = GetFilePathByType(filePathType);
var fullFilePath = GetFilePathByType(filePathType) + fileName;
subFolder = String.IsNullOrEmpty(subFolder) ? String.Empty : subFolder + @"\";
var folderPath = GetFilePathByType(filePathType) + subFolder;
var fullFilePath = folderPath + fileName;
Directory.CreateDirectory(folderPath);

View file

@ -1,20 +1,13 @@
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.Linq;
using System.Threading;
namespace MSFSPopoutPanelManager
{
public class ImageAnalysis
{
public static Point ExhaustiveTemplateMatchAnalysis(Bitmap sourceImage, Bitmap templateImage, int imageShrinkFactor, float similarityThreshHold)
{
const int MICROSOFT_WINDOWS_HANDLE_FRAME_X_VALUE_ADJUSTMENT = -8;
// Full image pixel to pixel matching algorithm
ExhaustiveTemplateMatching etm = new ExhaustiveTemplateMatching(similarityThreshHold);
TemplateMatch[] templateMatches = etm.ProcessImage(sourceImage, templateImage);
@ -23,12 +16,8 @@ namespace MSFSPopoutPanelManager
{
var match = templateMatches.OrderByDescending(x => x.Similarity).First(); // Just look at the first match since only one operation can be accomplished at a time on MSFS side
var x = match.Rectangle.X * imageShrinkFactor;
var y = match.Rectangle.Y * imageShrinkFactor;
//var width = match.Rectangle.Width * imageShrinkFactor;
//var height = match.Rectangle.Height * imageShrinkFactor;
//var centerX = x + width / 2 + MICROSOFT_WINDOWS_HANDLE_FRAME_X_VALUE_ADJUSTMENT;
//var centerY = y + height / 2;
var x = match.Rectangle.X * imageShrinkFactor + templateImage.Width * imageShrinkFactor / 4;
var y = match.Rectangle.Y * imageShrinkFactor + templateImage.Height * imageShrinkFactor / 4;
return new Point(x, y);
}
@ -38,17 +27,30 @@ namespace MSFSPopoutPanelManager
public static float ExhaustiveTemplateMatchAnalysisScore(Bitmap sourceImage, Bitmap templateImage, float similarityThreshHold)
{
ExhaustiveTemplateMatching etm = new ExhaustiveTemplateMatching(similarityThreshHold);
TemplateMatch[] templateMatches = etm.ProcessImage(sourceImage, templateImage);
// SUSAN corner block matching algorithm
SusanCornersDetector scd = new SusanCornersDetector(50, 8);
var points = scd.ProcessImage(sourceImage);
// Highlight the matchings that were found and saved a copy of the highlighted image
if (templateMatches != null && templateMatches.Length > 0)
{
var imageMatched = templateMatches.ToList().Max(x => x.Similarity);
return imageMatched;
}
// 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 0;
return templateMatches.Count;
// Full image pixel to pixel matching algorithm
//ExhaustiveTemplateMatching etm = new ExhaustiveTemplateMatching(similarityThreshHold);
//TemplateMatch[] templateMatches = etm.ProcessImage(sourceImage, templateImage);
//// Highlight the matchings that were found and saved a copy of the highlighted image
//if (templateMatches != null && templateMatches.Length > 0)
//{
// var imageMatched = templateMatches.ToList().Max(x => x.Similarity);
// return imageMatched;
//}
//return 0;
}
}
}

View file

@ -64,7 +64,12 @@ namespace MSFSPopoutPanelManager
var rect = new Rect();
PInvoke.GetWindowRect(windowHandle, out rect);
var bounds = new Rectangle(rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top);
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 bitmap = new Bitmap(bounds.Width, bounds.Height);
using (Graphics g = Graphics.FromImage(bitmap))

View file

@ -9,6 +9,7 @@ using System.Runtime.InteropServices;
using System.Text;
using System.Drawing.Imaging;
using System.Threading.Tasks;
using System.IO;
namespace MSFSPopoutPanelManager
{
@ -339,14 +340,20 @@ namespace MSFSPopoutPanelManager
private void AnalyzePopoutWindows(WindowProcess simulatorProcess, int profileId)
{
List<PanelScore> panelScores = new List<PanelScore>();
var panelScores = new List<PanelScore>();
// Get analysis template data for the profile
var planeProfile = FileManager.ReadPlaneProfileData().Find(x => x.ProfileId == profileId);
var templateData = FileManager.ReadAnalysisTemplateData().Find(x => x.TemplateName == planeProfile.AnalysisTemplateName);
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
List<KeyValuePair<string, Bitmap>> templates = new List<KeyValuePair<string, Bitmap>>();
var templates = new List<KeyValuePair<string, Bitmap>>();
foreach (var template in templateData.Templates)
{
foreach (var imagePath in template.ImagePaths)
@ -396,6 +403,62 @@ namespace MSFSPopoutPanelManager
}
}
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<Bitmap>();
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<string>() { @$"{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);

View file

@ -58,7 +58,7 @@ namespace MSFSPopoutPanelManager
Application.OpenForms[i].Close();
}
if (PlaneProfile.PanelSourceCoordinates != null && PlaneProfile.PanelSourceCoordinates.Count > 0)
if (PlaneProfile != null && PlaneProfile.PanelSourceCoordinates != null && PlaneProfile.PanelSourceCoordinates.Count > 0)
{
foreach (var coor in PlaneProfile.PanelSourceCoordinates)
WindowManager.AddPanelLocationSelectionOverlay(coor.PanelIndex.ToString(), coor.X, coor.Y);

View file

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
@ -68,14 +69,23 @@ namespace MSFSPopoutPanelManager
};
}
public void PlaneProfileChanged(int profileId, bool showCoordinateOverlay)
public void PlaneProfileChanged(int? profileId, bool showCoordinateOverlay)
{
Logger.LogStatus(String.Empty);
_currentPlaneProfile = FileManager.GetUserPlaneProfile(profileId);
_panelLocationSelectionModule.PlaneProfile = _currentPlaneProfile;
_panelLocationSelectionModule.ShowPanelLocationOverlay(showCoordinateOverlay);
_panelLocationSelectionModule.UpdatePanelLocationUI();
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()
@ -85,7 +95,7 @@ namespace MSFSPopoutPanelManager
FileManager.WriteUserData(userData);
var profileName = FileManager.ReadPlaneProfileData().Find(x => x.ProfileId == _currentPlaneProfile.ProfileId).ProfileName;
var profileName = FileManager.ReadAllPlaneProfileData().Find(x => x.ProfileId == _currentPlaneProfile.ProfileId).ProfileName;
Logger.LogStatus($"Profile '{profileName}' has been set as default.");
}
@ -138,12 +148,17 @@ namespace MSFSPopoutPanelManager
var hasExistingData = _currentPlaneProfile.PanelSettings.PanelDestinationList.Count > 0;
_currentPlaneProfile.PanelSettings.PanelDestinationList.ForEach(x => x.IsOpened = false);
foreach (var panel in panels)
{
if (hasExistingData)
var index = _currentPlaneProfile.PanelSettings.PanelDestinationList.FindIndex(x => x.PanelName == panel.Title);
if (index != -1)
{
var index = _currentPlaneProfile.PanelSettings.PanelDestinationList.FindIndex(x => x.PanelName == panel.Title);
_currentPlaneProfile.PanelSettings.PanelDestinationList[index].PanelHandle = panel.Handle;
_currentPlaneProfile.PanelSettings.PanelDestinationList[index].IsOpened = true;
_currentPlaneProfile.PanelSettings.PanelDestinationList[index].PanelType = panel.WindowType;
}
else
{
@ -157,14 +172,15 @@ namespace MSFSPopoutPanelManager
Left = rect.Left,
Top = rect.Top,
Width = rect.Right - rect.Left,
Height = rect.Bottom - rect.Top
Height = rect.Bottom - rect.Top,
IsOpened = true,
PanelType = panel.WindowType
};
_currentPlaneProfile.PanelSettings.PanelDestinationList.Add(panelDestinationInfo);
}
}
OnAnalysisCompleted?.Invoke(this, null);
if (panelDestinationList.Count > 0)
@ -226,10 +242,11 @@ namespace MSFSPopoutPanelManager
if (panelDestinationInfo == null)
{
panelDestinationInfo = new PanelDestinationInfo() { PanelName = panel.Title };
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;
@ -257,5 +274,70 @@ namespace MSFSPopoutPanelManager
{
_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;
}
}
}

View file

@ -4,22 +4,52 @@ 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<Template>();
IsUserTemplate = false;
}
public string TemplateName { get; set; }
public bool IsUserTemplate { get; set; }
public List<Template> Templates { get; set; }
public string TemplateImagePath
{
get
{
return TemplateName.Replace(" ", "_").Replace("/", "_").Replace(@"\", "_");
}
}
}
public class Template
{
public Template()
{
ImagePaths = new List<string>();
}
public int PopoutId { get; set; }
public string PopoutName { get; set; }

View file

@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
using Newtonsoft.Json;
namespace MSFSPopoutPanelManager
{
@ -60,10 +60,7 @@ namespace MSFSPopoutPanelManager
public class PanelDestinationInfo
{
public string PanelName { get; set; }
[JsonIgnore]
public IntPtr PanelHandle { get; set; }
public int Top { get; set; }
public int Left { get; set; }
@ -71,5 +68,13 @@ namespace MSFSPopoutPanelManager
public int Width { get; set; }
public int Height { get; set; }
public WindowType PanelType { get; set; }
[JsonIgnore]
public IntPtr PanelHandle { get; set; }
[JsonIgnore]
public bool IsOpened { get; set; }
}
}

View file

@ -73,10 +73,45 @@ Move your screen down a little bit by holding Right-Click in flight simulator un
10. Since the initial pop out positions may be different from plane to plane even when they are using the same instrumentation system, you can easily add new profiles in a configuration file and points it to the same analysisTemplateName. You can edit the configuration file [planeprofile.json](Config/planeprofile.json) in the **config** folder of the application to add additional profile. Look for profileId 1 and 2 in the file as example where both Cessna 172 and DA62 both uses Working Title G1000 NXi.
## Image Recognition Concept and Application Configurability
## Add or Delete Plane Profile
To-Do
The ability to add or delete plane profile is added starting in version 2.1 of the application.
<p align="center">
<img src="images/doc/screenshot9.png" width="600" hspace="10"/>
</p>
* For any existing built-in profile, you can delete them if they're not being used to reduce clutter. You can recreate them later by adding a new profile and selecting a built-in predefined image analysis template for the instrumentation set for that plane.
* To add a new profile for plane either by using built-in image recognition data for predefined instrumentation set or create a new one, please click "Add profile" button.
<p align="center">
<img src="images/doc/screenshot8.png" width="600" hspace="10"/>
</p>
1. Please specify a profile name and use alphanumeric characters only. The profile name will also be used to create windows folder for image recognition data.
2. Select either predefined image recognition analysis template or "New" for custom template. By selecting "New", the application will create custom image recognition data from the pop out panels.
3. Select "OK" when done. You should see the new profile displayed in the application. Now perform the same procedure to select pop out panel location in the game and click "Analyze".
4. Once analysis is completed, you should see the following if using a "New" template. The panel name will be generic such as "Panel 1", "Panel 2", and so forth. They work the same as built-in predefined templates. Continue to move pop out panels to their final screen locations and click "Save Settings".
<p align="center">
<img src="images/doc/screenshot10.png" width="600" hspace="10"/>
</p>
5. On next flight, your custom user profile will be available for selection.
## User Plane Profile Data
The user created plane profile and associate image recognition data are stored in the following folders in the application.
* Config/customplaneprofile.json - the list of user created plane profile.
* Config/AnalysisData/customanalysisconfig.json - define the location for custom plane profile image recognition data.
* Config/AnalsysData/User - XXXXXX - folders where custom image recognition data is stored for user created plane profile.
## Common Problem Resolution

145
UI/AddProfileForm.Designer.cs generated Normal file
View file

@ -0,0 +1,145 @@

namespace MSFSPopoutPanelManager
{
partial class AddProfileForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.darkLabel1 = new DarkUI.Controls.DarkLabel();
this.darkLabel2 = new DarkUI.Controls.DarkLabel();
this.textBoxProfileName = new DarkUI.Controls.DarkTextBox();
this.buttonOK = new System.Windows.Forms.Button();
this.buttonCancel = new System.Windows.Forms.Button();
this.comboBoxTemplates = new DarkUI.Controls.DarkComboBox();
this.SuspendLayout();
//
// darkLabel1
//
this.darkLabel1.AutoSize = true;
this.darkLabel1.Font = new System.Drawing.Font("Segoe UI", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.darkLabel1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
this.darkLabel1.Location = new System.Drawing.Point(33, 83);
this.darkLabel1.Name = "darkLabel1";
this.darkLabel1.Size = new System.Drawing.Size(128, 20);
this.darkLabel1.TabIndex = 1;
this.darkLabel1.Text = "Analysis Template";
//
// darkLabel2
//
this.darkLabel2.AutoSize = true;
this.darkLabel2.Font = new System.Drawing.Font("Segoe UI", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.darkLabel2.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
this.darkLabel2.Location = new System.Drawing.Point(33, 37);
this.darkLabel2.Name = "darkLabel2";
this.darkLabel2.Size = new System.Drawing.Size(96, 20);
this.darkLabel2.TabIndex = 2;
this.darkLabel2.Text = "Profile Name";
//
// textBoxProfileName
//
this.textBoxProfileName.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(69)))), ((int)(((byte)(73)))), ((int)(((byte)(74)))));
this.textBoxProfileName.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.textBoxProfileName.Font = new System.Drawing.Font("Segoe UI", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.textBoxProfileName.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
this.textBoxProfileName.Location = new System.Drawing.Point(177, 35);
this.textBoxProfileName.MaxLength = 50;
this.textBoxProfileName.Name = "textBoxProfileName";
this.textBoxProfileName.Size = new System.Drawing.Size(356, 27);
this.textBoxProfileName.TabIndex = 3;
this.textBoxProfileName.TextChanged += new System.EventHandler(this.textBoxProfileName_TextChanged);
this.textBoxProfileName.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.textBoxProfileName_KeyPress);
//
// buttonOK
//
this.buttonOK.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(17)))), ((int)(((byte)(158)))), ((int)(((byte)(218)))));
this.buttonOK.Enabled = false;
this.buttonOK.Font = new System.Drawing.Font("Segoe UI", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.buttonOK.ForeColor = System.Drawing.Color.White;
this.buttonOK.Location = new System.Drawing.Point(268, 141);
this.buttonOK.Name = "buttonOK";
this.buttonOK.Size = new System.Drawing.Size(118, 35);
this.buttonOK.TabIndex = 22;
this.buttonOK.Text = "OK";
this.buttonOK.UseVisualStyleBackColor = false;
this.buttonOK.Click += new System.EventHandler(this.buttonOK_Click);
//
// buttonCancel
//
this.buttonCancel.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(17)))), ((int)(((byte)(158)))), ((int)(((byte)(218)))));
this.buttonCancel.Font = new System.Drawing.Font("Segoe UI", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.buttonCancel.ForeColor = System.Drawing.Color.White;
this.buttonCancel.Location = new System.Drawing.Point(410, 141);
this.buttonCancel.Name = "buttonCancel";
this.buttonCancel.Size = new System.Drawing.Size(118, 35);
this.buttonCancel.TabIndex = 23;
this.buttonCancel.Text = "Cancel";
this.buttonCancel.UseVisualStyleBackColor = false;
this.buttonCancel.Click += new System.EventHandler(this.buttonCancel_Click);
//
// comboBoxTemplates
//
this.comboBoxTemplates.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawVariable;
this.comboBoxTemplates.Font = new System.Drawing.Font("Segoe UI", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.comboBoxTemplates.FormattingEnabled = true;
this.comboBoxTemplates.Location = new System.Drawing.Point(177, 79);
this.comboBoxTemplates.Name = "comboBoxTemplates";
this.comboBoxTemplates.Size = new System.Drawing.Size(356, 28);
this.comboBoxTemplates.TabIndex = 24;
//
// AddProfileForm
//
this.AcceptButton = this.buttonOK;
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.buttonCancel;
this.ClientSize = new System.Drawing.Size(562, 205);
this.ControlBox = false;
this.Controls.Add(this.comboBoxTemplates);
this.Controls.Add(this.buttonCancel);
this.Controls.Add(this.buttonOK);
this.Controls.Add(this.textBoxProfileName);
this.Controls.Add(this.darkLabel2);
this.Controls.Add(this.darkLabel1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "AddProfileForm";
this.Text = "Add Profile";
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private DarkUI.Controls.DarkLabel darkLabel1;
private DarkUI.Controls.DarkLabel darkLabel2;
private DarkUI.Controls.DarkTextBox textBoxProfileName;
private System.Windows.Forms.Button buttonOK;
private System.Windows.Forms.Button buttonCancel;
private DarkUI.Controls.DarkComboBox comboBoxTemplates;
}
}

69
UI/AddProfileForm.cs Normal file
View file

@ -0,0 +1,69 @@
using DarkUI.Forms;
using System;
using System.Data;
using System.Linq;
using System.Windows.Forms;
namespace MSFSPopoutPanelManager
{
public partial class AddProfileForm : DarkForm
{
private PanelManager _panelManager;
public event EventHandler<EventArgs<int>> OnAddProfile;
public AddProfileForm(PanelManager panelManager)
{
InitializeComponent();
_panelManager = panelManager;
SetTemplateDropDown();
}
private void textBoxProfileName_KeyPress(object sender, KeyPressEventArgs e)
{
e.Handled = !(Char.IsLetterOrDigit(e.KeyChar) ||
Char.IsPunctuation(e.KeyChar) ||
e.KeyChar == (char)Keys.Space ||
e.KeyChar == (char)Keys.Back);
}
private void textBoxProfileName_TextChanged(object sender, EventArgs e)
{
buttonOK.Enabled = textBoxProfileName.Text.Trim().Length > 0;
}
public void SetTemplateDropDown()
{
try
{
var templates = FileManager.ReadAllAnalysisTemplateData();
var templateNames = templates.OrderBy(x => x.TemplateName).Select(x => x.TemplateName).Distinct().ToList();
templateNames.Insert(0, "New");
comboBoxTemplates.DataSource = templateNames;
}
catch (Exception ex)
{
Logger.LogStatus(ex.Message);
}
}
private void buttonCancel_Click(object sender, EventArgs e)
{
this.Close();
}
private void buttonOK_Click(object sender, EventArgs e)
{
var profileName = textBoxProfileName.Text.Trim();
var analysisTemplateName = Convert.ToString(comboBoxTemplates.SelectedValue);
var profileId = _panelManager.AddUserProfile(profileName, analysisTemplateName);
OnAddProfile?.Invoke(this, new EventArgs<int>(profileId));
this.Close();
}
}
}

60
UI/AddProfileForm.resx Normal file
View file

@ -0,0 +1,60 @@
<root>
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View file

@ -116,6 +116,7 @@ namespace MSFSPopoutPanelManager
this.dataGridViewPanels.Size = new System.Drawing.Size(820, 225);
this.dataGridViewPanels.TabIndex = 8;
this.dataGridViewPanels.CellEndEdit += new System.Windows.Forms.DataGridViewCellEventHandler(this.dataGridViewPanels_CellEndEdit);
this.dataGridViewPanels.CellFormatting += new System.Windows.Forms.DataGridViewCellFormattingEventHandler(this.dataGridViewPanels_CellFormatting);
this.dataGridViewPanels.CellValidating += new System.Windows.Forms.DataGridViewCellValidatingEventHandler(this.dataGridViewPanels_CellValidating);
//
// PanelName

View file

@ -1,11 +1,8 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace MSFSPopoutPanelManager
@ -34,6 +31,8 @@ namespace MSFSPopoutPanelManager
checkBoxAlwaysOnTop.Checked = PanelManager.CurrentPanelProfile.PanelSettings.AlwaysOnTop;
checkBoxHidePanelTitleBar.Checked = PanelManager.CurrentPanelProfile.PanelSettings.HidePanelTitleBar;
}
private void buttonRestart_Click(object sender, EventArgs e)
@ -76,6 +75,20 @@ namespace MSFSPopoutPanelManager
PInvoke.MoveWindow(panel.PanelHandle, left, top, width, height, true);
}
private void dataGridViewPanels_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
if (e.ColumnIndex == 0)
{
DataGridView dgv = sender as DataGridView;
PanelDestinationInfo data = dgv.Rows[e.RowIndex].DataBoundItem as PanelDestinationInfo;
if(!data.IsOpened && data.PanelType == WindowType.Custom_Popout)
{
dataGridViewPanels.Rows[e.RowIndex].DefaultCellStyle.BackColor = Color.PaleVioletRed;
}
}
}
private void dataGridViewPanels_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
{
// must be numbers

View file

@ -30,6 +30,8 @@ namespace MSFSPopoutPanelManager
private void InitializeComponent()
{
this.panel1 = new System.Windows.Forms.Panel();
this.buttonDeleteProfile = new System.Windows.Forms.Button();
this.buttonAddProfile = new System.Windows.Forms.Button();
this.buttonSetDefault = new System.Windows.Forms.Button();
this.label2 = new System.Windows.Forms.Label();
this.comboBoxProfile = new System.Windows.Forms.ComboBox();
@ -54,21 +56,51 @@ namespace MSFSPopoutPanelManager
//
// panel1
//
this.panel1.Controls.Add(this.buttonDeleteProfile);
this.panel1.Controls.Add(this.buttonAddProfile);
this.panel1.Controls.Add(this.buttonSetDefault);
this.panel1.Controls.Add(this.label2);
this.panel1.Controls.Add(this.comboBoxProfile);
this.panel1.ForeColor = System.Drawing.Color.White;
this.panel1.Location = new System.Drawing.Point(0, 0);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(516, 79);
this.panel1.Size = new System.Drawing.Size(571, 118);
this.panel1.TabIndex = 0;
//
// buttonDeleteProfile
//
this.buttonDeleteProfile.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(17)))), ((int)(((byte)(158)))), ((int)(((byte)(218)))));
this.buttonDeleteProfile.Enabled = false;
this.buttonDeleteProfile.Font = new System.Drawing.Font("Segoe UI", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.buttonDeleteProfile.ForeColor = System.Drawing.Color.White;
this.buttonDeleteProfile.Location = new System.Drawing.Point(289, 76);
this.buttonDeleteProfile.Name = "buttonDeleteProfile";
this.buttonDeleteProfile.Size = new System.Drawing.Size(118, 35);
this.buttonDeleteProfile.TabIndex = 21;
this.buttonDeleteProfile.Text = "Delete Profile";
this.buttonDeleteProfile.UseVisualStyleBackColor = false;
this.buttonDeleteProfile.Click += new System.EventHandler(this.buttonDeleteProfile_Click);
//
// buttonAddProfile
//
this.buttonAddProfile.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(17)))), ((int)(((byte)(158)))), ((int)(((byte)(218)))));
this.buttonAddProfile.Font = new System.Drawing.Font("Segoe UI", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.buttonAddProfile.ForeColor = System.Drawing.Color.White;
this.buttonAddProfile.Location = new System.Drawing.Point(158, 76);
this.buttonAddProfile.Name = "buttonAddProfile";
this.buttonAddProfile.Size = new System.Drawing.Size(115, 35);
this.buttonAddProfile.TabIndex = 20;
this.buttonAddProfile.Text = "Add Profile";
this.buttonAddProfile.UseVisualStyleBackColor = false;
this.buttonAddProfile.Click += new System.EventHandler(this.buttonAddProfile_Click);
//
// buttonSetDefault
//
this.buttonSetDefault.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(17)))), ((int)(((byte)(158)))), ((int)(((byte)(218)))));
this.buttonSetDefault.Enabled = false;
this.buttonSetDefault.Font = new System.Drawing.Font("Segoe UI", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.buttonSetDefault.ForeColor = System.Drawing.Color.White;
this.buttonSetDefault.Location = new System.Drawing.Point(406, 37);
this.buttonSetDefault.Location = new System.Drawing.Point(32, 76);
this.buttonSetDefault.Name = "buttonSetDefault";
this.buttonSetDefault.Size = new System.Drawing.Size(107, 35);
this.buttonSetDefault.TabIndex = 19;
@ -95,7 +127,7 @@ namespace MSFSPopoutPanelManager
this.comboBoxProfile.FormattingEnabled = true;
this.comboBoxProfile.Location = new System.Drawing.Point(35, 41);
this.comboBoxProfile.Name = "comboBoxProfile";
this.comboBoxProfile.Size = new System.Drawing.Size(365, 28);
this.comboBoxProfile.Size = new System.Drawing.Size(445, 28);
this.comboBoxProfile.TabIndex = 5;
this.comboBoxProfile.SelectedIndexChanged += new System.EventHandler(this.comboBoxProfile_SelectedIndexChanged);
//
@ -106,9 +138,9 @@ namespace MSFSPopoutPanelManager
this.panel2.Controls.Add(this.label4);
this.panel2.Controls.Add(this.buttonPanelSelection);
this.panel2.Controls.Add(this.label3);
this.panel2.Location = new System.Drawing.Point(0, 80);
this.panel2.Location = new System.Drawing.Point(0, 117);
this.panel2.Name = "panel2";
this.panel2.Size = new System.Drawing.Size(516, 227);
this.panel2.Size = new System.Drawing.Size(571, 191);
this.panel2.TabIndex = 8;
//
// label1
@ -116,7 +148,7 @@ namespace MSFSPopoutPanelManager
this.label1.AutoSize = true;
this.label1.Font = new System.Drawing.Font("Segoe UI", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.label1.ForeColor = System.Drawing.Color.White;
this.label1.Location = new System.Drawing.Point(35, 86);
this.label1.Location = new System.Drawing.Point(35, 55);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(213, 20);
this.label1.TabIndex = 12;
@ -127,7 +159,7 @@ namespace MSFSPopoutPanelManager
this.label5.AutoSize = true;
this.label5.Font = new System.Drawing.Font("Segoe UI", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.label5.ForeColor = System.Drawing.Color.White;
this.label5.Location = new System.Drawing.Point(35, 146);
this.label5.Location = new System.Drawing.Point(35, 115);
this.label5.Name = "label5";
this.label5.Size = new System.Drawing.Size(372, 20);
this.label5.TabIndex = 11;
@ -138,7 +170,7 @@ namespace MSFSPopoutPanelManager
this.label4.AutoSize = true;
this.label4.Font = new System.Drawing.Font("Segoe UI", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.label4.ForeColor = System.Drawing.Color.White;
this.label4.Location = new System.Drawing.Point(35, 115);
this.label4.Location = new System.Drawing.Point(35, 84);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(418, 20);
this.label4.TabIndex = 10;
@ -147,9 +179,10 @@ namespace MSFSPopoutPanelManager
// buttonPanelSelection
//
this.buttonPanelSelection.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(17)))), ((int)(((byte)(158)))), ((int)(((byte)(218)))));
this.buttonPanelSelection.Enabled = false;
this.buttonPanelSelection.Font = new System.Drawing.Font("Segoe UI", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.buttonPanelSelection.ForeColor = System.Drawing.Color.White;
this.buttonPanelSelection.Location = new System.Drawing.Point(35, 177);
this.buttonPanelSelection.Location = new System.Drawing.Point(35, 146);
this.buttonPanelSelection.Name = "buttonPanelSelection";
this.buttonPanelSelection.Size = new System.Drawing.Size(170, 35);
this.buttonPanelSelection.TabIndex = 9;
@ -161,9 +194,9 @@ namespace MSFSPopoutPanelManager
//
this.label3.Font = new System.Drawing.Font("Segoe UI", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.label3.ForeColor = System.Drawing.Color.White;
this.label3.Location = new System.Drawing.Point(20, 10);
this.label3.Location = new System.Drawing.Point(20, 4);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(488, 63);
this.label3.Size = new System.Drawing.Size(563, 47);
this.label3.TabIndex = 7;
this.label3.Text = "2. Identify the pop out panels in the game by clicking on them. Their locations w" +
"ill be saved and for use on future flights. (You only need to do this once per p" +
@ -174,7 +207,7 @@ namespace MSFSPopoutPanelManager
this.label6.AutoSize = true;
this.label6.Font = new System.Drawing.Font("Segoe UI", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.label6.ForeColor = System.Drawing.Color.White;
this.label6.Location = new System.Drawing.Point(103, 11);
this.label6.Location = new System.Drawing.Point(88, 10);
this.label6.Name = "label6";
this.label6.Size = new System.Drawing.Size(111, 20);
this.label6.TabIndex = 11;
@ -184,11 +217,11 @@ namespace MSFSPopoutPanelManager
//
this.textBoxPanelLocations.Font = new System.Drawing.Font("Segoe UI", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.textBoxPanelLocations.ForeColor = System.Drawing.SystemColors.WindowText;
this.textBoxPanelLocations.Location = new System.Drawing.Point(21, 41);
this.textBoxPanelLocations.Location = new System.Drawing.Point(6, 41);
this.textBoxPanelLocations.Multiline = true;
this.textBoxPanelLocations.Name = "textBoxPanelLocations";
this.textBoxPanelLocations.ReadOnly = true;
this.textBoxPanelLocations.Size = new System.Drawing.Size(301, 277);
this.textBoxPanelLocations.Size = new System.Drawing.Size(271, 277);
this.textBoxPanelLocations.TabIndex = 12;
//
// panel4
@ -196,9 +229,9 @@ namespace MSFSPopoutPanelManager
this.panel4.Controls.Add(this.checkBoxShowPanelLocation);
this.panel4.Controls.Add(this.textBoxPanelLocations);
this.panel4.Controls.Add(this.label6);
this.panel4.Location = new System.Drawing.Point(522, 0);
this.panel4.Location = new System.Drawing.Point(569, 0);
this.panel4.Name = "panel4";
this.panel4.Size = new System.Drawing.Size(335, 403);
this.panel4.Size = new System.Drawing.Size(288, 403);
this.panel4.TabIndex = 13;
//
// checkBoxShowPanelLocation
@ -208,7 +241,7 @@ namespace MSFSPopoutPanelManager
this.checkBoxShowPanelLocation.CheckState = System.Windows.Forms.CheckState.Checked;
this.checkBoxShowPanelLocation.Font = new System.Drawing.Font("Segoe UI", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.checkBoxShowPanelLocation.ForeColor = System.Drawing.Color.White;
this.checkBoxShowPanelLocation.Location = new System.Drawing.Point(61, 337);
this.checkBoxShowPanelLocation.Location = new System.Drawing.Point(35, 337);
this.checkBoxShowPanelLocation.Name = "checkBoxShowPanelLocation";
this.checkBoxShowPanelLocation.Size = new System.Drawing.Size(213, 24);
this.checkBoxShowPanelLocation.TabIndex = 17;
@ -219,6 +252,7 @@ namespace MSFSPopoutPanelManager
// buttonAnalyze
//
this.buttonAnalyze.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(17)))), ((int)(((byte)(158)))), ((int)(((byte)(218)))));
this.buttonAnalyze.Enabled = false;
this.buttonAnalyze.Font = new System.Drawing.Font("Segoe UI", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.buttonAnalyze.ForeColor = System.Drawing.Color.White;
this.buttonAnalyze.Location = new System.Drawing.Point(32, 41);
@ -235,9 +269,9 @@ namespace MSFSPopoutPanelManager
this.panel3.Controls.Add(this.label7);
this.panel3.Enabled = false;
this.panel3.ForeColor = System.Drawing.Color.White;
this.panel3.Location = new System.Drawing.Point(3, 309);
this.panel3.Location = new System.Drawing.Point(0, 309);
this.panel3.Name = "panel3";
this.panel3.Size = new System.Drawing.Size(513, 94);
this.panel3.Size = new System.Drawing.Size(571, 94);
this.panel3.TabIndex = 14;
//
// label7
@ -294,5 +328,7 @@ namespace MSFSPopoutPanelManager
private System.Windows.Forms.Button buttonAnalyze;
private System.Windows.Forms.Label label7;
private System.Windows.Forms.Button buttonSetDefault;
private System.Windows.Forms.Button buttonAddProfile;
private System.Windows.Forms.Button buttonDeleteProfile;
}
}

View file

@ -23,7 +23,8 @@ namespace MSFSPopoutPanelManager
panelManager.PanelLocationSelection.OnSelectionCompleted += PanelLocationSelection_OnSelectionCompleted;
panelManager.PanelLocationSelection.OnLocationListChanged += PanelLocationSelection_OnLocationListChanged;
SetProfileDropDown();
var defaultProfileId = FileManager.ReadUserData().DefaultProfileId;
SetProfileDropDown(defaultProfileId);
}
public event EventHandler<EventArgs<bool>> OnAnalyzeAvailabilityChanged;
@ -49,7 +50,7 @@ namespace MSFSPopoutPanelManager
{
var sb = new StringBuilder();
if (PanelManager.CurrentPanelProfile.PanelSourceCoordinates.Count == 0)
if (PanelManager.CurrentPanelProfile == null || PanelManager.CurrentPanelProfile.PanelSourceCoordinates.Count == 0)
{
textBoxPanelLocations.Text = null;
}
@ -57,7 +58,7 @@ namespace MSFSPopoutPanelManager
{
foreach (var coor in PanelManager.CurrentPanelProfile.PanelSourceCoordinates)
{
sb.Append($"Panel: {coor.PanelIndex,-7} X-Pos: {coor.X,-10} Y-Pos: {coor.Y,-10}");
sb.Append($"Panel: {coor.PanelIndex,-5} X-Pos: {coor.X,-8} Y-Pos: {coor.Y,-8}");
sb.Append(Environment.NewLine);
}
@ -65,17 +66,19 @@ namespace MSFSPopoutPanelManager
}
}
public void SetProfileDropDown()
public void SetProfileDropDown(int? defaultProfileId)
{
try
{
var defaultProfileId = FileManager.ReadUserData().DefaultProfileId;
var profileData = FileManager.ReadPlaneProfileData();
var allProfiles = FileManager.ReadAllPlaneProfileData();
comboBoxProfile.DisplayMember = "ProfileName";
comboBoxProfile.ValueMember = "ProfileId";
comboBoxProfile.DataSource = profileData.OrderBy(x => x.ProfileName).ToList();
comboBoxProfile.SelectedValue = defaultProfileId;
comboBoxProfile.DataSource = allProfiles.OrderBy(x => x.ProfileName).ToList();
if (allProfiles.Exists(x => x.ProfileId == defaultProfileId))
comboBoxProfile.SelectedValue = defaultProfileId;
else
comboBoxProfile.SelectedIndex = -1;
}
catch (Exception ex)
{
@ -103,7 +106,10 @@ namespace MSFSPopoutPanelManager
private void comboBoxProfile_SelectedIndexChanged(object sender, EventArgs e)
{
PanelManager.PlaneProfileChanged(Convert.ToInt32(comboBoxProfile.SelectedValue), checkBoxShowPanelLocation.Checked);
buttonPanelSelection.Enabled = true;
buttonPanelSelection.Enabled = comboBoxProfile.SelectedValue != null;
buttonSetDefault.Enabled = comboBoxProfile.SelectedValue != null;
buttonDeleteProfile.Enabled = comboBoxProfile.SelectedValue != null;
buttonAnalyze.Enabled = !String.IsNullOrEmpty(textBoxPanelLocations.Text);
}
private void checkBoxShowPanelLocation_CheckedChanged(object sender, EventArgs e)
@ -128,7 +134,33 @@ namespace MSFSPopoutPanelManager
private void buttonSetDefault_Click(object sender, EventArgs e)
{
PanelManager.SetDefaultProfile();
if(comboBoxProfile.SelectedValue != null)
PanelManager.SetDefaultProfile();
}
private void buttonAddProfile_Click(object sender, EventArgs e)
{
AddProfileForm addProfileForm = new AddProfileForm(PanelManager);
addProfileForm.StartPosition = FormStartPosition.CenterParent;
addProfileForm.OnAddProfile += (soruce, e) => { SetProfileDropDown(e.Value); };
addProfileForm.ShowDialog();
}
private void buttonDeleteProfile_Click(object sender, EventArgs e)
{
var dialogResult = MessageBox.Show("Are you sure you want to delete the selected profile?", "Confirm Delete", MessageBoxButtons.YesNo);
if(dialogResult == DialogResult.Yes)
{
var selectedProfile = (PlaneProfile)comboBoxProfile.SelectedItem;
PanelManager.DeleteUserProfile(selectedProfile);
SetProfileDropDown(null);
PanelManager.PlaneProfileChanged(null, checkBoxShowPanelLocation.Checked);
buttonPanelSelection.Enabled = comboBoxProfile.SelectedValue != null;
buttonSetDefault.Enabled = comboBoxProfile.SelectedValue != null;
buttonDeleteProfile.Enabled = comboBoxProfile.SelectedValue != null;
buttonAnalyze.Enabled = !String.IsNullOrEmpty(textBoxPanelLocations.Text);
}
}
}
}

View file

@ -1,6 +1,11 @@
# Version History
<hr/>
## Vesion 2.1.0.0
* Added ability to delete built-in profile.
* Added ability to create and delete custom user profile.
* Improved image recognition algorithm using SUSAN Corner block matching algorithm.
## Vesion 2.0.3.0
* Fixed a crash bug when splitting out panel when trying to analyze the last split panel.
* Added PMS50.com GTN750 mod configuration

View file

@ -1,6 +1,7 @@
[
{
"templateName": "G1000",
"templateName": "Built-in G1000",
"IsUserTemplate": false,
"templates": [
{
"popoutId": 1,
@ -15,7 +16,8 @@
]
},
{
"templateName": "WT-G1000NXi",
"templateName": "Built-in WT-G1000NXi",
"IsUserTemplate": false,
"templates": [
{
"popoutId": 1,
@ -30,7 +32,8 @@
]
},
{
"templateName": "G3000-KINGAIR",
"templateName": "Built-in G3000-KINGAIR",
"IsUserTemplate": false,
"templates": [
{
"popoutId": 1,
@ -50,7 +53,8 @@
]
},
{
"templateName": "G3000",
"templateName": "Built-in G3000",
"IsUserTemplate": false,
"templates": [
{
"popoutId": 1,
@ -80,7 +84,8 @@
]
},
{
"templateName": "FBW-A32NX",
"templateName": "Built-in FBW-A32NX",
"IsUserTemplate": false,
"templates": [
{
"popoutId": 1,
@ -120,7 +125,8 @@
]
},
{
"templateName": "CJ4",
"templateName": "Built-in CJ4",
"IsUserTemplate": false,
"templates": [
{
"popoutId": 1,
@ -145,7 +151,8 @@
]
},
{
"templateName": "pms50-gtn750",
"templateName": "Built-in PMS50-GTN750",
"IsUserTemplate": false,
"templates": [
{
"popoutId": 1,

View file

@ -1,42 +1,50 @@
[
{
"profileId": 1,
"profileName": "Cessna 172 Skyhawk (WT G1000 NXi)",
"analysisTemplateName": "WT-G1000NXi"
"profileName": "Built-in - Cessna 172 Skyhawk (WT G1000 NXi)",
"IsUserProfile": false,
"analysisTemplateName": "Built-in WT-G1000NXi"
},
{
"profileId": 2,
"profileName": "DA62 (WT G1000 NXi)",
"analysisTemplateName": "WT-G1000NXi"
"profileName": "Built-in - DA62 (WT G1000 NXi)",
"IsUserProfile": false,
"analysisTemplateName": "Built-in WT-G1000NXi"
},
{
"profileId": 3,
"profileName": "A32NX (Flybywire) ",
"analysisTemplateName": "FBW-A32NX"
"profileName": "Built-in - A32NX (Flybywire)",
"IsUserProfile": false,
"analysisTemplateName": "Built-in FBW-A32NX"
},
{
"profileId": 4,
"profileName": "Beechcraft King Air 350i (Original G3000)",
"analysisTemplateName": "G3000-KINGAIR"
"profileName": "Built-in - Beechcraft King Air 350i (Original G3000)",
"IsUserProfile": false,
"analysisTemplateName": "Built-in G3000-KINGAIR"
},
{
"profileId": 5,
"profileName": "TBM 930 (Original G3000)",
"analysisTemplateName": "G3000"
"profileName": "Built-in - TBM 930 (Original G3000)",
"IsUserProfile": false,
"analysisTemplateName": "Built-in G3000"
},
{
"profileId": 6,
"profileName": "Cessna Citation CJ4",
"analysisTemplateName": "CJ4"
"profileName": "Built-in - Cessna Citation CJ4",
"IsUserProfile": false,
"analysisTemplateName": "Built-in CJ4"
},
{
"profileId": 7,
"profileName": "Asobo Planes with (Original G1000)",
"analysisTemplateName": "G1000"
"profileName": "Built-in - Asobo Planes with (Original G1000)",
"IsUserProfile": false,
"analysisTemplateName": "Built-in G1000"
},
{
"profileId": 8,
"profileName": "DA40NG (PMS50 GTN750)",
"analysisTemplateName": "pms50-gtn750"
"profileName": "Built-in - DA40NG (PMS50 GTN750)",
"IsUserProfile": false,
"analysisTemplateName": "Built-in PMS50-GTN750"
}
]

BIN
images/doc/screenshot10.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 50 KiB

BIN
images/doc/screenshot8.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

BIN
images/doc/screenshot9.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB