diff --git a/ILS_Window_Plugin/ILS_Window_Plugin.vcxproj b/ILS_Window_Plugin/ILS_Window_Plugin.vcxproj
index c7ba914..180c96f 100644
--- a/ILS_Window_Plugin/ILS_Window_Plugin.vcxproj
+++ b/ILS_Window_Plugin/ILS_Window_Plugin.vcxproj
@@ -115,14 +115,21 @@
-
-
-
+
+
+
+
+
+
+
-
+
+
+
+
Create
Create
@@ -136,16 +143,24 @@
-
-
-
+
+
+
+
+
+
+
-
+
+
+
+
+
diff --git a/ILS_Window_Plugin/ILS_Window_Plugin.vcxproj.filters b/ILS_Window_Plugin/ILS_Window_Plugin.vcxproj.filters
index 99de3c5..ac9e572 100644
--- a/ILS_Window_Plugin/ILS_Window_Plugin.vcxproj.filters
+++ b/ILS_Window_Plugin/ILS_Window_Plugin.vcxproj.filters
@@ -13,6 +13,24 @@
{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+ {06d11cba-76c2-4d9e-8107-6c5da2101bc1}
+
+
+ {8a7ca98b-50af-466b-a2f2-04eb78eba59e}
+
+
+ {687be561-bac8-411d-8e4a-810fa08569b5}
+
+
+ {c34849c8-8ad4-4f4f-acc9-2f8e9c582a1c}
+
+
+ {a1845d6f-0de7-4b25-9cd4-7b9629d2bf5e}
+
+
+ {f133707e-7c1a-4d8b-8a78-bdfd116acce5}
+
@@ -21,9 +39,6 @@
Source Files
-
- Source Files
-
Source Files
@@ -33,14 +48,38 @@
Source Files
-
+
Source Files
-
+
Source Files
-
- Source Files
+
+ Source Files\Theme\SolarisCDE
+
+
+ Source Files\Theme\SolarisCDE
+
+
+ Source Files\Theme\SolarisCDE
+
+
+ Source Files\Theme\X11
+
+
+ Source Files\Theme\X11
+
+
+ Source Files\Theme\X11
+
+
+ Source Files\Theme\X11
+
+
+ Source Files\Theme\SolarisCDE
+
+
+ Source Files\Theme\X11
@@ -70,9 +109,6 @@
Header Files
-
- Header Files
-
Header Files
@@ -88,18 +124,45 @@
Header Files
-
+
Header Files
-
- Header Files
+
+ Header Files\Theme\SolarisCDE
+
+
+ Header Files\Theme\SolarisCDE
-
+
+ Header Files\Theme\SolarisCDE
+
+
Header Files
-
+
+ Header Files\Theme\X11
+
+
+ Header Files\Theme\X11
+
+
+ Header Files\Theme\X11
+
+
Header Files
+
+ Header Files\Theme\SolarisCDE
+
+
+ Header Files\Theme\X11
+
+
+ Header Files\Theme\SolarisCDE
+
+
+ Header Files\Theme\X11
+
diff --git a/ILS_Window_Plugin/IWCdeIconifyBtn.cpp b/ILS_Window_Plugin/IWCdeIconifyBtn.cpp
new file mode 100644
index 0000000..acb91ef
--- /dev/null
+++ b/ILS_Window_Plugin/IWCdeIconifyBtn.cpp
@@ -0,0 +1,24 @@
+#include "pch.h"
+#include "IWCdeIconifyBtn.h"
+#include "RenderUtils.h"
+
+IWCdeIconifyBtn::IWCdeIconifyBtn(COLORREF backgroundColor, COLORREF lightColor, COLORREF darkColor) : IWTitleBarBtn(backgroundColor)
+{
+ this->lightColor = lightColor;
+ this->darkColor = darkColor;
+}
+
+void IWCdeIconifyBtn::DrawSymbol(CDC* pdc, CRect rect)
+{
+ CRect buttonFrame = rect;
+ buttonFrame.left = rect.right - rect.Height();
+
+ CRect icon = buttonFrame;
+ icon.left = buttonFrame.left + buttonFrame.Width() * 0.4;
+ icon.right = icon.right - buttonFrame.Width() * 0.4;
+ icon.top = buttonFrame.top + buttonFrame.Width() * 0.4;
+ icon.bottom = buttonFrame.bottom - buttonFrame.Width() * 0.4;
+
+ Draw3dRect(pdc, buttonFrame, 1, lightColor, darkColor);
+ Draw3dRect(pdc, icon, 1, lightColor, darkColor);
+}
\ No newline at end of file
diff --git a/ILS_Window_Plugin/IWCdeIconifyBtn.h b/ILS_Window_Plugin/IWCdeIconifyBtn.h
new file mode 100644
index 0000000..27ad3c0
--- /dev/null
+++ b/ILS_Window_Plugin/IWCdeIconifyBtn.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "IWTitleBarBtn.h"
+
+class IWCdeIconifyBtn : public IWTitleBarBtn
+{
+public:
+ IWCdeIconifyBtn(COLORREF backgroundColor, COLORREF lightColor, COLORREF darkColor);
+
+private:
+ void DrawSymbol(CDC* pDC, CRect rect) override;
+
+ COLORREF lightColor;
+ COLORREF darkColor;
+};
+
diff --git a/ILS_Window_Plugin/IWCdeMenuBtn.cpp b/ILS_Window_Plugin/IWCdeMenuBtn.cpp
new file mode 100644
index 0000000..1eb860d
--- /dev/null
+++ b/ILS_Window_Plugin/IWCdeMenuBtn.cpp
@@ -0,0 +1,21 @@
+#include "pch.h"
+#include "IWCdeMenuBtn.h"
+#include "RenderUtils.h"
+
+IWCdeMenuBtn::IWCdeMenuBtn(COLORREF backgroundColor, COLORREF lightColor, COLORREF darkColor) : IWTitleBarBtn(backgroundColor)
+{
+ this->lightColor = lightColor;
+ this->darkColor = darkColor;
+}
+
+void IWCdeMenuBtn::DrawSymbol(CDC* pdc, CRect rect)
+{
+ CRect barIcon = rect;
+ barIcon.left = rect.left + rect.Width() * 0.25;
+ barIcon.right = rect.right - rect.Width() * 0.25;
+ barIcon.top = rect.top + rect.Height() / 2 - 2;
+ barIcon.bottom = barIcon.top + 4;
+
+ Draw3dRect(pdc, rect, 1, lightColor, darkColor);
+ Draw3dRect(pdc, barIcon, 1, lightColor, darkColor);
+}
diff --git a/ILS_Window_Plugin/IWCdeMenuBtn.h b/ILS_Window_Plugin/IWCdeMenuBtn.h
new file mode 100644
index 0000000..7fd26fc
--- /dev/null
+++ b/ILS_Window_Plugin/IWCdeMenuBtn.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "IWTitleBarBtn.h"
+
+class IWCdeMenuBtn : public IWTitleBarBtn
+{
+public:
+ IWCdeMenuBtn(COLORREF backgroundColor, COLORREF lightColor, COLORREF darkColor);
+
+private:
+ void DrawSymbol(CDC* pDC, CRect rect) override;
+
+ COLORREF lightColor;
+ COLORREF darkColor;
+};
+
diff --git a/ILS_Window_Plugin/IWCdeTitleBar.cpp b/ILS_Window_Plugin/IWCdeTitleBar.cpp
new file mode 100644
index 0000000..376cb63
--- /dev/null
+++ b/ILS_Window_Plugin/IWCdeTitleBar.cpp
@@ -0,0 +1,44 @@
+#include "pch.h"
+#include "IWCdeTitleBar.h"
+#include "RenderUtils.h"
+#include "IWCdeIconifyBtn.h"
+#include "IWCdeMenuBtn.h"
+#include "IWX11ResizeBtn.h"
+
+IWCdeTitleBar::IWCdeTitleBar(COLORREF backgroundColor, COLORREF textColor, COLORREF lightColor, COLORREF darkcolor, IWTitleBarEventListener* listener) :
+ IWTitleBar(backgroundColor, 13, listener)
+{
+ this->lightColor = lightColor;
+ this->darkColor = darkcolor;
+ this->textColor = textColor;
+
+ this->iconifyButton = new IWCdeIconifyBtn(backgroundColor, lightColor, darkColor);
+ this->menuButton = new IWCdeMenuBtn(backgroundColor, lightColor, darkColor);
+ this->resizeButton = new IWX11ResizeBtn(backgroundColor);
+}
+
+void IWCdeTitleBar::PositionButtons(const CRect& rect)
+{
+ CRect menuButtonRect(rect.left, rect.top, rect.left + rect.Height(), rect.bottom);
+ if (menuButton->GetSafeHwnd())
+ menuButton->MoveWindow(menuButtonRect);
+
+ CRect iconifyButtonRect(rect.right - rect.Height(), rect.top, rect.right, rect.bottom);
+ if (iconifyButton->GetSafeHwnd())
+ iconifyButton->MoveWindow(iconifyButtonRect);
+
+ titleArea = rect;
+ titleArea.left = menuButtonRect.right;
+ titleArea.right = iconifyButtonRect.left;
+}
+
+void IWCdeTitleBar::DrawTitle(CDC* pdc, CRect rect, CString title)
+{
+ Draw3dRect(pdc, titleArea, 1, lightColor, darkColor);
+
+ auto oldFont = pdc->SelectObject(this->font);
+ pdc->SetTextColor(this->textColor);
+ pdc->SetBkMode(TRANSPARENT);
+ pdc->DrawText(_T(title), -1, titleArea, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
+ pdc->SelectObject(oldFont);
+}
diff --git a/ILS_Window_Plugin/IWCdeTitleBar.h b/ILS_Window_Plugin/IWCdeTitleBar.h
new file mode 100644
index 0000000..e02ddd5
--- /dev/null
+++ b/ILS_Window_Plugin/IWCdeTitleBar.h
@@ -0,0 +1,23 @@
+#pragma once
+#include
+#include
+#include "IWTitleBar.h"
+
+class IWTitleBarEventListener;
+
+class IWCdeTitleBar : public IWTitleBar {
+ public:
+ IWCdeTitleBar(COLORREF backgroundColor, COLORREF textColor, COLORREF lightColor, COLORREF darkcolor, IWTitleBarEventListener* listener);
+ virtual ~IWCdeTitleBar() {}
+
+ private:
+ void PositionButtons(const CRect& rect) override;
+ void DrawTitle(CDC* pdc, CRect rect, CString title) override;
+
+ COLORREF backgroundColor;
+ COLORREF textColor;
+ COLORREF lightColor;
+ COLORREF darkColor;
+
+ CRect titleArea;
+};
\ No newline at end of file
diff --git a/ILS_Window_Plugin/IWCdeWindow.cpp b/ILS_Window_Plugin/IWCdeWindow.cpp
new file mode 100644
index 0000000..703ad58
--- /dev/null
+++ b/ILS_Window_Plugin/IWCdeWindow.cpp
@@ -0,0 +1,147 @@
+#include "pch.h"
+#include "IWCdeWindow.h"
+#include "RenderUtils.h"
+#include "IWCdeTitleBar.h"
+
+IWCdeWindow::IWCdeWindow(IWApproachDefinition selectedApproach, IWStyling styling)
+ : IWWindow(selectedApproach, styling, 24, 5, 1)
+ , lightColor(AdjustColorBrightness(styling.windowFrameColor, 1.4))
+ , darkColor(AdjustColorBrightness(styling.windowFrameColor, 0.4))
+{
+ COLORREF textColor = RGB(styling.windowFrameTextColor.r, styling.windowFrameTextColor.g, styling.windowFrameTextColor.b);
+ this->titleBar = new IWCdeTitleBar(windowBorderColor, textColor, lightColor, darkColor, this);
+}
+
+int IWCdeWindow::GetEdgeCursorPosition(CPoint point)
+{
+ CRect clientRect;
+ GetClientRect(&clientRect);
+
+ bool topLeftCorner =
+ point.x < clientRect.left + TITLE_BAR_HEIGHT && point.y < clientRect.top + WINDOW_BORDER_THICKNESS ||
+ point.x < clientRect.left + WINDOW_BORDER_THICKNESS && point.y < clientRect.top + TITLE_BAR_HEIGHT;
+
+ bool topRightCorner =
+ point.x > clientRect.right - TITLE_BAR_HEIGHT && point.y < clientRect.top + WINDOW_BORDER_THICKNESS ||
+ point.x > clientRect.right - WINDOW_BORDER_THICKNESS && point.y < clientRect.top + TITLE_BAR_HEIGHT;
+
+ bool bottomLeftCorner =
+ point.x < clientRect.left + TITLE_BAR_HEIGHT && point.y > clientRect.bottom - WINDOW_BORDER_THICKNESS ||
+ point.x < clientRect.left + WINDOW_BORDER_THICKNESS && point.y > clientRect.bottom - TITLE_BAR_HEIGHT;
+
+ bool bottomRightCorner =
+ point.x > clientRect.right - TITLE_BAR_HEIGHT && point.y > clientRect.bottom - WINDOW_BORDER_THICKNESS ||
+ point.x > clientRect.right - WINDOW_BORDER_THICKNESS && point.y > clientRect.bottom - TITLE_BAR_HEIGHT;
+
+ bool topEdge = point.y < clientRect.top + WINDOW_BORDER_THICKNESS && point.y > clientRect.top;
+ bool bottomEdge = point.y > clientRect.bottom - WINDOW_BORDER_THICKNESS && point.y < clientRect.bottom;
+ bool leftEdge = point.x < clientRect.left + WINDOW_BORDER_THICKNESS && point.x > clientRect.left;
+ bool rightEdge = point.x > clientRect.right - WINDOW_BORDER_THICKNESS && point.x < clientRect.right;
+
+ if (topLeftCorner)
+ return HTTOPLEFT;
+
+ if (topRightCorner)
+ return HTTOPRIGHT;
+
+ if (bottomLeftCorner)
+ return HTBOTTOMLEFT;
+
+ if (bottomRightCorner)
+ return HTBOTTOMRIGHT;
+
+ if (topEdge)
+ return HTTOP;
+
+ if (leftEdge)
+ return HTLEFT;
+
+ if (rightEdge)
+ return HTRIGHT;
+
+ if (bottomEdge)
+ return HTBOTTOM;
+
+ return HTNOWHERE;
+}
+
+void IWCdeWindow::DrawBorder(CDC* pdc, CRect rect)
+{
+ int border3dSteps = 1; // Width of each 3D effect edge
+
+ // Edges
+
+ CRect leftBorderRect = rect;
+ leftBorderRect.top = leftBorderRect.top + TITLE_BAR_HEIGHT;
+ leftBorderRect.left = leftBorderRect.left + 1;
+ leftBorderRect.right = leftBorderRect.left + WINDOW_BORDER_THICKNESS - 1;
+ leftBorderRect.bottom = leftBorderRect.bottom - TITLE_BAR_HEIGHT;
+ Draw3dRect(pdc, leftBorderRect, border3dSteps, lightColor, darkColor);
+
+ CRect bottomBorderRect = rect;
+ bottomBorderRect.top = bottomBorderRect.bottom - WINDOW_BORDER_THICKNESS;
+ bottomBorderRect.left = bottomBorderRect.left + TITLE_BAR_HEIGHT;
+ bottomBorderRect.right = bottomBorderRect.right - TITLE_BAR_HEIGHT;
+ bottomBorderRect.bottom = bottomBorderRect.bottom - 1;
+ Draw3dRect(pdc, bottomBorderRect, border3dSteps, lightColor, darkColor);
+
+ CRect rightBorderRect = rect;
+ rightBorderRect.top = rightBorderRect.top + TITLE_BAR_HEIGHT;
+ rightBorderRect.left = rightBorderRect.right - WINDOW_BORDER_THICKNESS;
+ rightBorderRect.right = rightBorderRect.right - 1;
+ rightBorderRect.bottom = rightBorderRect.bottom - TITLE_BAR_HEIGHT;
+ Draw3dRect(pdc, rightBorderRect, border3dSteps, lightColor, darkColor);
+
+ CRect topBorderRect = rect;
+ topBorderRect.top = topBorderRect.top + 1;
+ topBorderRect.left = topBorderRect.left + TITLE_BAR_HEIGHT;
+ topBorderRect.right = topBorderRect.right - TITLE_BAR_HEIGHT;
+ topBorderRect.bottom = topBorderRect.top + WINDOW_BORDER_THICKNESS - 1;
+ Draw3dRect(pdc, topBorderRect, border3dSteps, lightColor, darkColor);
+
+ // Corners
+
+ CRect topLeftCornerRect = rect;
+ topLeftCornerRect.top = topLeftCornerRect.top + 1;
+ topLeftCornerRect.left = topLeftCornerRect.left + 1;
+ topLeftCornerRect.right = topLeftCornerRect.left + TITLE_BAR_HEIGHT - 2;
+ topLeftCornerRect.bottom = topLeftCornerRect.top + TITLE_BAR_HEIGHT - 2;
+ Draw3dCorner(pdc, topLeftCornerRect, WINDOW_BORDER_THICKNESS, border3dSteps, lightColor, darkColor, true, true);
+
+ CRect topRightCornerRect = rect;
+ topRightCornerRect.top = topRightCornerRect.top + 1;
+ topRightCornerRect.left = topRightCornerRect.right - TITLE_BAR_HEIGHT;
+ topRightCornerRect.right = topRightCornerRect.right - 2;
+ topRightCornerRect.bottom = topRightCornerRect.top + TITLE_BAR_HEIGHT - 2;
+ Draw3dCorner(pdc, topRightCornerRect, WINDOW_BORDER_THICKNESS, border3dSteps, lightColor, darkColor, true, false);
+
+ CRect bottomRightCornerRect = rect;
+ bottomRightCornerRect.top = bottomRightCornerRect.bottom - TITLE_BAR_HEIGHT;
+ bottomRightCornerRect.left = bottomRightCornerRect.right - TITLE_BAR_HEIGHT;
+ bottomRightCornerRect.right = bottomRightCornerRect.right - 2;
+ bottomRightCornerRect.bottom = bottomRightCornerRect.bottom - 2;
+ Draw3dCorner(pdc, bottomRightCornerRect, WINDOW_BORDER_THICKNESS, border3dSteps, lightColor, darkColor, false, false);
+
+ CRect bottomLeftCornerRect = rect;
+ bottomLeftCornerRect.top = bottomLeftCornerRect.bottom - TITLE_BAR_HEIGHT;
+ bottomLeftCornerRect.left = bottomLeftCornerRect.left + 1;
+ bottomLeftCornerRect.right = bottomLeftCornerRect.left + TITLE_BAR_HEIGHT - 2;
+ bottomLeftCornerRect.bottom = bottomLeftCornerRect.bottom - 2;
+ Draw3dCorner(pdc, bottomLeftCornerRect, WINDOW_BORDER_THICKNESS, border3dSteps, lightColor, darkColor, false, true);
+}
+
+COLORREF IWCdeWindow::AdjustColorBrightness(RGB color, double factor)
+{
+ // Adjust each component
+ int red = static_cast(color.r * factor);
+ int green = static_cast(color.g * factor);
+ int blue = static_cast(color.b * factor);
+
+ // Ensure the components are within the valid range
+ red = max(0, min(255, red));
+ green = max(0, min(255, green));
+ blue = max(0, min(255, blue));
+
+ // Combine them back into a COLORREF
+ return RGB(red, green, blue);
+}
diff --git a/ILS_Window_Plugin/IWCdeWindow.h b/ILS_Window_Plugin/IWCdeWindow.h
new file mode 100644
index 0000000..fb8b443
--- /dev/null
+++ b/ILS_Window_Plugin/IWCdeWindow.h
@@ -0,0 +1,17 @@
+#pragma once
+#include "IWWindow.h"
+class IWCdeWindow :
+ public IWWindow
+{
+
+public:
+ IWCdeWindow(IWApproachDefinition selectedApproach, IWStyling styling);
+
+private:
+ void DrawBorder(CDC* pdc, CRect rect) override;
+ COLORREF AdjustColorBrightness(RGB color, double factor);
+ virtual int GetEdgeCursorPosition(CPoint point) override;
+
+ const COLORREF lightColor;
+ const COLORREF darkColor;
+};
diff --git a/ILS_Window_Plugin/IWDataTypes.h b/ILS_Window_Plugin/IWDataTypes.h
index 3a322a6..80c8cc0 100644
--- a/ILS_Window_Plugin/IWDataTypes.h
+++ b/ILS_Window_Plugin/IWDataTypes.h
@@ -67,4 +67,5 @@ struct IWStyling {
struct IWBehaviourSettings {
bool openWindowsBasedOnActiveRunways;
+ std::string windowStyle;
};
\ No newline at end of file
diff --git a/ILS_Window_Plugin/IWMenuBtn.h b/ILS_Window_Plugin/IWMenuBtn.h
deleted file mode 100644
index e3e0abc..0000000
--- a/ILS_Window_Plugin/IWMenuBtn.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#pragma once
-#include "IWTitleBarBtn.h"
-
-class IWMenuBtn : public IWTitleBarBtn
-{
- void DrawSymbol(CDC* pDC, CRect rect) override;
-
- DECLARE_MESSAGE_MAP()
-
- afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
- afx_msg BOOL OnEraseBkgnd(CDC* pDC);
-};
-
diff --git a/ILS_Window_Plugin/IWPlugin.cpp b/ILS_Window_Plugin/IWPlugin.cpp
index 93a16f1..9de3e29 100644
--- a/ILS_Window_Plugin/IWPlugin.cpp
+++ b/ILS_Window_Plugin/IWPlugin.cpp
@@ -4,6 +4,8 @@
#include
#include "IWUtils.h"
#include
+#include "IWCdeWindow.h"
+#include "IWX11Window.h"
using json = nlohmann::json;
@@ -41,10 +43,27 @@ IWPlugin::~IWPlugin()
}
}
-void IWPlugin::OpenNewWindow(IWApproachDefinition* approach)
+void IWPlugin::ShowWindow(IWApproachDefinition* approach)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
+ // Check if a window with the same title is already open
+ IWWindow* windowWithSameTitle = nullptr;
+ for (const auto& window : windows) {
+ if (window->GetActiveApproachName() == approach->title) {
+ windowWithSameTitle = window;
+ break;
+ }
+ }
+
+ if (windowWithSameTitle) {
+ // Restore the window and bring it to the front
+ windowWithSameTitle->ShowWindow(SW_RESTORE);
+ windowWithSameTitle->SetForegroundWindow();
+ return;
+ }
+
+ // Calculate the spawning point for the new window
CPoint spawningPoint = CPoint(int(windows.size()) * 50, int(windows.size()) * 50 + 100);
CSize windowSize = CSize(300, 200);
@@ -64,10 +83,17 @@ void IWPlugin::OpenNewWindow(IWApproachDefinition* approach)
spawningPoint = CPoint(rect.left + 50, rect.top + 50);
}
}
+
+ IWWindow* newWindow = nullptr;
+ if (this->behaviourSettings.windowStyle == "X11") {
+ newWindow = new IWX11Window(*approach, windowStyling);
+ }
+ else {
+ newWindow = new IWCdeWindow(*approach, windowStyling);
+ }
- IWWindow* newWindow = new IWWindow(*approach, windowStyling);
auto hwndPopup = newWindow->CreateEx(
- WS_EX_NOACTIVATE | WS_EX_TOPMOST,
+ WS_EX_TOPMOST | WS_EX_APPWINDOW | WS_EX_NOACTIVATE,
WINDOW_CLASS_NAME,
_T(approach->title.c_str()),
WS_POPUP,
@@ -231,7 +257,7 @@ void IWPlugin::SyncWithActiveRunways()
});
if (!alreadyOpen) {
- this->OpenNewWindow(approach);
+ this->ShowWindow(approach);
}
}
}
@@ -376,8 +402,16 @@ IWBehaviourSettings IWPlugin::ReadBehaviourSettings(const std::string& jsonFileP
nlohmann::json jsonObject = jsonData["behaviour"];
+ auto readStringWithDefault = [&jsonData, this](const std::string& key, const std::string& defaultValue) -> std::string {
+ if (jsonData.contains("behaviour") && jsonData["behaviour"].contains(key)) {
+ return jsonData["behaviour"][key].get();
+ }
+ return defaultValue;
+ };
+
return IWBehaviourSettings{
- jsonObject.at("openWindowsBasedOnActiveRunways").get()
+ jsonObject.at("openWindowsBasedOnActiveRunways").get(),
+ readStringWithDefault("windowStyle", "X11")
};
}
@@ -404,10 +438,10 @@ void IWPlugin::OnWindowMenuOpenNew(std::string approachTitle)
});
if (selectedApproach != availableApproaches.end()) {
- OpenNewWindow(&(*selectedApproach));
+ ShowWindow(&(*selectedApproach));
}
else {
- OpenNewWindow(&availableApproaches[0]);
+ ShowWindow(&availableApproaches[0]);
}
}
@@ -471,7 +505,7 @@ bool IWPlugin::OnCompileCommand(const char* sCommandLine)
if (it != this->availableApproaches.end()) {
// Approach found: Open the approach window
- this->OpenNewWindow(&(*it));
+ this->ShowWindow(&(*it));
return true; // Command handled
}
else {
diff --git a/ILS_Window_Plugin/IWPlugin.h b/ILS_Window_Plugin/IWPlugin.h
index a8416ee..cc1eadd 100644
--- a/ILS_Window_Plugin/IWPlugin.h
+++ b/ILS_Window_Plugin/IWPlugin.h
@@ -24,7 +24,7 @@ class IWPlugin : public EuroScopePlugIn::CPlugIn, IIWWndEventListener {
std::map savedWindowPositions;
std::map airportTemperatures;
- void OpenNewWindow(IWApproachDefinition* approach);
+ void ShowWindow(IWApproachDefinition* approach);
void SyncWithActiveRunways();
void LoadSavedWindowPositions();
void ShowErrorMessage(std::string consequence, std::string details);
diff --git a/ILS_Window_Plugin/IWResizeBtn.h b/ILS_Window_Plugin/IWResizeBtn.h
deleted file mode 100644
index 67f624b..0000000
--- a/ILS_Window_Plugin/IWResizeBtn.h
+++ /dev/null
@@ -1,14 +0,0 @@
-#pragma once
-#include "IWTitleBarBtn.h"
-
-class IWResizeBtn : public IWTitleBarBtn
-{
- void DrawSymbol(CDC* pDC, CRect rect) override;
-
- afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
- afx_msg BOOL OnEraseBkgnd(CDC* pDC);
-
-
- DECLARE_MESSAGE_MAP()
-};
-
diff --git a/ILS_Window_Plugin/IWTitleBar.cpp b/ILS_Window_Plugin/IWTitleBar.cpp
index 9192654..1afd40d 100644
--- a/ILS_Window_Plugin/IWTitleBar.cpp
+++ b/ILS_Window_Plugin/IWTitleBar.cpp
@@ -1,23 +1,24 @@
#include "pch.h"
#include "IWTitleBar.h"
+#include "IWTitleBarBtn.h"
IMPLEMENT_DYNAMIC(IWTitleBar, CStatic)
BEGIN_MESSAGE_MAP(IWTitleBar, CStatic)
ON_WM_PAINT()
ON_WM_SIZE() // Handle resizing
- ON_BN_CLICKED(IDC_CLOSE_BUTTON, &IWTitleBar::OnCloseButtonClicked)
ON_WM_LBUTTONDOWN()
+ ON_BN_CLICKED(IDC_CLOSE_BUTTON, &IWTitleBar::OnIconifyButtonClicked)
+ ON_BN_CLICKED(IDC_MENU_BUTTON, &IWTitleBar::OnMenuButtonClicked)
+ ON_COMMAND(IDC_RESIZE_BUTTON, &IWTitleBar::OnResizeButtonPressed)
END_MESSAGE_MAP()
-IWTitleBar::IWTitleBar(std::string title, COLORREF backgroundColor, COLORREF textColor, COLORREF outerFrameColor, IWTitleBarEventListener* listener)
+IWTitleBar::IWTitleBar(COLORREF backgroundColor, int fontSize, IWTitleBarEventListener* listener)
{
this->backgroundColor = backgroundColor;
- this->textColor = textColor;
- this->outerFramePen.CreatePen(PS_SOLID, 1, outerFrameColor);
- this->text = title;
- this->euroScopeFont.CreatePointFont(110, _T("EuroScope"));
+ float fontPointsSize = fontSize * 72 / 96;
+ this->font.CreatePointFont(int(fontPointsSize * 10), _T("EuroScope"));
this->eventListener = listener;
}
@@ -27,123 +28,74 @@ BOOL IWTitleBar::CreateTopBar(CWnd* pParentWnd, const CRect& rect, UINT nID)
return FALSE;
// Create buttons with default settings
- if (!resizeButton.Create(_T(""), WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, CRect(), this, IDC_RESIZE_BUTTON) ||
- !menuButton.Create(_T(""), WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, CRect(), this, IDC_MENU_BUTTON) ||
- !closeButton.Create(_T(""), WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, CRect(), this, IDC_CLOSE_BUTTON))
+ if (!resizeButton->Create(_T(""), WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, CRect(), this, IDC_RESIZE_BUTTON) ||
+ !menuButton->Create(_T(""), WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, CRect(), this, IDC_MENU_BUTTON) ||
+ !iconifyButton->Create(_T(""), WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, CRect(), this, IDC_CLOSE_BUTTON))
return FALSE;
+ resizeButton->SetButtonID(IDC_RESIZE_BUTTON);
+
// Position buttons
PositionButtons(rect);
return TRUE;
}
-void IWTitleBar::OnPaint()
+void IWTitleBar::OnSize(UINT nType, int cx, int cy)
{
- CPaintDC dc(this); // Device context for painting
- CRect rect;
- GetClientRect(&rect); // Get the client area of the control
-
- auto oldFont = dc.SelectObject(this->euroScopeFont);
-
- // Fill the background with your custom color
- dc.FillSolidRect(rect, this->backgroundColor); // Dark background
-
- // Set the text color to white
- dc.SetTextColor(this->textColor);
- dc.SetBkMode(TRANSPARENT); // Transparent background for text
-
- // Draw the text centered in the client area
- CRect textPosition = rect;
- textPosition.left += 10;
-
- dc.DrawText(_T(this->text.c_str()), -1, textPosition, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
-
- dc.SelectObject(oldFont);
+ CStatic::OnSize(nType, cx, cy);
- dc.SelectStockObject(NULL_BRUSH);
- dc.SelectObject(this->outerFramePen);
- rect.bottom += 1; // Make the bottom border invisible
- dc.Rectangle(rect);
+ if (iconifyButton->GetSafeHwnd() && menuButton->GetSafeHwnd() && resizeButton->GetSafeHwnd())
+ {
+ CRect rect;
+ GetClientRect(&rect); // Get the updated size of the title bar
+ PositionButtons(rect);
+ }
}
-void IWTitleBar::OnCloseButtonClicked()
+void IWTitleBar::OnPaint()
{
- this->eventListener->OnCloseButtonClicked();
-}
+ CPaintDC dc(this);
+ CRect rect;
+ GetClientRect(&rect);
+ // Get parent window
+ CString title;
+ GetParent()->GetWindowText(title);
+ DrawTitle(&dc, rect, title);
+}
void IWTitleBar::OnLButtonDown(UINT nFlags, CPoint point)
{
- CRect resizeButtonRect;
- resizeButton.GetClientRect(&resizeButtonRect);
- resizeButton.ClientToScreen(&resizeButtonRect);
+ CWnd* pParent = GetParent();
+ if (!pParent)
+ return;
- CRect menuButtonRect;
- menuButton.GetClientRect(&menuButtonRect);
- menuButton.ClientToScreen(&menuButtonRect);
+ CRect resizeButtonRect;
+ resizeButton->GetClientRect(&resizeButtonRect);
+ resizeButton->ClientToScreen(&resizeButtonRect);
- if (resizeButtonRect.PtInRect(point)) // If click is not on the close button
- {
+ if (resizeButtonRect.PtInRect(point)) {
this->eventListener->OnResizeStart();
}
- else if (menuButtonRect.PtInRect(point))
- {
- this->eventListener->OnMenuButtonClicked();
- }
else {
- // Simulate dragging the window
- CWnd* pParent = GetParent();
- if (pParent)
- {
- pParent->SendMessage(WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(point.x, point.y));
- }
+ pParent->SendMessage(WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(point.x, point.y));
}
CWnd::OnLButtonDown(nFlags, point);
}
-void IWTitleBar::OnSize(UINT nType, int cx, int cy)
+void IWTitleBar::OnIconifyButtonClicked()
{
- CStatic::OnSize(nType, cx, cy);
+ this->eventListener->OnIconifyButtonClicked();
+}
- if (closeButton.GetSafeHwnd() && menuButton.GetSafeHwnd() && resizeButton.GetSafeHwnd())
- {
- CRect rect;
- GetClientRect(&rect); // Get the updated size of the title bar
- PositionButtons(rect);
- }
+void IWTitleBar::OnMenuButtonClicked()
+{
+ this->eventListener->OnMenuButtonClicked();
}
-void IWTitleBar::PositionButtons(const CRect& rect)
+void IWTitleBar::OnResizeButtonPressed()
{
- const int margin = 6;
- const int btnWidth = 16; // Button width
- const int btnHeight = 14; // Button width
- const int top = rect.top + 8;
- const int bottom = top + btnHeight;
-
- int right = rect.right - margin - 3;
- int left = right - btnWidth;
-
- // Position the resize button
- CRect resizeButtonRect(left, top, right, bottom);
- if (resizeButton.GetSafeHwnd())
- resizeButton.MoveWindow(resizeButtonRect);
-
- right = left - margin;
- left = right - btnWidth;
-
- // Position the menu button
- CRect menuButtonRect(left, top, right, bottom);
- if (menuButton.GetSafeHwnd())
- menuButton.MoveWindow(menuButtonRect);
-
- right = left - margin;
- left = right - btnWidth;
-
- // Position the close button
- CRect closeButtonRect(left, top, right, bottom);
- if (closeButton.GetSafeHwnd())
- closeButton.MoveWindow(closeButtonRect);
+ this->eventListener->OnResizeStart();
}
diff --git a/ILS_Window_Plugin/IWTitleBar.h b/ILS_Window_Plugin/IWTitleBar.h
index 935ae04..f082a03 100644
--- a/ILS_Window_Plugin/IWTitleBar.h
+++ b/ILS_Window_Plugin/IWTitleBar.h
@@ -1,9 +1,6 @@
#pragma once
#include
#include
-#include "IWCloseBtn.h"
-#include "IWMenuBtn.h"
-#include "IWResizeBtn.h"
#define IDC_CLOSE_BUTTON 1001
#define IDC_MENU_BUTTON 1002
@@ -12,46 +9,42 @@
class IWTitleBarEventListener {
public:
virtual void OnResizeStart() = 0;
- virtual void OnCloseButtonClicked() = 0;
+ virtual void OnIconifyButtonClicked() = 0;
virtual void OnMenuButtonClicked() = 0;
};
-class IWTitleBar : public CStatic {
+class IWTitleBarBtn;
+
+class IWTitleBar : public CStatic
+{
DECLARE_DYNAMIC(IWTitleBar)
+ DECLARE_MESSAGE_MAP()
+
+public:
+ IWTitleBar(COLORREF backgroundColor, int fontSize, IWTitleBarEventListener* listener);
+ BOOL CreateTopBar(CWnd* pParentWnd, const CRect& rect, UINT nID);
+
+ virtual ~IWTitleBar() {}
+
+protected:
+ virtual void PositionButtons(const CRect& rect) {};
+ virtual void DrawTitle(CDC* pdc, CRect rect, CString title) {};
+
+ IWTitleBarBtn* menuButton;
+ IWTitleBarBtn* iconifyButton;
+ IWTitleBarBtn* resizeButton;
+
+ CFont font;
+
+private:
+ COLORREF backgroundColor;
+ IWTitleBarEventListener* eventListener;
+
+ afx_msg void OnPaint();
+ afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
+ afx_msg void OnIconifyButtonClicked();
+ afx_msg void OnMenuButtonClicked();
+ afx_msg void OnResizeButtonPressed();
+ afx_msg void OnSize(UINT nType, int cx, int cy);
+};
- public:
- IWTitleBar(
- std::string title,
- COLORREF backgroundColor,
- COLORREF textColor,
- COLORREF outerFrameColor,
- IWTitleBarEventListener* listener
- );
- virtual ~IWTitleBar() {}
-
- // Initialize the top bar
- BOOL CreateTopBar(CWnd* pParentWnd, const CRect& rect, UINT nID);
-
- void SetTitle(const std::string& title) { this->text = title; }
-
- private:
- IWCloseBtn closeButton;
- IWMenuBtn menuButton;
- IWResizeBtn resizeButton;
- CFont euroScopeFont;
-
- COLORREF backgroundColor;
- COLORREF textColor;
- CPen outerFramePen;
- std::string text;
- IWTitleBarEventListener* eventListener;
-
- afx_msg void OnPaint();
- afx_msg void OnCloseButtonClicked();
- afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
- afx_msg void OnSize(UINT nType, int cx, int cy);
-
- void PositionButtons(const CRect& rect);
-
- DECLARE_MESSAGE_MAP()
-};
\ No newline at end of file
diff --git a/ILS_Window_Plugin/IWTitleBarBtn.cpp b/ILS_Window_Plugin/IWTitleBarBtn.cpp
index 1d1bbb0..efac844 100644
--- a/ILS_Window_Plugin/IWTitleBarBtn.cpp
+++ b/ILS_Window_Plugin/IWTitleBarBtn.cpp
@@ -1,11 +1,22 @@
#include "pch.h"
#include "IWTitleBarBtn.h"
+#include "RenderUtils.h"
BEGIN_MESSAGE_MAP(IWTitleBarBtn, CButton)
ON_WM_ERASEBKGND()
+ ON_WM_MOUSEMOVE()
+ ON_WM_MOUSELEAVE()
+ ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()
+IWTitleBarBtn::IWTitleBarBtn(COLORREF backgroundColor)
+{
+ this->backgroundColor = backgroundColor;
+ this->backgroundColorHover = backgroundColor; // TODO
+ buttonID = -1; // Set later by the parent if needed
+}
+
BOOL IWTitleBarBtn::OnEraseBkgnd(CDC* pDC) {
// Do nothing to keep the background transparent
return TRUE;
@@ -17,7 +28,15 @@ void IWTitleBarBtn::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) {
// Get the button's rectangle
CRect rect = lpDrawItemStruct->rcItem;
- // Draw border only (hollow interior)
+ // If the button is hovered, change the background color
+ if (mouseOver) {
+ pDC->FillSolidRect(&rect, this->backgroundColorHover); // Light blue background on hover
+ }
+ else {
+ pDC->FillSolidRect(&rect, this->backgroundColor); // White background
+ }
+
+ // Draw border (optional: use a different color on hover)
CPen pen(PS_SOLID, 1, RGB(0, 0, 0)); // Black pen for the border
CPen* oldPen = pDC->SelectObject(&pen);
pDC->SelectStockObject(HOLLOW_BRUSH); // No fill for the rectangle
@@ -27,3 +46,47 @@ void IWTitleBarBtn::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) {
// Call DrawSymbol to draw the custom symbol inside
DrawSymbol(pDC, rect);
}
+
+void IWTitleBarBtn::SetButtonID(int id)
+{
+ buttonID = id;
+}
+
+void IWTitleBarBtn::OnMouseMove(UINT nFlags, CPoint point) {
+ CRect rect;
+ GetClientRect(&rect);
+ if (!mouseOver && rect.PtInRect(point)) {
+ mouseOver = TRUE;
+ Invalidate();
+
+ TRACKMOUSEEVENT tme;
+ tme.cbSize = sizeof(TRACKMOUSEEVENT);
+ tme.dwFlags = TME_LEAVE;
+ tme.hwndTrack = GetSafeHwnd();
+ _TrackMouseEvent(&tme);
+ }
+
+ CButton::OnMouseMove(nFlags, point);
+}
+
+void IWTitleBarBtn::OnMouseLeave() {
+ if (mouseOver) {
+ mouseOver = FALSE;
+ Invalidate();
+ }
+ CButton::OnMouseLeave();
+}
+
+void IWTitleBarBtn::OnLButtonDown(UINT nFlags, CPoint point)
+{
+ mouseOver = TRUE;
+ Invalidate();
+
+ CWnd* parent = GetParent();
+ if (buttonID != -1 && parent) {
+ parent->SendMessage(WM_COMMAND, this->buttonID);
+ }
+ else {
+ CButton::OnLButtonDown(nFlags, point);
+ }
+}
diff --git a/ILS_Window_Plugin/IWTitleBarBtn.h b/ILS_Window_Plugin/IWTitleBarBtn.h
index 0827ab8..818541c 100644
--- a/ILS_Window_Plugin/IWTitleBarBtn.h
+++ b/ILS_Window_Plugin/IWTitleBarBtn.h
@@ -3,14 +3,27 @@
class IWTitleBarBtn : public CButton {
+DECLARE_MESSAGE_MAP()
+
+
public:
+ IWTitleBarBtn(COLORREF backgroundColor);
+ void SetButtonID(int id);
+
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
- virtual BOOL IWTitleBarBtn::OnEraseBkgnd(CDC* pDC);
protected:
- DECLARE_MESSAGE_MAP()
-
virtual void DrawSymbol(CDC* pDC, CRect rect) = 0;
+private:
+ BOOL mouseOver = FALSE;
+ int buttonID = -1;
+ COLORREF backgroundColor;
+ COLORREF backgroundColorHover;
+
+ afx_msg BOOL OnEraseBkgnd(CDC* pDC);
+ afx_msg void OnMouseMove(UINT nFlags, CPoint point);
+ afx_msg void OnMouseLeave();
+ afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
};
diff --git a/ILS_Window_Plugin/IWVisualization.cpp b/ILS_Window_Plugin/IWVisualization.cpp
new file mode 100644
index 0000000..4c5ebbb
--- /dev/null
+++ b/ILS_Window_Plugin/IWVisualization.cpp
@@ -0,0 +1,456 @@
+#include "pch.h"
+#include "IWVisualization.h"
+#include
+
+BEGIN_MESSAGE_MAP(IWVisualization, CWnd)
+ ON_WM_PAINT()
+ ON_WM_SIZE()
+ ON_WM_MOVE()
+ ON_WM_LBUTTONDOWN()
+ ON_WM_ERASEBKGND()
+ ON_WM_MOUSEWHEEL()
+ ON_WM_TIMER()
+END_MESSAGE_MAP()
+
+IWVisualization::IWVisualization(IWApproachDefinition selectedApproach, IWStyling styling, CFont* font)
+{
+ this->selectedApproach = selectedApproach;
+ this->approachLength = selectedApproach.defaultRange;
+
+ this->leftToRight = selectedApproach.localizerCourse > 0 && selectedApproach.localizerCourse < 180;
+ this->showTagsByDefault = styling.showTagByDefault;
+ this->applyTemperatureCorrection = true;
+ this->tagMode = styling.defaultTagMode;
+
+ this->rangeStatusTextColor = RGB(styling.rangeStatusTextColor.r, styling.rangeStatusTextColor.g, styling.rangeStatusTextColor.b);
+ this->windowBackground = RGB(styling.backgroundColor.r, styling.backgroundColor.g, styling.backgroundColor.b);
+ this->targetLabelColor = RGB(styling.targetLabelColor.r, styling.targetLabelColor.g, styling.targetLabelColor.b);
+ this->glideSlopePen.CreatePen(PS_SOLID, 1, RGB(styling.glideslopeColor.r, styling.glideslopeColor.g, styling.glideslopeColor.b));
+ this->localizerBrush.CreateSolidBrush(RGB(styling.localizerColor.r, styling.localizerColor.g, styling.localizerColor.b));
+ this->radarTargetPen.CreatePen(PS_SOLID, 1, RGB(styling.radarTargetColor.r, styling.radarTargetColor.g, styling.radarTargetColor.b));
+ this->historyTrailPen.CreatePen(PS_SOLID, 1, RGB(styling.historyTrailColor.r, styling.historyTrailColor.g, styling.historyTrailColor.b));
+
+ this->font = font;
+}
+
+
+void IWVisualization::OnPaint()
+{
+ CPaintDC dc(this); // Device context for painting
+
+ // Create a memory DC for double buffering
+ CRect rect;
+ GetClientRect(&rect);
+ CDC memDC;
+ memDC.CreateCompatibleDC(&dc);
+
+ CBitmap bufferBitmap;
+ bufferBitmap.CreateCompatibleBitmap(&dc, rect.Width(), rect.Height());
+ CBitmap* pOldBitmap = memDC.SelectObject(&bufferBitmap);
+
+ // Clear the background
+ memDC.FillSolidRect(rect, windowBackground);
+
+ // Select font
+ CFont* oldFont = memDC.SelectObject(font);
+
+ // Perform all drawing operations on memDC instead of dc
+ DrawGlideslopeAndLocalizer(memDC);
+ DrawRadarTargets(memDC);
+ if (showCurrentZoomValue) {
+ DrawCurrentZoomValue(memDC);
+ }
+
+ // Restore the font
+ memDC.SelectObject(oldFont);
+
+ // Copy the buffer to the screen
+ dc.BitBlt(0, 0, rect.Width(), rect.Height(), &memDC, 0, 0, SRCCOPY);
+
+ // Cleanup
+ memDC.SelectObject(pOldBitmap);
+}
+
+void IWVisualization::DrawGlideslopeAndLocalizer(CDC& dc)
+{
+ // Draw glideslope line
+ CPen* pOldPen = dc.SelectObject(&glideSlopePen);
+ dc.MoveTo(glidePathTop);
+ dc.LineTo(glidePathBottom);
+ dc.SelectObject(pOldPen);
+
+ // Draw localizer distance dots
+ CBrush* pOldBrush = dc.SelectObject(&localizerBrush);
+ for (int i = 0; i <= approachLength; i++) // Draw distance points. Every 5th point is large
+ {
+ int radius = i % 5 == 0 ? 4 : 2;
+ int x = glidePathBottom.x - i * pixelsPerNauticalMile;
+ int y = glidePathBottom.y;
+
+ DrawDiamond(CPoint(x, y), radius, dc);
+ }
+ dc.SelectObject(pOldBrush);
+}
+
+void IWVisualization::DrawRadarTargets(CDC& dc)
+{
+ if (m_latestLiveData == NULL) return;
+
+ // Draw the radar targets
+ for (const IWRadarTarget& radarTarget : m_latestLiveData->radarTargets)
+ {
+ auto it = radarTarget.positionHistory.rbegin();
+ auto end = radarTarget.positionHistory.rend();
+
+ // Draw the history trail in reverese so the newest dots appear on top
+ for (auto it = radarTarget.positionHistory.rbegin(); it != radarTarget.positionHistory.rend(); ++it) {
+ const IWTargetPosition& position = *it;
+ bool isNewestPosition = (it + 1 == end); // Check if the iterator is the last element
+
+ CPoint ptTopView, ptSideView;
+ if (!CalculateTargetCoordinates(position, ptTopView, ptSideView)) {
+ // Target is outside the visible area
+ continue;
+ }
+
+ int crossRadius = isNewestPosition ? TARGET_RADIUS : HISTORY_TRAIL_RADIUS;
+
+ // Draw position seen from the side
+ dc.SelectObject(isNewestPosition ? radarTargetPen : historyTrailPen);
+ dc.MoveTo(CPoint(ptSideView.x, ptSideView.y - crossRadius));
+ dc.LineTo(CPoint(ptSideView.x, ptSideView.y + crossRadius + 1));
+ dc.MoveTo(CPoint(ptSideView.x - crossRadius, ptSideView.y));
+ dc.LineTo(CPoint(ptSideView.x + crossRadius + 1, ptSideView.y));
+
+ if (isNewestPosition) {
+ dc.SelectStockObject(NULL_BRUSH);
+ dc.Ellipse(ptSideView.x - crossRadius, ptSideView.y - crossRadius, ptSideView.x + crossRadius + 1, ptSideView.y + crossRadius + 1);
+ }
+
+ // Draw position seen from above
+ dc.SelectObject(isNewestPosition ? radarTargetPen : historyTrailPen);
+ dc.MoveTo(CPoint(ptTopView.x, ptTopView.y - crossRadius));
+ dc.LineTo(CPoint(ptTopView.x, ptTopView.y + crossRadius + 1));
+ dc.MoveTo(CPoint(ptTopView.x - crossRadius, ptTopView.y));
+ dc.LineTo(CPoint(ptTopView.x + crossRadius + 1, ptTopView.y));
+
+ // Draw the main target
+ if (isNewestPosition) {
+ dc.SelectStockObject(NULL_BRUSH);
+ dc.Ellipse(ptTopView.x - crossRadius, ptTopView.y - crossRadius, ptTopView.x + crossRadius + 1, ptTopView.y + crossRadius + 1);
+
+ // Check if callsign is in the vector of clicked targets
+ bool isClicked = this->clickedTargets.find(radarTarget.callsign) != this->clickedTargets.end();
+
+ if (this->showTagsByDefault) {
+ if (isClicked) continue;
+ }
+ else {
+ if (!isClicked) continue;
+ }
+
+ dc.MoveTo(ptTopView);
+ dc.LineTo(ptTopView.x + LABEL_OFFSET, ptTopView.y + LABEL_OFFSET);
+
+ // Callsign label
+ dc.SetTextColor(targetLabelColor);
+ dc.SetBkMode(TRANSPARENT);
+
+
+ CString targetLabel;
+ if (this->tagMode == IWTagMode::Squawk) {
+ targetLabel.Format(_T("%s"), radarTarget.squawk.c_str());
+ }
+ else if (this->tagMode == IWTagMode::Callsign) {
+ targetLabel.Format(_T("%s"), radarTarget.callsign.c_str());
+ }
+
+ // Draw the label
+ CSize textSize = dc.GetTextExtent(targetLabel);
+ CPoint labelPosition(ptTopView.x + LABEL_OFFSET, ptTopView.y + LABEL_OFFSET);
+ CRect labelRect(labelPosition.x, labelPosition.y, labelPosition.x + textSize.cx, labelPosition.y + textSize.cy * 2);
+ dc.DrawText(targetLabel, labelRect, DT_LEFT);
+ }
+ }
+ }
+}
+
+void IWVisualization::DrawCurrentZoomValue(CDC& dc)
+{
+ CRect rect;
+ GetClientRect(&rect);
+
+ dc.SetTextColor(this->rangeStatusTextColor);
+ dc.SetBkMode(TRANSPARENT);
+
+ CString zoomMessage;
+ zoomMessage.Format(_T("%d NM"), this->approachLength);
+
+ CSize textSize = dc.GetTextExtent(zoomMessage);
+ CPoint position(
+ this->leftToRight ? rect.right - textSize.cx - 5 : rect.left + 5,
+ rect.top + 5
+ );
+ CRect textRect(position.x, position.y, position.x + textSize.cx, position.y + textSize.cy * 2);
+
+ dc.DrawText(zoomMessage, textRect, this->leftToRight ? DT_RIGHT : DT_LEFT);
+}
+
+void IWVisualization::SetActiveApproach(IWApproachDefinition selectedApproach)
+{
+ this->selectedApproach = selectedApproach;
+ this->approachLength = selectedApproach.defaultRange;
+ this->leftToRight = selectedApproach.localizerCourse > 0 && selectedApproach.localizerCourse < 180;
+
+ UpdateDimentions();
+ Invalidate();
+}
+
+void IWVisualization::SetLatestLiveData(IWLiveData* liveData)
+{
+ this->m_latestLiveData = liveData;
+ Invalidate();
+}
+
+void IWVisualization::DrawDiamond(CPoint pt, int radius, CDC& dc)
+{
+ CPoint pts[5];
+ pts[0] = CPoint(pt.x, pt.y - radius);
+ pts[1] = CPoint(pt.x + radius, pt.y);
+ pts[2] = CPoint(pt.x, pt.y + radius);
+ pts[3] = CPoint(pt.x - radius, pt.y);
+ pts[4] = CPoint(pt.x, pt.y - radius);
+
+ dc.SetPolyFillMode(ALTERNATE);
+ dc.Polygon(pts, 5);
+}
+
+BOOL IWVisualization::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
+{
+ if (zDelta > 0 && this->approachLength > 5)
+ {
+ this->approachLength -= 1;
+ }
+ else if (zDelta < 0 && this->approachLength < 50)
+ {
+ this->approachLength += 1;
+ }
+
+ UpdateDimentions();
+ Invalidate();
+
+ SetTimer(zoomMessageTimerId, 1000, nullptr);
+
+ showCurrentZoomValue = true;
+
+ return TRUE;
+}
+
+void IWVisualization::OnSize(UINT nType, int cx, int cy)
+{
+ CWnd::OnSize(nType, cx, cy);
+
+ if (nType != SIZE_MINIMIZED)
+ {
+ UpdateDimentions();
+ Invalidate();
+ }
+}
+
+bool IWVisualization::CalculateTargetCoordinates(const IWTargetPosition& position, CPoint& ptTopView, CPoint& ptSideView)
+{
+ // Calculate position relative to the runway
+ double distanceToThreshold = CalculateDistance(position.latitude, position.longitude, selectedApproach.thresholdLatitude, selectedApproach.thresholdLongitude);
+ double directionToThreshold = CalculateBearing(position.latitude, position.longitude, selectedApproach.thresholdLatitude, selectedApproach.thresholdLongitude);
+ double angleDiff = (selectedApproach.localizerCourse - directionToThreshold) / 180.0 * PI; // anglediff in radians
+ double heightAboveThreshold = position.trueAltitude - selectedApproach.thresholdAltitude;
+
+ auto airportTemperature = m_latestLiveData->airportTemperatures.find(selectedApproach.airport);
+ if (applyTemperatureCorrection && airportTemperature != m_latestLiveData->airportTemperatures.end()) {
+ // In newer flight simulators true altitude is affected by the temperature.
+ // In cold weather aircraft will be shown higher than they actually are, unless we correct for it.
+ // See: https://forums.flightsimulator.com/t/vatsim-ivao-pilotedge-users-be-aware-of-an-important-bug/426142/468
+ int temperatureCorrection = CalculateTemperatureCorrection(position.trueAltitude, selectedApproach.thresholdAltitude, airportTemperature->second);
+ heightAboveThreshold -= temperatureCorrection;
+ }
+
+ double projectedDistanceFromThreshold = distanceToThreshold * cos(angleDiff);
+ double projectedDistanceFromExtendedCenterline = distanceToThreshold * tan(angleDiff);
+
+ if (projectedDistanceFromExtendedCenterline < 0 && abs(projectedDistanceFromExtendedCenterline) > selectedApproach.maxOffsetLeft) {
+ // Too far left
+ return false;
+ }
+ if (projectedDistanceFromExtendedCenterline > 0 && abs(projectedDistanceFromExtendedCenterline) > selectedApproach.maxOffsetRight) {
+ // Too far right
+ return false;
+ }
+ if (projectedDistanceFromThreshold < 0) {
+ // Wrong side of the threshold
+ return false;
+ }
+ if (heightAboveThreshold > approachHeightFt) {
+ // Too high
+ return false;
+ }
+
+ int xPosition = glidePathBottom.x - projectedDistanceFromThreshold * pixelsPerNauticalMile;
+ int yPositionSlope = glidePathBottom.y - heightAboveThreshold * pixelsPerFt;
+ int yPositionCenterline = glidePathBottom.y + projectedDistanceFromExtendedCenterline * pixelsPerNauticalMile;
+ ptSideView = CPoint(xPosition, yPositionSlope);
+ ptTopView = CPoint(xPosition, yPositionCenterline);
+
+ return true;
+}
+
+void IWVisualization::UpdateDimentions()
+{
+ CRect rect;
+ GetClientRect(&rect);
+
+ const int CALC_SIDE_MARGIN = rect.Width() * APP_LINE_MARGIN_SIDES;
+ const int CALC_TOP_MARGIN = rect.Height() * APP_LINE_MARGIN_TOP;
+ const int CALC_BOTTOM_MARGIN = rect.Height() * APP_LINE_MARGIN_BOTTOM;
+
+ // Define the start and end coordintes for rendering the glidepath
+ this->glidePathTop = CPoint(leftToRight ? rect.left + CALC_SIDE_MARGIN : rect.right - CALC_SIDE_MARGIN, rect.top + CALC_TOP_MARGIN);
+ this->glidePathBottom = CPoint(leftToRight ? rect.right - CALC_SIDE_MARGIN : rect.left + CALC_SIDE_MARGIN, rect.bottom - CALC_BOTTOM_MARGIN);
+
+ this->approachHeightFt = (approachLength * FT_PER_NM * sin(selectedApproach.glideslopeAngle / 180.0 * PI));
+
+ this->pixelsPerFt = (glidePathBottom.y - glidePathTop.y) / approachHeightFt;
+ this->pixelsPerNauticalMile = (glidePathBottom.x - glidePathTop.x) / float(approachLength); // PS: negative when direction is left->right
+}
+
+void IWVisualization::OnTimer(UINT_PTR nIDEvent)
+{
+ if (nIDEvent == zoomMessageTimerId)
+ {
+ // Hide the zoom message
+ showCurrentZoomValue = false;
+ KillTimer(zoomMessageTimerId);
+
+ // Invalidate to redraw without the message
+ CRect updateRect;
+ GetClientRect(&updateRect);
+ InvalidateRect(updateRect);
+ }
+
+ CWnd::OnTimer(nIDEvent);
+}
+
+void IWVisualization::OnLButtonDown(UINT nFlags, CPoint point)
+{
+ for (const IWRadarTarget& radarTarget : m_latestLiveData->radarTargets) {
+ auto newestPosition = radarTarget.positionHistory.size() > 0 ? &radarTarget.positionHistory.front() : nullptr;
+
+ if (newestPosition) {
+ const IWTargetPosition& position = *newestPosition;
+
+ CPoint ptTopView, ptSideView;
+ if (!CalculateTargetCoordinates(position, ptTopView, ptSideView)) {
+ // Target is outside the visible area
+ continue;
+ }
+
+ CRect targetRect(ptTopView.x - TARGET_RADIUS, ptTopView.y - TARGET_RADIUS, ptTopView.x + TARGET_RADIUS, ptTopView.y + TARGET_RADIUS);
+
+ if (targetRect.PtInRect(point))
+ {
+ bool isAlreadyClicked = this->clickedTargets.find(radarTarget.callsign) != this->clickedTargets.end();
+
+ if (isAlreadyClicked) {
+ this->clickedTargets.erase(radarTarget.callsign);
+ }
+ else {
+ this->clickedTargets.insert(radarTarget.callsign);
+ }
+ CRect updateRect;
+ GetClientRect(&updateRect);
+ InvalidateRect(updateRect);
+ break;
+ }
+ }
+ }
+
+ CWnd::OnLButtonDown(nFlags, point);
+}
+
+double IWVisualization::CalculateDistance(double lat1, double lon1, double lat2, double lon2) {
+ // Convert latitude and longitude from degrees to radians
+ lat1 = lat1 * PI / 180.0;
+ lon1 = lon1 * PI / 180.0;
+ lat2 = lat2 * PI / 180.0;
+ lon2 = lon2 * PI / 180.0;
+ // Haversine formula
+ double dlat = lat2 - lat1;
+ double dlon = lon2 - lon1;
+ double a = pow(sin(dlat / 2), 2) + cos(lat1) * cos(lat2) * pow(sin(dlon / 2), 2);
+ double c = 2 * atan2(sqrt(a), sqrt(1 - a));
+ double distance = EARTH_RADIUS_NM * c;
+ return distance;
+}
+
+double IWVisualization::CalculateBearing(double lat1, double lon1, double lat2, double lon2) {
+ // Convert latitude and longitude from degrees to radians
+ lat1 = lat1 * PI / 180.0;
+ lon1 = lon1 * PI / 180.0;
+ lat2 = lat2 * PI / 180.0;
+ lon2 = lon2 * PI / 180.0;
+
+ // Calculate the bearing
+ double y = sin(lon2 - lon1) * cos(lat2);
+ double x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(lon2 - lon1);
+ double bearing = atan2(y, x);
+ bearing = fmod(bearing + 2 * PI, 2 * PI);
+
+ // Convert to degrees
+ return bearing * 180.0 / PI;
+}
+
+double IWVisualization::CalculateTemperatureCorrection(int planePressAlt, int airportPressureAlt, double surfTemp) {
+ double isaDeviation = surfTemp - 15;
+
+ // Formula is from here: https://www.pprune.org/tech-log/573002-accurate-temperature-correction-formula.html
+ return ((-isaDeviation / -0.0019812) * std::log(1 + (-0.0019812 * planePressAlt) / (288.15 + -0.0019812 * airportPressureAlt)));
+}
+
+BOOL IWVisualization::OnEraseBkgnd(CDC* pDC)
+{
+ // Do nothing here to prevent background clearing
+ return TRUE;
+}
+
+const bool IWVisualization::GetLeftToRight()
+{
+ return this->leftToRight;
+}
+
+const bool IWVisualization::GetShowTagsByDefault()
+{
+ return this->showTagsByDefault;
+}
+
+const bool IWVisualization::GetApplyTemperatureCorrection()
+{
+ return this->applyTemperatureCorrection;
+}
+
+void IWVisualization::SetLeftToRight(bool leftToRight)
+{
+ this->leftToRight = leftToRight;
+ UpdateDimentions();
+ Invalidate();
+}
+
+void IWVisualization::SetShowTagsByDefault(bool showTagsByDefault)
+{
+ this->showTagsByDefault = showTagsByDefault;
+ Invalidate();
+}
+
+void IWVisualization::SetApplyTemperatureCorrection(bool applyTemperatureCorrection)
+{
+ this->applyTemperatureCorrection = applyTemperatureCorrection;
+ Invalidate();
+}
\ No newline at end of file
diff --git a/ILS_Window_Plugin/IWVisualization.h b/ILS_Window_Plugin/IWVisualization.h
new file mode 100644
index 0000000..f28223b
--- /dev/null
+++ b/ILS_Window_Plugin/IWVisualization.h
@@ -0,0 +1,87 @@
+#pragma once
+#include
+#include "IWDataTypes.h"
+#include
+#include
+
+#define APP_LINE_MARGIN_TOP 0.08
+#define APP_LINE_MARGIN_SIDES 0.08
+#define APP_LINE_MARGIN_BOTTOM 0.35
+#define LABEL_OFFSET 15
+#define TARGET_RADIUS 5
+#define HISTORY_TRAIL_RADIUS 5
+#define FT_PER_NM 6076.11549
+#define EARTH_RADIUS_NM 3440.065
+#define PI 3.14159265359
+
+class IWVisualization : public CWnd
+{
+public:
+ IWVisualization(IWApproachDefinition selectedApproach, IWStyling styling, CFont* font);
+ void DrawGlideslopeAndLocalizer(CDC& dc);
+ void DrawRadarTargets(CDC& dc);
+ void DrawCurrentZoomValue(CDC& dc);
+ void SetActiveApproach(IWApproachDefinition selectedApproach);
+ void SetLatestLiveData(IWLiveData* liveData);
+
+ // Settings gettters
+ const bool GetLeftToRight();
+ const bool GetShowTagsByDefault();
+ const bool GetApplyTemperatureCorrection();
+
+ // Settings setters
+ void SetLeftToRight(bool leftToRight);
+ void SetShowTagsByDefault(bool showTagsByDefault);
+ void SetApplyTemperatureCorrection(bool applyTemperatureCorrection);
+
+ DECLARE_MESSAGE_MAP()
+
+private:
+ afx_msg BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt);
+ afx_msg void OnTimer(UINT_PTR nIDEvent);
+ afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
+ afx_msg void OnSize(UINT nType, int cx, int cy);
+ afx_msg void OnPaint();
+ afx_msg BOOL OnEraseBkgnd(CDC* pDC);
+
+ void DrawDiamond(CPoint pt, int size, CDC& dc);
+ bool CalculateTargetCoordinates(const IWTargetPosition& position, CPoint& ptTopView, CPoint& ptSideView);
+ void UpdateDimentions();
+
+ double CalculateDistance(double lat1, double lon1, double lat2, double lon2);
+ double CalculateBearing(double lat1, double lon1, double lat2, double lon2);
+ double CalculateTemperatureCorrection(int planePressAlt, int airportPressureAlt, double surfTemp);
+
+ IWLiveData* m_latestLiveData = NULL;
+ IWApproachDefinition selectedApproach;
+
+ COLORREF rangeStatusTextColor;
+ COLORREF windowBackground;
+ COLORREF targetLabelColor;
+
+ CPen glideSlopePen;
+ CBrush localizerBrush;
+ CPen radarTargetPen;
+ CPen historyTrailPen;
+ IWTagMode tagMode;
+ CFont* font;
+
+ std::set clickedTargets;
+
+ int approachLength;
+ bool showCurrentZoomValue = false;
+ UINT_PTR zoomMessageTimerId = 1;
+
+ // Dimentions
+ float approachHeightFt;
+ double pixelsPerFt;
+ double pixelsPerNauticalMile;
+ CPoint glidePathTop;
+ CPoint glidePathBottom;
+
+ // Settings
+ bool showTagsByDefault;
+ bool applyTemperatureCorrection;
+ bool leftToRight;
+};
+
diff --git a/ILS_Window_Plugin/IWWindow.cpp b/ILS_Window_Plugin/IWWindow.cpp
index 4a32216..3ec2ca4 100644
--- a/ILS_Window_Plugin/IWWindow.cpp
+++ b/ILS_Window_Plugin/IWWindow.cpp
@@ -1,70 +1,59 @@
#include "pch.h"
#include "IWWindow.h"
#include
+#include "RenderUtils.h"
+#include "IWX11TitleBar.h"
+#include "IWCdeTitleBar.h"
#define MAX_PROCEDURES 100
#define MENU_ITEM_FLIP 10000
#define MENU_ITEM_SHOW_LABELS 10001
#define MENU_ITEM_CORRECT_FOR_TEMPERATURE 10002
+#define MENU_ITEM_CLOSE 10003
#define MENU_ITEM_PROCEDURES_SEL_START 20000
#define MENU_ITEM_PROCEDURES_NEW_START 30000
BEGIN_MESSAGE_MAP(IWWindow, CWnd)
+ ON_WM_LBUTTONDOWN()
+ ON_WM_SETCURSOR()
ON_WM_PAINT()
ON_WM_CREATE()
ON_WM_SIZE()
ON_WM_MOVE()
ON_WM_SIZING()
- ON_WM_LBUTTONDOWN()
- ON_WM_LBUTTONUP()
- ON_WM_CTLCOLOR()
ON_MESSAGE(WM_UPDATE_DATA, &IWWindow::OnUpdateData)
ON_WM_DESTROY()
ON_WM_ERASEBKGND()
ON_WM_NCACTIVATE()
ON_WM_GETMINMAXINFO()
- ON_WM_MOUSEWHEEL()
- ON_WM_TIMER()
ON_MESSAGE(WM_EXITSIZEMOVE, &IWWindow::OnExitSizeMove)
ON_COMMAND_EX(MENU_ITEM_FLIP, &IWWindow::OnMenuOptionSelected)
ON_COMMAND_EX(MENU_ITEM_SHOW_LABELS, &IWWindow::OnMenuOptionSelected)
ON_COMMAND_EX(MENU_ITEM_CORRECT_FOR_TEMPERATURE, &IWWindow::OnMenuOptionSelected)
+ ON_COMMAND_EX(MENU_ITEM_CLOSE, &IWWindow::OnMenuOptionSelected)
ON_COMMAND_RANGE(MENU_ITEM_PROCEDURES_SEL_START, MENU_ITEM_PROCEDURES_SEL_START + MAX_PROCEDURES, &IWWindow::OnProcedureSelected)
ON_COMMAND_RANGE(MENU_ITEM_PROCEDURES_NEW_START, MENU_ITEM_PROCEDURES_NEW_START + MAX_PROCEDURES, &IWWindow::OnProcedureSelected)
END_MESSAGE_MAP()
-IWWindow::IWWindow(IWApproachDefinition selectedApproach, IWStyling styling) : titleBar(
- selectedApproach.title,
- RGB(styling.windowFrameColor.r, styling.windowFrameColor.g, styling.windowFrameColor.b),
- RGB(styling.windowFrameTextColor.r, styling.windowFrameTextColor.g, styling.windowFrameTextColor.b),
- RGB(styling.windowOuterFrameColor.r, styling.windowOuterFrameColor.g, styling.windowOuterFrameColor.b),
- this
-)
+IWWindow::IWWindow(IWApproachDefinition selectedApproach, IWStyling styling, int titleBarHeight, int windowBorderThickness, int windowOuterBorderThickness)
+ : ilsVisualization(selectedApproach, styling, &this->font)
+ , TITLE_BAR_HEIGHT(titleBarHeight)
+ , WINDOW_BORDER_THICKNESS(windowBorderThickness)
+ , WINDOW_OUTER_BORDER_WIDTH(windowOuterBorderThickness)
+ , textColor(RGB(styling.windowFrameTextColor.r, styling.windowFrameTextColor.g, styling.windowFrameTextColor.b))
+ , windowBorderColor(RGB(styling.windowFrameColor.r, styling.windowFrameColor.g, styling.windowFrameColor.b))
+ , windowOuterBorderColor(RGB(styling.windowOuterFrameColor.r, styling.windowOuterFrameColor.g, styling.windowOuterFrameColor.b))
{
- this->selectedApproach = selectedApproach;
- this->approachLength = selectedApproach.defaultRange;
- this->leftToRight = selectedApproach.localizerCourse > 0 && selectedApproach.localizerCourse < 180;
-
- this->rangeStatusTextColor = RGB(styling.rangeStatusTextColor.r, styling.rangeStatusTextColor.g, styling.rangeStatusTextColor.b);
- this->windowBackground = RGB(styling.backgroundColor.r, styling.backgroundColor.g, styling.backgroundColor.b);
- this->targetLabelColor = RGB(styling.targetLabelColor.r, styling.targetLabelColor.g, styling.targetLabelColor.b);
- this->windowBorderPen.CreatePen(PS_SOLID, 6, RGB(styling.windowFrameColor.r, styling.windowFrameColor.g, styling.windowFrameColor.b));
- this->windowOuterBorderPen.CreatePen(PS_SOLID, 1, RGB(styling.windowOuterFrameColor.r, styling.windowOuterFrameColor.g, styling.windowOuterFrameColor.b));
- this->glideSlopePen.CreatePen(PS_SOLID, 1, RGB(styling.glideslopeColor.r, styling.glideslopeColor.g, styling.glideslopeColor.b));
- this->localizerBrush.CreateSolidBrush(RGB(styling.localizerColor.r, styling.localizerColor.g, styling.localizerColor.b));
- this->radarTargetPen.CreatePen(PS_SOLID, 1, RGB(styling.radarTargetColor.r, styling.radarTargetColor.g, styling.radarTargetColor.b));
- this->historyTrailPen.CreatePen(PS_SOLID, 1, RGB(styling.historyTrailColor.r, styling.historyTrailColor.g, styling.historyTrailColor.b));
-
float fontPointsSize = styling.fontSize * 72 / 96;
- this->euroScopeFont.CreatePointFont(int(fontPointsSize * 10), _T("EuroScope"));
- this->tagMode = styling.defaultTagMode;
- this->showTagsByDefault = styling.showTagByDefault;
+ this->font.CreatePointFont(int(fontPointsSize * 10), _T("EuroScope"));
+
+ this->selectedApproach = selectedApproach;
}
IWWindow::~IWWindow()
{
- this->titleBar.DestroyWindow();
+ this->titleBar->DestroyWindow();
}
int IWWindow::OnCreate(LPCREATESTRUCT lpCreateStruct)
@@ -73,16 +62,23 @@ int IWWindow::OnCreate(LPCREATESTRUCT lpCreateStruct)
return -1;
CRect barRect(0, 0, lpCreateStruct->cx, TITLE_BAR_HEIGHT);
- if (!titleBar.CreateTopBar(this, barRect, IDC_TOPBAR))
+ if (!titleBar->CreateTopBar(this, barRect, IDC_TOPBAR))
{
AfxMessageBox(_T("Failed to create top bar"));
- return -1; // Handle error appropriately
+ return -1;
+ }
+
+ // Create the window content
+ CRect contentRect = GetClientRectBelowTitleBar();
+ if (!ilsVisualization.Create(NULL, NULL, WS_CHILD | WS_VISIBLE, contentRect, this, 0))
+ {
+ AfxMessageBox(_T("Failed to create window content"));
+ return -1;
}
return 0;
}
-
void IWWindow::OnPaint()
{
CPaintDC dc(this); // Device context for painting
@@ -90,18 +86,20 @@ void IWWindow::OnPaint()
// Create a memory DC for double buffering
CRect rect;
GetClientRect(&rect);
- CBitmap bufferBitmap;
CDC memDC;
-
memDC.CreateCompatibleDC(&dc);
+
+ CBitmap bufferBitmap;
bufferBitmap.CreateCompatibleBitmap(&dc, rect.Width(), rect.Height());
CBitmap* pOldBitmap = memDC.SelectObject(&bufferBitmap);
- // Clear the background
- memDC.FillSolidRect(rect, windowBackground);
+ // Clear the background and draw window borders
+ memDC.FillSolidRect(rect, windowOuterBorderColor);
+ CRect innerRect = rect;
+ innerRect.InflateRect(-WINDOW_OUTER_BORDER_WIDTH, -WINDOW_OUTER_BORDER_WIDTH);
+ memDC.FillSolidRect(innerRect, windowBorderColor);
- // Perform all drawing operations on memDC instead of dc
- DrawContent(memDC);
+ DrawBorder(&memDC, rect);
// Copy the buffer to the screen
dc.BitBlt(0, 0, rect.Width(), rect.Height(), &memDC, 0, 0, SRCCOPY);
@@ -110,144 +108,6 @@ void IWWindow::OnPaint()
memDC.SelectObject(pOldBitmap);
}
-void IWWindow::DrawContent(CDC& dc)
-{
- CRect rect = GetClientRectBelowTitleBar();
- CFont* oldFont = dc.SelectObject(&euroScopeFont);
-
- dc.FillSolidRect(rect, windowBackground);
-
- // Draw glideslope line
- CPen* pOldPen = dc.SelectObject(&glideSlopePen);
- dc.MoveTo(glidePathTop);
- dc.LineTo(glidePathBottom);
- dc.SelectObject(pOldPen);
-
- // Draw localizer distance dots
- CBrush* pOldBrush = dc.SelectObject(&localizerBrush);
- for (int i = 0; i <= approachLength; i++) // Draw distance points. Every 5th point is large
- {
- int radius = i % 5 == 0 ? 4 : 2;
- int x = glidePathBottom.x - i * pixelsPerNauticalMile;
- int y = glidePathBottom.y;
-
- DrawDiamond(CPoint(x, y), radius, dc);
- }
- dc.SelectObject(pOldBrush);
-
- // Draw the radar targets
- for (const IWRadarTarget& radarTarget : m_latestLiveData.radarTargets)
- {
- auto it = radarTarget.positionHistory.rbegin();
- auto end = radarTarget.positionHistory.rend();
-
- // Draw the history trail in reverese so the newest dots appear on top
- for (auto it = radarTarget.positionHistory.rbegin(); it != radarTarget.positionHistory.rend(); ++it) {
- const IWTargetPosition& position = *it;
- bool isNewestPosition = (it + 1 == end); // Check if the iterator is the last element
-
- CPoint ptTopView, ptSideView;
- if (!CalculateTargetCoordinates(position, ptTopView, ptSideView)) {
- // Target is outside the visible area
- continue;
- }
-
- int crossRadius = isNewestPosition ? TARGET_RADIUS : HISTORY_TRAIL_RADIUS;
-
- // Draw position seen from the side
- dc.SelectObject(isNewestPosition ? radarTargetPen : historyTrailPen);
- dc.MoveTo(CPoint(ptSideView.x, ptSideView.y - crossRadius));
- dc.LineTo(CPoint(ptSideView.x, ptSideView.y + crossRadius + 1));
- dc.MoveTo(CPoint(ptSideView.x - crossRadius, ptSideView.y));
- dc.LineTo(CPoint(ptSideView.x + crossRadius + 1, ptSideView.y));
-
- if (isNewestPosition) {
- dc.SelectStockObject(NULL_BRUSH);
- dc.Ellipse(ptSideView.x - crossRadius, ptSideView.y - crossRadius, ptSideView.x + crossRadius + 1, ptSideView.y + crossRadius + 1);
- }
-
- // Draw position seen from above
- dc.SelectObject(isNewestPosition ? radarTargetPen : historyTrailPen);
- dc.MoveTo(CPoint(ptTopView.x, ptTopView.y - crossRadius));
- dc.LineTo(CPoint(ptTopView.x, ptTopView.y + crossRadius + 1));
- dc.MoveTo(CPoint(ptTopView.x - crossRadius, ptTopView.y));
- dc.LineTo(CPoint(ptTopView.x + crossRadius + 1, ptTopView.y));
-
- // Draw the main target
- if (isNewestPosition) {
- dc.SelectStockObject(NULL_BRUSH);
- dc.Ellipse(ptTopView.x - crossRadius, ptTopView.y - crossRadius, ptTopView.x + crossRadius + 1, ptTopView.y + crossRadius + 1);
-
- // Check if callsign is in the vector of clicked targets
- bool isClicked = this->clickedTargets.find(radarTarget.callsign) != this->clickedTargets.end();
-
- if (this->showTagsByDefault) {
- if (isClicked) continue;
- }
- else {
- if (!isClicked) continue;
- }
-
- dc.MoveTo(ptTopView);
- dc.LineTo(ptTopView.x + LABEL_OFFSET, ptTopView.y + LABEL_OFFSET);
-
- // Callsign label
- dc.SetTextColor(targetLabelColor);
- dc.SetBkMode(TRANSPARENT);
-
-
- CString targetLabel;
- if (this->tagMode == IWTagMode::Squawk) {
- targetLabel.Format(_T("%s"), radarTarget.squawk.c_str());
- }
- else if (this->tagMode == IWTagMode::Callsign) {
- targetLabel.Format(_T("%s"), radarTarget.callsign.c_str());
- }
-
- // Draw the label
- CSize textSize = dc.GetTextExtent(targetLabel);
- CPoint labelPosition(ptTopView.x + LABEL_OFFSET, ptTopView.y + LABEL_OFFSET);
- CRect labelRect(labelPosition.x, labelPosition.y, labelPosition.x + textSize.cx, labelPosition.y + textSize.cy * 2);
- dc.DrawText(targetLabel, labelRect, DT_LEFT);
- }
- }
- }
-
- if (showZoomMessage)
- {
- CRect rect = GetClientRectBelowTitleBar();
-
- dc.SetTextColor(this->rangeStatusTextColor);
- dc.SetBkMode(TRANSPARENT);
-
- CString zoomMessage;
- zoomMessage.Format(_T("%d NM"), this->approachLength);
-
- CSize textSize = dc.GetTextExtent(zoomMessage);
- CPoint position(
- this->leftToRight ? rect.right - textSize.cx - 5 : rect.left + 5,
- rect.top + 5
- );
- CRect textRect(position.x, position.y, position.x + textSize.cx, position.y + textSize.cy*2);
-
- dc.DrawText(zoomMessage, textRect, this->leftToRight ? DT_RIGHT : DT_LEFT);
- }
-
- // Draw custom window border
- dc.SelectStockObject(NULL_BRUSH);
- dc.SelectObject(windowBorderPen);
- dc.Rectangle(rect);
-
- // Draw outer window border
- CRect fullWindowArea;
- GetClientRect(&fullWindowArea);
- dc.SelectObject(windowOuterBorderPen);
- dc.Rectangle(fullWindowArea);
-
- // Cleanup
- dc.SelectObject(oldFont);
-}
-
void IWWindow::OnSize(UINT nType, int cx, int cy)
{
CWnd::OnSize(nType, cx, cy);
@@ -255,14 +115,19 @@ void IWWindow::OnSize(UINT nType, int cx, int cy)
// Recalculate gradient or redraw on resize
if (nType != SIZE_MINIMIZED) // Ignore if window is minimized
{
- UpdateDimentions();
Invalidate(); // Mark the entire client area for repaint
}
- if (titleBar.GetSafeHwnd())
+ if (titleBar->GetSafeHwnd())
{
- CRect barRect(0, 0, cx, TITLE_BAR_HEIGHT);
- titleBar.MoveWindow(barRect);
+ CRect barRect(WINDOW_BORDER_THICKNESS, WINDOW_BORDER_THICKNESS, cx - WINDOW_BORDER_THICKNESS, TITLE_BAR_HEIGHT);
+ titleBar->MoveWindow(barRect);
+ }
+
+ if (ilsVisualization.GetSafeHwnd())
+ {
+ CRect contentRect = GetClientRectBelowTitleBar();
+ ilsVisualization.MoveWindow(contentRect);
}
}
@@ -285,12 +150,35 @@ void IWWindow::OnSizing(UINT nSide, LPRECT lpRect)
int snappedWidth = static_cast(std::round(width / SIZE_SNAP_INCREMENTS) * SIZE_SNAP_INCREMENTS);
int snappedHeight = static_cast(std::round(height / SIZE_SNAP_INCREMENTS) * SIZE_SNAP_INCREMENTS);
- // Adjust the RECT based on the resizing side.
- // It's always the top right corner in our case
+ // Adjust the RECT based on the resizing side.
if (nSide == WMSZ_TOPRIGHT) {
lpRect->top = lpRect->bottom - snappedHeight;
lpRect->right = lpRect->left + snappedWidth;
}
+ else if (nSide == WMSZ_BOTTOMLEFT) {
+ lpRect->bottom = lpRect->top + snappedHeight;
+ lpRect->left = lpRect->right - snappedWidth;
+ }
+ else if (nSide == WMSZ_BOTTOMRIGHT) {
+ lpRect->bottom = lpRect->top + snappedHeight;
+ lpRect->right = lpRect->left + snappedWidth;
+ }
+ else if (nSide == WMSZ_TOPLEFT) {
+ lpRect->top = lpRect->bottom - snappedHeight;
+ lpRect->left = lpRect->right - snappedWidth;
+ }
+ else if (nSide == WMSZ_TOP) {
+ lpRect->top = lpRect->bottom - snappedHeight;
+ }
+ else if (nSide == WMSZ_BOTTOM) {
+ lpRect->bottom = lpRect->top + snappedHeight;
+ }
+ else if (nSide == WMSZ_LEFT) {
+ lpRect->left = lpRect->right - snappedWidth;
+ }
+ else if (nSide == WMSZ_RIGHT) {
+ lpRect->right = lpRect->left + snappedWidth;
+ }
CWnd::OnSizing(nSide, lpRect); // Call the base class handler
}
@@ -320,16 +208,12 @@ LRESULT IWWindow::OnUpdateData(WPARAM wParam, LPARAM lParam)
IWLiveData* pData = reinterpret_cast(wParam);
if (pData) {
m_latestLiveData = *pData;
+ ilsVisualization.SetLatestLiveData(&m_latestLiveData);
}
- // Trigger a repaint
- CRect updateRect = GetClientRectBelowTitleBar(); // Adjust based on what needs redrawing
- InvalidateRect(&updateRect, FALSE); // FALSE to avoid erasing the background unnecessarily
-
return 0;
}
-
CRect IWWindow::GetClientRectBelowTitleBar()
{
CRect rect;
@@ -337,35 +221,25 @@ CRect IWWindow::GetClientRectBelowTitleBar()
// Adjust rect to exclude the top bar area (assume the top bar is 30 pixels high)
CRect topBarRect;
- titleBar.GetWindowRect(&topBarRect);
+ titleBar->GetWindowRect(&topBarRect);
ScreenToClient(&topBarRect); // Convert to client coordinates
rect.top = topBarRect.bottom; // Move the top to below the top bar
+ rect.left += WINDOW_BORDER_THICKNESS;
+ rect.right -= WINDOW_BORDER_THICKNESS;
+ rect.bottom -= WINDOW_BORDER_THICKNESS;
return rect;
}
-void IWWindow::DrawDiamond(CPoint pt, int radius, CDC& dc)
-{
- CPoint pts[5];
- pts[0] = CPoint(pt.x, pt.y - radius);
- pts[1] = CPoint(pt.x + radius, pt.y);
- pts[2] = CPoint(pt.x, pt.y + radius);
- pts[3] = CPoint(pt.x - radius, pt.y);
- pts[4] = CPoint(pt.x, pt.y - radius);
-
- dc.SetPolyFillMode(ALTERNATE);
- dc.Polygon(pts, 5);
-}
-
void IWWindow::OnResizeStart()
{
SendMessage(WM_NCLBUTTONDOWN, HTTOPRIGHT, NULL); // Resize using the top right corner
}
-void IWWindow::OnCloseButtonClicked()
+void IWWindow::OnIconifyButtonClicked()
{
- this->DestroyWindow();
+ this->ShowWindow(SW_MINIMIZE);
}
void IWWindow::OnMenuButtonClicked()
@@ -400,181 +274,62 @@ void IWWindow::OnGetMinMaxInfo(MINMAXINFO* lpMMI)
lpMMI->ptMinTrackSize.y = 100; // Minimum height in pixels
}
-BOOL IWWindow::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
+void IWWindow::OnLButtonDown(UINT nFlags, CPoint point)
{
- if (zDelta > 0 && this->approachLength > 5)
- {
- this->approachLength -= 1;
- }
- else if (zDelta < 0 && this->approachLength < 50)
- {
- this->approachLength += 1;
- }
-
- UpdateDimentions();
+ bool isTop, isLeft, isRight, isBottom;
+ int cursorPosition = GetEdgeCursorPosition(point);
- CRect updateRect = GetClientRectBelowTitleBar();
- InvalidateRect(updateRect);
-
- SetTimer(zoomMessageTimerId, 1000, nullptr);
-
- showZoomMessage = true;
+ if (cursorPosition != HTNOWHERE) {
+ SendMessage(WM_NCLBUTTONDOWN, cursorPosition, MAKELPARAM(point.x, point.y));
+ }
- return TRUE;
+ CWnd::OnLButtonDown(nFlags, point);
}
-void IWWindow::OnTimer(UINT_PTR nIDEvent)
+BOOL IWWindow::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
- if (nIDEvent == zoomMessageTimerId)
+ if (nHitTest == HTCLIENT)
{
- // Hide the zoom message
- showZoomMessage = false;
- KillTimer(zoomMessageTimerId);
+ // Get the cursor position in screen coordinates
+ POINT cursorPos;
+ GetCursorPos(&cursorPos);
- // Invalidate to redraw without the message
- CRect updateRect = GetClientRectBelowTitleBar();
- InvalidateRect(updateRect);
- }
+ // Convert the cursor position to client coordinates
+ ScreenToClient(&cursorPos);
- CWnd::OnTimer(nIDEvent);
-}
+ // Check the position of the cursor relative to the window
+ int cursorPosition = GetEdgeCursorPosition(cursorPos);
-void IWWindow::OnLButtonDown(UINT nFlags, CPoint point)
-{
- for (const IWRadarTarget& radarTarget : m_latestLiveData.radarTargets) {
- auto newestPosition = radarTarget.positionHistory.size() > 0 ? &radarTarget.positionHistory.front() : nullptr;
-
- if (newestPosition) {
- const IWTargetPosition& position = *newestPosition;
-
- CPoint ptTopView, ptSideView;
- if (!CalculateTargetCoordinates(position, ptTopView, ptSideView)) {
- // Target is outside the visible area
- continue;
- }
-
- CRect targetRect(ptTopView.x - TARGET_RADIUS, ptTopView.y - TARGET_RADIUS, ptTopView.x + TARGET_RADIUS, ptTopView.y + TARGET_RADIUS);
-
- if (targetRect.PtInRect(point))
- {
- bool isAlreadyClicked = this->clickedTargets.find(radarTarget.callsign) != this->clickedTargets.end();
-
- if (isAlreadyClicked) {
- this->clickedTargets.erase(radarTarget.callsign);
- }
- else {
- this->clickedTargets.insert(radarTarget.callsign);
- }
- CRect updateRect = GetClientRectBelowTitleBar();
- InvalidateRect(updateRect);
- break;
- }
+ if (cursorPosition == HTNOWHERE)
+ {
+ return FALSE; // Prevent the system from overriding the cursor
}
- }
-
- CWnd::OnLButtonDown(nFlags, point);
-}
-
-bool IWWindow::CalculateTargetCoordinates(const IWTargetPosition& position, CPoint& ptTopView, CPoint& ptSideView)
-{
- // Calculate position relative to the runway
- double distanceToThreshold = CalculateDistance(position.latitude, position.longitude, selectedApproach.thresholdLatitude, selectedApproach.thresholdLongitude);
- double directionToThreshold = CalculateBearing(position.latitude, position.longitude, selectedApproach.thresholdLatitude, selectedApproach.thresholdLongitude);
- double angleDiff = (selectedApproach.localizerCourse - directionToThreshold) / 180.0 * PI; // anglediff in radians
- double heightAboveThreshold = position.trueAltitude - selectedApproach.thresholdAltitude;
- auto airportTemperature = m_latestLiveData.airportTemperatures.find(selectedApproach.airport);
- if (applyTemperatureCorrection && airportTemperature != m_latestLiveData.airportTemperatures.end()) {
- // In newer flight simulators true altitude is affected by the temperature.
- // In cold weather aircraft will be shown higher than they actually are, unless we correct for it.
- // See: https://forums.flightsimulator.com/t/vatsim-ivao-pilotedge-users-be-aware-of-an-important-bug/426142/468
- int temperatureCorrection = CalculateTemperatureCorrection(position.trueAltitude, selectedApproach.thresholdAltitude, airportTemperature->second);
- heightAboveThreshold -= temperatureCorrection;
- }
-
- double projectedDistanceFromThreshold = distanceToThreshold * cos(angleDiff);
- double projectedDistanceFromExtendedCenterline = distanceToThreshold * tan(angleDiff);
+ switch (cursorPosition)
+ {
+ case HTTOPLEFT:
+ case HTBOTTOMRIGHT:
+ SetCursor(AfxGetApp()->LoadStandardCursor(IDC_SIZENWSE));
+ break;
+ case HTTOPRIGHT:
+ case HTBOTTOMLEFT:
+ SetCursor(AfxGetApp()->LoadStandardCursor(IDC_SIZENESW));
+ break;
+ case HTTOP:
+ case HTBOTTOM:
+ SetCursor(AfxGetApp()->LoadStandardCursor(IDC_SIZENS));
+ break;
+ case HTLEFT:
+ case HTRIGHT:
+ SetCursor(AfxGetApp()->LoadStandardCursor(IDC_SIZEWE));
+ break;
+ }
- if (projectedDistanceFromExtendedCenterline < 0 && abs(projectedDistanceFromExtendedCenterline) > this->selectedApproach.maxOffsetLeft) {
- // Too far left
- return false;
- }
- if (projectedDistanceFromExtendedCenterline > 0 && abs(projectedDistanceFromExtendedCenterline) > this->selectedApproach.maxOffsetRight) {
- // Too far right
- return false;
- }
- if (projectedDistanceFromThreshold < 0) {
- // Wrong side of the threshold
- return false;
- }
- if (heightAboveThreshold > approachHeightFt) {
- // Too high
- return false;
+ return TRUE; // Prevent the system from overriding the cursor
}
- int xPosition = glidePathBottom.x - projectedDistanceFromThreshold * pixelsPerNauticalMile;
- int yPositionSlope = glidePathBottom.y - heightAboveThreshold * pixelsPerFt;
- int yPositionCenterline = glidePathBottom.y + projectedDistanceFromExtendedCenterline * pixelsPerNauticalMile;
- ptSideView = CPoint(xPosition, yPositionSlope);
- ptTopView = CPoint(xPosition, yPositionCenterline);
- return true;
-}
-
-void IWWindow::UpdateDimentions()
-{
- CRect rect = GetClientRectBelowTitleBar();
-
- const int CALC_SIDE_MARGIN = rect.Width() * APP_LINE_MARGIN_SIDES;
- const int CALC_TOP_MARGIN = rect.Height() * APP_LINE_MARGIN_TOP;
- const int CALC_BOTTOM_MARGIN = rect.Height() * APP_LINE_MARGIN_BOTTOM;
-
- // Define the start and end coordintes for rendering the glidepath
- this->glidePathTop = CPoint(leftToRight ? rect.left + CALC_SIDE_MARGIN : rect.right - CALC_SIDE_MARGIN, rect.top + CALC_TOP_MARGIN);
- this->glidePathBottom = CPoint(leftToRight ? rect.right - CALC_SIDE_MARGIN : rect.left + CALC_SIDE_MARGIN, rect.bottom - CALC_BOTTOM_MARGIN);
-
- this->approachHeightFt = (approachLength * FT_PER_NM * sin(selectedApproach.glideslopeAngle / 180.0 * PI));
-
- this->pixelsPerFt = (glidePathBottom.y - glidePathTop.y) / approachHeightFt;
- this->pixelsPerNauticalMile = (glidePathBottom.x - glidePathTop.x) / float(approachLength); // PS: negative when direction is left->right
-}
-
-double IWWindow::CalculateDistance(double lat1, double lon1, double lat2, double lon2) {
- // Convert latitude and longitude from degrees to radians
- lat1 = lat1 * PI / 180.0;
- lon1 = lon1 * PI / 180.0;
- lat2 = lat2 * PI / 180.0;
- lon2 = lon2 * PI / 180.0;
- // Haversine formula
- double dlat = lat2 - lat1;
- double dlon = lon2 - lon1;
- double a = pow(sin(dlat / 2), 2) + cos(lat1) * cos(lat2) * pow(sin(dlon / 2), 2);
- double c = 2 * atan2(sqrt(a), sqrt(1 - a));
- double distance = EARTH_RADIUS_NM * c;
- return distance;
-}
-
-double IWWindow::CalculateBearing(double lat1, double lon1, double lat2, double lon2) {
- // Convert latitude and longitude from degrees to radians
- lat1 = lat1 * PI / 180.0;
- lon1 = lon1 * PI / 180.0;
- lat2 = lat2 * PI / 180.0;
- lon2 = lon2 * PI / 180.0;
-
- // Calculate the bearing
- double y = sin(lon2 - lon1) * cos(lat2);
- double x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(lon2 - lon1);
- double bearing = atan2(y, x);
- bearing = fmod(bearing + 2 * PI, 2 * PI);
-
- // Convert to degrees
- return bearing * 180.0 / PI;
-}
-
-double IWWindow::CalculateTemperatureCorrection(int planePressAlt, int airportPressureAlt, double surfTemp) {
- double isaDeviation = surfTemp - 15;
-
- // Formula is from here: https://www.pprune.org/tech-log/573002-accurate-temperature-correction-formula.html
- return ((-isaDeviation / -0.0019812) * std::log(1 + (-0.0019812 * planePressAlt) / (288.15 + -0.0019812 * airportPressureAlt)));
+ // Default behavior for other non-client areas
+ return CWnd::OnSetCursor(pWnd, nHitTest, message);
}
std::string IWWindow::GetActiveApproachName() const
@@ -587,10 +342,9 @@ void IWWindow::SetActiveApproach(const IWApproachDefinition& selectedApproach)
{
std::unique_lock lock(approachDataMutex);
this->selectedApproach = selectedApproach;
- this->approachLength = selectedApproach.defaultRange;
- this->leftToRight = selectedApproach.localizerCourse > 0 && selectedApproach.localizerCourse < 180;
- this->titleBar.SetTitle(selectedApproach.title);
- UpdateDimentions();
+ this->SetWindowTextA(selectedApproach.title.c_str());
+
+ ilsVisualization.SetActiveApproach(this->selectedApproach);
Invalidate();
lock.unlock();
@@ -628,7 +382,7 @@ void IWWindow::CreatePopupMenu(CPoint point)
// Add static menu items
menu.AppendMenu(
- MF_STRING | (this->showTagsByDefault ? MF_CHECKED : MF_UNCHECKED),
+ MF_STRING | (ilsVisualization.GetShowTagsByDefault() ? MF_CHECKED : MF_UNCHECKED),
MENU_ITEM_SHOW_LABELS,
_T("Show labels by default")
);
@@ -641,7 +395,7 @@ void IWWindow::CreatePopupMenu(CPoint point)
+ ")";
menu.AppendMenu(
- MF_STRING | (this->applyTemperatureCorrection ? MF_CHECKED : MF_UNCHECKED),
+ MF_STRING | (ilsVisualization.GetApplyTemperatureCorrection() ? MF_CHECKED : MF_UNCHECKED),
MENU_ITEM_CORRECT_FOR_TEMPERATURE,
_T(airportTemperatureMenuText.c_str())
);
@@ -651,29 +405,37 @@ void IWWindow::CreatePopupMenu(CPoint point)
_T("Change orientation")
);
+ menu.AppendMenu(
+ MF_STRING | MF_REMOVE,
+ MENU_ITEM_CLOSE,
+ _T("Close")
+ );
+
// Display the menu
menu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this);
-
}
BOOL IWWindow::OnMenuOptionSelected(UINT nID)
{
if (nID == MENU_ITEM_FLIP)
{
- this->leftToRight = !this->leftToRight;
- UpdateDimentions();
+ ilsVisualization.SetLeftToRight(!ilsVisualization.GetLeftToRight());
Invalidate();
}
else if (nID == MENU_ITEM_SHOW_LABELS)
{
- this->showTagsByDefault = !this->showTagsByDefault;
+ ilsVisualization.SetShowTagsByDefault(!ilsVisualization.GetShowTagsByDefault());
Invalidate();
}
else if (nID == MENU_ITEM_CORRECT_FOR_TEMPERATURE)
{
- this->applyTemperatureCorrection = !this->applyTemperatureCorrection;
+ ilsVisualization.SetApplyTemperatureCorrection(!ilsVisualization.GetApplyTemperatureCorrection());
Invalidate();
}
+ else if (nID == MENU_ITEM_CLOSE)
+ {
+ this->DestroyWindow();
+ }
return TRUE;
}
diff --git a/ILS_Window_Plugin/IWWindow.h b/ILS_Window_Plugin/IWWindow.h
index 8e96c93..38c0b45 100644
--- a/ILS_Window_Plugin/IWWindow.h
+++ b/ILS_Window_Plugin/IWWindow.h
@@ -1,6 +1,7 @@
#pragma once
#include
#include "IWDataTypes.h"
+#include "IWVisualization.h"
#include "IWTitleBar.h"
#include
#include
@@ -9,23 +10,11 @@
#define IDC_TOPBAR 1002
#define WM_UPDATE_DATA (WM_USER + 1)
-#define APP_LINE_MARGIN_TOP 0.08
-#define APP_LINE_MARGIN_SIDES 0.08
-#define APP_LINE_MARGIN_BOTTOM 0.35
-#define LABEL_OFFSET 15
-#define TARGET_RADIUS 5
-#define HISTORY_TRAIL_RADIUS 5
-#define PI 3.14159265359
-#define FT_PER_NM 6076.11549
-
-#define TITLE_BAR_HEIGHT 27
#define SIZE_SNAP_INCREMENTS 20.0
-#define EARTH_RADIUS_NM 3440.065
-#define PI 3.14159265359
-
class IWWindow;
+
class IIWWndEventListener {
public:
virtual void OnWindowClosed(IWWindow* window) = 0;
@@ -33,92 +22,66 @@ class IIWWndEventListener {
virtual void OnWindowRectangleChanged(IWWindow* window) = 0;
};
-class IWWindow : public CWnd, IWTitleBarEventListener {
- protected:
- afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
- afx_msg void OnPaint();
- afx_msg void OnSize(UINT nType, int cx, int cy);
- afx_msg LRESULT OnExitSizeMove(WPARAM wParam, LPARAM lParam);
- afx_msg void OnSizing(UINT nSide, LPRECT lpRect);
- afx_msg BOOL PreCreateWindow(CREATESTRUCT& cs);
- afx_msg LRESULT OnUpdateData(WPARAM wParam, LPARAM lParam);
- afx_msg void OnDestroy();
- afx_msg BOOL OnEraseBkgnd(CDC* pDC);
- afx_msg BOOL OnNcActivate(BOOL bActive);
- afx_msg void OnGetMinMaxInfo(MINMAXINFO* lpMMI);
- afx_msg BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt);
- afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
-
- BOOL OnMenuOptionSelected(UINT nID);
- void OnProcedureSelected(UINT nID);
-
- void DrawContent(CDC& dc);
- CRect GetClientRectBelowTitleBar();
-
- afx_msg void OnTimer(UINT_PTR nIDEvent);
-
-
- DECLARE_MESSAGE_MAP()
+class IWWindow : public CWnd, public IWTitleBarEventListener {
+ DECLARE_MESSAGE_MAP()
public:
- IWWindow(IWApproachDefinition selectedApproach, IWStyling styling);
+ IWWindow(IWApproachDefinition selectedApproach, IWStyling styling, int titleBarHeight, int windowBorderThickness, int windowOuterBorderThickness);
virtual ~IWWindow();
void SetListener(IIWWndEventListener* listener);
std::string GetActiveApproachName() const;
void SetActiveApproach(const IWApproachDefinition& selectedApproach);
void SetAvailableApproaches(const std::vector& approaches);
+ protected:
+ virtual void DrawBorder(CDC* pdc, CRect windowRect) = 0;
+ virtual int GetEdgeCursorPosition(CPoint point) = 0;
+
+ const int TITLE_BAR_HEIGHT;
+ const int WINDOW_BORDER_THICKNESS;
+ const int WINDOW_OUTER_BORDER_WIDTH;
+ const COLORREF textColor;
+ const COLORREF windowBorderColor;
+ const COLORREF windowOuterBorderColor;
+
+ IWTitleBar* titleBar;
+
private:
- IWTitleBar titleBar;
- IWLiveData m_latestLiveData;
- void DrawDiamond(CPoint pt, int size, CDC& dc);
- bool CalculateTargetCoordinates(const IWTargetPosition& position, CPoint& ptTopView, CPoint& ptSideView);
- void UpdateDimentions();
+
+ IWVisualization ilsVisualization;
+ CFont font;
+
void CreatePopupMenu(CPoint point);
IWApproachDefinition selectedApproach;
std::vector availableApproaches;
- double CalculateDistance(double lat1, double lon1, double lat2, double lon2);
- double CalculateBearing(double lat1, double lon1, double lat2, double lon2);
- double CalculateTemperatureCorrection(int planePressAlt, int airportPressureAlt, double surfTemp);
-
- COLORREF rangeStatusTextColor;
- COLORREF windowBackground;
- COLORREF targetLabelColor;
- CPen glideSlopePen;
- CBrush localizerBrush;
- CPen radarTargetPen;
- CPen historyTrailPen;
- CPen windowBorderPen;
- CPen windowOuterBorderPen;
IIWWndEventListener* m_listener = nullptr;
- IWTagMode tagMode;
- bool showTagsByDefault;
-
- std::set clickedTargets;
-
- int approachLength;
- bool leftToRight;
- bool applyTemperatureCorrection = true;
-
- // Dimentions
- float approachHeightFt;
- double pixelsPerFt;
- double pixelsPerNauticalMile;
- CPoint glidePathTop;
- CPoint glidePathBottom;
-
- bool showZoomMessage = false;
- UINT_PTR zoomMessageTimerId = 1;
-
- CFont euroScopeFont;
// For handling events from the title bar
void OnResizeStart() override;
- void OnCloseButtonClicked() override;
+ void OnIconifyButtonClicked() override;
void OnMenuButtonClicked() override;
// For thread safety between EuroScope and the window thread
+ IWLiveData m_latestLiveData;
mutable std::mutex approachDataMutex;
+
+ afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
+ afx_msg void OnPaint();
+ afx_msg void OnSize(UINT nType, int cx, int cy);
+ afx_msg LRESULT OnExitSizeMove(WPARAM wParam, LPARAM lParam);
+ afx_msg void OnSizing(UINT nSide, LPRECT lpRect);
+ afx_msg BOOL PreCreateWindow(CREATESTRUCT& cs);
+ afx_msg LRESULT OnUpdateData(WPARAM wParam, LPARAM lParam);
+ afx_msg void OnDestroy();
+ afx_msg BOOL OnEraseBkgnd(CDC* pDC);
+ afx_msg BOOL OnNcActivate(BOOL bActive);
+ afx_msg void OnGetMinMaxInfo(MINMAXINFO* lpMMI);
+ afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
+ afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message);
+
+ BOOL OnMenuOptionSelected(UINT nID);
+ void OnProcedureSelected(UINT nID);
+ CRect GetClientRectBelowTitleBar();
};
diff --git a/ILS_Window_Plugin/IWCloseBtn.cpp b/ILS_Window_Plugin/IWX11IconifyBtn.cpp
similarity index 84%
rename from ILS_Window_Plugin/IWCloseBtn.cpp
rename to ILS_Window_Plugin/IWX11IconifyBtn.cpp
index f5b5f2f..db8893a 100644
--- a/ILS_Window_Plugin/IWCloseBtn.cpp
+++ b/ILS_Window_Plugin/IWX11IconifyBtn.cpp
@@ -1,7 +1,7 @@
#include "pch.h"
-#include "IWCloseBtn.h"
+#include "IWX11IconifyBtn.h"
-void IWCloseBtn::DrawSymbol(CDC* pDC, CRect rect)
+void IWX11IconifyBtn::DrawSymbol(CDC* pDC, CRect rect)
{
// Draw a black circle in the center
CBrush brush(RGB(0, 0, 0)); // Solid black brush for the circle
diff --git a/ILS_Window_Plugin/IWX11IconifyBtn.h b/ILS_Window_Plugin/IWX11IconifyBtn.h
new file mode 100644
index 0000000..c4331a3
--- /dev/null
+++ b/ILS_Window_Plugin/IWX11IconifyBtn.h
@@ -0,0 +1,10 @@
+#pragma once
+#include "IWTitleBarBtn.h"
+
+class IWX11IconifyBtn : public IWTitleBarBtn
+{
+ using IWTitleBarBtn::IWTitleBarBtn;
+ void DrawSymbol(CDC* pDC, CRect rect) override;
+
+};
+
diff --git a/ILS_Window_Plugin/IWMenuBtn.cpp b/ILS_Window_Plugin/IWX11MenuBtn.cpp
similarity index 63%
rename from ILS_Window_Plugin/IWMenuBtn.cpp
rename to ILS_Window_Plugin/IWX11MenuBtn.cpp
index 53cf847..1a86cb9 100644
--- a/ILS_Window_Plugin/IWMenuBtn.cpp
+++ b/ILS_Window_Plugin/IWX11MenuBtn.cpp
@@ -1,12 +1,8 @@
#include "pch.h"
-#include "IWMenuBtn.h"
+#include "IWX11MenuBtn.h"
-BEGIN_MESSAGE_MAP(IWMenuBtn, CButton)
- ON_WM_LBUTTONDOWN()
- ON_WM_ERASEBKGND()
-END_MESSAGE_MAP()
-void IWMenuBtn::DrawSymbol(CDC* pDC, CRect rect)
+void IWX11MenuBtn::DrawSymbol(CDC* pDC, CRect rect)
{
// Calculate the center of the rectangle
CPoint center = rect.CenterPoint();
@@ -31,21 +27,3 @@ void IWMenuBtn::DrawSymbol(CDC* pDC, CRect rect)
// Restore the previous brush
pDC->SelectObject(oldBrush);
}
-
-void IWMenuBtn::OnLButtonDown(UINT nFlags, CPoint point)
-{
- CPoint screenPoint = point;
- ClientToScreen(&screenPoint);
-
- // Get the parent window
- CWnd* parentWnd = GetParent();
- if (parentWnd)
- {
- parentWnd->SendMessage(WM_LBUTTONDOWN, NULL, MAKELPARAM(screenPoint.x, screenPoint.y));
- }
-}
-
-BOOL IWMenuBtn::OnEraseBkgnd(CDC* pDC)
-{
- return TRUE;
-}
diff --git a/ILS_Window_Plugin/IWCloseBtn.h b/ILS_Window_Plugin/IWX11MenuBtn.h
similarity index 54%
rename from ILS_Window_Plugin/IWCloseBtn.h
rename to ILS_Window_Plugin/IWX11MenuBtn.h
index a5448fc..588bdd1 100644
--- a/ILS_Window_Plugin/IWCloseBtn.h
+++ b/ILS_Window_Plugin/IWX11MenuBtn.h
@@ -1,9 +1,9 @@
#pragma once
#include "IWTitleBarBtn.h"
-class IWCloseBtn : public IWTitleBarBtn
+class IWX11MenuBtn : public IWTitleBarBtn
{
+ using IWTitleBarBtn::IWTitleBarBtn;
void DrawSymbol(CDC* pDC, CRect rect) override;
-
};
diff --git a/ILS_Window_Plugin/IWResizeBtn.cpp b/ILS_Window_Plugin/IWX11ResizeBtn.cpp
similarity index 50%
rename from ILS_Window_Plugin/IWResizeBtn.cpp
rename to ILS_Window_Plugin/IWX11ResizeBtn.cpp
index 352261f..471ad02 100644
--- a/ILS_Window_Plugin/IWResizeBtn.cpp
+++ b/ILS_Window_Plugin/IWX11ResizeBtn.cpp
@@ -1,18 +1,7 @@
#include "pch.h"
-#include "IWResizeBtn.h"
+#include "IWX11ResizeBtn.h"
-BEGIN_MESSAGE_MAP(IWResizeBtn, CButton)
- ON_WM_LBUTTONDOWN()
- ON_WM_ERASEBKGND()
-END_MESSAGE_MAP()
-
-BOOL IWResizeBtn::OnEraseBkgnd(CDC* pDC)
-{
- // Do nothing to keep the background transparent
- return TRUE;
-}
-
-void IWResizeBtn::DrawSymbol(CDC* pDC, CRect rect) {
+void IWX11ResizeBtn::DrawSymbol(CDC* pDC, CRect rect) {
// Create a 1px black pen
CPen pen(PS_SOLID, 1, RGB(0, 0, 0));
CPen* oldPen = pDC->SelectObject(&pen);
@@ -38,17 +27,3 @@ void IWResizeBtn::DrawSymbol(CDC* pDC, CRect rect) {
// Restore old pen
pDC->SelectObject(oldPen);
}
-
-
-void IWResizeBtn::OnLButtonDown(UINT nFlags, CPoint point)
-{
- CPoint screenPoint = point;
- ClientToScreen(&screenPoint);
-
- // Get the parent window
- CWnd* parentWnd = GetParent();
- if (parentWnd)
- {
- parentWnd->SendMessage(WM_LBUTTONDOWN, NULL, MAKELPARAM(screenPoint.x, screenPoint.y));
- }
-}
\ No newline at end of file
diff --git a/ILS_Window_Plugin/IWX11ResizeBtn.h b/ILS_Window_Plugin/IWX11ResizeBtn.h
new file mode 100644
index 0000000..61f44a9
--- /dev/null
+++ b/ILS_Window_Plugin/IWX11ResizeBtn.h
@@ -0,0 +1,8 @@
+#pragma once
+#include "IWTitleBarBtn.h"
+
+class IWX11ResizeBtn : public IWTitleBarBtn
+{
+ using IWTitleBarBtn::IWTitleBarBtn;
+ void DrawSymbol(CDC* pDC, CRect rect) override;
+};
diff --git a/ILS_Window_Plugin/IWX11TitleBar.cpp b/ILS_Window_Plugin/IWX11TitleBar.cpp
new file mode 100644
index 0000000..f983189
--- /dev/null
+++ b/ILS_Window_Plugin/IWX11TitleBar.cpp
@@ -0,0 +1,60 @@
+#include "pch.h"
+#include "IWX11TitleBar.h"
+
+#include "IWX11IconifyBtn.h"
+#include "IWX11MenuBtn.h"
+#include "IWX11ResizeBtn.h"
+
+IWX11TitleBar::IWX11TitleBar(COLORREF backgroundColor, COLORREF textColor, IWTitleBarEventListener* listener)
+ : IWTitleBar(backgroundColor, 14, listener)
+{
+ this->iconifyButton = new IWX11IconifyBtn(backgroundColor);
+ this->menuButton = new IWX11MenuBtn(backgroundColor);
+ this->resizeButton = new IWX11ResizeBtn(backgroundColor);
+}
+
+void IWX11TitleBar::PositionButtons(const CRect& rect)
+{
+ const int margin = 6;
+ const int btnHeight = rect.Height() - 9;
+ const int btnWidth = btnHeight * 1.2;
+
+ // V center the buttons
+ const int top = (rect.Height() - btnHeight) / 2;
+ const int bottom = top + btnHeight;
+
+ int right = rect.right - 3;
+ int left = right - btnWidth;
+
+ // Position the resize button
+ CRect resizeButtonRect(left, top, right, bottom);
+ if (resizeButton->GetSafeHwnd())
+ resizeButton->MoveWindow(resizeButtonRect);
+
+ right = left - margin;
+ left = right - btnWidth;
+
+ // Position the menu button
+ CRect menuButtonRect(left, top, right, bottom);
+ if (menuButton->GetSafeHwnd())
+ menuButton->MoveWindow(menuButtonRect);
+
+ right = left - margin;
+ left = right - btnWidth;
+
+ // Position the iconify button
+ CRect iconifyButtonRect(left, top, right, bottom);
+ if (iconifyButton->GetSafeHwnd())
+ iconifyButton->MoveWindow(iconifyButtonRect);
+}
+
+void IWX11TitleBar::DrawTitle(CDC* pdc, CRect rect, CString title)
+{
+ CRect textArea = rect;
+ textArea.left += 5;
+ auto oldFont = pdc->SelectObject(this->font);
+ pdc->SetTextColor(this->textColor);
+ pdc->SetBkMode(TRANSPARENT);
+ pdc->DrawText(_T(title), -1, textArea, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
+ pdc->SelectObject(oldFont);
+}
diff --git a/ILS_Window_Plugin/IWX11TitleBar.h b/ILS_Window_Plugin/IWX11TitleBar.h
new file mode 100644
index 0000000..113f05b
--- /dev/null
+++ b/ILS_Window_Plugin/IWX11TitleBar.h
@@ -0,0 +1,16 @@
+#pragma once
+#include "IWTitleBar.h"
+
+class IWX11TitleBar : public IWTitleBar
+{
+public:
+ IWX11TitleBar(COLORREF backgroundColor, COLORREF textColor, IWTitleBarEventListener* listener);
+ virtual ~IWX11TitleBar() {}
+
+private:
+ void PositionButtons(const CRect& rect) override;
+ void DrawTitle(CDC* pdc, CRect rect, CString title) override;
+
+ COLORREF textColor;
+};
+
diff --git a/ILS_Window_Plugin/IWX11Window.cpp b/ILS_Window_Plugin/IWX11Window.cpp
new file mode 100644
index 0000000..b8c5a72
--- /dev/null
+++ b/ILS_Window_Plugin/IWX11Window.cpp
@@ -0,0 +1,24 @@
+#include "pch.h"
+#include "IWX11Window.h"
+#include "IWX11TitleBar.h"
+
+IWX11Window::IWX11Window(IWApproachDefinition selectedApproach, IWStyling styling)
+ : IWWindow(selectedApproach, styling, 26, 3, 1)
+{
+ this->titleBar = new IWX11TitleBar(windowBorderColor, textColor, this);
+}
+
+void IWX11Window::DrawBorder(CDC* pdc, CRect windowRect)
+{
+ // Draw the border
+ CPen pen(PS_SOLID, 1, RGB(0, 0, 0)); // Black pen for the border
+ CPen* oldPen = pdc->SelectObject(&pen);
+ pdc->SelectStockObject(NULL_BRUSH); // No fill for the rectangle
+ pdc->Rectangle(&windowRect);
+ pdc->SelectObject(oldPen);
+}
+
+int IWX11Window::GetEdgeCursorPosition(CPoint point)
+{
+ return 0;
+}
diff --git a/ILS_Window_Plugin/IWX11Window.h b/ILS_Window_Plugin/IWX11Window.h
new file mode 100644
index 0000000..874be80
--- /dev/null
+++ b/ILS_Window_Plugin/IWX11Window.h
@@ -0,0 +1,13 @@
+#pragma once
+#include "IWWindow.h"
+class IWX11Window :
+ public IWWindow
+{
+public:
+ IWX11Window(IWApproachDefinition selectedApproach, IWStyling styling);
+
+private:
+ void DrawBorder(CDC* pdc, CRect windowRect) override;
+ int GetEdgeCursorPosition(CPoint point) override;
+};
+
diff --git a/ILS_Window_Plugin/RenderUtils.h b/ILS_Window_Plugin/RenderUtils.h
new file mode 100644
index 0000000..a224c0b
--- /dev/null
+++ b/ILS_Window_Plugin/RenderUtils.h
@@ -0,0 +1,103 @@
+#pragma once
+
+#include
+
+inline void Draw3dRect(CDC* pDC, CRect rect, int steps, COLORREF lightColor, COLORREF darkColor)
+{
+ for (int drawnSunkSteps = 0; drawnSunkSteps < steps; drawnSunkSteps++) {
+ // Draw the top-left border (light color)
+ pDC->FillSolidRect(rect.left, rect.top, rect.Width(), 1, lightColor); // Top
+ pDC->FillSolidRect(rect.left, rect.top, 1, rect.Height(), lightColor); // Left
+
+ // Draw the bottom-right border (dark color)
+ pDC->FillSolidRect(rect.right - 1, rect.top, 1, rect.Height(), darkColor); // Right
+ pDC->FillSolidRect(rect.left, rect.bottom - 1, rect.Width(), 1, darkColor); // Bottom
+
+ rect.DeflateRect(1, 1);
+ }
+}
+
+inline void Draw3dCorner(CDC* pDC, CRect rect, int borderWidth, int steps, COLORREF lightColor, COLORREF darkColor, bool isTop, bool isLeft)
+{
+ CPen lightPen(PS_SOLID, 1, lightColor);
+ CPen darkPen(PS_SOLID, 1, darkColor);
+
+ for (int drawnSunkSteps = 0; drawnSunkSteps < steps; drawnSunkSteps++) {
+ if (isTop && isLeft)
+ {
+ // Light color
+ pDC->SelectObject(&lightPen);
+ pDC->MoveTo(rect.left, rect.bottom);
+ pDC->LineTo(rect.left, rect.top);
+ pDC->LineTo(rect.right, rect.top);
+
+ // Dark color
+ pDC->SelectObject(&darkPen);
+ pDC->MoveTo(rect.right, rect.top);
+ pDC->LineTo(rect.right, rect.top + borderWidth - drawnSunkSteps * 2 - 2);
+ pDC->LineTo(rect.left + borderWidth - drawnSunkSteps * 2 - 2, rect.top + borderWidth - drawnSunkSteps * 2 - 2);
+ pDC->LineTo(rect.left + borderWidth - drawnSunkSteps * 2 - 2, rect.bottom);
+ pDC->LineTo(rect.left, rect.bottom);
+ }
+ else if (isTop && !isLeft)
+ {
+ // Light color
+ pDC->SelectObject(&lightPen);
+ pDC->MoveTo(rect.left, rect.top + borderWidth - drawnSunkSteps * 2 - 2);
+ pDC->LineTo(rect.left, rect.top);
+ pDC->LineTo(rect.right, rect.top);
+
+ pDC->MoveTo(rect.right - borderWidth + drawnSunkSteps * 2 + 2, rect.bottom);
+ pDC->LineTo(rect.right - borderWidth + drawnSunkSteps * 2 + 2, rect.top + borderWidth - drawnSunkSteps * 2 - 2);
+
+ // Dark color
+ pDC->SelectObject(&darkPen);
+ pDC->MoveTo(rect.right, rect.top);
+ pDC->LineTo(rect.right, rect.bottom);
+ pDC->LineTo(rect.right - borderWidth + drawnSunkSteps * 2 + 2, rect.bottom);
+
+ pDC->MoveTo(rect.right - borderWidth + drawnSunkSteps * 2 + 2, rect.top + borderWidth - drawnSunkSteps * 2 - 2);
+ pDC->LineTo(rect.left, rect.top + borderWidth - drawnSunkSteps * 2 - 2);
+ }
+ else if (!isTop && !isLeft) // Bottom right
+ {
+ // Light color
+ pDC->SelectObject(&lightPen);
+ pDC->MoveTo(rect.left, rect.bottom);
+ pDC->LineTo(rect.left, rect.top);
+ pDC->LineTo(rect.right, rect.top);
+
+ pDC->MoveTo(rect.left, rect.bottom - borderWidth + drawnSunkSteps * 2 + 2);
+ pDC->LineTo(rect.right - borderWidth + drawnSunkSteps * 2 + 2, rect.bottom - borderWidth + drawnSunkSteps * 2 + 2);
+ pDC->LineTo(rect.right - borderWidth + drawnSunkSteps * 2 + 2, rect.top);
+
+ // Dark color
+ pDC->SelectObject(&darkPen);
+ pDC->MoveTo(rect.right, rect.top);
+ pDC->LineTo(rect.right, rect.bottom);
+ pDC->LineTo(rect.left, rect.bottom);
+ }
+ else if (!isTop && isLeft) // Bottom left
+ {
+ // Light color
+ pDC->SelectObject(&lightPen);
+ pDC->MoveTo(rect.left, rect.bottom);
+ pDC->LineTo(rect.left, rect.top);
+ pDC->LineTo(rect.left + borderWidth - drawnSunkSteps * 2 - 2, rect.top);
+
+ pDC->MoveTo(rect.right, rect.bottom - borderWidth + drawnSunkSteps * 2 + 2);
+ pDC->LineTo(rect.left + borderWidth - drawnSunkSteps * 2 - 2, rect.bottom - borderWidth + drawnSunkSteps * 2 + 2);
+
+ // Dark color
+ pDC->SelectObject(&darkPen);
+ pDC->MoveTo(rect.left, rect.bottom);
+ pDC->LineTo(rect.right, rect.bottom);
+ pDC->LineTo(rect.right, rect.bottom - borderWidth + drawnSunkSteps * 2 + 2);
+
+ pDC->MoveTo(rect.left + borderWidth - drawnSunkSteps * 2 - 2, rect.bottom - borderWidth + drawnSunkSteps * 2 + 2);
+ pDC->LineTo(rect.left + borderWidth - drawnSunkSteps * 2 - 2, rect.top);
+ }
+
+ rect.DeflateRect(1, 1);
+ }
+}
\ No newline at end of file
diff --git a/ILS_Window_Plugin/ReunderUtils.cpp b/ILS_Window_Plugin/ReunderUtils.cpp
new file mode 100644
index 0000000..d93b6e3
--- /dev/null
+++ b/ILS_Window_Plugin/ReunderUtils.cpp
@@ -0,0 +1,105 @@
+#include "RenderUtils.h"
+
+void Draw3dRect(CDC* pDC, CRect rect)
+{
+ // Colors for the sWunken effectrgb(150, 249, 252)
+ COLORREF lightColor = RGB(150, 249, 252); // Lighter shade for top/left
+ COLORREF darkColor = RGB(60, 111, 119); // Darker shade for bottom/right
+
+ for (int drawnSunkSteps = 0; drawnSunkSteps < 2; drawnSunkSteps++) {
+ // Draw the top-left border (light color)
+ pDC->FillSolidRect(rect.left, rect.top, rect.Width(), 1, lightColor); // Top
+ pDC->FillSolidRect(rect.left, rect.top, 1, rect.Height(), lightColor); // Left
+
+ // Draw the bottom-right border (dark color)
+ pDC->FillSolidRect(rect.right - 1, rect.top, 1, rect.Height(), darkColor); // Right
+ pDC->FillSolidRect(rect.left, rect.bottom - 1, rect.Width(), 1, darkColor); // Bottom
+
+ rect.DeflateRect(1, 1);
+ }
+}
+
+void Draw3dCorner(CDC* pDC, CRect rect, int borderWidth, COLORREF lightColor, COLORREF darkColor, bool isTop, bool isLeft)
+{
+ CPen lightPen(PS_SOLID, 1, lightColor);
+ CPen darkPen(PS_SOLID, 1, darkColor);
+
+ for (int drawnSunkSteps = 0; drawnSunkSteps < 2; drawnSunkSteps++) {
+ if (isTop && isLeft)
+ {
+ // Light color
+ pDC->SelectObject(&lightPen);
+ pDC->MoveTo(rect.left, rect.bottom);
+ pDC->LineTo(rect.left, rect.top);
+ pDC->LineTo(rect.right, rect.top);
+
+ // Dark color
+ pDC->SelectObject(&darkPen);
+ pDC->MoveTo(rect.right, rect.top);
+ pDC->LineTo(rect.right, rect.top + borderWidth - drawnSunkSteps * 2 - 2);
+ pDC->LineTo(rect.left + borderWidth - drawnSunkSteps * 2 - 2, rect.top + borderWidth - drawnSunkSteps * 2 - 2);
+ pDC->LineTo(rect.left + borderWidth - drawnSunkSteps * 2 - 2, rect.bottom);
+ pDC->LineTo(rect.left, rect.bottom);
+ }
+ else if (isTop && !isLeft)
+ {
+ // Light color
+ pDC->SelectObject(&lightPen);
+ pDC->MoveTo(rect.left, rect.top + borderWidth - drawnSunkSteps * 2 - 2);
+ pDC->LineTo(rect.left, rect.top);
+ pDC->LineTo(rect.right, rect.top);
+
+ pDC->MoveTo(rect.right - borderWidth + drawnSunkSteps * 2 + 2, rect.bottom);
+ pDC->LineTo(rect.right - borderWidth + drawnSunkSteps * 2 + 2, rect.top + borderWidth - drawnSunkSteps * 2 - 2);
+
+ // Dark color
+ pDC->SelectObject(&darkPen);
+ pDC->MoveTo(rect.right, rect.top);
+ pDC->LineTo(rect.right, rect.bottom);
+ pDC->LineTo(rect.right - borderWidth + drawnSunkSteps * 2 + 2, rect.bottom);
+
+ pDC->MoveTo(rect.right - borderWidth + drawnSunkSteps * 2 + 2, rect.top + borderWidth - drawnSunkSteps * 2 - 2);
+ pDC->LineTo(rect.left, rect.top + borderWidth - drawnSunkSteps * 2 - 2);
+ }
+ else if (!isTop && !isLeft) // Bottom right
+ {
+ // Light color
+ pDC->SelectObject(&lightPen);
+ pDC->MoveTo(rect.left, rect.bottom);
+ pDC->LineTo(rect.left, rect.top);
+ pDC->LineTo(rect.right, rect.top);
+
+ pDC->MoveTo(rect.left, rect.bottom - borderWidth + drawnSunkSteps * 2 + 2);
+ pDC->LineTo(rect.right - borderWidth + drawnSunkSteps * 2 + 2, rect.bottom - borderWidth + drawnSunkSteps * 2 + 2);
+ pDC->LineTo(rect.right - borderWidth + drawnSunkSteps * 2 + 2, rect.top);
+
+ // Dark color
+ pDC->SelectObject(&darkPen);
+ pDC->MoveTo(rect.right, rect.top);
+ pDC->LineTo(rect.right, rect.bottom);
+ pDC->LineTo(rect.left, rect.bottom);
+ }
+ else if (!isTop && isLeft) // Bottom left
+ {
+ // Light color
+ pDC->SelectObject(&lightPen);
+ pDC->MoveTo(rect.left, rect.bottom);
+ pDC->LineTo(rect.left, rect.top);
+ pDC->LineTo(rect.left + borderWidth - drawnSunkSteps * 2 - 2, rect.top);
+
+ pDC->MoveTo(rect.right, rect.bottom - borderWidth + drawnSunkSteps * 2 + 2);
+ pDC->LineTo(rect.left + borderWidth - drawnSunkSteps * 2 - 2, rect.bottom - borderWidth + drawnSunkSteps * 2 + 2);
+
+ // Dark color
+ pDC->SelectObject(&darkPen);
+ pDC->MoveTo(rect.left, rect.bottom);
+ pDC->LineTo(rect.right, rect.bottom);
+ pDC->LineTo(rect.right, rect.bottom - borderWidth + drawnSunkSteps * 2 + 2);
+
+ pDC->MoveTo(rect.left + borderWidth - drawnSunkSteps * 2 - 2, rect.bottom - borderWidth + drawnSunkSteps * 2 + 2);
+ pDC->LineTo(rect.left + borderWidth - drawnSunkSteps * 2 - 2, rect.top);
+ }
+
+ rect.DeflateRect(1, 1);
+ }
+}
\ No newline at end of file
diff --git a/README.md b/README.md
index 2102c63..3a37385 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# ILS window plugin for EuroScope
-Shows aircraft position relative to the ILS.
+Shows aircraft positions relative to the ILS.
## Installation and use
@@ -11,7 +11,7 @@ Shows aircraft position relative to the ILS.
By default the plugin will automatically open ILS windows based on the active arrival runways. It's also possible to open windows manually using `.ils ` (where `` refers to the `"title"` in the configuration file).
> [!NOTE]
-> **Limitation**: This plugin uses the altitude reported by the pilot clients. Depending on the pilot's weather settings and the flight simulator they are using, the height displayed in the ILS Window may not match what the pilot sees ([more info](https://forums.flightsimulator.com/t/vatsim-ivao-pilotedge-users-be-aware-of-an-important-bug/426142)).
+> The plugin applies cold temperature correction to improve aircraft height accuracy. This is necessary for newer flight simulators with advanced atmospheric models ([more info](https://forums.flightsimulator.com/t/vatsim-ivao-pilotedge-users-be-aware-of-an-important-bug/426142)). For pilots using older simulators or custom weather settings, the displayed height may be slightly different from what is shown in the simulator.
## Configuration
@@ -19,6 +19,8 @@ The plugin reads the JSON configuration file when the plugin is loaded. For deta
- `localizerCourse` must be the **true** heading in degrees
- `maxOffsetLeft` and `maxOffsetRight` is used to specify the maximum visible range (in nautical miles) left and right of the localizer. Most relevant for airports with paralell approaches.
+- `windowStyle` must be `"CDE"` (Common Desktop Environment) or `"X11"` (X Window System).
+- `defaultTagMode` must be set to `"squawk"` (shows only the SSR code) or `"callsign"`.
## Screenshot
diff --git a/Sample config/ILS_Window_Plugin-config.json b/Sample config/ILS_Window_Plugin-config.json
index 91be09e..7c55bd3 100644
--- a/Sample config/ILS_Window_Plugin-config.json
+++ b/Sample config/ILS_Window_Plugin-config.json
@@ -1,6 +1,7 @@
{
"behaviour": {
- "openWindowsBasedOnActiveRunways": true
+ "openWindowsBasedOnActiveRunways": true,
+ "windowStyle": "CDE"
},
"styling": {
"fontSize": 12,