From 73e4011e468753c5638666bb805b94700f0f9122 Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 12:01:20 +0000 Subject: [PATCH 01/44] add departure tracker --- vSMR/Constant.hpp | 421 +-- vSMR/Rimcas.cpp | 694 +++-- vSMR/Rimcas.hpp | 312 ++- vSMR/SMRRadar.cpp | 6773 +++++++++++++++++++++++++-------------------- vSMR/SMRRadar.hpp | 545 ++-- 5 files changed, 4807 insertions(+), 3938 deletions(-) diff --git a/vSMR/Constant.hpp b/vSMR/Constant.hpp index a9cda5784..7779686e5 100644 --- a/vSMR/Constant.hpp +++ b/vSMR/Constant.hpp @@ -1,14 +1,15 @@ #pragma once +#include "EuroScopePlugIn.h" #include "stdafx.h" #include -#include "EuroScopePlugIn.h" + #define _USE_MATH_DEFINES // ReSharper disable once CppUnusedIncludeDirective +#include #include -#include #include -#include +#include #define VSTRIPS_PORT 53487 @@ -24,248 +25,257 @@ const int TAG_FUNC_DATALINK_VOICE = 547; const int TAG_FUNC_DATALINK_RESET = 548; const int TAG_FUNC_DATALINK_MESSAGE = 549; - - -inline static bool startsWith(const char *pre, const char *str) -{ - size_t lenpre = strlen(pre), lenstr = strlen(str); - return lenstr < lenpre ? false : strncmp(pre, str, lenpre) == 0; +inline static bool startsWith(const char *pre, const char *str) { + size_t lenpre = strlen(pre), lenstr = strlen(str); + return lenstr < lenpre ? false : strncmp(pre, str, lenpre) == 0; }; -inline static void replaceAll(string& source, const string& from, const string& to) -{ - string newString; - newString.reserve(source.length()); // avoids a few memory allocations +inline static void replaceAll(string &source, const string &from, + const string &to) { + string newString; + newString.reserve(source.length()); // avoids a few memory allocations - string::size_type lastPos = 0; - string::size_type findPos; + string::size_type lastPos = 0; + string::size_type findPos; - while (string::npos != (findPos = source.find(from, lastPos))) - { - newString.append(source, lastPos, findPos - lastPos); - newString += to; - lastPos = findPos + from.length(); - } + while (string::npos != (findPos = source.find(from, lastPos))) { + newString.append(source, lastPos, findPos - lastPos); + newString += to; + lastPos = findPos + from.length(); + } - // Care for the rest after last occurrence - newString += source.substr(lastPos); + // Care for the rest after last occurrence + newString += source.substr(lastPos); - source.swap(newString); + source.swap(newString); }; -inline static Gdiplus::Rect CopyRect(CRect &rect) -{ - return Gdiplus::Rect(rect.left, rect.top, rect.Width(), rect.Height()); +inline static Gdiplus::Rect CopyRect(CRect &rect) { + return Gdiplus::Rect(rect.left, rect.top, rect.Width(), rect.Height()); }; -inline static std::vector &split(const std::string &s, char delim, std::vector &elems) { - std::stringstream ss(s); - std::string item; - while (std::getline(ss, item, delim)) { - elems.push_back(item); - } - return elems; +inline static std::vector &split(const std::string &s, char delim, + std::vector &elems) { + std::stringstream ss(s); + std::string item; + while (std::getline(ss, item, delim)) { + elems.push_back(item); + } + return elems; }; inline static std::vector split(const std::string &s, char delim) { - std::vector elems; - split(s, delim, elems); - return elems; + std::vector elems; + split(s, delim, elems); + return elems; }; -inline static double TrueBearing(CPosition pos1, CPosition pos2) -{ - const float PI = float(atan2(0, -1)); - - // returns the true bearing from pos1 to pos2 - double lat1 = pos1.m_Latitude / 180 * PI; - double lat2 = pos2.m_Latitude / 180 * PI; - double lon1 = pos1.m_Longitude / 180 * PI; - double lon2 = pos2.m_Longitude / 180 * PI; - double dir = fmod(atan2(sin(lon2 - lon1) * cos(lat2), cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(lon2 - lon1)), 2 * PI) * 180 / PI; - - return dir / 180 * PI; +inline static double TrueBearing(CPosition pos1, CPosition pos2) { + const float PI = float(atan2(0, -1)); + + // returns the true bearing from pos1 to pos2 + double lat1 = pos1.m_Latitude / 180 * PI; + double lat2 = pos2.m_Latitude / 180 * PI; + double lon1 = pos1.m_Longitude / 180 * PI; + double lon2 = pos2.m_Longitude / 180 * PI; + double dir = fmod(atan2(sin(lon2 - lon1) * cos(lat2), + cos(lat1) * sin(lat2) - + sin(lat1) * cos(lat2) * cos(lon2 - lon1)), + 2 * PI) * + 180 / PI; + + return dir / 180 * PI; }; -inline static POINT rotate_point(POINT p, double angle, POINT c) -{ - double sine = sin(angle * M_PI / 180); - double cosi = cos(angle * M_PI / 180); +inline static POINT rotate_point(POINT p, double angle, POINT c) { + double sine = sin(angle * M_PI / 180); + double cosi = cos(angle * M_PI / 180); - // translate point back to origin: - p.x -= c.x; - p.y -= c.y; + // translate point back to origin: + p.x -= c.x; + p.y -= c.y; - // rotate point - double xnew = p.x * cosi - p.y * sine; - double ynew = p.x * sine + p.y * cosi; + // rotate point + double xnew = p.x * cosi - p.y * sine; + double ynew = p.x * sine + p.y * cosi; - // translate point back: - p.x = LONG(xnew + c.x); - p.y = LONG(ynew + c.y); - return p; + // translate point back: + p.x = LONG(xnew + c.x); + p.y = LONG(ynew + c.y); + return p; } -inline static bool RectIntersect(RECT RectA, RECT RectB) -{ - if (RectA.left < RectB.right && RectA.right > RectB.left && - RectA.bottom < RectB.top && RectA.top > RectB.bottom) - { - return true; - } - return false; +inline static bool RectIntersect(RECT RectA, RECT RectB) { + if (RectA.left < RectB.right && RectA.right > RectB.left && + RectA.bottom < RectB.top && RectA.top > RectB.bottom) { + return true; + } + return false; } -inline static double DistancePts(POINT p0, POINT p1) -{ - return sqrt((p1.x - p0.x)*(p1.x - p0.x) + (p1.y - p0.y)*(p1.y - p0.y)); +inline static double DistancePts(POINT p0, POINT p1) { + return sqrt((p1.x - p0.x) * (p1.x - p0.x) + (p1.y - p0.y) * (p1.y - p0.y)); } -inline static int Is_Left(const POINT &p0, const POINT &p1, const POINT &point) -{ - return ((p1.x - p0.x) * (point.y - p0.y) - - (point.x - p0.x) * (p1.y - p0.y)); +inline static int Is_Left(const POINT &p0, const POINT &p1, + const POINT &point) { + return ((p1.x - p0.x) * (point.y - p0.y) - (point.x - p0.x) * (p1.y - p0.y)); }; -inline static bool Is_Inside(const POINT &point, const std::vector &points_list) -{ - // The winding number counter. - int winding_number = 0; - - // Loop through all edges of the polygon. - typedef std::vector::size_type size_type; - - size_type size = points_list.size(); - - for (size_type i = 0; i < size; ++i) // Edge from point1 to points_list[i+1] - { - POINT point1(points_list[i]); - POINT point2; - - // Wrap? - if (i == (size - 1)) - { - point2 = points_list[0]; - } - else - { - point2 = points_list[i + 1]; - } - - if (point1.y <= point.y) // start y <= point.y - { - if (point2.y > point.y) // An upward crossing - { - if (Is_Left(point1, point2, point) > 0) // Point left of edge - { - ++winding_number; // Have a valid up intersect - } - } - } - else - { - // start y > point.y (no test needed) - if (point2.y <= point.y) // A downward crossing - { - if (Is_Left(point1, point2, point) < 0) // Point right of edge - { - --winding_number; // Have a valid down intersect - } - } - } - } - - return (winding_number != 0); +inline static bool Is_Inside(const POINT &point, + const std::vector &points_list) { + // The winding number counter. + int winding_number = 0; + + // Loop through all edges of the polygon. + typedef std::vector::size_type size_type; + + size_type size = points_list.size(); + + for (size_type i = 0; i < size; ++i) // Edge from point1 to points_list[i+1] + { + POINT point1(points_list[i]); + POINT point2; + + // Wrap? + if (i == (size - 1)) { + point2 = points_list[0]; + } else { + point2 = points_list[i + 1]; + } + + if (point1.y <= point.y) // start y <= point.y + { + if (point2.y > point.y) // An upward crossing + { + if (Is_Left(point1, point2, point) > 0) // Point left of edge + { + ++winding_number; // Have a valid up intersect + } + } + } else { + // start y > point.y (no test needed) + if (point2.y <= point.y) // A downward crossing + { + if (Is_Left(point1, point2, point) < 0) // Point right of edge + { + --winding_number; // Have a valid down intersect + } + } + } + } + + return (winding_number != 0); }; -// Liang-Barsky function by Daniel White @ http://www.skytopia.com/project/articles/compsci/clipping.html -// This function inputs 8 numbers, and outputs 4 new numbers (plus a boolean value to say whether the clipped line is drawn at all). +// Liang-Barsky function by Daniel White @ +// http://www.skytopia.com/project/articles/compsci/clipping.html This function +// inputs 8 numbers, and outputs 4 new numbers (plus a boolean value to say +// whether the clipped line is drawn at all). // -inline static bool LiangBarsky(RECT Area, POINT fromSrc, POINT toSrc, POINT &ClipFrom, POINT &ClipTo) // The output values, so declare these outside. +inline static bool +LiangBarsky(RECT Area, POINT fromSrc, POINT toSrc, POINT &ClipFrom, + POINT &ClipTo) // The output values, so declare these outside. { - double edgeLeft, edgeRight, edgeBottom, edgeTop, x0src, y0src, x1src, y1src; - - edgeLeft = Area.left; - edgeRight = Area.right; - edgeBottom = Area.top; - edgeTop = Area.bottom; - - x0src = fromSrc.x; - y0src = fromSrc.y; - x1src = toSrc.x; - y1src = toSrc.y; - - double t0 = 0.0; double t1 = 1.0; - double xdelta = x1src - x0src; - double ydelta = y1src - y0src; - double p = 0, q = 0, r; - - for (int edge = 0; edge<4; edge++) { // Traverse through left, right, bottom, top edges. - if (edge == 0) { p = -xdelta; q = -(edgeLeft - x0src); } - if (edge == 1) { p = xdelta; q = (edgeRight - x0src); } - if (edge == 2) { p = -ydelta; q = -(edgeBottom - y0src); } - if (edge == 3) { p = ydelta; q = (edgeTop - y0src); } - r = q / p; - if (p == 0 && q<0) return false; // Don't draw line at all. (parallel line outside) - - if (p<0) { - if (r>t1) return false; // Don't draw line at all. - else if (r>t0) t0 = r; // Line is clipped! - } - else if (p>0) { - if (r t1) + return false; // Don't draw line at all. + else if (r > t0) + t0 = r; // Line is clipped! + } else if (p > 0) { + if (r < t0) + return false; // Don't draw line at all. + else if (r < t1) + t1 = r; // Line is clipped! + } + } + + ClipFrom.x = long(x0src + t0 * xdelta); + ClipFrom.y = long(y0src + t0 * ydelta); + ClipTo.x = long(x0src + t1 * xdelta); + ClipTo.y = long(y0src + t1 * ydelta); + + return true; // (clipped) line is drawn }; -static bool mouseWithin(POINT mouseLocation,CRect rect) { - if (mouseLocation.x >= rect.left + 1 && mouseLocation.x <= rect.right - 1 && mouseLocation.y >= rect.top + 1 && mouseLocation.y <= rect.bottom - 1) - return true; - return false; +static bool mouseWithin(POINT mouseLocation, CRect rect) { + if (mouseLocation.x >= rect.left + 1 && mouseLocation.x <= rect.right - 1 && + mouseLocation.y >= rect.top + 1 && mouseLocation.y <= rect.bottom - 1) + return true; + return false; } //---Radians----------------------------------------- -inline static double DegToRad(double x) -{ - return x / 180.0 * M_PI; -}; +inline static double DegToRad(double x) { return x / 180.0 * M_PI; }; -inline static double RadToDeg(double x) -{ - return x / M_PI * 180.0; -}; +inline static double RadToDeg(double x) { return x / M_PI * 180.0; }; -inline static CPosition BetterHarversine(CPosition init, double angle, double meters) -{ - CPosition newPos; +inline static CPosition BetterHarversine(CPosition init, double angle, + double meters) { + CPosition newPos; - double d = (meters*0.00053996) / 60 * M_PI / 180; - double trk = DegToRad(angle); - double lat0 = DegToRad(init.m_Latitude); - double lon0 = DegToRad(init.m_Longitude); + double d = (meters * 0.00053996) / 60 * M_PI / 180; + double trk = DegToRad(angle); + double lat0 = DegToRad(init.m_Latitude); + double lon0 = DegToRad(init.m_Longitude); - double lat = asin(sin(lat0) * cos(d) + cos(lat0) * sin(d) * cos(trk)); - double lon = cos(lat) == 0 ? lon0 : fmod(lon0 + asin(sin(trk) * sin(d) / cos(lat)) + M_PI, 2 * M_PI) - M_PI; + double lat = asin(sin(lat0) * cos(d) + cos(lat0) * sin(d) * cos(trk)); + double lon = + cos(lat) == 0 + ? lon0 + : fmod(lon0 + asin(sin(trk) * sin(d) / cos(lat)) + M_PI, 2 * M_PI) - + M_PI; - newPos.m_Latitude = RadToDeg(lat); - newPos.m_Longitude = RadToDeg(lon); + newPos.m_Latitude = RadToDeg(lat); + newPos.m_Longitude = RadToDeg(lon); - return newPos; + return newPos; }; -inline static string padWithZeros(int padding, int s) -{ - stringstream ss; - ss << setfill('0') << setw(padding) << s; - return ss.str(); +inline static string padWithZeros(int padding, int s) { + stringstream ss; + ss << setfill('0') << setw(padding) << s; + return ss.str(); }; // @@ -329,6 +339,17 @@ const int RIMCAS_UPDATEROTATE = 6025; const int RIMCAS_UPDATEROTATE1 = 6026; const int RIMCAS_UPDATEROTATE2 = 6027; +const int RIMCAS_DEP_WINDOW = 7001; +const int RIMCAS_DEP_WINDOW_ROW = 7002; +const int RIMCAS_DEP_WINDOW_TOGGLE = 8040; +const int RIMCAS_DEP_WINDOW_DURATION = 8041; +const int RIMCAS_DEP_WINDOW_COL_CALLSIGN = 8042; +const int RIMCAS_DEP_WINDOW_COL_DEST = 8043; +const int RIMCAS_DEP_WINDOW_COL_ACTYPE = 8044; +const int RIMCAS_DEP_WINDOW_COL_WAKE = 8045; +const int RIMCAS_DEP_WINDOW_COL_FREQ = 8046; +const int RIMCAS_DEP_WINDOW_COL_TIME = 8047; + const int APPWINDOW_BASE = 8887; const int APPWINDOW_ONE = 8888; const int APPWINDOW_TWO = 8889; diff --git a/vSMR/Rimcas.cpp b/vSMR/Rimcas.cpp index a7725555e..69819f618 100644 --- a/vSMR/Rimcas.cpp +++ b/vSMR/Rimcas.cpp @@ -1,332 +1,434 @@ -#include "stdafx.h" #include "Rimcas.hpp" -CRimcas::CRimcas() -{ -} +CRimcas::CRimcas() {} -CRimcas::~CRimcas() -{ - Reset(); -} +CRimcas::~CRimcas() { Reset(); } void CRimcas::Reset() { - Logger::info(string(__FUNCSIG__)); - RunwayAreas.clear(); - AcColor.clear(); - AcOnRunway.clear(); - TimeTable.clear(); - MonitoredRunwayArr.clear(); - MonitoredRunwayDep.clear(); - ApproachingAircrafts.clear(); + Logger::info(string(__FUNCSIG__)); + RunwayAreas.clear(); + AcColor.clear(); + AcOnRunway.clear(); + TimeTable.clear(); + MonitoredRunwayArr.clear(); + MonitoredRunwayDep.clear(); + ApproachingAircrafts.clear(); + DepartedAircraft.clear(); + AircraftWasOnGround.clear(); } void CRimcas::OnRefreshBegin(bool isLVP) { - Logger::info(string(__FUNCSIG__)); - AcColor.clear(); - AcOnRunway.clear(); - TimeTable.clear(); - ApproachingAircrafts.clear(); - this->IsLVP = isLVP; + Logger::info(string(__FUNCSIG__)); + AcColor.clear(); + AcOnRunway.clear(); + TimeTable.clear(); + ApproachingAircrafts.clear(); + this->IsLVP = isLVP; } -void CRimcas::OnRefresh(CRadarTarget Rt, CRadarScreen *instance, bool isCorrelated, string acType, string acStand) { - Logger::info(string(__FUNCSIG__)); - GetAcInRunwayArea(Rt, instance); - GetAcInRunwayAreaSoon(Rt, instance, isCorrelated, acType, acStand); +void CRimcas::OnRefresh(CRadarTarget Rt, CRadarScreen *instance, + bool isCorrelated, string acType, string acStand) { + Logger::info(string(__FUNCSIG__)); + GetAcInRunwayArea(Rt, instance); + GetAcInRunwayAreaSoon(Rt, instance, isCorrelated, acType, acStand); } -void CRimcas::AddRunwayArea(CRadarScreen *instance, string runway_name1, string runway_name2, vector Definition) { - Logger::info(string(__FUNCSIG__)); - string Name = runway_name1 + " / " + runway_name2; - - RunwayAreaType Runway; - Runway.Name = Name; - Runway.Definition = Definition; +void CRimcas::AddRunwayArea(CRadarScreen *instance, string runway_name1, + string runway_name2, vector Definition) { + Logger::info(string(__FUNCSIG__)); + string Name = runway_name1 + " / " + runway_name2; + + RunwayAreaType Runway; + Runway.Name = Name; + Runway.Definition = Definition; - RunwayAreas[Name] = Runway; + RunwayAreas[Name] = Runway; } string CRimcas::GetAcInRunwayArea(CRadarTarget Ac, CRadarScreen *instance) { - Logger::info(string(__FUNCSIG__)); - int AltitudeDif = Ac.GetPosition().GetFlightLevel() - Ac.GetPreviousPosition(Ac.GetPosition()).GetFlightLevel(); - if (!Ac.GetPosition().GetTransponderC()) - AltitudeDif = 0; - - if (Ac.GetGS() > 160 || AltitudeDif > 200) - return string_false; - - POINT AcPosPix = instance->ConvertCoordFromPositionToPixel(Ac.GetPosition().GetPosition()); - - for (std::map::iterator it = RunwayAreas.begin(); it != RunwayAreas.end(); ++it) - { - if (!MonitoredRunwayDep[string(it->first)]) - continue; - - vector RunwayOnScreen; - - for (auto &Point : it->second.Definition) - { - RunwayOnScreen.push_back(instance->ConvertCoordFromPositionToPixel(Point)); - } - - if (Is_Inside(AcPosPix, RunwayOnScreen)) { - AcOnRunway.insert(std::pair(it->first, Ac.GetCallsign())); - return string(it->first); - } - } - - return string_false; + Logger::info(string(__FUNCSIG__)); + int AltitudeDif = Ac.GetPosition().GetFlightLevel() - + Ac.GetPreviousPosition(Ac.GetPosition()).GetFlightLevel(); + if (!Ac.GetPosition().GetTransponderC()) + AltitudeDif = 0; + + if (Ac.GetGS() > 160 || AltitudeDif > 200) + return string_false; + + POINT AcPosPix = + instance->ConvertCoordFromPositionToPixel(Ac.GetPosition().GetPosition()); + + for (std::map::iterator it = RunwayAreas.begin(); + it != RunwayAreas.end(); ++it) { + if (!MonitoredRunwayDep[string(it->first)]) + continue; + + vector RunwayOnScreen; + + for (auto &Point : it->second.Definition) { + RunwayOnScreen.push_back( + instance->ConvertCoordFromPositionToPixel(Point)); + } + + if (Is_Inside(AcPosPix, RunwayOnScreen)) { + AcOnRunway.insert(std::pair(it->first, Ac.GetCallsign())); + return string(it->first); + } + } + + return string_false; } -string CRimcas::GetAcInRunwayAreaSoon(CRadarTarget Ac, CRadarScreen *instance, bool isCorrelated, string acType, string acStand) { - Logger::info(string(__FUNCSIG__)); - int AltitudeDif = Ac.GetPosition().GetFlightLevel() - Ac.GetPreviousPosition(Ac.GetPosition()).GetFlightLevel(); - if (!Ac.GetPosition().GetTransponderC()) - AltitudeDif = 0; - - // Making sure the AC is airborne and not climbing, but below transition - if (Ac.GetGS() < 50 || - AltitudeDif > 50 || - Ac.GetPosition().GetPressureAltitude() > instance->GetPlugIn()->GetTransitionAltitude()) - return string_false; - - // If the AC is already on the runway, then there is no point in this step - if (isAcOnRunway(Ac.GetCallsign())) - return string_false; - - POINT AcPosPix = instance->ConvertCoordFromPositionToPixel(Ac.GetPosition().GetPosition()); - - for (std::map::iterator it = RunwayAreas.begin(); it != RunwayAreas.end(); ++it) - { - if (!MonitoredRunwayArr[it->first]) - continue; - - // We need to know when and if the AC is going to enter the runway within 5 minutes (by steps of 10 seconds - - vector RunwayOnScreen; - - for (auto &Point : it->second.Definition) - { - RunwayOnScreen.push_back(instance->ConvertCoordFromPositionToPixel(Point)); - } - - double AcSpeed = Ac.GetPosition().GetReportedGS(); - if (!Ac.GetPosition().GetTransponderC()) - AcSpeed = Ac.GetGS(); - - for (int t = 5; t <= 300; t+= 5) - { - double distance = Ac.GetPosition().GetReportedGS()*0.514444*t; - - // We tolerate up 2 degree variations to the runway at long range (> 120 s) - // And 3 degrees after (<= 120 t) - - bool isGoingToLand = false; - int AngleMin = -2; - int AngleMax = 2; - if (t <= 120) - { - AngleMin = -3; - AngleMax = 3; - } - - for (int a = AngleMin; a <= AngleMax; a++) - { - POINT PredictedPosition = instance->ConvertCoordFromPositionToPixel( - BetterHarversine(Ac.GetPosition().GetPosition(), fmod(Ac.GetTrackHeading() + a, 360), distance)); - isGoingToLand = Is_Inside(PredictedPosition, RunwayOnScreen); - - if (isGoingToLand) - break; - } - - if (isGoingToLand) - { - // The aircraft is going to be on the runway, we need to decide where it needs to be shown on the AIW - bool first = true; - vector Definiton = CountdownDefinition; - if (IsLVP) - Definiton = CountdownDefinitionLVP; - for (size_t k = 0; k < Definiton.size(); k++) - { - int Time = Definiton.at(k); - - int PreviousTime = 0; - if (first) - { - PreviousTime = Time + 15; - first = false; - } - else - { - PreviousTime = Definiton.at(k-1); - } - if (t < PreviousTime && t >= Time) - { - AircraftInfo info; - info.callsign = Ac.GetCallsign(); - info.type = acType; - info.stand = acStand; - TimeTable[it->first][Time] = info; - break; - } - } - - // If the AC is xx seconds away from the runway, we consider him on it - - int StageTwoTrigger = 20; - if (IsLVP) - StageTwoTrigger = 30; - - if (t <= StageTwoTrigger) - AcOnRunway.insert(std::pair(it->first, Ac.GetCallsign())); - - // If the AC is 45 seconds away from the runway, we consider him approaching - - if (t > StageTwoTrigger && t <= 45) - ApproachingAircrafts.insert(std::pair(it->first, Ac.GetCallsign())); - - return Ac.GetCallsign(); - } - } - } - - return CRimcas::string_false; +string CRimcas::GetAcInRunwayAreaSoon(CRadarTarget Ac, CRadarScreen *instance, + bool isCorrelated, string acType, + string acStand) { + Logger::info(string(__FUNCSIG__)); + int AltitudeDif = Ac.GetPosition().GetFlightLevel() - + Ac.GetPreviousPosition(Ac.GetPosition()).GetFlightLevel(); + if (!Ac.GetPosition().GetTransponderC()) + AltitudeDif = 0; + + // Making sure the AC is airborne and not climbing, but below transition + if (Ac.GetGS() < 50 || AltitudeDif > 50 || + Ac.GetPosition().GetPressureAltitude() > + instance->GetPlugIn()->GetTransitionAltitude()) + return string_false; + + // If the AC is already on the runway, then there is no point in this step + if (isAcOnRunway(Ac.GetCallsign())) + return string_false; + + POINT AcPosPix = + instance->ConvertCoordFromPositionToPixel(Ac.GetPosition().GetPosition()); + + for (std::map::iterator it = RunwayAreas.begin(); + it != RunwayAreas.end(); ++it) { + if (!MonitoredRunwayArr[it->first]) + continue; + + // We need to know when and if the AC is going to enter the runway within 5 + // minutes (by steps of 10 seconds + + vector RunwayOnScreen; + + for (auto &Point : it->second.Definition) { + RunwayOnScreen.push_back( + instance->ConvertCoordFromPositionToPixel(Point)); + } + + double AcSpeed = Ac.GetPosition().GetReportedGS(); + if (!Ac.GetPosition().GetTransponderC()) + AcSpeed = Ac.GetGS(); + + for (int t = 5; t <= 300; t += 5) { + double distance = Ac.GetPosition().GetReportedGS() * 0.514444 * t; + + // We tolerate up 2 degree variations to the runway at long range (> 120 + // s) And 3 degrees after (<= 120 t) + + bool isGoingToLand = false; + int AngleMin = -2; + int AngleMax = 2; + if (t <= 120) { + AngleMin = -3; + AngleMax = 3; + } + + for (int a = AngleMin; a <= AngleMax; a++) { + POINT PredictedPosition = instance->ConvertCoordFromPositionToPixel( + BetterHarversine(Ac.GetPosition().GetPosition(), + fmod(Ac.GetTrackHeading() + a, 360), distance)); + isGoingToLand = Is_Inside(PredictedPosition, RunwayOnScreen); + + if (isGoingToLand) + break; + } + + if (isGoingToLand) { + // The aircraft is going to be on the runway, we need to decide where it + // needs to be shown on the AIW + bool first = true; + vector Definiton = CountdownDefinition; + if (IsLVP) + Definiton = CountdownDefinitionLVP; + for (size_t k = 0; k < Definiton.size(); k++) { + int Time = Definiton.at(k); + + int PreviousTime = 0; + if (first) { + PreviousTime = Time + 15; + first = false; + } else { + PreviousTime = Definiton.at(k - 1); + } + if (t < PreviousTime && t >= Time) { + AircraftInfo info; + info.callsign = Ac.GetCallsign(); + info.type = acType; + info.stand = acStand; + TimeTable[it->first][Time] = info; + break; + } + } + + // If the AC is xx seconds away from the runway, we consider him on it + + int StageTwoTrigger = 20; + if (IsLVP) + StageTwoTrigger = 30; + + if (t <= StageTwoTrigger) + AcOnRunway.insert( + std::pair(it->first, Ac.GetCallsign())); + + // If the AC is 45 seconds away from the runway, we consider him + // approaching + + if (t > StageTwoTrigger && t <= 45) + ApproachingAircrafts.insert( + std::pair(it->first, Ac.GetCallsign())); + + return Ac.GetCallsign(); + } + } + } + + return CRimcas::string_false; } -vector CRimcas::GetRunwayArea(CPosition Left, CPosition Right, float hwidth) { - Logger::info(string(__FUNCSIG__)); - vector out; - - double RunwayBearing = RadToDeg(TrueBearing(Left, Right)); +vector CRimcas::GetRunwayArea(CPosition Left, CPosition Right, + float hwidth) { + Logger::info(string(__FUNCSIG__)); + vector out; + + double RunwayBearing = RadToDeg(TrueBearing(Left, Right)); - out.push_back(BetterHarversine(Left, fmod(RunwayBearing + 90, 360), hwidth)); // Bottom Left - out.push_back(BetterHarversine(Right, fmod(RunwayBearing + 90, 360), hwidth)); // Bottom Right - out.push_back(BetterHarversine(Right, fmod(RunwayBearing - 90, 360), hwidth)); // Top Right - out.push_back(BetterHarversine(Left, fmod(RunwayBearing - 90, 360), hwidth)); // Top Left + out.push_back(BetterHarversine(Left, fmod(RunwayBearing + 90, 360), + hwidth)); // Bottom Left + out.push_back(BetterHarversine(Right, fmod(RunwayBearing + 90, 360), + hwidth)); // Bottom Right + out.push_back(BetterHarversine(Right, fmod(RunwayBearing - 90, 360), + hwidth)); // Top Right + out.push_back(BetterHarversine(Left, fmod(RunwayBearing - 90, 360), + hwidth)); // Top Left - return out; + return out; } void CRimcas::OnRefreshEnd(CRadarScreen *instance, int threshold) { - Logger::info(string(__FUNCSIG__)); - for (map::iterator it = RunwayAreas.begin(); it != RunwayAreas.end(); ++it) - { - - if (!MonitoredRunwayArr[string(it->first)] && !MonitoredRunwayDep[string(it->first)]) - continue; - - bool isOnClosedRunway = false; - if (ClosedRunway.find(it->first) != ClosedRunway.end()) { - if (ClosedRunway[it->first]) - isOnClosedRunway = true; - } - - bool isAnotherAcApproaching = ApproachingAircrafts.count(it->first) > 0; - - if (AcOnRunway.count(it->first) > 1 || isOnClosedRunway || isAnotherAcApproaching) { - - auto AcOnRunwayRange = AcOnRunway.equal_range(it->first); - - for (map::iterator it2 = AcOnRunwayRange.first; it2 != AcOnRunwayRange.second; ++it2) - { - if (isOnClosedRunway) { - AcColor[it2->second] = StageTwo; - } else - { - if (instance->GetPlugIn()->RadarTargetSelect(it2->second.c_str()).GetGS() > threshold) - { - // If the aircraft is on the runway and stage two, we check if - // the aircraft is going towards any aircraft thats on the runway - // if not, we don't display the warning - bool triggerStageTwo = false; - CRadarTarget rd1 = instance->GetPlugIn()->RadarTargetSelect(it2->second.c_str()); - CRadarTargetPositionData currentRd1 = rd1.GetPosition(); - for (map::iterator it3 = AcOnRunwayRange.first; it3 != AcOnRunwayRange.second; ++it3) - { - CRadarTarget rd2 = instance->GetPlugIn()->RadarTargetSelect(it3->second.c_str()); - - double currentDist = currentRd1.GetPosition().DistanceTo(rd2.GetPosition().GetPosition()); - double oldDist = rd1.GetPreviousPosition(currentRd1).GetPosition() - .DistanceTo(rd2.GetPreviousPosition(rd2.GetPosition()).GetPosition()); - - if (currentDist < oldDist) - { - triggerStageTwo = true; - break; - } - } - - if (triggerStageTwo) - AcColor[it2->second] = StageTwo; - } else - { - AcColor[it2->second] = StageOne; - } - } - } - - for (auto &ac : ApproachingAircrafts) - { - if (ac.first == it->first && AcOnRunway.count(it->first) > 1) - AcColor[ac.second] = StageOne; - - if (ac.first == it->first && isOnClosedRunway) - AcColor[ac.second] = StageTwo; - } - } - - } - + Logger::info(string(__FUNCSIG__)); + for (map::iterator it = RunwayAreas.begin(); + it != RunwayAreas.end(); ++it) { + + if (!MonitoredRunwayArr[string(it->first)] && + !MonitoredRunwayDep[string(it->first)]) + continue; + + bool isOnClosedRunway = false; + if (ClosedRunway.find(it->first) != ClosedRunway.end()) { + if (ClosedRunway[it->first]) + isOnClosedRunway = true; + } + + bool isAnotherAcApproaching = ApproachingAircrafts.count(it->first) > 0; + + if (AcOnRunway.count(it->first) > 1 || isOnClosedRunway || + isAnotherAcApproaching) { + + auto AcOnRunwayRange = AcOnRunway.equal_range(it->first); + + for (map::iterator it2 = AcOnRunwayRange.first; + it2 != AcOnRunwayRange.second; ++it2) { + if (isOnClosedRunway) { + AcColor[it2->second] = StageTwo; + } else { + if (instance->GetPlugIn() + ->RadarTargetSelect(it2->second.c_str()) + .GetGS() > threshold) { + // If the aircraft is on the runway and stage two, we check if + // the aircraft is going towards any aircraft thats on the runway + // if not, we don't display the warning + bool triggerStageTwo = false; + CRadarTarget rd1 = + instance->GetPlugIn()->RadarTargetSelect(it2->second.c_str()); + CRadarTargetPositionData currentRd1 = rd1.GetPosition(); + for (map::iterator it3 = AcOnRunwayRange.first; + it3 != AcOnRunwayRange.second; ++it3) { + CRadarTarget rd2 = + instance->GetPlugIn()->RadarTargetSelect(it3->second.c_str()); + + double currentDist = currentRd1.GetPosition().DistanceTo( + rd2.GetPosition().GetPosition()); + double oldDist = + rd1.GetPreviousPosition(currentRd1) + .GetPosition() + .DistanceTo(rd2.GetPreviousPosition(rd2.GetPosition()) + .GetPosition()); + + if (currentDist < oldDist) { + triggerStageTwo = true; + break; + } + } + + if (triggerStageTwo) + AcColor[it2->second] = StageTwo; + } else { + AcColor[it2->second] = StageOne; + } + } + } + + for (auto &ac : ApproachingAircrafts) { + if (ac.first == it->first && AcOnRunway.count(it->first) > 1) + AcColor[ac.second] = StageOne; + + if (ac.first == it->first && isOnClosedRunway) + AcColor[ac.second] = StageTwo; + } + } + } } bool CRimcas::isAcOnRunway(string callsign) { - Logger::info(string(__FUNCSIG__)); - for (std::map::iterator it = AcOnRunway.begin(); it != AcOnRunway.end(); ++it) - { - if (it->second == callsign) - return true; - } - - return false; + Logger::info(string(__FUNCSIG__)); + for (std::map::iterator it = AcOnRunway.begin(); + it != AcOnRunway.end(); ++it) { + if (it->second == callsign) + return true; + } + + return false; } -CRimcas::RimcasAlertTypes CRimcas::getAlert(string callsign) -{ - Logger::info(string(__FUNCSIG__)); - if (AcColor.find(callsign) == AcColor.end()) - return NoAlert; +CRimcas::RimcasAlertTypes CRimcas::getAlert(string callsign) { + Logger::info(string(__FUNCSIG__)); + if (AcColor.find(callsign) == AcColor.end()) + return NoAlert; + + return AcColor[callsign]; +} - return AcColor[callsign]; +Color CRimcas::GetAircraftColor(string AcCallsign, Color StandardColor, + Color OnRunwayColor, Color RimcasStageOne, + Color RimcasStageTwo) { + Logger::info(string(__FUNCSIG__)); + if (AcColor.find(AcCallsign) == AcColor.end()) { + if (isAcOnRunway(AcCallsign)) { + return OnRunwayColor; + } else { + return StandardColor; + } + } else { + if (AcColor[AcCallsign] == StageOne) { + return RimcasStageOne; + } else { + return RimcasStageTwo; + } + } } -Color CRimcas::GetAircraftColor(string AcCallsign, Color StandardColor, Color OnRunwayColor, Color RimcasStageOne, Color RimcasStageTwo) { - Logger::info(string(__FUNCSIG__)); - if (AcColor.find(AcCallsign) == AcColor.end()) { - if (isAcOnRunway(AcCallsign)) { - return OnRunwayColor; - } - else { - return StandardColor; - } - } - else { - if (AcColor[AcCallsign] == StageOne) { - return RimcasStageOne; - } - else { - return RimcasStageTwo; - } - } +Color CRimcas::GetAircraftColor(string AcCallsign, Color StandardColor, + Color OnRunwayColor) { + Logger::info(string(__FUNCSIG__)); + if (isAcOnRunway(AcCallsign)) { + return OnRunwayColor; + } else { + return StandardColor; + } } -Color CRimcas::GetAircraftColor(string AcCallsign, Color StandardColor, Color OnRunwayColor) { - Logger::info(string(__FUNCSIG__)); - if (isAcOnRunway(AcCallsign)) { - return OnRunwayColor; - } - else { - return StandardColor; - } +void CRimcas::TrackDeparture(CRadarTarget Rt, CFlightPlan fp, + CRadarScreen *instance, string activeAirport) { + Logger::info(string(__FUNCSIG__)); + + if (!Rt.IsValid() || !fp.IsValid()) + return; + + string callsign = Rt.GetCallsign(); + + string origin = fp.GetFlightPlanData().GetOrigin(); + if (origin != activeAirport) + return; + + int reportedGS = Rt.GetPosition().GetReportedGS(); + bool isAirborne = reportedGS > 50; + + bool wasOnGround = false; + if (AircraftWasOnGround.find(callsign) != AircraftWasOnGround.end()) { + wasOnGround = AircraftWasOnGround[callsign]; + } else { + wasOnGround = !isAirborne; + } + + AircraftWasOnGround[callsign] = !isAirborne; + + if (wasOnGround && isAirborne) { + if (DepartedAircraft.find(callsign) == DepartedAircraft.end()) { + DepartureInfo info; + info.callsign = callsign; + info.destination = fp.GetFlightPlanData().GetDestination(); + info.acType = fp.GetFlightPlanData().GetAircraftFPType(); + if (info.acType.size() > 4) { + info.acType = info.acType.substr(0, 4); + } + info.wakeTurbCat = ""; + info.wakeTurbCat += fp.GetFlightPlanData().GetAircraftWtc(); + + info.airborneFreq = "QSY"; + + info.liftoffTime = clock(); + info.dismissed = false; + + DepartedAircraft[callsign] = info; + } + } +} + +void CRimcas::UpdateDepartureTimer(int departureDisplayDurationSecs) { + Logger::info(string(__FUNCSIG__)); + + clock_t currentTime = clock(); + double maxDurationSecs = (double)departureDisplayDurationSecs; + + vector toRemove; + for (auto &pair : DepartedAircraft) { + if (pair.second.dismissed) { + toRemove.push_back(pair.first); + continue; + } + + double elapsedSecs = + (double)(currentTime - pair.second.liftoffTime) / CLOCKS_PER_SEC; + if (elapsedSecs >= maxDurationSecs) { + toRemove.push_back(pair.first); + } + } + + for (const string &callsign : toRemove) { + DepartedAircraft.erase(callsign); + } +} + +void CRimcas::DismissDeparture(string callsign) { + Logger::info(string(__FUNCSIG__)); + + if (DepartedAircraft.find(callsign) != DepartedAircraft.end()) { + DepartedAircraft[callsign].dismissed = true; + } +} + +void CRimcas::ClearDismissedDepartures() { + Logger::info(string(__FUNCSIG__)); + + vector toRemove; + for (auto &pair : DepartedAircraft) { + if (pair.second.dismissed) { + toRemove.push_back(pair.first); + } + } + + for (const string &callsign : toRemove) { + DepartedAircraft.erase(callsign); + } } diff --git a/vSMR/Rimcas.hpp b/vSMR/Rimcas.hpp index b9e852490..da2938ac1 100644 --- a/vSMR/Rimcas.hpp +++ b/vSMR/Rimcas.hpp @@ -1,15 +1,17 @@ #pragma once #include +#include #include -#include #include #include -#include +#include + #define _USE_MATH_DEFINES -#include #include "Constant.hpp" -#include #include "Logger.h" +#include +#include + class CSMRRadar; using namespace std; @@ -18,143 +20,167 @@ using namespace EuroScopePlugIn; class CRimcas { public: - CRimcas(); - virtual ~CRimcas(); - - const string string_false = "!NO"; - - struct RunwayAreaType { - string Name = ""; - vector Definition; - bool set = false; - }; - - COLORREF WarningColor = RGB(160, 90, 30); //RGB(180, 100, 50) - COLORREF AlertColor = RGB(150, 0, 0); - - enum RimcasAlertTypes { NoAlert, StageOne, StageTwo }; - - struct AircraftInfo { - string callsign = ""; - string type = ""; - string stand = ""; - }; - - map RunwayAreas; - multimap AcOnRunway; - vector CountdownDefinition; - vector CountdownDefinitionLVP; - multimap ApproachingAircrafts; - map> TimeTable; - map MonitoredRunwayDep; - map MonitoredRunwayArr; - map AcColor; - - bool IsLVP = false; - - int Is_Left(const POINT &p0, const POINT &p1, const POINT &point) - { - return ((p1.x - p0.x) * (point.y - p0.y) - - (point.x - p0.x) * (p1.y - p0.y)); - } - - bool Is_Inside(const POINT &point, const std::vector &points_list) - { - // The winding number counter. - int winding_number = 0; - - // Loop through all edges of the polygon. - typedef std::vector::size_type size_type; - - size_type size = points_list.size(); - - for (size_type i = 0; i < size; ++i) // Edge from point1 to points_list[i+1] - { - POINT point1(points_list[i]); - POINT point2; - - // Wrap? - if (i == (size - 1)) - { - point2 = points_list[0]; - } - else - { - point2 = points_list[i + 1]; - } - - if (point1.y <= point.y) // start y <= point.y - { - if (point2.y > point.y) // An upward crossing - { - if (Is_Left(point1, point2, point) > 0) // Point left of edge - { - ++winding_number; // Have a valid up intersect - } - } - } - else - { - // start y > point.y (no test needed) - if (point2.y <= point.y) // A downward crossing - { - if (Is_Left(point1, point2, point) < 0) // Point right of edge - { - --winding_number; // Have a valid down intersect - } - } - } - } - - return (winding_number != 0); - } - - string GetAcInRunwayArea(CRadarTarget Ac, CRadarScreen *instance); - string GetAcInRunwayAreaSoon(CRadarTarget Ac, CRadarScreen *instance, bool isCorrelated, string acType = "", string acStand = ""); - void AddRunwayArea(CRadarScreen *instance, string runway_name1, string runway_name2, vector Definition); - Color GetAircraftColor(string AcCallsign, Color StandardColor, Color OnRunwayColor, Color RimcasStageOne, Color RimcasStageTwo); - Color GetAircraftColor(string AcCallsign, Color StandardColor, Color OnRunwayColor); - - bool isAcOnRunway(string callsign); - - vector GetRunwayArea(CPosition Left, CPosition Right, float hwidth = 92.5f); - - void OnRefreshBegin(bool isLVP); - void OnRefresh(CRadarTarget Rt, CRadarScreen *instance, bool isCorrelated, string acType = "", string acStand = ""); - void OnRefreshEnd(CRadarScreen *instance, int threshold); - void Reset(); - - RimcasAlertTypes getAlert(string callsign); - - void setCountdownDefinition(vector data, vector dataLVP) - { - CountdownDefinition = data; - std::sort(CountdownDefinition.begin(), CountdownDefinition.end(), std::greater()); - - CountdownDefinitionLVP = dataLVP; - std::sort(CountdownDefinitionLVP.begin(), CountdownDefinitionLVP.end(), std::greater()); - } - - void toggleClosedRunway(string runway) { - if (ClosedRunway.find(runway) == ClosedRunway.end()) - ClosedRunway[runway] = true; - else - ClosedRunway[runway] = !ClosedRunway[runway]; - } - - void toggleMonitoredRunwayDep(string runway) { - if (MonitoredRunwayDep.find(runway) == MonitoredRunwayDep.end()) - MonitoredRunwayDep[runway] = true; - else - MonitoredRunwayDep[runway] = !MonitoredRunwayDep[runway]; - } - - void toggleMonitoredRunwayArr(string runway) { - if (MonitoredRunwayArr.find(runway) == MonitoredRunwayArr.end()) - MonitoredRunwayArr[runway] = true; - else - MonitoredRunwayArr[runway] = !MonitoredRunwayArr[runway]; - } - - map ClosedRunway; + CRimcas(); + virtual ~CRimcas(); + + const string string_false = "!NO"; + + struct RunwayAreaType { + string Name = ""; + vector Definition; + bool set = false; + }; + + COLORREF WarningColor = RGB(160, 90, 30); // RGB(180, 100, 50) + COLORREF AlertColor = RGB(150, 0, 0); + + enum RimcasAlertTypes { NoAlert, StageOne, StageTwo }; + + struct AircraftInfo { + string callsign = ""; + string type = ""; + string stand = ""; + }; + + // Departure timer info + struct DepartureInfo { + string callsign = ""; + string destination = ""; + string acType = ""; + string wakeTurbCat = ""; + string airborneFreq = ""; // QSY frequency + clock_t liftoffTime = 0; + bool dismissed = false; + }; + + map RunwayAreas; + multimap AcOnRunway; + vector CountdownDefinition; + vector CountdownDefinitionLVP; + multimap ApproachingAircrafts; + map> TimeTable; + map MonitoredRunwayDep; + map MonitoredRunwayArr; + map AcColor; + + // Departure timer tracking + map DepartedAircraft; // keyed by callsign + map AircraftWasOnGround; // tracks previous ground state + + bool IsLVP = false; + + int Is_Left(const POINT &p0, const POINT &p1, const POINT &point) { + return ((p1.x - p0.x) * (point.y - p0.y) - + (point.x - p0.x) * (p1.y - p0.y)); + } + + bool Is_Inside(const POINT &point, const std::vector &points_list) { + // The winding number counter. + int winding_number = 0; + + // Loop through all edges of the polygon. + typedef std::vector::size_type size_type; + + size_type size = points_list.size(); + + for (size_type i = 0; i < size; ++i) // Edge from point1 to points_list[i+1] + { + POINT point1(points_list[i]); + POINT point2; + + // Wrap? + if (i == (size - 1)) { + point2 = points_list[0]; + } else { + point2 = points_list[i + 1]; + } + + if (point1.y <= point.y) // start y <= point.y + { + if (point2.y > point.y) // An upward crossing + { + if (Is_Left(point1, point2, point) > 0) // Point left of edge + { + ++winding_number; // Have a valid up intersect + } + } + } else { + // start y > point.y (no test needed) + if (point2.y <= point.y) // A downward crossing + { + if (Is_Left(point1, point2, point) < 0) // Point right of edge + { + --winding_number; // Have a valid down intersect + } + } + } + } + + return (winding_number != 0); + } + + string GetAcInRunwayArea(CRadarTarget Ac, CRadarScreen *instance); + string GetAcInRunwayAreaSoon(CRadarTarget Ac, CRadarScreen *instance, + bool isCorrelated, string acType = "", + string acStand = ""); + void AddRunwayArea(CRadarScreen *instance, string runway_name1, + string runway_name2, vector Definition); + Color GetAircraftColor(string AcCallsign, Color StandardColor, + Color OnRunwayColor, Color RimcasStageOne, + Color RimcasStageTwo); + Color GetAircraftColor(string AcCallsign, Color StandardColor, + Color OnRunwayColor); + + bool isAcOnRunway(string callsign); + + vector GetRunwayArea(CPosition Left, CPosition Right, + float hwidth = 92.5f); + + void OnRefreshBegin(bool isLVP); + void OnRefresh(CRadarTarget Rt, CRadarScreen *instance, bool isCorrelated, + string acType = "", string acStand = ""); + void OnRefreshEnd(CRadarScreen *instance, int threshold); + void Reset(); + + // Departure timer methods + void TrackDeparture(CRadarTarget Rt, CFlightPlan fp, CRadarScreen *instance, + string activeAirport); + void UpdateDepartureTimer(int departureDisplayDuration); + void DismissDeparture(string callsign); + void ClearDismissedDepartures(); + + RimcasAlertTypes getAlert(string callsign); + + void setCountdownDefinition(vector data, vector dataLVP) { + CountdownDefinition = data; + std::sort(CountdownDefinition.begin(), CountdownDefinition.end(), + std::greater()); + + CountdownDefinitionLVP = dataLVP; + std::sort(CountdownDefinitionLVP.begin(), CountdownDefinitionLVP.end(), + std::greater()); + } + + void toggleClosedRunway(string runway) { + if (ClosedRunway.find(runway) == ClosedRunway.end()) + ClosedRunway[runway] = true; + else + ClosedRunway[runway] = !ClosedRunway[runway]; + } + + void toggleMonitoredRunwayDep(string runway) { + if (MonitoredRunwayDep.find(runway) == MonitoredRunwayDep.end()) + MonitoredRunwayDep[runway] = true; + else + MonitoredRunwayDep[runway] = !MonitoredRunwayDep[runway]; + } + + void toggleMonitoredRunwayArr(string runway) { + if (MonitoredRunwayArr.find(runway) == MonitoredRunwayArr.end()) + MonitoredRunwayArr[runway] = true; + else + MonitoredRunwayArr[runway] = !MonitoredRunwayArr[runway]; + } + + map ClosedRunway; }; diff --git a/vSMR/SMRRadar.cpp b/vSMR/SMRRadar.cpp index a015a3235..96477cce5 100644 --- a/vSMR/SMRRadar.cpp +++ b/vSMR/SMRRadar.cpp @@ -1,6 +1,7 @@ -#include "stdafx.h" -#include "Resource.h" #include "SMRRadar.hpp" +#include "Resource.h" +#include "stdafx.h" + ULONG_PTR m_gdiplusToken; CPoint mouseLocation(0, 0); @@ -13,8 +14,9 @@ int LeaderLineDefaultlenght = 50; bool initCursor = true; HCURSOR smrCursor = NULL; -bool standardCursor; // switches between mouse cursor and pointer cursor when moving tags -bool customCursor; // use SMR version or default windows mouse symbol +bool standardCursor; // switches between mouse cursor and pointer cursor when + // moving tags +bool customCursor; // use SMR version or default windows mouse symbol WNDPROC gSourceProc; HWND pluginWindow; LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); @@ -23,3154 +25,3847 @@ map CSMRRadar::vStripsStands; map appWindows; -inline double closest(std::vector const& vec, double value) { - auto const it = std::lower_bound(vec.begin(), vec.end(), value); - if (it == vec.end()) { return -1; } +inline double closest(std::vector const &vec, double value) { + auto const it = std::lower_bound(vec.begin(), vec.end(), value); + if (it == vec.end()) { + return -1; + } - return *it; + return *it; }; -inline bool IsTagBeingDragged(string c) -{ - return TagBeingDragged == c; -} +inline bool IsTagBeingDragged(string c) { return TagBeingDragged == c; } bool mouseWithin(CRect rect) { - if (mouseLocation.x >= rect.left + 1 && mouseLocation.x <= rect.right - 1 && mouseLocation.y >= rect.top + 1 && mouseLocation.y <= rect.bottom - 1) - return true; - return false; + if (mouseLocation.x >= rect.left + 1 && mouseLocation.x <= rect.right - 1 && + mouseLocation.y >= rect.top + 1 && mouseLocation.y <= rect.bottom - 1) + return true; + return false; } // ReSharper disable CppMsExtAddressOfClassRValue -CSMRRadar::CSMRRadar() -{ - - Logger::info("CSMRRadar::CSMRRadar()"); - - // Initializing randomizer - srand(static_cast(time(nullptr))); - - // Initialize GDI+ - GdiplusStartupInput gdiplusStartupInput; - GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, nullptr); - - // Getting the DLL file folder - GetModuleFileNameA(HINSTANCE(&__ImageBase), DllPathFile, sizeof(DllPathFile)); - DllPath = DllPathFile; - DllPath.resize(DllPath.size() - strlen("vSMR.dll")); - - ConfigPath = DllPath + "\\vSMR_Profiles.json"; - - Logger::info("Loading callsigns"); - - // Creating the RIMCAS instance - if (Callsigns == nullptr) - Callsigns = new CCallsignLookup(); - - // We can look in three places for this file: - // 1. Within the plugin directory - // 2. In the ICAO folder of a GNG package - // 3. In the working directory of EuroScope - fs::path possible_paths[3]; - possible_paths[0] = fs::path(DllPath) / fs::path("ICAO_Airlines.txt"); - possible_paths[1] = fs::path(DllPath).parent_path().parent_path() / fs::path("ICAO") / fs::path("ICAO_Airlines.txt"); - possible_paths[2] = fs::path(DllPath).parent_path().parent_path().parent_path() / fs::path("ICAO") / fs::path("ICAO_Airlines.txt"); - - for (auto p : possible_paths) { - Logger::info("Trying to read callsigns from: " + p.string()); - if (fs::exists(p)) { - Logger::info("Found callsign file!"); - Callsigns->readFile(p.string()); - - break; - } - }; - - Logger::info("Loading aircraft type data"); - - if (AircraftTypes == nullptr) - AircraftTypes = new CAircraftTypeLookup(); - - fs::path path = fs::path(DllPath) / fs::path("aircraft-data.csv"); - - if (fs::exists(path)) { - Logger::info("Found aircraft type data file!"); - AircraftTypes->readFile(path.string()); - } - - Logger::info("Loading RIMCAS & Config"); - // Creating the RIMCAS instance - if (RimcasInstance == nullptr) - RimcasInstance = new CRimcas(); - - // Loading up the config file - if (CurrentConfig == nullptr) - CurrentConfig = new CConfig(ConfigPath); - - if (ColorManager == nullptr) - ColorManager = new CColorManager(); - - standardCursor = true; - ActiveAirport = "EGKK"; - - // Setting up the data for the 2 approach windows - appWindowDisplays[1] = false; - appWindowDisplays[2] = false; - appWindows[1] = new CInsetWindow(APPWINDOW_ONE); - appWindows[2] = new CInsetWindow(APPWINDOW_TWO); - - Logger::info("Loading WIP areas"); - Logger::info(curl_version()); - string raw; - string url = "https://raw.githubusercontent.com/VATSIM-UK/uk-controller-pack/refs/heads/main/.data/vSMR_WIP_areas.txt"; - HttpHelper* httpHelper = new HttpHelper(); - raw.assign(httpHelper->downloadStringFromURL(url)); - - stringstream ss(raw); - string line; - vector wipArea; - while (getline(ss, line, '\n')) { - Logger::info(line); - if (startsWith("COORD:", line.c_str())) { - CPosition pos; - pos.LoadFromStrings(line.substr(21, 14).c_str(), line.substr(6, 14).c_str()); - wipArea.push_back(pos); - } - else { - if (wipArea.size() > 0) { - wipAreas.push_back(wipArea); - wipArea.clear(); - } - } - } - - if (wipArea.size() > 0) { - wipAreas.push_back(wipArea); - } - - Logger::info("Loading profile"); - - this->CSMRRadar::LoadProfile("Default"); - - this->CSMRRadar::LoadCustomFont(); - - this->CSMRRadar::RefreshAirportActivity(); +CSMRRadar::CSMRRadar() { + + Logger::info("CSMRRadar::CSMRRadar()"); + + // Initializing randomizer + srand(static_cast(time(nullptr))); + + // Initialize GDI+ + GdiplusStartupInput gdiplusStartupInput; + GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, nullptr); + + // Getting the DLL file folder + GetModuleFileNameA(HINSTANCE(&__ImageBase), DllPathFile, sizeof(DllPathFile)); + DllPath = DllPathFile; + DllPath.resize(DllPath.size() - strlen("vSMR.dll")); + + ConfigPath = DllPath + "\\vSMR_Profiles.json"; + + Logger::info("Loading callsigns"); + + // Creating the RIMCAS instance + if (Callsigns == nullptr) + Callsigns = new CCallsignLookup(); + + // We can look in three places for this file: + // 1. Within the plugin directory + // 2. In the ICAO folder of a GNG package + // 3. In the working directory of EuroScope + fs::path possible_paths[3]; + possible_paths[0] = fs::path(DllPath) / fs::path("ICAO_Airlines.txt"); + possible_paths[1] = fs::path(DllPath).parent_path().parent_path() / + fs::path("ICAO") / fs::path("ICAO_Airlines.txt"); + possible_paths[2] = + fs::path(DllPath).parent_path().parent_path().parent_path() / + fs::path("ICAO") / fs::path("ICAO_Airlines.txt"); + + for (auto p : possible_paths) { + Logger::info("Trying to read callsigns from: " + p.string()); + if (fs::exists(p)) { + Logger::info("Found callsign file!"); + Callsigns->readFile(p.string()); + + break; + } + }; + + Logger::info("Loading aircraft type data"); + + if (AircraftTypes == nullptr) + AircraftTypes = new CAircraftTypeLookup(); + + fs::path path = fs::path(DllPath) / fs::path("aircraft-data.csv"); + + if (fs::exists(path)) { + Logger::info("Found aircraft type data file!"); + AircraftTypes->readFile(path.string()); + } + + Logger::info("Loading RIMCAS & Config"); + // Creating the RIMCAS instance + if (RimcasInstance == nullptr) + RimcasInstance = new CRimcas(); + + // Loading up the config file + if (CurrentConfig == nullptr) + CurrentConfig = new CConfig(ConfigPath); + + if (ColorManager == nullptr) + ColorManager = new CColorManager(); + + standardCursor = true; + ActiveAirport = "EGKK"; + + // Setting up the data for the 2 approach windows + appWindowDisplays[1] = false; + appWindowDisplays[2] = false; + appWindows[1] = new CInsetWindow(APPWINDOW_ONE); + appWindows[2] = new CInsetWindow(APPWINDOW_TWO); + + Logger::info("Loading WIP areas"); + Logger::info(curl_version()); + string raw; + string url = "https://raw.githubusercontent.com/VATSIM-UK/uk-controller-pack/" + "refs/heads/main/.data/vSMR_WIP_areas.txt"; + HttpHelper *httpHelper = new HttpHelper(); + raw.assign(httpHelper->downloadStringFromURL(url)); + + stringstream ss(raw); + string line; + vector wipArea; + while (getline(ss, line, '\n')) { + Logger::info(line); + if (startsWith("COORD:", line.c_str())) { + CPosition pos; + pos.LoadFromStrings(line.substr(21, 14).c_str(), + line.substr(6, 14).c_str()); + wipArea.push_back(pos); + } else { + if (wipArea.size() > 0) { + wipAreas.push_back(wipArea); + wipArea.clear(); + } + } + } + + if (wipArea.size() > 0) { + wipAreas.push_back(wipArea); + } + + Logger::info("Loading profile"); + + this->CSMRRadar::LoadProfile("Default"); + + this->CSMRRadar::LoadCustomFont(); + + this->CSMRRadar::RefreshAirportActivity(); } -CSMRRadar::~CSMRRadar() -{ - Logger::info(string(__FUNCSIG__)); - try { - //this->OnAsrContentToBeSaved(); - //this->EuroScopePlugInExitCustom(); - } - catch (exception &e) { - stringstream s; - s << e.what() << endl; - AfxMessageBox(string("Error occurred " + s.str()).c_str()); - } - // Shutting down GDI+ - GdiplusShutdown(m_gdiplusToken); - delete CurrentConfig; +CSMRRadar::~CSMRRadar() { + Logger::info(string(__FUNCSIG__)); + try { + // this->OnAsrContentToBeSaved(); + // this->EuroScopePlugInExitCustom(); + } catch (exception &e) { + stringstream s; + s << e.what() << endl; + AfxMessageBox(string("Error occurred " + s.str()).c_str()); + } + // Shutting down GDI+ + GdiplusShutdown(m_gdiplusToken); + delete CurrentConfig; } void CSMRRadar::CorrelateCursor() { - if (NeedCorrelateCursor) - { - if (standardCursor) - { - smrCursor = CopyCursor((HCURSOR)::LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDC_SMRCORRELATE), IMAGE_CURSOR, 0, 0, LR_SHARED)); - - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - ASSERT(smrCursor); - SetCursor(smrCursor); - standardCursor = false; - } - } - else - { - if (!standardCursor) - { - if (customCursor) { - smrCursor = CopyCursor((HCURSOR)::LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDC_SMRCURSOR), IMAGE_CURSOR, 0, 0, LR_SHARED)); - } - else { - smrCursor = (HCURSOR)::LoadCursor(NULL, IDC_ARROW); - } - - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - ASSERT(smrCursor); - SetCursor(smrCursor); - standardCursor = true; - } - } + if (NeedCorrelateCursor) { + if (standardCursor) { + smrCursor = CopyCursor((HCURSOR)::LoadImage( + AfxGetInstanceHandle(), MAKEINTRESOURCE(IDC_SMRCORRELATE), + IMAGE_CURSOR, 0, 0, LR_SHARED)); + + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + ASSERT(smrCursor); + SetCursor(smrCursor); + standardCursor = false; + } + } else { + if (!standardCursor) { + if (customCursor) { + smrCursor = CopyCursor((HCURSOR)::LoadImage( + AfxGetInstanceHandle(), MAKEINTRESOURCE(IDC_SMRCURSOR), + IMAGE_CURSOR, 0, 0, LR_SHARED)); + } else { + smrCursor = (HCURSOR)::LoadCursor(NULL, IDC_ARROW); + } + + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + ASSERT(smrCursor); + SetCursor(smrCursor); + standardCursor = true; + } + } } void CSMRRadar::LoadCustomFont() { - Logger::info(string(__FUNCSIG__)); - // Loading the custom font if there is one in use - customFonts.clear(); - - const Value& FSizes = CurrentConfig->getActiveProfile()["font"]["sizes"]; - string font_name = CurrentConfig->getActiveProfile()["font"]["font_name"].GetString(); - wstring buffer = wstring(font_name.begin(), font_name.end()); - Gdiplus::FontStyle fontStyle = Gdiplus::FontStyleRegular; - if (strcmp(CurrentConfig->getActiveProfile()["font"]["weight"].GetString(), "Bold") == 0) - fontStyle = Gdiplus::FontStyleBold; - if (strcmp(CurrentConfig->getActiveProfile()["font"]["weight"].GetString(), "Italic") == 0) - fontStyle = Gdiplus::FontStyleItalic; - - customFonts[1] = new Gdiplus::Font(buffer.c_str(), Gdiplus::REAL(FSizes["one"].GetInt()), fontStyle, Gdiplus::UnitPixel); - customFonts[2] = new Gdiplus::Font(buffer.c_str(), Gdiplus::REAL(FSizes["two"].GetInt()), fontStyle, Gdiplus::UnitPixel); - customFonts[3] = new Gdiplus::Font(buffer.c_str(), Gdiplus::REAL(FSizes["three"].GetInt()), fontStyle, Gdiplus::UnitPixel); - customFonts[4] = new Gdiplus::Font(buffer.c_str(), Gdiplus::REAL(FSizes["four"].GetInt()), fontStyle, Gdiplus::UnitPixel); - customFonts[5] = new Gdiplus::Font(buffer.c_str(), Gdiplus::REAL(FSizes["five"].GetInt()), fontStyle, Gdiplus::UnitPixel); + Logger::info(string(__FUNCSIG__)); + // Loading the custom font if there is one in use + customFonts.clear(); + + const Value &FSizes = CurrentConfig->getActiveProfile()["font"]["sizes"]; + string font_name = + CurrentConfig->getActiveProfile()["font"]["font_name"].GetString(); + wstring buffer = wstring(font_name.begin(), font_name.end()); + Gdiplus::FontStyle fontStyle = Gdiplus::FontStyleRegular; + if (strcmp(CurrentConfig->getActiveProfile()["font"]["weight"].GetString(), + "Bold") == 0) + fontStyle = Gdiplus::FontStyleBold; + if (strcmp(CurrentConfig->getActiveProfile()["font"]["weight"].GetString(), + "Italic") == 0) + fontStyle = Gdiplus::FontStyleItalic; + + customFonts[1] = + new Gdiplus::Font(buffer.c_str(), Gdiplus::REAL(FSizes["one"].GetInt()), + fontStyle, Gdiplus::UnitPixel); + customFonts[2] = + new Gdiplus::Font(buffer.c_str(), Gdiplus::REAL(FSizes["two"].GetInt()), + fontStyle, Gdiplus::UnitPixel); + customFonts[3] = + new Gdiplus::Font(buffer.c_str(), Gdiplus::REAL(FSizes["three"].GetInt()), + fontStyle, Gdiplus::UnitPixel); + customFonts[4] = + new Gdiplus::Font(buffer.c_str(), Gdiplus::REAL(FSizes["four"].GetInt()), + fontStyle, Gdiplus::UnitPixel); + customFonts[5] = + new Gdiplus::Font(buffer.c_str(), Gdiplus::REAL(FSizes["five"].GetInt()), + fontStyle, Gdiplus::UnitPixel); } void CSMRRadar::LoadProfile(string profileName) { - Logger::info(string(__FUNCSIG__)); - // Loading the new profile - CurrentConfig->setActiveProfile(profileName); - - // Loading all the new data - const Value &RimcasTimer = CurrentConfig->getActiveProfile()["rimcas"]["timer"]; - const Value &RimcasTimerLVP = CurrentConfig->getActiveProfile()["rimcas"]["timer_lvp"]; - - vector RimcasNorm; - for (SizeType i = 0; i < RimcasTimer.Size(); i++) { - RimcasNorm.push_back(RimcasTimer[i].GetInt()); - } - - vector RimcasLVP; - for (SizeType i = 0; i < RimcasTimerLVP.Size(); i++) { - RimcasLVP.push_back(RimcasTimerLVP[i].GetInt()); - } - RimcasInstance->setCountdownDefinition(RimcasNorm, RimcasLVP); - LeaderLineDefaultlenght = CurrentConfig->getActiveProfile()["labels"]["leader_line_length"].GetInt(); - - customCursor = CurrentConfig->isCustomCursorUsed(); - - // Reloading the fonts - this->LoadCustomFont(); + Logger::info(string(__FUNCSIG__)); + // Loading the new profile + CurrentConfig->setActiveProfile(profileName); + + // Loading all the new data + const Value &RimcasTimer = + CurrentConfig->getActiveProfile()["rimcas"]["timer"]; + const Value &RimcasTimerLVP = + CurrentConfig->getActiveProfile()["rimcas"]["timer_lvp"]; + + vector RimcasNorm; + for (SizeType i = 0; i < RimcasTimer.Size(); i++) { + RimcasNorm.push_back(RimcasTimer[i].GetInt()); + } + + vector RimcasLVP; + for (SizeType i = 0; i < RimcasTimerLVP.Size(); i++) { + RimcasLVP.push_back(RimcasTimerLVP[i].GetInt()); + } + RimcasInstance->setCountdownDefinition(RimcasNorm, RimcasLVP); + LeaderLineDefaultlenght = + CurrentConfig->getActiveProfile()["labels"]["leader_line_length"] + .GetInt(); + + customCursor = CurrentConfig->isCustomCursorUsed(); + + // Reloading the fonts + this->LoadCustomFont(); } -void CSMRRadar::OnAsrContentLoaded(bool Loaded) -{ - Logger::info(string(__FUNCSIG__)); - const char * p_value; - - // ReSharper disable CppZeroConstantCanBeReplacedWithNullptr - if ((p_value = GetDataFromAsr("Airport")) != NULL) - setActiveAirport(p_value); +void CSMRRadar::OnAsrContentLoaded(bool Loaded) { + Logger::info(string(__FUNCSIG__)); + const char *p_value; - if ((p_value = GetDataFromAsr("ActiveProfile")) != NULL) - this->LoadProfile(string(p_value)); + // ReSharper disable CppZeroConstantCanBeReplacedWithNullptr + if ((p_value = GetDataFromAsr("Airport")) != NULL) + setActiveAirport(p_value); - if ((p_value = GetDataFromAsr("FontSize")) != NULL) - currentFontSize = atoi(p_value); + if ((p_value = GetDataFromAsr("ActiveProfile")) != NULL) + this->LoadProfile(string(p_value)); - if ((p_value = GetDataFromAsr("Afterglow")) != NULL) - Afterglow = atoi(p_value) == 1 ? true : false; + if ((p_value = GetDataFromAsr("FontSize")) != NULL) + currentFontSize = atoi(p_value); - if ((p_value = GetDataFromAsr("AppTrailsDots")) != NULL) - Trail_App = atoi(p_value); + if ((p_value = GetDataFromAsr("Afterglow")) != NULL) + Afterglow = atoi(p_value) == 1 ? true : false; - if ((p_value = GetDataFromAsr("GndTrailsDots")) != NULL) - Trail_Gnd = atoi(p_value); + if ((p_value = GetDataFromAsr("AppTrailsDots")) != NULL) + Trail_App = atoi(p_value); - if ((p_value = GetDataFromAsr("PredictedLine")) != NULL) - PredictedLength = atoi(p_value); + if ((p_value = GetDataFromAsr("GndTrailsDots")) != NULL) + Trail_Gnd = atoi(p_value); - if ((p_value = GetDataFromAsr("WIPareas")) != NULL) - wipAreasActive = atoi(p_value); + if ((p_value = GetDataFromAsr("PredictedLine")) != NULL) + PredictedLength = atoi(p_value); - if ((p_value = GetDataFromAsr("ShowAircraftType")) != NULL) - showAircraftType = atoi(p_value) == 1 ? true : false; + if ((p_value = GetDataFromAsr("WIPareas")) != NULL) + wipAreasActive = atoi(p_value); - if ((p_value = GetDataFromAsr("ShowSID")) != NULL) - showSID = atoi(p_value) == 1 ? true : false; + if ((p_value = GetDataFromAsr("ShowAircraftType")) != NULL) + showAircraftType = atoi(p_value) == 1 ? true : false; - if ((p_value = GetDataFromAsr("ShowWakeTurb")) != NULL) - showWakeTurb = atoi(p_value) == 1 ? true : false; + if ((p_value = GetDataFromAsr("ShowSID")) != NULL) + showSID = atoi(p_value) == 1 ? true : false; - string temp; + if ((p_value = GetDataFromAsr("ShowWakeTurb")) != NULL) + showWakeTurb = atoi(p_value) == 1 ? true : false; - for (int i = 1; i < 3; i++) - { - string prefix = "SRW" + std::to_string(i); + // Departure Timer Window settings + if ((p_value = GetDataFromAsr("DepWindowShow")) != NULL) + showDepartureWindow = atoi(p_value) == 1 ? true : false; - if ((p_value = GetDataFromAsr(string(prefix + "TopLeftX").c_str())) != NULL) - appWindows[i]->m_Area.left = atoi(p_value); + if ((p_value = GetDataFromAsr("DepWindowDuration")) != NULL) + departureDisplayDuration = atoi(p_value); - if ((p_value = GetDataFromAsr(string(prefix + "TopLeftY").c_str())) != NULL) - appWindows[i]->m_Area.top = atoi(p_value); + if ((p_value = GetDataFromAsr("DepWindowColCallsign")) != NULL) + depWindowShowCallsign = atoi(p_value) == 1 ? true : false; - if ((p_value = GetDataFromAsr(string(prefix + "BottomRightX").c_str())) != NULL) - appWindows[i]->m_Area.right = atoi(p_value); + if ((p_value = GetDataFromAsr("DepWindowColDest")) != NULL) + depWindowShowDest = atoi(p_value) == 1 ? true : false; - if ((p_value = GetDataFromAsr(string(prefix + "BottomRightY").c_str())) != NULL) - appWindows[i]->m_Area.bottom = atoi(p_value); + if ((p_value = GetDataFromAsr("DepWindowColAcType")) != NULL) + depWindowShowAcType = atoi(p_value) == 1 ? true : false; - if ((p_value = GetDataFromAsr(string(prefix + "OffsetX").c_str())) != NULL) - appWindows[i]->m_Offset.x = atoi(p_value); + if ((p_value = GetDataFromAsr("DepWindowColWake")) != NULL) + depWindowShowWake = atoi(p_value) == 1 ? true : false; - if ((p_value = GetDataFromAsr(string(prefix + "OffsetY").c_str())) != NULL) - appWindows[i]->m_Offset.y = atoi(p_value); + if ((p_value = GetDataFromAsr("DepWindowColFreq")) != NULL) + depWindowShowFreq = atoi(p_value) == 1 ? true : false; + if ((p_value = GetDataFromAsr("DepWindowColTime")) != NULL) + depWindowShowTime = atoi(p_value) == 1 ? true : false; - if ((p_value = GetDataFromAsr(string(prefix + "Filter").c_str())) != NULL) - appWindows[i]->m_Filter = atoi(p_value); + if ((p_value = GetDataFromAsr("DepWindowPosX")) != NULL) + DepartureWindowArea.left = atoi(p_value); - if ((p_value = GetDataFromAsr(string(prefix + "Scale").c_str())) != NULL) - appWindows[i]->m_Scale = atoi(p_value); - - if ((p_value = GetDataFromAsr(string(prefix + "Rotation").c_str())) != NULL) - appWindows[i]->m_Rotation = atoi(p_value); - - if ((p_value = GetDataFromAsr(string(prefix + "Display").c_str())) != NULL) - appWindowDisplays[i] = atoi(p_value) == 1 ? true : false; - } - - // Auto load the airport config on ASR opened. - CSectorElement rwy; - for (rwy = GetPlugIn()->SectorFileElementSelectFirst(SECTOR_ELEMENT_RUNWAY); - rwy.IsValid(); - rwy = GetPlugIn()->SectorFileElementSelectNext(rwy, SECTOR_ELEMENT_RUNWAY)) - { - if (startsWith(getActiveAirport().c_str(), rwy.GetAirportName())) { - string name = rwy.GetRunwayName(0) + string(" / ") + rwy.GetRunwayName(1); - - if (rwy.IsElementActive(true, 0) || rwy.IsElementActive(true, 1) || rwy.IsElementActive(false, 0) || rwy.IsElementActive(false, 1)) { - RimcasInstance->toggleMonitoredRunwayDep(name); - if (rwy.IsElementActive(false, 0) || rwy.IsElementActive(false, 1)) { - RimcasInstance->toggleMonitoredRunwayArr(name); - } - } - } - } - - // ReSharper restore CppZeroConstantCanBeReplacedWithNullptr -} + if ((p_value = GetDataFromAsr("DepWindowPosY")) != NULL) + DepartureWindowArea.top = atoi(p_value); -void CSMRRadar::OnAsrContentToBeSaved() -{ - Logger::info(string(__FUNCSIG__)); + string temp; - SaveDataToAsr("Airport", "Active airport for RIMCAS", getActiveAirport().c_str()); + for (int i = 1; i < 3; i++) { + string prefix = "SRW" + std::to_string(i); - SaveDataToAsr("ActiveProfile", "vSMR active profile", CurrentConfig->getActiveProfileName().c_str()); + if ((p_value = GetDataFromAsr(string(prefix + "TopLeftX").c_str())) != NULL) + appWindows[i]->m_Area.left = atoi(p_value); - SaveDataToAsr("FontSize", "vSMR font size", std::to_string(currentFontSize).c_str()); + if ((p_value = GetDataFromAsr(string(prefix + "TopLeftY").c_str())) != NULL) + appWindows[i]->m_Area.top = atoi(p_value); - SaveDataToAsr("Afterglow", "vSMR Afterglow enabled", std::to_string(int(Afterglow)).c_str()); + if ((p_value = GetDataFromAsr(string(prefix + "BottomRightX").c_str())) != + NULL) + appWindows[i]->m_Area.right = atoi(p_value); - SaveDataToAsr("AppTrailsDots", "vSMR APPR Trail Dots", std::to_string(Trail_App).c_str()); + if ((p_value = GetDataFromAsr(string(prefix + "BottomRightY").c_str())) != + NULL) + appWindows[i]->m_Area.bottom = atoi(p_value); - SaveDataToAsr("GndTrailsDots", "vSMR GRND Trail Dots", std::to_string(Trail_Gnd).c_str()); + if ((p_value = GetDataFromAsr(string(prefix + "OffsetX").c_str())) != NULL) + appWindows[i]->m_Offset.x = atoi(p_value); - SaveDataToAsr("PredictedLine", "vSMR Predicted Track Lines", std::to_string(PredictedLength).c_str()); + if ((p_value = GetDataFromAsr(string(prefix + "OffsetY").c_str())) != NULL) + appWindows[i]->m_Offset.y = atoi(p_value); - SaveDataToAsr("WIPareas", "vSMR WIP Areas", std::to_string(wipAreasActive).c_str()); + if ((p_value = GetDataFromAsr(string(prefix + "Filter").c_str())) != NULL) + appWindows[i]->m_Filter = atoi(p_value); - SaveDataToAsr("ShowAircraftType", "Show Aircraft Type", std::to_string(showAircraftType).c_str()); + if ((p_value = GetDataFromAsr(string(prefix + "Scale").c_str())) != NULL) + appWindows[i]->m_Scale = atoi(p_value); - SaveDataToAsr("ShowSID", "Show SID", std::to_string(showSID).c_str()); + if ((p_value = GetDataFromAsr(string(prefix + "Rotation").c_str())) != NULL) + appWindows[i]->m_Rotation = atoi(p_value); - SaveDataToAsr("ShowWakeTurb", "Show Wake Turbulence", std::to_string(showWakeTurb).c_str()); + if ((p_value = GetDataFromAsr(string(prefix + "Display").c_str())) != NULL) + appWindowDisplays[i] = atoi(p_value) == 1 ? true : false; + } - string temp = ""; + // Auto load the airport config on ASR opened. + CSectorElement rwy; + for (rwy = GetPlugIn()->SectorFileElementSelectFirst(SECTOR_ELEMENT_RUNWAY); + rwy.IsValid(); rwy = GetPlugIn()->SectorFileElementSelectNext( + rwy, SECTOR_ELEMENT_RUNWAY)) { + if (startsWith(getActiveAirport().c_str(), rwy.GetAirportName())) { + string name = rwy.GetRunwayName(0) + string(" / ") + rwy.GetRunwayName(1); - for (int i = 1; i < 3; i++) - { - string prefix = "SRW" + std::to_string(i); + if (rwy.IsElementActive(true, 0) || rwy.IsElementActive(true, 1) || + rwy.IsElementActive(false, 0) || rwy.IsElementActive(false, 1)) { + RimcasInstance->toggleMonitoredRunwayDep(name); + if (rwy.IsElementActive(false, 0) || rwy.IsElementActive(false, 1)) { + RimcasInstance->toggleMonitoredRunwayArr(name); + } + } + } + } - temp = std::to_string(appWindows[i]->m_Area.left); - SaveDataToAsr(string(prefix + "TopLeftX").c_str(), "SRW position", temp.c_str()); - - temp = std::to_string(appWindows[i]->m_Area.top); - SaveDataToAsr(string(prefix + "TopLeftY").c_str(), "SRW position", temp.c_str()); - - temp = std::to_string(appWindows[i]->m_Area.right); - SaveDataToAsr(string(prefix + "BottomRightX").c_str(), "SRW position", temp.c_str()); - - temp = std::to_string(appWindows[i]->m_Area.bottom); - SaveDataToAsr(string(prefix + "BottomRightY").c_str(), "SRW position", temp.c_str()); - - temp = std::to_string(appWindows[i]->m_Offset.x); - SaveDataToAsr(string(prefix + "OffsetX").c_str(), "SRW offset", temp.c_str()); - - temp = std::to_string(appWindows[i]->m_Offset.y); - SaveDataToAsr(string(prefix + "OffsetY").c_str(), "SRW offset", temp.c_str()); - - temp = std::to_string(appWindows[i]->m_Filter); - SaveDataToAsr(string(prefix + "Filter").c_str(), "SRW filter", temp.c_str()); - - temp = std::to_string(appWindows[i]->m_Scale); - SaveDataToAsr(string(prefix + "Scale").c_str(), "SRW range", temp.c_str()); - - temp = std::to_string((int)appWindows[i]->m_Rotation); - SaveDataToAsr(string(prefix + "Rotation").c_str(), "SRW rotation", temp.c_str()); - - string to_save = "0"; - if (appWindowDisplays[i]) - to_save = "1"; - SaveDataToAsr(string(prefix + "Display").c_str(), "Display Secondary Radar Window", to_save.c_str()); - } + // ReSharper restore CppZeroConstantCanBeReplacedWithNullptr } -void CSMRRadar::OnMoveScreenObject(int ObjectType, const char * sObjectId, POINT Pt, RECT Area, bool Released) { - Logger::info(string(__FUNCSIG__)); - - if (ObjectType == APPWINDOW_ONE || ObjectType == APPWINDOW_TWO) { - int appWindowId = ObjectType - APPWINDOW_BASE; - - bool toggleCursor = appWindows[appWindowId]->OnMoveScreenObject(sObjectId, Pt, Area, Released); - - if (Released) - OnAsrContentToBeSaved(); - - if (!toggleCursor) - { - if (standardCursor) - { - if (strcmp(sObjectId, "topbar") == 0) - smrCursor = CopyCursor((HCURSOR)::LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDC_SMRMOVEWINDOW), IMAGE_CURSOR, 0, 0, LR_SHARED)); - else if (strcmp(sObjectId, "resize") == 0) - smrCursor = CopyCursor((HCURSOR)::LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDC_SMRRESIZE), IMAGE_CURSOR, 0, 0, LR_SHARED)); - - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - ASSERT(smrCursor); - SetCursor(smrCursor); - standardCursor = false; - } - } else - { - if (!standardCursor) - { - if (customCursor) { - smrCursor = CopyCursor((HCURSOR)::LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDC_SMRCURSOR), IMAGE_CURSOR, 0, 0, LR_SHARED)); - } - else { - smrCursor = (HCURSOR)::LoadCursor(NULL, IDC_ARROW); - } - - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - ASSERT(smrCursor); - SetCursor(smrCursor); - standardCursor = true; - } - } - } - - if (ObjectType == DRAWING_TAG || ObjectType == TAG_CITEM_MANUALCORRELATE ||ObjectType == TAG_CITEM_CALLSIGN || ObjectType == TAG_CITEM_FPBOX || ObjectType == TAG_CITEM_RWY || ObjectType == TAG_CITEM_SID || ObjectType == TAG_CITEM_GATE || ObjectType == TAG_CITEM_NO || ObjectType == TAG_CITEM_GROUNDSTATUS) { - CRadarTarget rt = GetPlugIn()->RadarTargetSelect(sObjectId); - - if (!Released) - { - if (standardCursor) - { - smrCursor = CopyCursor((HCURSOR)::LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDC_SMRMOVETAG), IMAGE_CURSOR, 0, 0, LR_SHARED)); - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - ASSERT(smrCursor); - SetCursor(smrCursor); - standardCursor = false; - } - } - else - { - if (!standardCursor) - { - if (customCursor) { - smrCursor = CopyCursor((HCURSOR)::LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDC_SMRCURSOR), IMAGE_CURSOR, 0, 0, LR_SHARED)); - } - else { - smrCursor = (HCURSOR)::LoadCursor(NULL, IDC_ARROW); - } - - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - ASSERT(smrCursor); - SetCursor(smrCursor); - standardCursor = true; - } - } - - if (rt.IsValid()) { - CRect Temp = Area; - POINT TagCenterPix = Temp.CenterPoint(); - POINT AcPosPix = ConvertCoordFromPositionToPixel(GetPlugIn()->RadarTargetSelect(sObjectId).GetPosition().GetPosition()); - POINT CustomTag = { TagCenterPix.x - AcPosPix.x, TagCenterPix.y - AcPosPix.y }; - - if (CurrentConfig->getActiveProfile()["labels"]["auto_deconfliction"].GetBool()) - { - double angle = RadToDeg(atan2(CustomTag.y, CustomTag.x)); - angle = fmod(angle + 360, 360); - vector angles; - for (double k = 0.0; k <= 360.0; k += 22.5) - angles.push_back(k); - - TagAngles[sObjectId] = closest(angles, angle); - TagLeaderLineLength[sObjectId] = max(LeaderLineDefaultlenght, min(int(DistancePts(AcPosPix, TagCenterPix)), LeaderLineDefaultlenght * 2)); - - } - else - { - TagsOffsets[sObjectId] = CustomTag; - } - - - GetPlugIn()->SetASELAircraft(GetPlugIn()->FlightPlanSelect(sObjectId)); - - if (Released) - { - TagBeingDragged = ""; - } - else - { - TagBeingDragged = sObjectId; - } - - RequestRefresh(); - } - } - - if (ObjectType == RIMCAS_IAW) { - TimePopupAreas[sObjectId] = Area; - - if (!Released) - { - if (standardCursor) - { - smrCursor = CopyCursor((HCURSOR)::LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDC_SMRMOVEWINDOW), IMAGE_CURSOR, 0, 0, LR_SHARED)); - - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - ASSERT(smrCursor); - SetCursor(smrCursor); - standardCursor = false; - } - } - else - { - if (!standardCursor) - { - if (customCursor) { - smrCursor = CopyCursor((HCURSOR)::LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDC_SMRCURSOR), IMAGE_CURSOR, 0, 0, LR_SHARED)); - } - else { - smrCursor = (HCURSOR)::LoadCursor(NULL, IDC_ARROW); - } - - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - ASSERT(smrCursor); - SetCursor(smrCursor); - standardCursor = true; - } - } - } - - mouseLocation = Pt; - RequestRefresh(); +void CSMRRadar::OnAsrContentToBeSaved() { + Logger::info(string(__FUNCSIG__)); + + SaveDataToAsr("Airport", "Active airport for RIMCAS", + getActiveAirport().c_str()); + + SaveDataToAsr("ActiveProfile", "vSMR active profile", + CurrentConfig->getActiveProfileName().c_str()); + + SaveDataToAsr("FontSize", "vSMR font size", + std::to_string(currentFontSize).c_str()); + + SaveDataToAsr("Afterglow", "vSMR Afterglow enabled", + std::to_string(int(Afterglow)).c_str()); + + SaveDataToAsr("AppTrailsDots", "vSMR APPR Trail Dots", + std::to_string(Trail_App).c_str()); + + SaveDataToAsr("GndTrailsDots", "vSMR GRND Trail Dots", + std::to_string(Trail_Gnd).c_str()); + + SaveDataToAsr("PredictedLine", "vSMR Predicted Track Lines", + std::to_string(PredictedLength).c_str()); + + SaveDataToAsr("WIPareas", "vSMR WIP Areas", + std::to_string(wipAreasActive).c_str()); + + SaveDataToAsr("ShowAircraftType", "Show Aircraft Type", + std::to_string(showAircraftType).c_str()); + + SaveDataToAsr("ShowSID", "Show SID", std::to_string(showSID).c_str()); + + SaveDataToAsr("ShowWakeTurb", "Show Wake Turbulence", + std::to_string(showWakeTurb).c_str()); + + // Departure Timer Window settings + SaveDataToAsr("DepWindowShow", "Show Departure Timer Window", + std::to_string(int(showDepartureWindow)).c_str()); + SaveDataToAsr("DepWindowDuration", "Departure Timer Display Duration", + std::to_string(departureDisplayDuration).c_str()); + SaveDataToAsr("DepWindowColCallsign", "Departure Timer Show Callsign", + std::to_string(int(depWindowShowCallsign)).c_str()); + SaveDataToAsr("DepWindowColDest", "Departure Timer Show Destination", + std::to_string(int(depWindowShowDest)).c_str()); + SaveDataToAsr("DepWindowColAcType", "Departure Timer Show AC Type", + std::to_string(int(depWindowShowAcType)).c_str()); + SaveDataToAsr("DepWindowColWake", "Departure Timer Show Wake Cat", + std::to_string(int(depWindowShowWake)).c_str()); + SaveDataToAsr("DepWindowColFreq", "Departure Timer Show Freq", + std::to_string(int(depWindowShowFreq)).c_str()); + SaveDataToAsr("DepWindowColTime", "Departure Timer Show Time", + std::to_string(int(depWindowShowTime)).c_str()); + SaveDataToAsr("DepWindowPosX", "Departure Timer Window X", + std::to_string(DepartureWindowArea.left).c_str()); + SaveDataToAsr("DepWindowPosY", "Departure Timer Window Y", + std::to_string(DepartureWindowArea.top).c_str()); + + string temp = ""; + + for (int i = 1; i < 3; i++) { + string prefix = "SRW" + std::to_string(i); + + temp = std::to_string(appWindows[i]->m_Area.left); + SaveDataToAsr(string(prefix + "TopLeftX").c_str(), "SRW position", + temp.c_str()); + + temp = std::to_string(appWindows[i]->m_Area.top); + SaveDataToAsr(string(prefix + "TopLeftY").c_str(), "SRW position", + temp.c_str()); + + temp = std::to_string(appWindows[i]->m_Area.right); + SaveDataToAsr(string(prefix + "BottomRightX").c_str(), "SRW position", + temp.c_str()); + + temp = std::to_string(appWindows[i]->m_Area.bottom); + SaveDataToAsr(string(prefix + "BottomRightY").c_str(), "SRW position", + temp.c_str()); + + temp = std::to_string(appWindows[i]->m_Offset.x); + SaveDataToAsr(string(prefix + "OffsetX").c_str(), "SRW offset", + temp.c_str()); + + temp = std::to_string(appWindows[i]->m_Offset.y); + SaveDataToAsr(string(prefix + "OffsetY").c_str(), "SRW offset", + temp.c_str()); + + temp = std::to_string(appWindows[i]->m_Filter); + SaveDataToAsr(string(prefix + "Filter").c_str(), "SRW filter", + temp.c_str()); + + temp = std::to_string(appWindows[i]->m_Scale); + SaveDataToAsr(string(prefix + "Scale").c_str(), "SRW range", temp.c_str()); + + temp = std::to_string((int)appWindows[i]->m_Rotation); + SaveDataToAsr(string(prefix + "Rotation").c_str(), "SRW rotation", + temp.c_str()); + + string to_save = "0"; + if (appWindowDisplays[i]) + to_save = "1"; + SaveDataToAsr(string(prefix + "Display").c_str(), + "Display Secondary Radar Window", to_save.c_str()); + } +} +void CSMRRadar::OnMoveScreenObject(int ObjectType, const char *sObjectId, + POINT Pt, RECT Area, bool Released) { + Logger::info(string(__FUNCSIG__)); + + if (ObjectType == APPWINDOW_ONE || ObjectType == APPWINDOW_TWO) { + int appWindowId = ObjectType - APPWINDOW_BASE; + + bool toggleCursor = appWindows[appWindowId]->OnMoveScreenObject( + sObjectId, Pt, Area, Released); + + if (Released) + OnAsrContentToBeSaved(); + + if (!toggleCursor) { + if (standardCursor) { + if (strcmp(sObjectId, "topbar") == 0) + smrCursor = CopyCursor((HCURSOR)::LoadImage( + AfxGetInstanceHandle(), MAKEINTRESOURCE(IDC_SMRMOVEWINDOW), + IMAGE_CURSOR, 0, 0, LR_SHARED)); + else if (strcmp(sObjectId, "resize") == 0) + smrCursor = CopyCursor((HCURSOR)::LoadImage( + AfxGetInstanceHandle(), MAKEINTRESOURCE(IDC_SMRRESIZE), + IMAGE_CURSOR, 0, 0, LR_SHARED)); + + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + ASSERT(smrCursor); + SetCursor(smrCursor); + standardCursor = false; + } + } else { + if (!standardCursor) { + if (customCursor) { + smrCursor = CopyCursor((HCURSOR)::LoadImage( + AfxGetInstanceHandle(), MAKEINTRESOURCE(IDC_SMRCURSOR), + IMAGE_CURSOR, 0, 0, LR_SHARED)); + } else { + smrCursor = (HCURSOR)::LoadCursor(NULL, IDC_ARROW); + } + + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + ASSERT(smrCursor); + SetCursor(smrCursor); + standardCursor = true; + } + } + } + + if (ObjectType == DRAWING_TAG || ObjectType == TAG_CITEM_MANUALCORRELATE || + ObjectType == TAG_CITEM_CALLSIGN || ObjectType == TAG_CITEM_FPBOX || + ObjectType == TAG_CITEM_RWY || ObjectType == TAG_CITEM_SID || + ObjectType == TAG_CITEM_GATE || ObjectType == TAG_CITEM_NO || + ObjectType == TAG_CITEM_GROUNDSTATUS) { + CRadarTarget rt = GetPlugIn()->RadarTargetSelect(sObjectId); + + if (!Released) { + if (standardCursor) { + smrCursor = CopyCursor((HCURSOR)::LoadImage( + AfxGetInstanceHandle(), MAKEINTRESOURCE(IDC_SMRMOVETAG), + IMAGE_CURSOR, 0, 0, LR_SHARED)); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + ASSERT(smrCursor); + SetCursor(smrCursor); + standardCursor = false; + } + } else { + if (!standardCursor) { + if (customCursor) { + smrCursor = CopyCursor((HCURSOR)::LoadImage( + AfxGetInstanceHandle(), MAKEINTRESOURCE(IDC_SMRCURSOR), + IMAGE_CURSOR, 0, 0, LR_SHARED)); + } else { + smrCursor = (HCURSOR)::LoadCursor(NULL, IDC_ARROW); + } + + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + ASSERT(smrCursor); + SetCursor(smrCursor); + standardCursor = true; + } + } + + if (rt.IsValid()) { + CRect Temp = Area; + POINT TagCenterPix = Temp.CenterPoint(); + POINT AcPosPix = + ConvertCoordFromPositionToPixel(GetPlugIn() + ->RadarTargetSelect(sObjectId) + .GetPosition() + .GetPosition()); + POINT CustomTag = {TagCenterPix.x - AcPosPix.x, + TagCenterPix.y - AcPosPix.y}; + + if (CurrentConfig->getActiveProfile()["labels"]["auto_deconfliction"] + .GetBool()) { + double angle = RadToDeg(atan2(CustomTag.y, CustomTag.x)); + angle = fmod(angle + 360, 360); + vector angles; + for (double k = 0.0; k <= 360.0; k += 22.5) + angles.push_back(k); + + TagAngles[sObjectId] = closest(angles, angle); + TagLeaderLineLength[sObjectId] = + max(LeaderLineDefaultlenght, + min(int(DistancePts(AcPosPix, TagCenterPix)), + LeaderLineDefaultlenght * 2)); + + } else { + TagsOffsets[sObjectId] = CustomTag; + } + + GetPlugIn()->SetASELAircraft(GetPlugIn()->FlightPlanSelect(sObjectId)); + + if (Released) { + TagBeingDragged = ""; + } else { + TagBeingDragged = sObjectId; + } + + RequestRefresh(); + } + } + + if (ObjectType == RIMCAS_IAW) { + TimePopupAreas[sObjectId] = Area; + + if (!Released) { + if (standardCursor) { + smrCursor = CopyCursor((HCURSOR)::LoadImage( + AfxGetInstanceHandle(), MAKEINTRESOURCE(IDC_SMRMOVEWINDOW), + IMAGE_CURSOR, 0, 0, LR_SHARED)); + + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + ASSERT(smrCursor); + SetCursor(smrCursor); + standardCursor = false; + } + } else { + if (!standardCursor) { + if (customCursor) { + smrCursor = CopyCursor((HCURSOR)::LoadImage( + AfxGetInstanceHandle(), MAKEINTRESOURCE(IDC_SMRCURSOR), + IMAGE_CURSOR, 0, 0, LR_SHARED)); + } else { + smrCursor = (HCURSOR)::LoadCursor(NULL, IDC_ARROW); + } + + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + ASSERT(smrCursor); + SetCursor(smrCursor); + standardCursor = true; + } + } + } + + // Handle Departure Timer Window movement + if (ObjectType == RIMCAS_DEP_WINDOW) { + DepartureWindowArea = Area; + OnAsrContentToBeSaved(); + + if (!Released) { + if (standardCursor) { + smrCursor = CopyCursor((HCURSOR)::LoadImage( + AfxGetInstanceHandle(), MAKEINTRESOURCE(IDC_SMRMOVEWINDOW), + IMAGE_CURSOR, 0, 0, LR_SHARED)); + + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + ASSERT(smrCursor); + SetCursor(smrCursor); + standardCursor = false; + } + } else { + if (!standardCursor) { + if (customCursor) { + smrCursor = CopyCursor((HCURSOR)::LoadImage( + AfxGetInstanceHandle(), MAKEINTRESOURCE(IDC_SMRCURSOR), + IMAGE_CURSOR, 0, 0, LR_SHARED)); + } else { + smrCursor = (HCURSOR)::LoadCursor(NULL, IDC_ARROW); + } + + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + ASSERT(smrCursor); + SetCursor(smrCursor); + standardCursor = true; + } + } + } + + mouseLocation = Pt; + RequestRefresh(); } -void CSMRRadar::OnOverScreenObject(int ObjectType, const char * sObjectId, POINT Pt, RECT Area) -{ - Logger::info(string(__FUNCSIG__)); - mouseLocation = Pt; - RequestRefresh(); +void CSMRRadar::OnOverScreenObject(int ObjectType, const char *sObjectId, + POINT Pt, RECT Area) { + Logger::info(string(__FUNCSIG__)); + mouseLocation = Pt; + RequestRefresh(); } -void CSMRRadar::OnClickScreenObject(int ObjectType, const char * sObjectId, POINT Pt, RECT Area, int Button) -{ - Logger::info(string(__FUNCSIG__)); - mouseLocation = Pt; - - if (ObjectType == APPWINDOW_ONE || ObjectType == APPWINDOW_TWO) { - int appWindowId = ObjectType - APPWINDOW_BASE; - - if (strcmp(sObjectId, "close") == 0) { - appWindowDisplays[appWindowId] = false; - OnAsrContentToBeSaved(); - } - if (strcmp(sObjectId, "range") == 0) { - GetPlugIn()->OpenPopupList(Area, "SRW Zoom", 1); - GetPlugIn()->AddPopupListElement("55", "", RIMCAS_UPDATERANGE + appWindowId, false, int(appWindows[appWindowId]->m_Scale == 55)); - GetPlugIn()->AddPopupListElement("50", "", RIMCAS_UPDATERANGE + appWindowId, false, int(appWindows[appWindowId]->m_Scale == 50)); - GetPlugIn()->AddPopupListElement("45", "", RIMCAS_UPDATERANGE + appWindowId, false, int(appWindows[appWindowId]->m_Scale == 45)); - GetPlugIn()->AddPopupListElement("40", "", RIMCAS_UPDATERANGE + appWindowId, false, int(appWindows[appWindowId]->m_Scale == 40)); - GetPlugIn()->AddPopupListElement("35", "", RIMCAS_UPDATERANGE + appWindowId, false, int(appWindows[appWindowId]->m_Scale == 35)); - GetPlugIn()->AddPopupListElement("30", "", RIMCAS_UPDATERANGE + appWindowId, false, int(appWindows[appWindowId]->m_Scale == 30)); - GetPlugIn()->AddPopupListElement("25", "", RIMCAS_UPDATERANGE + appWindowId, false, int(appWindows[appWindowId]->m_Scale == 25)); - GetPlugIn()->AddPopupListElement("20", "", RIMCAS_UPDATERANGE + appWindowId, false, int(appWindows[appWindowId]->m_Scale == 20)); - GetPlugIn()->AddPopupListElement("15", "", RIMCAS_UPDATERANGE + appWindowId, false, int(appWindows[appWindowId]->m_Scale == 15)); - GetPlugIn()->AddPopupListElement("10", "", RIMCAS_UPDATERANGE + appWindowId, false, int(appWindows[appWindowId]->m_Scale == 10)); - GetPlugIn()->AddPopupListElement("5", "", RIMCAS_UPDATERANGE + appWindowId, false, int(appWindows[appWindowId]->m_Scale == 5)); - GetPlugIn()->AddPopupListElement("1", "", RIMCAS_UPDATERANGE + appWindowId, false, int(appWindows[appWindowId]->m_Scale == 1)); - GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, false, true); - } - if (strcmp(sObjectId, "filter") == 0) { - GetPlugIn()->OpenPopupList(Area, "SRW Filter (ft)", 1); - GetPlugIn()->AddPopupListElement("UNL", "", RIMCAS_UPDATEFILTER + appWindowId, false, int(appWindows[appWindowId]->m_Filter == 66000)); - GetPlugIn()->AddPopupListElement("9500", "", RIMCAS_UPDATEFILTER + appWindowId, false, int(appWindows[appWindowId]->m_Filter == 9500)); - GetPlugIn()->AddPopupListElement("8500", "", RIMCAS_UPDATEFILTER + appWindowId, false, int(appWindows[appWindowId]->m_Filter == 8500)); - GetPlugIn()->AddPopupListElement("7500", "", RIMCAS_UPDATEFILTER + appWindowId, false, int(appWindows[appWindowId]->m_Filter == 7500)); - GetPlugIn()->AddPopupListElement("6500", "", RIMCAS_UPDATEFILTER + appWindowId, false, int(appWindows[appWindowId]->m_Filter == 6500)); - GetPlugIn()->AddPopupListElement("5500", "", RIMCAS_UPDATEFILTER + appWindowId, false, int(appWindows[appWindowId]->m_Filter == 5500)); - GetPlugIn()->AddPopupListElement("4500", "", RIMCAS_UPDATEFILTER + appWindowId, false, int(appWindows[appWindowId]->m_Filter == 4500)); - GetPlugIn()->AddPopupListElement("3500", "", RIMCAS_UPDATEFILTER + appWindowId, false, int(appWindows[appWindowId]->m_Filter == 3500)); - GetPlugIn()->AddPopupListElement("2500", "", RIMCAS_UPDATEFILTER + appWindowId, false, int(appWindows[appWindowId]->m_Filter == 2500)); - GetPlugIn()->AddPopupListElement("1500", "", RIMCAS_UPDATEFILTER + appWindowId, false, int(appWindows[appWindowId]->m_Filter == 1500)); - GetPlugIn()->AddPopupListElement("500", "", RIMCAS_UPDATEFILTER + appWindowId, false, int(appWindows[appWindowId]->m_Filter == 500)); - string tmp = std::to_string(GetPlugIn()->GetTransitionAltitude()); - GetPlugIn()->AddPopupListElement(tmp.c_str(), "", RIMCAS_UPDATEFILTER + appWindowId, false, 2, false, true); - GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, false, true); - } - if (strcmp(sObjectId, "rotate") == 0) { - GetPlugIn()->OpenPopupList(Area, "SRW Rotate (deg)", 1); - for (int k = 0; k <= 360; k++) - { - string tmp = std::to_string(k); - GetPlugIn()->AddPopupListElement(tmp.c_str(), "", RIMCAS_UPDATEROTATE + appWindowId, false, int(appWindows[appWindowId]->m_Rotation == k)); - } - GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, false, true); - } - } - - if (ObjectType == RIMCAS_ACTIVE_AIRPORT) { - GetPlugIn()->OpenPopupEdit(Area, RIMCAS_ACTIVE_AIRPORT_FUNC, getActiveAirport().c_str()); - } - - if (ObjectType == DRAWING_BACKGROUND_CLICK) - { - if (QDMSelectEnabled) - { - if (Button == BUTTON_LEFT) - { - QDMSelectPt = Pt; - RequestRefresh(); - } - - if (Button == BUTTON_RIGHT) - { - QDMSelectEnabled = false; - RequestRefresh(); - } - } - - if (QDMenabled) - { - if (Button == BUTTON_RIGHT) - { - QDMenabled = false; - RequestRefresh(); - } - } - } - - if (ObjectType == RIMCAS_MENU) { - - if (strcmp(sObjectId, "DisplayMenu") == 0) { - Area.top = Area.top + 30; - Area.bottom = Area.bottom + 30; - - GetPlugIn()->OpenPopupList(Area, "Display Menu", 1); - GetPlugIn()->AddPopupListElement("QDR Fixed Reference", "", RIMCAS_QDM_TOGGLE); - GetPlugIn()->AddPopupListElement("QDR Select Reference", "", RIMCAS_QDM_SELECT_TOGGLE); - GetPlugIn()->AddPopupListElement("SRW 1", "", APPWINDOW_ONE, false, int(appWindowDisplays[1])); - GetPlugIn()->AddPopupListElement("SRW 2", "", APPWINDOW_TWO, false, int(appWindowDisplays[2])); - GetPlugIn()->AddPopupListElement("WIP Areas", "", WIP_AREAS, false, int(wipAreasActive)); - GetPlugIn()->AddPopupListElement("Show Aircraft Type", "", RIMCAS_TOGGLE_AIRCRAFT_TYPE, false, int(showAircraftType)); - GetPlugIn()->AddPopupListElement("Show SID", "", RIMCAS_TOGGLE_SID, false, int(showSID)); - GetPlugIn()->AddPopupListElement("Show Wake Catagory", "", RIMCAS_TOGGLE_WAKE_TURB, false, int(showWakeTurb)); - GetPlugIn()->AddPopupListElement("Profiles", "", RIMCAS_OPEN_LIST); - GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, false, true); - } - - if (strcmp(sObjectId, "TargetMenu") == 0) { - Area.top = Area.top + 30; - Area.bottom = Area.bottom + 30; - - GetPlugIn()->OpenPopupList(Area, "Target", 1); - GetPlugIn()->AddPopupListElement("Label Font Size", "", RIMCAS_OPEN_LIST); - GetPlugIn()->AddPopupListElement("Afterglow", "", RIMCAS_UPDATE_AFTERGLOW, false, int(Afterglow)); - GetPlugIn()->AddPopupListElement("GRND Trail Dots", "", RIMCAS_OPEN_LIST); - GetPlugIn()->AddPopupListElement("APPR Trail Dots", "", RIMCAS_OPEN_LIST); - GetPlugIn()->AddPopupListElement("Predicted Track Line", "", RIMCAS_OPEN_LIST); - GetPlugIn()->AddPopupListElement("Acquire", "", RIMCAS_UPDATE_ACQUIRE); - GetPlugIn()->AddPopupListElement("Release", "", RIMCAS_UPDATE_RELEASE); - GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, false, true); - } - - if (strcmp(sObjectId, "MapMenu") == 0) { - Area.top = Area.top + 30; - Area.bottom = Area.bottom + 30; - - GetPlugIn()->OpenPopupList(Area, "Maps", 1); - GetPlugIn()->AddPopupListElement("Airport Maps", "", RIMCAS_OPEN_LIST); - GetPlugIn()->AddPopupListElement("Custom Maps", "", RIMCAS_OPEN_LIST); - GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, false, true); - } - - if (strcmp(sObjectId, "ColourMenu") == 0) { - Area.top = Area.top + 30; - Area.bottom = Area.bottom + 30; - - GetPlugIn()->OpenPopupList(Area, "Colours", 1); - GetPlugIn()->AddPopupListElement("Colour Settings", "", RIMCAS_OPEN_LIST); - GetPlugIn()->AddPopupListElement("Brightness", "", RIMCAS_OPEN_LIST); - GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, false, true); - } - - if (strcmp(sObjectId, "RIMCASMenu") == 0) { - Area.top = Area.top + 30; - Area.bottom = Area.bottom + 30; - - GetPlugIn()->OpenPopupList(Area, "Alerts", 1); - GetPlugIn()->AddPopupListElement("Conflict Alert ARR", "", RIMCAS_OPEN_LIST); - GetPlugIn()->AddPopupListElement("Conflict Alert DEP", "", RIMCAS_OPEN_LIST); - GetPlugIn()->AddPopupListElement("Runway closed", "", RIMCAS_OPEN_LIST); - GetPlugIn()->AddPopupListElement("Visibility", "", RIMCAS_OPEN_LIST); - GetPlugIn()->AddPopupListElement("RIMCAS Detail", "", RIMCAS_OPEN_LIST); - GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, false, true); - } - - if (strcmp(sObjectId, "/") == 0) - { - if (Button == BUTTON_LEFT) - { - DistanceToolActive = !DistanceToolActive; - if (!DistanceToolActive) - ActiveDistance = pair("", ""); - - if (DistanceToolActive) - { - QDMenabled = false; - QDMSelectEnabled = false; - } - } - if (Button == BUTTON_RIGHT) - { - DistanceToolActive = false; - ActiveDistance = pair("", ""); - DistanceTools.clear(); - } - - } - - } - - if (ObjectType == DRAWING_TAG || ObjectType == DRAWING_AC_SYMBOL) { - CRadarTarget rt = GetPlugIn()->RadarTargetSelect(sObjectId); - //GetPlugIn()->SetASELAircraft(rt); // NOTE: This does NOT work eventhough the api says it should? - GetPlugIn()->SetASELAircraft(GetPlugIn()->FlightPlanSelect(sObjectId)); // make sure the correct aircraft is selected before calling 'StartTagFunction' - - if (rt.GetCorrelatedFlightPlan().IsValid()) { - StartTagFunction(rt.GetCallsign(), NULL, EuroScopePlugIn::TAG_ITEM_TYPE_CALLSIGN, rt.GetCallsign(), NULL, EuroScopePlugIn::TAG_ITEM_FUNCTION_NO, Pt, Area); - } - - // Release & correlate actions - - if (ReleaseInProgress || AcquireInProgress) - { - if (ReleaseInProgress) - { - ReleaseInProgress = NeedCorrelateCursor = false; - - ReleasedTracks.push_back(rt.GetSystemID()); - - if (std::find(ManuallyCorrelated.begin(), ManuallyCorrelated.end(), rt.GetSystemID()) != ManuallyCorrelated.end()) - ManuallyCorrelated.erase(std::find(ManuallyCorrelated.begin(), ManuallyCorrelated.end(), rt.GetSystemID())); - } - - if (AcquireInProgress) - { - AcquireInProgress = NeedCorrelateCursor = false; - - ManuallyCorrelated.push_back(rt.GetSystemID()); - - if (std::find(ReleasedTracks.begin(), ReleasedTracks.end(), rt.GetSystemID()) != ReleasedTracks.end()) - ReleasedTracks.erase(std::find(ReleasedTracks.begin(), ReleasedTracks.end(), rt.GetSystemID())); - } - - - CorrelateCursor(); - - return; - } - - if (ObjectType == DRAWING_AC_SYMBOL) - { - if (QDMSelectEnabled) - { - if (Button == BUTTON_LEFT) - { - QDMSelectPt = Pt; - RequestRefresh(); - } - } - else if (DistanceToolActive) { - if (ActiveDistance.first == "") - { - ActiveDistance.first = sObjectId; - } - else if (ActiveDistance.second == "") - { - ActiveDistance.second = sObjectId; - DistanceTools.insert(ActiveDistance); - ActiveDistance = pair("", ""); - DistanceToolActive = false; - } - RequestRefresh(); - } - else - { - if (TagsOffsets.find(sObjectId) != TagsOffsets.end()) - TagsOffsets.erase(sObjectId); - - if (Button == BUTTON_LEFT) - { - if (TagAngles.find(sObjectId) == TagAngles.end()) - { - TagAngles[sObjectId] = 0; - } else - { - TagAngles[sObjectId] = fmod(TagAngles[sObjectId] - 22.5, 360); - } - } - - if (Button == BUTTON_RIGHT) - { - if (TagAngles.find(sObjectId) == TagAngles.end()) - { - TagAngles[sObjectId] = 0; - } - else - { - TagAngles[sObjectId] = fmod(TagAngles[sObjectId] + 22.5, 360); - } - } - - RequestRefresh(); - } - } - } - - if (ObjectType == DRAWING_AC_SYMBOL_APPWINDOW1 || ObjectType == DRAWING_AC_SYMBOL_APPWINDOW2) - { - if (DistanceToolActive) { - if (ActiveDistance.first == "") - { - ActiveDistance.first = sObjectId; - } - else if (ActiveDistance.second == "") - { - ActiveDistance.second = sObjectId; - DistanceTools.insert(ActiveDistance); - ActiveDistance = pair("", ""); - DistanceToolActive = false; - } - RequestRefresh(); - } else - { - if (ObjectType == DRAWING_AC_SYMBOL_APPWINDOW1) - appWindows[1]->OnClickScreenObject(sObjectId, Pt, Button); - - if (ObjectType == DRAWING_AC_SYMBOL_APPWINDOW2) - appWindows[2]->OnClickScreenObject(sObjectId, Pt, Button); - } - } - - map TagObjectMiddleTypes = { - { TAG_CITEM_CALLSIGN, TAG_ITEM_FUNCTION_COMMUNICATION_POPUP }, - }; - - map TagObjectRightTypes = { - { TAG_CITEM_CALLSIGN, TAG_ITEM_FUNCTION_HANDOFF_POPUP_MENU }, - { TAG_CITEM_FPBOX, TAG_ITEM_FUNCTION_OPEN_FP_DIALOG }, - { TAG_CITEM_RWY, TAG_ITEM_FUNCTION_ASSIGNED_RUNWAY }, - { TAG_CITEM_SID, TAG_ITEM_FUNCTION_ASSIGNED_SID }, - { TAG_CITEM_GATE, TAG_ITEM_FUNCTION_EDIT_SCRATCH_PAD }, - { TAG_CITEM_GROUNDSTATUS, TAG_ITEM_FUNCTION_SET_GROUND_STATUS } - }; - - if (Button == BUTTON_LEFT) { - CRadarTarget rt = GetPlugIn()->RadarTargetSelect(sObjectId); - GetPlugIn()->SetASELAircraft(GetPlugIn()->FlightPlanSelect(sObjectId)); - if (rt.GetCorrelatedFlightPlan().IsValid()) { - StartTagFunction(rt.GetCallsign(), NULL, TAG_ITEM_TYPE_CALLSIGN, rt.GetCallsign(), NULL, TAG_ITEM_FUNCTION_NO, Pt, Area); - } - } - - if (Button == BUTTON_MIDDLE && TagObjectMiddleTypes[ObjectType]) { - int TagMenu = TagObjectMiddleTypes[ObjectType]; - CRadarTarget rt = GetPlugIn()->RadarTargetSelect(sObjectId); - GetPlugIn()->SetASELAircraft(GetPlugIn()->FlightPlanSelect(sObjectId)); - StartTagFunction(rt.GetCallsign(), NULL, EuroScopePlugIn::TAG_ITEM_TYPE_CALLSIGN, rt.GetCallsign(), NULL, TagMenu, Pt, Area); - } - - if (Button == BUTTON_RIGHT && TagObjectRightTypes[ObjectType]) { - int TagMenu = TagObjectRightTypes[ObjectType]; - CRadarTarget rt = GetPlugIn()->RadarTargetSelect(sObjectId); - GetPlugIn()->SetASELAircraft(GetPlugIn()->FlightPlanSelect(sObjectId)); - StartTagFunction(rt.GetCallsign(), NULL, EuroScopePlugIn::TAG_ITEM_TYPE_CALLSIGN, rt.GetCallsign(), NULL, TagMenu, Pt, Area); - } - - if (ObjectType == RIMCAS_DISTANCE_TOOL) - { - vector s = split(sObjectId, ','); - pair toRemove = pair(s.front(), s.back()); - - typedef multimap::iterator iterator; - std::pair iterpair = DistanceTools.equal_range(toRemove.first); - - iterator it = iterpair.first; - for (; it != iterpair.second; ++it) { - if (it->second == toRemove.second) { - it = DistanceTools.erase(it); - break; - } - } - - } - - RequestRefresh(); +void CSMRRadar::OnClickScreenObject(int ObjectType, const char *sObjectId, + POINT Pt, RECT Area, int Button) { + Logger::info(string(__FUNCSIG__)); + mouseLocation = Pt; + + if (ObjectType == APPWINDOW_ONE || ObjectType == APPWINDOW_TWO) { + int appWindowId = ObjectType - APPWINDOW_BASE; + + if (strcmp(sObjectId, "close") == 0) { + appWindowDisplays[appWindowId] = false; + OnAsrContentToBeSaved(); + } + if (strcmp(sObjectId, "range") == 0) { + GetPlugIn()->OpenPopupList(Area, "SRW Zoom", 1); + GetPlugIn()->AddPopupListElement( + "55", "", RIMCAS_UPDATERANGE + appWindowId, false, + int(appWindows[appWindowId]->m_Scale == 55)); + GetPlugIn()->AddPopupListElement( + "50", "", RIMCAS_UPDATERANGE + appWindowId, false, + int(appWindows[appWindowId]->m_Scale == 50)); + GetPlugIn()->AddPopupListElement( + "45", "", RIMCAS_UPDATERANGE + appWindowId, false, + int(appWindows[appWindowId]->m_Scale == 45)); + GetPlugIn()->AddPopupListElement( + "40", "", RIMCAS_UPDATERANGE + appWindowId, false, + int(appWindows[appWindowId]->m_Scale == 40)); + GetPlugIn()->AddPopupListElement( + "35", "", RIMCAS_UPDATERANGE + appWindowId, false, + int(appWindows[appWindowId]->m_Scale == 35)); + GetPlugIn()->AddPopupListElement( + "30", "", RIMCAS_UPDATERANGE + appWindowId, false, + int(appWindows[appWindowId]->m_Scale == 30)); + GetPlugIn()->AddPopupListElement( + "25", "", RIMCAS_UPDATERANGE + appWindowId, false, + int(appWindows[appWindowId]->m_Scale == 25)); + GetPlugIn()->AddPopupListElement( + "20", "", RIMCAS_UPDATERANGE + appWindowId, false, + int(appWindows[appWindowId]->m_Scale == 20)); + GetPlugIn()->AddPopupListElement( + "15", "", RIMCAS_UPDATERANGE + appWindowId, false, + int(appWindows[appWindowId]->m_Scale == 15)); + GetPlugIn()->AddPopupListElement( + "10", "", RIMCAS_UPDATERANGE + appWindowId, false, + int(appWindows[appWindowId]->m_Scale == 10)); + GetPlugIn()->AddPopupListElement( + "5", "", RIMCAS_UPDATERANGE + appWindowId, false, + int(appWindows[appWindowId]->m_Scale == 5)); + GetPlugIn()->AddPopupListElement( + "1", "", RIMCAS_UPDATERANGE + appWindowId, false, + int(appWindows[appWindowId]->m_Scale == 1)); + GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, + false, true); + } + if (strcmp(sObjectId, "filter") == 0) { + GetPlugIn()->OpenPopupList(Area, "SRW Filter (ft)", 1); + GetPlugIn()->AddPopupListElement( + "UNL", "", RIMCAS_UPDATEFILTER + appWindowId, false, + int(appWindows[appWindowId]->m_Filter == 66000)); + GetPlugIn()->AddPopupListElement( + "9500", "", RIMCAS_UPDATEFILTER + appWindowId, false, + int(appWindows[appWindowId]->m_Filter == 9500)); + GetPlugIn()->AddPopupListElement( + "8500", "", RIMCAS_UPDATEFILTER + appWindowId, false, + int(appWindows[appWindowId]->m_Filter == 8500)); + GetPlugIn()->AddPopupListElement( + "7500", "", RIMCAS_UPDATEFILTER + appWindowId, false, + int(appWindows[appWindowId]->m_Filter == 7500)); + GetPlugIn()->AddPopupListElement( + "6500", "", RIMCAS_UPDATEFILTER + appWindowId, false, + int(appWindows[appWindowId]->m_Filter == 6500)); + GetPlugIn()->AddPopupListElement( + "5500", "", RIMCAS_UPDATEFILTER + appWindowId, false, + int(appWindows[appWindowId]->m_Filter == 5500)); + GetPlugIn()->AddPopupListElement( + "4500", "", RIMCAS_UPDATEFILTER + appWindowId, false, + int(appWindows[appWindowId]->m_Filter == 4500)); + GetPlugIn()->AddPopupListElement( + "3500", "", RIMCAS_UPDATEFILTER + appWindowId, false, + int(appWindows[appWindowId]->m_Filter == 3500)); + GetPlugIn()->AddPopupListElement( + "2500", "", RIMCAS_UPDATEFILTER + appWindowId, false, + int(appWindows[appWindowId]->m_Filter == 2500)); + GetPlugIn()->AddPopupListElement( + "1500", "", RIMCAS_UPDATEFILTER + appWindowId, false, + int(appWindows[appWindowId]->m_Filter == 1500)); + GetPlugIn()->AddPopupListElement( + "500", "", RIMCAS_UPDATEFILTER + appWindowId, false, + int(appWindows[appWindowId]->m_Filter == 500)); + string tmp = std::to_string(GetPlugIn()->GetTransitionAltitude()); + GetPlugIn()->AddPopupListElement(tmp.c_str(), "", + RIMCAS_UPDATEFILTER + appWindowId, false, + 2, false, true); + GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, + false, true); + } + if (strcmp(sObjectId, "rotate") == 0) { + GetPlugIn()->OpenPopupList(Area, "SRW Rotate (deg)", 1); + for (int k = 0; k <= 360; k++) { + string tmp = std::to_string(k); + GetPlugIn()->AddPopupListElement( + tmp.c_str(), "", RIMCAS_UPDATEROTATE + appWindowId, false, + int(appWindows[appWindowId]->m_Rotation == k)); + } + GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, + false, true); + } + } + + if (ObjectType == RIMCAS_ACTIVE_AIRPORT) { + GetPlugIn()->OpenPopupEdit(Area, RIMCAS_ACTIVE_AIRPORT_FUNC, + getActiveAirport().c_str()); + } + + // Handle click on departure timer row - dismiss the aircraft + if (ObjectType == RIMCAS_DEP_WINDOW_ROW) { + RimcasInstance->DismissDeparture(sObjectId); + RequestRefresh(); + } + + if (ObjectType == DRAWING_BACKGROUND_CLICK) { + if (QDMSelectEnabled) { + if (Button == BUTTON_LEFT) { + QDMSelectPt = Pt; + RequestRefresh(); + } + + if (Button == BUTTON_RIGHT) { + QDMSelectEnabled = false; + RequestRefresh(); + } + } + + if (QDMenabled) { + if (Button == BUTTON_RIGHT) { + QDMenabled = false; + RequestRefresh(); + } + } + } + + if (ObjectType == RIMCAS_MENU) { + + if (strcmp(sObjectId, "DisplayMenu") == 0) { + Area.top = Area.top + 30; + Area.bottom = Area.bottom + 30; + + GetPlugIn()->OpenPopupList(Area, "Display Menu", 1); + GetPlugIn()->AddPopupListElement("QDR Fixed Reference", "", + RIMCAS_QDM_TOGGLE); + GetPlugIn()->AddPopupListElement("QDR Select Reference", "", + RIMCAS_QDM_SELECT_TOGGLE); + GetPlugIn()->AddPopupListElement("SRW 1", "", APPWINDOW_ONE, false, + int(appWindowDisplays[1])); + GetPlugIn()->AddPopupListElement("SRW 2", "", APPWINDOW_TWO, false, + int(appWindowDisplays[2])); + GetPlugIn()->AddPopupListElement("WIP Areas", "", WIP_AREAS, false, + int(wipAreasActive)); + GetPlugIn()->AddPopupListElement("Departure Timer", "", + RIMCAS_DEP_WINDOW_TOGGLE, false, + int(showDepartureWindow)); + GetPlugIn()->AddPopupListElement("Show Aircraft Type", "", + RIMCAS_TOGGLE_AIRCRAFT_TYPE, false, + int(showAircraftType)); + GetPlugIn()->AddPopupListElement("Show SID", "", RIMCAS_TOGGLE_SID, false, + int(showSID)); + GetPlugIn()->AddPopupListElement("Show Wake Catagory", "", + RIMCAS_TOGGLE_WAKE_TURB, false, + int(showWakeTurb)); + GetPlugIn()->AddPopupListElement("Profiles", "", RIMCAS_OPEN_LIST); + GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, + false, true); + } + + if (strcmp(sObjectId, "TargetMenu") == 0) { + Area.top = Area.top + 30; + Area.bottom = Area.bottom + 30; + + GetPlugIn()->OpenPopupList(Area, "Target", 1); + GetPlugIn()->AddPopupListElement("Label Font Size", "", RIMCAS_OPEN_LIST); + GetPlugIn()->AddPopupListElement("Afterglow", "", RIMCAS_UPDATE_AFTERGLOW, + false, int(Afterglow)); + GetPlugIn()->AddPopupListElement("GRND Trail Dots", "", RIMCAS_OPEN_LIST); + GetPlugIn()->AddPopupListElement("APPR Trail Dots", "", RIMCAS_OPEN_LIST); + GetPlugIn()->AddPopupListElement("Predicted Track Line", "", + RIMCAS_OPEN_LIST); + GetPlugIn()->AddPopupListElement("Acquire", "", RIMCAS_UPDATE_ACQUIRE); + GetPlugIn()->AddPopupListElement("Release", "", RIMCAS_UPDATE_RELEASE); + GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, + false, true); + } + + if (strcmp(sObjectId, "MapMenu") == 0) { + Area.top = Area.top + 30; + Area.bottom = Area.bottom + 30; + + GetPlugIn()->OpenPopupList(Area, "Maps", 1); + GetPlugIn()->AddPopupListElement("Airport Maps", "", RIMCAS_OPEN_LIST); + GetPlugIn()->AddPopupListElement("Custom Maps", "", RIMCAS_OPEN_LIST); + GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, + false, true); + } + + if (strcmp(sObjectId, "ColourMenu") == 0) { + Area.top = Area.top + 30; + Area.bottom = Area.bottom + 30; + + GetPlugIn()->OpenPopupList(Area, "Colours", 1); + GetPlugIn()->AddPopupListElement("Colour Settings", "", RIMCAS_OPEN_LIST); + GetPlugIn()->AddPopupListElement("Brightness", "", RIMCAS_OPEN_LIST); + GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, + false, true); + } + + if (strcmp(sObjectId, "RIMCASMenu") == 0) { + Area.top = Area.top + 30; + Area.bottom = Area.bottom + 30; + + GetPlugIn()->OpenPopupList(Area, "Alerts", 1); + GetPlugIn()->AddPopupListElement("Conflict Alert ARR", "", + RIMCAS_OPEN_LIST); + GetPlugIn()->AddPopupListElement("Conflict Alert DEP", "", + RIMCAS_OPEN_LIST); + GetPlugIn()->AddPopupListElement("Runway closed", "", RIMCAS_OPEN_LIST); + GetPlugIn()->AddPopupListElement("Visibility", "", RIMCAS_OPEN_LIST); + GetPlugIn()->AddPopupListElement("RIMCAS Detail", "", RIMCAS_OPEN_LIST); + GetPlugIn()->AddPopupListElement("Departure Timer", "", RIMCAS_OPEN_LIST); + GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, + false, true); + } + + if (strcmp(sObjectId, "/") == 0) { + if (Button == BUTTON_LEFT) { + DistanceToolActive = !DistanceToolActive; + if (!DistanceToolActive) + ActiveDistance = pair("", ""); + + if (DistanceToolActive) { + QDMenabled = false; + QDMSelectEnabled = false; + } + } + if (Button == BUTTON_RIGHT) { + DistanceToolActive = false; + ActiveDistance = pair("", ""); + DistanceTools.clear(); + } + } + } + + if (ObjectType == DRAWING_TAG || ObjectType == DRAWING_AC_SYMBOL) { + CRadarTarget rt = GetPlugIn()->RadarTargetSelect(sObjectId); + // GetPlugIn()->SetASELAircraft(rt); // NOTE: This does NOT work eventhough + // the api says it should? + GetPlugIn()->SetASELAircraft(GetPlugIn()->FlightPlanSelect( + sObjectId)); // make sure the correct aircraft is selected before + // calling 'StartTagFunction' + + if (rt.GetCorrelatedFlightPlan().IsValid()) { + StartTagFunction(rt.GetCallsign(), NULL, + EuroScopePlugIn::TAG_ITEM_TYPE_CALLSIGN, + rt.GetCallsign(), NULL, + EuroScopePlugIn::TAG_ITEM_FUNCTION_NO, Pt, Area); + } + + // Release & correlate actions + + if (ReleaseInProgress || AcquireInProgress) { + if (ReleaseInProgress) { + ReleaseInProgress = NeedCorrelateCursor = false; + + ReleasedTracks.push_back(rt.GetSystemID()); + + if (std::find(ManuallyCorrelated.begin(), ManuallyCorrelated.end(), + rt.GetSystemID()) != ManuallyCorrelated.end()) + ManuallyCorrelated.erase(std::find(ManuallyCorrelated.begin(), + ManuallyCorrelated.end(), + rt.GetSystemID())); + } + + if (AcquireInProgress) { + AcquireInProgress = NeedCorrelateCursor = false; + + ManuallyCorrelated.push_back(rt.GetSystemID()); + + if (std::find(ReleasedTracks.begin(), ReleasedTracks.end(), + rt.GetSystemID()) != ReleasedTracks.end()) + ReleasedTracks.erase(std::find( + ReleasedTracks.begin(), ReleasedTracks.end(), rt.GetSystemID())); + } + + CorrelateCursor(); + + return; + } + + if (ObjectType == DRAWING_AC_SYMBOL) { + if (QDMSelectEnabled) { + if (Button == BUTTON_LEFT) { + QDMSelectPt = Pt; + RequestRefresh(); + } + } else if (DistanceToolActive) { + if (ActiveDistance.first == "") { + ActiveDistance.first = sObjectId; + } else if (ActiveDistance.second == "") { + ActiveDistance.second = sObjectId; + DistanceTools.insert(ActiveDistance); + ActiveDistance = pair("", ""); + DistanceToolActive = false; + } + RequestRefresh(); + } else { + if (TagsOffsets.find(sObjectId) != TagsOffsets.end()) + TagsOffsets.erase(sObjectId); + + if (Button == BUTTON_LEFT) { + if (TagAngles.find(sObjectId) == TagAngles.end()) { + TagAngles[sObjectId] = 0; + } else { + TagAngles[sObjectId] = fmod(TagAngles[sObjectId] - 22.5, 360); + } + } + + if (Button == BUTTON_RIGHT) { + if (TagAngles.find(sObjectId) == TagAngles.end()) { + TagAngles[sObjectId] = 0; + } else { + TagAngles[sObjectId] = fmod(TagAngles[sObjectId] + 22.5, 360); + } + } + + RequestRefresh(); + } + } + } + + if (ObjectType == DRAWING_AC_SYMBOL_APPWINDOW1 || + ObjectType == DRAWING_AC_SYMBOL_APPWINDOW2) { + if (DistanceToolActive) { + if (ActiveDistance.first == "") { + ActiveDistance.first = sObjectId; + } else if (ActiveDistance.second == "") { + ActiveDistance.second = sObjectId; + DistanceTools.insert(ActiveDistance); + ActiveDistance = pair("", ""); + DistanceToolActive = false; + } + RequestRefresh(); + } else { + if (ObjectType == DRAWING_AC_SYMBOL_APPWINDOW1) + appWindows[1]->OnClickScreenObject(sObjectId, Pt, Button); + + if (ObjectType == DRAWING_AC_SYMBOL_APPWINDOW2) + appWindows[2]->OnClickScreenObject(sObjectId, Pt, Button); + } + } + + map TagObjectMiddleTypes = { + {TAG_CITEM_CALLSIGN, TAG_ITEM_FUNCTION_COMMUNICATION_POPUP}, + }; + + map TagObjectRightTypes = { + {TAG_CITEM_CALLSIGN, TAG_ITEM_FUNCTION_HANDOFF_POPUP_MENU}, + {TAG_CITEM_FPBOX, TAG_ITEM_FUNCTION_OPEN_FP_DIALOG}, + {TAG_CITEM_RWY, TAG_ITEM_FUNCTION_ASSIGNED_RUNWAY}, + {TAG_CITEM_SID, TAG_ITEM_FUNCTION_ASSIGNED_SID}, + {TAG_CITEM_GATE, TAG_ITEM_FUNCTION_EDIT_SCRATCH_PAD}, + {TAG_CITEM_GROUNDSTATUS, TAG_ITEM_FUNCTION_SET_GROUND_STATUS}}; + + if (Button == BUTTON_LEFT) { + CRadarTarget rt = GetPlugIn()->RadarTargetSelect(sObjectId); + GetPlugIn()->SetASELAircraft(GetPlugIn()->FlightPlanSelect(sObjectId)); + if (rt.GetCorrelatedFlightPlan().IsValid()) { + StartTagFunction(rt.GetCallsign(), NULL, TAG_ITEM_TYPE_CALLSIGN, + rt.GetCallsign(), NULL, TAG_ITEM_FUNCTION_NO, Pt, Area); + } + } + + if (Button == BUTTON_MIDDLE && TagObjectMiddleTypes[ObjectType]) { + int TagMenu = TagObjectMiddleTypes[ObjectType]; + CRadarTarget rt = GetPlugIn()->RadarTargetSelect(sObjectId); + GetPlugIn()->SetASELAircraft(GetPlugIn()->FlightPlanSelect(sObjectId)); + StartTagFunction(rt.GetCallsign(), NULL, + EuroScopePlugIn::TAG_ITEM_TYPE_CALLSIGN, rt.GetCallsign(), + NULL, TagMenu, Pt, Area); + } + + if (Button == BUTTON_RIGHT && TagObjectRightTypes[ObjectType]) { + int TagMenu = TagObjectRightTypes[ObjectType]; + CRadarTarget rt = GetPlugIn()->RadarTargetSelect(sObjectId); + GetPlugIn()->SetASELAircraft(GetPlugIn()->FlightPlanSelect(sObjectId)); + StartTagFunction(rt.GetCallsign(), NULL, + EuroScopePlugIn::TAG_ITEM_TYPE_CALLSIGN, rt.GetCallsign(), + NULL, TagMenu, Pt, Area); + } + + if (ObjectType == RIMCAS_DISTANCE_TOOL) { + vector s = split(sObjectId, ','); + pair toRemove = pair(s.front(), s.back()); + + typedef multimap::iterator iterator; + std::pair iterpair = + DistanceTools.equal_range(toRemove.first); + + iterator it = iterpair.first; + for (; it != iterpair.second; ++it) { + if (it->second == toRemove.second) { + it = DistanceTools.erase(it); + break; + } + } + } + + RequestRefresh(); }; -void CSMRRadar::OnFunctionCall(int FunctionId, const char * sItemString, POINT Pt, RECT Area) { - Logger::info(string(__FUNCSIG__)); - mouseLocation = Pt; - if (FunctionId == APPWINDOW_ONE || FunctionId == APPWINDOW_TWO) { - int id = FunctionId - APPWINDOW_BASE; - appWindowDisplays[id] = !appWindowDisplays[id]; - OnAsrContentToBeSaved(); - } - - if (FunctionId == WIP_AREAS) { - wipAreasActive = !wipAreasActive; - SaveDataToAsr("WIPareas", "vSMR WIP Areas", std::to_string(wipAreasActive).c_str()); - } - - if (FunctionId == RIMCAS_ACTIVE_AIRPORT_FUNC) { - setActiveAirport(sItemString); - SaveDataToAsr("Airport", "Active airport", getActiveAirport().c_str()); - } - - if (FunctionId == RIMCAS_UPDATE_FONTS) { - if (strcmp(sItemString, "Size 1") == 0) - currentFontSize = 1; - if (strcmp(sItemString, "Size 2") == 0) - currentFontSize = 2; - if (strcmp(sItemString, "Size 3") == 0) - currentFontSize = 3; - if (strcmp(sItemString, "Size 4") == 0) - currentFontSize = 4; - if (strcmp(sItemString, "Size 5") == 0) - currentFontSize = 5; - - ShowLists["Label Font Size"] = true; - - OnAsrContentToBeSaved(); - } - - if (FunctionId == RIMCAS_QDM_TOGGLE) { - QDMenabled = !QDMenabled; - QDMSelectEnabled = false; - } - - if (FunctionId == RIMCAS_QDM_SELECT_TOGGLE) - { - if (!QDMSelectEnabled) - { - QDMSelectPt = ConvertCoordFromPositionToPixel(AirportPositions[getActiveAirport()]); - } - QDMSelectEnabled = !QDMSelectEnabled; - QDMenabled = false; - } - - if (FunctionId == RIMCAS_UPDATE_PROFILE) { - this->CSMRRadar::LoadProfile(sItemString); - LoadCustomFont(); - SaveDataToAsr("ActiveProfile", "vSMR active profile", sItemString); - - ShowLists["Profiles"] = true; - } - - if (FunctionId == RIMCAS_UPDATEFILTER1 || FunctionId == RIMCAS_UPDATEFILTER2) { - int id = FunctionId - RIMCAS_UPDATEFILTER; - if (startsWith("UNL", sItemString)) - sItemString = "66000"; - appWindows[id]->m_Filter = atoi(sItemString); - - OnAsrContentToBeSaved(); - } - - if (FunctionId == RIMCAS_UPDATERANGE1 || FunctionId == RIMCAS_UPDATERANGE2) { - int id = FunctionId - RIMCAS_UPDATERANGE; - appWindows[id]->m_Scale = atoi(sItemString); - - OnAsrContentToBeSaved(); - } - - if (FunctionId == RIMCAS_UPDATEROTATE1 || FunctionId == RIMCAS_UPDATEROTATE2) { - int id = FunctionId - RIMCAS_UPDATEROTATE; - appWindows[id]->m_Rotation = atoi(sItemString); - - OnAsrContentToBeSaved(); - } - - if (FunctionId == RIMCAS_UPDATE_BRIGHNESS) { - if (strcmp(sItemString, "Day") == 0) - ColorSettingsDay = true; - else - ColorSettingsDay = false; - - ShowLists["Colour Settings"] = true; - - RequestRefresh(); - } - - if (FunctionId == RIMCAS_CA_ARRIVAL_FUNC) { - RimcasInstance->toggleMonitoredRunwayArr(string(sItemString)); - - ShowLists["Conflict Alert ARR"] = true; - - RequestRefresh(); - } - - if (FunctionId == RIMCAS_CA_MONITOR_FUNC) { - RimcasInstance->toggleMonitoredRunwayDep(string(sItemString)); - - ShowLists["Conflict Alert DEP"] = true; - - RequestRefresh(); - } - - if (FunctionId == RIMCAS_CLOSED_RUNWAYS_FUNC) { - RimcasInstance->toggleClosedRunway(string(sItemString)); - - ShowLists["Runway closed"] = true; - - RequestRefresh(); - } - - if (FunctionId == RIMCAS_OPEN_LIST) { - - ShowLists[string(sItemString)] = true; - ListAreas[string(sItemString)] = Area; - - RequestRefresh(); - } - - if (FunctionId == RIMCAS_TOGGLE_AIRCRAFT_TYPE) { - showAircraftType = !showAircraftType; - SaveDataToAsr("ShowAircraftType", "Show Aircraft Type", std::to_string(showAircraftType).c_str()); - } - - if (FunctionId == RIMCAS_TOGGLE_SID) { - showSID = !showSID; - SaveDataToAsr("ShowSID", "Show SID", std::to_string(showSID).c_str()); - } - - if (FunctionId == RIMCAS_TOGGLE_WAKE_TURB) { - showWakeTurb = !showWakeTurb; - SaveDataToAsr("ShowWakeTurb", "Show Wake Turbulence", std::to_string(showWakeTurb).c_str()); - } - - if (FunctionId == RIMCAS_UPDATE_LVP) { - if (strcmp(sItemString, "Normal") == 0) - isLVP = false; - if (strcmp(sItemString, "Low") == 0) - isLVP = true; - - ShowLists["Visibility"] = true; - } - - if (FunctionId == RIMCAS_UPDATE_DETAILED) { - if (strcmp(sItemString, "Detailed") == 0) - detailedRIMCAS = true; - if (strcmp(sItemString, "Simple") == 0) - detailedRIMCAS = false; - - ShowLists["RIMCAS Detail"] = true; - - RequestRefresh(); - } - - if (FunctionId == RIMCAS_UPDATE_AFTERGLOW) - { - Afterglow = !Afterglow; - OnAsrContentToBeSaved(); - } - - if (FunctionId == RIMCAS_UPDATE_GND_TRAIL) - { - Trail_Gnd = atoi(sItemString); - OnAsrContentToBeSaved(); - - ShowLists["GRND Trail Dots"] = true; - } - - if (FunctionId == RIMCAS_UPDATE_APP_TRAIL) - { - Trail_App = atoi(sItemString); - OnAsrContentToBeSaved(); - - ShowLists["APPR Trail Dots"] = true; - } - - if (FunctionId == RIMCAS_UPDATE_PTL) - { - PredictedLength = atoi(sItemString); - OnAsrContentToBeSaved(); - - ShowLists["Predicted Track Line"] = true; - } - - if (FunctionId == RIMCAS_BRIGHTNESS_LABEL) - { - ColorManager->update_brightness("label", std::atoi(sItemString)); - ShowLists["Label"] = true; - } - - if (FunctionId == RIMCAS_BRIGHTNESS_AFTERGLOW) - { - ColorManager->update_brightness("afterglow", std::atoi(sItemString)); - ShowLists["Afterglow"] = true; - } - - if (FunctionId == RIMCAS_BRIGHTNESS_SYMBOL) - { - ColorManager->update_brightness("symbol", std::atoi(sItemString)); - ShowLists["Symbol"] = true; - } - - if (FunctionId == RIMCAS_UPDATE_RELEASE) - { - ReleaseInProgress = !ReleaseInProgress; - if (ReleaseInProgress) - AcquireInProgress = false; - NeedCorrelateCursor = ReleaseInProgress; - - CorrelateCursor(); - } - - if (FunctionId == RIMCAS_UPDATE_ACQUIRE) - { - AcquireInProgress = !AcquireInProgress; - if (AcquireInProgress) - ReleaseInProgress = false; - NeedCorrelateCursor = AcquireInProgress; - - CorrelateCursor(); - } +void CSMRRadar::OnFunctionCall(int FunctionId, const char *sItemString, + POINT Pt, RECT Area) { + Logger::info(string(__FUNCSIG__)); + mouseLocation = Pt; + if (FunctionId == APPWINDOW_ONE || FunctionId == APPWINDOW_TWO) { + int id = FunctionId - APPWINDOW_BASE; + appWindowDisplays[id] = !appWindowDisplays[id]; + OnAsrContentToBeSaved(); + } + + if (FunctionId == WIP_AREAS) { + wipAreasActive = !wipAreasActive; + SaveDataToAsr("WIPareas", "vSMR WIP Areas", + std::to_string(wipAreasActive).c_str()); + } + + if (FunctionId == RIMCAS_ACTIVE_AIRPORT_FUNC) { + setActiveAirport(sItemString); + SaveDataToAsr("Airport", "Active airport", getActiveAirport().c_str()); + } + + if (FunctionId == RIMCAS_UPDATE_FONTS) { + if (strcmp(sItemString, "Size 1") == 0) + currentFontSize = 1; + if (strcmp(sItemString, "Size 2") == 0) + currentFontSize = 2; + if (strcmp(sItemString, "Size 3") == 0) + currentFontSize = 3; + if (strcmp(sItemString, "Size 4") == 0) + currentFontSize = 4; + if (strcmp(sItemString, "Size 5") == 0) + currentFontSize = 5; + + ShowLists["Label Font Size"] = true; + + OnAsrContentToBeSaved(); + } + + if (FunctionId == RIMCAS_QDM_TOGGLE) { + QDMenabled = !QDMenabled; + QDMSelectEnabled = false; + } + + if (FunctionId == RIMCAS_QDM_SELECT_TOGGLE) { + if (!QDMSelectEnabled) { + QDMSelectPt = + ConvertCoordFromPositionToPixel(AirportPositions[getActiveAirport()]); + } + QDMSelectEnabled = !QDMSelectEnabled; + QDMenabled = false; + } + + if (FunctionId == RIMCAS_UPDATE_PROFILE) { + this->CSMRRadar::LoadProfile(sItemString); + LoadCustomFont(); + SaveDataToAsr("ActiveProfile", "vSMR active profile", sItemString); + + ShowLists["Profiles"] = true; + } + + if (FunctionId == RIMCAS_UPDATEFILTER1 || + FunctionId == RIMCAS_UPDATEFILTER2) { + int id = FunctionId - RIMCAS_UPDATEFILTER; + if (startsWith("UNL", sItemString)) + sItemString = "66000"; + appWindows[id]->m_Filter = atoi(sItemString); + + OnAsrContentToBeSaved(); + } + + if (FunctionId == RIMCAS_UPDATERANGE1 || FunctionId == RIMCAS_UPDATERANGE2) { + int id = FunctionId - RIMCAS_UPDATERANGE; + appWindows[id]->m_Scale = atoi(sItemString); + + OnAsrContentToBeSaved(); + } + + if (FunctionId == RIMCAS_UPDATEROTATE1 || + FunctionId == RIMCAS_UPDATEROTATE2) { + int id = FunctionId - RIMCAS_UPDATEROTATE; + appWindows[id]->m_Rotation = atoi(sItemString); + + OnAsrContentToBeSaved(); + } + + if (FunctionId == RIMCAS_UPDATE_BRIGHNESS) { + if (strcmp(sItemString, "Day") == 0) + ColorSettingsDay = true; + else + ColorSettingsDay = false; + + ShowLists["Colour Settings"] = true; + + RequestRefresh(); + } + + if (FunctionId == RIMCAS_CA_ARRIVAL_FUNC) { + RimcasInstance->toggleMonitoredRunwayArr(string(sItemString)); + + ShowLists["Conflict Alert ARR"] = true; + + RequestRefresh(); + } + + if (FunctionId == RIMCAS_CA_MONITOR_FUNC) { + RimcasInstance->toggleMonitoredRunwayDep(string(sItemString)); + + ShowLists["Conflict Alert DEP"] = true; + + RequestRefresh(); + } + + if (FunctionId == RIMCAS_CLOSED_RUNWAYS_FUNC) { + RimcasInstance->toggleClosedRunway(string(sItemString)); + + ShowLists["Runway closed"] = true; + + RequestRefresh(); + } + + if (FunctionId == RIMCAS_OPEN_LIST) { + + ShowLists[string(sItemString)] = true; + ListAreas[string(sItemString)] = Area; + + RequestRefresh(); + } + + if (FunctionId == RIMCAS_TOGGLE_AIRCRAFT_TYPE) { + showAircraftType = !showAircraftType; + SaveDataToAsr("ShowAircraftType", "Show Aircraft Type", + std::to_string(showAircraftType).c_str()); + } + + if (FunctionId == RIMCAS_TOGGLE_SID) { + showSID = !showSID; + SaveDataToAsr("ShowSID", "Show SID", std::to_string(showSID).c_str()); + } + + if (FunctionId == RIMCAS_TOGGLE_WAKE_TURB) { + showWakeTurb = !showWakeTurb; + SaveDataToAsr("ShowWakeTurb", "Show Wake Turbulence", + std::to_string(showWakeTurb).c_str()); + } + + // Departure Timer Window function handlers + if (FunctionId == RIMCAS_DEP_WINDOW_TOGGLE) { + showDepartureWindow = !showDepartureWindow; + OnAsrContentToBeSaved(); + } + + if (FunctionId == RIMCAS_DEP_WINDOW_DURATION) { + departureDisplayDuration = atoi(sItemString); + OnAsrContentToBeSaved(); + ShowLists["Departure Timer"] = true; + } + + if (FunctionId == RIMCAS_DEP_WINDOW_COL_CALLSIGN) { + depWindowShowCallsign = !depWindowShowCallsign; + OnAsrContentToBeSaved(); + ShowLists["Departure Timer"] = true; + } + + if (FunctionId == RIMCAS_DEP_WINDOW_COL_DEST) { + depWindowShowDest = !depWindowShowDest; + OnAsrContentToBeSaved(); + ShowLists["Departure Timer"] = true; + } + + if (FunctionId == RIMCAS_DEP_WINDOW_COL_ACTYPE) { + depWindowShowAcType = !depWindowShowAcType; + OnAsrContentToBeSaved(); + ShowLists["Departure Timer"] = true; + } + + if (FunctionId == RIMCAS_DEP_WINDOW_COL_WAKE) { + depWindowShowWake = !depWindowShowWake; + OnAsrContentToBeSaved(); + ShowLists["Departure Timer"] = true; + } + + if (FunctionId == RIMCAS_DEP_WINDOW_COL_FREQ) { + depWindowShowFreq = !depWindowShowFreq; + OnAsrContentToBeSaved(); + ShowLists["Departure Timer"] = true; + } + + if (FunctionId == RIMCAS_DEP_WINDOW_COL_TIME) { + depWindowShowTime = !depWindowShowTime; + OnAsrContentToBeSaved(); + ShowLists["Departure Timer"] = true; + } + + if (FunctionId == RIMCAS_UPDATE_LVP) { + if (strcmp(sItemString, "Normal") == 0) + isLVP = false; + if (strcmp(sItemString, "Low") == 0) + isLVP = true; + + ShowLists["Visibility"] = true; + } + + if (FunctionId == RIMCAS_UPDATE_DETAILED) { + if (strcmp(sItemString, "Detailed") == 0) + detailedRIMCAS = true; + if (strcmp(sItemString, "Simple") == 0) + detailedRIMCAS = false; + + ShowLists["RIMCAS Detail"] = true; + + RequestRefresh(); + } + + if (FunctionId == RIMCAS_UPDATE_AFTERGLOW) { + Afterglow = !Afterglow; + OnAsrContentToBeSaved(); + } + + if (FunctionId == RIMCAS_UPDATE_GND_TRAIL) { + Trail_Gnd = atoi(sItemString); + OnAsrContentToBeSaved(); + + ShowLists["GRND Trail Dots"] = true; + } + + if (FunctionId == RIMCAS_UPDATE_APP_TRAIL) { + Trail_App = atoi(sItemString); + OnAsrContentToBeSaved(); + + ShowLists["APPR Trail Dots"] = true; + } + + if (FunctionId == RIMCAS_UPDATE_PTL) { + PredictedLength = atoi(sItemString); + OnAsrContentToBeSaved(); + + ShowLists["Predicted Track Line"] = true; + } + + if (FunctionId == RIMCAS_BRIGHTNESS_LABEL) { + ColorManager->update_brightness("label", std::atoi(sItemString)); + ShowLists["Label"] = true; + } + + if (FunctionId == RIMCAS_BRIGHTNESS_AFTERGLOW) { + ColorManager->update_brightness("afterglow", std::atoi(sItemString)); + ShowLists["Afterglow"] = true; + } + + if (FunctionId == RIMCAS_BRIGHTNESS_SYMBOL) { + ColorManager->update_brightness("symbol", std::atoi(sItemString)); + ShowLists["Symbol"] = true; + } + + if (FunctionId == RIMCAS_UPDATE_RELEASE) { + ReleaseInProgress = !ReleaseInProgress; + if (ReleaseInProgress) + AcquireInProgress = false; + NeedCorrelateCursor = ReleaseInProgress; + + CorrelateCursor(); + } + + if (FunctionId == RIMCAS_UPDATE_ACQUIRE) { + AcquireInProgress = !AcquireInProgress; + if (AcquireInProgress) + ReleaseInProgress = false; + NeedCorrelateCursor = AcquireInProgress; + + CorrelateCursor(); + } } void CSMRRadar::RefreshAirportActivity(void) { - Logger::info(string(__FUNCSIG__)); - // - // Getting the depatures and arrivals airports - // - - Active_Arrivals.clear(); - CSectorElement airport; - for (airport = GetPlugIn()->SectorFileElementSelectFirst(SECTOR_ELEMENT_AIRPORT); - airport.IsValid(); - airport = GetPlugIn()->SectorFileElementSelectNext(airport, SECTOR_ELEMENT_AIRPORT)) - { - if (airport.IsElementActive(false)) { - string s = airport.GetName(); - s = s.substr(0, 4); - transform(s.begin(), s.end(), s.begin(), ::toupper); - Active_Arrivals.push_back(s); - } - } + Logger::info(string(__FUNCSIG__)); + // + // Getting the depatures and arrivals airports + // + + Active_Arrivals.clear(); + CSectorElement airport; + for (airport = + GetPlugIn()->SectorFileElementSelectFirst(SECTOR_ELEMENT_AIRPORT); + airport.IsValid(); airport = GetPlugIn()->SectorFileElementSelectNext( + airport, SECTOR_ELEMENT_AIRPORT)) { + if (airport.IsElementActive(false)) { + string s = airport.GetName(); + s = s.substr(0, 4); + transform(s.begin(), s.end(), s.begin(), ::toupper); + Active_Arrivals.push_back(s); + } + } } -void CSMRRadar::OnRadarTargetPositionUpdate(CRadarTarget RadarTarget) -{ - Logger::info(string(__FUNCSIG__)); - if (!RadarTarget.IsValid() || !RadarTarget.GetPosition().IsValid()) - return; - - CRadarTargetPositionData RtPos = RadarTarget.GetPosition(); - - Patatoides[RadarTarget.GetCallsign()].History_three_points = Patatoides[RadarTarget.GetCallsign()].History_two_points; - Patatoides[RadarTarget.GetCallsign()].History_two_points = Patatoides[RadarTarget.GetCallsign()].History_one_points; - Patatoides[RadarTarget.GetCallsign()].History_one_points = Patatoides[RadarTarget.GetCallsign()].points; - - Patatoides[RadarTarget.GetCallsign()].points.clear(); - - CFlightPlan fp = GetPlugIn()->FlightPlanSelect(RadarTarget.GetCallsign()); - - if ((getActiveAirport() == "EGLL" || getActiveAirport() == "EGKK") && CurrentConfig->isPositionInGeofenceArea(getActiveAirport(), RtPos.GetPosition())) { // If on stand at Heathrow - int c = 0; - CPosition centre = RtPos.GetPosition(); - - for (int hdg = 0; hdg <= 360; hdg += 90) { - CPosition pos = Haversine(centre, hdg, 14.0f); - Patatoides[RadarTarget.GetCallsign()].points[c++] = { pos.m_Latitude, pos.m_Longitude }; - } - - for (int hdg = 0; hdg <= 360; hdg += 90) { - CPosition pos = Haversine(centre, hdg, 10.0f); - Patatoides[RadarTarget.GetCallsign()].points[c++] = { pos.m_Latitude, pos.m_Longitude }; - } - - return; - } - - // All units in M - float width = 34.0f; - float cabin_width = 4.0f; - float lenght = 38.0f; - - if (fp.IsValid()) { - const char* acType = fp.GetFlightPlanData().GetAircraftFPType(); - char* shortACType = ""; - - if (strlen(acType) >= 4) { - char truncated[5]; - strncpy_s(truncated, acType, 4); - truncated[4] = '\0'; - shortACType = truncated; - } - - CAircraftTypeLookup::AircraftData data = AircraftTypes->getAircraftData(shortACType); - if (data.length != -1) { // primarily use recorded data values - width = data.wingspan / 3.281; - cabin_width = (data.gearWidth / 2) / 3.281; - lenght = data.length / 3.281; - } - else { // use wtc instead - char wtc = fp.GetFlightPlanData().GetAircraftWtc(); - - if (wtc == 'L') { - width = 13.0f; - cabin_width = 2.0f; - lenght = 12.0f; - } - - if (wtc == 'H') { - width = 61.0f; - cabin_width = 7.0f; - lenght = 64.0f; - } - - if (wtc == 'J') { - width = 80.0f; - cabin_width = 7.0f; - lenght = 73.0f; - } - } - } - - - width = width + float((rand() % 5) - 2); - cabin_width = cabin_width + float((rand() % 3) - 1); - lenght = lenght + float((rand() % 5) - 2); - - - float trackHead = float(RadarTarget.GetPosition().GetReportedHeadingTrueNorth()); - float inverseTrackHead = float(fmod(trackHead + 180.0f, 360)); - float leftTrackHead = float(fmod(trackHead - 90.0f, 360)); - float rightTrackHead = float(fmod(trackHead + 90.0f, 360)); - - float HalfLenght = lenght / 2.0f; - float HalfCabWidth = cabin_width / 2.0f; - float HalfSpanWidth = width / 2.0f; - - // Base shape is like a deformed cross - - - CPosition topMiddle = Haversine(RtPos.GetPosition(), trackHead, HalfLenght); - CPosition topLeft = Haversine(topMiddle, leftTrackHead, HalfCabWidth); - CPosition topRight = Haversine(topMiddle, rightTrackHead, HalfCabWidth); - - CPosition bottomMiddle = Haversine(RtPos.GetPosition(), inverseTrackHead, HalfLenght); - CPosition bottomLeft = Haversine(bottomMiddle, leftTrackHead, HalfCabWidth); - CPosition bottomRight = Haversine(bottomMiddle, rightTrackHead, HalfCabWidth); - - CPosition middleTopLeft = Haversine(topLeft, float(fmod(inverseTrackHead + 25.0f, 360)), 0.8f*HalfLenght); - CPosition middleTopRight = Haversine(topRight, float(fmod(inverseTrackHead - 25.0f, 360)), 0.8f*HalfLenght); - CPosition middleBottomLeft = Haversine(bottomLeft, float(fmod(trackHead - 15.0f, 360)), 0.8f*HalfLenght); - CPosition middleBottomRight = Haversine(bottomRight, float(fmod(trackHead + 15.0f, 360)), 0.8f*HalfLenght); - - CPosition rightTop = Haversine(middleBottomRight, rightTrackHead, 0.7f*HalfSpanWidth); - CPosition rightBottom = Haversine(rightTop, inverseTrackHead, cabin_width); - - CPosition leftTop = Haversine(middleBottomLeft, leftTrackHead, 0.7f*HalfSpanWidth); - CPosition leftBottom = Haversine(leftTop, inverseTrackHead, cabin_width); - - CPosition basePoints[12]; - basePoints[0] = topLeft; - basePoints[1] = middleTopLeft; - basePoints[2] = leftTop; - basePoints[3] = leftBottom; - basePoints[4] = middleBottomLeft; - basePoints[5] = bottomLeft; - basePoints[6] = bottomRight; - basePoints[7] = middleBottomRight; - basePoints[8] = rightBottom; - basePoints[9] = rightTop; - basePoints[10] = middleTopRight; - basePoints[11] = topRight; - - // 12 points total, so 11 from 0 - // ------ - - // Random points between points of base shape - - for (int i = 0; i < 12; i++){ - - CPosition newPoint, lastPoint, endPoint, startPoint; - - startPoint = basePoints[i]; - if (i == 11) endPoint = basePoints[0]; - else endPoint = basePoints[i + 1]; - - double dist, rndHeading; - dist = startPoint.DistanceTo(endPoint); - - Patatoides[RadarTarget.GetCallsign()].points[i * 7] = { startPoint.m_Latitude, startPoint.m_Longitude }; - lastPoint = startPoint; - - for (int k = 1; k < 7; k++){ - - rndHeading = float(fmod(lastPoint.DirectionTo(endPoint) + (-25.0 + (rand() % 50 + 1)), 360)); - newPoint = Haversine(lastPoint, rndHeading, dist * 200); - Patatoides[RadarTarget.GetCallsign()].points[(i * 7) + k] = { newPoint.m_Latitude, newPoint.m_Longitude }; - lastPoint = newPoint; - } - } +void CSMRRadar::OnRadarTargetPositionUpdate(CRadarTarget RadarTarget) { + Logger::info(string(__FUNCSIG__)); + if (!RadarTarget.IsValid() || !RadarTarget.GetPosition().IsValid()) + return; + + CRadarTargetPositionData RtPos = RadarTarget.GetPosition(); + + Patatoides[RadarTarget.GetCallsign()].History_three_points = + Patatoides[RadarTarget.GetCallsign()].History_two_points; + Patatoides[RadarTarget.GetCallsign()].History_two_points = + Patatoides[RadarTarget.GetCallsign()].History_one_points; + Patatoides[RadarTarget.GetCallsign()].History_one_points = + Patatoides[RadarTarget.GetCallsign()].points; + + Patatoides[RadarTarget.GetCallsign()].points.clear(); + + CFlightPlan fp = GetPlugIn()->FlightPlanSelect(RadarTarget.GetCallsign()); + + if ((getActiveAirport() == "EGLL" || getActiveAirport() == "EGKK") && + CurrentConfig->isPositionInGeofenceArea( + getActiveAirport(), RtPos.GetPosition())) { // If on stand at Heathrow + int c = 0; + CPosition centre = RtPos.GetPosition(); + + for (int hdg = 0; hdg <= 360; hdg += 90) { + CPosition pos = Haversine(centre, hdg, 14.0f); + Patatoides[RadarTarget.GetCallsign()].points[c++] = {pos.m_Latitude, + pos.m_Longitude}; + } + + for (int hdg = 0; hdg <= 360; hdg += 90) { + CPosition pos = Haversine(centre, hdg, 10.0f); + Patatoides[RadarTarget.GetCallsign()].points[c++] = {pos.m_Latitude, + pos.m_Longitude}; + } + + return; + } + + // All units in M + float width = 34.0f; + float cabin_width = 4.0f; + float lenght = 38.0f; + + if (fp.IsValid()) { + const char *acType = fp.GetFlightPlanData().GetAircraftFPType(); + char *shortACType = ""; + + if (strlen(acType) >= 4) { + char truncated[5]; + strncpy_s(truncated, acType, 4); + truncated[4] = '\0'; + shortACType = truncated; + } + + CAircraftTypeLookup::AircraftData data = + AircraftTypes->getAircraftData(shortACType); + if (data.length != -1) { // primarily use recorded data values + width = data.wingspan / 3.281; + cabin_width = (data.gearWidth / 2) / 3.281; + lenght = data.length / 3.281; + } else { // use wtc instead + char wtc = fp.GetFlightPlanData().GetAircraftWtc(); + + if (wtc == 'L') { + width = 13.0f; + cabin_width = 2.0f; + lenght = 12.0f; + } + + if (wtc == 'H') { + width = 61.0f; + cabin_width = 7.0f; + lenght = 64.0f; + } + + if (wtc == 'J') { + width = 80.0f; + cabin_width = 7.0f; + lenght = 73.0f; + } + } + } + + width = width + float((rand() % 5) - 2); + cabin_width = cabin_width + float((rand() % 3) - 1); + lenght = lenght + float((rand() % 5) - 2); + + float trackHead = + float(RadarTarget.GetPosition().GetReportedHeadingTrueNorth()); + float inverseTrackHead = float(fmod(trackHead + 180.0f, 360)); + float leftTrackHead = float(fmod(trackHead - 90.0f, 360)); + float rightTrackHead = float(fmod(trackHead + 90.0f, 360)); + + float HalfLenght = lenght / 2.0f; + float HalfCabWidth = cabin_width / 2.0f; + float HalfSpanWidth = width / 2.0f; + + // Base shape is like a deformed cross + + CPosition topMiddle = Haversine(RtPos.GetPosition(), trackHead, HalfLenght); + CPosition topLeft = Haversine(topMiddle, leftTrackHead, HalfCabWidth); + CPosition topRight = Haversine(topMiddle, rightTrackHead, HalfCabWidth); + + CPosition bottomMiddle = + Haversine(RtPos.GetPosition(), inverseTrackHead, HalfLenght); + CPosition bottomLeft = Haversine(bottomMiddle, leftTrackHead, HalfCabWidth); + CPosition bottomRight = Haversine(bottomMiddle, rightTrackHead, HalfCabWidth); + + CPosition middleTopLeft = Haversine( + topLeft, float(fmod(inverseTrackHead + 25.0f, 360)), 0.8f * HalfLenght); + CPosition middleTopRight = Haversine( + topRight, float(fmod(inverseTrackHead - 25.0f, 360)), 0.8f * HalfLenght); + CPosition middleBottomLeft = Haversine( + bottomLeft, float(fmod(trackHead - 15.0f, 360)), 0.8f * HalfLenght); + CPosition middleBottomRight = Haversine( + bottomRight, float(fmod(trackHead + 15.0f, 360)), 0.8f * HalfLenght); + + CPosition rightTop = + Haversine(middleBottomRight, rightTrackHead, 0.7f * HalfSpanWidth); + CPosition rightBottom = Haversine(rightTop, inverseTrackHead, cabin_width); + + CPosition leftTop = + Haversine(middleBottomLeft, leftTrackHead, 0.7f * HalfSpanWidth); + CPosition leftBottom = Haversine(leftTop, inverseTrackHead, cabin_width); + + CPosition basePoints[12]; + basePoints[0] = topLeft; + basePoints[1] = middleTopLeft; + basePoints[2] = leftTop; + basePoints[3] = leftBottom; + basePoints[4] = middleBottomLeft; + basePoints[5] = bottomLeft; + basePoints[6] = bottomRight; + basePoints[7] = middleBottomRight; + basePoints[8] = rightBottom; + basePoints[9] = rightTop; + basePoints[10] = middleTopRight; + basePoints[11] = topRight; + + // 12 points total, so 11 from 0 + // ------ + + // Random points between points of base shape + + for (int i = 0; i < 12; i++) { + + CPosition newPoint, lastPoint, endPoint, startPoint; + + startPoint = basePoints[i]; + if (i == 11) + endPoint = basePoints[0]; + else + endPoint = basePoints[i + 1]; + + double dist, rndHeading; + dist = startPoint.DistanceTo(endPoint); + + Patatoides[RadarTarget.GetCallsign()].points[i * 7] = { + startPoint.m_Latitude, startPoint.m_Longitude}; + lastPoint = startPoint; + + for (int k = 1; k < 7; k++) { + + rndHeading = float(fmod( + lastPoint.DirectionTo(endPoint) + (-25.0 + (rand() % 50 + 1)), 360)); + newPoint = Haversine(lastPoint, rndHeading, dist * 200); + Patatoides[RadarTarget.GetCallsign()].points[(i * 7) + k] = { + newPoint.m_Latitude, newPoint.m_Longitude}; + lastPoint = newPoint; + } + } } -string CSMRRadar::GetBottomLine(const char * Callsign) { - Logger::info(string(__FUNCSIG__)); - - CFlightPlan fp = GetPlugIn()->FlightPlanSelect(Callsign); - string to_render = ""; - if (fp.IsValid()) { - to_render += fp.GetCallsign(); - - string callsign_code = fp.GetCallsign(); - callsign_code = callsign_code.substr(0, 3); - to_render += " (" + Callsigns->getCallsign(callsign_code) + ")"; - - to_render += " ("; - to_render += fp.GetPilotName(); - to_render += "): "; - to_render += fp.GetFlightPlanData().GetAircraftFPType(); - to_render += " "; - - if (fp.GetFlightPlanData().IsReceived()) { - const char * assr = fp.GetControllerAssignedData().GetSquawk(); - const char * ssr = GetPlugIn()->RadarTargetSelect(fp.GetCallsign()).GetPosition().GetSquawk(); - if (strlen(assr) != 0 && !startsWith(ssr, assr)) { - to_render += assr; - to_render += ":"; - to_render += ssr; - } - else { - to_render += "I:"; - to_render += ssr; - } - - to_render += " "; - to_render += fp.GetFlightPlanData().GetOrigin(); - to_render += "==>"; - to_render += fp.GetFlightPlanData().GetDestination(); - to_render += " ("; - to_render += fp.GetFlightPlanData().GetAlternate(); - to_render += ")"; - - to_render += " at "; - int rfl = fp.GetControllerAssignedData().GetFinalAltitude(); - string rfl_s; - if (rfl == 0) - rfl = fp.GetFlightPlanData().GetFinalAltitude(); - if (rfl > GetPlugIn()->GetTransitionAltitude()) - rfl_s = "FL" + std::to_string(rfl / 100); - else - rfl_s = std::to_string(rfl) + "ft"; - - to_render += rfl_s; - to_render += " Route: "; - to_render += fp.GetFlightPlanData().GetRoute(); - } - } - - return to_render; +string CSMRRadar::GetBottomLine(const char *Callsign) { + Logger::info(string(__FUNCSIG__)); + + CFlightPlan fp = GetPlugIn()->FlightPlanSelect(Callsign); + string to_render = ""; + if (fp.IsValid()) { + to_render += fp.GetCallsign(); + + string callsign_code = fp.GetCallsign(); + callsign_code = callsign_code.substr(0, 3); + to_render += " (" + Callsigns->getCallsign(callsign_code) + ")"; + + to_render += " ("; + to_render += fp.GetPilotName(); + to_render += "): "; + to_render += fp.GetFlightPlanData().GetAircraftFPType(); + to_render += " "; + + if (fp.GetFlightPlanData().IsReceived()) { + const char *assr = fp.GetControllerAssignedData().GetSquawk(); + const char *ssr = GetPlugIn() + ->RadarTargetSelect(fp.GetCallsign()) + .GetPosition() + .GetSquawk(); + if (strlen(assr) != 0 && !startsWith(ssr, assr)) { + to_render += assr; + to_render += ":"; + to_render += ssr; + } else { + to_render += "I:"; + to_render += ssr; + } + + to_render += " "; + to_render += fp.GetFlightPlanData().GetOrigin(); + to_render += "==>"; + to_render += fp.GetFlightPlanData().GetDestination(); + to_render += " ("; + to_render += fp.GetFlightPlanData().GetAlternate(); + to_render += ")"; + + to_render += " at "; + int rfl = fp.GetControllerAssignedData().GetFinalAltitude(); + string rfl_s; + if (rfl == 0) + rfl = fp.GetFlightPlanData().GetFinalAltitude(); + if (rfl > GetPlugIn()->GetTransitionAltitude()) + rfl_s = "FL" + std::to_string(rfl / 100); + else + rfl_s = std::to_string(rfl) + "ft"; + + to_render += rfl_s; + to_render += " Route: "; + to_render += fp.GetFlightPlanData().GetRoute(); + } + } + + return to_render; } -bool CSMRRadar::OnCompileCommand(const char * sCommandLine) -{ - Logger::info(string(__FUNCSIG__)); - if (strcmp(sCommandLine, ".smr reload") == 0) { - CurrentConfig = new CConfig(ConfigPath); - LoadProfile(CurrentConfig->getActiveProfileName()); - return true; - } +bool CSMRRadar::OnCompileCommand(const char *sCommandLine) { + Logger::info(string(__FUNCSIG__)); + if (strcmp(sCommandLine, ".smr reload") == 0) { + CurrentConfig = new CConfig(ConfigPath); + LoadProfile(CurrentConfig->getActiveProfileName()); + return true; + } - return false; + return false; } -map CSMRRadar::GenerateTagData(CRadarTarget rt, CFlightPlan fp, bool isAcCorrelated, bool isProMode, int TransitionAltitude, bool useSpeedForGates, string ActiveAirport, bool showAircraftType, bool showSID, bool showWakeTurb) -{ - Logger::info(string(__FUNCSIG__)); - // ---- - // Tag items available - // callsign: Callsign with freq state and comm * - // actype: Aircraft type * - // sctype: Aircraft type that changes for squawk error * - // sqerror: Squawk error if there is one, or empty * - // deprwy: Departure runway * - // seprwy: Departure runway that changes to speed if speed > 25kts * - // arvrwy: Arrival runway * - // srvrwy: Speed that changes to arrival runway if speed < 25kts * - // gate: Gate, from speed or scratchpad * - // sate: Gate, from speed or scratchpad that changes to speed if speed > 25kts * - // flightlevel: Flightlevel/Pressure altitude of the ac * - // gs: Ground speed of the ac * - // tendency: Climbing or descending symbol * - // wake: Wake turbulance cat * - // groundstatus: Current status * - // ssr: the current squawk of the ac - // sid: the assigned SID - // ssid: a short version of the SID - // origin: origin aerodrome - // dest: destination aerodrome - // ---- - - bool IsPrimary = !rt.GetPosition().GetTransponderC(); - bool isAirborne = rt.GetPosition().GetReportedGS() > 50; - - // ----- Callsign ------- - string callsign = rt.GetCallsign(); - if (fp.IsValid()) { - if (fp.GetControllerAssignedData().GetCommunicationType() == 't' || - fp.GetControllerAssignedData().GetCommunicationType() == 'T' || - fp.GetControllerAssignedData().GetCommunicationType() == 'r' || - fp.GetControllerAssignedData().GetCommunicationType() == 'R' || - fp.GetControllerAssignedData().GetCommunicationType() == 'v' || - fp.GetControllerAssignedData().GetCommunicationType() == 'V') - { - if (fp.GetControllerAssignedData().GetCommunicationType() != 'v' && - fp.GetControllerAssignedData().GetCommunicationType() != 'V') { - callsign.append("/"); - callsign += fp.GetControllerAssignedData().GetCommunicationType(); - } - } - else if (fp.GetFlightPlanData().GetCommunicationType() == 't' || - fp.GetFlightPlanData().GetCommunicationType() == 'r' || - fp.GetFlightPlanData().GetCommunicationType() == 'T' || - fp.GetFlightPlanData().GetCommunicationType() == 'R') - { - callsign.append("/"); - callsign += fp.GetFlightPlanData().GetCommunicationType(); - } - - switch (fp.GetState()) { - - case FLIGHT_PLAN_STATE_TRANSFER_TO_ME_INITIATED: - callsign = ">>" + callsign; - break; - - case FLIGHT_PLAN_STATE_TRANSFER_FROM_ME_INITIATED: - callsign = callsign + ">>"; - break; - - case FLIGHT_PLAN_STATE_ASSUMED: - callsign = "[" + callsign + "]"; - break; - - } - } - - // ----- Squawk error ------- - string sqerror = ""; - const char * assr = fp.GetControllerAssignedData().GetSquawk(); - const char * ssr = rt.GetPosition().GetSquawk(); - bool has_squawk_error = false; - if (strlen(assr) != 0 && !startsWith(ssr, assr)) { - has_squawk_error = true; - sqerror = "A"; - sqerror.append(assr); - } - - // ----- Aircraft type ------- - - string actype = "NoFPL"; - if (fp.IsValid() && fp.GetFlightPlanData().IsReceived()) - actype = fp.GetFlightPlanData().GetAircraftFPType(); - if (actype.size() > 4 && actype != "NoFPL") - actype = actype.substr(0, 4); - - // ----- Aircraft type that changes to squawk error ------- - string sctype = actype; - if (has_squawk_error) - sctype = sqerror; - - // ----- Groundspeed ------- - string speed = std::to_string(rt.GetPosition().GetReportedGS()); - - // ----- Departure runway ------- - string deprwy = fp.GetFlightPlanData().GetDepartureRwy(); - if (deprwy.length() == 0) - deprwy = "RWY"; - - // ----- Departure runway that changes for overspeed ------- - string seprwy = deprwy; - if (rt.GetPosition().GetReportedGS() > 25) - seprwy = std::to_string(rt.GetPosition().GetReportedGS()); - - // ----- Arrival runway ------- - string arvrwy = fp.GetFlightPlanData().GetArrivalRwy(); - if (arvrwy.length() == 0) - arvrwy = "RWY"; - - // ----- Speed that changes to arrival runway ----- - string srvrwy = speed; - if (rt.GetPosition().GetReportedGS() < 25) - srvrwy = arvrwy; - - // ----- Gate ------- - string gate; - if (useSpeedForGates) - gate = std::to_string(fp.GetControllerAssignedData().GetAssignedSpeed()); - else - gate = fp.GetControllerAssignedData().GetScratchPadString(); - - replaceAll(gate, "STAND=", ""); - gate = gate.substr(0, 4); - - if (gate.size() == 0 || gate == "0" || !isAcCorrelated) - gate = "NoGate"; - - // ----- Gate that changes to speed ------- - string sate = gate; - if (rt.GetPosition().GetReportedGS() > 25) - sate = speed; - - // ----- Flightlevel ------- - int fl = rt.GetPosition().GetFlightLevel(); - int padding = 5; - string pfls = ""; - if (fl <= TransitionAltitude) { - fl = rt.GetPosition().GetPressureAltitude(); - pfls = "A"; - padding = 4; - } - string flightlevel = (pfls + padWithZeros(padding, fl)).substr(0, 3); - - // ----- Tendency ------- - string tendency = "-"; - int delta_fl = rt.GetPosition().GetFlightLevel() - rt.GetPreviousPosition(rt.GetPosition()).GetFlightLevel(); - if (abs(delta_fl) >= 50) { - if (delta_fl < 0) { - tendency = "|"; - } - else { - tendency = "^"; - } - } - - // ----- Wake cat ------- - string wake = "?"; - if (fp.IsValid() && isAcCorrelated) { - wake = ""; - wake += fp.GetFlightPlanData().GetAircraftWtc(); - } - - // ----- SSR ------- - string tssr = ""; - if (rt.IsValid()) - { - tssr = rt.GetPosition().GetSquawk(); - } - - // ----- SID ------- - string dep = "SID"; - if (fp.IsValid() && isAcCorrelated) - { - dep = fp.GetFlightPlanData().GetSidName(); - } - - // ----- Short SID ------- - string ssid = dep; - if (fp.IsValid() && ssid.size() > 5 && isAcCorrelated) - { - ssid = dep.substr(0, 3); - ssid += dep.substr(dep.size() - 2, dep.size()); - } - - // ------- Origin aerodrome ------- - string origin = "????"; - if (isAcCorrelated) - { - origin = fp.GetFlightPlanData().GetOrigin(); - } - - // ------- Destination aerodrome ------- - string dest = "????"; - if (isAcCorrelated) - { - dest = fp.GetFlightPlanData().GetDestination(); - } - - // ----- GSTAT ------- - string gstat = "STS"; - if (fp.IsValid() && isAcCorrelated) { - if (strlen(fp.GetGroundState()) != 0) - gstat = fp.GetGroundState(); - } - - // ----- UK Controller Plugin / Assigned Stand ------- - string uk_stand; - uk_stand = fp.GetControllerAssignedData().GetFlightStripAnnotation(3); - if (uk_stand.length() == 0) - uk_stand = "NoGate"; - - // ----- Generating the replacing map ----- - map TagReplacingMap; - - // System ID for uncorrelated - TagReplacingMap["systemid"] = "T:"; - string tpss = rt.GetSystemID(); - TagReplacingMap["systemid"].append(tpss.substr(1, 6)); - - // Pro mode data here - if (isProMode) - { - - if (isAirborne && !isAcCorrelated) - { - callsign = tssr; - } - - if (!isAcCorrelated) - { - actype = "NoFPL"; - } - - // Is a primary target - - if (isAirborne && !isAcCorrelated && IsPrimary) - { - flightlevel = "NoALT"; - tendency = "?"; - speed = std::to_string(rt.GetGS()); - } - - if (isAirborne && !isAcCorrelated && IsPrimary) - { - callsign = TagReplacingMap["systemid"]; - } - } - - TagReplacingMap["callsign"] = callsign; - TagReplacingMap["actype"] = showAircraftType ? actype : ""; - TagReplacingMap["sctype"] = showAircraftType ? sctype : ""; - TagReplacingMap["sqerror"] = sqerror; - TagReplacingMap["deprwy"] = deprwy; - TagReplacingMap["seprwy"] = seprwy; - TagReplacingMap["arvrwy"] = arvrwy; - TagReplacingMap["srvrwy"] = srvrwy; - TagReplacingMap["gate"] = gate; - TagReplacingMap["sate"] = sate; - TagReplacingMap["flightlevel"] = flightlevel; - TagReplacingMap["gs"] = speed; - TagReplacingMap["tendency"] = tendency; - TagReplacingMap["wake"] = showWakeTurb ? wake : ""; - TagReplacingMap["ssr"] = tssr; - TagReplacingMap["asid"] = showSID ? dep : ""; - TagReplacingMap["ssid"] = showSID ? ssid : ""; - TagReplacingMap["origin"] = origin; - TagReplacingMap["dest"] = dest; - TagReplacingMap["groundstatus"] = gstat; - TagReplacingMap["uk_stand"] = uk_stand; - - return TagReplacingMap; +map CSMRRadar::GenerateTagData( + CRadarTarget rt, CFlightPlan fp, bool isAcCorrelated, bool isProMode, + int TransitionAltitude, bool useSpeedForGates, string ActiveAirport, + bool showAircraftType, bool showSID, bool showWakeTurb) { + Logger::info(string(__FUNCSIG__)); + // ---- + // Tag items available + // callsign: Callsign with freq state and comm * + // actype: Aircraft type * + // sctype: Aircraft type that changes for squawk error * + // sqerror: Squawk error if there is one, or empty * + // deprwy: Departure runway * + // seprwy: Departure runway that changes to speed if speed > 25kts * + // arvrwy: Arrival runway * + // srvrwy: Speed that changes to arrival runway if speed < 25kts * + // gate: Gate, from speed or scratchpad * + // sate: Gate, from speed or scratchpad that changes to speed if speed > 25kts + // * flightlevel: Flightlevel/Pressure altitude of the ac * gs: Ground speed + // of the ac * tendency: Climbing or descending symbol * wake: Wake turbulance + // cat * groundstatus: Current status * ssr: the current squawk of the ac sid: + // the assigned SID ssid: a short version of the SID origin: origin aerodrome + // dest: destination aerodrome + // ---- + + bool IsPrimary = !rt.GetPosition().GetTransponderC(); + bool isAirborne = rt.GetPosition().GetReportedGS() > 50; + + // ----- Callsign ------- + string callsign = rt.GetCallsign(); + if (fp.IsValid()) { + if (fp.GetControllerAssignedData().GetCommunicationType() == 't' || + fp.GetControllerAssignedData().GetCommunicationType() == 'T' || + fp.GetControllerAssignedData().GetCommunicationType() == 'r' || + fp.GetControllerAssignedData().GetCommunicationType() == 'R' || + fp.GetControllerAssignedData().GetCommunicationType() == 'v' || + fp.GetControllerAssignedData().GetCommunicationType() == 'V') { + if (fp.GetControllerAssignedData().GetCommunicationType() != 'v' && + fp.GetControllerAssignedData().GetCommunicationType() != 'V') { + callsign.append("/"); + callsign += fp.GetControllerAssignedData().GetCommunicationType(); + } + } else if (fp.GetFlightPlanData().GetCommunicationType() == 't' || + fp.GetFlightPlanData().GetCommunicationType() == 'r' || + fp.GetFlightPlanData().GetCommunicationType() == 'T' || + fp.GetFlightPlanData().GetCommunicationType() == 'R') { + callsign.append("/"); + callsign += fp.GetFlightPlanData().GetCommunicationType(); + } + + switch (fp.GetState()) { + + case FLIGHT_PLAN_STATE_TRANSFER_TO_ME_INITIATED: + callsign = ">>" + callsign; + break; + + case FLIGHT_PLAN_STATE_TRANSFER_FROM_ME_INITIATED: + callsign = callsign + ">>"; + break; + + case FLIGHT_PLAN_STATE_ASSUMED: + callsign = "[" + callsign + "]"; + break; + } + } + + // ----- Squawk error ------- + string sqerror = ""; + const char *assr = fp.GetControllerAssignedData().GetSquawk(); + const char *ssr = rt.GetPosition().GetSquawk(); + bool has_squawk_error = false; + if (strlen(assr) != 0 && !startsWith(ssr, assr)) { + has_squawk_error = true; + sqerror = "A"; + sqerror.append(assr); + } + + // ----- Aircraft type ------- + + string actype = "NoFPL"; + if (fp.IsValid() && fp.GetFlightPlanData().IsReceived()) + actype = fp.GetFlightPlanData().GetAircraftFPType(); + if (actype.size() > 4 && actype != "NoFPL") + actype = actype.substr(0, 4); + + // ----- Aircraft type that changes to squawk error ------- + string sctype = actype; + if (has_squawk_error) + sctype = sqerror; + + // ----- Groundspeed ------- + string speed = std::to_string(rt.GetPosition().GetReportedGS()); + + // ----- Departure runway ------- + string deprwy = fp.GetFlightPlanData().GetDepartureRwy(); + if (deprwy.length() == 0) + deprwy = "RWY"; + + // ----- Departure runway that changes for overspeed ------- + string seprwy = deprwy; + if (rt.GetPosition().GetReportedGS() > 25) + seprwy = std::to_string(rt.GetPosition().GetReportedGS()); + + // ----- Arrival runway ------- + string arvrwy = fp.GetFlightPlanData().GetArrivalRwy(); + if (arvrwy.length() == 0) + arvrwy = "RWY"; + + // ----- Speed that changes to arrival runway ----- + string srvrwy = speed; + if (rt.GetPosition().GetReportedGS() < 25) + srvrwy = arvrwy; + + // ----- Gate ------- + string gate; + if (useSpeedForGates) + gate = std::to_string(fp.GetControllerAssignedData().GetAssignedSpeed()); + else + gate = fp.GetControllerAssignedData().GetScratchPadString(); + + replaceAll(gate, "STAND=", ""); + gate = gate.substr(0, 4); + + if (gate.size() == 0 || gate == "0" || !isAcCorrelated) + gate = "NoGate"; + + // ----- Gate that changes to speed ------- + string sate = gate; + if (rt.GetPosition().GetReportedGS() > 25) + sate = speed; + + // ----- Flightlevel ------- + int fl = rt.GetPosition().GetFlightLevel(); + int padding = 5; + string pfls = ""; + if (fl <= TransitionAltitude) { + fl = rt.GetPosition().GetPressureAltitude(); + pfls = "A"; + padding = 4; + } + string flightlevel = (pfls + padWithZeros(padding, fl)).substr(0, 3); + + // ----- Tendency ------- + string tendency = "-"; + int delta_fl = rt.GetPosition().GetFlightLevel() - + rt.GetPreviousPosition(rt.GetPosition()).GetFlightLevel(); + if (abs(delta_fl) >= 50) { + if (delta_fl < 0) { + tendency = "|"; + } else { + tendency = "^"; + } + } + + // ----- Wake cat ------- + string wake = "?"; + if (fp.IsValid() && isAcCorrelated) { + wake = ""; + wake += fp.GetFlightPlanData().GetAircraftWtc(); + } + + // ----- SSR ------- + string tssr = ""; + if (rt.IsValid()) { + tssr = rt.GetPosition().GetSquawk(); + } + + // ----- SID ------- + string dep = "SID"; + if (fp.IsValid() && isAcCorrelated) { + dep = fp.GetFlightPlanData().GetSidName(); + } + + // ----- Short SID ------- + string ssid = dep; + if (fp.IsValid() && ssid.size() > 5 && isAcCorrelated) { + ssid = dep.substr(0, 3); + ssid += dep.substr(dep.size() - 2, dep.size()); + } + + // ------- Origin aerodrome ------- + string origin = "????"; + if (isAcCorrelated) { + origin = fp.GetFlightPlanData().GetOrigin(); + } + + // ------- Destination aerodrome ------- + string dest = "????"; + if (isAcCorrelated) { + dest = fp.GetFlightPlanData().GetDestination(); + } + + // ----- GSTAT ------- + string gstat = "STS"; + if (fp.IsValid() && isAcCorrelated) { + if (strlen(fp.GetGroundState()) != 0) + gstat = fp.GetGroundState(); + } + + // ----- UK Controller Plugin / Assigned Stand ------- + string uk_stand; + uk_stand = fp.GetControllerAssignedData().GetFlightStripAnnotation(3); + if (uk_stand.length() == 0) + uk_stand = "NoGate"; + + // ----- Generating the replacing map ----- + map TagReplacingMap; + + // System ID for uncorrelated + TagReplacingMap["systemid"] = "T:"; + string tpss = rt.GetSystemID(); + TagReplacingMap["systemid"].append(tpss.substr(1, 6)); + + // Pro mode data here + if (isProMode) { + + if (isAirborne && !isAcCorrelated) { + callsign = tssr; + } + + if (!isAcCorrelated) { + actype = "NoFPL"; + } + + // Is a primary target + + if (isAirborne && !isAcCorrelated && IsPrimary) { + flightlevel = "NoALT"; + tendency = "?"; + speed = std::to_string(rt.GetGS()); + } + + if (isAirborne && !isAcCorrelated && IsPrimary) { + callsign = TagReplacingMap["systemid"]; + } + } + + TagReplacingMap["callsign"] = callsign; + TagReplacingMap["actype"] = showAircraftType ? actype : ""; + TagReplacingMap["sctype"] = showAircraftType ? sctype : ""; + TagReplacingMap["sqerror"] = sqerror; + TagReplacingMap["deprwy"] = deprwy; + TagReplacingMap["seprwy"] = seprwy; + TagReplacingMap["arvrwy"] = arvrwy; + TagReplacingMap["srvrwy"] = srvrwy; + TagReplacingMap["gate"] = gate; + TagReplacingMap["sate"] = sate; + TagReplacingMap["flightlevel"] = flightlevel; + TagReplacingMap["gs"] = speed; + TagReplacingMap["tendency"] = tendency; + TagReplacingMap["wake"] = showWakeTurb ? wake : ""; + TagReplacingMap["ssr"] = tssr; + TagReplacingMap["asid"] = showSID ? dep : ""; + TagReplacingMap["ssid"] = showSID ? ssid : ""; + TagReplacingMap["origin"] = origin; + TagReplacingMap["dest"] = dest; + TagReplacingMap["groundstatus"] = gstat; + TagReplacingMap["uk_stand"] = uk_stand; + + return TagReplacingMap; } -void CSMRRadar::OnFlightPlanDisconnect(CFlightPlan FlightPlan) -{ - Logger::info(string(__FUNCSIG__)); - string callsign = string(FlightPlan.GetCallsign()); +void CSMRRadar::OnFlightPlanDisconnect(CFlightPlan FlightPlan) { + Logger::info(string(__FUNCSIG__)); + string callsign = string(FlightPlan.GetCallsign()); - for (multimap::iterator itr = DistanceTools.begin(); itr != DistanceTools.end(); ++itr) { - if (itr->first == callsign || itr->second == callsign) - itr = DistanceTools.erase(itr); - } -} - -LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - switch (uMsg) - { - case WM_SETCURSOR: - SetCursor(smrCursor); - return true; - default: - return CallWindowProc(gSourceProc, hwnd, uMsg, wParam, lParam); - } -} + for (multimap::iterator itr = DistanceTools.begin(); + itr != DistanceTools.end(); ++itr) { + if (itr->first == callsign || itr->second == callsign) + itr = DistanceTools.erase(itr); + } -void CSMRRadar::OnRefresh(HDC hDC, int Phase) -{ - Logger::info(string(__FUNCSIG__)); - // Changing the mouse cursor - if (initCursor) - { - if (customCursor) { - smrCursor = CopyCursor((HCURSOR)::LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDC_SMRCURSOR), IMAGE_CURSOR, 0, 0, LR_SHARED)); - // This got broken because of threading as far as I can tell - // The cursor does change for some milliseconds but gets reset almost instantly by external MFC code - - } - else { - smrCursor = (HCURSOR)::LoadCursor(NULL, IDC_ARROW); - } - - if (smrCursor != nullptr) - { - pluginWindow = GetActiveWindow(); - gSourceProc = (WNDPROC)SetWindowLong(pluginWindow, GWL_WNDPROC, (LONG)WindowProc); - } - initCursor = false; - } - - if (Phase == REFRESH_PHASE_AFTER_LISTS) { - Logger::info("Phase == REFRESH_PHASE_AFTER_LISTS"); - if (!ColorSettingsDay) { - // Creating the gdi+ graphics - Graphics graphics(hDC); - graphics.SetPageUnit(Gdiplus::UnitPixel); - - graphics.SetSmoothingMode(SmoothingModeAntiAlias); - - SolidBrush AlphaBrush(Color(CurrentConfig->getActiveProfile()["filters"]["night_alpha_setting"].GetInt(), 0, 0, 0)); - - CRect RadarArea(GetRadarArea()); - RadarArea.top = RadarArea.top - 1; - RadarArea.bottom = GetChatArea().bottom; - - graphics.FillRectangle(&AlphaBrush, CopyRect(CRect(RadarArea))); - - graphics.ReleaseHDC(hDC); - } - - Logger::info("break Phase == REFRESH_PHASE_AFTER_LISTS"); - return; - } - - if (Phase != REFRESH_PHASE_BEFORE_TAGS) - return; - - Logger::info("Phase != REFRESH_PHASE_BEFORE_TAGS"); - - struct Utils { - static RECT GetAreaFromText(CDC * dc, string text, POINT Pos) { - RECT Area = { Pos.x, Pos.y, Pos.x + dc->GetTextExtent(text.c_str()).cx, Pos.y + dc->GetTextExtent(text.c_str()).cy }; - return Area; - } - static string getEnumString(TagTypes type) { - if (type == TagTypes::Departure) - return "departure"; - if (type == TagTypes::Arrival) - return "arrival"; - if (type == TagTypes::Uncorrelated) - return "uncorrelated"; - return "airborne"; - } - }; - - // Timer each seconds - clock_final = clock() - clock_init; - double delta_t = (double)clock_final / ((double)CLOCKS_PER_SEC); - if (delta_t >= 1) { - clock_init = clock(); - BLINK = !BLINK; - RefreshAirportActivity(); - } - - if (!QDMenabled && !QDMSelectEnabled) - { - POINT p; - if (GetCursorPos(&p)) { - if (ScreenToClient(GetActiveWindow(), &p)) { - mouseLocation = p; - } - } - } - - Logger::info("Graphics set up"); - CDC dc; - dc.Attach(hDC); - - // Creating the gdi+ graphics - Graphics graphics(hDC); - graphics.SetPageUnit(Gdiplus::UnitPixel); - - graphics.SetSmoothingMode(SmoothingModeAntiAlias); - - RECT RadarArea = GetRadarArea(); - RECT ChatArea = GetChatArea(); - RadarArea.bottom = ChatArea.top; - - AirportPositions.clear(); - - - CSectorElement apt; - for (apt = GetPlugIn()->SectorFileElementSelectFirst(SECTOR_ELEMENT_AIRPORT); - apt.IsValid(); - apt = GetPlugIn()->SectorFileElementSelectNext(apt, SECTOR_ELEMENT_AIRPORT)) - { - CPosition Pos; - apt.GetPosition(&Pos, 0); - AirportPositions[string(apt.GetName())] = Pos; - } - - RimcasInstance->RunwayAreas.clear(); - - if (QDMSelectEnabled || QDMenabled) - { - CRect R(GetRadarArea()); - R.top += 20; - R.bottom = GetChatArea().top; - - R.NormalizeRect(); - AddScreenObject(DRAWING_BACKGROUND_CLICK, "", R, false, ""); - } - - Logger::info("Runway loop"); - CSectorElement rwy; - for (rwy = GetPlugIn()->SectorFileElementSelectFirst(SECTOR_ELEMENT_RUNWAY); - rwy.IsValid(); - rwy = GetPlugIn()->SectorFileElementSelectNext(rwy, SECTOR_ELEMENT_RUNWAY)) - { - if (startsWith(getActiveAirport().c_str(), rwy.GetAirportName())) { - - CPosition Left; - rwy.GetPosition(&Left, 1); - CPosition Right; - rwy.GetPosition(&Right, 0); - - string runway_name = rwy.GetRunwayName(0); - string runway_name2 = rwy.GetRunwayName(1); - - double bearing1 = TrueBearing(Left, Right); - double bearing2 = TrueBearing(Right, Left); - - const Value& CustomMap = CurrentConfig->getAirportMapIfAny(getActiveAirport()); - - vector def; -// Rimcas now ignores the defined runway polygon to ensure that the correct detection area is used, defined runway is now only used for closed runway -// if (CurrentConfig->isCustomRunwayAvail(getActiveAirport(), runway_name, runway_name2)) { - // const Value& Runways = CustomMap["runways"]; - // - // if (Runways.IsArray()) { - // for (SizeType i = 0; i < Runways.Size(); i++) { - // if (startsWith(runway_name.c_str(), Runways[i]["runway_name"].GetString()) || - // startsWith(runway_name2.c_str(), Runways[i]["runway_name"].GetString())) { - // - // string path_name = "path"; - // - // if (isLVP) - // path_name = "path_lvp"; - // - // const Value& Path = Runways[i][path_name.c_str()]; - // for (SizeType j = 0; j < Path.Size(); j++) { - // CPosition position; - // position.LoadFromStrings(Path[j][(SizeType)1].GetString(), Path[j][(SizeType)0].GetString()); - // - // def.push_back(position); - // } - // - // } - // } - // } - //} - //else { - def = RimcasInstance->GetRunwayArea(Left, Right); - //} - - RimcasInstance->AddRunwayArea(this, runway_name, runway_name2, def); - - string RwName = runway_name + " / " + runway_name2; - - if (RimcasInstance->ClosedRunway.find(RwName) != RimcasInstance->ClosedRunway.end()) { - if (RimcasInstance->ClosedRunway[RwName]) { - - CPen RedPen(PS_SOLID, 2, RGB(150, 0, 0)); - CPen * oldPen = dc.SelectObject(&RedPen); - - if (CurrentConfig->isCustomRunwayAvail(getActiveAirport(), runway_name, runway_name2)) { - const Value& Runways = CustomMap["runways"]; - - if (Runways.IsArray()) { - for (SizeType i = 0; i < Runways.Size(); i++) { - if (startsWith(runway_name.c_str(), Runways[i]["runway_name"].GetString()) || - startsWith(runway_name2.c_str(), Runways[i]["runway_name"].GetString())) { - - string path_name = "path"; - - if (isLVP) - path_name = "path_lvp"; - - const Value& Path = Runways[i][path_name.c_str()]; - - PointF lpPoints[5000]; - - int k = 1; - int l = 0; - for (SizeType w = 0; w < Path.Size(); w++) { - CPosition position; - position.LoadFromStrings(Path[w][static_cast(1)].GetString(), Path[w][static_cast(0)].GetString()); - - POINT cv = ConvertCoordFromPositionToPixel(position); - lpPoints[l] = { REAL(cv.x), REAL(cv.y) }; - - k++; - l++; - } - - graphics.FillPolygon(&SolidBrush(Color(150, 0, 0)), lpPoints, k - 1); - - break; - } - } - } - - } - else { - vector Area = RimcasInstance->GetRunwayArea(Left, Right); - - PointF lpPoints[5000]; - int w = 0; - for(auto &Point : Area) - { - POINT toDraw = ConvertCoordFromPositionToPixel(Point); - - lpPoints[w] = { REAL(toDraw.x), REAL(toDraw.y) }; - w++; - } + // Remove from departure timer when aircraft disconnects + RimcasInstance->DismissDeparture(callsign); - graphics.FillPolygon(&SolidBrush(Color(150, 0, 0)), lpPoints, w); - } - - dc.SelectObject(oldPen); - } - } - } - } - - RimcasInstance->OnRefreshBegin(isLVP); - - Logger::info("Drawing WIP Areas"); - - if (wipAreasActive) { - CPen RedPen(PS_SOLID, 2, RGB(150, 0, 0)); - CPen* oldPen = dc.SelectObject(&RedPen); + // Clear the ground tracking state for this aircraft + if (RimcasInstance->AircraftWasOnGround.find(callsign) != + RimcasInstance->AircraftWasOnGround.end()) { + RimcasInstance->AircraftWasOnGround.erase(callsign); + } +} - for (vector wipArea : wipAreas) { - PointF lpPoints[5000]; - int w = 0; - for (auto& Point : wipArea) { - POINT toDraw = ConvertCoordFromPositionToPixel(Point); +LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, + LPARAM lParam) { + switch (uMsg) { + case WM_SETCURSOR: + SetCursor(smrCursor); + return true; + default: + return CallWindowProc(gSourceProc, hwnd, uMsg, wParam, lParam); + } +} - lpPoints[w] = { REAL(toDraw.x), REAL(toDraw.y) }; - w++; - } +void CSMRRadar::OnRefresh(HDC hDC, int Phase) { + Logger::info(string(__FUNCSIG__)); + // Changing the mouse cursor + if (initCursor) { + if (customCursor) { + smrCursor = CopyCursor((HCURSOR)::LoadImage( + AfxGetInstanceHandle(), MAKEINTRESOURCE(IDC_SMRCURSOR), IMAGE_CURSOR, + 0, 0, LR_SHARED)); + // This got broken because of threading as far as I can tell + // The cursor does change for some milliseconds but gets reset almost + // instantly by external MFC code + + } else { + smrCursor = (HCURSOR)::LoadCursor(NULL, IDC_ARROW); + } + + if (smrCursor != nullptr) { + pluginWindow = GetActiveWindow(); + gSourceProc = + (WNDPROC)SetWindowLong(pluginWindow, GWL_WNDPROC, (LONG)WindowProc); + } + initCursor = false; + } + + if (Phase == REFRESH_PHASE_AFTER_LISTS) { + Logger::info("Phase == REFRESH_PHASE_AFTER_LISTS"); + if (!ColorSettingsDay) { + // Creating the gdi+ graphics + Graphics graphics(hDC); + graphics.SetPageUnit(Gdiplus::UnitPixel); + + graphics.SetSmoothingMode(SmoothingModeAntiAlias); + + SolidBrush AlphaBrush(Color( + CurrentConfig->getActiveProfile()["filters"]["night_alpha_setting"] + .GetInt(), + 0, 0, 0)); + + CRect RadarArea(GetRadarArea()); + RadarArea.top = RadarArea.top - 1; + RadarArea.bottom = GetChatArea().bottom; + + graphics.FillRectangle(&AlphaBrush, CopyRect(CRect(RadarArea))); + + graphics.ReleaseHDC(hDC); + } + + Logger::info("break Phase == REFRESH_PHASE_AFTER_LISTS"); + return; + } + + if (Phase != REFRESH_PHASE_BEFORE_TAGS) + return; + + Logger::info("Phase != REFRESH_PHASE_BEFORE_TAGS"); + + struct Utils { + static RECT GetAreaFromText(CDC *dc, string text, POINT Pos) { + RECT Area = {Pos.x, Pos.y, Pos.x + dc->GetTextExtent(text.c_str()).cx, + Pos.y + dc->GetTextExtent(text.c_str()).cy}; + return Area; + } + static string getEnumString(TagTypes type) { + if (type == TagTypes::Departure) + return "departure"; + if (type == TagTypes::Arrival) + return "arrival"; + if (type == TagTypes::Uncorrelated) + return "uncorrelated"; + return "airborne"; + } + }; + + // Timer each seconds + clock_final = clock() - clock_init; + double delta_t = (double)clock_final / ((double)CLOCKS_PER_SEC); + if (delta_t >= 1) { + clock_init = clock(); + BLINK = !BLINK; + RefreshAirportActivity(); + } + + if (!QDMenabled && !QDMSelectEnabled) { + POINT p; + if (GetCursorPos(&p)) { + if (ScreenToClient(GetActiveWindow(), &p)) { + mouseLocation = p; + } + } + } + + Logger::info("Graphics set up"); + CDC dc; + dc.Attach(hDC); + + // Creating the gdi+ graphics + Graphics graphics(hDC); + graphics.SetPageUnit(Gdiplus::UnitPixel); + + graphics.SetSmoothingMode(SmoothingModeAntiAlias); + + RECT RadarArea = GetRadarArea(); + RECT ChatArea = GetChatArea(); + RadarArea.bottom = ChatArea.top; + + AirportPositions.clear(); + + CSectorElement apt; + for (apt = GetPlugIn()->SectorFileElementSelectFirst(SECTOR_ELEMENT_AIRPORT); + apt.IsValid(); apt = GetPlugIn()->SectorFileElementSelectNext( + apt, SECTOR_ELEMENT_AIRPORT)) { + CPosition Pos; + apt.GetPosition(&Pos, 0); + AirportPositions[string(apt.GetName())] = Pos; + } + + RimcasInstance->RunwayAreas.clear(); + + if (QDMSelectEnabled || QDMenabled) { + CRect R(GetRadarArea()); + R.top += 20; + R.bottom = GetChatArea().top; + + R.NormalizeRect(); + AddScreenObject(DRAWING_BACKGROUND_CLICK, "", R, false, ""); + } + + Logger::info("Runway loop"); + CSectorElement rwy; + for (rwy = GetPlugIn()->SectorFileElementSelectFirst(SECTOR_ELEMENT_RUNWAY); + rwy.IsValid(); rwy = GetPlugIn()->SectorFileElementSelectNext( + rwy, SECTOR_ELEMENT_RUNWAY)) { + if (startsWith(getActiveAirport().c_str(), rwy.GetAirportName())) { + + CPosition Left; + rwy.GetPosition(&Left, 1); + CPosition Right; + rwy.GetPosition(&Right, 0); + + string runway_name = rwy.GetRunwayName(0); + string runway_name2 = rwy.GetRunwayName(1); + + double bearing1 = TrueBearing(Left, Right); + double bearing2 = TrueBearing(Right, Left); + + const Value &CustomMap = + CurrentConfig->getAirportMapIfAny(getActiveAirport()); + + vector def; + // Rimcas now ignores the defined runway polygon to ensure that the + // correct detection area is used, defined runway is now only used for + // closed runway + // if + //(CurrentConfig->isCustomRunwayAvail(getActiveAirport(), runway_name, + //runway_name2)) { const Value& Runways = CustomMap["runways"]; + // + // if (Runways.IsArray()) { + // for (SizeType i = 0; i < Runways.Size(); i++) { + // if (startsWith(runway_name.c_str(), + //Runways[i]["runway_name"].GetString()) || + // startsWith(runway_name2.c_str(), + //Runways[i]["runway_name"].GetString())) { + // + // string path_name = "path"; + // + // if (isLVP) + // path_name = "path_lvp"; + // + // const Value& Path = + //Runways[i][path_name.c_str()]; for (SizeType j = 0; j < Path.Size(); + //j++) { CPosition position; + // position.LoadFromStrings(Path[j][(SizeType)1].GetString(), + //Path[j][(SizeType)0].GetString()); + // + // def.push_back(position); + // } + // + // } + // } + // } + //} + // else { + def = RimcasInstance->GetRunwayArea(Left, Right); + //} + + RimcasInstance->AddRunwayArea(this, runway_name, runway_name2, def); + + string RwName = runway_name + " / " + runway_name2; + + if (RimcasInstance->ClosedRunway.find(RwName) != + RimcasInstance->ClosedRunway.end()) { + if (RimcasInstance->ClosedRunway[RwName]) { + + CPen RedPen(PS_SOLID, 2, RGB(150, 0, 0)); + CPen *oldPen = dc.SelectObject(&RedPen); + + if (CurrentConfig->isCustomRunwayAvail(getActiveAirport(), + runway_name, runway_name2)) { + const Value &Runways = CustomMap["runways"]; + + if (Runways.IsArray()) { + for (SizeType i = 0; i < Runways.Size(); i++) { + if (startsWith(runway_name.c_str(), + Runways[i]["runway_name"].GetString()) || + startsWith(runway_name2.c_str(), + Runways[i]["runway_name"].GetString())) { + + string path_name = "path"; + + if (isLVP) + path_name = "path_lvp"; + + const Value &Path = Runways[i][path_name.c_str()]; + + PointF lpPoints[5000]; + + int k = 1; + int l = 0; + for (SizeType w = 0; w < Path.Size(); w++) { + CPosition position; + position.LoadFromStrings( + Path[w][static_cast(1)].GetString(), + Path[w][static_cast(0)].GetString()); + + POINT cv = ConvertCoordFromPositionToPixel(position); + lpPoints[l] = {REAL(cv.x), REAL(cv.y)}; + + k++; + l++; + } + + graphics.FillPolygon(&SolidBrush(Color(150, 0, 0)), lpPoints, + k - 1); + + break; + } + } + } + + } else { + vector Area = RimcasInstance->GetRunwayArea(Left, Right); + + PointF lpPoints[5000]; + int w = 0; + for (auto &Point : Area) { + POINT toDraw = ConvertCoordFromPositionToPixel(Point); + + lpPoints[w] = {REAL(toDraw.x), REAL(toDraw.y)}; + w++; + } + + graphics.FillPolygon(&SolidBrush(Color(150, 0, 0)), lpPoints, w); + } + + dc.SelectObject(oldPen); + } + } + } + } + + RimcasInstance->OnRefreshBegin(isLVP); + + Logger::info("Drawing WIP Areas"); + + if (wipAreasActive) { + CPen RedPen(PS_SOLID, 2, RGB(150, 0, 0)); + CPen *oldPen = dc.SelectObject(&RedPen); + + for (vector wipArea : wipAreas) { + PointF lpPoints[5000]; + int w = 0; + for (auto &Point : wipArea) { + POINT toDraw = ConvertCoordFromPositionToPixel(Point); - graphics.FillPolygon(&SolidBrush(Color(150, 0, 0)), lpPoints, w); - } + lpPoints[w] = {REAL(toDraw.x), REAL(toDraw.y)}; + w++; + } - dc.SelectObject(oldPen); - } + graphics.FillPolygon(&SolidBrush(Color(150, 0, 0)), lpPoints, w); + } + dc.SelectObject(oldPen); + } #pragma region symbols - // Drawing the symbols - Logger::info("Symbols loop"); - EuroScopePlugIn::CRadarTarget rt; - for (rt = GetPlugIn()->RadarTargetSelectFirst(); - rt.IsValid(); - rt = GetPlugIn()->RadarTargetSelectNext(rt)) - { - if (!rt.IsValid() || !rt.GetPosition().IsValid()) - continue; - - int reportedGs = rt.GetPosition().GetReportedGS(); - int radarRange = CurrentConfig->getActiveProfile()["filters"]["radar_range_nm"].GetInt(); - int altitudeFilter = CurrentConfig->getActiveProfile()["filters"]["hide_above_alt"].GetInt(); - int speedFilter = CurrentConfig->getActiveProfile()["filters"]["hide_above_spd"].GetInt(); - bool isAcDisplayed = isVisible(rt); - - if (!isAcDisplayed) - continue; - - // Get aircraft type and stand for RIMCAS - CFlightPlan fp = GetPlugIn()->FlightPlanSelect(rt.GetCallsign()); - string acType = ""; - string acStand = ""; - if (fp.IsValid() && fp.GetFlightPlanData().IsReceived()) { - acType = fp.GetFlightPlanData().GetAircraftFPType(); - acStand = fp.GetControllerAssignedData().GetFlightStripAnnotation(3); - if (acStand.length() == 0) - acStand = ""; - } - - RimcasInstance->OnRefresh(rt, this, IsCorrelated(fp, rt), acType, acStand); - - CRadarTargetPositionData RtPos = rt.GetPosition(); - - POINT acPosPix = ConvertCoordFromPositionToPixel(RtPos.GetPosition()); - - if (rt.GetGS() > 5) { - POINT oldacPosPix; - CRadarTargetPositionData pAcPos = rt.GetPosition(); - - for (int i = 1; i <= 2; i++) { - oldacPosPix = ConvertCoordFromPositionToPixel(pAcPos.GetPosition()); - pAcPos = rt.GetPreviousPosition(pAcPos); - acPosPix = ConvertCoordFromPositionToPixel(pAcPos.GetPosition()); - - if (i == 1 && !Patatoides[rt.GetCallsign()].History_one_points.empty() && Afterglow && CurrentConfig->getActiveProfile()["targets"]["show_primary_target"].GetBool()) { - SolidBrush H_Brush(ColorManager->get_corrected_color("afterglow", - CurrentConfig->getConfigColor(CurrentConfig->getActiveProfile()["targets"]["history_one_color"]))); - - PointF lpPoints[100]; - for (unsigned int i1 = 0; i1 < Patatoides[rt.GetCallsign()].History_one_points.size(); i1++) - { - CPosition pos; - pos.m_Latitude = Patatoides[rt.GetCallsign()].History_one_points[i1].x; - pos.m_Longitude = Patatoides[rt.GetCallsign()].History_one_points[i1].y; - - lpPoints[i1] = { REAL(ConvertCoordFromPositionToPixel(pos).x), REAL(ConvertCoordFromPositionToPixel(pos).y) }; - } - graphics.FillPolygon(&H_Brush, lpPoints, Patatoides[rt.GetCallsign()].History_one_points.size()); - } - - if (i != 2) { - if (!Patatoides[rt.GetCallsign()].History_two_points.empty() && Afterglow && CurrentConfig->getActiveProfile()["targets"]["show_primary_target"].GetBool()) { - SolidBrush H_Brush(ColorManager->get_corrected_color("afterglow", - CurrentConfig->getConfigColor(CurrentConfig->getActiveProfile()["targets"]["history_two_color"]))); - - PointF lpPoints[100]; - for (unsigned int i1 = 0; i1 < Patatoides[rt.GetCallsign()].History_two_points.size(); i1++) - { - CPosition pos; - pos.m_Latitude = Patatoides[rt.GetCallsign()].History_two_points[i1].x; - pos.m_Longitude = Patatoides[rt.GetCallsign()].History_two_points[i1].y; - - lpPoints[i1] = { REAL(ConvertCoordFromPositionToPixel(pos).x), REAL(ConvertCoordFromPositionToPixel(pos).y) }; - } - graphics.FillPolygon(&H_Brush, lpPoints, Patatoides[rt.GetCallsign()].History_two_points.size()); - } - } - - if (i == 2 && !Patatoides[rt.GetCallsign()].History_three_points.empty() && Afterglow && CurrentConfig->getActiveProfile()["targets"]["show_primary_target"].GetBool()) { - SolidBrush H_Brush(ColorManager->get_corrected_color("afterglow", - CurrentConfig->getConfigColor(CurrentConfig->getActiveProfile()["targets"]["history_three_color"]))); - - PointF lpPoints[100]; - for (unsigned int i1 = 0; i1 < Patatoides[rt.GetCallsign()].History_three_points.size(); i1++) - { - CPosition pos; - pos.m_Latitude = Patatoides[rt.GetCallsign()].History_three_points[i1].x; - pos.m_Longitude = Patatoides[rt.GetCallsign()].History_three_points[i1].y; - - lpPoints[i1] = { REAL(ConvertCoordFromPositionToPixel(pos).x), REAL(ConvertCoordFromPositionToPixel(pos).y) }; - } - graphics.FillPolygon(&H_Brush, lpPoints, Patatoides[rt.GetCallsign()].History_three_points.size()); - } - } - - int TrailNumber = Trail_Gnd; - if (reportedGs > 50) - TrailNumber = Trail_App; - - CRadarTargetPositionData previousPos = rt.GetPreviousPosition(rt.GetPosition()); - for (int j = 1; j <= TrailNumber; j++) { - POINT pCoord = ConvertCoordFromPositionToPixel(previousPos.GetPosition()); - - graphics.FillRectangle(&SolidBrush(ColorManager->get_corrected_color("symbol", Gdiplus::Color::White)), - pCoord.x - 1, pCoord.y - 1, 2, 2); - - previousPos = rt.GetPreviousPosition(previousPos); - } - } - - - if (CurrentConfig->getActiveProfile()["targets"]["show_primary_target"].GetBool()) { - PointF lpPoints[100]; - for (unsigned int i = 0; i < Patatoides[rt.GetCallsign()].points.size(); i++) - { - CPosition pos; - pos.m_Latitude = Patatoides[rt.GetCallsign()].points[i].x; - pos.m_Longitude = Patatoides[rt.GetCallsign()].points[i].y; - - lpPoints[i] = { REAL(ConvertCoordFromPositionToPixel(pos).x), REAL(ConvertCoordFromPositionToPixel(pos).y) }; - } - - if ((getActiveAirport() == "EGLL" || getActiveAirport() == "EGKK") && CurrentConfig->isPositionInGeofenceArea(getActiveAirport(), RtPos.GetPosition())) { - SolidBrush H_Brush(ColorManager->get_corrected_color("afterglow", Gdiplus::Color::White)); - graphics.FillPolygon(&H_Brush, lpPoints, Patatoides[rt.GetCallsign()].points.size()); - - } - else { - SolidBrush H_Brush(ColorManager->get_corrected_color("afterglow", - CurrentConfig->getConfigColor(CurrentConfig->getActiveProfile()["targets"]["target_color"]))); - - graphics.FillPolygon(&H_Brush, lpPoints, Patatoides[rt.GetCallsign()].points.size()); - } - } - acPosPix = ConvertCoordFromPositionToPixel(RtPos.GetPosition()); - - bool AcisCorrelated = IsCorrelated(GetPlugIn()->FlightPlanSelect(rt.GetCallsign()), rt); - - if (!AcisCorrelated && reportedGs < 1 && !ReleaseInProgress && !AcquireInProgress) - continue; - - CPen qTrailPen(PS_SOLID, 1, ColorManager->get_corrected_color("symbol", Gdiplus::Color::White).ToCOLORREF()); - CPen* pqOrigPen = dc.SelectObject(&qTrailPen); - - // Correlation cross - - if ((getActiveAirport() == "EGLL" || getActiveAirport() == "EGKK") && CurrentConfig->isPositionInGeofenceArea(getActiveAirport(), RtPos.GetPosition())) { - dc.MoveTo({ acPosPix.x, acPosPix.y + 1 }); - dc.LineTo({ acPosPix.x + 1, acPosPix.y }); - dc.LineTo({ acPosPix.x, acPosPix.y - 1 }); - dc.LineTo({ acPosPix.x - 1, acPosPix.y }); - dc.LineTo({ acPosPix.x, acPosPix.y + 1 }); - } else { - if (RtPos.GetTransponderC()) { - dc.MoveTo({ acPosPix.x, acPosPix.y - 6 }); - dc.LineTo({ acPosPix.x - 6, acPosPix.y }); - dc.LineTo({ acPosPix.x, acPosPix.y + 6 }); - dc.LineTo({ acPosPix.x + 6, acPosPix.y }); - dc.LineTo({ acPosPix.x, acPosPix.y - 6 }); - } - else { - dc.MoveTo(acPosPix.x, acPosPix.y); - dc.LineTo(acPosPix.x - 4, acPosPix.y - 4); - dc.MoveTo(acPosPix.x, acPosPix.y); - dc.LineTo(acPosPix.x + 4, acPosPix.y - 4); - dc.MoveTo(acPosPix.x, acPosPix.y); - dc.LineTo(acPosPix.x - 4, acPosPix.y + 4); - dc.MoveTo(acPosPix.x, acPosPix.y); - dc.LineTo(acPosPix.x + 4, acPosPix.y + 4); - } - } - - // Predicted Track Line - // It starts 20 seconds away from the ac - if (reportedGs > 50 && PredictedLength > 0) - { - double d = double(rt.GetPosition().GetReportedGS()*0.514444) * 10; - CPosition AwayBase = BetterHarversine(rt.GetPosition().GetPosition(), rt.GetTrackHeading(), d); - - d = double(rt.GetPosition().GetReportedGS()*0.514444) * (PredictedLength * 60) - 10; - CPosition PredictedEnd = BetterHarversine(AwayBase, rt.GetTrackHeading(), d); - - dc.MoveTo(ConvertCoordFromPositionToPixel(AwayBase)); - dc.LineTo(ConvertCoordFromPositionToPixel(PredictedEnd)); - } - - if (mouseWithin({ acPosPix.x - 5, acPosPix.y - 5, acPosPix.x + 5, acPosPix.y + 5 })) { - dc.MoveTo(acPosPix.x, acPosPix.y - 8); - dc.LineTo(acPosPix.x - 6, acPosPix.y - 12); - dc.MoveTo(acPosPix.x, acPosPix.y - 8); - dc.LineTo(acPosPix.x + 6, acPosPix.y - 12); - - dc.MoveTo(acPosPix.x, acPosPix.y + 8); - dc.LineTo(acPosPix.x - 6, acPosPix.y + 12); - dc.MoveTo(acPosPix.x, acPosPix.y + 8); - dc.LineTo(acPosPix.x + 6, acPosPix.y + 12); - - dc.MoveTo(acPosPix.x - 8, acPosPix.y ); - dc.LineTo(acPosPix.x - 12, acPosPix.y -6); - dc.MoveTo(acPosPix.x - 8, acPosPix.y); - dc.LineTo(acPosPix.x - 12 , acPosPix.y + 6); - - dc.MoveTo(acPosPix.x + 8, acPosPix.y); - dc.LineTo(acPosPix.x + 12, acPosPix.y - 6); - dc.MoveTo(acPosPix.x + 8, acPosPix.y); - dc.LineTo(acPosPix.x + 12, acPosPix.y + 6); - } - - AddScreenObject(DRAWING_AC_SYMBOL, rt.GetCallsign(), { acPosPix.x - 5, acPosPix.y - 5, acPosPix.x + 5, acPosPix.y + 5 }, false, AcisCorrelated ? GetBottomLine(rt.GetCallsign()).c_str() : rt.GetSystemID()); - - dc.SelectObject(pqOrigPen); - } + // Drawing the symbols + Logger::info("Symbols loop"); + EuroScopePlugIn::CRadarTarget rt; + for (rt = GetPlugIn()->RadarTargetSelectFirst(); rt.IsValid(); + rt = GetPlugIn()->RadarTargetSelectNext(rt)) { + if (!rt.IsValid() || !rt.GetPosition().IsValid()) + continue; + + int reportedGs = rt.GetPosition().GetReportedGS(); + int radarRange = + CurrentConfig->getActiveProfile()["filters"]["radar_range_nm"].GetInt(); + int altitudeFilter = + CurrentConfig->getActiveProfile()["filters"]["hide_above_alt"].GetInt(); + int speedFilter = + CurrentConfig->getActiveProfile()["filters"]["hide_above_spd"].GetInt(); + bool isAcDisplayed = isVisible(rt); + + if (!isAcDisplayed) + continue; + + // Get aircraft type and stand for RIMCAS + CFlightPlan fp = GetPlugIn()->FlightPlanSelect(rt.GetCallsign()); + string acType = ""; + string acStand = ""; + if (fp.IsValid() && fp.GetFlightPlanData().IsReceived()) { + acType = fp.GetFlightPlanData().GetAircraftFPType(); + acStand = fp.GetControllerAssignedData().GetFlightStripAnnotation(3); + if (acStand.length() == 0) + acStand = ""; + } + + RimcasInstance->OnRefresh(rt, this, IsCorrelated(fp, rt), acType, acStand); + + // Track departures for the departure timer window + RimcasInstance->TrackDeparture(rt, fp, this, getActiveAirport()); + + CRadarTargetPositionData RtPos = rt.GetPosition(); + + POINT acPosPix = ConvertCoordFromPositionToPixel(RtPos.GetPosition()); + + if (rt.GetGS() > 5) { + POINT oldacPosPix; + CRadarTargetPositionData pAcPos = rt.GetPosition(); + + for (int i = 1; i <= 2; i++) { + oldacPosPix = ConvertCoordFromPositionToPixel(pAcPos.GetPosition()); + pAcPos = rt.GetPreviousPosition(pAcPos); + acPosPix = ConvertCoordFromPositionToPixel(pAcPos.GetPosition()); + + if (i == 1 && + !Patatoides[rt.GetCallsign()].History_one_points.empty() && + Afterglow && + CurrentConfig->getActiveProfile()["targets"]["show_primary_target"] + .GetBool()) { + SolidBrush H_Brush(ColorManager->get_corrected_color( + "afterglow", + CurrentConfig->getConfigColor( + CurrentConfig + ->getActiveProfile()["targets"]["history_one_color"]))); + + PointF lpPoints[100]; + for (unsigned int i1 = 0; + i1 < Patatoides[rt.GetCallsign()].History_one_points.size(); + i1++) { + CPosition pos; + pos.m_Latitude = + Patatoides[rt.GetCallsign()].History_one_points[i1].x; + pos.m_Longitude = + Patatoides[rt.GetCallsign()].History_one_points[i1].y; + + lpPoints[i1] = {REAL(ConvertCoordFromPositionToPixel(pos).x), + REAL(ConvertCoordFromPositionToPixel(pos).y)}; + } + graphics.FillPolygon( + &H_Brush, lpPoints, + Patatoides[rt.GetCallsign()].History_one_points.size()); + } + + if (i != 2) { + if (!Patatoides[rt.GetCallsign()].History_two_points.empty() && + Afterglow && + CurrentConfig + ->getActiveProfile()["targets"]["show_primary_target"] + .GetBool()) { + SolidBrush H_Brush(ColorManager->get_corrected_color( + "afterglow", + CurrentConfig->getConfigColor( + CurrentConfig + ->getActiveProfile()["targets"]["history_two_color"]))); + + PointF lpPoints[100]; + for (unsigned int i1 = 0; + i1 < Patatoides[rt.GetCallsign()].History_two_points.size(); + i1++) { + CPosition pos; + pos.m_Latitude = + Patatoides[rt.GetCallsign()].History_two_points[i1].x; + pos.m_Longitude = + Patatoides[rt.GetCallsign()].History_two_points[i1].y; + + lpPoints[i1] = {REAL(ConvertCoordFromPositionToPixel(pos).x), + REAL(ConvertCoordFromPositionToPixel(pos).y)}; + } + graphics.FillPolygon( + &H_Brush, lpPoints, + Patatoides[rt.GetCallsign()].History_two_points.size()); + } + } + + if (i == 2 && + !Patatoides[rt.GetCallsign()].History_three_points.empty() && + Afterglow && + CurrentConfig->getActiveProfile()["targets"]["show_primary_target"] + .GetBool()) { + SolidBrush H_Brush(ColorManager->get_corrected_color( + "afterglow", + CurrentConfig->getConfigColor( + CurrentConfig + ->getActiveProfile()["targets"]["history_three_color"]))); + + PointF lpPoints[100]; + for (unsigned int i1 = 0; + i1 < Patatoides[rt.GetCallsign()].History_three_points.size(); + i1++) { + CPosition pos; + pos.m_Latitude = + Patatoides[rt.GetCallsign()].History_three_points[i1].x; + pos.m_Longitude = + Patatoides[rt.GetCallsign()].History_three_points[i1].y; + + lpPoints[i1] = {REAL(ConvertCoordFromPositionToPixel(pos).x), + REAL(ConvertCoordFromPositionToPixel(pos).y)}; + } + graphics.FillPolygon( + &H_Brush, lpPoints, + Patatoides[rt.GetCallsign()].History_three_points.size()); + } + } + + int TrailNumber = Trail_Gnd; + if (reportedGs > 50) + TrailNumber = Trail_App; + + CRadarTargetPositionData previousPos = + rt.GetPreviousPosition(rt.GetPosition()); + for (int j = 1; j <= TrailNumber; j++) { + POINT pCoord = + ConvertCoordFromPositionToPixel(previousPos.GetPosition()); + + graphics.FillRectangle(&SolidBrush(ColorManager->get_corrected_color( + "symbol", Gdiplus::Color::White)), + pCoord.x - 1, pCoord.y - 1, 2, 2); + + previousPos = rt.GetPreviousPosition(previousPos); + } + } + + if (CurrentConfig->getActiveProfile()["targets"]["show_primary_target"] + .GetBool()) { + PointF lpPoints[100]; + for (unsigned int i = 0; i < Patatoides[rt.GetCallsign()].points.size(); + i++) { + CPosition pos; + pos.m_Latitude = Patatoides[rt.GetCallsign()].points[i].x; + pos.m_Longitude = Patatoides[rt.GetCallsign()].points[i].y; + + lpPoints[i] = {REAL(ConvertCoordFromPositionToPixel(pos).x), + REAL(ConvertCoordFromPositionToPixel(pos).y)}; + } + + if ((getActiveAirport() == "EGLL" || getActiveAirport() == "EGKK") && + CurrentConfig->isPositionInGeofenceArea(getActiveAirport(), + RtPos.GetPosition())) { + SolidBrush H_Brush(ColorManager->get_corrected_color( + "afterglow", Gdiplus::Color::White)); + graphics.FillPolygon(&H_Brush, lpPoints, + Patatoides[rt.GetCallsign()].points.size()); + + } else { + SolidBrush H_Brush(ColorManager->get_corrected_color( + "afterglow", + CurrentConfig->getConfigColor( + CurrentConfig->getActiveProfile()["targets"]["target_color"]))); + + graphics.FillPolygon(&H_Brush, lpPoints, + Patatoides[rt.GetCallsign()].points.size()); + } + } + acPosPix = ConvertCoordFromPositionToPixel(RtPos.GetPosition()); + + bool AcisCorrelated = + IsCorrelated(GetPlugIn()->FlightPlanSelect(rt.GetCallsign()), rt); + + if (!AcisCorrelated && reportedGs < 1 && !ReleaseInProgress && + !AcquireInProgress) + continue; + + CPen qTrailPen( + PS_SOLID, 1, + ColorManager->get_corrected_color("symbol", Gdiplus::Color::White) + .ToCOLORREF()); + CPen *pqOrigPen = dc.SelectObject(&qTrailPen); + + // Correlation cross + + if ((getActiveAirport() == "EGLL" || getActiveAirport() == "EGKK") && + CurrentConfig->isPositionInGeofenceArea(getActiveAirport(), + RtPos.GetPosition())) { + dc.MoveTo({acPosPix.x, acPosPix.y + 1}); + dc.LineTo({acPosPix.x + 1, acPosPix.y}); + dc.LineTo({acPosPix.x, acPosPix.y - 1}); + dc.LineTo({acPosPix.x - 1, acPosPix.y}); + dc.LineTo({acPosPix.x, acPosPix.y + 1}); + } else { + if (RtPos.GetTransponderC()) { + dc.MoveTo({acPosPix.x, acPosPix.y - 6}); + dc.LineTo({acPosPix.x - 6, acPosPix.y}); + dc.LineTo({acPosPix.x, acPosPix.y + 6}); + dc.LineTo({acPosPix.x + 6, acPosPix.y}); + dc.LineTo({acPosPix.x, acPosPix.y - 6}); + } else { + dc.MoveTo(acPosPix.x, acPosPix.y); + dc.LineTo(acPosPix.x - 4, acPosPix.y - 4); + dc.MoveTo(acPosPix.x, acPosPix.y); + dc.LineTo(acPosPix.x + 4, acPosPix.y - 4); + dc.MoveTo(acPosPix.x, acPosPix.y); + dc.LineTo(acPosPix.x - 4, acPosPix.y + 4); + dc.MoveTo(acPosPix.x, acPosPix.y); + dc.LineTo(acPosPix.x + 4, acPosPix.y + 4); + } + } + + // Predicted Track Line + // It starts 20 seconds away from the ac + if (reportedGs > 50 && PredictedLength > 0) { + double d = double(rt.GetPosition().GetReportedGS() * 0.514444) * 10; + CPosition AwayBase = BetterHarversine(rt.GetPosition().GetPosition(), + rt.GetTrackHeading(), d); + + d = double(rt.GetPosition().GetReportedGS() * 0.514444) * + (PredictedLength * 60) - + 10; + CPosition PredictedEnd = + BetterHarversine(AwayBase, rt.GetTrackHeading(), d); + + dc.MoveTo(ConvertCoordFromPositionToPixel(AwayBase)); + dc.LineTo(ConvertCoordFromPositionToPixel(PredictedEnd)); + } + + if (mouseWithin( + {acPosPix.x - 5, acPosPix.y - 5, acPosPix.x + 5, acPosPix.y + 5})) { + dc.MoveTo(acPosPix.x, acPosPix.y - 8); + dc.LineTo(acPosPix.x - 6, acPosPix.y - 12); + dc.MoveTo(acPosPix.x, acPosPix.y - 8); + dc.LineTo(acPosPix.x + 6, acPosPix.y - 12); + + dc.MoveTo(acPosPix.x, acPosPix.y + 8); + dc.LineTo(acPosPix.x - 6, acPosPix.y + 12); + dc.MoveTo(acPosPix.x, acPosPix.y + 8); + dc.LineTo(acPosPix.x + 6, acPosPix.y + 12); + + dc.MoveTo(acPosPix.x - 8, acPosPix.y); + dc.LineTo(acPosPix.x - 12, acPosPix.y - 6); + dc.MoveTo(acPosPix.x - 8, acPosPix.y); + dc.LineTo(acPosPix.x - 12, acPosPix.y + 6); + + dc.MoveTo(acPosPix.x + 8, acPosPix.y); + dc.LineTo(acPosPix.x + 12, acPosPix.y - 6); + dc.MoveTo(acPosPix.x + 8, acPosPix.y); + dc.LineTo(acPosPix.x + 12, acPosPix.y + 6); + } + + AddScreenObject( + DRAWING_AC_SYMBOL, rt.GetCallsign(), + {acPosPix.x - 5, acPosPix.y - 5, acPosPix.x + 5, acPosPix.y + 5}, false, + AcisCorrelated ? GetBottomLine(rt.GetCallsign()).c_str() + : rt.GetSystemID()); + + dc.SelectObject(pqOrigPen); + } #pragma endregion Drawing of the symbols - TimePopupData.clear(); - AcOnRunway.clear(); - ColorAC.clear(); - tagAreas.clear(); + TimePopupData.clear(); + AcOnRunway.clear(); + ColorAC.clear(); + tagAreas.clear(); + + RimcasInstance->OnRefreshEnd( + this, + CurrentConfig + ->getActiveProfile()["rimcas"]["rimcas_stage_two_speed_threshold"] + .GetInt()); - RimcasInstance->OnRefreshEnd(this, CurrentConfig->getActiveProfile()["rimcas"]["rimcas_stage_two_speed_threshold"].GetInt()); + // Update departure timer - remove expired/dismissed entries + RimcasInstance->UpdateDepartureTimer(departureDisplayDuration); + RimcasInstance->ClearDismissedDepartures(); - graphics.SetSmoothingMode(SmoothingModeDefault); + graphics.SetSmoothingMode(SmoothingModeDefault); #pragma region tags - // Drawing the Tags - Logger::info("Tags loop"); - for (rt = GetPlugIn()->RadarTargetSelectFirst(); - rt.IsValid(); - rt = GetPlugIn()->RadarTargetSelectNext(rt)) - { - if (!rt.IsValid()) - continue; - - CRadarTargetPositionData RtPos = rt.GetPosition(); - POINT acPosPix = ConvertCoordFromPositionToPixel(RtPos.GetPosition()); - CFlightPlan fp = GetPlugIn()->FlightPlanSelect(rt.GetCallsign()); - int reportedGs = RtPos.GetReportedGS(); - - // Filtering the targets - - int radarRange = CurrentConfig->getActiveProfile()["filters"]["radar_range_nm"].GetInt(); - int altitudeFilter = CurrentConfig->getActiveProfile()["filters"]["hide_above_alt"].GetInt(); - int speedFilter = CurrentConfig->getActiveProfile()["filters"]["hide_above_spd"].GetInt(); - bool isAcDisplayed = isVisible(rt); - - bool AcisCorrelated = IsCorrelated(fp, rt); - - if (!AcisCorrelated && reportedGs < 3) - isAcDisplayed = false; - - if (std::find(ReleasedTracks.begin(), ReleasedTracks.end(), rt.GetSystemID()) != ReleasedTracks.end()) - isAcDisplayed = false; - - if (CurrentConfig->isPositionInGeofenceArea(getActiveAirport(), RtPos.GetPosition())) - isAcDisplayed = false; - - // - - if (!isAcDisplayed) - continue; - - // Getting the tag center/offset - - POINT TagCenter; - map::iterator it = TagsOffsets.find(rt.GetCallsign()); - if (it != TagsOffsets.end()) { - TagCenter = { acPosPix.x + it->second.x, acPosPix.y + it->second.y }; - } - else { - // Use angle: - - if (TagAngles.find(rt.GetCallsign()) == TagAngles.end()) - TagAngles[rt.GetCallsign()] = 270.0f; - - int lenght = LeaderLineDefaultlenght; - if (TagLeaderLineLength.find(rt.GetCallsign()) != TagLeaderLineLength.end()) - lenght = TagLeaderLineLength[rt.GetCallsign()]; - - TagCenter.x = long(acPosPix.x + float(lenght * cos(DegToRad(TagAngles[rt.GetCallsign()])))); - TagCenter.y = long(acPosPix.y + float(lenght * sin(DegToRad(TagAngles[rt.GetCallsign()])))); - } - - TagTypes TagType = TagTypes::Departure; - TagTypes ColorTagType = TagTypes::Departure; - - if (fp.IsValid() && strcmp(fp.GetFlightPlanData().GetDestination(), getActiveAirport().c_str()) == 0) { - // Circuit aircraft are treated as departures; not arrivals - if (strcmp(fp.GetFlightPlanData().GetOrigin(), getActiveAirport().c_str()) != 0) { - TagType = TagTypes::Arrival; - ColorTagType = TagTypes::Arrival; - } - } - - if (reportedGs > 50) { - TagType = TagTypes::Airborne; - - // Is "use_departure_arrival_coloring" enabled? if not, then use the airborne colors - bool useDepArrColors = CurrentConfig->getActiveProfile()["labels"]["airborne"]["use_departure_arrival_coloring"].GetBool(); - if (!useDepArrColors) { - ColorTagType = TagTypes::Airborne; - } - } - - if (!AcisCorrelated && reportedGs >= 3) - { - TagType = TagTypes::Uncorrelated; - ColorTagType = TagTypes::Uncorrelated; - } - - map TagReplacingMap = GenerateTagData(rt, fp, IsCorrelated(fp, rt), CurrentConfig->getActiveProfile()["filters"]["pro_mode"]["enable"].GetBool(), GetPlugIn()->GetTransitionAltitude(), CurrentConfig->getActiveProfile()["labels"]["use_aspeed_for_gate"].GetBool(), getActiveAirport(), showAircraftType, showSID, showWakeTurb); - - // ----- Generating the clickable map ----- - map TagClickableMap; - TagClickableMap[TagReplacingMap["callsign"]] = TAG_CITEM_CALLSIGN; - TagClickableMap[TagReplacingMap["actype"]] = TAG_CITEM_FPBOX; - TagClickableMap[TagReplacingMap["sctype"]] = TAG_CITEM_FPBOX; - TagClickableMap[TagReplacingMap["sqerror"]] = TAG_CITEM_FPBOX; - TagClickableMap[TagReplacingMap["deprwy"]] = TAG_CITEM_RWY; - TagClickableMap[TagReplacingMap["seprwy"]] = TAG_CITEM_RWY; - TagClickableMap[TagReplacingMap["arvrwy"]] = TAG_CITEM_RWY; - TagClickableMap[TagReplacingMap["srvrwy"]] = TAG_CITEM_RWY; - TagClickableMap[TagReplacingMap["gate"]] = TAG_CITEM_GATE; - TagClickableMap[TagReplacingMap["sate"]] = TAG_CITEM_GATE; - TagClickableMap[TagReplacingMap["flightlevel"]] = TAG_CITEM_NO; - TagClickableMap[TagReplacingMap["gs"]] = TAG_CITEM_NO; - TagClickableMap[TagReplacingMap["tendency"]] = TAG_CITEM_NO; - TagClickableMap[TagReplacingMap["wake"]] = TAG_CITEM_FPBOX; - TagClickableMap[TagReplacingMap["tssr"]] = TAG_CITEM_NO; - TagClickableMap[TagReplacingMap["asid"]] = TagClickableMap[TagReplacingMap["ssid"]] = TAG_CITEM_SID; - TagClickableMap[TagReplacingMap["origin"]] = TAG_CITEM_FPBOX; - TagClickableMap[TagReplacingMap["dest"]] = TAG_CITEM_FPBOX; - TagClickableMap[TagReplacingMap["systemid"]] = TAG_CITEM_NO; - TagClickableMap[TagReplacingMap["groundstatus"]] = TAG_CITEM_GROUNDSTATUS; - TagClickableMap[TagReplacingMap["uk_stand"]] = TAG_CITEM_UKSTAND; - - // - // ----- Now the hard part, drawing (using gdi+) ------- - // - - // First we need to figure out the tag size - - int TagWidth = 0, TagHeight = 0; - RectF mesureRect; - graphics.MeasureString(L" ", wcslen(L" "), customFonts[currentFontSize], PointF(0, 0), &Gdiplus::StringFormat(), &mesureRect); - int blankWidth = (int)mesureRect.GetRight(); - - mesureRect = RectF(0, 0, 0, 0); - graphics.MeasureString(L"AZERTYUIOPQSDFGHJKLMWXCVBN", wcslen(L"AZERTYUIOPQSDFGHJKLMWXCVBN"), - customFonts[currentFontSize], PointF(0, 0), &Gdiplus::StringFormat(), &mesureRect); - int oneLineHeight = (int)mesureRect.GetBottom(); - - const Value& LabelsSettings = CurrentConfig->getActiveProfile()["labels"]; - const Value& LabelLines = LabelsSettings[Utils::getEnumString(TagType).c_str()]["definition"]; - vector> ReplacedLabelLines; - vector LineWidths; // Store width of each line - - if (!LabelLines.IsArray()) - return; - - for (unsigned int i = 0; i < LabelLines.Size(); i++) - { - - const Value& line = LabelLines[i]; - vector lineStringArray; - - int TempTagWidth = 0; - bool lineHasContent = false; - - for(unsigned int j = 0; j < line.Size(); j++) - { - mesureRect = RectF(0, 0, 0, 0); - string element = line[j].GetString(); - - for (auto& kv : TagReplacingMap) - replaceAll(element, kv.first, kv.second); - - lineStringArray.push_back(element); - - // Check if this element has actual content - if (!element.empty()) { - lineHasContent = true; - } - - wstring wstr = wstring(element.begin(), element.end()); - graphics.MeasureString(wstr.c_str(), wcslen(wstr.c_str()), - customFonts[currentFontSize], PointF(0, 0), &Gdiplus::StringFormat(), &mesureRect); - - TempTagWidth += (int) mesureRect.GetRight(); - - // Only add blank space if this element is not empty AND there's a next element that's not empty - if (!element.empty() && j != line.Size() - 1) { - // Check if any subsequent element has content - bool hasNextContent = false; - for (unsigned int k = j + 1; k < line.Size(); k++) { - string nextElement = line[k].GetString(); - for (auto& kv : TagReplacingMap) - replaceAll(nextElement, kv.first, kv.second); - if (!nextElement.empty()) { - hasNextContent = true; - break; - } - } - if (hasNextContent) { - TempTagWidth += (int) blankWidth; - } - } - } - - // Only add height for lines that have actual content - if (lineHasContent) { - TagHeight += oneLineHeight; - } - - LineWidths.push_back(TempTagWidth); // Store actual line width - TagWidth = max(TagWidth, TempTagWidth); - - ReplacedLabelLines.push_back(lineStringArray); - } - - Color definedBackgroundColor = CurrentConfig->getConfigColor(LabelsSettings[Utils::getEnumString(ColorTagType).c_str()]["background_color"]); - - if (ColorTagType == TagTypes::Departure) { - if (!TagReplacingMap["asid"].empty() && CurrentConfig->isSidColorAvail(TagReplacingMap["asid"], getActiveAirport())) { - definedBackgroundColor = CurrentConfig->getSidColor(TagReplacingMap["asid"], getActiveAirport()); - } - if (fp.GetFlightPlanData().GetPlanType()[0] == 'I' && TagReplacingMap["asid"].empty() && LabelsSettings[Utils::getEnumString(ColorTagType).c_str()].HasMember("nosid_color")) { - definedBackgroundColor = CurrentConfig->getConfigColor(LabelsSettings[Utils::getEnumString(ColorTagType).c_str()]["nosid_color"]); - } - } - if (TagReplacingMap["actype"] == "NoFPL" && LabelsSettings[Utils::getEnumString(ColorTagType).c_str()].HasMember("nofpl_color")) { - definedBackgroundColor = CurrentConfig->getConfigColor(LabelsSettings[Utils::getEnumString(ColorTagType).c_str()]["nofpl_color"]); - } - - Color TagBackgroundColor = RimcasInstance->GetAircraftColor(rt.GetCallsign(), - definedBackgroundColor, - CurrentConfig->getConfigColor(LabelsSettings[Utils::getEnumString(ColorTagType).c_str()]["background_color_on_runway"]), - CurrentConfig->getConfigColor(CurrentConfig->getActiveProfile()["rimcas"]["background_color_stage_one"]), - CurrentConfig->getConfigColor(CurrentConfig->getActiveProfile()["rimcas"]["background_color_stage_two"])); - - // We need to figure out if the tag color changes according to RIMCAS alerts, or not - bool rimcasLabelOnly = CurrentConfig->getActiveProfile()["rimcas"]["rimcas_label_only"].GetBool(); - - if (rimcasLabelOnly) - TagBackgroundColor = RimcasInstance->GetAircraftColor(rt.GetCallsign(), - definedBackgroundColor, - CurrentConfig->getConfigColor(LabelsSettings[Utils::getEnumString(ColorTagType).c_str()]["background_color_on_runway"])); - - TagBackgroundColor = ColorManager->get_corrected_color("label", TagBackgroundColor); - - // Drawing the tag background - - CRect TagBackgroundRect(TagCenter.x - (TagWidth / 2), TagCenter.y - (TagHeight / 2), TagCenter.x + (TagWidth / 2), TagCenter.y + (TagHeight / 2)); - SolidBrush TagBackgroundBrush(TagBackgroundColor); - graphics.FillRectangle(&TagBackgroundBrush, CopyRect(TagBackgroundRect)); - if (mouseWithin(TagBackgroundRect) || IsTagBeingDragged(rt.GetCallsign())) - { - Pen pw(ColorManager->get_corrected_color("label", Color::White)); - graphics.DrawRectangle(&pw, CopyRect(TagBackgroundRect)); - } - - // Drawing the tag text - - SolidBrush FontColor(ColorManager->get_corrected_color("label", - CurrentConfig->getConfigColor(LabelsSettings[Utils::getEnumString(ColorTagType).c_str()]["text_color"]))); - SolidBrush SquawkErrorColor(ColorManager->get_corrected_color("label", - CurrentConfig->getConfigColor(LabelsSettings["squawk_error_color"]))); - SolidBrush RimcasTextColor(CurrentConfig->getConfigColor(CurrentConfig->getActiveProfile()["rimcas"]["alert_text_color"])); - SolidBrush GroundPushColor(ColorManager->get_corrected_color("label", - CurrentConfig->getConfigColor(LabelsSettings["groundstatus_colors"]["push"]))); - SolidBrush GroundTaxiColor(ColorManager->get_corrected_color("label", - CurrentConfig->getConfigColor(LabelsSettings["groundstatus_colors"]["taxi"]))); - SolidBrush GroundDepaColor(ColorManager->get_corrected_color("label", - CurrentConfig->getConfigColor(LabelsSettings["groundstatus_colors"]["depa"]))); - - - // Drawing the leader line - RECT TagBackRectData = TagBackgroundRect; - POINT toDraw1, toDraw2; - if (LiangBarsky(TagBackRectData, acPosPix, TagBackgroundRect.CenterPoint(), toDraw1, toDraw2)) - graphics.DrawLine(&Pen(ColorManager->get_corrected_color("symbol", Color::White)), PointF(Gdiplus::REAL(acPosPix.x), Gdiplus::REAL(acPosPix.y)), PointF(Gdiplus::REAL(toDraw1.x), Gdiplus::REAL(toDraw1.y))); - - // If we use a RIMCAS label only, we display it, and adapt the rectangle - CRect oldCrectSave = TagBackgroundRect; - - if (rimcasLabelOnly) { - Color RimcasLabelColor = RimcasInstance->GetAircraftColor(rt.GetCallsign(), Color::AliceBlue, Color::AliceBlue, - CurrentConfig->getConfigColor(CurrentConfig->getActiveProfile()["rimcas"]["background_color_stage_one"]), - CurrentConfig->getConfigColor(CurrentConfig->getActiveProfile()["rimcas"]["background_color_stage_two"])); - - if (RimcasLabelColor.ToCOLORREF() != Color(Color::AliceBlue).ToCOLORREF()) { - RimcasLabelColor = ColorManager->get_corrected_color("label", RimcasLabelColor); - int rimcas_height = 0; - - wstring wrimcas_height = wstring(L"ALERT"); - - RectF RectRimcas_height; - - graphics.MeasureString(wrimcas_height.c_str(), wcslen(wrimcas_height.c_str()), customFonts[currentFontSize], PointF(0, 0), &Gdiplus::StringFormat(), &RectRimcas_height); - rimcas_height = int(RectRimcas_height.GetBottom()); - - // Drawing the rectangle - - CRect RimcasLabelRect(TagBackgroundRect.left, TagBackgroundRect.top - rimcas_height, TagBackgroundRect.right, TagBackgroundRect.top); - graphics.FillRectangle(&SolidBrush(RimcasLabelColor), CopyRect(RimcasLabelRect)); - TagBackgroundRect.top -= rimcas_height; - - // Drawing the text - - wstring rimcasw = wstring(L"ALERT"); - StringFormat stformat = new StringFormat(); - stformat.SetAlignment(StringAlignment::StringAlignmentCenter); - graphics.DrawString(rimcasw.c_str(), wcslen(rimcasw.c_str()), customFonts[currentFontSize], PointF(Gdiplus::REAL((TagBackgroundRect.left + TagBackgroundRect.right) / 2), Gdiplus::REAL(TagBackgroundRect.top)), &stformat, &RimcasTextColor); - } - } - - // Adding the tag screen object - tagAreas[rt.GetCallsign()] = TagBackgroundRect; - AddScreenObject(DRAWING_TAG, rt.GetCallsign(), TagBackgroundRect, true, GetBottomLine(rt.GetCallsign()).c_str()); - - TagBackgroundRect = oldCrectSave; - - // Clickable zones - int heightOffset = 0; - int lineIndex = 0; - for (auto&& line : ReplacedLabelLines) - { - // Check if line has any content - bool lineHasContent = false; - for (auto&& element : line) { - if (!element.empty()) { - lineHasContent = true; - break; - } - } - - // Skip empty lines - if (!lineHasContent) { - lineIndex++; - continue; - } - - // Left-align all text - int widthOffset = 0; - int elementIndex = 0; - - for (auto&& element : line) - { - SolidBrush* color = &FontColor; - if (TagReplacingMap["sqerror"].size() > 0 && strcmp(element.c_str(), TagReplacingMap["sqerror"].c_str()) == 0) - color = &SquawkErrorColor; - - if (RimcasInstance->getAlert(rt.GetCallsign()) != CRimcas::NoAlert) - color = &RimcasTextColor; - - // Ground tag colors - if (strcmp(element.c_str(), "PUSH") == 0) - color = &GroundPushColor; - if (strcmp(element.c_str(), "TAXI") == 0) - color = &GroundTaxiColor; - if (strcmp(element.c_str(), "DEPA") == 0) - color = &GroundDepaColor; - - RectF mRect(0, 0, 0, 0); - - wstring welement = wstring(element.begin(), element.end()); - - graphics.DrawString(welement.c_str(), wcslen(welement.c_str()), customFonts[currentFontSize], - PointF(Gdiplus::REAL(TagBackgroundRect.left + widthOffset), Gdiplus::REAL(TagBackgroundRect.top + heightOffset)), - &Gdiplus::StringFormat(), color); - - - graphics.MeasureString(welement.c_str(), wcslen(welement.c_str()), customFonts[currentFontSize], - PointF(0, 0), &Gdiplus::StringFormat(), &mRect); - - CRect ItemRect(TagBackgroundRect.left + widthOffset, TagBackgroundRect.top + heightOffset, - TagBackgroundRect.left + widthOffset + (int)mRect.GetRight(), TagBackgroundRect.top + heightOffset + (int)mRect.GetBottom()); - - AddScreenObject(TagClickableMap[element], rt.GetCallsign(), ItemRect, true, GetBottomLine(rt.GetCallsign()).c_str()); - - widthOffset += (int)mRect.GetRight(); - - // Only add blank space if this element is not empty AND there's a next element with content - if (!element.empty() && elementIndex < line.size() - 1) { - bool hasNextContent = false; - for (size_t k = elementIndex + 1; k < line.size(); k++) { - if (!line[k].empty()) { - hasNextContent = true; - break; - } - } - if (hasNextContent) { - widthOffset += blankWidth; - } - } - elementIndex++; - } - - heightOffset += oneLineHeight; - lineIndex++; - } - - - } + // Drawing the Tags + Logger::info("Tags loop"); + for (rt = GetPlugIn()->RadarTargetSelectFirst(); rt.IsValid(); + rt = GetPlugIn()->RadarTargetSelectNext(rt)) { + if (!rt.IsValid()) + continue; + + CRadarTargetPositionData RtPos = rt.GetPosition(); + POINT acPosPix = ConvertCoordFromPositionToPixel(RtPos.GetPosition()); + CFlightPlan fp = GetPlugIn()->FlightPlanSelect(rt.GetCallsign()); + int reportedGs = RtPos.GetReportedGS(); + + // Filtering the targets + + int radarRange = + CurrentConfig->getActiveProfile()["filters"]["radar_range_nm"].GetInt(); + int altitudeFilter = + CurrentConfig->getActiveProfile()["filters"]["hide_above_alt"].GetInt(); + int speedFilter = + CurrentConfig->getActiveProfile()["filters"]["hide_above_spd"].GetInt(); + bool isAcDisplayed = isVisible(rt); + + bool AcisCorrelated = IsCorrelated(fp, rt); + + if (!AcisCorrelated && reportedGs < 3) + isAcDisplayed = false; + + if (std::find(ReleasedTracks.begin(), ReleasedTracks.end(), + rt.GetSystemID()) != ReleasedTracks.end()) + isAcDisplayed = false; + + if (CurrentConfig->isPositionInGeofenceArea(getActiveAirport(), + RtPos.GetPosition())) + isAcDisplayed = false; + + // + + if (!isAcDisplayed) + continue; + + // Getting the tag center/offset + + POINT TagCenter; + map::iterator it = TagsOffsets.find(rt.GetCallsign()); + if (it != TagsOffsets.end()) { + TagCenter = {acPosPix.x + it->second.x, acPosPix.y + it->second.y}; + } else { + // Use angle: + + if (TagAngles.find(rt.GetCallsign()) == TagAngles.end()) + TagAngles[rt.GetCallsign()] = 270.0f; + + int lenght = LeaderLineDefaultlenght; + if (TagLeaderLineLength.find(rt.GetCallsign()) != + TagLeaderLineLength.end()) + lenght = TagLeaderLineLength[rt.GetCallsign()]; + + TagCenter.x = + long(acPosPix.x + + float(lenght * cos(DegToRad(TagAngles[rt.GetCallsign()])))); + TagCenter.y = + long(acPosPix.y + + float(lenght * sin(DegToRad(TagAngles[rt.GetCallsign()])))); + } + + TagTypes TagType = TagTypes::Departure; + TagTypes ColorTagType = TagTypes::Departure; + + if (fp.IsValid() && strcmp(fp.GetFlightPlanData().GetDestination(), + getActiveAirport().c_str()) == 0) { + // Circuit aircraft are treated as departures; not arrivals + if (strcmp(fp.GetFlightPlanData().GetOrigin(), + getActiveAirport().c_str()) != 0) { + TagType = TagTypes::Arrival; + ColorTagType = TagTypes::Arrival; + } + } + + if (reportedGs > 50) { + TagType = TagTypes::Airborne; + + // Is "use_departure_arrival_coloring" enabled? if not, then use the + // airborne colors + bool useDepArrColors = + CurrentConfig + ->getActiveProfile()["labels"]["airborne"] + ["use_departure_arrival_coloring"] + .GetBool(); + if (!useDepArrColors) { + ColorTagType = TagTypes::Airborne; + } + } + + if (!AcisCorrelated && reportedGs >= 3) { + TagType = TagTypes::Uncorrelated; + ColorTagType = TagTypes::Uncorrelated; + } + + map TagReplacingMap = GenerateTagData( + rt, fp, IsCorrelated(fp, rt), + CurrentConfig->getActiveProfile()["filters"]["pro_mode"]["enable"] + .GetBool(), + GetPlugIn()->GetTransitionAltitude(), + CurrentConfig->getActiveProfile()["labels"]["use_aspeed_for_gate"] + .GetBool(), + getActiveAirport(), showAircraftType, showSID, showWakeTurb); + + // ----- Generating the clickable map ----- + map TagClickableMap; + TagClickableMap[TagReplacingMap["callsign"]] = TAG_CITEM_CALLSIGN; + TagClickableMap[TagReplacingMap["actype"]] = TAG_CITEM_FPBOX; + TagClickableMap[TagReplacingMap["sctype"]] = TAG_CITEM_FPBOX; + TagClickableMap[TagReplacingMap["sqerror"]] = TAG_CITEM_FPBOX; + TagClickableMap[TagReplacingMap["deprwy"]] = TAG_CITEM_RWY; + TagClickableMap[TagReplacingMap["seprwy"]] = TAG_CITEM_RWY; + TagClickableMap[TagReplacingMap["arvrwy"]] = TAG_CITEM_RWY; + TagClickableMap[TagReplacingMap["srvrwy"]] = TAG_CITEM_RWY; + TagClickableMap[TagReplacingMap["gate"]] = TAG_CITEM_GATE; + TagClickableMap[TagReplacingMap["sate"]] = TAG_CITEM_GATE; + TagClickableMap[TagReplacingMap["flightlevel"]] = TAG_CITEM_NO; + TagClickableMap[TagReplacingMap["gs"]] = TAG_CITEM_NO; + TagClickableMap[TagReplacingMap["tendency"]] = TAG_CITEM_NO; + TagClickableMap[TagReplacingMap["wake"]] = TAG_CITEM_FPBOX; + TagClickableMap[TagReplacingMap["tssr"]] = TAG_CITEM_NO; + TagClickableMap[TagReplacingMap["asid"]] = + TagClickableMap[TagReplacingMap["ssid"]] = TAG_CITEM_SID; + TagClickableMap[TagReplacingMap["origin"]] = TAG_CITEM_FPBOX; + TagClickableMap[TagReplacingMap["dest"]] = TAG_CITEM_FPBOX; + TagClickableMap[TagReplacingMap["systemid"]] = TAG_CITEM_NO; + TagClickableMap[TagReplacingMap["groundstatus"]] = TAG_CITEM_GROUNDSTATUS; + TagClickableMap[TagReplacingMap["uk_stand"]] = TAG_CITEM_UKSTAND; + + // + // ----- Now the hard part, drawing (using gdi+) ------- + // + + // First we need to figure out the tag size + + int TagWidth = 0, TagHeight = 0; + RectF mesureRect; + graphics.MeasureString(L" ", wcslen(L" "), customFonts[currentFontSize], + PointF(0, 0), &Gdiplus::StringFormat(), &mesureRect); + int blankWidth = (int)mesureRect.GetRight(); + + mesureRect = RectF(0, 0, 0, 0); + graphics.MeasureString(L"AZERTYUIOPQSDFGHJKLMWXCVBN", + wcslen(L"AZERTYUIOPQSDFGHJKLMWXCVBN"), + customFonts[currentFontSize], PointF(0, 0), + &Gdiplus::StringFormat(), &mesureRect); + int oneLineHeight = (int)mesureRect.GetBottom(); + + const Value &LabelsSettings = CurrentConfig->getActiveProfile()["labels"]; + const Value &LabelLines = + LabelsSettings[Utils::getEnumString(TagType).c_str()]["definition"]; + vector> ReplacedLabelLines; + vector LineWidths; // Store width of each line + + if (!LabelLines.IsArray()) + return; + + for (unsigned int i = 0; i < LabelLines.Size(); i++) { + + const Value &line = LabelLines[i]; + vector lineStringArray; + + int TempTagWidth = 0; + bool lineHasContent = false; + + for (unsigned int j = 0; j < line.Size(); j++) { + mesureRect = RectF(0, 0, 0, 0); + string element = line[j].GetString(); + + for (auto &kv : TagReplacingMap) + replaceAll(element, kv.first, kv.second); + + lineStringArray.push_back(element); + + // Check if this element has actual content + if (!element.empty()) { + lineHasContent = true; + } + + wstring wstr = wstring(element.begin(), element.end()); + graphics.MeasureString(wstr.c_str(), wcslen(wstr.c_str()), + customFonts[currentFontSize], PointF(0, 0), + &Gdiplus::StringFormat(), &mesureRect); + + TempTagWidth += (int)mesureRect.GetRight(); + + // Only add blank space if this element is not empty AND there's a next + // element that's not empty + if (!element.empty() && j != line.Size() - 1) { + // Check if any subsequent element has content + bool hasNextContent = false; + for (unsigned int k = j + 1; k < line.Size(); k++) { + string nextElement = line[k].GetString(); + for (auto &kv : TagReplacingMap) + replaceAll(nextElement, kv.first, kv.second); + if (!nextElement.empty()) { + hasNextContent = true; + break; + } + } + if (hasNextContent) { + TempTagWidth += (int)blankWidth; + } + } + } + + // Only add height for lines that have actual content + if (lineHasContent) { + TagHeight += oneLineHeight; + } + + LineWidths.push_back(TempTagWidth); // Store actual line width + TagWidth = max(TagWidth, TempTagWidth); + + ReplacedLabelLines.push_back(lineStringArray); + } + + Color definedBackgroundColor = CurrentConfig->getConfigColor( + LabelsSettings[Utils::getEnumString(ColorTagType).c_str()] + ["background_color"]); + + if (ColorTagType == TagTypes::Departure) { + if (!TagReplacingMap["asid"].empty() && + CurrentConfig->isSidColorAvail(TagReplacingMap["asid"], + getActiveAirport())) { + definedBackgroundColor = CurrentConfig->getSidColor( + TagReplacingMap["asid"], getActiveAirport()); + } + if (fp.GetFlightPlanData().GetPlanType()[0] == 'I' && + TagReplacingMap["asid"].empty() && + LabelsSettings[Utils::getEnumString(ColorTagType).c_str()].HasMember( + "nosid_color")) { + definedBackgroundColor = CurrentConfig->getConfigColor( + LabelsSettings[Utils::getEnumString(ColorTagType).c_str()] + ["nosid_color"]); + } + } + if (TagReplacingMap["actype"] == "NoFPL" && + LabelsSettings[Utils::getEnumString(ColorTagType).c_str()].HasMember( + "nofpl_color")) { + definedBackgroundColor = CurrentConfig->getConfigColor( + LabelsSettings[Utils::getEnumString(ColorTagType).c_str()] + ["nofpl_color"]); + } + + Color TagBackgroundColor = RimcasInstance->GetAircraftColor( + rt.GetCallsign(), definedBackgroundColor, + CurrentConfig->getConfigColor( + LabelsSettings[Utils::getEnumString(ColorTagType).c_str()] + ["background_color_on_runway"]), + CurrentConfig->getConfigColor( + CurrentConfig + ->getActiveProfile()["rimcas"]["background_color_stage_one"]), + CurrentConfig->getConfigColor( + CurrentConfig + ->getActiveProfile()["rimcas"]["background_color_stage_two"])); + + // We need to figure out if the tag color changes according to RIMCAS + // alerts, or not + bool rimcasLabelOnly = + CurrentConfig->getActiveProfile()["rimcas"]["rimcas_label_only"] + .GetBool(); + + if (rimcasLabelOnly) + TagBackgroundColor = RimcasInstance->GetAircraftColor( + rt.GetCallsign(), definedBackgroundColor, + CurrentConfig->getConfigColor( + LabelsSettings[Utils::getEnumString(ColorTagType).c_str()] + ["background_color_on_runway"])); + + TagBackgroundColor = + ColorManager->get_corrected_color("label", TagBackgroundColor); + + // Drawing the tag background + + CRect TagBackgroundRect( + TagCenter.x - (TagWidth / 2), TagCenter.y - (TagHeight / 2), + TagCenter.x + (TagWidth / 2), TagCenter.y + (TagHeight / 2)); + SolidBrush TagBackgroundBrush(TagBackgroundColor); + graphics.FillRectangle(&TagBackgroundBrush, CopyRect(TagBackgroundRect)); + if (mouseWithin(TagBackgroundRect) || IsTagBeingDragged(rt.GetCallsign())) { + Pen pw(ColorManager->get_corrected_color("label", Color::White)); + graphics.DrawRectangle(&pw, CopyRect(TagBackgroundRect)); + } + + // Drawing the tag text + + SolidBrush FontColor(ColorManager->get_corrected_color( + "label", CurrentConfig->getConfigColor( + LabelsSettings[Utils::getEnumString(ColorTagType).c_str()] + ["text_color"]))); + SolidBrush SquawkErrorColor(ColorManager->get_corrected_color( + "label", + CurrentConfig->getConfigColor(LabelsSettings["squawk_error_color"]))); + SolidBrush RimcasTextColor(CurrentConfig->getConfigColor( + CurrentConfig->getActiveProfile()["rimcas"]["alert_text_color"])); + SolidBrush GroundPushColor(ColorManager->get_corrected_color( + "label", CurrentConfig->getConfigColor( + LabelsSettings["groundstatus_colors"]["push"]))); + SolidBrush GroundTaxiColor(ColorManager->get_corrected_color( + "label", CurrentConfig->getConfigColor( + LabelsSettings["groundstatus_colors"]["taxi"]))); + SolidBrush GroundDepaColor(ColorManager->get_corrected_color( + "label", CurrentConfig->getConfigColor( + LabelsSettings["groundstatus_colors"]["depa"]))); + + // Drawing the leader line + RECT TagBackRectData = TagBackgroundRect; + POINT toDraw1, toDraw2; + if (LiangBarsky(TagBackRectData, acPosPix, TagBackgroundRect.CenterPoint(), + toDraw1, toDraw2)) + graphics.DrawLine( + &Pen(ColorManager->get_corrected_color("symbol", Color::White)), + PointF(Gdiplus::REAL(acPosPix.x), Gdiplus::REAL(acPosPix.y)), + PointF(Gdiplus::REAL(toDraw1.x), Gdiplus::REAL(toDraw1.y))); + + // If we use a RIMCAS label only, we display it, and adapt the rectangle + CRect oldCrectSave = TagBackgroundRect; + + if (rimcasLabelOnly) { + Color RimcasLabelColor = RimcasInstance->GetAircraftColor( + rt.GetCallsign(), Color::AliceBlue, Color::AliceBlue, + CurrentConfig->getConfigColor( + CurrentConfig + ->getActiveProfile()["rimcas"]["background_color_stage_one"]), + CurrentConfig->getConfigColor( + CurrentConfig->getActiveProfile()["rimcas"] + ["background_color_stage_two"])); + + if (RimcasLabelColor.ToCOLORREF() != + Color(Color::AliceBlue).ToCOLORREF()) { + RimcasLabelColor = + ColorManager->get_corrected_color("label", RimcasLabelColor); + int rimcas_height = 0; + + wstring wrimcas_height = wstring(L"ALERT"); + + RectF RectRimcas_height; + + graphics.MeasureString(wrimcas_height.c_str(), + wcslen(wrimcas_height.c_str()), + customFonts[currentFontSize], PointF(0, 0), + &Gdiplus::StringFormat(), &RectRimcas_height); + rimcas_height = int(RectRimcas_height.GetBottom()); + + // Drawing the rectangle + + CRect RimcasLabelRect(TagBackgroundRect.left, + TagBackgroundRect.top - rimcas_height, + TagBackgroundRect.right, TagBackgroundRect.top); + graphics.FillRectangle(&SolidBrush(RimcasLabelColor), + CopyRect(RimcasLabelRect)); + TagBackgroundRect.top -= rimcas_height; + + // Drawing the text + + wstring rimcasw = wstring(L"ALERT"); + StringFormat stformat = new StringFormat(); + stformat.SetAlignment(StringAlignment::StringAlignmentCenter); + graphics.DrawString( + rimcasw.c_str(), wcslen(rimcasw.c_str()), + customFonts[currentFontSize], + PointF(Gdiplus::REAL( + (TagBackgroundRect.left + TagBackgroundRect.right) / 2), + Gdiplus::REAL(TagBackgroundRect.top)), + &stformat, &RimcasTextColor); + } + } + + // Adding the tag screen object + tagAreas[rt.GetCallsign()] = TagBackgroundRect; + AddScreenObject(DRAWING_TAG, rt.GetCallsign(), TagBackgroundRect, true, + GetBottomLine(rt.GetCallsign()).c_str()); + + TagBackgroundRect = oldCrectSave; + + // Clickable zones + int heightOffset = 0; + int lineIndex = 0; + for (auto &&line : ReplacedLabelLines) { + // Check if line has any content + bool lineHasContent = false; + for (auto &&element : line) { + if (!element.empty()) { + lineHasContent = true; + break; + } + } + + // Skip empty lines + if (!lineHasContent) { + lineIndex++; + continue; + } + + // Left-align all text + int widthOffset = 0; + int elementIndex = 0; + + for (auto &&element : line) { + SolidBrush *color = &FontColor; + if (TagReplacingMap["sqerror"].size() > 0 && + strcmp(element.c_str(), TagReplacingMap["sqerror"].c_str()) == 0) + color = &SquawkErrorColor; + + if (RimcasInstance->getAlert(rt.GetCallsign()) != CRimcas::NoAlert) + color = &RimcasTextColor; + + // Ground tag colors + if (strcmp(element.c_str(), "PUSH") == 0) + color = &GroundPushColor; + if (strcmp(element.c_str(), "TAXI") == 0) + color = &GroundTaxiColor; + if (strcmp(element.c_str(), "DEPA") == 0) + color = &GroundDepaColor; + + RectF mRect(0, 0, 0, 0); + + wstring welement = wstring(element.begin(), element.end()); + + graphics.DrawString( + welement.c_str(), wcslen(welement.c_str()), + customFonts[currentFontSize], + PointF(Gdiplus::REAL(TagBackgroundRect.left + widthOffset), + Gdiplus::REAL(TagBackgroundRect.top + heightOffset)), + &Gdiplus::StringFormat(), color); + + graphics.MeasureString(welement.c_str(), wcslen(welement.c_str()), + customFonts[currentFontSize], PointF(0, 0), + &Gdiplus::StringFormat(), &mRect); + + CRect ItemRect( + TagBackgroundRect.left + widthOffset, + TagBackgroundRect.top + heightOffset, + TagBackgroundRect.left + widthOffset + (int)mRect.GetRight(), + TagBackgroundRect.top + heightOffset + (int)mRect.GetBottom()); + + AddScreenObject(TagClickableMap[element], rt.GetCallsign(), ItemRect, + true, GetBottomLine(rt.GetCallsign()).c_str()); + + widthOffset += (int)mRect.GetRight(); + + // Only add blank space if this element is not empty AND there's a next + // element with content + if (!element.empty() && elementIndex < line.size() - 1) { + bool hasNextContent = false; + for (size_t k = elementIndex + 1; k < line.size(); k++) { + if (!line[k].empty()) { + hasNextContent = true; + break; + } + } + if (hasNextContent) { + widthOffset += blankWidth; + } + } + elementIndex++; + } + + heightOffset += oneLineHeight; + lineIndex++; + } + } #pragma endregion Drawing of the tags - // Releasing the hDC after the drawing - graphics.ReleaseHDC(hDC); - - CBrush BrushGrey(RGB(150, 150, 150)); - COLORREF oldColor = dc.SetTextColor(RGB(33, 33, 33)); - - int TextHeight = dc.GetTextExtent("60").cy; - int LineSpacing = TextHeight + 3; // Add 3 pixels between lines - Logger::info("RIMCAS Loop"); - for (std::map::iterator it = RimcasInstance->MonitoredRunwayArr.begin(); it != RimcasInstance->MonitoredRunwayArr.end(); ++it) - { - if (!it->second || RimcasInstance->TimeTable[it->first].empty()) - continue; - - vector TimeDefinition = RimcasInstance->CountdownDefinition; - if (isLVP) - TimeDefinition = RimcasInstance->CountdownDefinitionLVP; - - // Calculate box dimensions based on content - int maxWidth = 0; - char buffer[256]; - for (auto &Time : TimeDefinition) - { - if (RimcasInstance->TimeTable[it->first].find(Time) != RimcasInstance->TimeTable[it->first].end()) - { - auto acInfo = RimcasInstance->TimeTable[it->first][Time]; - if (detailedRIMCAS) - { - sprintf_s(buffer, "%2d: %-10s %-8s %s", - Time, - acInfo.callsign.c_str(), - acInfo.type.empty() ? "" : acInfo.type.c_str(), - acInfo.stand.empty() ? "" : acInfo.stand.c_str()); - } - else - { - sprintf_s(buffer, "%2d: %s", Time, acInfo.callsign.c_str()); - } - } - else - { - sprintf_s(buffer, "%2d: ", Time); - } - int textWidth = dc.GetTextExtent(buffer).cx; - if (textWidth > maxWidth) - maxWidth = textWidth; - } - - int boxPadding = detailedRIMCAS ? 15 : 10; // Smaller padding for simple mode - int boxWidth = maxWidth + boxPadding; - int boxHeight = LineSpacing * (TimeDefinition.size() + 1) + 5; // +1 for runway name, +5px total padding - - if (TimePopupAreas.find(it->first) == TimePopupAreas.end()) - TimePopupAreas[it->first] = { 300, 300, 300 + boxWidth, 300 + boxHeight }; - else - TimePopupAreas[it->first] = { TimePopupAreas[it->first].left, TimePopupAreas[it->first].top, TimePopupAreas[it->first].left + boxWidth, TimePopupAreas[it->first].top + boxHeight }; - - CRect CRectTime = TimePopupAreas[it->first]; - CRectTime.NormalizeRect(); - - dc.FillRect(CRectTime, &BrushGrey); - - // Drawing the runway name - string tempS = it->first; - dc.TextOutA(CRectTime.left + CRectTime.Width() / 2 - dc.GetTextExtent(tempS.c_str()).cx / 2, CRectTime.top + 2, tempS.c_str()); - - int TopOffset = LineSpacing; - // Drawing the times - for (auto &Time : TimeDefinition) - { - dc.SetTextColor(RGB(33, 33, 33)); - - // Get aircraft info from TimeTable - if (RimcasInstance->TimeTable[it->first].find(Time) != RimcasInstance->TimeTable[it->first].end()) - { - auto acInfo = RimcasInstance->TimeTable[it->first][Time]; - - // Build formatted string with fixed spacing - char buffer[256]; - if (detailedRIMCAS) - { - sprintf_s(buffer, "%2d: %-10s %-8s %s", - Time, - acInfo.callsign.c_str(), - acInfo.type.empty() ? "" : acInfo.type.c_str(), - acInfo.stand.empty() ? "" : acInfo.stand.c_str()); - } - else - { - sprintf_s(buffer, "%2d: %s", Time, acInfo.callsign.c_str()); - } - tempS = buffer; - - if (RimcasInstance->AcColor.find(acInfo.callsign) != RimcasInstance->AcColor.end()) - { - CBrush RimcasBrush(RimcasInstance->GetAircraftColor(acInfo.callsign, - Color::Black, - Color::Black, - CurrentConfig->getConfigColor(CurrentConfig->getActiveProfile()["rimcas"]["background_color_stage_one"]), - CurrentConfig->getConfigColor(CurrentConfig->getActiveProfile()["rimcas"]["background_color_stage_two"])).ToCOLORREF() - ); - - CRect TempRect = { CRectTime.left, CRectTime.top + TopOffset, CRectTime.right, CRectTime.top + TopOffset + TextHeight }; - TempRect.NormalizeRect(); - - dc.FillRect(TempRect, &RimcasBrush); - dc.SetTextColor(RGB(238, 238, 208)); - } - - dc.TextOutA(CRectTime.left + 5, CRectTime.top + TopOffset, tempS.c_str()); - } - else - { - tempS = std::to_string(Time) + ": "; - dc.TextOutA(CRectTime.left + 5, CRectTime.top + TopOffset, tempS.c_str()); - } - - TopOffset += LineSpacing; - } - - AddScreenObject(RIMCAS_IAW, it->first.c_str(), CRectTime, true, ""); - - } - - Logger::info("Menu bar lists"); - - if (ShowLists["Conflict Alert ARR"]) { - GetPlugIn()->OpenPopupList(ListAreas["Conflict Alert ARR"], "CA Arrival", 1); - for (std::map::iterator it = RimcasInstance->RunwayAreas.begin(); it != RimcasInstance->RunwayAreas.end(); ++it) - { - GetPlugIn()->AddPopupListElement(it->first.c_str(), "", RIMCAS_CA_ARRIVAL_FUNC, false, RimcasInstance->MonitoredRunwayArr[it->first.c_str()]); - } - GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, false, true); - ShowLists["Conflict Alert ARR"] = false; - } - - if (ShowLists["Conflict Alert DEP"]) { - GetPlugIn()->OpenPopupList(ListAreas["Conflict Alert DEP"], "CA Departure", 1); - for (std::map::iterator it = RimcasInstance->RunwayAreas.begin(); it != RimcasInstance->RunwayAreas.end(); ++it) - { - GetPlugIn()->AddPopupListElement(it->first.c_str(), "", RIMCAS_CA_MONITOR_FUNC, false, RimcasInstance->MonitoredRunwayDep[it->first.c_str()]); - } - GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, false, true); - ShowLists["Conflict Alert DEP"] = false; - } - - if (ShowLists["Runway closed"]) { - GetPlugIn()->OpenPopupList(ListAreas["Runway closed"], "Runway Closed", 1); - for (std::map::iterator it = RimcasInstance->RunwayAreas.begin(); it != RimcasInstance->RunwayAreas.end(); ++it) - { - GetPlugIn()->AddPopupListElement(it->first.c_str(), "", RIMCAS_CLOSED_RUNWAYS_FUNC, false, RimcasInstance->ClosedRunway[it->first.c_str()]); - } - GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, false, true); - ShowLists["Runway closed"] = false; - } - - if (ShowLists["Visibility"]) { - GetPlugIn()->OpenPopupList(ListAreas["Visibility"], "Visibility", 1); - GetPlugIn()->AddPopupListElement("Normal", "", RIMCAS_UPDATE_LVP, false, int(!isLVP)); - GetPlugIn()->AddPopupListElement("Low", "", RIMCAS_UPDATE_LVP, false, int(isLVP)); - GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, false, true); - ShowLists["Visibility"] = false; - } - - if (ShowLists["RIMCAS Detail"]) { - GetPlugIn()->OpenPopupList(ListAreas["RIMCAS Detail"], "RIMCAS Detail", 1); - GetPlugIn()->AddPopupListElement("Detailed", "", RIMCAS_UPDATE_DETAILED, false, int(detailedRIMCAS)); - GetPlugIn()->AddPopupListElement("Simple", "", RIMCAS_UPDATE_DETAILED, false, int(!detailedRIMCAS)); - GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, false, true); - ShowLists["RIMCAS Detail"] = false; - } - - if (ShowLists["Profiles"]) { - GetPlugIn()->OpenPopupList(ListAreas["Profiles"], "Profiles", 1); - vector allProfiles = CurrentConfig->getAllProfiles(); - for (std::vector::iterator it = allProfiles.begin(); it != allProfiles.end(); ++it) { - GetPlugIn()->AddPopupListElement(it->c_str(), "", RIMCAS_UPDATE_PROFILE, false, int(CurrentConfig->isItActiveProfile(it->c_str()))); - } - GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, false, true); - ShowLists["Profiles"] = false; - } - - if (ShowLists["Colour Settings"]) { - GetPlugIn()->OpenPopupList(ListAreas["Colour Settings"], "Colour Settings", 1); - GetPlugIn()->AddPopupListElement("Day", "", RIMCAS_UPDATE_BRIGHNESS, false, int(ColorSettingsDay)); - GetPlugIn()->AddPopupListElement("Night", "", RIMCAS_UPDATE_BRIGHNESS, false, int(!ColorSettingsDay)); - GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, false, true); - ShowLists["Colour Settings"] = false; - } - - if (ShowLists["Label Font Size"]) { - GetPlugIn()->OpenPopupList(ListAreas["Label Font Size"], "Label Font Size", 1); - GetPlugIn()->AddPopupListElement("Size 1", "", RIMCAS_UPDATE_FONTS, false, int(bool(currentFontSize == 1))); - GetPlugIn()->AddPopupListElement("Size 2", "", RIMCAS_UPDATE_FONTS, false, int(bool(currentFontSize == 2))); - GetPlugIn()->AddPopupListElement("Size 3", "", RIMCAS_UPDATE_FONTS, false, int(bool(currentFontSize == 3))); - GetPlugIn()->AddPopupListElement("Size 4", "", RIMCAS_UPDATE_FONTS, false, int(bool(currentFontSize == 4))); - GetPlugIn()->AddPopupListElement("Size 5", "", RIMCAS_UPDATE_FONTS, false, int(bool(currentFontSize == 5))); - GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, false, true); - ShowLists["Label Font Size"] = false; - } - - if (ShowLists["GRND Trail Dots"]) { - GetPlugIn()->OpenPopupList(ListAreas["GRND Trail Dots"], "GRND Trail Dots", 1); - GetPlugIn()->AddPopupListElement("0", "", RIMCAS_UPDATE_GND_TRAIL, false, int(bool(Trail_Gnd == 0))); - GetPlugIn()->AddPopupListElement("2", "", RIMCAS_UPDATE_GND_TRAIL, false, int(bool(Trail_Gnd == 2))); - GetPlugIn()->AddPopupListElement("4", "", RIMCAS_UPDATE_GND_TRAIL, false, int(bool(Trail_Gnd == 4))); - GetPlugIn()->AddPopupListElement("8", "", RIMCAS_UPDATE_GND_TRAIL, false, int(bool(Trail_Gnd == 8))); - GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, false, true); - ShowLists["GRND Trail Dots"] = false; - } - - if (ShowLists["APPR Trail Dots"]) { - GetPlugIn()->OpenPopupList(ListAreas["APPR Trail Dots"], "APPR Trail Dots", 1); - GetPlugIn()->AddPopupListElement("0", "", RIMCAS_UPDATE_APP_TRAIL, false, int(bool(Trail_App == 0))); - GetPlugIn()->AddPopupListElement("4", "", RIMCAS_UPDATE_APP_TRAIL, false, int(bool(Trail_App == 4))); - GetPlugIn()->AddPopupListElement("8", "", RIMCAS_UPDATE_APP_TRAIL, false, int(bool(Trail_App == 8))); - GetPlugIn()->AddPopupListElement("12", "", RIMCAS_UPDATE_APP_TRAIL, false, int(bool(Trail_App == 12))); - GetPlugIn()->AddPopupListElement("16", "", RIMCAS_UPDATE_APP_TRAIL, false, int(bool(Trail_App == 16))); - GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, false, true); - ShowLists["APPR Trail Dots"] = false; - } - - if (ShowLists["Predicted Track Line"]) { - GetPlugIn()->OpenPopupList(ListAreas["Predicted Track Line"], "Predicted Track Line", 1); - GetPlugIn()->AddPopupListElement("0", "", RIMCAS_UPDATE_PTL, false, int(bool(PredictedLength == 0))); - GetPlugIn()->AddPopupListElement("1", "", RIMCAS_UPDATE_PTL, false, int(bool(PredictedLength == 1))); - GetPlugIn()->AddPopupListElement("2", "", RIMCAS_UPDATE_PTL, false, int(bool(PredictedLength == 2))); - GetPlugIn()->AddPopupListElement("3", "", RIMCAS_UPDATE_PTL, false, int(bool(PredictedLength == 3))); - GetPlugIn()->AddPopupListElement("4", "", RIMCAS_UPDATE_PTL, false, int(bool(PredictedLength == 4))); - GetPlugIn()->AddPopupListElement("5", "", RIMCAS_UPDATE_PTL, false, int(bool(PredictedLength == 5))); - GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, false, true); - ShowLists["Predicted Track Line"] = false; - } - - if (ShowLists["Brightness"]) - { - GetPlugIn()->OpenPopupList(ListAreas["Brightness"], "Brightness", 1); - GetPlugIn()->AddPopupListElement("Label", "", RIMCAS_OPEN_LIST, false); - GetPlugIn()->AddPopupListElement("Symbol", "", RIMCAS_OPEN_LIST, false); - GetPlugIn()->AddPopupListElement("Afterglow", "", RIMCAS_OPEN_LIST, false); - GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, false, true); - ShowLists["Brightness"] = false; - } - - if (ShowLists["Label"]) - { - GetPlugIn()->OpenPopupList(ListAreas["Label"], "Label Brightness", 1); - for(int i = CColorManager::bounds_low(); i <= CColorManager::bounds_high(); i +=10) - GetPlugIn()->AddPopupListElement(std::to_string(i).c_str(), "", RIMCAS_BRIGHTNESS_LABEL, false, int(bool(i == ColorManager->get_brightness("label")))); - - GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, false, true); - ShowLists["Label"] = false; - } - - if (ShowLists["Symbol"]) - { - GetPlugIn()->OpenPopupList(ListAreas["Symbol"], "Symbol Brightness", 1); - for (int i = CColorManager::bounds_low(); i <= CColorManager::bounds_high(); i += 10) - GetPlugIn()->AddPopupListElement(std::to_string(i).c_str(), "", RIMCAS_BRIGHTNESS_SYMBOL, false, int(bool(i == ColorManager->get_brightness("symbol")))); - - GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, false, true); - ShowLists["Symbol"] = false; - } - - if (ShowLists["Afterglow"]) - { - GetPlugIn()->OpenPopupList(ListAreas["Afterglow"], "Afterglow Brightness", 1); - for (int i = CColorManager::bounds_low(); i <= CColorManager::bounds_high(); i += 10) - GetPlugIn()->AddPopupListElement(std::to_string(i).c_str(), "", RIMCAS_BRIGHTNESS_AFTERGLOW, false, int(bool(i == ColorManager->get_brightness("afterglow")))); - - GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, false, true); - ShowLists["Afterglow"] = false; - } - - Logger::info("QRD"); - - //--------------------------------- - // QRD - //--------------------------------- - - if (QDMenabled || QDMSelectEnabled || (DistanceToolActive && ActiveDistance.first != "")) { - CPen Pen(PS_SOLID, 1, RGB(255, 255, 255)); - CPen *oldPen = dc.SelectObject(&Pen); - - POINT AirportPos = ConvertCoordFromPositionToPixel(AirportPositions[getActiveAirport()]); - CPosition AirportCPos = AirportPositions[getActiveAirport()]; - if (QDMSelectEnabled) - { - AirportPos = QDMSelectPt; - AirportCPos = ConvertCoordFromPixelToPosition(QDMSelectPt); - } - if (DistanceToolActive) - { - CPosition r = GetPlugIn()->RadarTargetSelect(ActiveDistance.first.c_str()).GetPosition().GetPosition(); - AirportPos = ConvertCoordFromPositionToPixel(r); - AirportCPos = r; - } - dc.MoveTo(AirportPos); - POINT point = mouseLocation; - dc.LineTo(point); - - CPosition CursorPos = ConvertCoordFromPixelToPosition(point); - double Distance = AirportCPos.DistanceTo(CursorPos); - double Bearing = AirportCPos.DirectionTo(CursorPos); - - Gdiplus::Pen WhitePen(Color::White); - graphics.DrawEllipse(&WhitePen, point.x - 5, point.y - 5, 10, 10); - - Distance = Distance / 0.00053996f; - - Distance = round(Distance * 10) / 10; - - Bearing = round(Bearing * 10) / 10; - - POINT TextPos = { point.x + 20, point.y }; - - if (!DistanceToolActive) - { - string distances = std::to_string(Distance); - size_t decimal_pos = distances.find("."); - distances = distances.substr(0, decimal_pos + 2); - - string bearings = std::to_string(Bearing); - decimal_pos = bearings.find("."); - bearings = bearings.substr(0, decimal_pos + 2); - - string text = bearings; - text += "° / "; - text += distances; - text += "m"; - COLORREF old_color = dc.SetTextColor(RGB(255, 255, 255)); - dc.TextOutA(TextPos.x, TextPos.y, text.c_str()); - dc.SetTextColor(old_color); - } - - dc.SelectObject(oldPen); - RequestRefresh(); - } - - // Distance tools here - for (auto&& kv : DistanceTools) - { - CRadarTarget one = GetPlugIn()->RadarTargetSelect(kv.first.c_str()); - CRadarTarget two = GetPlugIn()->RadarTargetSelect(kv.second.c_str()); - - if (!isVisible(one) || !isVisible(two)) - continue; - - CPen Pen(PS_SOLID, 1, RGB(255, 255, 255)); - CPen *oldPen = dc.SelectObject(&Pen); - - POINT onePoint = ConvertCoordFromPositionToPixel(one.GetPosition().GetPosition()); - POINT twoPoint = ConvertCoordFromPositionToPixel(two.GetPosition().GetPosition()); - - dc.MoveTo(onePoint); - dc.LineTo(twoPoint); - - POINT TextPos = { twoPoint.x + 20, twoPoint.y }; - - double Distance = one.GetPosition().GetPosition().DistanceTo(two.GetPosition().GetPosition()); - double Bearing = one.GetPosition().GetPosition().DirectionTo(two.GetPosition().GetPosition()); - - string distances = std::to_string(Distance); - size_t decimal_pos = distances.find("."); - distances = distances.substr(0, decimal_pos + 2); - - string bearings = std::to_string(Bearing); - decimal_pos = bearings.find("."); - bearings = bearings.substr(0, decimal_pos + 2); - - string text = bearings; - text += "° / "; - text += distances; - text += "nm"; - COLORREF old_color = dc.SetTextColor(RGB(0, 0, 0)); - - CRect ClickableRect = { TextPos.x - 2, TextPos.y, TextPos.x + dc.GetTextExtent(text.c_str()).cx + 2, TextPos.y + dc.GetTextExtent(text.c_str()).cy }; - graphics.FillRectangle(&SolidBrush(Color(127, 122, 122)), CopyRect(ClickableRect)); - dc.Draw3dRect(ClickableRect, RGB(75, 75, 75), RGB(45, 45, 45)); - dc.TextOutA(TextPos.x, TextPos.y, text.c_str()); - - AddScreenObject(RIMCAS_DISTANCE_TOOL, string(kv.first+","+kv.second).c_str(), ClickableRect, false, ""); - - dc.SetTextColor(old_color); - - dc.SelectObject(oldPen); - } - - //--------------------------------- - // Drawing the toolbar - //--------------------------------- - - Logger::info("Menu Bar"); - - COLORREF qToolBarColor = RGB(127, 122, 122); - - // Drawing the toolbar on the top - CRect ToolBarAreaTop(RadarArea.left, RadarArea.top, RadarArea.right, RadarArea.top + 20); - dc.FillSolidRect(ToolBarAreaTop, qToolBarColor); - - COLORREF oldTextColor = dc.SetTextColor(RGB(0, 0, 0)); - - int offset = 2; - dc.TextOutA(ToolBarAreaTop.left + offset, ToolBarAreaTop.top + 4, getActiveAirport().c_str()); - AddScreenObject(RIMCAS_ACTIVE_AIRPORT, "ActiveAirport", { ToolBarAreaTop.left + offset, ToolBarAreaTop.top + 4, ToolBarAreaTop.left + offset + dc.GetTextExtent(getActiveAirport().c_str()).cx, ToolBarAreaTop.top + 4 + dc.GetTextExtent(getActiveAirport().c_str()).cy }, false, "Active Airport"); - - offset += dc.GetTextExtent(getActiveAirport().c_str()).cx + 10; - dc.TextOutA(ToolBarAreaTop.left + offset, ToolBarAreaTop.top + 4, "Display"); - AddScreenObject(RIMCAS_MENU, "DisplayMenu", { ToolBarAreaTop.left + offset, ToolBarAreaTop.top + 4, ToolBarAreaTop.left + offset + dc.GetTextExtent("Display").cx, ToolBarAreaTop.top + 4 + dc.GetTextExtent("Display").cy }, false, "Display menu"); - - offset += dc.GetTextExtent("Display").cx + 10; - dc.TextOutA(ToolBarAreaTop.left + offset, ToolBarAreaTop.top + 4, "Target"); - AddScreenObject(RIMCAS_MENU, "TargetMenu", { ToolBarAreaTop.left + offset, ToolBarAreaTop.top + 4, ToolBarAreaTop.left + offset + dc.GetTextExtent("Target").cx, ToolBarAreaTop.top + 4 + dc.GetTextExtent("Target").cy }, false, "Target menu"); - - offset += dc.GetTextExtent("Target").cx + 10; - dc.TextOutA(ToolBarAreaTop.left + offset, ToolBarAreaTop.top + 4, "Colours"); - AddScreenObject(RIMCAS_MENU, "ColourMenu", { ToolBarAreaTop.left + offset, ToolBarAreaTop.top + 4, ToolBarAreaTop.left + offset + dc.GetTextExtent("Colour").cx, ToolBarAreaTop.top + 4 + dc.GetTextExtent("Colour").cy }, false, "Colour menu"); - - offset += dc.GetTextExtent("Colours").cx + 10; - dc.TextOutA(ToolBarAreaTop.left + offset, ToolBarAreaTop.top + 4, "Alerts"); - AddScreenObject(RIMCAS_MENU, "RIMCASMenu", { ToolBarAreaTop.left + offset, ToolBarAreaTop.top + 4, ToolBarAreaTop.left + offset + dc.GetTextExtent("Alerts").cx, ToolBarAreaTop.top + 4 + +dc.GetTextExtent("Alerts").cy }, false, "RIMCAS menu"); - - offset += dc.GetTextExtent("Alerts").cx + 10; - dc.TextOutA(ToolBarAreaTop.left + offset, ToolBarAreaTop.top + 4, "/"); - CRect barDistanceRect = { ToolBarAreaTop.left + offset - 2, ToolBarAreaTop.top + 4, ToolBarAreaTop.left + offset + dc.GetTextExtent("/").cx, ToolBarAreaTop.top + 4 + +dc.GetTextExtent("/").cy }; - if (DistanceToolActive) - { - graphics.DrawRectangle(&Pen(Color::White), CopyRect(barDistanceRect)); - } - AddScreenObject(RIMCAS_MENU, "/", barDistanceRect, false, "Distance tool"); - - dc.SetTextColor(oldTextColor); - - // - // Tag deconflicting - // - - Logger::info("Tag deconfliction loop"); - - for (const auto areas : tagAreas) - { - if (!CurrentConfig->getActiveProfile()["labels"]["auto_deconfliction"].GetBool()) - break; - - if (TagsOffsets.find(areas.first) != TagsOffsets.end()) - continue; - - if (IsTagBeingDragged(areas.first)) - continue; - - if (RecentlyAutoMovedTags.find(areas.first) != RecentlyAutoMovedTags.end()) - { - double t = (double)clock() - RecentlyAutoMovedTags[areas.first] / ((double)CLOCKS_PER_SEC); - if (t >= 0.8) { - RecentlyAutoMovedTags.erase(areas.first); - } else - { - continue; - } - } - - // We need to see wether the rotation will be clockwise or anti-clockwise - - bool isAntiClockwise = false; - - for (const auto area2 : tagAreas) - { - if (areas.first == area2.first) - continue; - - if (IsTagBeingDragged(area2.first)) - continue; - - CRect h; - - if (h.IntersectRect(tagAreas[areas.first], area2.second)) - { - if (areas.second.left <= area2.second.left) - { - isAntiClockwise = true; - } - - break; - } - } - - // We then rotate the tags until we did a 360 or there is no more conflicts - - POINT acPosPix = ConvertCoordFromPositionToPixel(GetPlugIn()->RadarTargetSelect(areas.first.c_str()).GetPosition().GetPosition()); - int lenght = LeaderLineDefaultlenght; - if (TagLeaderLineLength.find(areas.first) != TagLeaderLineLength.end()) - lenght = TagLeaderLineLength[areas.first]; - - int width = areas.second.Width(); - int height = areas.second.Height(); - - for (double rotated = 0.0; abs(rotated) <= 360.0;) - { - // We first rotate the tag - double newangle = fmod(TagAngles[areas.first] + rotated, 360.0f); - - POINT TagCenter; - TagCenter.x = long(acPosPix.x + float(lenght * cos(DegToRad(newangle)))); - TagCenter.y = long(acPosPix.y + float(lenght * sin(DegToRad(newangle)))); - - CRect NewRectangle(TagCenter.x - (width / 2), TagCenter.y - (height / 2), TagCenter.x + (width / 2), TagCenter.y + (height / 2)); - NewRectangle.NormalizeRect(); - - // Assume there is no conflict, then try again - - bool isTagConflicing = false; - - for (const auto area2 : tagAreas) - { - if (areas.first == area2.first) - continue; - - if (IsTagBeingDragged(area2.first)) - continue; - - CRect h; - - if (h.IntersectRect(NewRectangle, area2.second)) - { - isTagConflicing = true; - break; - } - } + // Releasing the hDC after the drawing + graphics.ReleaseHDC(hDC); + + CBrush BrushGrey(RGB(150, 150, 150)); + COLORREF oldColor = dc.SetTextColor(RGB(33, 33, 33)); + + int TextHeight = dc.GetTextExtent("60").cy; + int LineSpacing = TextHeight + 3; // Add 3 pixels between lines + Logger::info("RIMCAS Loop"); + for (std::map::iterator it = + RimcasInstance->MonitoredRunwayArr.begin(); + it != RimcasInstance->MonitoredRunwayArr.end(); ++it) { + if (!it->second || RimcasInstance->TimeTable[it->first].empty()) + continue; + + vector TimeDefinition = RimcasInstance->CountdownDefinition; + if (isLVP) + TimeDefinition = RimcasInstance->CountdownDefinitionLVP; + + // Calculate box dimensions based on content + int maxWidth = 0; + char buffer[256]; + for (auto &Time : TimeDefinition) { + if (RimcasInstance->TimeTable[it->first].find(Time) != + RimcasInstance->TimeTable[it->first].end()) { + auto acInfo = RimcasInstance->TimeTable[it->first][Time]; + if (detailedRIMCAS) { + sprintf_s(buffer, "%2d: %-10s %-8s %s", Time, acInfo.callsign.c_str(), + acInfo.type.empty() ? "" : acInfo.type.c_str(), + acInfo.stand.empty() ? "" : acInfo.stand.c_str()); + } else { + sprintf_s(buffer, "%2d: %s", Time, acInfo.callsign.c_str()); + } + } else { + sprintf_s(buffer, "%2d: ", Time); + } + int textWidth = dc.GetTextExtent(buffer).cx; + if (textWidth > maxWidth) + maxWidth = textWidth; + } + + int boxPadding = + detailedRIMCAS ? 15 : 10; // Smaller padding for simple mode + int boxWidth = maxWidth + boxPadding; + int boxHeight = LineSpacing * (TimeDefinition.size() + 1) + + 5; // +1 for runway name, +5px total padding + + if (TimePopupAreas.find(it->first) == TimePopupAreas.end()) + TimePopupAreas[it->first] = {300, 300, 300 + boxWidth, 300 + boxHeight}; + else + TimePopupAreas[it->first] = {TimePopupAreas[it->first].left, + TimePopupAreas[it->first].top, + TimePopupAreas[it->first].left + boxWidth, + TimePopupAreas[it->first].top + boxHeight}; + + CRect CRectTime = TimePopupAreas[it->first]; + CRectTime.NormalizeRect(); + + dc.FillRect(CRectTime, &BrushGrey); + + // Drawing the runway name + string tempS = it->first; + dc.TextOutA(CRectTime.left + CRectTime.Width() / 2 - + dc.GetTextExtent(tempS.c_str()).cx / 2, + CRectTime.top + 2, tempS.c_str()); + + int TopOffset = LineSpacing; + // Drawing the times + for (auto &Time : TimeDefinition) { + dc.SetTextColor(RGB(33, 33, 33)); + + // Get aircraft info from TimeTable + if (RimcasInstance->TimeTable[it->first].find(Time) != + RimcasInstance->TimeTable[it->first].end()) { + auto acInfo = RimcasInstance->TimeTable[it->first][Time]; + + // Build formatted string with fixed spacing + char buffer[256]; + if (detailedRIMCAS) { + sprintf_s(buffer, "%2d: %-10s %-8s %s", Time, acInfo.callsign.c_str(), + acInfo.type.empty() ? "" : acInfo.type.c_str(), + acInfo.stand.empty() ? "" : acInfo.stand.c_str()); + } else { + sprintf_s(buffer, "%2d: %s", Time, acInfo.callsign.c_str()); + } + tempS = buffer; + + if (RimcasInstance->AcColor.find(acInfo.callsign) != + RimcasInstance->AcColor.end()) { + CBrush RimcasBrush( + RimcasInstance + ->GetAircraftColor( + acInfo.callsign, Color::Black, Color::Black, + CurrentConfig->getConfigColor( + CurrentConfig->getActiveProfile() + ["rimcas"]["background_color_stage_one"]), + CurrentConfig->getConfigColor( + CurrentConfig->getActiveProfile() + ["rimcas"]["background_color_stage_two"])) + .ToCOLORREF()); + + CRect TempRect = {CRectTime.left, CRectTime.top + TopOffset, + CRectTime.right, + CRectTime.top + TopOffset + TextHeight}; + TempRect.NormalizeRect(); + + dc.FillRect(TempRect, &RimcasBrush); + dc.SetTextColor(RGB(238, 238, 208)); + } + + dc.TextOutA(CRectTime.left + 5, CRectTime.top + TopOffset, + tempS.c_str()); + } else { + tempS = std::to_string(Time) + ": "; + dc.TextOutA(CRectTime.left + 5, CRectTime.top + TopOffset, + tempS.c_str()); + } + + TopOffset += LineSpacing; + } + + AddScreenObject(RIMCAS_IAW, it->first.c_str(), CRectTime, true, ""); + } + + // ========================================== + // Departure Timer Window + // ========================================== + Logger::info("Departure Timer Window"); + + if (showDepartureWindow && !RimcasInstance->DepartedAircraft.empty()) { + // Calculate box dimensions based on content + int maxWidth = 0; + int numColumns = 0; + if (depWindowShowCallsign) + numColumns++; + if (depWindowShowDest) + numColumns++; + if (depWindowShowAcType) + numColumns++; + if (depWindowShowWake) + numColumns++; + if (depWindowShowFreq) + numColumns++; + if (depWindowShowTime) + numColumns++; + + // Calculate column widths + int colCallsignWidth = dc.GetTextExtent("SPEEDBIRD123").cx; + int colDestWidth = dc.GetTextExtent("XXXX").cx; + int colAcTypeWidth = dc.GetTextExtent("A320").cx; + int colWakeWidth = dc.GetTextExtent("H").cx; + int colFreqWidth = dc.GetTextExtent("124.850").cx; + int colTimeWidth = dc.GetTextExtent("59:59").cx; + int colPadding = 8; + + // Calculate total width based on enabled columns + int totalWidth = 10; // Left padding + if (depWindowShowCallsign) + totalWidth += colCallsignWidth + colPadding; + if (depWindowShowDest) + totalWidth += colDestWidth + colPadding; + if (depWindowShowAcType) + totalWidth += colAcTypeWidth + colPadding; + if (depWindowShowWake) + totalWidth += colWakeWidth + colPadding; + if (depWindowShowFreq) + totalWidth += colFreqWidth + colPadding; + if (depWindowShowTime) + totalWidth += colTimeWidth + colPadding; + totalWidth += 5; // Right padding + + int boxHeight = + LineSpacing * (RimcasInstance->DepartedAircraft.size() + 1) + + 10; // +1 for header + + // Update the window area dimensions + DepartureWindowArea.right = DepartureWindowArea.left + totalWidth; + DepartureWindowArea.bottom = DepartureWindowArea.top + boxHeight; + + CRect DepWindowRect = DepartureWindowArea; + DepWindowRect.NormalizeRect(); + + // Draw background + dc.FillRect(DepWindowRect, &BrushGrey); + + // Draw header + int headerX = DepWindowRect.left + 5; + int headerY = DepWindowRect.top + 3; + dc.SetTextColor(RGB(33, 33, 33)); + + string headerText = "DEPARTURES"; + dc.TextOutA(DepWindowRect.left + DepWindowRect.Width() / 2 - + dc.GetTextExtent(headerText.c_str()).cx / 2, + headerY, headerText.c_str()); + + // Draw each departure row + int rowY = DepWindowRect.top + LineSpacing; + clock_t currentTime = clock(); + + for (auto &pair : RimcasInstance->DepartedAircraft) { + if (pair.second.dismissed) + continue; + + CRimcas::DepartureInfo &info = pair.second; + int rowX = DepWindowRect.left + 5; + + // Calculate elapsed time since liftoff + double elapsedSecs = + (double)(currentTime - info.liftoffTime) / CLOCKS_PER_SEC; + int minutes = (int)(elapsedSecs / 60); + int seconds = (int)(elapsedSecs) % 60; + + // Create row rect for click detection + CRect rowRect = {DepWindowRect.left, rowY, DepWindowRect.right, + rowY + TextHeight}; + + dc.SetTextColor(RGB(33, 33, 33)); + + // Draw each enabled column + if (depWindowShowCallsign) { + dc.TextOutA(rowX, rowY, info.callsign.c_str()); + rowX += colCallsignWidth + colPadding; + } + + if (depWindowShowDest) { + dc.TextOutA(rowX, rowY, info.destination.c_str()); + rowX += colDestWidth + colPadding; + } + + if (depWindowShowAcType) { + dc.TextOutA(rowX, rowY, info.acType.c_str()); + rowX += colAcTypeWidth + colPadding; + } + + if (depWindowShowWake) { + dc.TextOutA(rowX, rowY, info.wakeTurbCat.c_str()); + rowX += colWakeWidth + colPadding; + } + + if (depWindowShowFreq) { + dc.TextOutA(rowX, rowY, info.airborneFreq.c_str()); + rowX += colFreqWidth + colPadding; + } + + if (depWindowShowTime) { + char timeBuffer[16]; + sprintf_s(timeBuffer, "%d:%02d", minutes, seconds); + dc.TextOutA(rowX, rowY, timeBuffer); + } + + // Add click target for this row + AddScreenObject(RIMCAS_DEP_WINDOW_ROW, info.callsign.c_str(), rowRect, + true, ""); + + rowY += LineSpacing; + } + + // Add the window itself as a movable object + AddScreenObject(RIMCAS_DEP_WINDOW, "dep_window", DepWindowRect, true, ""); + } + + Logger::info("Menu bar lists"); + + if (ShowLists["Conflict Alert ARR"]) { + GetPlugIn()->OpenPopupList(ListAreas["Conflict Alert ARR"], "CA Arrival", + 1); + for (std::map::iterator it = + RimcasInstance->RunwayAreas.begin(); + it != RimcasInstance->RunwayAreas.end(); ++it) { + GetPlugIn()->AddPopupListElement( + it->first.c_str(), "", RIMCAS_CA_ARRIVAL_FUNC, false, + RimcasInstance->MonitoredRunwayArr[it->first.c_str()]); + } + GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, false, + true); + ShowLists["Conflict Alert ARR"] = false; + } + + if (ShowLists["Conflict Alert DEP"]) { + GetPlugIn()->OpenPopupList(ListAreas["Conflict Alert DEP"], "CA Departure", + 1); + for (std::map::iterator it = + RimcasInstance->RunwayAreas.begin(); + it != RimcasInstance->RunwayAreas.end(); ++it) { + GetPlugIn()->AddPopupListElement( + it->first.c_str(), "", RIMCAS_CA_MONITOR_FUNC, false, + RimcasInstance->MonitoredRunwayDep[it->first.c_str()]); + } + GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, false, + true); + ShowLists["Conflict Alert DEP"] = false; + } + + if (ShowLists["Runway closed"]) { + GetPlugIn()->OpenPopupList(ListAreas["Runway closed"], "Runway Closed", 1); + for (std::map::iterator it = + RimcasInstance->RunwayAreas.begin(); + it != RimcasInstance->RunwayAreas.end(); ++it) { + GetPlugIn()->AddPopupListElement( + it->first.c_str(), "", RIMCAS_CLOSED_RUNWAYS_FUNC, false, + RimcasInstance->ClosedRunway[it->first.c_str()]); + } + GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, false, + true); + ShowLists["Runway closed"] = false; + } + + if (ShowLists["Visibility"]) { + GetPlugIn()->OpenPopupList(ListAreas["Visibility"], "Visibility", 1); + GetPlugIn()->AddPopupListElement("Normal", "", RIMCAS_UPDATE_LVP, false, + int(!isLVP)); + GetPlugIn()->AddPopupListElement("Low", "", RIMCAS_UPDATE_LVP, false, + int(isLVP)); + GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, false, + true); + ShowLists["Visibility"] = false; + } + + if (ShowLists["RIMCAS Detail"]) { + GetPlugIn()->OpenPopupList(ListAreas["RIMCAS Detail"], "RIMCAS Detail", 1); + GetPlugIn()->AddPopupListElement("Detailed", "", RIMCAS_UPDATE_DETAILED, + false, int(detailedRIMCAS)); + GetPlugIn()->AddPopupListElement("Simple", "", RIMCAS_UPDATE_DETAILED, + false, int(!detailedRIMCAS)); + GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, false, + true); + ShowLists["RIMCAS Detail"] = false; + } + + if (ShowLists["Departure Timer"]) { + GetPlugIn()->OpenPopupList(ListAreas["Departure Timer"], "Departure Timer", + 1); + GetPlugIn()->AddPopupListElement("Show Callsign", "", + RIMCAS_DEP_WINDOW_COL_CALLSIGN, false, + int(depWindowShowCallsign)); + GetPlugIn()->AddPopupListElement("Show Destination", "", + RIMCAS_DEP_WINDOW_COL_DEST, false, + int(depWindowShowDest)); + GetPlugIn()->AddPopupListElement("Show AC Type", "", + RIMCAS_DEP_WINDOW_COL_ACTYPE, false, + int(depWindowShowAcType)); + GetPlugIn()->AddPopupListElement("Show Wake Cat", "", + RIMCAS_DEP_WINDOW_COL_WAKE, false, + int(depWindowShowWake)); + GetPlugIn()->AddPopupListElement("Show QSY Freq", "", + RIMCAS_DEP_WINDOW_COL_FREQ, false, + int(depWindowShowFreq)); + GetPlugIn()->AddPopupListElement("Show Time", "", + RIMCAS_DEP_WINDOW_COL_TIME, false, + int(depWindowShowTime)); + GetPlugIn()->AddPopupListElement("Duration (seconds)", "", + RIMCAS_OPEN_LIST); + GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, false, + true); + ShowLists["Departure Timer"] = false; + } + + if (ShowLists["Duration (seconds)"]) { + GetPlugIn()->OpenPopupList(ListAreas["Duration (seconds)"], "Duration", 1); + GetPlugIn()->AddPopupListElement("60", "", RIMCAS_DEP_WINDOW_DURATION, + false, + int(departureDisplayDuration == 60)); + GetPlugIn()->AddPopupListElement("120", "", RIMCAS_DEP_WINDOW_DURATION, + false, + int(departureDisplayDuration == 120)); + GetPlugIn()->AddPopupListElement("180", "", RIMCAS_DEP_WINDOW_DURATION, + false, + int(departureDisplayDuration == 180)); + GetPlugIn()->AddPopupListElement("240", "", RIMCAS_DEP_WINDOW_DURATION, + false, + int(departureDisplayDuration == 240)); + GetPlugIn()->AddPopupListElement("300", "", RIMCAS_DEP_WINDOW_DURATION, + false, + int(departureDisplayDuration == 300)); + GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, false, + true); + ShowLists["Duration (seconds)"] = false; + } + + if (ShowLists["Profiles"]) { + GetPlugIn()->OpenPopupList(ListAreas["Profiles"], "Profiles", 1); + vector allProfiles = CurrentConfig->getAllProfiles(); + for (std::vector::iterator it = allProfiles.begin(); + it != allProfiles.end(); ++it) { + GetPlugIn()->AddPopupListElement( + it->c_str(), "", RIMCAS_UPDATE_PROFILE, false, + int(CurrentConfig->isItActiveProfile(it->c_str()))); + } + GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, false, + true); + ShowLists["Profiles"] = false; + } + + if (ShowLists["Colour Settings"]) { + GetPlugIn()->OpenPopupList(ListAreas["Colour Settings"], "Colour Settings", + 1); + GetPlugIn()->AddPopupListElement("Day", "", RIMCAS_UPDATE_BRIGHNESS, false, + int(ColorSettingsDay)); + GetPlugIn()->AddPopupListElement("Night", "", RIMCAS_UPDATE_BRIGHNESS, + false, int(!ColorSettingsDay)); + GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, false, + true); + ShowLists["Colour Settings"] = false; + } + + if (ShowLists["Label Font Size"]) { + GetPlugIn()->OpenPopupList(ListAreas["Label Font Size"], "Label Font Size", + 1); + GetPlugIn()->AddPopupListElement("Size 1", "", RIMCAS_UPDATE_FONTS, false, + int(bool(currentFontSize == 1))); + GetPlugIn()->AddPopupListElement("Size 2", "", RIMCAS_UPDATE_FONTS, false, + int(bool(currentFontSize == 2))); + GetPlugIn()->AddPopupListElement("Size 3", "", RIMCAS_UPDATE_FONTS, false, + int(bool(currentFontSize == 3))); + GetPlugIn()->AddPopupListElement("Size 4", "", RIMCAS_UPDATE_FONTS, false, + int(bool(currentFontSize == 4))); + GetPlugIn()->AddPopupListElement("Size 5", "", RIMCAS_UPDATE_FONTS, false, + int(bool(currentFontSize == 5))); + GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, false, + true); + ShowLists["Label Font Size"] = false; + } + + if (ShowLists["GRND Trail Dots"]) { + GetPlugIn()->OpenPopupList(ListAreas["GRND Trail Dots"], "GRND Trail Dots", + 1); + GetPlugIn()->AddPopupListElement("0", "", RIMCAS_UPDATE_GND_TRAIL, false, + int(bool(Trail_Gnd == 0))); + GetPlugIn()->AddPopupListElement("2", "", RIMCAS_UPDATE_GND_TRAIL, false, + int(bool(Trail_Gnd == 2))); + GetPlugIn()->AddPopupListElement("4", "", RIMCAS_UPDATE_GND_TRAIL, false, + int(bool(Trail_Gnd == 4))); + GetPlugIn()->AddPopupListElement("8", "", RIMCAS_UPDATE_GND_TRAIL, false, + int(bool(Trail_Gnd == 8))); + GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, false, + true); + ShowLists["GRND Trail Dots"] = false; + } + + if (ShowLists["APPR Trail Dots"]) { + GetPlugIn()->OpenPopupList(ListAreas["APPR Trail Dots"], "APPR Trail Dots", + 1); + GetPlugIn()->AddPopupListElement("0", "", RIMCAS_UPDATE_APP_TRAIL, false, + int(bool(Trail_App == 0))); + GetPlugIn()->AddPopupListElement("4", "", RIMCAS_UPDATE_APP_TRAIL, false, + int(bool(Trail_App == 4))); + GetPlugIn()->AddPopupListElement("8", "", RIMCAS_UPDATE_APP_TRAIL, false, + int(bool(Trail_App == 8))); + GetPlugIn()->AddPopupListElement("12", "", RIMCAS_UPDATE_APP_TRAIL, false, + int(bool(Trail_App == 12))); + GetPlugIn()->AddPopupListElement("16", "", RIMCAS_UPDATE_APP_TRAIL, false, + int(bool(Trail_App == 16))); + GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, false, + true); + ShowLists["APPR Trail Dots"] = false; + } + + if (ShowLists["Predicted Track Line"]) { + GetPlugIn()->OpenPopupList(ListAreas["Predicted Track Line"], + "Predicted Track Line", 1); + GetPlugIn()->AddPopupListElement("0", "", RIMCAS_UPDATE_PTL, false, + int(bool(PredictedLength == 0))); + GetPlugIn()->AddPopupListElement("1", "", RIMCAS_UPDATE_PTL, false, + int(bool(PredictedLength == 1))); + GetPlugIn()->AddPopupListElement("2", "", RIMCAS_UPDATE_PTL, false, + int(bool(PredictedLength == 2))); + GetPlugIn()->AddPopupListElement("3", "", RIMCAS_UPDATE_PTL, false, + int(bool(PredictedLength == 3))); + GetPlugIn()->AddPopupListElement("4", "", RIMCAS_UPDATE_PTL, false, + int(bool(PredictedLength == 4))); + GetPlugIn()->AddPopupListElement("5", "", RIMCAS_UPDATE_PTL, false, + int(bool(PredictedLength == 5))); + GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, false, + true); + ShowLists["Predicted Track Line"] = false; + } + + if (ShowLists["Brightness"]) { + GetPlugIn()->OpenPopupList(ListAreas["Brightness"], "Brightness", 1); + GetPlugIn()->AddPopupListElement("Label", "", RIMCAS_OPEN_LIST, false); + GetPlugIn()->AddPopupListElement("Symbol", "", RIMCAS_OPEN_LIST, false); + GetPlugIn()->AddPopupListElement("Afterglow", "", RIMCAS_OPEN_LIST, false); + GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, false, + true); + ShowLists["Brightness"] = false; + } + + if (ShowLists["Label"]) { + GetPlugIn()->OpenPopupList(ListAreas["Label"], "Label Brightness", 1); + for (int i = CColorManager::bounds_low(); i <= CColorManager::bounds_high(); + i += 10) + GetPlugIn()->AddPopupListElement( + std::to_string(i).c_str(), "", RIMCAS_BRIGHTNESS_LABEL, false, + int(bool(i == ColorManager->get_brightness("label")))); + + GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, false, + true); + ShowLists["Label"] = false; + } + + if (ShowLists["Symbol"]) { + GetPlugIn()->OpenPopupList(ListAreas["Symbol"], "Symbol Brightness", 1); + for (int i = CColorManager::bounds_low(); i <= CColorManager::bounds_high(); + i += 10) + GetPlugIn()->AddPopupListElement( + std::to_string(i).c_str(), "", RIMCAS_BRIGHTNESS_SYMBOL, false, + int(bool(i == ColorManager->get_brightness("symbol")))); + + GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, false, + true); + ShowLists["Symbol"] = false; + } + + if (ShowLists["Afterglow"]) { + GetPlugIn()->OpenPopupList(ListAreas["Afterglow"], "Afterglow Brightness", + 1); + for (int i = CColorManager::bounds_low(); i <= CColorManager::bounds_high(); + i += 10) + GetPlugIn()->AddPopupListElement( + std::to_string(i).c_str(), "", RIMCAS_BRIGHTNESS_AFTERGLOW, false, + int(bool(i == ColorManager->get_brightness("afterglow")))); + + GetPlugIn()->AddPopupListElement("Close", "", RIMCAS_CLOSE, false, 2, false, + true); + ShowLists["Afterglow"] = false; + } + + Logger::info("QRD"); + + //--------------------------------- + // QRD + //--------------------------------- + + if (QDMenabled || QDMSelectEnabled || + (DistanceToolActive && ActiveDistance.first != "")) { + CPen Pen(PS_SOLID, 1, RGB(255, 255, 255)); + CPen *oldPen = dc.SelectObject(&Pen); + + POINT AirportPos = + ConvertCoordFromPositionToPixel(AirportPositions[getActiveAirport()]); + CPosition AirportCPos = AirportPositions[getActiveAirport()]; + if (QDMSelectEnabled) { + AirportPos = QDMSelectPt; + AirportCPos = ConvertCoordFromPixelToPosition(QDMSelectPt); + } + if (DistanceToolActive) { + CPosition r = GetPlugIn() + ->RadarTargetSelect(ActiveDistance.first.c_str()) + .GetPosition() + .GetPosition(); + AirportPos = ConvertCoordFromPositionToPixel(r); + AirportCPos = r; + } + dc.MoveTo(AirportPos); + POINT point = mouseLocation; + dc.LineTo(point); + + CPosition CursorPos = ConvertCoordFromPixelToPosition(point); + double Distance = AirportCPos.DistanceTo(CursorPos); + double Bearing = AirportCPos.DirectionTo(CursorPos); + + Gdiplus::Pen WhitePen(Color::White); + graphics.DrawEllipse(&WhitePen, point.x - 5, point.y - 5, 10, 10); + + Distance = Distance / 0.00053996f; + + Distance = round(Distance * 10) / 10; + + Bearing = round(Bearing * 10) / 10; + + POINT TextPos = {point.x + 20, point.y}; + + if (!DistanceToolActive) { + string distances = std::to_string(Distance); + size_t decimal_pos = distances.find("."); + distances = distances.substr(0, decimal_pos + 2); + + string bearings = std::to_string(Bearing); + decimal_pos = bearings.find("."); + bearings = bearings.substr(0, decimal_pos + 2); + + string text = bearings; + text += "° / "; + text += distances; + text += "m"; + COLORREF old_color = dc.SetTextColor(RGB(255, 255, 255)); + dc.TextOutA(TextPos.x, TextPos.y, text.c_str()); + dc.SetTextColor(old_color); + } + + dc.SelectObject(oldPen); + RequestRefresh(); + } + + // Distance tools here + for (auto &&kv : DistanceTools) { + CRadarTarget one = GetPlugIn()->RadarTargetSelect(kv.first.c_str()); + CRadarTarget two = GetPlugIn()->RadarTargetSelect(kv.second.c_str()); + + if (!isVisible(one) || !isVisible(two)) + continue; + + CPen Pen(PS_SOLID, 1, RGB(255, 255, 255)); + CPen *oldPen = dc.SelectObject(&Pen); + + POINT onePoint = + ConvertCoordFromPositionToPixel(one.GetPosition().GetPosition()); + POINT twoPoint = + ConvertCoordFromPositionToPixel(two.GetPosition().GetPosition()); + + dc.MoveTo(onePoint); + dc.LineTo(twoPoint); + + POINT TextPos = {twoPoint.x + 20, twoPoint.y}; + + double Distance = one.GetPosition().GetPosition().DistanceTo( + two.GetPosition().GetPosition()); + double Bearing = one.GetPosition().GetPosition().DirectionTo( + two.GetPosition().GetPosition()); + + string distances = std::to_string(Distance); + size_t decimal_pos = distances.find("."); + distances = distances.substr(0, decimal_pos + 2); + + string bearings = std::to_string(Bearing); + decimal_pos = bearings.find("."); + bearings = bearings.substr(0, decimal_pos + 2); + + string text = bearings; + text += "° / "; + text += distances; + text += "nm"; + COLORREF old_color = dc.SetTextColor(RGB(0, 0, 0)); + + CRect ClickableRect = {TextPos.x - 2, TextPos.y, + TextPos.x + dc.GetTextExtent(text.c_str()).cx + 2, + TextPos.y + dc.GetTextExtent(text.c_str()).cy}; + graphics.FillRectangle(&SolidBrush(Color(127, 122, 122)), + CopyRect(ClickableRect)); + dc.Draw3dRect(ClickableRect, RGB(75, 75, 75), RGB(45, 45, 45)); + dc.TextOutA(TextPos.x, TextPos.y, text.c_str()); + + AddScreenObject(RIMCAS_DISTANCE_TOOL, + string(kv.first + "," + kv.second).c_str(), ClickableRect, + false, ""); + + dc.SetTextColor(old_color); + + dc.SelectObject(oldPen); + } + + //--------------------------------- + // Drawing the toolbar + //--------------------------------- + + Logger::info("Menu Bar"); + + COLORREF qToolBarColor = RGB(127, 122, 122); + + // Drawing the toolbar on the top + CRect ToolBarAreaTop(RadarArea.left, RadarArea.top, RadarArea.right, + RadarArea.top + 20); + dc.FillSolidRect(ToolBarAreaTop, qToolBarColor); + + COLORREF oldTextColor = dc.SetTextColor(RGB(0, 0, 0)); + + int offset = 2; + dc.TextOutA(ToolBarAreaTop.left + offset, ToolBarAreaTop.top + 4, + getActiveAirport().c_str()); + AddScreenObject(RIMCAS_ACTIVE_AIRPORT, "ActiveAirport", + {ToolBarAreaTop.left + offset, ToolBarAreaTop.top + 4, + ToolBarAreaTop.left + offset + + dc.GetTextExtent(getActiveAirport().c_str()).cx, + ToolBarAreaTop.top + 4 + + dc.GetTextExtent(getActiveAirport().c_str()).cy}, + false, "Active Airport"); + + offset += dc.GetTextExtent(getActiveAirport().c_str()).cx + 10; + dc.TextOutA(ToolBarAreaTop.left + offset, ToolBarAreaTop.top + 4, "Display"); + AddScreenObject( + RIMCAS_MENU, "DisplayMenu", + {ToolBarAreaTop.left + offset, ToolBarAreaTop.top + 4, + ToolBarAreaTop.left + offset + dc.GetTextExtent("Display").cx, + ToolBarAreaTop.top + 4 + dc.GetTextExtent("Display").cy}, + false, "Display menu"); + + offset += dc.GetTextExtent("Display").cx + 10; + dc.TextOutA(ToolBarAreaTop.left + offset, ToolBarAreaTop.top + 4, "Target"); + AddScreenObject(RIMCAS_MENU, "TargetMenu", + {ToolBarAreaTop.left + offset, ToolBarAreaTop.top + 4, + ToolBarAreaTop.left + offset + dc.GetTextExtent("Target").cx, + ToolBarAreaTop.top + 4 + dc.GetTextExtent("Target").cy}, + false, "Target menu"); + + offset += dc.GetTextExtent("Target").cx + 10; + dc.TextOutA(ToolBarAreaTop.left + offset, ToolBarAreaTop.top + 4, "Colours"); + AddScreenObject(RIMCAS_MENU, "ColourMenu", + {ToolBarAreaTop.left + offset, ToolBarAreaTop.top + 4, + ToolBarAreaTop.left + offset + dc.GetTextExtent("Colour").cx, + ToolBarAreaTop.top + 4 + dc.GetTextExtent("Colour").cy}, + false, "Colour menu"); + + offset += dc.GetTextExtent("Colours").cx + 10; + dc.TextOutA(ToolBarAreaTop.left + offset, ToolBarAreaTop.top + 4, "Alerts"); + AddScreenObject(RIMCAS_MENU, "RIMCASMenu", + {ToolBarAreaTop.left + offset, ToolBarAreaTop.top + 4, + ToolBarAreaTop.left + offset + dc.GetTextExtent("Alerts").cx, + ToolBarAreaTop.top + 4 + +dc.GetTextExtent("Alerts").cy}, + false, "RIMCAS menu"); + + offset += dc.GetTextExtent("Alerts").cx + 10; + dc.TextOutA(ToolBarAreaTop.left + offset, ToolBarAreaTop.top + 4, "/"); + CRect barDistanceRect = { + ToolBarAreaTop.left + offset - 2, ToolBarAreaTop.top + 4, + ToolBarAreaTop.left + offset + dc.GetTextExtent("/").cx, + ToolBarAreaTop.top + 4 + +dc.GetTextExtent("/").cy}; + if (DistanceToolActive) { + graphics.DrawRectangle(&Pen(Color::White), CopyRect(barDistanceRect)); + } + AddScreenObject(RIMCAS_MENU, "/", barDistanceRect, false, "Distance tool"); + + dc.SetTextColor(oldTextColor); + + // + // Tag deconflicting + // + + Logger::info("Tag deconfliction loop"); + + for (const auto areas : tagAreas) { + if (!CurrentConfig->getActiveProfile()["labels"]["auto_deconfliction"] + .GetBool()) + break; + + if (TagsOffsets.find(areas.first) != TagsOffsets.end()) + continue; + + if (IsTagBeingDragged(areas.first)) + continue; + + if (RecentlyAutoMovedTags.find(areas.first) != + RecentlyAutoMovedTags.end()) { + double t = (double)clock() - + RecentlyAutoMovedTags[areas.first] / ((double)CLOCKS_PER_SEC); + if (t >= 0.8) { + RecentlyAutoMovedTags.erase(areas.first); + } else { + continue; + } + } + + // We need to see wether the rotation will be clockwise or anti-clockwise + + bool isAntiClockwise = false; + + for (const auto area2 : tagAreas) { + if (areas.first == area2.first) + continue; + + if (IsTagBeingDragged(area2.first)) + continue; + + CRect h; + + if (h.IntersectRect(tagAreas[areas.first], area2.second)) { + if (areas.second.left <= area2.second.left) { + isAntiClockwise = true; + } + + break; + } + } + + // We then rotate the tags until we did a 360 or there is no more conflicts + + POINT acPosPix = ConvertCoordFromPositionToPixel( + GetPlugIn() + ->RadarTargetSelect(areas.first.c_str()) + .GetPosition() + .GetPosition()); + int lenght = LeaderLineDefaultlenght; + if (TagLeaderLineLength.find(areas.first) != TagLeaderLineLength.end()) + lenght = TagLeaderLineLength[areas.first]; + + int width = areas.second.Width(); + int height = areas.second.Height(); + + for (double rotated = 0.0; abs(rotated) <= 360.0;) { + // We first rotate the tag + double newangle = fmod(TagAngles[areas.first] + rotated, 360.0f); + + POINT TagCenter; + TagCenter.x = long(acPosPix.x + float(lenght * cos(DegToRad(newangle)))); + TagCenter.y = long(acPosPix.y + float(lenght * sin(DegToRad(newangle)))); + + CRect NewRectangle(TagCenter.x - (width / 2), TagCenter.y - (height / 2), + TagCenter.x + (width / 2), TagCenter.y + (height / 2)); + NewRectangle.NormalizeRect(); + + // Assume there is no conflict, then try again + + bool isTagConflicing = false; + + for (const auto area2 : tagAreas) { + if (areas.first == area2.first) + continue; + + if (IsTagBeingDragged(area2.first)) + continue; - if (!isTagConflicing) - { - TagAngles[areas.first] = fmod(TagAngles[areas.first] + rotated, 360); - tagAreas[areas.first] = NewRectangle; - RecentlyAutoMovedTags[areas.first] = clock(); - break; - } + CRect h; + + if (h.IntersectRect(NewRectangle, area2.second)) { + isTagConflicing = true; + break; + } + } - if (isAntiClockwise) - rotated -= 22.5f; - else - rotated += 22.5f; - } - } + if (!isTagConflicing) { + TagAngles[areas.first] = fmod(TagAngles[areas.first] + rotated, 360); + tagAreas[areas.first] = NewRectangle; + RecentlyAutoMovedTags[areas.first] = clock(); + break; + } - // - // App windows - // + if (isAntiClockwise) + rotated -= 22.5f; + else + rotated += 22.5f; + } + } - Logger::info("App window rendering"); + // + // App windows + // - for (std::map::iterator it = appWindowDisplays.begin(); it != appWindowDisplays.end(); ++it) - { - if (!it->second) - continue; + Logger::info("App window rendering"); - int appWindowId = it->first; - appWindows[appWindowId]->render(hDC, this, &graphics, mouseLocation, DistanceTools); - } + for (std::map::iterator it = appWindowDisplays.begin(); + it != appWindowDisplays.end(); ++it) { + if (!it->second) + continue; - dc.Detach(); + int appWindowId = it->first; + appWindows[appWindowId]->render(hDC, this, &graphics, mouseLocation, + DistanceTools); + } - Logger::info("END "+ string(__FUNCSIG__)); + dc.Detach(); + Logger::info("END " + string(__FUNCSIG__)); } // ReSharper restore CppMsExtAddressOfClassRValue //---EuroScopePlugInExitCustom----------------------------------------------- -void CSMRRadar::EuroScopePlugInExitCustom() -{ - AFX_MANAGE_STATE(AfxGetStaticModuleState()) +void CSMRRadar::EuroScopePlugInExitCustom() { + AFX_MANAGE_STATE(AfxGetStaticModuleState()) - if (smrCursor != nullptr && smrCursor != NULL) - { - SetWindowLong(pluginWindow, GWL_WNDPROC, (LONG)gSourceProc); - } + if (smrCursor != nullptr && smrCursor != NULL) { + SetWindowLong(pluginWindow, GWL_WNDPROC, (LONG)gSourceProc); + } } diff --git a/vSMR/SMRRadar.hpp b/vSMR/SMRRadar.hpp index 19c3f732e..c8a4f96c1 100644 --- a/vSMR/SMRRadar.hpp +++ b/vSMR/SMRRadar.hpp @@ -1,390 +1,415 @@ #pragma once #include -#include -#include -#include +#include #include +#include +#include #include -#include +#include + #define _USE_MATH_DEFINES -#include -#include "Constant.hpp" +#include "AircraftTypeLookup.hpp" #include "CallsignLookup.hpp" +#include "ColorManager.h" #include "Config.hpp" -#include "Rimcas.hpp" +#include "Constant.hpp" +#include "HttpHelper.hpp" #include "InsetWindow.h" -#include -#include -#include -#include "ColorManager.h" #include "Logger.h" +#include "Rimcas.hpp" +#include #include #include -#include "AircraftTypeLookup.hpp" -#include "HttpHelper.hpp" +#include +#include +#include + using namespace std; using namespace Gdiplus; using namespace EuroScopePlugIn; namespace fs = std::filesystem; -namespace SMRSharedData -{ - static vector ReleasedTracks; - static vector ManuallyCorrelated; -}; - +namespace SMRSharedData { +static vector ReleasedTracks; +static vector ManuallyCorrelated; +}; // namespace SMRSharedData -namespace SMRPluginSharedData -{ - static asio::io_service io_service; +namespace SMRPluginSharedData { +static asio::io_service io_service; } using namespace SMRSharedData; -class CSMRRadar : - public EuroScopePlugIn::CRadarScreen -{ +class CSMRRadar : public EuroScopePlugIn::CRadarScreen { public: - CSMRRadar(); - virtual ~CSMRRadar(); + CSMRRadar(); + virtual ~CSMRRadar(); - static map vStripsStands; + static map vStripsStands; - bool BLINK = false; + bool BLINK = false; - map TagsOffsets; + map TagsOffsets; - vector Active_Arrivals; + vector Active_Arrivals; - clock_t clock_init, clock_final; + clock_t clock_init, clock_final; - COLORREF SMR_TARGET_COLOR = RGB(255, 242, 73); - COLORREF SMR_H1_COLOR = RGB(0, 255, 255); - COLORREF SMR_H2_COLOR = RGB(0, 219, 219); - COLORREF SMR_H3_COLOR = RGB(0, 183, 183); + COLORREF SMR_TARGET_COLOR = RGB(255, 242, 73); + COLORREF SMR_H1_COLOR = RGB(0, 255, 255); + COLORREF SMR_H2_COLOR = RGB(0, 219, 219); + COLORREF SMR_H3_COLOR = RGB(0, 183, 183); - typedef struct tagPOINT2 { - double x; - double y; - } POINT2; + typedef struct tagPOINT2 { + double x; + double y; + } POINT2; - struct Patatoide_Points { - map points; - map History_one_points; - map History_two_points; - map History_three_points; - }; + struct Patatoide_Points { + map points; + map History_one_points; + map History_two_points; + map History_three_points; + }; - map Patatoides; + map Patatoides; - map ClosedRunway; + map ClosedRunway; - char DllPathFile[_MAX_PATH]; - string DllPath; - string ConfigPath; - CCallsignLookup * Callsigns = nullptr; - CAircraftTypeLookup * AircraftTypes = nullptr; - CColorManager * ColorManager; + char DllPathFile[_MAX_PATH]; + string DllPath; + string ConfigPath; + CCallsignLookup *Callsigns = nullptr; + CAircraftTypeLookup *AircraftTypes = nullptr; + CColorManager *ColorManager; - map ShowLists; - map ListAreas; + map ShowLists; + map ListAreas; - map appWindowDisplays; - bool wipAreasActive = true; + map appWindowDisplays; + bool wipAreasActive = true; - map tagAreas; - map TagAngles; - map TagLeaderLineLength; + map tagAreas; + map TagAngles; + map TagLeaderLineLength; - bool QDMenabled = false; - bool QDMSelectEnabled = false; - POINT QDMSelectPt; - POINT QDMmousePt; + bool QDMenabled = false; + bool QDMSelectEnabled = false; + POINT QDMSelectPt; + POINT QDMmousePt; - bool ColorSettingsDay = true; + bool ColorSettingsDay = true; - bool isLVP = false; - bool detailedRIMCAS = true; + bool isLVP = false; + bool detailedRIMCAS = true; - map TimePopupAreas; + // Departure Timer Window settings + bool showDepartureWindow = true; + int departureDisplayDuration = 180; // 3 minutes in seconds (default) + bool depWindowShowCallsign = true; + bool depWindowShowDest = true; + bool depWindowShowAcType = true; + bool depWindowShowWake = true; + bool depWindowShowFreq = true; + bool depWindowShowTime = true; + RECT DepartureWindowArea = {500, 300, 700, 400}; - map TimePopupData; - multimap AcOnRunway; - map ColorAC; + map TimePopupAreas; - map RunwayAreas; + map TimePopupData; + multimap AcOnRunway; + map ColorAC; - map MenuPositions; - map DisplayMenu; + map RunwayAreas; - map RecentlyAutoMovedTags; + map MenuPositions; + map DisplayMenu; - CRimcas * RimcasInstance = nullptr; - CConfig * CurrentConfig = nullptr; + map RecentlyAutoMovedTags; - map customFonts; - int currentFontSize = 1; + CRimcas *RimcasInstance = nullptr; + CConfig *CurrentConfig = nullptr; - map AirportPositions; + map customFonts; + int currentFontSize = 1; - bool Afterglow = true; + map AirportPositions; - int Trail_Gnd = 4; - int Trail_App = 4; - int PredictedLength = 0; + bool Afterglow = true; - bool showAircraftType = true; - bool showSID = true; - bool showWakeTurb = true; + int Trail_Gnd = 4; + int Trail_App = 4; + int PredictedLength = 0; - bool NeedCorrelateCursor = false; - bool ReleaseInProgress = false; - bool AcquireInProgress = false; + bool showAircraftType = true; + bool showSID = true; + bool showWakeTurb = true; - multimap DistanceTools; - bool DistanceToolActive = false; - pair ActiveDistance; + bool NeedCorrelateCursor = false; + bool ReleaseInProgress = false; + bool AcquireInProgress = false; - vector> wipAreas; + multimap DistanceTools; + bool DistanceToolActive = false; + pair ActiveDistance; - //---- - // Tag types - //--- + vector> wipAreas; - enum TagTypes { Departure, Arrival, Airborne, Uncorrelated }; + //---- + // Tag types + //--- + enum TagTypes { Departure, Arrival, Airborne, Uncorrelated }; - string ActiveAirport = "EGKK"; + string ActiveAirport = "EGKK"; - inline string getActiveAirport() { - return ActiveAirport; - } + inline string getActiveAirport() { return ActiveAirport; } - inline string setActiveAirport(string value) { - return ActiveAirport = value; - } + inline string setActiveAirport(string value) { return ActiveAirport = value; } - //---GenerateTagData-------------------------------------------- + //---GenerateTagData-------------------------------------------- - static map GenerateTagData(CRadarTarget Rt, CFlightPlan fp, bool isAcCorrelated, bool isProMode, int TransitionAltitude, bool useSpeedForGates, string ActiveAirport, bool showAircraftType, bool showSID, bool showWakeTurb); + static map + GenerateTagData(CRadarTarget Rt, CFlightPlan fp, bool isAcCorrelated, + bool isProMode, int TransitionAltitude, bool useSpeedForGates, + string ActiveAirport, bool showAircraftType, bool showSID, + bool showWakeTurb); - //---IsCorrelatedFuncs--------------------------------------------- + //---IsCorrelatedFuncs--------------------------------------------- - inline virtual bool IsCorrelated(CFlightPlan fp, CRadarTarget rt) - { + inline virtual bool IsCorrelated(CFlightPlan fp, CRadarTarget rt) { - if (CurrentConfig->getActiveProfile()["filters"]["pro_mode"]["enable"].GetBool()) - { - if (fp.IsValid()) - { - bool isCorr = false; - if (strcmp(fp.GetControllerAssignedData().GetSquawk(), rt.GetPosition().GetSquawk()) == 0) - { - isCorr = true; - } + if (CurrentConfig->getActiveProfile()["filters"]["pro_mode"]["enable"] + .GetBool()) { + if (fp.IsValid()) { + bool isCorr = false; + if (strcmp(fp.GetControllerAssignedData().GetSquawk(), + rt.GetPosition().GetSquawk()) == 0) { + isCorr = true; + } - if (CurrentConfig->getActiveProfile()["filters"]["pro_mode"]["accept_pilot_squawk"].GetBool()) - { - isCorr = true; - } + if (CurrentConfig + ->getActiveProfile()["filters"]["pro_mode"] + ["accept_pilot_squawk"] + .GetBool()) { + isCorr = true; + } - if (isCorr) - { - const Value& sqs = CurrentConfig->getActiveProfile()["filters"]["pro_mode"]["do_not_autocorrelate_squawks"]; - for (SizeType i = 0; i < sqs.Size(); i++) { - if (strcmp(rt.GetPosition().GetSquawk(), sqs[i].GetString()) == 0) - { - isCorr = false; - break; - } - } - } + if (isCorr) { + const Value &sqs = + CurrentConfig->getActiveProfile()["filters"]["pro_mode"] + ["do_not_autocorrelate_squawks"]; + for (SizeType i = 0; i < sqs.Size(); i++) { + if (strcmp(rt.GetPosition().GetSquawk(), sqs[i].GetString()) == 0) { + isCorr = false; + break; + } + } + } - if (std::find(ManuallyCorrelated.begin(), ManuallyCorrelated.end(), rt.GetSystemID()) != ManuallyCorrelated.end()) - { - isCorr = true; - } + if (std::find(ManuallyCorrelated.begin(), ManuallyCorrelated.end(), + rt.GetSystemID()) != ManuallyCorrelated.end()) { + isCorr = true; + } - if (std::find(ReleasedTracks.begin(), ReleasedTracks.end(), rt.GetSystemID()) != ReleasedTracks.end()) - { - isCorr = false; - } + if (std::find(ReleasedTracks.begin(), ReleasedTracks.end(), + rt.GetSystemID()) != ReleasedTracks.end()) { + isCorr = false; + } - return isCorr; - } + return isCorr; + } - return false; - } else - { - // If the pro mode is not used, then the AC is always correlated - return true; - } - }; + return false; + } else { + // If the pro mode is not used, then the AC is always correlated + return true; + } + }; - //---CorrelateCursor-------------------------------------------- + //---CorrelateCursor-------------------------------------------- - virtual void CorrelateCursor(); + virtual void CorrelateCursor(); - //---LoadCustomFont-------------------------------------------- + //---LoadCustomFont-------------------------------------------- - virtual void LoadCustomFont(); + virtual void LoadCustomFont(); - //---LoadProfile-------------------------------------------- + //---LoadProfile-------------------------------------------- - virtual void LoadProfile(string profileName); + virtual void LoadProfile(string profileName); - //---OnAsrContentLoaded-------------------------------------------- + //---OnAsrContentLoaded-------------------------------------------- - virtual void OnAsrContentLoaded(bool Loaded); + virtual void OnAsrContentLoaded(bool Loaded); - //---OnAsrContentToBeSaved------------------------------------------ + //---OnAsrContentToBeSaved------------------------------------------ - virtual void OnAsrContentToBeSaved(); + virtual void OnAsrContentToBeSaved(); - //---OnRefresh------------------------------------------------------ + //---OnRefresh------------------------------------------------------ - virtual void OnRefresh(HDC hDC, int Phase); + virtual void OnRefresh(HDC hDC, int Phase); - //---OnClickScreenObject----------------------------------------- + //---OnClickScreenObject----------------------------------------- - virtual void OnClickScreenObject(int ObjectType, const char * sObjectId, POINT Pt, RECT Area, int Button); + virtual void OnClickScreenObject(int ObjectType, const char *sObjectId, + POINT Pt, RECT Area, int Button); - //---OnMoveScreenObject--------------------------------------------- + //---OnMoveScreenObject--------------------------------------------- - virtual void OnMoveScreenObject(int ObjectType, const char * sObjectId, POINT Pt, RECT Area, bool Released); + virtual void OnMoveScreenObject(int ObjectType, const char *sObjectId, + POINT Pt, RECT Area, bool Released); - //---OnOverScreenObject--------------------------------------------- + //---OnOverScreenObject--------------------------------------------- - virtual void OnOverScreenObject(int ObjectType, const char * sObjectId, POINT Pt, RECT Area); + virtual void OnOverScreenObject(int ObjectType, const char *sObjectId, + POINT Pt, RECT Area); - //---OnCompileCommand----------------------------------------- + //---OnCompileCommand----------------------------------------- - virtual bool OnCompileCommand(const char * sCommandLine); + virtual bool OnCompileCommand(const char *sCommandLine); - //---RefreshAirportActivity--------------------------------------------- + //---RefreshAirportActivity--------------------------------------------- - virtual void RefreshAirportActivity(void); + virtual void RefreshAirportActivity(void); - //---OnRadarTargetPositionUpdate--------------------------------------------- + //---OnRadarTargetPositionUpdate--------------------------------------------- - virtual void OnRadarTargetPositionUpdate(CRadarTarget RadarTarget); + virtual void OnRadarTargetPositionUpdate(CRadarTarget RadarTarget); - //---OnFlightPlanDisconnect--------------------------------------------- + //---OnFlightPlanDisconnect--------------------------------------------- - virtual void OnFlightPlanDisconnect(CFlightPlan FlightPlan); + virtual void OnFlightPlanDisconnect(CFlightPlan FlightPlan); - virtual bool isVisible(CRadarTarget rt) - { - CRadarTargetPositionData RtPos = rt.GetPosition(); - int radarRange = CurrentConfig->getActiveProfile()["filters"]["radar_range_nm"].GetInt(); - int altitudeFilter = CurrentConfig->getActiveProfile()["filters"]["hide_above_alt"].GetInt(); - int speedFilter = CurrentConfig->getActiveProfile()["filters"]["hide_above_spd"].GetInt(); - bool isAcDisplayed = true; + virtual bool isVisible(CRadarTarget rt) { + CRadarTargetPositionData RtPos = rt.GetPosition(); + int radarRange = + CurrentConfig->getActiveProfile()["filters"]["radar_range_nm"].GetInt(); + int altitudeFilter = + CurrentConfig->getActiveProfile()["filters"]["hide_above_alt"].GetInt(); + int speedFilter = + CurrentConfig->getActiveProfile()["filters"]["hide_above_spd"].GetInt(); + bool isAcDisplayed = true; - if (AirportPositions[getActiveAirport()].DistanceTo(RtPos.GetPosition()) > radarRange) - isAcDisplayed = false; + if (AirportPositions[getActiveAirport()].DistanceTo(RtPos.GetPosition()) > + radarRange) + isAcDisplayed = false; - if (altitudeFilter != 0) { - if (RtPos.GetPressureAltitude() > altitudeFilter) - isAcDisplayed = false; - } + if (altitudeFilter != 0) { + if (RtPos.GetPressureAltitude() > altitudeFilter) + isAcDisplayed = false; + } - if (speedFilter != 0) { - if (RtPos.GetReportedGS() > speedFilter) - isAcDisplayed = false; - } + if (speedFilter != 0) { + if (RtPos.GetReportedGS() > speedFilter) + isAcDisplayed = false; + } - return isAcDisplayed; - } + return isAcDisplayed; + } - //---Haversine--------------------------------------------- - // Heading in deg, distance in m - const double PI = (double)M_PI; + //---Haversine--------------------------------------------- + // Heading in deg, distance in m + const double PI = (double)M_PI; - inline virtual CPosition Haversine(CPosition origin, double heading, double distance) { + inline virtual CPosition Haversine(CPosition origin, double heading, + double distance) { - CPosition newPos; + CPosition newPos; - double d = (distance*0.00053996) / 60 * PI / 180; - double trk = DegToRad(heading); - double lat0 = DegToRad(origin.m_Latitude); - double lon0 = DegToRad(origin.m_Longitude); + double d = (distance * 0.00053996) / 60 * PI / 180; + double trk = DegToRad(heading); + double lat0 = DegToRad(origin.m_Latitude); + double lon0 = DegToRad(origin.m_Longitude); - double lat = asin(sin(lat0) * cos(d) + cos(lat0) * sin(d) * cos(trk)); - double lon = cos(lat) == 0 ? lon0 : fmod(lon0 + asin(sin(trk) * sin(d) / cos(lat)) + PI, 2 * PI) - PI; + double lat = asin(sin(lat0) * cos(d) + cos(lat0) * sin(d) * cos(trk)); + double lon = + cos(lat) == 0 + ? lon0 + : fmod(lon0 + asin(sin(trk) * sin(d) / cos(lat)) + PI, 2 * PI) - PI; - newPos.m_Latitude = RadToDeg(lat); - newPos.m_Longitude = RadToDeg(lon); + newPos.m_Latitude = RadToDeg(lat); + newPos.m_Longitude = RadToDeg(lon); - return newPos; - } + return newPos; + } - inline virtual float randomizeHeading(float originHead) { - return float(fmod(originHead + float((rand() % 5) - 2), 360)); - } + inline virtual float randomizeHeading(float originHead) { + return float(fmod(originHead + float((rand() % 5) - 2), 360)); + } - // IsPointInPolygon + // IsPointInPolygon - static bool IsPointInPolygon(const CPosition& point, const std::vector& polygon) { - int n = polygon.size(); - bool inside = false; + static bool IsPointInPolygon(const CPosition &point, + const std::vector &polygon) { + int n = polygon.size(); + bool inside = false; - for (int i = 0, j = n - 1; i < n; j = i++) { - if (((polygon[i].m_Longitude > point.m_Longitude) != (polygon[j].m_Longitude > point.m_Longitude)) && - (point.m_Latitude < (polygon[j].m_Latitude - polygon[i].m_Latitude) * (point.m_Longitude - polygon[i].m_Longitude) / (polygon[j].m_Longitude - polygon[i].m_Longitude) + polygon[i].m_Latitude)) { - inside = !inside; - } - } + for (int i = 0, j = n - 1; i < n; j = i++) { + if (((polygon[i].m_Longitude > point.m_Longitude) != + (polygon[j].m_Longitude > point.m_Longitude)) && + (point.m_Latitude < + (polygon[j].m_Latitude - polygon[i].m_Latitude) * + (point.m_Longitude - polygon[i].m_Longitude) / + (polygon[j].m_Longitude - polygon[i].m_Longitude) + + polygon[i].m_Latitude)) { + inside = !inside; + } + } - return inside; - } + return inside; + } - //---GetBottomLine--------------------------------------------- + //---GetBottomLine--------------------------------------------- - virtual string GetBottomLine(const char * Callsign); + virtual string GetBottomLine(const char *Callsign); - //---LineIntersect--------------------------------------------- + //---LineIntersect--------------------------------------------- - /*inline virtual POINT getIntersectionPoint(POINT lineA, POINT lineB, POINT lineC, POINT lineD) { + /*inline virtual POINT getIntersectionPoint(POINT lineA, POINT lineB, POINT + lineC, POINT lineD) { - double x1 = lineA.x; - double y1 = lineA.y; - double x2 = lineB.x; - double y2 = lineB.y; + double x1 = lineA.x; + double y1 = lineA.y; + double x2 = lineB.x; + double y2 = lineB.y; - double x3 = lineC.x; - double y3 = lineC.y; - double x4 = lineD.x; - double y4 = lineD.y; + double x3 = lineC.x; + double y3 = lineC.y; + double x4 = lineD.x; + double y4 = lineD.y; - POINT p = { 0, 0 }; + POINT p = { 0, 0 }; - double d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4); - if (d != 0) { - double xi = ((x3 - x4) * (x1 * y2 - y1 * x2) - (x1 - x2) * (x3 * y4 - y3 * x4)) / d; - double yi = ((y3 - y4) * (x1 * y2 - y1 * x2) - (y1 - y2) * (x3 * y4 - y3 * x4)) / d; + double d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4); + if (d != 0) { + double xi = ((x3 - x4) * (x1 * y2 - y1 * x2) - (x1 - x2) * (x3 + * y4 - y3 * x4)) / d; double yi = ((y3 - y4) * (x1 * y2 - y1 * x2) - (y1 - y2) + * (x3 * y4 - y3 * x4)) / d; - p = { (int)xi, (int)yi }; + p = { (int)xi, (int)yi }; - } - return p; - }*/ + } + return p; + }*/ - //---OnFunctionCall------------------------------------------------- + //---OnFunctionCall------------------------------------------------- - virtual void OnFunctionCall(int FunctionId, const char * sItemString, POINT Pt, RECT Area); + virtual void OnFunctionCall(int FunctionId, const char *sItemString, POINT Pt, + RECT Area); - //---OnAsrContentToBeClosed----------------------------------------- + //---OnAsrContentToBeClosed----------------------------------------- - void CSMRRadar::EuroScopePlugInExitCustom(); + void CSMRRadar::EuroScopePlugInExitCustom(); - // This gets called before OnAsrContentToBeSaved() - // -> we can't delete CurrentConfig just yet otherwise we can't save the active profile - inline virtual void OnAsrContentToBeClosed(void) - { - delete RimcasInstance; - //delete CurrentConfig; - delete this; - }; + // This gets called before OnAsrContentToBeSaved() + // -> we can't delete CurrentConfig just yet otherwise we can't save the + // active profile + inline virtual void OnAsrContentToBeClosed(void) { + delete RimcasInstance; + // delete CurrentConfig; + delete this; + }; }; From 046dd8c6aae01cddfb6c4a3cd8050a6ac7453c92 Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 12:18:51 +0000 Subject: [PATCH 02/44] correct includes --- vSMR/Rimcas.cpp | 2 ++ vSMR/SMRRadar.cpp | 14 +++++++------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/vSMR/Rimcas.cpp b/vSMR/Rimcas.cpp index 69819f618..d05868265 100644 --- a/vSMR/Rimcas.cpp +++ b/vSMR/Rimcas.cpp @@ -1,4 +1,6 @@ #include "Rimcas.hpp" +#include "stdafx.h" + CRimcas::CRimcas() {} diff --git a/vSMR/SMRRadar.cpp b/vSMR/SMRRadar.cpp index 96477cce5..6fe4591d8 100644 --- a/vSMR/SMRRadar.cpp +++ b/vSMR/SMRRadar.cpp @@ -2,7 +2,6 @@ #include "Resource.h" #include "stdafx.h" - ULONG_PTR m_gdiplusToken; CPoint mouseLocation(0, 0); string TagBeingDragged; @@ -2104,14 +2103,14 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { // closed runway // if //(CurrentConfig->isCustomRunwayAvail(getActiveAirport(), runway_name, - //runway_name2)) { const Value& Runways = CustomMap["runways"]; + // runway_name2)) { const Value& Runways = CustomMap["runways"]; // // if (Runways.IsArray()) { // for (SizeType i = 0; i < Runways.Size(); i++) { // if (startsWith(runway_name.c_str(), - //Runways[i]["runway_name"].GetString()) || + // Runways[i]["runway_name"].GetString()) || // startsWith(runway_name2.c_str(), - //Runways[i]["runway_name"].GetString())) { + // Runways[i]["runway_name"].GetString())) { // // string path_name = "path"; // @@ -2119,10 +2118,11 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { // path_name = "path_lvp"; // // const Value& Path = - //Runways[i][path_name.c_str()]; for (SizeType j = 0; j < Path.Size(); - //j++) { CPosition position; + // Runways[i][path_name.c_str()]; for + // (SizeType j = 0; j < Path.Size(); j++) { + // CPosition position; // position.LoadFromStrings(Path[j][(SizeType)1].GetString(), - //Path[j][(SizeType)0].GetString()); + // Path[j][(SizeType)0].GetString()); // // def.push_back(position); // } From cb76a999f892cce20c5ec5bb7c0f82fa854f5523 Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 12:23:23 +0000 Subject: [PATCH 03/44] correct include order --- vSMR/Rimcas.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vSMR/Rimcas.cpp b/vSMR/Rimcas.cpp index d05868265..d446f8fa2 100644 --- a/vSMR/Rimcas.cpp +++ b/vSMR/Rimcas.cpp @@ -1,6 +1,6 @@ -#include "Rimcas.hpp" #include "stdafx.h" +#include "Rimcas.hpp" CRimcas::CRimcas() {} From 7743b7634a9de63a380556f36dc8ecb1d37cd282 Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 12:26:28 +0000 Subject: [PATCH 04/44] oops --- vSMR/SMRRadar.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/vSMR/SMRRadar.cpp b/vSMR/SMRRadar.cpp index 6fe4591d8..81b640f0a 100644 --- a/vSMR/SMRRadar.cpp +++ b/vSMR/SMRRadar.cpp @@ -1,7 +1,9 @@ -#include "SMRRadar.hpp" -#include "Resource.h" #include "stdafx.h" +#include "Resource.h" +#include "SMRRadar.hpp" + + ULONG_PTR m_gdiplusToken; CPoint mouseLocation(0, 0); string TagBeingDragged; From 74dc6c8192f454f88266eddc67b51c7f438ba86b Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 12:31:41 +0000 Subject: [PATCH 05/44] fix circular import --- vSMR/InsetWindow.cpp | 1665 +++++++++++++++++++++++------------------- vSMR/InsetWindow.h | 56 +- 2 files changed, 930 insertions(+), 791 deletions(-) diff --git a/vSMR/InsetWindow.cpp b/vSMR/InsetWindow.cpp index 0a22c79af..fe168f9a8 100644 --- a/vSMR/InsetWindow.cpp +++ b/vSMR/InsetWindow.cpp @@ -1,779 +1,914 @@ #include "stdafx.h" -#include "InsetWindow.h" - - -CInsetWindow::CInsetWindow(int Id) -{ - m_Id = Id; -} - -CInsetWindow::~CInsetWindow() -{ -} - -void CInsetWindow::setAirport(string icao) -{ - this->icao = icao; -} -void CInsetWindow::OnClickScreenObject(const char * sItemString, POINT Pt, int Button) -{ - if (Button == EuroScopePlugIn::BUTTON_RIGHT) - { - if (m_TagAngles.find(sItemString) != m_TagAngles.end()) - { - m_TagAngles[sItemString] = fmod(m_TagAngles[sItemString] + 45.0, 360.0); - } else - { - m_TagAngles[sItemString] = 45.0; - } - } - - if (Button == EuroScopePlugIn::BUTTON_LEFT) - { - if (m_TagAngles.find(sItemString) != m_TagAngles.end()) - { - m_TagAngles[sItemString] = fmod(m_TagAngles[sItemString] - 45.0, 360.0); - } - else - { - m_TagAngles[sItemString] = 45.0; - } - } +#include "InsetWindow.h" +#include "SMRRadar.hpp" + +CInsetWindow::CInsetWindow(int Id) { m_Id = Id; } + +CInsetWindow::~CInsetWindow() {} + +void CInsetWindow::setAirport(string icao) { this->icao = icao; } + +void CInsetWindow::OnClickScreenObject(const char *sItemString, POINT Pt, + int Button) { + if (Button == EuroScopePlugIn::BUTTON_RIGHT) { + if (m_TagAngles.find(sItemString) != m_TagAngles.end()) { + m_TagAngles[sItemString] = fmod(m_TagAngles[sItemString] + 45.0, 360.0); + } else { + m_TagAngles[sItemString] = 45.0; + } + } + + if (Button == EuroScopePlugIn::BUTTON_LEFT) { + if (m_TagAngles.find(sItemString) != m_TagAngles.end()) { + m_TagAngles[sItemString] = fmod(m_TagAngles[sItemString] - 45.0, 360.0); + } else { + m_TagAngles[sItemString] = 45.0; + } + } } -bool CInsetWindow::OnMoveScreenObject(const char * sObjectId, POINT Pt, RECT Area, bool Released) -{ - if (strcmp(sObjectId, "window") == 0) { - if (!this->m_Grip) - { - m_OffsetInit = m_Offset; - m_OffsetDrag = Pt; - m_Grip = true; - } - - POINT maxoffset = { (m_Area.right - m_Area.left) / 2, (m_Area.bottom - (m_Area.top + 15)) / 2 }; - m_Offset.x = max(-maxoffset.x, min(maxoffset.x, m_OffsetInit.x + (Pt.x - m_OffsetDrag.x))); - m_Offset.y = max(-maxoffset.y, min(maxoffset.y, m_OffsetInit.y + (Pt.y - m_OffsetDrag.y))); - - if (Released) - { - m_Grip = false; - } - } - if (strcmp(sObjectId, "resize") == 0) { - POINT TopLeft = { m_Area.left, m_Area.top }; - POINT BottomRight = { Area.right, Area.bottom }; - - CRect newSize(TopLeft, BottomRight); - newSize.NormalizeRect(); - - if (newSize.Height() < 100) { - newSize.top = m_Area.top; - newSize.bottom = m_Area.bottom; - } - - if (newSize.Width() < 300) { - newSize.left = m_Area.left; - newSize.right = m_Area.right; - } - - m_Area = newSize; - - return Released; - } - if (strcmp(sObjectId, "topbar") == 0) { - - CRect appWindowRect(m_Area); - appWindowRect.NormalizeRect(); - - POINT TopLeft = { Area.left, Area.bottom + 1 }; - POINT BottomRight = { TopLeft.x + appWindowRect.Width(), TopLeft.y + appWindowRect.Height() }; - CRect newPos(TopLeft, BottomRight); - newPos.NormalizeRect(); - - m_Area = newPos; - - return Released; - } - - return true; +bool CInsetWindow::OnMoveScreenObject(const char *sObjectId, POINT Pt, + RECT Area, bool Released) { + if (strcmp(sObjectId, "window") == 0) { + if (!this->m_Grip) { + m_OffsetInit = m_Offset; + m_OffsetDrag = Pt; + m_Grip = true; + } + + POINT maxoffset = {(m_Area.right - m_Area.left) / 2, + (m_Area.bottom - (m_Area.top + 15)) / 2}; + m_Offset.x = + max(-maxoffset.x, + min(maxoffset.x, m_OffsetInit.x + (Pt.x - m_OffsetDrag.x))); + m_Offset.y = + max(-maxoffset.y, + min(maxoffset.y, m_OffsetInit.y + (Pt.y - m_OffsetDrag.y))); + + if (Released) { + m_Grip = false; + } + } + if (strcmp(sObjectId, "resize") == 0) { + POINT TopLeft = {m_Area.left, m_Area.top}; + POINT BottomRight = {Area.right, Area.bottom}; + + CRect newSize(TopLeft, BottomRight); + newSize.NormalizeRect(); + + if (newSize.Height() < 100) { + newSize.top = m_Area.top; + newSize.bottom = m_Area.bottom; + } + + if (newSize.Width() < 300) { + newSize.left = m_Area.left; + newSize.right = m_Area.right; + } + + m_Area = newSize; + + return Released; + } + if (strcmp(sObjectId, "topbar") == 0) { + + CRect appWindowRect(m_Area); + appWindowRect.NormalizeRect(); + + POINT TopLeft = {Area.left, Area.bottom + 1}; + POINT BottomRight = {TopLeft.x + appWindowRect.Width(), + TopLeft.y + appWindowRect.Height()}; + CRect newPos(TopLeft, BottomRight); + newPos.NormalizeRect(); + + m_Area = newPos; + + return Released; + } + + return true; } -POINT CInsetWindow::projectPoint(CPosition pos) -{ - CRect areaRect(m_Area); - areaRect.NormalizeRect(); +POINT CInsetWindow::projectPoint(CPosition pos) { + CRect areaRect(m_Area); + areaRect.NormalizeRect(); - POINT refPt = areaRect.CenterPoint(); - refPt.x += m_Offset.x; - refPt.y += m_Offset.y; + POINT refPt = areaRect.CenterPoint(); + refPt.x += m_Offset.x; + refPt.y += m_Offset.y; - POINT out = {0, 0}; + POINT out = {0, 0}; - double dist = AptPositions[icao].DistanceTo(pos); - double dir = TrueBearing(AptPositions[icao], pos); + double dist = AptPositions[icao].DistanceTo(pos); + double dir = TrueBearing(AptPositions[icao], pos); + out.x = refPt.x + int(m_Scale * dist * sin(dir) + 0.5); + out.y = refPt.y - int(m_Scale * dist * cos(dir) + 0.5); - out.x = refPt.x + int(m_Scale * dist * sin(dir) + 0.5); - out.y = refPt.y - int(m_Scale * dist * cos(dir) + 0.5); - - if (m_Rotation != 0) - { - return rotate_point(out, m_Rotation, refPt); - } else - { - return out; - } + if (m_Rotation != 0) { + return rotate_point(out, m_Rotation, refPt); + } else { + return out; + } } -void CInsetWindow::render(HDC hDC, CSMRRadar * radar_screen, Graphics* gdi, POINT mouseLocation, multimap DistanceTools) -{ - CDC dc; - dc.Attach(hDC); - - if (this->m_Id == -1) - return; - - struct Utils - { - static string getEnumString(CSMRRadar::TagTypes type) { - if (type == CSMRRadar::TagTypes::Departure) - return "departure"; - if (type == CSMRRadar::TagTypes::Arrival) - return "arrival"; - if (type == CSMRRadar::TagTypes::Uncorrelated) - return "uncorrelated"; - return "airborne"; - } - static RECT GetAreaFromText(CDC * dc, string text, POINT Pos) { - RECT Area = { Pos.x, Pos.y, Pos.x + dc->GetTextExtent(text.c_str()).cx, Pos.y + dc->GetTextExtent(text.c_str()).cy }; - return Area; - } - - static RECT drawToolbarButton(CDC * dc, string letter, CRect TopBar, int left, POINT mouseLocation) - { - POINT TopLeft = { TopBar.right - left, TopBar.top + 2 }; - POINT BottomRight = { TopBar.right - (left - 11), TopBar.bottom - 2 }; - CRect Rect(TopLeft, BottomRight); - Rect.NormalizeRect(); - CBrush ButtonBrush(RGB(60, 60, 60)); - dc->FillRect(Rect, &ButtonBrush); - dc->SetTextColor(RGB(0, 0, 0)); - dc->TextOutA(Rect.left + 2, Rect.top, letter.c_str()); - - if (mouseWithin(mouseLocation, Rect)) - dc->Draw3dRect(Rect, RGB(45, 45, 45), RGB(75, 75, 75)); - else - dc->Draw3dRect(Rect, RGB(75, 75, 75), RGB(45, 45, 45)); - - return Rect; - } - }; - - icao = radar_screen->ActiveAirport; - AptPositions = radar_screen->AirportPositions; - - COLORREF qBackgroundColor = radar_screen->CurrentConfig->getConfigColorRef(radar_screen->CurrentConfig->getActiveProfile()["approach_insets"]["background_color"]); - CRect windowAreaCRect(m_Area); - windowAreaCRect.NormalizeRect(); - - // We create the radar - dc.FillSolidRect(windowAreaCRect, qBackgroundColor); - radar_screen->AddScreenObject(m_Id, "window", m_Area, true, ""); - - auto scale = m_Scale; - - POINT refPt = windowAreaCRect.CenterPoint(); - refPt.x += m_Offset.x; - refPt.y += m_Offset.y; - - // Here we draw all runways for the airport - CSectorElement rwy; - for (rwy = radar_screen->GetPlugIn()->SectorFileElementSelectFirst(SECTOR_ELEMENT_RUNWAY); - rwy.IsValid(); - rwy = radar_screen->GetPlugIn()->SectorFileElementSelectNext(rwy, SECTOR_ELEMENT_RUNWAY)) - { - - if (startsWith(icao.c_str(), rwy.GetAirportName())) - { - - CPen RunwayPen(PS_SOLID, 1, radar_screen->CurrentConfig->getConfigColorRef(radar_screen->CurrentConfig->getActiveProfile()["approach_insets"]["runway_color"])); - CPen ExtendedCentreLinePen(PS_SOLID, 1, radar_screen->CurrentConfig->getConfigColorRef(radar_screen->CurrentConfig->getActiveProfile()["approach_insets"]["extended_lines_color"])); - CPen* oldPen = dc.SelectObject(&RunwayPen); - - CPosition EndOne, EndTwo; - rwy.GetPosition(&EndOne, 0); - rwy.GetPosition(&EndTwo, 1); - - POINT Pt1, Pt2; - Pt1 = projectPoint(EndOne); - Pt2 = projectPoint(EndTwo); - - POINT toDraw1, toDraw2; - if (LiangBarsky(m_Area, Pt1, Pt2, toDraw1, toDraw2)) { - dc.MoveTo(toDraw1); - dc.LineTo(toDraw2); - - } - - if (rwy.IsElementActive(false, 0) || rwy.IsElementActive(false, 1)) - { - CPosition Threshold, OtherEnd; - if (rwy.IsElementActive(false, 0)) - { - Threshold = EndOne; - OtherEnd = EndTwo; - } else - { - Threshold = EndTwo; - OtherEnd = EndOne; - } - - - double reverseHeading = RadToDeg(TrueBearing(OtherEnd, Threshold)); - double lenght = double(radar_screen->CurrentConfig->getActiveProfile()["approach_insets"]["extended_lines_length"].GetDouble()) * 1852.0; - - // Drawing the extended centreline - CPosition endExtended = BetterHarversine(Threshold, reverseHeading, lenght); - - Pt1 = projectPoint(Threshold); - Pt2 = projectPoint(endExtended); - - if (LiangBarsky(m_Area, Pt1, Pt2, toDraw1, toDraw2)) { - dc.SelectObject(&ExtendedCentreLinePen); - dc.MoveTo(toDraw1); - dc.LineTo(toDraw2); - } - - // Drawing the ticks - int increment = radar_screen->CurrentConfig->getActiveProfile()["approach_insets"]["extended_lines_ticks_spacing"].GetInt() * 1852; - - for (int j = increment; j <= int(radar_screen->CurrentConfig->getActiveProfile()["approach_insets"]["extended_lines_length"].GetInt() * 1852); j += increment) { - - CPosition tickPosition = BetterHarversine(Threshold, reverseHeading, j); - CPosition tickBottom = BetterHarversine(tickPosition, fmod(reverseHeading - 90, 360), 500); - CPosition tickTop = BetterHarversine(tickPosition, fmod(reverseHeading + 90, 360), 500); - - - Pt1 = projectPoint(tickBottom); - Pt2 = projectPoint(tickTop); - - if (LiangBarsky(m_Area, Pt1, Pt2, toDraw1, toDraw2)) { - dc.SelectObject(&ExtendedCentreLinePen); - dc.MoveTo(toDraw1); - dc.LineTo(toDraw2); - } - - } - } - - dc.SelectObject(&oldPen); - } - } - - // Aircrafts - - vector appAreaVect = { windowAreaCRect.TopLeft(),{ windowAreaCRect.right, windowAreaCRect.top }, windowAreaCRect.BottomRight(),{ windowAreaCRect.left, windowAreaCRect.bottom } }; - CPen WhitePen(PS_SOLID, 1, radar_screen->ColorManager->get_corrected_color("symbol", Color::White).ToCOLORREF()); - - CRadarTarget rt; - for (rt = radar_screen->GetPlugIn()->RadarTargetSelectFirst(); - rt.IsValid(); - rt = radar_screen->GetPlugIn()->RadarTargetSelectNext(rt)) - { - int radarRange = radar_screen->CurrentConfig->getActiveProfile()["filters"]["radar_range_nm"].GetInt(); - - if (rt.GetGS() < 60 || - rt.GetPosition().GetPressureAltitude() > m_Filter || - !rt.IsValid() || - !rt.GetPosition().IsValid() || - rt.GetPosition().GetPosition().DistanceTo(AptPositions[icao]) > radarRange) - continue; - - CPosition RtPos2 = rt.GetPosition().GetPosition(); - CRadarTargetPositionData RtPos = rt.GetPosition(); - auto fp = radar_screen->GetPlugIn()->FlightPlanSelect(rt.GetCallsign()); - auto reportedGs = RtPos.GetReportedGS(); - - // Filtering the targets - - POINT RtPoint, hPoint; - - RtPoint = projectPoint(RtPos2); - - CRadarTargetPositionData hPos = rt.GetPreviousPosition(rt.GetPosition()); - for (int i = 1; i < radar_screen->Trail_App; i++) { - if (!hPos.IsValid()) - continue; - - hPoint = projectPoint(hPos.GetPosition()); - - if (Is_Inside(hPoint, appAreaVect)) { - dc.SetPixel(hPoint, radar_screen->ColorManager->get_corrected_color("symbol", Color::White).ToCOLORREF()); - } - - hPos = rt.GetPreviousPosition(hPos); - } - - if (Is_Inside(RtPoint, appAreaVect)) { - dc.SelectObject(&WhitePen); - - if (RtPos.GetTransponderC()) { - dc.MoveTo({ RtPoint.x, RtPoint.y - 4 }); - dc.LineTo({ RtPoint.x - 4, RtPoint.y }); - dc.LineTo({ RtPoint.x, RtPoint.y + 4 }); - dc.LineTo({ RtPoint.x + 4, RtPoint.y }); - dc.LineTo({ RtPoint.x, RtPoint.y - 4 }); - } - else { - dc.MoveTo(RtPoint.x, RtPoint.y); - dc.LineTo(RtPoint.x - 4, RtPoint.y - 4); - dc.MoveTo(RtPoint.x, RtPoint.y); - dc.LineTo(RtPoint.x + 4, RtPoint.y - 4); - dc.MoveTo(RtPoint.x, RtPoint.y); - dc.LineTo(RtPoint.x - 4, RtPoint.y + 4); - dc.MoveTo(RtPoint.x, RtPoint.y); - dc.LineTo(RtPoint.x + 4, RtPoint.y + 4); - } - - CRect TargetArea(RtPoint.x - 4, RtPoint.y - 4, RtPoint.x + 4, RtPoint.y + 4); - TargetArea.NormalizeRect(); - radar_screen->AddScreenObject(DRAWING_AC_SYMBOL_APPWINDOW_BASE + (m_Id - APPWINDOW_BASE), rt.GetCallsign(), TargetArea, false, radar_screen->GetBottomLine(rt.GetCallsign()).c_str()); - } - - // Predicted Track Line - // It starts 10 seconds away from the ac - if (radar_screen->PredictedLength > 0) { - double d = double(rt.GetPosition().GetReportedGS() * 0.514444) * 10; - CPosition AwayBase = BetterHarversine(rt.GetPosition().GetPosition(), rt.GetTrackHeading(), d); - - d = double(rt.GetPosition().GetReportedGS() * 0.514444) * (radar_screen->PredictedLength * 60) - 10; - CPosition PredictedEnd = BetterHarversine(AwayBase, rt.GetTrackHeading(), d); - - POINT liangOne, liangTwo; - - if (LiangBarsky(m_Area, projectPoint(AwayBase), projectPoint(PredictedEnd), liangOne, liangTwo)) - { - dc.SelectObject(&WhitePen); - dc.MoveTo(liangOne); - dc.LineTo(liangTwo); - } - } - - if (mouseWithin(mouseLocation, { RtPoint.x - 4, RtPoint.y - 4, RtPoint.x + 4, RtPoint.y + 4 })) { - dc.MoveTo(RtPoint.x, RtPoint.y - 6); - dc.LineTo(RtPoint.x - 4, RtPoint.y - 10); - dc.MoveTo(RtPoint.x, RtPoint.y - 6); - dc.LineTo(RtPoint.x + 4, RtPoint.y - 10); - - dc.MoveTo(RtPoint.x, RtPoint.y + 6); - dc.LineTo(RtPoint.x - 4, RtPoint.y + 10); - dc.MoveTo(RtPoint.x, RtPoint.y + 6); - dc.LineTo(RtPoint.x + 4, RtPoint.y + 10); - - dc.MoveTo(RtPoint.x - 6, RtPoint.y); - dc.LineTo(RtPoint.x - 10, RtPoint.y - 4); - dc.MoveTo(RtPoint.x - 6, RtPoint.y); - dc.LineTo(RtPoint.x - 10, RtPoint.y + 4); - - dc.MoveTo(RtPoint.x + 6, RtPoint.y); - dc.LineTo(RtPoint.x + 10, RtPoint.y - 4); - dc.MoveTo(RtPoint.x + 6, RtPoint.y); - dc.LineTo(RtPoint.x + 10, RtPoint.y + 4); - } - - int lenght = 50; - - POINT TagCenter; - if (m_TagAngles.find(rt.GetCallsign()) == m_TagAngles.end()) - { - m_TagAngles[rt.GetCallsign()] = 45.0; // TODO: Not the best, ah well - } - - TagCenter.x = long(RtPoint.x + float(lenght * cos(DegToRad(m_TagAngles[rt.GetCallsign()])))); - TagCenter.y = long(RtPoint.y + float(lenght * sin(DegToRad(m_TagAngles[rt.GetCallsign()])))); - // Drawing the tags, what a mess - - // ----- Generating the replacing map ----- - map TagReplacingMap = CSMRRadar::GenerateTagData(rt, fp, radar_screen->IsCorrelated(fp, rt), radar_screen->CurrentConfig->getActiveProfile()["filters"]["pro_mode"]["enable"].GetBool(), radar_screen->GetPlugIn()->GetTransitionAltitude(), radar_screen->CurrentConfig->getActiveProfile()["labels"]["use_aspeed_for_gate"].GetBool(), icao, radar_screen->showAircraftType, radar_screen->showSID, radar_screen->showWakeTurb); - - // ----- Generating the clickable map ----- - map TagClickableMap; - TagClickableMap[TagReplacingMap["callsign"]] = TAG_CITEM_CALLSIGN; - TagClickableMap[TagReplacingMap["actype"]] = TAG_CITEM_FPBOX; - TagClickableMap[TagReplacingMap["sctype"]] = TAG_CITEM_FPBOX; - TagClickableMap[TagReplacingMap["sqerror"]] = TAG_CITEM_FPBOX; - TagClickableMap[TagReplacingMap["deprwy"]] = TAG_CITEM_RWY; - TagClickableMap[TagReplacingMap["seprwy"]] = TAG_CITEM_RWY; - TagClickableMap[TagReplacingMap["arvrwy"]] = TAG_CITEM_RWY; - TagClickableMap[TagReplacingMap["srvrwy"]] = TAG_CITEM_RWY; - TagClickableMap[TagReplacingMap["gate"]] = TAG_CITEM_GATE; - TagClickableMap[TagReplacingMap["sate"]] = TAG_CITEM_GATE; - TagClickableMap[TagReplacingMap["flightlevel"]] = TAG_CITEM_NO; - TagClickableMap[TagReplacingMap["gs"]] = TAG_CITEM_NO; - TagClickableMap[TagReplacingMap["tendency"]] = TAG_CITEM_NO; - TagClickableMap[TagReplacingMap["wake"]] = TAG_CITEM_FPBOX; - TagClickableMap[TagReplacingMap["tssr"]] = TAG_CITEM_NO; - TagClickableMap[TagReplacingMap["sid"]] = TagClickableMap[TagReplacingMap["shid"]] = TAG_CITEM_SID; - TagClickableMap[TagReplacingMap["origin"]] = TAG_CITEM_FPBOX; - TagClickableMap[TagReplacingMap["dest"]] = TAG_CITEM_FPBOX; - TagClickableMap[TagReplacingMap["systemid"]] = TAG_CITEM_MANUALCORRELATE; - TagClickableMap[TagReplacingMap["gstatus"]] = TAG_CITEM_GROUNDSTATUS; - TagClickableMap[TagReplacingMap["uk_stand"]] = TAG_CITEM_UKSTAND; - - - // - // ----- Now the hard part, drawing (using gdi+) ------- - // - - CSMRRadar::TagTypes TagType = CSMRRadar::TagTypes::Departure; - CSMRRadar::TagTypes ColorTagType = CSMRRadar::TagTypes::Departure; - - if (fp.IsValid() && strcmp(fp.GetFlightPlanData().GetDestination(), radar_screen->getActiveAirport().c_str()) == 0) { - TagType = CSMRRadar::TagTypes::Arrival; - ColorTagType = CSMRRadar::TagTypes::Arrival; - } - - if (reportedGs > 50) { - TagType = CSMRRadar::TagTypes::Airborne; - - // Is "use_departure_arrival_coloring" enabled? if not, then use the airborne colors - bool useDepArrColors = radar_screen->CurrentConfig->getActiveProfile()["labels"]["airborne"]["use_departure_arrival_coloring"].GetBool(); - if (!useDepArrColors) { - ColorTagType = CSMRRadar::TagTypes::Airborne; - } - } - - bool AcisCorrelated = radar_screen->IsCorrelated(radar_screen->GetPlugIn()->FlightPlanSelect(rt.GetCallsign()), rt); - if (!AcisCorrelated && reportedGs >= 3) - { - TagType = CSMRRadar::TagTypes::Uncorrelated; - ColorTagType = CSMRRadar::TagTypes::Uncorrelated; - } - - // First we need to figure out the tag size - - int TagWidth = 0, TagHeight = 0; - RectF mesureRect; - gdi->MeasureString(L" ", wcslen(L" "), radar_screen->customFonts[radar_screen->currentFontSize], PointF(0, 0), &Gdiplus::StringFormat(), &mesureRect); - int blankWidth = (int)mesureRect.GetRight(); - - mesureRect = RectF(0, 0, 0, 0); - gdi->MeasureString(L"AZERTYUIOPQSDFGHJKLMWXCVBN", wcslen(L"AZERTYUIOPQSDFGHJKLMWXCVBN"), - radar_screen->customFonts[radar_screen->currentFontSize], PointF(0, 0), &Gdiplus::StringFormat(), &mesureRect); - int oneLineHeight = (int)mesureRect.GetBottom(); - - const Value& LabelsSettings = radar_screen->CurrentConfig->getActiveProfile()["labels"]; - const Value& LabelLines = LabelsSettings[Utils::getEnumString(TagType).c_str()]["definition"]; - vector> ReplacedLabelLines; - - if (!LabelLines.IsArray()) - return; - - for (unsigned int i = 0; i < LabelLines.Size(); i++) - { - - const Value& line = LabelLines[i]; - vector lineStringArray; - - // Adds one line height - TagHeight += oneLineHeight; - - int TempTagWidth = 0; - - for (unsigned int j = 0; j < line.Size(); j++) - { - mesureRect = RectF(0, 0, 0, 0); - string element = line[j].GetString(); - - for (auto& kv : TagReplacingMap) - replaceAll(element, kv.first, kv.second); - - lineStringArray.push_back(element); - - wstring wstr = wstring(element.begin(), element.end()); - gdi->MeasureString(wstr.c_str(), wcslen(wstr.c_str()), - radar_screen->customFonts[radar_screen->currentFontSize], PointF(0, 0), &Gdiplus::StringFormat(), &mesureRect); - - TempTagWidth += (int) mesureRect.GetRight(); - - if (j != line.Size() - 1) - TempTagWidth += (int) blankWidth; - } - - TagWidth = max(TagWidth, TempTagWidth); - - ReplacedLabelLines.push_back(lineStringArray); - } - TagHeight = TagHeight - 2; - - // Pfiou, done with that, now we can draw the actual rectangle. - - // We need to figure out if the tag color changes according to RIMCAS alerts, or not - bool rimcasLabelOnly = radar_screen->CurrentConfig->getActiveProfile()["rimcas"]["rimcas_label_only"].GetBool(); - - Color definedBackgroundColor = radar_screen->CurrentConfig->getConfigColor(LabelsSettings[Utils::getEnumString(ColorTagType).c_str()]["background_color"]); - if (TagType == CSMRRadar::TagTypes::Departure) { - if (!TagReplacingMap["sid"].empty() && radar_screen->CurrentConfig->isSidColorAvail(TagReplacingMap["sid"], radar_screen->getActiveAirport())) { - definedBackgroundColor = radar_screen->CurrentConfig->getSidColor(TagReplacingMap["sid"], radar_screen->getActiveAirport()); - } - - if (fp.GetFlightPlanData().GetPlanType()[0] == 'I' && TagReplacingMap["asid"].empty() && LabelsSettings[Utils::getEnumString(ColorTagType).c_str()].HasMember("nosid_color")) { - definedBackgroundColor = radar_screen->CurrentConfig->getConfigColor(LabelsSettings[Utils::getEnumString(ColorTagType).c_str()]["nosid_color"]); - } - } - if (TagReplacingMap["actype"] == "NoFPL" && LabelsSettings[Utils::getEnumString(ColorTagType).c_str()].HasMember("nofpl_color")) { - definedBackgroundColor = radar_screen->CurrentConfig->getConfigColor(LabelsSettings[Utils::getEnumString(ColorTagType).c_str()]["nofpl_color"]); - } - - Color TagBackgroundColor = radar_screen->RimcasInstance->GetAircraftColor(rt.GetCallsign(), - definedBackgroundColor, - radar_screen->CurrentConfig->getConfigColor(LabelsSettings[Utils::getEnumString(ColorTagType).c_str()]["background_color_on_runway"]), - radar_screen->CurrentConfig->getConfigColor(radar_screen->CurrentConfig->getActiveProfile()["rimcas"]["background_color_stage_one"]), - radar_screen->CurrentConfig->getConfigColor(radar_screen->CurrentConfig->getActiveProfile()["rimcas"]["background_color_stage_two"])); - - if (rimcasLabelOnly) - TagBackgroundColor = radar_screen->RimcasInstance->GetAircraftColor(rt.GetCallsign(), - definedBackgroundColor, - radar_screen->CurrentConfig->getConfigColor(LabelsSettings[Utils::getEnumString(ColorTagType).c_str()]["background_color_on_runway"])); - - CRect TagBackgroundRect(TagCenter.x - (TagWidth / 2), TagCenter.y - (TagHeight / 2), TagCenter.x + (TagWidth / 2), TagCenter.y + (TagHeight / 2)); - - if (Is_Inside(TagBackgroundRect.TopLeft(), appAreaVect) && - Is_Inside(RtPoint, appAreaVect) && - Is_Inside(TagBackgroundRect.BottomRight(), appAreaVect)) { - - SolidBrush TagBackgroundBrush(TagBackgroundColor); - gdi->FillRectangle(&TagBackgroundBrush, CopyRect(TagBackgroundRect)); - - SolidBrush FontColor(radar_screen->ColorManager->get_corrected_color("label", - radar_screen->CurrentConfig->getConfigColor(LabelsSettings[Utils::getEnumString(ColorTagType).c_str()]["text_color"]))); - SolidBrush SquawkErrorColor(radar_screen->ColorManager->get_corrected_color("label", - radar_screen->CurrentConfig->getConfigColor(LabelsSettings["squawk_error_color"]))); - SolidBrush RimcasTextColor(radar_screen->CurrentConfig->getConfigColor(radar_screen->CurrentConfig->getActiveProfile()["rimcas"]["alert_text_color"])); - - int heightOffset = 0; - for (auto&& line : ReplacedLabelLines) - { - int widthOffset = 0; - for (auto&& element : line) - { - SolidBrush* color = &FontColor; - if (TagReplacingMap["sqerror"].size() > 0 && strcmp(element.c_str(), TagReplacingMap["sqerror"].c_str()) == 0) - color = &SquawkErrorColor; - - if (radar_screen->RimcasInstance->getAlert(rt.GetCallsign()) != CRimcas::NoAlert) - color = &RimcasTextColor; - - RectF mRect(0, 0, 0, 0); - - wstring welement = wstring(element.begin(), element.end()); - - gdi->DrawString(welement.c_str(), wcslen(welement.c_str()), radar_screen->customFonts[radar_screen->currentFontSize], - PointF(Gdiplus::REAL(TagBackgroundRect.left + widthOffset), Gdiplus::REAL(TagBackgroundRect.top + heightOffset)), - &Gdiplus::StringFormat(), color); - - - gdi->MeasureString(welement.c_str(), wcslen(welement.c_str()), - radar_screen->customFonts[radar_screen->currentFontSize], PointF(0, 0), &Gdiplus::StringFormat(), &mRect); - - CRect ItemRect(TagBackgroundRect.left + widthOffset, TagBackgroundRect.top + heightOffset, - TagBackgroundRect.left + widthOffset + (int)mRect.GetRight(), TagBackgroundRect.top + heightOffset + (int)mRect.GetBottom()); - - radar_screen->AddScreenObject(TagClickableMap[element], rt.GetCallsign(), ItemRect, false, radar_screen->GetBottomLine(rt.GetCallsign()).c_str()); - - widthOffset += (int)mRect.GetRight(); - widthOffset += blankWidth; - } - - heightOffset += oneLineHeight; - } - - // Drawing the leader line - RECT TagBackRectData = TagBackgroundRect; - POINT toDraw1, toDraw2; - if (LiangBarsky(TagBackRectData, RtPoint, TagBackgroundRect.CenterPoint(), toDraw1, toDraw2)) - gdi->DrawLine(&Pen(radar_screen->ColorManager->get_corrected_color("symbol", Color::White)), PointF(Gdiplus::REAL(RtPoint.x), Gdiplus::REAL(RtPoint.y)), PointF(Gdiplus::REAL(toDraw1.x), Gdiplus::REAL(toDraw1.y))); - - // If we use a RIMCAS label only, we display it, and adapt the rectangle - CRect oldCrectSave = TagBackgroundRect; - - if (rimcasLabelOnly) { - Color RimcasLabelColor = radar_screen->RimcasInstance->GetAircraftColor(rt.GetCallsign(), Color::AliceBlue, Color::AliceBlue, - radar_screen->CurrentConfig->getConfigColor(radar_screen->CurrentConfig->getActiveProfile()["rimcas"]["background_color_stage_one"]), - radar_screen->CurrentConfig->getConfigColor(radar_screen->CurrentConfig->getActiveProfile()["rimcas"]["background_color_stage_two"])); - - if (RimcasLabelColor.ToCOLORREF() != Color(Color::AliceBlue).ToCOLORREF()) { - int rimcas_height = 0; - - wstring wrimcas_height = wstring(L"ALERT"); - - RectF RectRimcas_height; - - gdi->MeasureString(wrimcas_height.c_str(), wcslen(wrimcas_height.c_str()), radar_screen->customFonts[radar_screen->currentFontSize], PointF(0, 0), &Gdiplus::StringFormat(), &RectRimcas_height); - rimcas_height = int(RectRimcas_height.GetBottom()); - - // Drawing the rectangle - - CRect RimcasLabelRect(TagBackgroundRect.left, TagBackgroundRect.top - rimcas_height, TagBackgroundRect.right, TagBackgroundRect.top); - gdi->FillRectangle(&SolidBrush(RimcasLabelColor), CopyRect(RimcasLabelRect)); - TagBackgroundRect.top -= rimcas_height; - - // Drawing the text - - wstring rimcasw = wstring(L"ALERT"); - StringFormat stformat = new StringFormat(); - stformat.SetAlignment(StringAlignment::StringAlignmentCenter); - gdi->DrawString(rimcasw.c_str(), wcslen(rimcasw.c_str()), radar_screen->customFonts[radar_screen->currentFontSize], PointF(Gdiplus::REAL((TagBackgroundRect.left + TagBackgroundRect.right) / 2), Gdiplus::REAL(TagBackgroundRect.top)), &stformat, &RimcasTextColor); - - } - } - - // Adding the tag screen object - - //radar_screen->AddScreenObject(DRAWING_TAG, rt.GetCallsign(), TagBackgroundRect, true, GetBottomLine(rt.GetCallsign()).c_str()); - - TagBackgroundRect = oldCrectSave; - - // Now adding the clickable zones - } - } - - // Distance tools here - for (auto&& kv : DistanceTools) - { - CRadarTarget one = radar_screen->GetPlugIn()->RadarTargetSelect(kv.first.c_str()); - CRadarTarget two = radar_screen->GetPlugIn()->RadarTargetSelect(kv.second.c_str()); - - int radarRange = radar_screen->CurrentConfig->getActiveProfile()["filters"]["radar_range_nm"].GetInt(); - - if (one.GetGS() < 60 || - one.GetPosition().GetPressureAltitude() > m_Filter || - !one.IsValid() || - !one.GetPosition().IsValid() || - one.GetPosition().GetPosition().DistanceTo(AptPositions[icao]) > radarRange) - continue; - - if (two.GetGS() < 60 || - two.GetPosition().GetPressureAltitude() > m_Filter || - !two.IsValid() || - !two.GetPosition().IsValid() || - two.GetPosition().GetPosition().DistanceTo(AptPositions[icao]) > radarRange) - continue; - - CPen Pen(PS_SOLID, 1, RGB(255, 255, 255)); - CPen *oldPen = dc.SelectObject(&Pen); - - POINT onePoint = projectPoint(one.GetPosition().GetPosition()); - POINT twoPoint = projectPoint(two.GetPosition().GetPosition()); - - POINT toDraw1, toDraw2; - if (LiangBarsky(m_Area, onePoint, twoPoint, toDraw1, toDraw2)) { - dc.MoveTo(toDraw1); - dc.LineTo(toDraw2); - } - - POINT TextPos = { twoPoint.x + 20, twoPoint.y }; - - double Distance = one.GetPosition().GetPosition().DistanceTo(two.GetPosition().GetPosition()); - double Bearing = one.GetPosition().GetPosition().DirectionTo(two.GetPosition().GetPosition()); - - string distances = std::to_string(Distance); - size_t decimal_pos = distances.find("."); - distances = distances.substr(0, decimal_pos + 2); - - string bearings = std::to_string(Bearing); - decimal_pos = bearings.find("."); - bearings = bearings.substr(0, decimal_pos + 2); - - string text = bearings; - text += "� / "; - text += distances; - text += "nm"; - COLORREF old_color = dc.SetTextColor(RGB(0, 0, 0)); - - CRect ClickableRect = { TextPos.x - 2, TextPos.y, TextPos.x + dc.GetTextExtent(text.c_str()).cx + 2, TextPos.y + dc.GetTextExtent(text.c_str()).cy }; - if (Is_Inside(ClickableRect.TopLeft(), appAreaVect) && Is_Inside(ClickableRect.BottomRight(), appAreaVect)) - { - gdi->FillRectangle(&SolidBrush(Color(127, 122, 122)), CopyRect(ClickableRect)); - dc.Draw3dRect(ClickableRect, RGB(75, 75, 75), RGB(45, 45, 45)); - dc.TextOutA(TextPos.x, TextPos.y, text.c_str()); - - radar_screen->AddScreenObject(RIMCAS_DISTANCE_TOOL, string(kv.first + "," + kv.second).c_str(), ClickableRect, false, ""); - } - - dc.SetTextColor(old_color); - - dc.SelectObject(oldPen); - } - - // Resize square - qBackgroundColor = RGB(60, 60, 60); - POINT BottomRight = { m_Area.right, m_Area.bottom }; - POINT TopLeft = { BottomRight.x - 10, BottomRight.y - 10 }; - CRect ResizeArea = { TopLeft, BottomRight }; - ResizeArea.NormalizeRect(); - dc.FillSolidRect(ResizeArea, qBackgroundColor); - radar_screen->AddScreenObject(m_Id, "resize", ResizeArea, true, ""); - - dc.Draw3dRect(ResizeArea, RGB(0, 0, 0), RGB(0, 0, 0)); - - // Sides - //CBrush FrameBrush(RGB(35, 35, 35)); - CBrush FrameBrush(RGB(127, 122, 122)); - COLORREF TopBarTextColor(RGB(35, 35, 35)); - dc.FrameRect(windowAreaCRect, &FrameBrush); - - // Topbar - TopLeft = windowAreaCRect.TopLeft(); - TopLeft.y = TopLeft.y - 15; - BottomRight = { windowAreaCRect.right, windowAreaCRect.top }; - CRect TopBar(TopLeft, BottomRight); - TopBar.NormalizeRect(); - dc.FillRect(TopBar, &FrameBrush); - POINT TopLeftText = { TopBar.left + 5, TopBar.bottom - dc.GetTextExtent("SRW 1").cy }; - COLORREF oldTextColorC = dc.SetTextColor(TopBarTextColor); - - radar_screen->AddScreenObject(m_Id, "topbar", TopBar, true, ""); - - string Toptext = "SRW " + std::to_string(m_Id - APPWINDOW_BASE); - dc.TextOutA(TopLeftText.x + (TopBar.right-TopBar.left) / 2 - dc.GetTextExtent("SRW 1").cx , TopLeftText.y, Toptext.c_str()); - - // Range button - CRect RangeRect = Utils::drawToolbarButton(&dc, "Z", TopBar, 29, mouseLocation); - radar_screen->AddScreenObject(m_Id, "range", RangeRect, false, ""); - - // Filter button - CRect FilterRect = Utils::drawToolbarButton(&dc, "F", TopBar, 42, mouseLocation); - radar_screen->AddScreenObject(m_Id, "filter", FilterRect, false, ""); - - // Rotate button - CRect RotateRect = Utils::drawToolbarButton(&dc, "R", TopBar, 55, mouseLocation); - radar_screen->AddScreenObject(m_Id, "rotate", RotateRect, false, ""); - - dc.SetTextColor(oldTextColorC); - - // Close - POINT TopLeftClose = { TopBar.right - 16, TopBar.top + 2 }; - POINT BottomRightClose = { TopBar.right - 5, TopBar.bottom - 2 }; - CRect CloseRect(TopLeftClose, BottomRightClose); - CloseRect.NormalizeRect(); - CBrush CloseBrush(RGB(60, 60, 60)); - dc.FillRect(CloseRect, &CloseBrush); - CPen BlackPen(PS_SOLID, 1, RGB(0, 0, 0)); - dc.SelectObject(BlackPen); - dc.MoveTo(CloseRect.TopLeft()); - dc.LineTo(CloseRect.BottomRight()); - dc.MoveTo({ CloseRect.right - 1, CloseRect.top }); - dc.LineTo({ CloseRect.left - 1, CloseRect.bottom }); - - if (mouseWithin(mouseLocation, CloseRect)) - dc.Draw3dRect(CloseRect, RGB(45, 45, 45), RGB(75, 75, 75)); - else - dc.Draw3dRect(CloseRect, RGB(75, 75, 75), RGB(45, 45, 45)); - - radar_screen->AddScreenObject(m_Id, "close", CloseRect, false, ""); - - dc.Detach(); +void CInsetWindow::render(HDC hDC, CSMRRadar *radar_screen, Graphics *gdi, + POINT mouseLocation, + multimap DistanceTools) { + CDC dc; + dc.Attach(hDC); + + if (this->m_Id == -1) + return; + + struct Utils { + static string getEnumString(CSMRRadar::TagTypes type) { + if (type == CSMRRadar::TagTypes::Departure) + return "departure"; + if (type == CSMRRadar::TagTypes::Arrival) + return "arrival"; + if (type == CSMRRadar::TagTypes::Uncorrelated) + return "uncorrelated"; + return "airborne"; + } + static RECT GetAreaFromText(CDC *dc, string text, POINT Pos) { + RECT Area = {Pos.x, Pos.y, Pos.x + dc->GetTextExtent(text.c_str()).cx, + Pos.y + dc->GetTextExtent(text.c_str()).cy}; + return Area; + } + + static RECT drawToolbarButton(CDC *dc, string letter, CRect TopBar, + int left, POINT mouseLocation) { + POINT TopLeft = {TopBar.right - left, TopBar.top + 2}; + POINT BottomRight = {TopBar.right - (left - 11), TopBar.bottom - 2}; + CRect Rect(TopLeft, BottomRight); + Rect.NormalizeRect(); + CBrush ButtonBrush(RGB(60, 60, 60)); + dc->FillRect(Rect, &ButtonBrush); + dc->SetTextColor(RGB(0, 0, 0)); + dc->TextOutA(Rect.left + 2, Rect.top, letter.c_str()); + + if (mouseWithin(mouseLocation, Rect)) + dc->Draw3dRect(Rect, RGB(45, 45, 45), RGB(75, 75, 75)); + else + dc->Draw3dRect(Rect, RGB(75, 75, 75), RGB(45, 45, 45)); + + return Rect; + } + }; + + icao = radar_screen->ActiveAirport; + AptPositions = radar_screen->AirportPositions; + + COLORREF qBackgroundColor = radar_screen->CurrentConfig->getConfigColorRef( + radar_screen->CurrentConfig + ->getActiveProfile()["approach_insets"]["background_color"]); + CRect windowAreaCRect(m_Area); + windowAreaCRect.NormalizeRect(); + + // We create the radar + dc.FillSolidRect(windowAreaCRect, qBackgroundColor); + radar_screen->AddScreenObject(m_Id, "window", m_Area, true, ""); + + auto scale = m_Scale; + + POINT refPt = windowAreaCRect.CenterPoint(); + refPt.x += m_Offset.x; + refPt.y += m_Offset.y; + + // Here we draw all runways for the airport + CSectorElement rwy; + for (rwy = radar_screen->GetPlugIn()->SectorFileElementSelectFirst( + SECTOR_ELEMENT_RUNWAY); + rwy.IsValid(); + rwy = radar_screen->GetPlugIn()->SectorFileElementSelectNext( + rwy, SECTOR_ELEMENT_RUNWAY)) { + + if (startsWith(icao.c_str(), rwy.GetAirportName())) { + + CPen RunwayPen( + PS_SOLID, 1, + radar_screen->CurrentConfig->getConfigColorRef( + radar_screen->CurrentConfig + ->getActiveProfile()["approach_insets"]["runway_color"])); + CPen ExtendedCentreLinePen( + PS_SOLID, 1, + radar_screen->CurrentConfig->getConfigColorRef( + radar_screen->CurrentConfig + ->getActiveProfile()["approach_insets"] + ["extended_lines_color"])); + CPen *oldPen = dc.SelectObject(&RunwayPen); + + CPosition EndOne, EndTwo; + rwy.GetPosition(&EndOne, 0); + rwy.GetPosition(&EndTwo, 1); + + POINT Pt1, Pt2; + Pt1 = projectPoint(EndOne); + Pt2 = projectPoint(EndTwo); + + POINT toDraw1, toDraw2; + if (LiangBarsky(m_Area, Pt1, Pt2, toDraw1, toDraw2)) { + dc.MoveTo(toDraw1); + dc.LineTo(toDraw2); + } + + if (rwy.IsElementActive(false, 0) || rwy.IsElementActive(false, 1)) { + CPosition Threshold, OtherEnd; + if (rwy.IsElementActive(false, 0)) { + Threshold = EndOne; + OtherEnd = EndTwo; + } else { + Threshold = EndTwo; + OtherEnd = EndOne; + } + + double reverseHeading = RadToDeg(TrueBearing(OtherEnd, Threshold)); + double lenght = double(radar_screen->CurrentConfig + ->getActiveProfile()["approach_insets"] + ["extended_lines_length"] + .GetDouble()) * + 1852.0; + + // Drawing the extended centreline + CPosition endExtended = + BetterHarversine(Threshold, reverseHeading, lenght); + + Pt1 = projectPoint(Threshold); + Pt2 = projectPoint(endExtended); + + if (LiangBarsky(m_Area, Pt1, Pt2, toDraw1, toDraw2)) { + dc.SelectObject(&ExtendedCentreLinePen); + dc.MoveTo(toDraw1); + dc.LineTo(toDraw2); + } + + // Drawing the ticks + int increment = radar_screen->CurrentConfig + ->getActiveProfile()["approach_insets"] + ["extended_lines_ticks_spacing"] + .GetInt() * + 1852; + + for (int j = increment; + j <= int(radar_screen->CurrentConfig + ->getActiveProfile()["approach_insets"] + ["extended_lines_length"] + .GetInt() * + 1852); + j += increment) { + + CPosition tickPosition = + BetterHarversine(Threshold, reverseHeading, j); + CPosition tickBottom = BetterHarversine( + tickPosition, fmod(reverseHeading - 90, 360), 500); + CPosition tickTop = BetterHarversine( + tickPosition, fmod(reverseHeading + 90, 360), 500); + + Pt1 = projectPoint(tickBottom); + Pt2 = projectPoint(tickTop); + + if (LiangBarsky(m_Area, Pt1, Pt2, toDraw1, toDraw2)) { + dc.SelectObject(&ExtendedCentreLinePen); + dc.MoveTo(toDraw1); + dc.LineTo(toDraw2); + } + } + } + + dc.SelectObject(&oldPen); + } + } + + // Aircrafts + + vector appAreaVect = {windowAreaCRect.TopLeft(), + {windowAreaCRect.right, windowAreaCRect.top}, + windowAreaCRect.BottomRight(), + {windowAreaCRect.left, windowAreaCRect.bottom}}; + CPen WhitePen( + PS_SOLID, 1, + radar_screen->ColorManager->get_corrected_color("symbol", Color::White) + .ToCOLORREF()); + + CRadarTarget rt; + for (rt = radar_screen->GetPlugIn()->RadarTargetSelectFirst(); rt.IsValid(); + rt = radar_screen->GetPlugIn()->RadarTargetSelectNext(rt)) { + int radarRange = radar_screen->CurrentConfig + ->getActiveProfile()["filters"]["radar_range_nm"] + .GetInt(); + + if (rt.GetGS() < 60 || rt.GetPosition().GetPressureAltitude() > m_Filter || + !rt.IsValid() || !rt.GetPosition().IsValid() || + rt.GetPosition().GetPosition().DistanceTo(AptPositions[icao]) > + radarRange) + continue; + + CPosition RtPos2 = rt.GetPosition().GetPosition(); + CRadarTargetPositionData RtPos = rt.GetPosition(); + auto fp = radar_screen->GetPlugIn()->FlightPlanSelect(rt.GetCallsign()); + auto reportedGs = RtPos.GetReportedGS(); + + // Filtering the targets + + POINT RtPoint, hPoint; + + RtPoint = projectPoint(RtPos2); + + CRadarTargetPositionData hPos = rt.GetPreviousPosition(rt.GetPosition()); + for (int i = 1; i < radar_screen->Trail_App; i++) { + if (!hPos.IsValid()) + continue; + + hPoint = projectPoint(hPos.GetPosition()); + + if (Is_Inside(hPoint, appAreaVect)) { + dc.SetPixel(hPoint, radar_screen->ColorManager + ->get_corrected_color("symbol", Color::White) + .ToCOLORREF()); + } + + hPos = rt.GetPreviousPosition(hPos); + } + + if (Is_Inside(RtPoint, appAreaVect)) { + dc.SelectObject(&WhitePen); + + if (RtPos.GetTransponderC()) { + dc.MoveTo({RtPoint.x, RtPoint.y - 4}); + dc.LineTo({RtPoint.x - 4, RtPoint.y}); + dc.LineTo({RtPoint.x, RtPoint.y + 4}); + dc.LineTo({RtPoint.x + 4, RtPoint.y}); + dc.LineTo({RtPoint.x, RtPoint.y - 4}); + } else { + dc.MoveTo(RtPoint.x, RtPoint.y); + dc.LineTo(RtPoint.x - 4, RtPoint.y - 4); + dc.MoveTo(RtPoint.x, RtPoint.y); + dc.LineTo(RtPoint.x + 4, RtPoint.y - 4); + dc.MoveTo(RtPoint.x, RtPoint.y); + dc.LineTo(RtPoint.x - 4, RtPoint.y + 4); + dc.MoveTo(RtPoint.x, RtPoint.y); + dc.LineTo(RtPoint.x + 4, RtPoint.y + 4); + } + + CRect TargetArea(RtPoint.x - 4, RtPoint.y - 4, RtPoint.x + 4, + RtPoint.y + 4); + TargetArea.NormalizeRect(); + radar_screen->AddScreenObject( + DRAWING_AC_SYMBOL_APPWINDOW_BASE + (m_Id - APPWINDOW_BASE), + rt.GetCallsign(), TargetArea, false, + radar_screen->GetBottomLine(rt.GetCallsign()).c_str()); + } + + // Predicted Track Line + // It starts 10 seconds away from the ac + if (radar_screen->PredictedLength > 0) { + double d = double(rt.GetPosition().GetReportedGS() * 0.514444) * 10; + CPosition AwayBase = BetterHarversine(rt.GetPosition().GetPosition(), + rt.GetTrackHeading(), d); + + d = double(rt.GetPosition().GetReportedGS() * 0.514444) * + (radar_screen->PredictedLength * 60) - + 10; + CPosition PredictedEnd = + BetterHarversine(AwayBase, rt.GetTrackHeading(), d); + + POINT liangOne, liangTwo; + + if (LiangBarsky(m_Area, projectPoint(AwayBase), + projectPoint(PredictedEnd), liangOne, liangTwo)) { + dc.SelectObject(&WhitePen); + dc.MoveTo(liangOne); + dc.LineTo(liangTwo); + } + } + + if (mouseWithin(mouseLocation, {RtPoint.x - 4, RtPoint.y - 4, RtPoint.x + 4, + RtPoint.y + 4})) { + dc.MoveTo(RtPoint.x, RtPoint.y - 6); + dc.LineTo(RtPoint.x - 4, RtPoint.y - 10); + dc.MoveTo(RtPoint.x, RtPoint.y - 6); + dc.LineTo(RtPoint.x + 4, RtPoint.y - 10); + + dc.MoveTo(RtPoint.x, RtPoint.y + 6); + dc.LineTo(RtPoint.x - 4, RtPoint.y + 10); + dc.MoveTo(RtPoint.x, RtPoint.y + 6); + dc.LineTo(RtPoint.x + 4, RtPoint.y + 10); + + dc.MoveTo(RtPoint.x - 6, RtPoint.y); + dc.LineTo(RtPoint.x - 10, RtPoint.y - 4); + dc.MoveTo(RtPoint.x - 6, RtPoint.y); + dc.LineTo(RtPoint.x - 10, RtPoint.y + 4); + + dc.MoveTo(RtPoint.x + 6, RtPoint.y); + dc.LineTo(RtPoint.x + 10, RtPoint.y - 4); + dc.MoveTo(RtPoint.x + 6, RtPoint.y); + dc.LineTo(RtPoint.x + 10, RtPoint.y + 4); + } + + int lenght = 50; + + POINT TagCenter; + if (m_TagAngles.find(rt.GetCallsign()) == m_TagAngles.end()) { + m_TagAngles[rt.GetCallsign()] = 45.0; // TODO: Not the best, ah well + } + + TagCenter.x = + long(RtPoint.x + + float(lenght * cos(DegToRad(m_TagAngles[rt.GetCallsign()])))); + TagCenter.y = + long(RtPoint.y + + float(lenght * sin(DegToRad(m_TagAngles[rt.GetCallsign()])))); + // Drawing the tags, what a mess + + // ----- Generating the replacing map ----- + map TagReplacingMap = CSMRRadar::GenerateTagData( + rt, fp, radar_screen->IsCorrelated(fp, rt), + radar_screen->CurrentConfig + ->getActiveProfile()["filters"]["pro_mode"]["enable"] + .GetBool(), + radar_screen->GetPlugIn()->GetTransitionAltitude(), + radar_screen->CurrentConfig + ->getActiveProfile()["labels"]["use_aspeed_for_gate"] + .GetBool(), + icao, radar_screen->showAircraftType, radar_screen->showSID, + radar_screen->showWakeTurb); + + // ----- Generating the clickable map ----- + map TagClickableMap; + TagClickableMap[TagReplacingMap["callsign"]] = TAG_CITEM_CALLSIGN; + TagClickableMap[TagReplacingMap["actype"]] = TAG_CITEM_FPBOX; + TagClickableMap[TagReplacingMap["sctype"]] = TAG_CITEM_FPBOX; + TagClickableMap[TagReplacingMap["sqerror"]] = TAG_CITEM_FPBOX; + TagClickableMap[TagReplacingMap["deprwy"]] = TAG_CITEM_RWY; + TagClickableMap[TagReplacingMap["seprwy"]] = TAG_CITEM_RWY; + TagClickableMap[TagReplacingMap["arvrwy"]] = TAG_CITEM_RWY; + TagClickableMap[TagReplacingMap["srvrwy"]] = TAG_CITEM_RWY; + TagClickableMap[TagReplacingMap["gate"]] = TAG_CITEM_GATE; + TagClickableMap[TagReplacingMap["sate"]] = TAG_CITEM_GATE; + TagClickableMap[TagReplacingMap["flightlevel"]] = TAG_CITEM_NO; + TagClickableMap[TagReplacingMap["gs"]] = TAG_CITEM_NO; + TagClickableMap[TagReplacingMap["tendency"]] = TAG_CITEM_NO; + TagClickableMap[TagReplacingMap["wake"]] = TAG_CITEM_FPBOX; + TagClickableMap[TagReplacingMap["tssr"]] = TAG_CITEM_NO; + TagClickableMap[TagReplacingMap["sid"]] = + TagClickableMap[TagReplacingMap["shid"]] = TAG_CITEM_SID; + TagClickableMap[TagReplacingMap["origin"]] = TAG_CITEM_FPBOX; + TagClickableMap[TagReplacingMap["dest"]] = TAG_CITEM_FPBOX; + TagClickableMap[TagReplacingMap["systemid"]] = TAG_CITEM_MANUALCORRELATE; + TagClickableMap[TagReplacingMap["gstatus"]] = TAG_CITEM_GROUNDSTATUS; + TagClickableMap[TagReplacingMap["uk_stand"]] = TAG_CITEM_UKSTAND; + + // + // ----- Now the hard part, drawing (using gdi+) ------- + // + + CSMRRadar::TagTypes TagType = CSMRRadar::TagTypes::Departure; + CSMRRadar::TagTypes ColorTagType = CSMRRadar::TagTypes::Departure; + + if (fp.IsValid() && strcmp(fp.GetFlightPlanData().GetDestination(), + radar_screen->getActiveAirport().c_str()) == 0) { + TagType = CSMRRadar::TagTypes::Arrival; + ColorTagType = CSMRRadar::TagTypes::Arrival; + } + + if (reportedGs > 50) { + TagType = CSMRRadar::TagTypes::Airborne; + + // Is "use_departure_arrival_coloring" enabled? if not, then use the + // airborne colors + bool useDepArrColors = + radar_screen->CurrentConfig + ->getActiveProfile()["labels"]["airborne"] + ["use_departure_arrival_coloring"] + .GetBool(); + if (!useDepArrColors) { + ColorTagType = CSMRRadar::TagTypes::Airborne; + } + } + + bool AcisCorrelated = radar_screen->IsCorrelated( + radar_screen->GetPlugIn()->FlightPlanSelect(rt.GetCallsign()), rt); + if (!AcisCorrelated && reportedGs >= 3) { + TagType = CSMRRadar::TagTypes::Uncorrelated; + ColorTagType = CSMRRadar::TagTypes::Uncorrelated; + } + + // First we need to figure out the tag size + + int TagWidth = 0, TagHeight = 0; + RectF mesureRect; + gdi->MeasureString(L" ", wcslen(L" "), + radar_screen->customFonts[radar_screen->currentFontSize], + PointF(0, 0), &Gdiplus::StringFormat(), &mesureRect); + int blankWidth = (int)mesureRect.GetRight(); + + mesureRect = RectF(0, 0, 0, 0); + gdi->MeasureString(L"AZERTYUIOPQSDFGHJKLMWXCVBN", + wcslen(L"AZERTYUIOPQSDFGHJKLMWXCVBN"), + radar_screen->customFonts[radar_screen->currentFontSize], + PointF(0, 0), &Gdiplus::StringFormat(), &mesureRect); + int oneLineHeight = (int)mesureRect.GetBottom(); + + const Value &LabelsSettings = + radar_screen->CurrentConfig->getActiveProfile()["labels"]; + const Value &LabelLines = + LabelsSettings[Utils::getEnumString(TagType).c_str()]["definition"]; + vector> ReplacedLabelLines; + + if (!LabelLines.IsArray()) + return; + + for (unsigned int i = 0; i < LabelLines.Size(); i++) { + + const Value &line = LabelLines[i]; + vector lineStringArray; + + // Adds one line height + TagHeight += oneLineHeight; + + int TempTagWidth = 0; + + for (unsigned int j = 0; j < line.Size(); j++) { + mesureRect = RectF(0, 0, 0, 0); + string element = line[j].GetString(); + + for (auto &kv : TagReplacingMap) + replaceAll(element, kv.first, kv.second); + + lineStringArray.push_back(element); + + wstring wstr = wstring(element.begin(), element.end()); + gdi->MeasureString( + wstr.c_str(), wcslen(wstr.c_str()), + radar_screen->customFonts[radar_screen->currentFontSize], + PointF(0, 0), &Gdiplus::StringFormat(), &mesureRect); + + TempTagWidth += (int)mesureRect.GetRight(); + + if (j != line.Size() - 1) + TempTagWidth += (int)blankWidth; + } + + TagWidth = max(TagWidth, TempTagWidth); + + ReplacedLabelLines.push_back(lineStringArray); + } + TagHeight = TagHeight - 2; + + // Pfiou, done with that, now we can draw the actual rectangle. + + // We need to figure out if the tag color changes according to RIMCAS + // alerts, or not + bool rimcasLabelOnly = + radar_screen->CurrentConfig + ->getActiveProfile()["rimcas"]["rimcas_label_only"] + .GetBool(); + + Color definedBackgroundColor = radar_screen->CurrentConfig->getConfigColor( + LabelsSettings[Utils::getEnumString(ColorTagType).c_str()] + ["background_color"]); + if (TagType == CSMRRadar::TagTypes::Departure) { + if (!TagReplacingMap["sid"].empty() && + radar_screen->CurrentConfig->isSidColorAvail( + TagReplacingMap["sid"], radar_screen->getActiveAirport())) { + definedBackgroundColor = radar_screen->CurrentConfig->getSidColor( + TagReplacingMap["sid"], radar_screen->getActiveAirport()); + } + + if (fp.GetFlightPlanData().GetPlanType()[0] == 'I' && + TagReplacingMap["asid"].empty() && + LabelsSettings[Utils::getEnumString(ColorTagType).c_str()].HasMember( + "nosid_color")) { + definedBackgroundColor = radar_screen->CurrentConfig->getConfigColor( + LabelsSettings[Utils::getEnumString(ColorTagType).c_str()] + ["nosid_color"]); + } + } + if (TagReplacingMap["actype"] == "NoFPL" && + LabelsSettings[Utils::getEnumString(ColorTagType).c_str()].HasMember( + "nofpl_color")) { + definedBackgroundColor = radar_screen->CurrentConfig->getConfigColor( + LabelsSettings[Utils::getEnumString(ColorTagType).c_str()] + ["nofpl_color"]); + } + + Color TagBackgroundColor = radar_screen->RimcasInstance->GetAircraftColor( + rt.GetCallsign(), definedBackgroundColor, + radar_screen->CurrentConfig->getConfigColor( + LabelsSettings[Utils::getEnumString(ColorTagType).c_str()] + ["background_color_on_runway"]), + radar_screen->CurrentConfig->getConfigColor( + radar_screen->CurrentConfig + ->getActiveProfile()["rimcas"]["background_color_stage_one"]), + radar_screen->CurrentConfig->getConfigColor( + radar_screen->CurrentConfig + ->getActiveProfile()["rimcas"]["background_color_stage_two"])); + + if (rimcasLabelOnly) + TagBackgroundColor = radar_screen->RimcasInstance->GetAircraftColor( + rt.GetCallsign(), definedBackgroundColor, + radar_screen->CurrentConfig->getConfigColor( + LabelsSettings[Utils::getEnumString(ColorTagType).c_str()] + ["background_color_on_runway"])); + + CRect TagBackgroundRect( + TagCenter.x - (TagWidth / 2), TagCenter.y - (TagHeight / 2), + TagCenter.x + (TagWidth / 2), TagCenter.y + (TagHeight / 2)); + + if (Is_Inside(TagBackgroundRect.TopLeft(), appAreaVect) && + Is_Inside(RtPoint, appAreaVect) && + Is_Inside(TagBackgroundRect.BottomRight(), appAreaVect)) { + + SolidBrush TagBackgroundBrush(TagBackgroundColor); + gdi->FillRectangle(&TagBackgroundBrush, CopyRect(TagBackgroundRect)); + + SolidBrush FontColor(radar_screen->ColorManager->get_corrected_color( + "label", + radar_screen->CurrentConfig->getConfigColor( + LabelsSettings[Utils::getEnumString(ColorTagType).c_str()] + ["text_color"]))); + SolidBrush SquawkErrorColor( + radar_screen->ColorManager->get_corrected_color( + "label", radar_screen->CurrentConfig->getConfigColor( + LabelsSettings["squawk_error_color"]))); + SolidBrush RimcasTextColor(radar_screen->CurrentConfig->getConfigColor( + radar_screen->CurrentConfig + ->getActiveProfile()["rimcas"]["alert_text_color"])); + + int heightOffset = 0; + for (auto &&line : ReplacedLabelLines) { + int widthOffset = 0; + for (auto &&element : line) { + SolidBrush *color = &FontColor; + if (TagReplacingMap["sqerror"].size() > 0 && + strcmp(element.c_str(), TagReplacingMap["sqerror"].c_str()) == 0) + color = &SquawkErrorColor; + + if (radar_screen->RimcasInstance->getAlert(rt.GetCallsign()) != + CRimcas::NoAlert) + color = &RimcasTextColor; + + RectF mRect(0, 0, 0, 0); + + wstring welement = wstring(element.begin(), element.end()); + + gdi->DrawString( + welement.c_str(), wcslen(welement.c_str()), + radar_screen->customFonts[radar_screen->currentFontSize], + PointF(Gdiplus::REAL(TagBackgroundRect.left + widthOffset), + Gdiplus::REAL(TagBackgroundRect.top + heightOffset)), + &Gdiplus::StringFormat(), color); + + gdi->MeasureString( + welement.c_str(), wcslen(welement.c_str()), + radar_screen->customFonts[radar_screen->currentFontSize], + PointF(0, 0), &Gdiplus::StringFormat(), &mRect); + + CRect ItemRect( + TagBackgroundRect.left + widthOffset, + TagBackgroundRect.top + heightOffset, + TagBackgroundRect.left + widthOffset + (int)mRect.GetRight(), + TagBackgroundRect.top + heightOffset + (int)mRect.GetBottom()); + + radar_screen->AddScreenObject( + TagClickableMap[element], rt.GetCallsign(), ItemRect, false, + radar_screen->GetBottomLine(rt.GetCallsign()).c_str()); + + widthOffset += (int)mRect.GetRight(); + widthOffset += blankWidth; + } + + heightOffset += oneLineHeight; + } + + // Drawing the leader line + RECT TagBackRectData = TagBackgroundRect; + POINT toDraw1, toDraw2; + if (LiangBarsky(TagBackRectData, RtPoint, TagBackgroundRect.CenterPoint(), + toDraw1, toDraw2)) + gdi->DrawLine( + &Pen(radar_screen->ColorManager->get_corrected_color("symbol", + Color::White)), + PointF(Gdiplus::REAL(RtPoint.x), Gdiplus::REAL(RtPoint.y)), + PointF(Gdiplus::REAL(toDraw1.x), Gdiplus::REAL(toDraw1.y))); + + // If we use a RIMCAS label only, we display it, and adapt the rectangle + CRect oldCrectSave = TagBackgroundRect; + + if (rimcasLabelOnly) { + Color RimcasLabelColor = radar_screen->RimcasInstance->GetAircraftColor( + rt.GetCallsign(), Color::AliceBlue, Color::AliceBlue, + radar_screen->CurrentConfig->getConfigColor( + radar_screen->CurrentConfig + ->getActiveProfile()["rimcas"] + ["background_color_stage_one"]), + radar_screen->CurrentConfig->getConfigColor( + radar_screen->CurrentConfig + ->getActiveProfile()["rimcas"] + ["background_color_stage_two"])); + + if (RimcasLabelColor.ToCOLORREF() != + Color(Color::AliceBlue).ToCOLORREF()) { + int rimcas_height = 0; + + wstring wrimcas_height = wstring(L"ALERT"); + + RectF RectRimcas_height; + + gdi->MeasureString( + wrimcas_height.c_str(), wcslen(wrimcas_height.c_str()), + radar_screen->customFonts[radar_screen->currentFontSize], + PointF(0, 0), &Gdiplus::StringFormat(), &RectRimcas_height); + rimcas_height = int(RectRimcas_height.GetBottom()); + + // Drawing the rectangle + + CRect RimcasLabelRect(TagBackgroundRect.left, + TagBackgroundRect.top - rimcas_height, + TagBackgroundRect.right, TagBackgroundRect.top); + gdi->FillRectangle(&SolidBrush(RimcasLabelColor), + CopyRect(RimcasLabelRect)); + TagBackgroundRect.top -= rimcas_height; + + // Drawing the text + + wstring rimcasw = wstring(L"ALERT"); + StringFormat stformat = new StringFormat(); + stformat.SetAlignment(StringAlignment::StringAlignmentCenter); + gdi->DrawString( + rimcasw.c_str(), wcslen(rimcasw.c_str()), + radar_screen->customFonts[radar_screen->currentFontSize], + PointF( + Gdiplus::REAL( + (TagBackgroundRect.left + TagBackgroundRect.right) / 2), + Gdiplus::REAL(TagBackgroundRect.top)), + &stformat, &RimcasTextColor); + } + } + + // Adding the tag screen object + + // radar_screen->AddScreenObject(DRAWING_TAG, rt.GetCallsign(), + // TagBackgroundRect, true, GetBottomLine(rt.GetCallsign()).c_str()); + + TagBackgroundRect = oldCrectSave; + + // Now adding the clickable zones + } + } + + // Distance tools here + for (auto &&kv : DistanceTools) { + CRadarTarget one = + radar_screen->GetPlugIn()->RadarTargetSelect(kv.first.c_str()); + CRadarTarget two = + radar_screen->GetPlugIn()->RadarTargetSelect(kv.second.c_str()); + + int radarRange = radar_screen->CurrentConfig + ->getActiveProfile()["filters"]["radar_range_nm"] + .GetInt(); + + if (one.GetGS() < 60 || + one.GetPosition().GetPressureAltitude() > m_Filter || !one.IsValid() || + !one.GetPosition().IsValid() || + one.GetPosition().GetPosition().DistanceTo(AptPositions[icao]) > + radarRange) + continue; + + if (two.GetGS() < 60 || + two.GetPosition().GetPressureAltitude() > m_Filter || !two.IsValid() || + !two.GetPosition().IsValid() || + two.GetPosition().GetPosition().DistanceTo(AptPositions[icao]) > + radarRange) + continue; + + CPen Pen(PS_SOLID, 1, RGB(255, 255, 255)); + CPen *oldPen = dc.SelectObject(&Pen); + + POINT onePoint = projectPoint(one.GetPosition().GetPosition()); + POINT twoPoint = projectPoint(two.GetPosition().GetPosition()); + + POINT toDraw1, toDraw2; + if (LiangBarsky(m_Area, onePoint, twoPoint, toDraw1, toDraw2)) { + dc.MoveTo(toDraw1); + dc.LineTo(toDraw2); + } + + POINT TextPos = {twoPoint.x + 20, twoPoint.y}; + + double Distance = one.GetPosition().GetPosition().DistanceTo( + two.GetPosition().GetPosition()); + double Bearing = one.GetPosition().GetPosition().DirectionTo( + two.GetPosition().GetPosition()); + + string distances = std::to_string(Distance); + size_t decimal_pos = distances.find("."); + distances = distances.substr(0, decimal_pos + 2); + + string bearings = std::to_string(Bearing); + decimal_pos = bearings.find("."); + bearings = bearings.substr(0, decimal_pos + 2); + + string text = bearings; + text += "� / "; + text += distances; + text += "nm"; + COLORREF old_color = dc.SetTextColor(RGB(0, 0, 0)); + + CRect ClickableRect = {TextPos.x - 2, TextPos.y, + TextPos.x + dc.GetTextExtent(text.c_str()).cx + 2, + TextPos.y + dc.GetTextExtent(text.c_str()).cy}; + if (Is_Inside(ClickableRect.TopLeft(), appAreaVect) && + Is_Inside(ClickableRect.BottomRight(), appAreaVect)) { + gdi->FillRectangle(&SolidBrush(Color(127, 122, 122)), + CopyRect(ClickableRect)); + dc.Draw3dRect(ClickableRect, RGB(75, 75, 75), RGB(45, 45, 45)); + dc.TextOutA(TextPos.x, TextPos.y, text.c_str()); + + radar_screen->AddScreenObject(RIMCAS_DISTANCE_TOOL, + string(kv.first + "," + kv.second).c_str(), + ClickableRect, false, ""); + } + + dc.SetTextColor(old_color); + + dc.SelectObject(oldPen); + } + + // Resize square + qBackgroundColor = RGB(60, 60, 60); + POINT BottomRight = {m_Area.right, m_Area.bottom}; + POINT TopLeft = {BottomRight.x - 10, BottomRight.y - 10}; + CRect ResizeArea = {TopLeft, BottomRight}; + ResizeArea.NormalizeRect(); + dc.FillSolidRect(ResizeArea, qBackgroundColor); + radar_screen->AddScreenObject(m_Id, "resize", ResizeArea, true, ""); + + dc.Draw3dRect(ResizeArea, RGB(0, 0, 0), RGB(0, 0, 0)); + + // Sides + // CBrush FrameBrush(RGB(35, 35, 35)); + CBrush FrameBrush(RGB(127, 122, 122)); + COLORREF TopBarTextColor(RGB(35, 35, 35)); + dc.FrameRect(windowAreaCRect, &FrameBrush); + + // Topbar + TopLeft = windowAreaCRect.TopLeft(); + TopLeft.y = TopLeft.y - 15; + BottomRight = {windowAreaCRect.right, windowAreaCRect.top}; + CRect TopBar(TopLeft, BottomRight); + TopBar.NormalizeRect(); + dc.FillRect(TopBar, &FrameBrush); + POINT TopLeftText = {TopBar.left + 5, + TopBar.bottom - dc.GetTextExtent("SRW 1").cy}; + COLORREF oldTextColorC = dc.SetTextColor(TopBarTextColor); + + radar_screen->AddScreenObject(m_Id, "topbar", TopBar, true, ""); + + string Toptext = "SRW " + std::to_string(m_Id - APPWINDOW_BASE); + dc.TextOutA(TopLeftText.x + (TopBar.right - TopBar.left) / 2 - + dc.GetTextExtent("SRW 1").cx, + TopLeftText.y, Toptext.c_str()); + + // Range button + CRect RangeRect = + Utils::drawToolbarButton(&dc, "Z", TopBar, 29, mouseLocation); + radar_screen->AddScreenObject(m_Id, "range", RangeRect, false, ""); + + // Filter button + CRect FilterRect = + Utils::drawToolbarButton(&dc, "F", TopBar, 42, mouseLocation); + radar_screen->AddScreenObject(m_Id, "filter", FilterRect, false, ""); + + // Rotate button + CRect RotateRect = + Utils::drawToolbarButton(&dc, "R", TopBar, 55, mouseLocation); + radar_screen->AddScreenObject(m_Id, "rotate", RotateRect, false, ""); + + dc.SetTextColor(oldTextColorC); + + // Close + POINT TopLeftClose = {TopBar.right - 16, TopBar.top + 2}; + POINT BottomRightClose = {TopBar.right - 5, TopBar.bottom - 2}; + CRect CloseRect(TopLeftClose, BottomRightClose); + CloseRect.NormalizeRect(); + CBrush CloseBrush(RGB(60, 60, 60)); + dc.FillRect(CloseRect, &CloseBrush); + CPen BlackPen(PS_SOLID, 1, RGB(0, 0, 0)); + dc.SelectObject(BlackPen); + dc.MoveTo(CloseRect.TopLeft()); + dc.LineTo(CloseRect.BottomRight()); + dc.MoveTo({CloseRect.right - 1, CloseRect.top}); + dc.LineTo({CloseRect.left - 1, CloseRect.bottom}); + + if (mouseWithin(mouseLocation, CloseRect)) + dc.Draw3dRect(CloseRect, RGB(45, 45, 45), RGB(75, 75, 75)); + else + dc.Draw3dRect(CloseRect, RGB(75, 75, 75), RGB(45, 45, 45)); + + radar_screen->AddScreenObject(m_Id, "close", CloseRect, false, ""); + + dc.Detach(); } \ No newline at end of file diff --git a/vSMR/InsetWindow.h b/vSMR/InsetWindow.h index d2c0e0132..1d46b3db4 100644 --- a/vSMR/InsetWindow.h +++ b/vSMR/InsetWindow.h @@ -1,36 +1,40 @@ #pragma once -#include "SMRRadar.hpp" #include "EuroScopePlugIn.h" -#include -#include #include "Logger.h" +#include +#include + using namespace std; using namespace EuroScopePlugIn; -class CInsetWindow -{ +class CSMRRadar; + +class CInsetWindow { public: - CInsetWindow(int Id); - virtual ~CInsetWindow(); - - // Definition - int m_Id = -1, m_Scale = 15, m_Filter = 5500; - RECT m_Area = { 200, 200, 600, 500 }; - POINT m_Offset = { 0, 0 }, m_OffsetInit = { 0, 0 }, m_OffsetDrag = { 0, 0 }; - bool m_Grip = false; - double m_Rotation = 0; - - map m_TagAngles; - - virtual void render(HDC Hdc, CSMRRadar * radar_screen, Graphics* gdi, POINT mouseLocation, multimap DistanceTools); - virtual void setAirport(string icao); - virtual POINT projectPoint(CPosition pos); - virtual void OnClickScreenObject(const char * sItemString, POINT Pt, int Button); - virtual bool OnMoveScreenObject(const char * sObjectId, POINT Pt, RECT Area, bool released); - + CInsetWindow(int Id); + virtual ~CInsetWindow(); + + // Definition + int m_Id = -1, m_Scale = 15, m_Filter = 5500; + RECT m_Area = {200, 200, 600, 500}; + POINT m_Offset = {0, 0}, m_OffsetInit = {0, 0}, m_OffsetDrag = {0, 0}; + bool m_Grip = false; + double m_Rotation = 0; + + map m_TagAngles; + + virtual void render(HDC Hdc, CSMRRadar *radar_screen, Graphics *gdi, + POINT mouseLocation, + multimap DistanceTools); + virtual void setAirport(string icao); + virtual POINT projectPoint(CPosition pos); + virtual void OnClickScreenObject(const char *sItemString, POINT Pt, + int Button); + virtual bool OnMoveScreenObject(const char *sObjectId, POINT Pt, RECT Area, + bool released); + private: - string icao; - map AptPositions; + string icao; + map AptPositions; }; - From 339d812eed63d91eb2b6df616ffdd3092ead1f35 Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 12:41:15 +0000 Subject: [PATCH 06/44] fix msvc errors --- vSMR/InsetWindow.cpp | 2 -- vSMR/InsetWindow.h | 6 ++---- vSMR/Rimcas.cpp | 2 +- vSMR/SMRRadar.cpp | 4 +--- 4 files changed, 4 insertions(+), 10 deletions(-) diff --git a/vSMR/InsetWindow.cpp b/vSMR/InsetWindow.cpp index fe168f9a8..ed810d91d 100644 --- a/vSMR/InsetWindow.cpp +++ b/vSMR/InsetWindow.cpp @@ -1,7 +1,5 @@ #include "stdafx.h" - #include "InsetWindow.h" -#include "SMRRadar.hpp" CInsetWindow::CInsetWindow(int Id) { m_Id = Id; } diff --git a/vSMR/InsetWindow.h b/vSMR/InsetWindow.h index 1d46b3db4..4572e9f4d 100644 --- a/vSMR/InsetWindow.h +++ b/vSMR/InsetWindow.h @@ -1,15 +1,13 @@ #pragma once #include "EuroScopePlugIn.h" #include "Logger.h" +#include "SMRRadar.hpp" #include #include - using namespace std; using namespace EuroScopePlugIn; -class CSMRRadar; - class CInsetWindow { public: CInsetWindow(int Id); @@ -24,7 +22,7 @@ class CInsetWindow { map m_TagAngles; - virtual void render(HDC Hdc, CSMRRadar *radar_screen, Graphics *gdi, + virtual void render(HDC Hdc, CSMRRadar *radar_screen, Gdiplus::Graphics *gdi, POINT mouseLocation, multimap DistanceTools); virtual void setAirport(string icao); diff --git a/vSMR/Rimcas.cpp b/vSMR/Rimcas.cpp index d446f8fa2..addf59763 100644 --- a/vSMR/Rimcas.cpp +++ b/vSMR/Rimcas.cpp @@ -1,7 +1,7 @@ #include "stdafx.h" - #include "Rimcas.hpp" + CRimcas::CRimcas() {} CRimcas::~CRimcas() { Reset(); } diff --git a/vSMR/SMRRadar.cpp b/vSMR/SMRRadar.cpp index 81b640f0a..2b5e1baa8 100644 --- a/vSMR/SMRRadar.cpp +++ b/vSMR/SMRRadar.cpp @@ -1,8 +1,6 @@ #include "stdafx.h" - -#include "Resource.h" #include "SMRRadar.hpp" - +#include "Resource.h" ULONG_PTR m_gdiplusToken; CPoint mouseLocation(0, 0); From e513317bdd50f3b7963dc388608b7a68677375e6 Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 12:46:30 +0000 Subject: [PATCH 07/44] refactor: remove unnecessary include of stdafx.h and adjust include order --- vSMR/Constant.hpp | 1 - vSMR/InsetWindow.cpp | 2 ++ vSMR/InsetWindow.h | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/vSMR/Constant.hpp b/vSMR/Constant.hpp index 7779686e5..a45a5f1b9 100644 --- a/vSMR/Constant.hpp +++ b/vSMR/Constant.hpp @@ -1,7 +1,6 @@ #pragma once #include "EuroScopePlugIn.h" -#include "stdafx.h" #include #define _USE_MATH_DEFINES diff --git a/vSMR/InsetWindow.cpp b/vSMR/InsetWindow.cpp index ed810d91d..c84e46ad6 100644 --- a/vSMR/InsetWindow.cpp +++ b/vSMR/InsetWindow.cpp @@ -1,5 +1,7 @@ #include "stdafx.h" #include "InsetWindow.h" +#include "SMRRadar.hpp" + CInsetWindow::CInsetWindow(int Id) { m_Id = Id; } diff --git a/vSMR/InsetWindow.h b/vSMR/InsetWindow.h index 4572e9f4d..777c406a2 100644 --- a/vSMR/InsetWindow.h +++ b/vSMR/InsetWindow.h @@ -1,13 +1,14 @@ #pragma once #include "EuroScopePlugIn.h" #include "Logger.h" -#include "SMRRadar.hpp" #include #include using namespace std; using namespace EuroScopePlugIn; +class CSMRRadar; + class CInsetWindow { public: CInsetWindow(int Id); From 007bf89a195bf982b875d61d31df05e5f044c92a Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 13:02:44 +0000 Subject: [PATCH 08/44] start time at liftoff --- vSMR/Rimcas.cpp | 20 +++++++++++++++++--- vSMR/Rimcas.hpp | 3 ++- vSMR/SMRRadar.cpp | 11 +++++++---- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/vSMR/Rimcas.cpp b/vSMR/Rimcas.cpp index addf59763..2798798ea 100644 --- a/vSMR/Rimcas.cpp +++ b/vSMR/Rimcas.cpp @@ -1,6 +1,5 @@ -#include "stdafx.h" #include "Rimcas.hpp" - +#include "stdafx.h" CRimcas::CRimcas() {} @@ -379,12 +378,27 @@ void CRimcas::TrackDeparture(CRadarTarget Rt, CFlightPlan fp, info.airborneFreq = "QSY"; - info.liftoffTime = clock(); + info.groundAltitude = + Rt.GetPreviousPosition(Rt.GetPosition()).GetPressureAltitude(); + info.liftoffTime = 0; + info.timerStarted = false; info.dismissed = false; DepartedAircraft[callsign] = info; } } + + // Update timer once aircraft climbs above ground level + if (DepartedAircraft.find(callsign) != DepartedAircraft.end()) { + DepartureInfo &info = DepartedAircraft[callsign]; + if (!info.timerStarted && isAirborne) { + int currentAltitude = Rt.GetPosition().GetPressureAltitude(); + if (currentAltitude > info.groundAltitude) { + info.liftoffTime = clock(); + info.timerStarted = true; + } + } + } } void CRimcas::UpdateDepartureTimer(int departureDisplayDurationSecs) { diff --git a/vSMR/Rimcas.hpp b/vSMR/Rimcas.hpp index da2938ac1..94be21bdb 100644 --- a/vSMR/Rimcas.hpp +++ b/vSMR/Rimcas.hpp @@ -12,7 +12,6 @@ #include #include - class CSMRRadar; using namespace std; using namespace Gdiplus; @@ -51,6 +50,8 @@ class CRimcas { string airborneFreq = ""; // QSY frequency clock_t liftoffTime = 0; bool dismissed = false; + int groundAltitude = 0; // Altitude when on ground + bool timerStarted = false; // Whether timer has begun }; map RunwayAreas; diff --git a/vSMR/SMRRadar.cpp b/vSMR/SMRRadar.cpp index 2b5e1baa8..26db6aac5 100644 --- a/vSMR/SMRRadar.cpp +++ b/vSMR/SMRRadar.cpp @@ -1,6 +1,7 @@ -#include "stdafx.h" #include "SMRRadar.hpp" #include "Resource.h" +#include "stdafx.h" + ULONG_PTR m_gdiplusToken; CPoint mouseLocation(0, 0); @@ -3211,9 +3212,11 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { CRimcas::DepartureInfo &info = pair.second; int rowX = DepWindowRect.left + 5; - // Calculate elapsed time since liftoff - double elapsedSecs = - (double)(currentTime - info.liftoffTime) / CLOCKS_PER_SEC; + // Calculate elapsed time since liftoff (only if timer has started) + double elapsedSecs = 0; + if (info.timerStarted) { + elapsedSecs = (double)(currentTime - info.liftoffTime) / CLOCKS_PER_SEC; + } int minutes = (int)(elapsedSecs / 60); int seconds = (int)(elapsedSecs) % 60; From da42d839f003d748cbbc8440443e7a7ddafa0a4a Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 13:05:14 +0000 Subject: [PATCH 09/44] change to use SID now --- vSMR/Rimcas.cpp | 2 +- vSMR/Rimcas.hpp | 2 +- vSMR/SMRRadar.cpp | 5 ++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/vSMR/Rimcas.cpp b/vSMR/Rimcas.cpp index 2798798ea..83aca74b2 100644 --- a/vSMR/Rimcas.cpp +++ b/vSMR/Rimcas.cpp @@ -368,7 +368,7 @@ void CRimcas::TrackDeparture(CRadarTarget Rt, CFlightPlan fp, if (DepartedAircraft.find(callsign) == DepartedAircraft.end()) { DepartureInfo info; info.callsign = callsign; - info.destination = fp.GetFlightPlanData().GetDestination(); + info.sid = fp.GetFlightPlanData().GetSidName(); info.acType = fp.GetFlightPlanData().GetAircraftFPType(); if (info.acType.size() > 4) { info.acType = info.acType.substr(0, 4); diff --git a/vSMR/Rimcas.hpp b/vSMR/Rimcas.hpp index 94be21bdb..6dfd03f28 100644 --- a/vSMR/Rimcas.hpp +++ b/vSMR/Rimcas.hpp @@ -44,7 +44,7 @@ class CRimcas { // Departure timer info struct DepartureInfo { string callsign = ""; - string destination = ""; + string sid = ""; // SID/departure string acType = ""; string wakeTurbCat = ""; string airborneFreq = ""; // QSY frequency diff --git a/vSMR/SMRRadar.cpp b/vSMR/SMRRadar.cpp index 26db6aac5..0acf45763 100644 --- a/vSMR/SMRRadar.cpp +++ b/vSMR/SMRRadar.cpp @@ -1,7 +1,6 @@ +#include "stdafx.h" #include "SMRRadar.hpp" #include "Resource.h" -#include "stdafx.h" - ULONG_PTR m_gdiplusToken; CPoint mouseLocation(0, 0); @@ -3233,7 +3232,7 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { } if (depWindowShowDest) { - dc.TextOutA(rowX, rowY, info.destination.c_str()); + dc.TextOutA(rowX, rowY, info.sid.c_str()); rowX += colDestWidth + colPadding; } From a8d659037a299970b49c76de9dc1a9b4571bbd78 Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 13:07:40 +0000 Subject: [PATCH 10/44] attempt to get airborne QSY --- vSMR/Rimcas.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/vSMR/Rimcas.cpp b/vSMR/Rimcas.cpp index 83aca74b2..a63c83f4b 100644 --- a/vSMR/Rimcas.cpp +++ b/vSMR/Rimcas.cpp @@ -376,7 +376,11 @@ void CRimcas::TrackDeparture(CRadarTarget Rt, CFlightPlan fp, info.wakeTurbCat = ""; info.wakeTurbCat += fp.GetFlightPlanData().GetAircraftWtc(); - info.airborneFreq = "QSY"; + // Get airborne QSY from UKCP annotation (index 8) or default to "QSY" + info.airborneFreq = fp.GetControllerAssignedData().GetFlightStripAnnotation(8); + if (info.airborneFreq.length() == 0) { + info.airborneFreq = "QSY"; + } info.groundAltitude = Rt.GetPreviousPosition(Rt.GetPosition()).GetPressureAltitude(); From 5b0d12a5fd1adc5ef2e9ed7e5dca53ff4ac02211 Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 13:11:42 +0000 Subject: [PATCH 11/44] auto formatting :/ --- vSMR/Rimcas.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vSMR/Rimcas.cpp b/vSMR/Rimcas.cpp index a63c83f4b..df43064a6 100644 --- a/vSMR/Rimcas.cpp +++ b/vSMR/Rimcas.cpp @@ -1,5 +1,5 @@ -#include "Rimcas.hpp" #include "stdafx.h" +#include "Rimcas.hpp" CRimcas::CRimcas() {} From 26dfd44fecb44703438a6f16d577cc948feea834 Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 13:22:23 +0000 Subject: [PATCH 12/44] use the vertical speeed to work out if the plane is airborne --- vSMR/Rimcas.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/vSMR/Rimcas.cpp b/vSMR/Rimcas.cpp index df43064a6..00d933c26 100644 --- a/vSMR/Rimcas.cpp +++ b/vSMR/Rimcas.cpp @@ -392,12 +392,14 @@ void CRimcas::TrackDeparture(CRadarTarget Rt, CFlightPlan fp, } } - // Update timer once aircraft climbs above ground level + // Update timer once aircraft climbs above ground level and is ascending if (DepartedAircraft.find(callsign) != DepartedAircraft.end()) { DepartureInfo &info = DepartedAircraft[callsign]; if (!info.timerStarted && isAirborne) { int currentAltitude = Rt.GetPosition().GetPressureAltitude(); - if (currentAltitude > info.groundAltitude) { + int verticalSpeed = Rt.GetPosition().GetVerticalSpeed(); + // Only start timer if climbing (positive vertical speed > 100 fpm) + if (currentAltitude > info.groundAltitude && verticalSpeed > 100) { info.liftoffTime = clock(); info.timerStarted = true; } From c99045b6fe137677da7f31ed5667813600f4d639 Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 13:30:53 +0000 Subject: [PATCH 13/44] apparently not that easy --- vSMR/Rimcas.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/vSMR/Rimcas.cpp b/vSMR/Rimcas.cpp index 00d933c26..c508261e6 100644 --- a/vSMR/Rimcas.cpp +++ b/vSMR/Rimcas.cpp @@ -397,9 +397,10 @@ void CRimcas::TrackDeparture(CRadarTarget Rt, CFlightPlan fp, DepartureInfo &info = DepartedAircraft[callsign]; if (!info.timerStarted && isAirborne) { int currentAltitude = Rt.GetPosition().GetPressureAltitude(); - int verticalSpeed = Rt.GetPosition().GetVerticalSpeed(); - // Only start timer if climbing (positive vertical speed > 100 fpm) - if (currentAltitude > info.groundAltitude && verticalSpeed > 100) { + int previousAltitude = Rt.GetPreviousPosition(Rt.GetPosition()).GetPressureAltitude(); + int verticalSpeed = currentAltitude - previousAltitude; + // Only start timer if climbing (positive altitude change) + if (currentAltitude > info.groundAltitude && verticalSpeed > 0) { info.liftoffTime = clock(); info.timerStarted = true; } From ab86ca7f418894a2cf96aefa09a41f57a3c71f8e Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 13:45:07 +0000 Subject: [PATCH 14/44] Thanks Euroscope.h --- vSMR/Rimcas.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/vSMR/Rimcas.cpp b/vSMR/Rimcas.cpp index c508261e6..c55ab6434 100644 --- a/vSMR/Rimcas.cpp +++ b/vSMR/Rimcas.cpp @@ -392,15 +392,14 @@ void CRimcas::TrackDeparture(CRadarTarget Rt, CFlightPlan fp, } } - // Update timer once aircraft climbs above ground level and is ascending if (DepartedAircraft.find(callsign) != DepartedAircraft.end()) { DepartureInfo &info = DepartedAircraft[callsign]; if (!info.timerStarted && isAirborne) { int currentAltitude = Rt.GetPosition().GetPressureAltitude(); - int previousAltitude = Rt.GetPreviousPosition(Rt.GetPosition()).GetPressureAltitude(); - int verticalSpeed = currentAltitude - previousAltitude; - // Only start timer if climbing (positive altitude change) - if (currentAltitude > info.groundAltitude && verticalSpeed > 0) { + int verticalSpeed = Rt.GetVerticalSpeed(); + + // Only start timer if climbing above ground altitude with > 100 fpm climb rate + if (currentAltitude > info.groundAltitude && verticalSpeed > 100) { info.liftoffTime = clock(); info.timerStarted = true; } From 614d4081ae8a2afc9e545b2498f5c6d89449be42 Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 14:07:35 +0000 Subject: [PATCH 15/44] swap wording --- vSMR/SMRRadar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vSMR/SMRRadar.cpp b/vSMR/SMRRadar.cpp index 0acf45763..ba2f74498 100644 --- a/vSMR/SMRRadar.cpp +++ b/vSMR/SMRRadar.cpp @@ -3342,7 +3342,7 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { GetPlugIn()->AddPopupListElement("Show Callsign", "", RIMCAS_DEP_WINDOW_COL_CALLSIGN, false, int(depWindowShowCallsign)); - GetPlugIn()->AddPopupListElement("Show Destination", "", + GetPlugIn()->AddPopupListElement("Show Departure", "", RIMCAS_DEP_WINDOW_COL_DEST, false, int(depWindowShowDest)); GetPlugIn()->AddPopupListElement("Show AC Type", "", From 66ea6be2f85167d96428d3bc318844066b4dbde1 Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 14:07:46 +0000 Subject: [PATCH 16/44] add to list once on the roll --- vSMR/Rimcas.cpp | 59 ++++++++++++++++++++----------------------------- 1 file changed, 24 insertions(+), 35 deletions(-) diff --git a/vSMR/Rimcas.cpp b/vSMR/Rimcas.cpp index c55ab6434..f72c19ffc 100644 --- a/vSMR/Rimcas.cpp +++ b/vSMR/Rimcas.cpp @@ -353,48 +353,37 @@ void CRimcas::TrackDeparture(CRadarTarget Rt, CFlightPlan fp, return; int reportedGS = Rt.GetPosition().GetReportedGS(); - bool isAirborne = reportedGS > 50; - - bool wasOnGround = false; - if (AircraftWasOnGround.find(callsign) != AircraftWasOnGround.end()) { - wasOnGround = AircraftWasOnGround[callsign]; - } else { - wasOnGround = !isAirborne; - } - - AircraftWasOnGround[callsign] = !isAirborne; - - if (wasOnGround && isAirborne) { - if (DepartedAircraft.find(callsign) == DepartedAircraft.end()) { - DepartureInfo info; - info.callsign = callsign; - info.sid = fp.GetFlightPlanData().GetSidName(); - info.acType = fp.GetFlightPlanData().GetAircraftFPType(); - if (info.acType.size() > 4) { - info.acType = info.acType.substr(0, 4); - } - info.wakeTurbCat = ""; - info.wakeTurbCat += fp.GetFlightPlanData().GetAircraftWtc(); + bool isMoving = reportedGS > 50; + + // Add aircraft to display once they start moving (> 50 kts) + if (isMoving && DepartedAircraft.find(callsign) == DepartedAircraft.end()) { + DepartureInfo info; + info.callsign = callsign; + info.sid = fp.GetFlightPlanData().GetSidName(); + info.acType = fp.GetFlightPlanData().GetAircraftFPType(); + if (info.acType.size() > 4) { + info.acType = info.acType.substr(0, 4); + } + info.wakeTurbCat = ""; + info.wakeTurbCat += fp.GetFlightPlanData().GetAircraftWtc(); - // Get airborne QSY from UKCP annotation (index 8) or default to "QSY" - info.airborneFreq = fp.GetControllerAssignedData().GetFlightStripAnnotation(8); - if (info.airborneFreq.length() == 0) { - info.airborneFreq = "QSY"; - } + info.airborneFreq = fp.GetControllerAssignedData().GetFlightStripAnnotation(8); + if (info.airborneFreq.length() == 0) { + info.airborneFreq = "QSY"; + } - info.groundAltitude = - Rt.GetPreviousPosition(Rt.GetPosition()).GetPressureAltitude(); - info.liftoffTime = 0; - info.timerStarted = false; - info.dismissed = false; + info.groundAltitude = Rt.GetPosition().GetPressureAltitude(); + info.liftoffTime = 0; + info.timerStarted = false; + info.dismissed = false; - DepartedAircraft[callsign] = info; - } + DepartedAircraft[callsign] = info; } + // Start timer once aircraft is airborne and climbing if (DepartedAircraft.find(callsign) != DepartedAircraft.end()) { DepartureInfo &info = DepartedAircraft[callsign]; - if (!info.timerStarted && isAirborne) { + if (!info.timerStarted) { int currentAltitude = Rt.GetPosition().GetPressureAltitude(); int verticalSpeed = Rt.GetVerticalSpeed(); From a167fa52ae366568323cea0d909722bcdcfdda64 Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 14:15:30 +0000 Subject: [PATCH 17/44] remove once above 6 --- vSMR/Rimcas.cpp | 18 ++++++++++++++++-- vSMR/Rimcas.hpp | 2 +- vSMR/SMRRadar.cpp | 2 +- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/vSMR/Rimcas.cpp b/vSMR/Rimcas.cpp index f72c19ffc..5f6612e54 100644 --- a/vSMR/Rimcas.cpp +++ b/vSMR/Rimcas.cpp @@ -383,8 +383,15 @@ void CRimcas::TrackDeparture(CRadarTarget Rt, CFlightPlan fp, // Start timer once aircraft is airborne and climbing if (DepartedAircraft.find(callsign) != DepartedAircraft.end()) { DepartureInfo &info = DepartedAircraft[callsign]; + int currentAltitude = Rt.GetPosition().GetPressureAltitude(); + + // Remove aircraft if above 6000 feet + if (currentAltitude > 6000) { + DepartedAircraft.erase(callsign); + return; + } + if (!info.timerStarted) { - int currentAltitude = Rt.GetPosition().GetPressureAltitude(); int verticalSpeed = Rt.GetVerticalSpeed(); // Only start timer if climbing above ground altitude with > 100 fpm climb rate @@ -396,7 +403,7 @@ void CRimcas::TrackDeparture(CRadarTarget Rt, CFlightPlan fp, } } -void CRimcas::UpdateDepartureTimer(int departureDisplayDurationSecs) { +void CRimcas::UpdateDepartureTimer(int departureDisplayDurationSecs, CRadarScreen *instance) { Logger::info(string(__FUNCSIG__)); clock_t currentTime = clock(); @@ -409,6 +416,13 @@ void CRimcas::UpdateDepartureTimer(int departureDisplayDurationSecs) { continue; } + // Remove if above 6000 feet + CRadarTarget rt = instance->GetPlugIn()->RadarTargetSelect(pair.first.c_str()); + if (rt.IsValid() && rt.GetPosition().GetPressureAltitude() > 6000) { + toRemove.push_back(pair.first); + continue; + } + double elapsedSecs = (double)(currentTime - pair.second.liftoffTime) / CLOCKS_PER_SEC; if (elapsedSecs >= maxDurationSecs) { diff --git a/vSMR/Rimcas.hpp b/vSMR/Rimcas.hpp index 6dfd03f28..589ca7ffa 100644 --- a/vSMR/Rimcas.hpp +++ b/vSMR/Rimcas.hpp @@ -146,7 +146,7 @@ class CRimcas { // Departure timer methods void TrackDeparture(CRadarTarget Rt, CFlightPlan fp, CRadarScreen *instance, string activeAirport); - void UpdateDepartureTimer(int departureDisplayDuration); + void UpdateDepartureTimer(int departureDisplayDuration, CRadarScreen *instance); void DismissDeparture(string callsign); void ClearDismissedDepartures(); diff --git a/vSMR/SMRRadar.cpp b/vSMR/SMRRadar.cpp index ba2f74498..3d0793306 100644 --- a/vSMR/SMRRadar.cpp +++ b/vSMR/SMRRadar.cpp @@ -2530,7 +2530,7 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { .GetInt()); // Update departure timer - remove expired/dismissed entries - RimcasInstance->UpdateDepartureTimer(departureDisplayDuration); + RimcasInstance->UpdateDepartureTimer(departureDisplayDuration, this); RimcasInstance->ClearDismissedDepartures(); graphics.SetSmoothingMode(SmoothingModeDefault); From dbf9da65f16b745c53d7ffb572e10fba16b1f0d4 Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 14:18:29 +0000 Subject: [PATCH 18/44] update spacing --- vSMR/Rimcas.cpp | 11 +++++++++++ vSMR/SMRRadar.cpp | 14 +++++++------- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/vSMR/Rimcas.cpp b/vSMR/Rimcas.cpp index 5f6612e54..5b0e83379 100644 --- a/vSMR/Rimcas.cpp +++ b/vSMR/Rimcas.cpp @@ -359,7 +359,15 @@ void CRimcas::TrackDeparture(CRadarTarget Rt, CFlightPlan fp, if (isMoving && DepartedAircraft.find(callsign) == DepartedAircraft.end()) { DepartureInfo info; info.callsign = callsign; + if (info.callsign.length() > 8) { + info.callsign = info.callsign.substr(0, 8); + } + info.sid = fp.GetFlightPlanData().GetSidName(); + if (info.sid.length() > 7) { + info.sid = info.sid.substr(0, 7); + } + info.acType = fp.GetFlightPlanData().GetAircraftFPType(); if (info.acType.size() > 4) { info.acType = info.acType.substr(0, 4); @@ -371,6 +379,9 @@ void CRimcas::TrackDeparture(CRadarTarget Rt, CFlightPlan fp, if (info.airborneFreq.length() == 0) { info.airborneFreq = "QSY"; } + if (info.airborneFreq.length() > 7) { + info.airborneFreq = info.airborneFreq.substr(0, 7); + } info.groundAltitude = Rt.GetPosition().GetPressureAltitude(); info.liftoffTime = 0; diff --git a/vSMR/SMRRadar.cpp b/vSMR/SMRRadar.cpp index 3d0793306..74f4560b3 100644 --- a/vSMR/SMRRadar.cpp +++ b/vSMR/SMRRadar.cpp @@ -3151,13 +3151,13 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { if (depWindowShowTime) numColumns++; - // Calculate column widths - int colCallsignWidth = dc.GetTextExtent("SPEEDBIRD123").cx; - int colDestWidth = dc.GetTextExtent("XXXX").cx; - int colAcTypeWidth = dc.GetTextExtent("A320").cx; - int colWakeWidth = dc.GetTextExtent("H").cx; - int colFreqWidth = dc.GetTextExtent("124.850").cx; - int colTimeWidth = dc.GetTextExtent("59:59").cx; + // Calculate column widths based on maximum character counts + int colCallsignWidth = dc.GetTextExtent("XXXXXXXX").cx; // 8 chars max + int colDestWidth = dc.GetTextExtent("XXXXXXX").cx; // 7 chars max (SID) + int colAcTypeWidth = dc.GetTextExtent("XXXX").cx; // 4 chars max + int colWakeWidth = dc.GetTextExtent("X").cx; // 1 char max + int colFreqWidth = dc.GetTextExtent("XXXXXXX").cx; // 7 chars max (QSY) + int colTimeWidth = dc.GetTextExtent("XX:XX").cx; // 5 chars (MM:SS) int colPadding = 8; // Calculate total width based on enabled columns From 8a9e69dd9f78b2db01e5642f4ce4eab7312167b6 Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 14:21:04 +0000 Subject: [PATCH 19/44] show dep window --- vSMR/SMRRadar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vSMR/SMRRadar.cpp b/vSMR/SMRRadar.cpp index 74f4560b3..a621ed875 100644 --- a/vSMR/SMRRadar.cpp +++ b/vSMR/SMRRadar.cpp @@ -3134,7 +3134,7 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { // ========================================== Logger::info("Departure Timer Window"); - if (showDepartureWindow && !RimcasInstance->DepartedAircraft.empty()) { + if (showDepartureWindow) { // Calculate box dimensions based on content int maxWidth = 0; int numColumns = 0; From bfd104cb8aacd309b5b6ebb7714a6a426fe26348 Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 14:40:49 +0000 Subject: [PATCH 20/44] correct some logic meaning new planes were not being added --- vSMR/Rimcas.cpp | 4 ++-- vSMR/SMRRadar.cpp | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/vSMR/Rimcas.cpp b/vSMR/Rimcas.cpp index 5b0e83379..3bc2038fd 100644 --- a/vSMR/Rimcas.cpp +++ b/vSMR/Rimcas.cpp @@ -352,8 +352,8 @@ void CRimcas::TrackDeparture(CRadarTarget Rt, CFlightPlan fp, if (origin != activeAirport) return; - int reportedGS = Rt.GetPosition().GetReportedGS(); - bool isMoving = reportedGS > 50; + int groundSpeed = Rt.GetGS(); + bool isMoving = groundSpeed > 50; // Add aircraft to display once they start moving (> 50 kts) if (isMoving && DepartedAircraft.find(callsign) == DepartedAircraft.end()) { diff --git a/vSMR/SMRRadar.cpp b/vSMR/SMRRadar.cpp index a621ed875..70fcf60a9 100644 --- a/vSMR/SMRRadar.cpp +++ b/vSMR/SMRRadar.cpp @@ -2243,6 +2243,12 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { if (!rt.IsValid() || !rt.GetPosition().IsValid()) continue; + // Get flight plan for departure tracking (needed before visibility check) + CFlightPlan fp = GetPlugIn()->FlightPlanSelect(rt.GetCallsign()); + + // Track departures for the departure timer window (before visibility check) + RimcasInstance->TrackDeparture(rt, fp, this, getActiveAirport()); + int reportedGs = rt.GetPosition().GetReportedGS(); int radarRange = CurrentConfig->getActiveProfile()["filters"]["radar_range_nm"].GetInt(); @@ -2256,7 +2262,6 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { continue; // Get aircraft type and stand for RIMCAS - CFlightPlan fp = GetPlugIn()->FlightPlanSelect(rt.GetCallsign()); string acType = ""; string acStand = ""; if (fp.IsValid() && fp.GetFlightPlanData().IsReceived()) { @@ -2268,9 +2273,6 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { RimcasInstance->OnRefresh(rt, this, IsCorrelated(fp, rt), acType, acStand); - // Track departures for the departure timer window - RimcasInstance->TrackDeparture(rt, fp, this, getActiveAirport()); - CRadarTargetPositionData RtPos = rt.GetPosition(); POINT acPosPix = ConvertCoordFromPositionToPixel(RtPos.GetPosition()); From 352ffe3ba7332f77289eff3c539f50f1ff026f98 Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 15:00:14 +0000 Subject: [PATCH 21/44] try logging --- vSMR/Rimcas.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/vSMR/Rimcas.cpp b/vSMR/Rimcas.cpp index 3bc2038fd..d215874ee 100644 --- a/vSMR/Rimcas.cpp +++ b/vSMR/Rimcas.cpp @@ -343,20 +343,27 @@ void CRimcas::TrackDeparture(CRadarTarget Rt, CFlightPlan fp, CRadarScreen *instance, string activeAirport) { Logger::info(string(__FUNCSIG__)); - if (!Rt.IsValid() || !fp.IsValid()) + if (!Rt.IsValid() || !fp.IsValid()) { + Logger::info("TrackDeparture: Invalid RT or FP"); return; + } string callsign = Rt.GetCallsign(); + Logger::info("TrackDeparture: Checking " + callsign); string origin = fp.GetFlightPlanData().GetOrigin(); - if (origin != activeAirport) + if (origin != activeAirport) { + Logger::info("TrackDeparture: " + callsign + " origin " + origin + " != active " + activeAirport); return; + } int groundSpeed = Rt.GetGS(); bool isMoving = groundSpeed > 50; + Logger::info("TrackDeparture: " + callsign + " GS=" + std::to_string(groundSpeed) + " isMoving=" + (isMoving ? "true" : "false")); // Add aircraft to display once they start moving (> 50 kts) if (isMoving && DepartedAircraft.find(callsign) == DepartedAircraft.end()) { + Logger::info("TrackDeparture: Adding " + callsign + " to departed list"); DepartureInfo info; info.callsign = callsign; if (info.callsign.length() > 8) { @@ -389,6 +396,7 @@ void CRimcas::TrackDeparture(CRadarTarget Rt, CFlightPlan fp, info.dismissed = false; DepartedAircraft[callsign] = info; + Logger::info("TrackDeparture: Successfully added " + callsign + " to departed list"); } // Start timer once aircraft is airborne and climbing @@ -398,6 +406,7 @@ void CRimcas::TrackDeparture(CRadarTarget Rt, CFlightPlan fp, // Remove aircraft if above 6000 feet if (currentAltitude > 6000) { + Logger::info("TrackDeparture: Removing " + callsign + " - above 6000 feet (alt=" + std::to_string(currentAltitude) + ")"); DepartedAircraft.erase(callsign); return; } @@ -409,6 +418,7 @@ void CRimcas::TrackDeparture(CRadarTarget Rt, CFlightPlan fp, if (currentAltitude > info.groundAltitude && verticalSpeed > 100) { info.liftoffTime = clock(); info.timerStarted = true; + Logger::info("TrackDeparture: Started timer for " + callsign + " VS=" + std::to_string(verticalSpeed)); } } } From 08fcde0d723ae49fd96b48bbde4544bb5c6c30c1 Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 15:15:02 +0000 Subject: [PATCH 22/44] clean up loggin --- vSMR/Rimcas.cpp | 46 ++++++++++++--------------------- vSMR/SMRPlugin.cpp | 10 ++++---- vSMR/SMRRadar.cpp | 64 ++++++++++++++++++++++------------------------ 3 files changed, 53 insertions(+), 67 deletions(-) diff --git a/vSMR/Rimcas.cpp b/vSMR/Rimcas.cpp index d215874ee..c1c41c0d7 100644 --- a/vSMR/Rimcas.cpp +++ b/vSMR/Rimcas.cpp @@ -6,7 +6,7 @@ CRimcas::CRimcas() {} CRimcas::~CRimcas() { Reset(); } void CRimcas::Reset() { - Logger::info(string(__FUNCSIG__)); + RunwayAreas.clear(); AcColor.clear(); AcOnRunway.clear(); @@ -19,7 +19,7 @@ void CRimcas::Reset() { } void CRimcas::OnRefreshBegin(bool isLVP) { - Logger::info(string(__FUNCSIG__)); + AcColor.clear(); AcOnRunway.clear(); TimeTable.clear(); @@ -29,14 +29,14 @@ void CRimcas::OnRefreshBegin(bool isLVP) { void CRimcas::OnRefresh(CRadarTarget Rt, CRadarScreen *instance, bool isCorrelated, string acType, string acStand) { - Logger::info(string(__FUNCSIG__)); + GetAcInRunwayArea(Rt, instance); GetAcInRunwayAreaSoon(Rt, instance, isCorrelated, acType, acStand); } void CRimcas::AddRunwayArea(CRadarScreen *instance, string runway_name1, string runway_name2, vector Definition) { - Logger::info(string(__FUNCSIG__)); + string Name = runway_name1 + " / " + runway_name2; RunwayAreaType Runway; @@ -47,7 +47,7 @@ void CRimcas::AddRunwayArea(CRadarScreen *instance, string runway_name1, } string CRimcas::GetAcInRunwayArea(CRadarTarget Ac, CRadarScreen *instance) { - Logger::info(string(__FUNCSIG__)); + int AltitudeDif = Ac.GetPosition().GetFlightLevel() - Ac.GetPreviousPosition(Ac.GetPosition()).GetFlightLevel(); if (!Ac.GetPosition().GetTransponderC()) @@ -83,7 +83,7 @@ string CRimcas::GetAcInRunwayArea(CRadarTarget Ac, CRadarScreen *instance) { string CRimcas::GetAcInRunwayAreaSoon(CRadarTarget Ac, CRadarScreen *instance, bool isCorrelated, string acType, string acStand) { - Logger::info(string(__FUNCSIG__)); + int AltitudeDif = Ac.GetPosition().GetFlightLevel() - Ac.GetPreviousPosition(Ac.GetPosition()).GetFlightLevel(); if (!Ac.GetPosition().GetTransponderC()) @@ -199,7 +199,7 @@ string CRimcas::GetAcInRunwayAreaSoon(CRadarTarget Ac, CRadarScreen *instance, vector CRimcas::GetRunwayArea(CPosition Left, CPosition Right, float hwidth) { - Logger::info(string(__FUNCSIG__)); + vector out; double RunwayBearing = RadToDeg(TrueBearing(Left, Right)); @@ -217,7 +217,7 @@ vector CRimcas::GetRunwayArea(CPosition Left, CPosition Right, } void CRimcas::OnRefreshEnd(CRadarScreen *instance, int threshold) { - Logger::info(string(__FUNCSIG__)); + for (map::iterator it = RunwayAreas.begin(); it != RunwayAreas.end(); ++it) { @@ -292,7 +292,7 @@ void CRimcas::OnRefreshEnd(CRadarScreen *instance, int threshold) { } bool CRimcas::isAcOnRunway(string callsign) { - Logger::info(string(__FUNCSIG__)); + for (std::map::iterator it = AcOnRunway.begin(); it != AcOnRunway.end(); ++it) { if (it->second == callsign) @@ -303,7 +303,7 @@ bool CRimcas::isAcOnRunway(string callsign) { } CRimcas::RimcasAlertTypes CRimcas::getAlert(string callsign) { - Logger::info(string(__FUNCSIG__)); + if (AcColor.find(callsign) == AcColor.end()) return NoAlert; @@ -313,7 +313,7 @@ CRimcas::RimcasAlertTypes CRimcas::getAlert(string callsign) { Color CRimcas::GetAircraftColor(string AcCallsign, Color StandardColor, Color OnRunwayColor, Color RimcasStageOne, Color RimcasStageTwo) { - Logger::info(string(__FUNCSIG__)); + if (AcColor.find(AcCallsign) == AcColor.end()) { if (isAcOnRunway(AcCallsign)) { return OnRunwayColor; @@ -331,7 +331,7 @@ Color CRimcas::GetAircraftColor(string AcCallsign, Color StandardColor, Color CRimcas::GetAircraftColor(string AcCallsign, Color StandardColor, Color OnRunwayColor) { - Logger::info(string(__FUNCSIG__)); + if (isAcOnRunway(AcCallsign)) { return OnRunwayColor; } else { @@ -341,29 +341,20 @@ Color CRimcas::GetAircraftColor(string AcCallsign, Color StandardColor, void CRimcas::TrackDeparture(CRadarTarget Rt, CFlightPlan fp, CRadarScreen *instance, string activeAirport) { - Logger::info(string(__FUNCSIG__)); - - if (!Rt.IsValid() || !fp.IsValid()) { - Logger::info("TrackDeparture: Invalid RT or FP"); + if (!Rt.IsValid() || !fp.IsValid()) return; - } string callsign = Rt.GetCallsign(); - Logger::info("TrackDeparture: Checking " + callsign); string origin = fp.GetFlightPlanData().GetOrigin(); - if (origin != activeAirport) { - Logger::info("TrackDeparture: " + callsign + " origin " + origin + " != active " + activeAirport); + if (origin != activeAirport) return; - } int groundSpeed = Rt.GetGS(); bool isMoving = groundSpeed > 50; - Logger::info("TrackDeparture: " + callsign + " GS=" + std::to_string(groundSpeed) + " isMoving=" + (isMoving ? "true" : "false")); // Add aircraft to display once they start moving (> 50 kts) if (isMoving && DepartedAircraft.find(callsign) == DepartedAircraft.end()) { - Logger::info("TrackDeparture: Adding " + callsign + " to departed list"); DepartureInfo info; info.callsign = callsign; if (info.callsign.length() > 8) { @@ -396,7 +387,6 @@ void CRimcas::TrackDeparture(CRadarTarget Rt, CFlightPlan fp, info.dismissed = false; DepartedAircraft[callsign] = info; - Logger::info("TrackDeparture: Successfully added " + callsign + " to departed list"); } // Start timer once aircraft is airborne and climbing @@ -406,7 +396,6 @@ void CRimcas::TrackDeparture(CRadarTarget Rt, CFlightPlan fp, // Remove aircraft if above 6000 feet if (currentAltitude > 6000) { - Logger::info("TrackDeparture: Removing " + callsign + " - above 6000 feet (alt=" + std::to_string(currentAltitude) + ")"); DepartedAircraft.erase(callsign); return; } @@ -418,14 +407,13 @@ void CRimcas::TrackDeparture(CRadarTarget Rt, CFlightPlan fp, if (currentAltitude > info.groundAltitude && verticalSpeed > 100) { info.liftoffTime = clock(); info.timerStarted = true; - Logger::info("TrackDeparture: Started timer for " + callsign + " VS=" + std::to_string(verticalSpeed)); } } } } void CRimcas::UpdateDepartureTimer(int departureDisplayDurationSecs, CRadarScreen *instance) { - Logger::info(string(__FUNCSIG__)); + clock_t currentTime = clock(); double maxDurationSecs = (double)departureDisplayDurationSecs; @@ -457,7 +445,7 @@ void CRimcas::UpdateDepartureTimer(int departureDisplayDurationSecs, CRadarScree } void CRimcas::DismissDeparture(string callsign) { - Logger::info(string(__FUNCSIG__)); + if (DepartedAircraft.find(callsign) != DepartedAircraft.end()) { DepartedAircraft[callsign].dismissed = true; @@ -465,7 +453,7 @@ void CRimcas::DismissDeparture(string callsign) { } void CRimcas::ClearDismissedDepartures() { - Logger::info(string(__FUNCSIG__)); + vector toRemove; for (auto &pair : DepartedAircraft) { diff --git a/vSMR/SMRPlugin.cpp b/vSMR/SMRPlugin.cpp index e8d27da47..213559961 100644 --- a/vSMR/SMRPlugin.cpp +++ b/vSMR/SMRPlugin.cpp @@ -412,7 +412,7 @@ bool CSMRPlugin::OnCompileCommand(const char * sCommandLine) { } void CSMRPlugin::OnGetTagItem(CFlightPlan FlightPlan, CRadarTarget RadarTarget, int ItemCode, int TagData, char sItemString[16], int * pColorCode, COLORREF * pRGB, double * pFontSize) { - Logger::info(string(__FUNCSIG__)); + if (ItemCode == TAG_ITEM_DATALINK_STS) { if (FlightPlan.IsValid()) { if (std::find(AircraftDemandingClearance.begin(), AircraftDemandingClearance.end(), FlightPlan.GetCallsign()) != AircraftDemandingClearance.end()) { @@ -457,7 +457,7 @@ void CSMRPlugin::OnGetTagItem(CFlightPlan FlightPlan, CRadarTarget RadarTarget, void CSMRPlugin::OnFunctionCall(int FunctionId, const char * sItemString, POINT Pt, RECT Area) { - Logger::info(string(__FUNCSIG__)); + if (FunctionId == TAG_FUNC_DATALINK_MENU) { CFlightPlan FlightPlan = FlightPlanSelectASEL(); @@ -634,7 +634,7 @@ void CSMRPlugin::OnFunctionCall(int FunctionId, const char * sItemString, POINT void CSMRPlugin::OnFlightPlanDisconnect(CFlightPlan FlightPlan) { - Logger::info(string(__FUNCSIG__)); + CRadarTarget rt = RadarTargetSelect(FlightPlan.GetCallsign()); if (std::find(ReleasedTracks.begin(), ReleasedTracks.end(), rt.GetSystemID()) != ReleasedTracks.end()) @@ -646,7 +646,7 @@ void CSMRPlugin::OnFlightPlanDisconnect(CFlightPlan FlightPlan) void CSMRPlugin::OnTimer(int Counter) { - Logger::info(string(__FUNCSIG__)); + BLINK = !BLINK; if (HoppieConnected && ConnectionMessage) { @@ -684,7 +684,7 @@ void CSMRPlugin::OnTimer(int Counter) CRadarScreen * CSMRPlugin::OnRadarScreenCreated(const char * sDisplayName, bool NeedRadarContent, bool GeoReferenced, bool CanBeSaved, bool CanBeCreated) { - Logger::info(string(__FUNCSIG__)); + if (!strcmp(sDisplayName, MY_PLUGIN_VIEW_AVISO)) { CSMRRadar* rd = new CSMRRadar(); RadarScreensOpened.push_back(rd); diff --git a/vSMR/SMRRadar.cpp b/vSMR/SMRRadar.cpp index 70fcf60a9..f55e2b750 100644 --- a/vSMR/SMRRadar.cpp +++ b/vSMR/SMRRadar.cpp @@ -161,7 +161,7 @@ CSMRRadar::CSMRRadar() { } CSMRRadar::~CSMRRadar() { - Logger::info(string(__FUNCSIG__)); + try { // this->OnAsrContentToBeSaved(); // this->EuroScopePlugInExitCustom(); @@ -206,7 +206,7 @@ void CSMRRadar::CorrelateCursor() { } void CSMRRadar::LoadCustomFont() { - Logger::info(string(__FUNCSIG__)); + // Loading the custom font if there is one in use customFonts.clear(); @@ -240,7 +240,7 @@ void CSMRRadar::LoadCustomFont() { } void CSMRRadar::LoadProfile(string profileName) { - Logger::info(string(__FUNCSIG__)); + // Loading the new profile CurrentConfig->setActiveProfile(profileName); @@ -271,7 +271,7 @@ void CSMRRadar::LoadProfile(string profileName) { } void CSMRRadar::OnAsrContentLoaded(bool Loaded) { - Logger::info(string(__FUNCSIG__)); + const char *p_value; // ReSharper disable CppZeroConstantCanBeReplacedWithNullptr @@ -399,7 +399,7 @@ void CSMRRadar::OnAsrContentLoaded(bool Loaded) { } void CSMRRadar::OnAsrContentToBeSaved() { - Logger::info(string(__FUNCSIG__)); + SaveDataToAsr("Airport", "Active airport for RIMCAS", getActiveAirport().c_str()); @@ -505,7 +505,7 @@ void CSMRRadar::OnAsrContentToBeSaved() { void CSMRRadar::OnMoveScreenObject(int ObjectType, const char *sObjectId, POINT Pt, RECT Area, bool Released) { - Logger::info(string(__FUNCSIG__)); + if (ObjectType == APPWINDOW_ONE || ObjectType == APPWINDOW_TWO) { int appWindowId = ObjectType - APPWINDOW_BASE; @@ -697,14 +697,14 @@ void CSMRRadar::OnMoveScreenObject(int ObjectType, const char *sObjectId, void CSMRRadar::OnOverScreenObject(int ObjectType, const char *sObjectId, POINT Pt, RECT Area) { - Logger::info(string(__FUNCSIG__)); + mouseLocation = Pt; RequestRefresh(); } void CSMRRadar::OnClickScreenObject(int ObjectType, const char *sObjectId, POINT Pt, RECT Area, int Button) { - Logger::info(string(__FUNCSIG__)); + mouseLocation = Pt; if (ObjectType == APPWINDOW_ONE || ObjectType == APPWINDOW_TWO) { @@ -1120,7 +1120,7 @@ void CSMRRadar::OnClickScreenObject(int ObjectType, const char *sObjectId, void CSMRRadar::OnFunctionCall(int FunctionId, const char *sItemString, POINT Pt, RECT Area) { - Logger::info(string(__FUNCSIG__)); + mouseLocation = Pt; if (FunctionId == APPWINDOW_ONE || FunctionId == APPWINDOW_TWO) { int id = FunctionId - APPWINDOW_BASE; @@ -1392,7 +1392,7 @@ void CSMRRadar::OnFunctionCall(int FunctionId, const char *sItemString, } void CSMRRadar::RefreshAirportActivity(void) { - Logger::info(string(__FUNCSIG__)); + // // Getting the depatures and arrivals airports // @@ -1413,7 +1413,7 @@ void CSMRRadar::RefreshAirportActivity(void) { } void CSMRRadar::OnRadarTargetPositionUpdate(CRadarTarget RadarTarget) { - Logger::info(string(__FUNCSIG__)); + if (!RadarTarget.IsValid() || !RadarTarget.GetPosition().IsValid()) return; @@ -1587,7 +1587,7 @@ void CSMRRadar::OnRadarTargetPositionUpdate(CRadarTarget RadarTarget) { } string CSMRRadar::GetBottomLine(const char *Callsign) { - Logger::info(string(__FUNCSIG__)); + CFlightPlan fp = GetPlugIn()->FlightPlanSelect(Callsign); string to_render = ""; @@ -1647,7 +1647,7 @@ string CSMRRadar::GetBottomLine(const char *Callsign) { } bool CSMRRadar::OnCompileCommand(const char *sCommandLine) { - Logger::info(string(__FUNCSIG__)); + if (strcmp(sCommandLine, ".smr reload") == 0) { CurrentConfig = new CConfig(ConfigPath); LoadProfile(CurrentConfig->getActiveProfileName()); @@ -1661,7 +1661,7 @@ map CSMRRadar::GenerateTagData( CRadarTarget rt, CFlightPlan fp, bool isAcCorrelated, bool isProMode, int TransitionAltitude, bool useSpeedForGates, string ActiveAirport, bool showAircraftType, bool showSID, bool showWakeTurb) { - Logger::info(string(__FUNCSIG__)); + // ---- // Tag items available // callsign: Callsign with freq state and comm * @@ -1919,7 +1919,7 @@ map CSMRRadar::GenerateTagData( } void CSMRRadar::OnFlightPlanDisconnect(CFlightPlan FlightPlan) { - Logger::info(string(__FUNCSIG__)); + string callsign = string(FlightPlan.GetCallsign()); for (multimap::iterator itr = DistanceTools.begin(); @@ -1950,7 +1950,7 @@ LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, } void CSMRRadar::OnRefresh(HDC hDC, int Phase) { - Logger::info(string(__FUNCSIG__)); + // Changing the mouse cursor if (initCursor) { if (customCursor) { @@ -1974,7 +1974,7 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { } if (Phase == REFRESH_PHASE_AFTER_LISTS) { - Logger::info("Phase == REFRESH_PHASE_AFTER_LISTS"); + if (!ColorSettingsDay) { // Creating the gdi+ graphics Graphics graphics(hDC); @@ -1996,14 +1996,14 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { graphics.ReleaseHDC(hDC); } - Logger::info("break Phase == REFRESH_PHASE_AFTER_LISTS"); + return; } if (Phase != REFRESH_PHASE_BEFORE_TAGS) return; - Logger::info("Phase != REFRESH_PHASE_BEFORE_TAGS"); + struct Utils { static RECT GetAreaFromText(CDC *dc, string text, POINT Pos) { @@ -2040,7 +2040,7 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { } } - Logger::info("Graphics set up"); + CDC dc; dc.Attach(hDC); @@ -2076,7 +2076,7 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { AddScreenObject(DRAWING_BACKGROUND_CLICK, "", R, false, ""); } - Logger::info("Runway loop"); + CSectorElement rwy; for (rwy = GetPlugIn()->SectorFileElementSelectFirst(SECTOR_ELEMENT_RUNWAY); rwy.IsValid(); rwy = GetPlugIn()->SectorFileElementSelectNext( @@ -2212,7 +2212,7 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { RimcasInstance->OnRefreshBegin(isLVP); - Logger::info("Drawing WIP Areas"); + if (wipAreasActive) { CPen RedPen(PS_SOLID, 2, RGB(150, 0, 0)); @@ -2236,7 +2236,7 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { #pragma region symbols // Drawing the symbols - Logger::info("Symbols loop"); + EuroScopePlugIn::CRadarTarget rt; for (rt = GetPlugIn()->RadarTargetSelectFirst(); rt.IsValid(); rt = GetPlugIn()->RadarTargetSelectNext(rt)) { @@ -2539,7 +2539,7 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { #pragma region tags // Drawing the Tags - Logger::info("Tags loop"); + for (rt = GetPlugIn()->RadarTargetSelectFirst(); rt.IsValid(); rt = GetPlugIn()->RadarTargetSelectNext(rt)) { if (!rt.IsValid()) @@ -3015,7 +3015,7 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { int TextHeight = dc.GetTextExtent("60").cy; int LineSpacing = TextHeight + 3; // Add 3 pixels between lines - Logger::info("RIMCAS Loop"); + for (std::map::iterator it = RimcasInstance->MonitoredRunwayArr.begin(); it != RimcasInstance->MonitoredRunwayArr.end(); ++it) { @@ -3134,7 +3134,7 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { // ========================================== // Departure Timer Window // ========================================== - Logger::info("Departure Timer Window"); + if (showDepartureWindow) { // Calculate box dimensions based on content @@ -3270,7 +3270,7 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { AddScreenObject(RIMCAS_DEP_WINDOW, "dep_window", DepWindowRect, true, ""); } - Logger::info("Menu bar lists"); + if (ShowLists["Conflict Alert ARR"]) { GetPlugIn()->OpenPopupList(ListAreas["Conflict Alert ARR"], "CA Arrival", @@ -3536,7 +3536,7 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { ShowLists["Afterglow"] = false; } - Logger::info("QRD"); + //--------------------------------- // QRD @@ -3664,7 +3664,7 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { // Drawing the toolbar //--------------------------------- - Logger::info("Menu Bar"); + COLORREF qToolBarColor = RGB(127, 122, 122); @@ -3736,7 +3736,7 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { // Tag deconflicting // - Logger::info("Tag deconfliction loop"); + for (const auto areas : tagAreas) { if (!CurrentConfig->getActiveProfile()["labels"]["auto_deconfliction"] @@ -3845,7 +3845,7 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { // App windows // - Logger::info("App window rendering"); + for (std::map::iterator it = appWindowDisplays.begin(); it != appWindowDisplays.end(); ++it) { @@ -3858,8 +3858,6 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { } dc.Detach(); - - Logger::info("END " + string(__FUNCSIG__)); } // ReSharper restore CppMsExtAddressOfClassRValue From 21a3dcadeffba2015a443ed7f226eb06cc63dd20 Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 15:37:39 +0000 Subject: [PATCH 23/44] enable loging --- vSMR/SMRPlugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vSMR/SMRPlugin.cpp b/vSMR/SMRPlugin.cpp index 213559961..0c4962295 100644 --- a/vSMR/SMRPlugin.cpp +++ b/vSMR/SMRPlugin.cpp @@ -301,7 +301,7 @@ CSMRPlugin::CSMRPlugin(void) :CPlugIn(EuroScopePlugIn::COMPATIBILITY_CODE, MY_PL { Logger::DLL_PATH = ""; - Logger::ENABLED = false; + Logger::ENABLED = true; // // Adding the SMR Display type From f949c46e55c4d5a2fd089101fae42be25dce4eb9 Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 15:37:55 +0000 Subject: [PATCH 24/44] simplify departure detection --- vSMR/Rimcas.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/vSMR/Rimcas.cpp b/vSMR/Rimcas.cpp index c1c41c0d7..01a839603 100644 --- a/vSMR/Rimcas.cpp +++ b/vSMR/Rimcas.cpp @@ -350,11 +350,9 @@ void CRimcas::TrackDeparture(CRadarTarget Rt, CFlightPlan fp, if (origin != activeAirport) return; - int groundSpeed = Rt.GetGS(); - bool isMoving = groundSpeed > 50; + bool onRunway = isAcOnRunway(callsign); - // Add aircraft to display once they start moving (> 50 kts) - if (isMoving && DepartedAircraft.find(callsign) == DepartedAircraft.end()) { + if (onRunway && DepartedAircraft.find(callsign) == DepartedAircraft.end()) { DepartureInfo info; info.callsign = callsign; if (info.callsign.length() > 8) { @@ -389,7 +387,7 @@ void CRimcas::TrackDeparture(CRadarTarget Rt, CFlightPlan fp, DepartedAircraft[callsign] = info; } - // Start timer once aircraft is airborne and climbing + // Update and start timer once aircraft is airborne if (DepartedAircraft.find(callsign) != DepartedAircraft.end()) { DepartureInfo &info = DepartedAircraft[callsign]; int currentAltitude = Rt.GetPosition().GetPressureAltitude(); @@ -403,7 +401,7 @@ void CRimcas::TrackDeparture(CRadarTarget Rt, CFlightPlan fp, if (!info.timerStarted) { int verticalSpeed = Rt.GetVerticalSpeed(); - // Only start timer if climbing above ground altitude with > 100 fpm climb rate + // Start timer once airborne: climbing above ground altitude with > 100 fpm climb rate if (currentAltitude > info.groundAltitude && verticalSpeed > 100) { info.liftoffTime = clock(); info.timerStarted = true; From 5f612a3cf9f9e7d5adf1fb400784b37a8d0e8610 Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 16:37:18 +0000 Subject: [PATCH 25/44] log, and hide/display the window on demand --- vSMR/Rimcas.cpp | 9 ++++++++- vSMR/SMRRadar.cpp | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/vSMR/Rimcas.cpp b/vSMR/Rimcas.cpp index 01a839603..a0cb448b1 100644 --- a/vSMR/Rimcas.cpp +++ b/vSMR/Rimcas.cpp @@ -1,5 +1,6 @@ #include "stdafx.h" #include "Rimcas.hpp" +#include "Logger.h" CRimcas::CRimcas() {} @@ -347,10 +348,13 @@ void CRimcas::TrackDeparture(CRadarTarget Rt, CFlightPlan fp, string callsign = Rt.GetCallsign(); string origin = fp.GetFlightPlanData().GetOrigin(); - if (origin != activeAirport) + if (origin != activeAirport) { + Logger::info("TrackDeparture: " + callsign + " origin " + origin + " != activeAirport " + activeAirport); return; + } bool onRunway = isAcOnRunway(callsign); + Logger::info("TrackDeparture: " + callsign + " onRunway=" + (onRunway ? "true" : "false")); if (onRunway && DepartedAircraft.find(callsign) == DepartedAircraft.end()) { DepartureInfo info; @@ -385,6 +389,7 @@ void CRimcas::TrackDeparture(CRadarTarget Rt, CFlightPlan fp, info.dismissed = false; DepartedAircraft[callsign] = info; + Logger::info("TrackDeparture: Added " + callsign + " to DepartedAircraft, groundAlt=" + std::to_string(info.groundAltitude)); } // Update and start timer once aircraft is airborne @@ -394,6 +399,7 @@ void CRimcas::TrackDeparture(CRadarTarget Rt, CFlightPlan fp, // Remove aircraft if above 6000 feet if (currentAltitude > 6000) { + Logger::info("TrackDeparture: Removing " + callsign + " - above 6000ft (alt=" + std::to_string(currentAltitude) + ")"); DepartedAircraft.erase(callsign); return; } @@ -405,6 +411,7 @@ void CRimcas::TrackDeparture(CRadarTarget Rt, CFlightPlan fp, if (currentAltitude > info.groundAltitude && verticalSpeed > 100) { info.liftoffTime = clock(); info.timerStarted = true; + Logger::info("TrackDeparture: Timer started for " + callsign + " - airborne (alt=" + std::to_string(currentAltitude) + ", vs=" + std::to_string(verticalSpeed) + ")"); } } } diff --git a/vSMR/SMRRadar.cpp b/vSMR/SMRRadar.cpp index f55e2b750..d6fd7778e 100644 --- a/vSMR/SMRRadar.cpp +++ b/vSMR/SMRRadar.cpp @@ -3136,7 +3136,7 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { // ========================================== - if (showDepartureWindow) { + if (showDepartureWindow && RimcasInstance->DepartedAircraft.size() > 0) { // Calculate box dimensions based on content int maxWidth = 0; int numColumns = 0; From 409a1c354edf19b56f8e53051fd7ed93f24da713 Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 16:47:04 +0000 Subject: [PATCH 26/44] apparently isAcOnRunway doesn't work the way I thought it did --- vSMR/Rimcas.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/vSMR/Rimcas.cpp b/vSMR/Rimcas.cpp index a0cb448b1..1c7133e8a 100644 --- a/vSMR/Rimcas.cpp +++ b/vSMR/Rimcas.cpp @@ -349,12 +349,13 @@ void CRimcas::TrackDeparture(CRadarTarget Rt, CFlightPlan fp, string origin = fp.GetFlightPlanData().GetOrigin(); if (origin != activeAirport) { - Logger::info("TrackDeparture: " + callsign + " origin " + origin + " != activeAirport " + activeAirport); return; } - bool onRunway = isAcOnRunway(callsign); - Logger::info("TrackDeparture: " + callsign + " onRunway=" + (onRunway ? "true" : "false")); + // Check if aircraft is actually in a runway area (using same logic as GetAcInRunwayArea) + string runwayArea = GetAcInRunwayArea(Rt, instance); + bool onRunway = (runwayArea != string_false); + Logger::info("TrackDeparture: " + callsign + " onRunway=" + (onRunway ? "true" : "false") + " runway=" + runwayArea); if (onRunway && DepartedAircraft.find(callsign) == DepartedAircraft.end()) { DepartureInfo info; From e7e8b7ac7eb8fa4a4a930eeb1599f12c6e4ba07d Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 17:07:24 +0000 Subject: [PATCH 27/44] more logs --- vSMR/Rimcas.cpp | 1 - vSMR/SMRRadar.cpp | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/vSMR/Rimcas.cpp b/vSMR/Rimcas.cpp index 1c7133e8a..b86e6ba73 100644 --- a/vSMR/Rimcas.cpp +++ b/vSMR/Rimcas.cpp @@ -355,7 +355,6 @@ void CRimcas::TrackDeparture(CRadarTarget Rt, CFlightPlan fp, // Check if aircraft is actually in a runway area (using same logic as GetAcInRunwayArea) string runwayArea = GetAcInRunwayArea(Rt, instance); bool onRunway = (runwayArea != string_false); - Logger::info("TrackDeparture: " + callsign + " onRunway=" + (onRunway ? "true" : "false") + " runway=" + runwayArea); if (onRunway && DepartedAircraft.find(callsign) == DepartedAircraft.end()) { DepartureInfo info; diff --git a/vSMR/SMRRadar.cpp b/vSMR/SMRRadar.cpp index d6fd7778e..94ee0e3f2 100644 --- a/vSMR/SMRRadar.cpp +++ b/vSMR/SMRRadar.cpp @@ -3135,6 +3135,7 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { // Departure Timer Window // ========================================== + Logger::info("Departure Window: showDepartureWindow=" + std::to_string(showDepartureWindow) + ", DepartedAircraft.size=" + std::to_string(RimcasInstance->DepartedAircraft.size())); if (showDepartureWindow && RimcasInstance->DepartedAircraft.size() > 0) { // Calculate box dimensions based on content From 1a647b32e00c0e091c851a0048c67f265f92bd75 Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 17:20:27 +0000 Subject: [PATCH 28/44] don't remove aircraft that aren't airborne --- vSMR/Rimcas.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/vSMR/Rimcas.cpp b/vSMR/Rimcas.cpp index b86e6ba73..7dc5cee9b 100644 --- a/vSMR/Rimcas.cpp +++ b/vSMR/Rimcas.cpp @@ -437,10 +437,13 @@ void CRimcas::UpdateDepartureTimer(int departureDisplayDurationSecs, CRadarScree continue; } - double elapsedSecs = - (double)(currentTime - pair.second.liftoffTime) / CLOCKS_PER_SEC; - if (elapsedSecs >= maxDurationSecs) { - toRemove.push_back(pair.first); + // Only check timer expiry if timer has actually started + if (pair.second.timerStarted) { + double elapsedSecs = + (double)(currentTime - pair.second.liftoffTime) / CLOCKS_PER_SEC; + if (elapsedSecs >= maxDurationSecs) { + toRemove.push_back(pair.first); + } } } From e1511c0f577fb8db0378b3217dd9260cdce3abf1 Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 17:31:27 +0000 Subject: [PATCH 29/44] Add trigger to QSY --- vSMR/Constant.hpp | 1 + vSMR/SMRRadar.cpp | 27 +++++++++++++++++++-------- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/vSMR/Constant.hpp b/vSMR/Constant.hpp index a45a5f1b9..28e1dc976 100644 --- a/vSMR/Constant.hpp +++ b/vSMR/Constant.hpp @@ -340,6 +340,7 @@ const int RIMCAS_UPDATEROTATE2 = 6027; const int RIMCAS_DEP_WINDOW = 7001; const int RIMCAS_DEP_WINDOW_ROW = 7002; +const int RIMCAS_DEP_WINDOW_QSY = 7003; const int RIMCAS_DEP_WINDOW_TOGGLE = 8040; const int RIMCAS_DEP_WINDOW_DURATION = 8041; const int RIMCAS_DEP_WINDOW_COL_CALLSIGN = 8042; diff --git a/vSMR/SMRRadar.cpp b/vSMR/SMRRadar.cpp index 94ee0e3f2..ff560df1b 100644 --- a/vSMR/SMRRadar.cpp +++ b/vSMR/SMRRadar.cpp @@ -815,6 +815,12 @@ void CSMRRadar::OnClickScreenObject(int ObjectType, const char *sObjectId, getActiveAirport().c_str()); } + // Handle click on QSY to dismiss the aircraft + if (ObjectType == RIMCAS_DEP_WINDOW_QSY) { + RimcasInstance->DismissDeparture(sObjectId); + RequestRefresh(); + } + // Handle click on departure timer row - dismiss the aircraft if (ObjectType == RIMCAS_DEP_WINDOW_ROW) { RimcasInstance->DismissDeparture(sObjectId); @@ -3173,10 +3179,10 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { totalWidth += colAcTypeWidth + colPadding; if (depWindowShowWake) totalWidth += colWakeWidth + colPadding; - if (depWindowShowFreq) - totalWidth += colFreqWidth + colPadding; if (depWindowShowTime) totalWidth += colTimeWidth + colPadding; + if (depWindowShowFreq) + totalWidth += colFreqWidth + colPadding; totalWidth += 5; // Right padding int boxHeight = @@ -3249,18 +3255,23 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { rowX += colWakeWidth + colPadding; } - if (depWindowShowFreq) { - dc.TextOutA(rowX, rowY, info.airborneFreq.c_str()); - rowX += colFreqWidth + colPadding; - } - if (depWindowShowTime) { char timeBuffer[16]; sprintf_s(timeBuffer, "%d:%02d", minutes, seconds); dc.TextOutA(rowX, rowY, timeBuffer); + rowX += colTimeWidth + colPadding; + } + + if (depWindowShowFreq) { + CRect qsyRect = {rowX, rowY, rowX + colFreqWidth, rowY + TextHeight}; + dc.TextOutA(rowX, rowY, info.airborneFreq.c_str()); + // Make QSY clickable to dismiss + AddScreenObject(RIMCAS_DEP_WINDOW_QSY, info.callsign.c_str(), qsyRect, + true, "Click to dismiss"); + rowX += colFreqWidth + colPadding; } - // Add click target for this row + // Add click target for the entire row AddScreenObject(RIMCAS_DEP_WINDOW_ROW, info.callsign.c_str(), rowRect, true, ""); From ceed5bb8cd14881849e307d21decaddda2cbd838 Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 17:32:41 +0000 Subject: [PATCH 30/44] remove unneeded logging, and disable by default --- vSMR/Rimcas.cpp | 1 - vSMR/SMRPlugin.cpp | 2 +- vSMR/SMRRadar.cpp | 2 -- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/vSMR/Rimcas.cpp b/vSMR/Rimcas.cpp index 7dc5cee9b..8e0f06b33 100644 --- a/vSMR/Rimcas.cpp +++ b/vSMR/Rimcas.cpp @@ -389,7 +389,6 @@ void CRimcas::TrackDeparture(CRadarTarget Rt, CFlightPlan fp, info.dismissed = false; DepartedAircraft[callsign] = info; - Logger::info("TrackDeparture: Added " + callsign + " to DepartedAircraft, groundAlt=" + std::to_string(info.groundAltitude)); } // Update and start timer once aircraft is airborne diff --git a/vSMR/SMRPlugin.cpp b/vSMR/SMRPlugin.cpp index 0c4962295..213559961 100644 --- a/vSMR/SMRPlugin.cpp +++ b/vSMR/SMRPlugin.cpp @@ -301,7 +301,7 @@ CSMRPlugin::CSMRPlugin(void) :CPlugIn(EuroScopePlugIn::COMPATIBILITY_CODE, MY_PL { Logger::DLL_PATH = ""; - Logger::ENABLED = true; + Logger::ENABLED = false; // // Adding the SMR Display type diff --git a/vSMR/SMRRadar.cpp b/vSMR/SMRRadar.cpp index ff560df1b..15599b949 100644 --- a/vSMR/SMRRadar.cpp +++ b/vSMR/SMRRadar.cpp @@ -3141,8 +3141,6 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { // Departure Timer Window // ========================================== - Logger::info("Departure Window: showDepartureWindow=" + std::to_string(showDepartureWindow) + ", DepartedAircraft.size=" + std::to_string(RimcasInstance->DepartedAircraft.size())); - if (showDepartureWindow && RimcasInstance->DepartedAircraft.size() > 0) { // Calculate box dimensions based on content int maxWidth = 0; From 2a20dd66466a5d55f5bd58cef4acabe02db134f3 Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 17:40:42 +0000 Subject: [PATCH 31/44] correct drawing order --- vSMR/SMRRadar.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/vSMR/SMRRadar.cpp b/vSMR/SMRRadar.cpp index 15599b949..107338bcd 100644 --- a/vSMR/SMRRadar.cpp +++ b/vSMR/SMRRadar.cpp @@ -3230,6 +3230,10 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { CRect rowRect = {DepWindowRect.left, rowY, DepWindowRect.right, rowY + TextHeight}; + // Add click target for the entire row FIRST (so QSY can be on top) + AddScreenObject(RIMCAS_DEP_WINDOW_ROW, info.callsign.c_str(), rowRect, + true, ""); + dc.SetTextColor(RGB(33, 33, 33)); // Draw each enabled column @@ -3263,16 +3267,12 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { if (depWindowShowFreq) { CRect qsyRect = {rowX, rowY, rowX + colFreqWidth, rowY + TextHeight}; dc.TextOutA(rowX, rowY, info.airborneFreq.c_str()); - // Make QSY clickable to dismiss + // Make QSY clickable to dismiss (added AFTER row so it's on top) AddScreenObject(RIMCAS_DEP_WINDOW_QSY, info.callsign.c_str(), qsyRect, true, "Click to dismiss"); rowX += colFreqWidth + colPadding; } - // Add click target for the entire row - AddScreenObject(RIMCAS_DEP_WINDOW_ROW, info.callsign.c_str(), rowRect, - true, ""); - rowY += LineSpacing; } From e14c9a095c45900ccaa8ddd64ac9670739c46bec Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 17:41:44 +0000 Subject: [PATCH 32/44] right justify QSY --- vSMR/SMRRadar.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/vSMR/SMRRadar.cpp b/vSMR/SMRRadar.cpp index 107338bcd..fe80c2928 100644 --- a/vSMR/SMRRadar.cpp +++ b/vSMR/SMRRadar.cpp @@ -3266,7 +3266,10 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { if (depWindowShowFreq) { CRect qsyRect = {rowX, rowY, rowX + colFreqWidth, rowY + TextHeight}; - dc.TextOutA(rowX, rowY, info.airborneFreq.c_str()); + // Right-justify the QSY text + int textWidth = dc.GetTextExtent(info.airborneFreq.c_str()).cx; + int qsyX = rowX + colFreqWidth - textWidth; + dc.TextOutA(qsyX, rowY, info.airborneFreq.c_str()); // Make QSY clickable to dismiss (added AFTER row so it's on top) AddScreenObject(RIMCAS_DEP_WINDOW_QSY, info.callsign.c_str(), qsyRect, true, "Click to dismiss"); From 5f1fd12daed4074be78639236b0ed01d9c70a159 Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 17:48:45 +0000 Subject: [PATCH 33/44] draw a box around the QSY button --- vSMR/SMRRadar.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/vSMR/SMRRadar.cpp b/vSMR/SMRRadar.cpp index fe80c2928..58d09a3b8 100644 --- a/vSMR/SMRRadar.cpp +++ b/vSMR/SMRRadar.cpp @@ -3266,9 +3266,16 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { if (depWindowShowFreq) { CRect qsyRect = {rowX, rowY, rowX + colFreqWidth, rowY + TextHeight}; + + // Draw box around QSY button + CPen ButtonPen(PS_SOLID, 1, RGB(100, 100, 100)); + CPen *oldPen = dc.SelectObject(&ButtonPen); + dc.Rectangle(&qsyRect); + dc.SelectObject(oldPen); + // Right-justify the QSY text int textWidth = dc.GetTextExtent(info.airborneFreq.c_str()).cx; - int qsyX = rowX + colFreqWidth - textWidth; + int qsyX = rowX + colFreqWidth - textWidth - 2; // -2 for padding from edge dc.TextOutA(qsyX, rowY, info.airborneFreq.c_str()); // Make QSY clickable to dismiss (added AFTER row so it's on top) AddScreenObject(RIMCAS_DEP_WINDOW_QSY, info.callsign.c_str(), qsyRect, From f36d7ad4b413bdb3bd307be64d4175901206ca1c Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 17:57:45 +0000 Subject: [PATCH 34/44] logs for QSY button --- vSMR/Rimcas.cpp | 4 ++++ vSMR/SMRRadar.cpp | 2 ++ 2 files changed, 6 insertions(+) diff --git a/vSMR/Rimcas.cpp b/vSMR/Rimcas.cpp index 8e0f06b33..eb02615a4 100644 --- a/vSMR/Rimcas.cpp +++ b/vSMR/Rimcas.cpp @@ -453,9 +453,13 @@ void CRimcas::UpdateDepartureTimer(int departureDisplayDurationSecs, CRadarScree void CRimcas::DismissDeparture(string callsign) { + Logger::info("DismissDeparture called for: " + callsign); if (DepartedAircraft.find(callsign) != DepartedAircraft.end()) { DepartedAircraft[callsign].dismissed = true; + Logger::info("DismissDeparture: Marked " + callsign + " as dismissed"); + } else { + Logger::info("DismissDeparture: " + callsign + " not found in DepartedAircraft"); } } diff --git a/vSMR/SMRRadar.cpp b/vSMR/SMRRadar.cpp index 58d09a3b8..13e7f26c2 100644 --- a/vSMR/SMRRadar.cpp +++ b/vSMR/SMRRadar.cpp @@ -817,12 +817,14 @@ void CSMRRadar::OnClickScreenObject(int ObjectType, const char *sObjectId, // Handle click on QSY to dismiss the aircraft if (ObjectType == RIMCAS_DEP_WINDOW_QSY) { + Logger::info("QSY clicked for " + string(sObjectId)); RimcasInstance->DismissDeparture(sObjectId); RequestRefresh(); } // Handle click on departure timer row - dismiss the aircraft if (ObjectType == RIMCAS_DEP_WINDOW_ROW) { + Logger::info("Row clicked for " + string(sObjectId)); RimcasInstance->DismissDeparture(sObjectId); RequestRefresh(); } From e5d59f3b383293aba7f15a16a836d6500a5f7849 Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 18:06:45 +0000 Subject: [PATCH 35/44] check registered ac --- vSMR/SMRRadar.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/vSMR/SMRRadar.cpp b/vSMR/SMRRadar.cpp index 13e7f26c2..8290af848 100644 --- a/vSMR/SMRRadar.cpp +++ b/vSMR/SMRRadar.cpp @@ -3280,6 +3280,7 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { int qsyX = rowX + colFreqWidth - textWidth - 2; // -2 for padding from edge dc.TextOutA(qsyX, rowY, info.airborneFreq.c_str()); // Make QSY clickable to dismiss (added AFTER row so it's on top) + Logger::info("Adding QSY button for callsign: " + info.callsign); AddScreenObject(RIMCAS_DEP_WINDOW_QSY, info.callsign.c_str(), qsyRect, true, "Click to dismiss"); rowX += colFreqWidth + colPadding; From f068776132d0f86a171d269067e4650f6b1203eb Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 18:58:32 +0000 Subject: [PATCH 36/44] match what we are looking for --- vSMR/SMRRadar.cpp | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/vSMR/SMRRadar.cpp b/vSMR/SMRRadar.cpp index 8290af848..3673c3828 100644 --- a/vSMR/SMRRadar.cpp +++ b/vSMR/SMRRadar.cpp @@ -817,15 +817,21 @@ void CSMRRadar::OnClickScreenObject(int ObjectType, const char *sObjectId, // Handle click on QSY to dismiss the aircraft if (ObjectType == RIMCAS_DEP_WINDOW_QSY) { - Logger::info("QSY clicked for " + string(sObjectId)); - RimcasInstance->DismissDeparture(sObjectId); + // Extract callsign from "QSY_" + callsign + string idStr(sObjectId); + string callsign = idStr.substr(4); // Skip "QSY_" + Logger::info("QSY clicked for " + callsign); + RimcasInstance->DismissDeparture(callsign); RequestRefresh(); } // Handle click on departure timer row - dismiss the aircraft if (ObjectType == RIMCAS_DEP_WINDOW_ROW) { - Logger::info("Row clicked for " + string(sObjectId)); - RimcasInstance->DismissDeparture(sObjectId); + // Extract callsign from "ROW_" + callsign + string idStr(sObjectId); + string callsign = idStr.substr(4); // Skip "ROW_" + Logger::info("Row clicked for " + callsign); + RimcasInstance->DismissDeparture(callsign); RequestRefresh(); } @@ -3233,8 +3239,9 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { rowY + TextHeight}; // Add click target for the entire row FIRST (so QSY can be on top) - AddScreenObject(RIMCAS_DEP_WINDOW_ROW, info.callsign.c_str(), rowRect, - true, ""); + // Use a unique key format: "ROW_" + callsign + string rowKey = "ROW_" + info.callsign; + AddScreenObject(RIMCAS_DEP_WINDOW_ROW, rowKey.c_str(), rowRect, true, ""); dc.SetTextColor(RGB(33, 33, 33)); @@ -3280,8 +3287,10 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { int qsyX = rowX + colFreqWidth - textWidth - 2; // -2 for padding from edge dc.TextOutA(qsyX, rowY, info.airborneFreq.c_str()); // Make QSY clickable to dismiss (added AFTER row so it's on top) - Logger::info("Adding QSY button for callsign: " + info.callsign); - AddScreenObject(RIMCAS_DEP_WINDOW_QSY, info.callsign.c_str(), qsyRect, + // Use a unique key format: "QSY_" + callsign + string qsyKey = "QSY_" + info.callsign; + Logger::info("Adding QSY button for callsign: " + info.callsign + " with key: " + qsyKey); + AddScreenObject(RIMCAS_DEP_WINDOW_QSY, qsyKey.c_str(), qsyRect, true, "Click to dismiss"); rowX += colFreqWidth + colPadding; } From d6967e55869e23d08e877532b2786b5e783c29e6 Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 19:05:52 +0000 Subject: [PATCH 37/44] update logging for sId --- vSMR/SMRRadar.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/vSMR/SMRRadar.cpp b/vSMR/SMRRadar.cpp index 3673c3828..ed69029cc 100644 --- a/vSMR/SMRRadar.cpp +++ b/vSMR/SMRRadar.cpp @@ -817,12 +817,17 @@ void CSMRRadar::OnClickScreenObject(int ObjectType, const char *sObjectId, // Handle click on QSY to dismiss the aircraft if (ObjectType == RIMCAS_DEP_WINDOW_QSY) { + Logger::info("QSY clicked - raw sObjectId: '" + string(sObjectId) + "' length=" + std::to_string(strlen(sObjectId))); // Extract callsign from "QSY_" + callsign string idStr(sObjectId); - string callsign = idStr.substr(4); // Skip "QSY_" - Logger::info("QSY clicked for " + callsign); - RimcasInstance->DismissDeparture(callsign); - RequestRefresh(); + if (idStr.length() > 4 && idStr.substr(0, 4) == "QSY_") { + string callsign = idStr.substr(4); // Skip "QSY_" + Logger::info("QSY clicked for " + callsign); + RimcasInstance->DismissDeparture(callsign); + RequestRefresh(); + } else { + Logger::info("ERROR: sObjectId doesn't start with 'QSY_': '" + idStr + "'"); + } } // Handle click on departure timer row - dismiss the aircraft @@ -3289,7 +3294,6 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { // Make QSY clickable to dismiss (added AFTER row so it's on top) // Use a unique key format: "QSY_" + callsign string qsyKey = "QSY_" + info.callsign; - Logger::info("Adding QSY button for callsign: " + info.callsign + " with key: " + qsyKey); AddScreenObject(RIMCAS_DEP_WINDOW_QSY, qsyKey.c_str(), qsyRect, true, "Click to dismiss"); rowX += colFreqWidth + colPadding; From cbb9877a40f728d208b268d4942f192bdfc9446a Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 19:13:35 +0000 Subject: [PATCH 38/44] sort by time, and LOG! --- vSMR/SMRRadar.cpp | 45 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/vSMR/SMRRadar.cpp b/vSMR/SMRRadar.cpp index ed69029cc..6bdfe86c7 100644 --- a/vSMR/SMRRadar.cpp +++ b/vSMR/SMRRadar.cpp @@ -817,12 +817,20 @@ void CSMRRadar::OnClickScreenObject(int ObjectType, const char *sObjectId, // Handle click on QSY to dismiss the aircraft if (ObjectType == RIMCAS_DEP_WINDOW_QSY) { - Logger::info("QSY clicked - raw sObjectId: '" + string(sObjectId) + "' length=" + std::to_string(strlen(sObjectId))); + Logger::info("=== QSY CLICK ==="); + Logger::info("Mouse Pos: x=" + std::to_string(Pt.x) + ", y=" + std::to_string(Pt.y)); + Logger::info("Click Area: left=" + std::to_string(Area.left) + ", right=" + std::to_string(Area.right) + ", top=" + std::to_string(Area.top) + ", bottom=" + std::to_string(Area.bottom)); + Logger::info("Raw sObjectId: '" + string(sObjectId) + "' (length=" + std::to_string(strlen(sObjectId)) + ")"); + Logger::info("Departed Aircraft in list: " + std::to_string(RimcasInstance->DepartedAircraft.size())); + for (auto &pair : RimcasInstance->DepartedAircraft) { + Logger::info(" - " + pair.first + " (dismissed=" + (pair.second.dismissed ? "true" : "false") + ")"); + } + // Extract callsign from "QSY_" + callsign string idStr(sObjectId); if (idStr.length() > 4 && idStr.substr(0, 4) == "QSY_") { string callsign = idStr.substr(4); // Skip "QSY_" - Logger::info("QSY clicked for " + callsign); + Logger::info("Attempting to dismiss: " + callsign); RimcasInstance->DismissDeparture(callsign); RequestRefresh(); } else { @@ -832,12 +840,17 @@ void CSMRRadar::OnClickScreenObject(int ObjectType, const char *sObjectId, // Handle click on departure timer row - dismiss the aircraft if (ObjectType == RIMCAS_DEP_WINDOW_ROW) { + Logger::info("ROW clicked - raw sObjectId: '" + string(sObjectId) + "'"); // Extract callsign from "ROW_" + callsign string idStr(sObjectId); - string callsign = idStr.substr(4); // Skip "ROW_" - Logger::info("Row clicked for " + callsign); - RimcasInstance->DismissDeparture(callsign); - RequestRefresh(); + if (idStr.length() > 4 && idStr.substr(0, 4) == "ROW_") { + string callsign = idStr.substr(4); // Skip "ROW_" + Logger::info("Row clicked for " + callsign); + RimcasInstance->DismissDeparture(callsign); + RequestRefresh(); + } else { + Logger::info("ERROR: ROW sObjectId doesn't start with 'ROW_': '" + idStr + "'"); + } } if (ObjectType == DRAWING_BACKGROUND_CLICK) { @@ -3220,14 +3233,26 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { dc.GetTextExtent(headerText.c_str()).cx / 2, headerY, headerText.c_str()); - // Draw each departure row + // Draw each departure row (sorted by time) int rowY = DepWindowRect.top + LineSpacing; clock_t currentTime = clock(); + // Create sorted vector of departures by liftoff time (newest first) + vector> sortedDepartures; for (auto &pair : RimcasInstance->DepartedAircraft) { - if (pair.second.dismissed) - continue; - + if (!pair.second.dismissed) { + sortedDepartures.push_back(pair); + } + } + + // Sort by liftoff time descending (most recent at top) + std::sort(sortedDepartures.begin(), sortedDepartures.end(), + [](const pair &a, + const pair &b) { + return a.second.liftoffTime > b.second.liftoffTime; + }); + + for (auto &pair : sortedDepartures) { CRimcas::DepartureInfo &info = pair.second; int rowX = DepWindowRect.left + 5; From c52ecc3c200326e264d5bde803ed57d15a3ff5bf Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 19:38:22 +0000 Subject: [PATCH 39/44] log buttons --- vSMR/SMRPlugin.cpp | 2 +- vSMR/SMRRadar.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/vSMR/SMRPlugin.cpp b/vSMR/SMRPlugin.cpp index 213559961..0c4962295 100644 --- a/vSMR/SMRPlugin.cpp +++ b/vSMR/SMRPlugin.cpp @@ -301,7 +301,7 @@ CSMRPlugin::CSMRPlugin(void) :CPlugIn(EuroScopePlugIn::COMPATIBILITY_CODE, MY_PL { Logger::DLL_PATH = ""; - Logger::ENABLED = false; + Logger::ENABLED = true; // // Adding the SMR Display type diff --git a/vSMR/SMRRadar.cpp b/vSMR/SMRRadar.cpp index 6bdfe86c7..1d198e28a 100644 --- a/vSMR/SMRRadar.cpp +++ b/vSMR/SMRRadar.cpp @@ -3319,6 +3319,7 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { // Make QSY clickable to dismiss (added AFTER row so it's on top) // Use a unique key format: "QSY_" + callsign string qsyKey = "QSY_" + info.callsign; + Logger::info("Registering QSY object: key='" + qsyKey + "', rect=(" + std::to_string(qsyRect.left) + "," + std::to_string(qsyRect.top) + "," + std::to_string(qsyRect.right) + "," + std::to_string(qsyRect.bottom) + ")"); AddScreenObject(RIMCAS_DEP_WINDOW_QSY, qsyKey.c_str(), qsyRect, true, "Click to dismiss"); rowX += colFreqWidth + colPadding; From 4bff9e64b80fcf79ce2030389bb3c5aa9c2e6a0e Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 19:45:17 +0000 Subject: [PATCH 40/44] brute log clicks --- vSMR/SMRRadar.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/vSMR/SMRRadar.cpp b/vSMR/SMRRadar.cpp index 1d198e28a..0f2d1e69c 100644 --- a/vSMR/SMRRadar.cpp +++ b/vSMR/SMRRadar.cpp @@ -815,6 +815,9 @@ void CSMRRadar::OnClickScreenObject(int ObjectType, const char *sObjectId, getActiveAirport().c_str()); } + // DEBUG: Log ALL clicks to see what's happening + Logger::info("OnClickScreenObject: ObjectType=" + std::to_string(ObjectType) + ", sObjectId='" + string(sObjectId) + "', RIMCAS_DEP_WINDOW_QSY=" + std::to_string(RIMCAS_DEP_WINDOW_QSY)); + // Handle click on QSY to dismiss the aircraft if (ObjectType == RIMCAS_DEP_WINDOW_QSY) { Logger::info("=== QSY CLICK ==="); From ffbcdd0f02d1273dea774909e4ea5e402ab38ca9 Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 19:52:21 +0000 Subject: [PATCH 41/44] fix window --- vSMR/SMRRadar.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vSMR/SMRRadar.cpp b/vSMR/SMRRadar.cpp index 0f2d1e69c..0a1ad3f88 100644 --- a/vSMR/SMRRadar.cpp +++ b/vSMR/SMRRadar.cpp @@ -3331,8 +3331,8 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { rowY += LineSpacing; } - // Add the window itself as a movable object - AddScreenObject(RIMCAS_DEP_WINDOW, "dep_window", DepWindowRect, true, ""); + // Add the window itself as a movable object (non-clickable so it doesn't block QSY/ROW clicks) + AddScreenObject(RIMCAS_DEP_WINDOW, "dep_window", DepWindowRect, false, ""); } From bc4da3f24c7e0d50571ec64771a5db05101d3757 Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 20:03:15 +0000 Subject: [PATCH 42/44] correct the order of buttons --- vSMR/SMRRadar.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vSMR/SMRRadar.cpp b/vSMR/SMRRadar.cpp index 0a1ad3f88..217d9c821 100644 --- a/vSMR/SMRRadar.cpp +++ b/vSMR/SMRRadar.cpp @@ -3236,6 +3236,9 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { dc.GetTextExtent(headerText.c_str()).cx / 2, headerY, headerText.c_str()); + // Add the window itself as a movable object FIRST (so QSY/ROW objects will be on top) + AddScreenObject(RIMCAS_DEP_WINDOW, "dep_window", DepWindowRect, true, ""); + // Draw each departure row (sorted by time) int rowY = DepWindowRect.top + LineSpacing; clock_t currentTime = clock(); @@ -3330,9 +3333,6 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { rowY += LineSpacing; } - - // Add the window itself as a movable object (non-clickable so it doesn't block QSY/ROW clicks) - AddScreenObject(RIMCAS_DEP_WINDOW, "dep_window", DepWindowRect, false, ""); } From adf78ce7653b6d8ffe2c1c3304ac5f548904444f Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 20:11:19 +0000 Subject: [PATCH 43/44] re allow moving the window --- vSMR/SMRRadar.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/vSMR/SMRRadar.cpp b/vSMR/SMRRadar.cpp index 217d9c821..eeaee3286 100644 --- a/vSMR/SMRRadar.cpp +++ b/vSMR/SMRRadar.cpp @@ -3270,15 +3270,6 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { int minutes = (int)(elapsedSecs / 60); int seconds = (int)(elapsedSecs) % 60; - // Create row rect for click detection - CRect rowRect = {DepWindowRect.left, rowY, DepWindowRect.right, - rowY + TextHeight}; - - // Add click target for the entire row FIRST (so QSY can be on top) - // Use a unique key format: "ROW_" + callsign - string rowKey = "ROW_" + info.callsign; - AddScreenObject(RIMCAS_DEP_WINDOW_ROW, rowKey.c_str(), rowRect, true, ""); - dc.SetTextColor(RGB(33, 33, 33)); // Draw each enabled column From 445eebb3947b2257fcca2b6596281f08a7e186d2 Mon Sep 17 00:00:00 2001 From: Ben Walker <146777065+RedstonePilot@users.noreply.github.com> Date: Sat, 27 Dec 2025 20:19:41 +0000 Subject: [PATCH 44/44] remove QSY logging --- vSMR/Rimcas.cpp | 6 ------ vSMR/SMRRadar.cpp | 37 ++----------------------------------- 2 files changed, 2 insertions(+), 41 deletions(-) diff --git a/vSMR/Rimcas.cpp b/vSMR/Rimcas.cpp index eb02615a4..d39eba3c0 100644 --- a/vSMR/Rimcas.cpp +++ b/vSMR/Rimcas.cpp @@ -452,14 +452,8 @@ void CRimcas::UpdateDepartureTimer(int departureDisplayDurationSecs, CRadarScree } void CRimcas::DismissDeparture(string callsign) { - - Logger::info("DismissDeparture called for: " + callsign); - if (DepartedAircraft.find(callsign) != DepartedAircraft.end()) { DepartedAircraft[callsign].dismissed = true; - Logger::info("DismissDeparture: Marked " + callsign + " as dismissed"); - } else { - Logger::info("DismissDeparture: " + callsign + " not found in DepartedAircraft"); } } diff --git a/vSMR/SMRRadar.cpp b/vSMR/SMRRadar.cpp index eeaee3286..013255c9d 100644 --- a/vSMR/SMRRadar.cpp +++ b/vSMR/SMRRadar.cpp @@ -815,44 +815,13 @@ void CSMRRadar::OnClickScreenObject(int ObjectType, const char *sObjectId, getActiveAirport().c_str()); } - // DEBUG: Log ALL clicks to see what's happening - Logger::info("OnClickScreenObject: ObjectType=" + std::to_string(ObjectType) + ", sObjectId='" + string(sObjectId) + "', RIMCAS_DEP_WINDOW_QSY=" + std::to_string(RIMCAS_DEP_WINDOW_QSY)); - // Handle click on QSY to dismiss the aircraft if (ObjectType == RIMCAS_DEP_WINDOW_QSY) { - Logger::info("=== QSY CLICK ==="); - Logger::info("Mouse Pos: x=" + std::to_string(Pt.x) + ", y=" + std::to_string(Pt.y)); - Logger::info("Click Area: left=" + std::to_string(Area.left) + ", right=" + std::to_string(Area.right) + ", top=" + std::to_string(Area.top) + ", bottom=" + std::to_string(Area.bottom)); - Logger::info("Raw sObjectId: '" + string(sObjectId) + "' (length=" + std::to_string(strlen(sObjectId)) + ")"); - Logger::info("Departed Aircraft in list: " + std::to_string(RimcasInstance->DepartedAircraft.size())); - for (auto &pair : RimcasInstance->DepartedAircraft) { - Logger::info(" - " + pair.first + " (dismissed=" + (pair.second.dismissed ? "true" : "false") + ")"); - } - - // Extract callsign from "QSY_" + callsign string idStr(sObjectId); if (idStr.length() > 4 && idStr.substr(0, 4) == "QSY_") { - string callsign = idStr.substr(4); // Skip "QSY_" - Logger::info("Attempting to dismiss: " + callsign); - RimcasInstance->DismissDeparture(callsign); - RequestRefresh(); - } else { - Logger::info("ERROR: sObjectId doesn't start with 'QSY_': '" + idStr + "'"); - } - } - - // Handle click on departure timer row - dismiss the aircraft - if (ObjectType == RIMCAS_DEP_WINDOW_ROW) { - Logger::info("ROW clicked - raw sObjectId: '" + string(sObjectId) + "'"); - // Extract callsign from "ROW_" + callsign - string idStr(sObjectId); - if (idStr.length() > 4 && idStr.substr(0, 4) == "ROW_") { - string callsign = idStr.substr(4); // Skip "ROW_" - Logger::info("Row clicked for " + callsign); + string callsign = idStr.substr(4); RimcasInstance->DismissDeparture(callsign); RequestRefresh(); - } else { - Logger::info("ERROR: ROW sObjectId doesn't start with 'ROW_': '" + idStr + "'"); } } @@ -3313,10 +3282,8 @@ void CSMRRadar::OnRefresh(HDC hDC, int Phase) { int textWidth = dc.GetTextExtent(info.airborneFreq.c_str()).cx; int qsyX = rowX + colFreqWidth - textWidth - 2; // -2 for padding from edge dc.TextOutA(qsyX, rowY, info.airborneFreq.c_str()); - // Make QSY clickable to dismiss (added AFTER row so it's on top) - // Use a unique key format: "QSY_" + callsign + // Make QSY clickable to dismiss string qsyKey = "QSY_" + info.callsign; - Logger::info("Registering QSY object: key='" + qsyKey + "', rect=(" + std::to_string(qsyRect.left) + "," + std::to_string(qsyRect.top) + "," + std::to_string(qsyRect.right) + "," + std::to_string(qsyRect.bottom) + ")"); AddScreenObject(RIMCAS_DEP_WINDOW_QSY, qsyKey.c_str(), qsyRect, true, "Click to dismiss"); rowX += colFreqWidth + colPadding;