y-XWr4(-WK`KR9S-ddvHuEDvXiLyy`TE*lh
zE`mhJ2%E|=fS~k9srX@P4M8bNMRzkzfFpgbC+s@g0lg665m`*g9A{`~Px{}RV0h@f
zV3aaT_#e@hHMHsY`DepSLrAdTec=vX3XXUyyl8krtue@6FZc;kKc4cGf`)zXO|`$6
zEQb_JJoe7-+(#gIug63A@iv834Y*XQm~#GN(7xJYmhMW(%i6*(8N=02CjJiTz%gPg
zpF|W5!}2>FLuaEX%sUD6nub~FT1~yGhVlKaU_N}CYPC)MfN!&
zSu~erkrT3-huvgjLLYd4IZ{BVaB94ZVniV5xNw>wS+yvI;3Hj75M#dFTfYV>C}`<|
z5`cDJp_4mnHnwO)>*9#AzNgmtEBW4HCy^+M^JPf{asx*sIBX8R94viC4NARswsSc_
zTylP`VzU6F)w@bjplUE56oKX3s`O!h^hVCZv7u9XP4_njHfTv@whXi#fnO3wh_NyOi*2hH;4s}#k<=v2#o^h(4NZqCH
zhabk-+N=9T*UZh`GwYnu9g^H*udzHqDK$&58C{Cik63kR@+d6S^I3iPlH`V9^y8LB
zzU~|^Z2iy_=V>X@Ux~Zo!+vHo|FNhva*6U)i@#8RaQbQ<^qKPr_4u3ccDx^_@r%gz
zfJVjS@`Bg!*?xm2=(=}@BSf+AdwbY+Ko9zAc!ZWNqgbD~I?6FUJ1!$t14agG
m`gC3wJCwi8y6mH}6#3wsqxBbboD0z92ALkWFfKB5x%LmVjatM2
literal 0
HcmV?d00001
From aef12a6e339023ff56777bf4aa15c50ef30b3c58 Mon Sep 17 00:00:00 2001
From: Devin G <149845346+DeathSoulYTDev@users.noreply.github.com>
Date: Wed, 6 Aug 2025 17:49:42 -0400
Subject: [PATCH 05/17] Added Multi Tone Paging - QC 2
---
WhackerLinkConsoleV2/ChannelBox.xaml.cs | 1 +
WhackerLinkConsoleV2/MainWindow.xaml.cs | 42 ++++++++++-----------
WhackerLinkConsoleV2/ToneSet.xaml | 24 +++++++++---
WhackerLinkConsoleV2/ToneSet.xaml.cs | 33 +++++++++++++++-
WhackerLinkConsoleV2/codeplugs/codeplug.yml | 2 +-
5 files changed, 71 insertions(+), 31 deletions(-)
diff --git a/WhackerLinkConsoleV2/ChannelBox.xaml.cs b/WhackerLinkConsoleV2/ChannelBox.xaml.cs
index f6750c9..b60f7cf 100644
--- a/WhackerLinkConsoleV2/ChannelBox.xaml.cs
+++ b/WhackerLinkConsoleV2/ChannelBox.xaml.cs
@@ -300,6 +300,7 @@ private async void PTTButton_Click(object sender, RoutedEventArgs e)
private void PageSelectButton_Click(object sender, RoutedEventArgs e)
{
if (!IsSelected) return;
+
PageState = !PageState;
PageButtonClicked.Invoke(sender, this);
diff --git a/WhackerLinkConsoleV2/MainWindow.xaml.cs b/WhackerLinkConsoleV2/MainWindow.xaml.cs
index c8c75c0..17b969e 100644
--- a/WhackerLinkConsoleV2/MainWindow.xaml.cs
+++ b/WhackerLinkConsoleV2/MainWindow.xaml.cs
@@ -429,6 +429,7 @@ private void GenerateChannelWidgets()
{
foreach (var tone in Codeplug.Tones)
{
+
var toneSetControl = new ToneSet(tone.Name, tone.ToneA, tone.ToneB);
// Hook up events
@@ -447,34 +448,28 @@ private void GenerateChannelWidgets()
var selected = selectedTones[i];
var key = (selected.ToneA, selected.ToneB);
- // Check if any of the original channels have lost their PageState
- var prematurelyCleared = initiallyActiveChannels
- .Where(c => !c.PageState)
- .ToList();
-
- if (prematurelyCleared.Any())
+ // ✅ Restore PageState for each tone before playback
+ foreach (var ch in initiallyActiveChannels)
{
- // Pause logic: restore PageState for affected channels
- foreach (var ch in prematurelyCleared)
+ ch.PageState = true;
+ Dispatcher.Invoke(() =>
{
- ch.PageState = true;
- Dispatcher.Invoke(() =>
- {
- ch.PageSelectButton.Background = ch.orangeGradient;
- });
- }
+ ch.PageSelectButton.Background = ch.orangeGradient;
+ });
- // Wait a moment to visually reflect the reset before continuing
- await Task.Delay(300);
+ // This ensures PageState on the *server* is activated
+ ChannelBox_PageButtonClicked(ch, ch);
}
- // Play the tone
+ await Task.Delay(2000); // Optional visual buffer
+
+ // ▶️ Play the tone
await PlayTone(selected.ToneA.ToString(), selected.ToneB.ToString());
- // Remove from selected set
+ // ❌ Remove from selected set
_selectedToneSets.Remove(key);
- // Deselect the tone visually
+ // 🔄 Deselect tone visually
foreach (var child in ChannelsCanvas.Children)
{
if (child is ToneSet ts && ts.ToneA == key.ToneA && ts.ToneB == key.ToneB)
@@ -485,21 +480,24 @@ private void GenerateChannelWidgets()
}
}
- // Now that all tones are done, clean up page state
+ // 🧹 Final cleanup: clear PageState on all initially active channels
foreach (var ch in initiallyActiveChannels)
{
ch.PageState = false;
- ch.PageSelectButton.Background = ch.grayGradient;
+ Dispatcher.Invoke(() =>
+ {
+ ch.PageSelectButton.Background = ch.grayGradient;
+ });
}
}
else
{
+ // 🔁 Single tone fallback
var hasActivePage = _selectedChannelsManager.GetSelectedChannels().Any(c => c.PageState);
if (hasActivePage)
{
await PlayTone(tone.ToneA.ToString(), tone.ToneB.ToString());
- // Clear PageState after this tone
foreach (var channel in _selectedChannelsManager.GetSelectedChannels())
{
channel.PageState = false;
diff --git a/WhackerLinkConsoleV2/ToneSet.xaml b/WhackerLinkConsoleV2/ToneSet.xaml
index b3cc0bc..c70c39f 100644
--- a/WhackerLinkConsoleV2/ToneSet.xaml
+++ b/WhackerLinkConsoleV2/ToneSet.xaml
@@ -5,24 +5,36 @@
-
-
diff --git a/WhackerLinkConsoleV2/ToneSet.xaml.cs b/WhackerLinkConsoleV2/ToneSet.xaml.cs
index 66e2c14..642a602 100644
--- a/WhackerLinkConsoleV2/ToneSet.xaml.cs
+++ b/WhackerLinkConsoleV2/ToneSet.xaml.cs
@@ -11,6 +11,9 @@ public partial class ToneSet : UserControl, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
+ internal LinearGradientBrush grayGradient;
+ internal LinearGradientBrush orangeGradient;
+
public string ToneName { get; set; }
public double ToneA { get; set; }
public double ToneB { get; set; }
@@ -25,6 +28,27 @@ public partial class ToneSet : UserControl, INotifyPropertyChanged
public ToneSet(string toneName, double toneA, double toneB)
{
InitializeComponent();
+ UpdateBackground();
+
+ grayGradient = new LinearGradientBrush
+ {
+ StartPoint = new Point(0.5, 0),
+ EndPoint = new Point(0.5, 1)
+ };
+
+ grayGradient.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#FFF0F0F0"), 0.485));
+ grayGradient.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#FFDCDCDC"), 0.517));
+
+ orangeGradient = new LinearGradientBrush
+ {
+ StartPoint = new Point(0.5, 0),
+ EndPoint = new Point(0.5, 1)
+ };
+
+ orangeGradient.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#FFFFAF00"), 0.485));
+ orangeGradient.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#FFEEA400"), 0.517));
+ ToneSetSelectBtn.Background = grayGradient;
+
ToneName = toneName;
ToneA = toneA;
ToneB = toneB;
@@ -62,11 +86,16 @@ public void SetSelected(bool selected)
private void UpdateSelectButton()
{
- //if (ToneSetSelectBtn != null)
+ if (ToneSetSelectBtn != null)
{
//ToneSetSelectBtn.Content = _isSelected ? "Deselect" : "Select";
- //ToneSetSelectBtn.Background = _isSelected ? Brushes.LightGreen : Brushes.LightGray;
+ ToneSetSelectBtn.Background = _isSelected ? orangeGradient : grayGradient;
}
}
+
+ private void UpdateBackground()
+ {
+ Background = _isSelected ? (Brush)new BrushConverter().ConvertFrom("#FF0B004B") : Brushes.DarkGray;
+ }
}
}
diff --git a/WhackerLinkConsoleV2/codeplugs/codeplug.yml b/WhackerLinkConsoleV2/codeplugs/codeplug.yml
index 0e1bc56..7ccfa5a 100644
--- a/WhackerLinkConsoleV2/codeplugs/codeplug.yml
+++ b/WhackerLinkConsoleV2/codeplugs/codeplug.yml
@@ -78,4 +78,4 @@ zones:
tgid: "16002"
- name: "Channel C"
system: "System 1"
- tgid: "16002"
+ tgid: "16002"
\ No newline at end of file
From 4d73207a0a5b3dbc40b3c330cc39230f962e90b2 Mon Sep 17 00:00:00 2001
From: Devin G <149845346+DeathSoulYTDev@users.noreply.github.com>
Date: Wed, 6 Aug 2025 18:47:47 -0400
Subject: [PATCH 06/17] Tones Delay
- Removed Delay for First Tone When Using Stacked Tones
- Lowered the Delay from 2000ms to 1750ms
---
WhackerLinkConsoleV2/MainWindow.xaml.cs | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/WhackerLinkConsoleV2/MainWindow.xaml.cs b/WhackerLinkConsoleV2/MainWindow.xaml.cs
index 17b969e..c3b05d6 100644
--- a/WhackerLinkConsoleV2/MainWindow.xaml.cs
+++ b/WhackerLinkConsoleV2/MainWindow.xaml.cs
@@ -461,7 +461,11 @@ private void GenerateChannelWidgets()
ChannelBox_PageButtonClicked(ch, ch);
}
- await Task.Delay(2000); // Optional visual buffer
+ if (i > 0)
+ {
+ await Task.Delay(1750); // delay only after the first tone
+ }
+
// ▶️ Play the tone
await PlayTone(selected.ToneA.ToString(), selected.ToneB.ToString());
From d44cfc3ee4150d373974fb2b9c0e37e0af1d2c8c Mon Sep 17 00:00:00 2001
From: Devin G <149845346+DeathSoulYTDev@users.noreply.github.com>
Date: Sun, 17 Aug 2025 18:48:25 -0400
Subject: [PATCH 07/17] Zone Tabs
- Loaded Zones and there channels into there own tabs
- Added a Zones Filter to QC2 Tones to allow setting Tones Per Zone/Agency
---
WhackerLinkConsoleV2/App.xaml.cs | 15 +
WhackerLinkConsoleV2/MainWindow.xaml | 286 +++----
WhackerLinkConsoleV2/MainWindow.xaml.cs | 974 ++++++++++++++++--------
WhackerLinkConsoleV2/SettingsManager.cs | 9 +
4 files changed, 779 insertions(+), 505 deletions(-)
diff --git a/WhackerLinkConsoleV2/App.xaml.cs b/WhackerLinkConsoleV2/App.xaml.cs
index 6f584cf..af25b44 100644
--- a/WhackerLinkConsoleV2/App.xaml.cs
+++ b/WhackerLinkConsoleV2/App.xaml.cs
@@ -1,6 +1,7 @@
using System.Configuration;
using System.Data;
using System.Windows;
+using WhackerLinkLib.Models.Radio;
namespace WhackerLinkConsoleV2
{
@@ -9,6 +10,20 @@ namespace WhackerLinkConsoleV2
///
public partial class App : Application
{
+ public Codeplug Codeplug { get; set; }
+
+ [System.Runtime.InteropServices.DllImport("kernel32.dll")]
+ private static extern bool AllocConsole();
+
+ public App()
+ {
+ //AllocConsole();
+ //Console.WriteLine($"Debug: {Codeplug}");
+ //if (Codeplug.Systems[0].debug)
+ //{
+ // Console.WriteLine("Hello, world!");
+ //}
+ }
}
}
diff --git a/WhackerLinkConsoleV2/MainWindow.xaml b/WhackerLinkConsoleV2/MainWindow.xaml
index 58b36fb..37d5449 100644
--- a/WhackerLinkConsoleV2/MainWindow.xaml
+++ b/WhackerLinkConsoleV2/MainWindow.xaml
@@ -1,19 +1,22 @@
+
+
+
-
+
+
@@ -23,12 +26,14 @@
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -73,151 +105,63 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/WhackerLinkConsoleV2/MainWindow.xaml.cs b/WhackerLinkConsoleV2/MainWindow.xaml.cs
index c3b05d6..a3d28e5 100644
--- a/WhackerLinkConsoleV2/MainWindow.xaml.cs
+++ b/WhackerLinkConsoleV2/MainWindow.xaml.cs
@@ -48,7 +48,7 @@
using WebSocketSharp;
using NWaves.Signals;
using static WhackerLinkConsoleV2.P25Crypto;
-using static WhackerLinkLib.Models.Radio.Codeplug;
+//using static WhackerLinkLib.Models.Radio.Codeplug;
using System.Threading.Channels;
namespace WhackerLinkConsoleV2
@@ -66,7 +66,9 @@ public partial class MainWindow : Window
private double _offsetY;
private bool _isDragging;
+ public List AllowedZones { get; set; }
private SettingsManager _settingsManager = new SettingsManager();
+ private Dictionary _zoneCanvases = new Dictionary();
private SelectedChannelsManager _selectedChannelsManager;
private List<(double ToneA, double ToneB)> _selectedToneSets = new List<(double, double)>();
@@ -97,13 +99,23 @@ public partial class MainWindow : Window
List> fneAffs = new List>();
+ [System.Runtime.InteropServices.DllImport("kernel32.dll")]
+ private static extern bool AllocConsole();
+
public MainWindow()
{
#if !DEBUG
ConsoleNative.ShowConsole();
#endif
InitializeComponent();
+
_settingsManager.LoadSettings();
+
+ if (_settingsManager.DebugMode)
+ {
+ ShowDebugConsole();
+ }
+
_selectedChannelsManager = new SelectedChannelsManager();
_flashingManager = new FlashingBackgroundManager(null, ChannelsCanvas, null, this);
_emergencyAlertPlayback = new WaveFilePlaybackManager(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "emergency.wav"));
@@ -131,6 +143,22 @@ public MainWindow()
private class YamlConfig
{
public List Tones { get; set; }
+ public List Zones { get; set; }
+ }
+
+ public void ShowDebugConsole()
+ {
+ AllocConsole();
+ Console.WriteLine($"Debug Console: Active");
+ if (_settingsManager.DebugMode)
+ {
+ Console.WriteLine($"DebugMode: Enabled");
+ }
+ }
+
+ private void OpenDebugConsole_Click(object sender, RoutedEventArgs e)
+ {
+ ShowDebugConsole();
}
private void OpenCodeplug_Click(object sender, RoutedEventArgs e)
@@ -175,420 +203,638 @@ private void LoadCodeplug(string filePath)
}
}
+ //private void GenerateChannelWidgets()
+ //{
+ // // Clear previous UI
+ // ChannelsTabControl.Items.Clear();
+ // ChannelsCanvas.Children.Clear();
+
+ // double globalOffsetX = 20;
+ // double globalOffsetY = 20;
+
+ // if (Codeplug != null)
+ // {
+ // // 1. Add System Status Boxes (global)
+ // foreach (var system in Codeplug.Systems)
+ // {
+ // AddSystemBox(system, ref globalOffsetX, ref globalOffsetY);
+ // }
+
+ // // 2. Add Zone Tabs (each tab gets its own ScrollViewer + Canvas)
+ // foreach (var zone in Codeplug.Zones)
+ // {
+ // var tabItem = new TabItem { Header = zone.Name };
+
+ // var scrollViewer = new ScrollViewer
+ // {
+ // VerticalScrollBarVisibility = ScrollBarVisibility.Auto,
+ // HorizontalScrollBarVisibility = ScrollBarVisibility.Auto
+ // };
+
+ // var zoneCanvas = new Canvas { Background = Brushes.Transparent };
+
+ // scrollViewer.Content = zoneCanvas;
+ // tabItem.Content = scrollViewer;
+ // ChannelsTabControl.Items.Add(tabItem);
+
+ // double offsetX = 20;
+ // double offsetY = 20;
+
+ // foreach (var channel in zone.Channels)
+ // {
+ // // Add the channel box without capturing a return value
+ // AddChannelBox(channel, zoneCanvas, ref offsetX, ref offsetY);
+
+ // // Dynamically expand canvas as needed
+ // double requiredWidth = offsetX + 40;
+ // double requiredHeight = offsetY + 40;
+ // if (zoneCanvas.Width < requiredWidth)
+ // zoneCanvas.Width = requiredWidth;
+ // if (zoneCanvas.Height < requiredHeight)
+ // zoneCanvas.Height = requiredHeight;
+ // }
+ // }
+
+
+
+
+ // // 3. Add Playback Channel (global)
+ // AddPlaybackChannel(globalOffsetX, globalOffsetY);
+
+ // // 4. Add Alert Tones (global)
+ // AddAlertTones(globalOffsetX, globalOffsetY);
+
+ // // 5. Add QC ToneSets (global)
+ // AddToneSets(globalOffsetX, globalOffsetY);
+ // }
+
+ // AdjustCanvasHeight();
+ //}
+
private void GenerateChannelWidgets()
{
- ChannelsCanvas.Children.Clear();
- double offsetX = 20;
- double offsetY = 20;
+ // Clear previous UI
+ ChannelsTabControl.Items.Clear();
+
+ double globalOffsetX = 20;
+ double globalOffsetY = 20;
if (Codeplug != null)
{
+ // 1. Add System Status Boxes (global)
foreach (var system in Codeplug.Systems)
{
- var systemStatusBox = new SystemStatusBox(system.Name, system.Address, system.Port);
+ AddSystemBox(system, ref globalOffsetX, ref globalOffsetY);
+ }
- if (_settingsManager.SystemStatusPositions.TryGetValue(system.Name, out var position))
+ // 2. Add Zone Tabs
+ foreach (var zone in Codeplug.Zones)
+ {
+ var tabItem = new TabItem { Header = zone.Name };
+
+ var scrollViewer = new ScrollViewer
{
- Canvas.SetLeft(systemStatusBox, position.X);
- Canvas.SetTop(systemStatusBox, position.Y);
- }
- else
+ VerticalScrollBarVisibility = ScrollBarVisibility.Auto,
+ HorizontalScrollBarVisibility = ScrollBarVisibility.Auto
+ };
+
+ var zoneCanvas = new Canvas
{
- Canvas.SetLeft(systemStatusBox, offsetX);
- Canvas.SetTop(systemStatusBox, offsetY);
- }
+ Background = Brushes.Transparent,
+ Width = 1000,
+ Height = 800
+ };
- systemStatusBox.MouseLeftButtonDown += SystemStatusBox_MouseLeftButtonDown;
- systemStatusBox.MouseMove += SystemStatusBox_MouseMove;
- systemStatusBox.MouseRightButtonDown += SystemStatusBox_MouseRightButtonDown;
+ scrollViewer.Content = zoneCanvas;
+ tabItem.Content = scrollViewer;
+ ChannelsTabControl.Items.Add(tabItem);
- ChannelsCanvas.Children.Add(systemStatusBox);
+ double offsetX = 20;
+ double offsetY = 20;
- offsetX += 225;
- if (offsetX + 220 > ChannelsCanvas.ActualWidth)
+ // Add channels first
+ foreach (var channel in zone.Channels)
{
- offsetX = 20;
- offsetY += 106;
+ AddChannelBox(channel, zoneCanvas, ref offsetX, ref offsetY);
}
- if (File.Exists(system.AliasPath))
- system.RidAlias = AliasTools.LoadAliases(system.AliasPath);
+ // Then add tones once for the zone
+ AddToneSets(zoneCanvas, offsetX, offsetY, zone.Name);
- if (!system.IsDvm)
- {
- _webSocketManager.AddWebSocketHandler(system.Name);
+ ChannelsCanvas = zoneCanvas;
- IPeer handler = _webSocketManager.GetWebSocketHandler(system.Name);
- handler.OnVoiceChannelResponse += HandleVoiceResponse;
- handler.OnVoiceChannelRelease += HandleVoiceRelease;
- handler.OnEmergencyAlarmResponse += HandleEmergencyAlarmResponse;
- handler.OnAudioData += HandleReceivedAudio;
- handler.OnAffiliationUpdate += HandleAffiliationUpdate;
- handler.OnCallAlert += HandleCallAlert;
-
- handler.OnUnitRegistrationResponse += (response) =>
+ scrollViewer.Loaded += (s, e) =>
+ {
+ var sv = s as ScrollViewer;
+ if (sv?.Content is Canvas c)
{
- Dispatcher.Invoke(() =>
- {
- if (response.Status == (int)ResponseType.GRANT)
- {
- systemStatusBox.Background = (Brush)new BrushConverter().ConvertFrom("#FF00BC48");
- systemStatusBox.ConnectionState = "Connected";
-
- foreach (ChannelBox channel in _selectedChannelsManager.GetSelectedChannels())
- {
- if (channel.SystemName == PLAYBACKSYS || channel.ChannelName == PLAYBACKCHNAME || channel.DstId == PLAYBACKTG)
- continue;
+ if (sv.ViewportWidth > c.Width) c.Width = sv.ViewportWidth;
+ if (sv.ViewportHeight > c.Height) c.Height = sv.ViewportHeight;
+ }
+ };
+ }
+ }
+ }
- Codeplug.System system = Codeplug.GetSystemForChannel(channel.ChannelName);
- Codeplug.Channel cpgChannel = Codeplug.GetChannelByName(channel.ChannelName);
+ // ---------------------- Helper Methods ----------------------
- if (!system.IsDvm)
- {
- IPeer handler = _webSocketManager.GetWebSocketHandler(system.Name);
+ private void AddSystemBox(Codeplug.System system, ref double offsetX, ref double offsetY)
+ {
+ var systemStatusBox = new SystemStatusBox(system.Name, system.Address, system.Port);
- if (channel.IsSelected && handler.IsConnected)
- {
- Console.WriteLine("sending WLINK master aff");
-
- Task.Run(() =>
- {
- GRP_AFF_REQ release = new GRP_AFF_REQ
- {
- SrcId = system.Rid,
- DstId = cpgChannel.Tgid,
- Site = system.Site
- };
-
- handler.SendMessage(release.GetData());
- });
- }
- }
- }
- }
- else
- {
- systemStatusBox.Background = new SolidColorBrush(Colors.Red);
- systemStatusBox.ConnectionState = "Disconnected";
- }
- });
- };
+ if (_settingsManager.SystemStatusPositions.TryGetValue(system.Name, out var pos))
+ {
+ Canvas.SetLeft(systemStatusBox, pos.X);
+ Canvas.SetTop(systemStatusBox, pos.Y);
+ }
+ else
+ {
+ Canvas.SetLeft(systemStatusBox, offsetX);
+ Canvas.SetTop(systemStatusBox, offsetY);
+ }
- handler.OnClose += () =>
- {
- Dispatcher.Invoke(() =>
- {
- systemStatusBox.Background = new SolidColorBrush(Colors.Red);
- systemStatusBox.ConnectionState = "Disconnected";
- });
- };
+ systemStatusBox.MouseLeftButtonDown += SystemStatusBox_MouseLeftButtonDown;
+ systemStatusBox.MouseMove += SystemStatusBox_MouseMove;
+ systemStatusBox.MouseRightButtonDown += SystemStatusBox_MouseRightButtonDown;
- handler.OnOpen += () =>
- {
- Console.WriteLine("Peer connected");
- U_REG_REQ release = new U_REG_REQ
- {
- SrcId = system.Rid,
- Site = system.Site
- };
+ ChannelsCanvas.Children.Add(systemStatusBox);
- handler.SendMessage(release.GetData());
- };
+ offsetX += 225;
+ if (offsetX + 220 > ChannelsCanvas.ActualWidth)
+ {
+ offsetX = 20;
+ offsetY += 106;
+ }
- handler.OnReconnecting += () =>
- {
- Console.WriteLine("Peer reconnecting");
- };
+ if (File.Exists(system.AliasPath))
+ system.RidAlias = AliasTools.LoadAliases(system.AliasPath);
- Task.Run(() =>
- {
- handler.Connect(system.Address, system.Port, system.AuthKey);
+ if (!system.IsDvm)
+ SetupWebSocketHandler(system, systemStatusBox);
+ else
+ SetupFneSystem(system, systemStatusBox);
- handler.OnGroupAffiliationResponse += (response) => { /* TODO */ };
+ if (!_settingsManager.ShowSystemStatus)
+ systemStatusBox.Visibility = Visibility.Collapsed;
+ }
- if (!handler.IsConnected)
- {
- systemStatusBox.Background = new SolidColorBrush(Colors.Red);
- systemStatusBox.ConnectionState = "Disconnected";
- }
- });
- } else {
- _fneSystemManager.AddFneSystem(system.Name, system, this);
+ private void SetupWebSocketHandler(Codeplug.System system, SystemStatusBox systemStatusBox)
+ {
+ _webSocketManager.AddWebSocketHandler(system.Name);
- PeerSystem peer = _fneSystemManager.GetFneSystem(system.Name);
+ IPeer handler = _webSocketManager.GetWebSocketHandler(system.Name);
- peer.peer.PeerConnected += (sender, response) =>
- {
- Console.WriteLine("FNE Peer connected");
-
- Dispatcher.Invoke(() =>
- {
- systemStatusBox.Background = (Brush)new BrushConverter().ConvertFrom("#FF00BC48");
- systemStatusBox.ConnectionState = "Connected";
- });
- };
+ handler.OnVoiceChannelResponse += HandleVoiceResponse;
+ handler.OnVoiceChannelRelease += HandleVoiceRelease;
+ handler.OnEmergencyAlarmResponse += HandleEmergencyAlarmResponse;
+ handler.OnAudioData += HandleReceivedAudio;
+ handler.OnAffiliationUpdate += HandleAffiliationUpdate;
+ handler.OnCallAlert += HandleCallAlert;
+ handler.OnUnitRegistrationResponse += (response) =>
+ {
+ Dispatcher.Invoke(() =>
+ {
+ if (response.Status == (int)ResponseType.GRANT)
+ {
+ systemStatusBox.Background = (Brush)new BrushConverter().ConvertFrom("#FF00BC48");
+ systemStatusBox.ConnectionState = "Connected";
- peer.peer.PeerDisconnected += (response) =>
+ // Send WLINK master aff for selected channels
+ foreach (ChannelBox channel in _selectedChannelsManager.GetSelectedChannels())
{
- Console.WriteLine("FNE Peer disconnected");
+ if (channel.SystemName == PLAYBACKSYS || channel.ChannelName == PLAYBACKCHNAME || channel.DstId == PLAYBACKTG)
+ continue;
- Dispatcher.Invoke(() =>
- {
- systemStatusBox.Background = new SolidColorBrush(Colors.Red);
- systemStatusBox.ConnectionState = "Disconnected";
- });
- };
+ Codeplug.System sys = Codeplug.GetSystemForChannel(channel.ChannelName);
+ Codeplug.Channel cpgChannel = Codeplug.GetChannelByName(channel.ChannelName);
- Task.Run(() =>
- {
- peer.Start();
- });
+ if (!sys.IsDvm)
+ {
+ IPeer peerHandler = _webSocketManager.GetWebSocketHandler(sys.Name);
+ if (channel.IsSelected && peerHandler.IsConnected)
+ {
+ Task.Run(() =>
+ {
+ GRP_AFF_REQ release = new GRP_AFF_REQ
+ {
+ SrcId = sys.Rid,
+ DstId = cpgChannel.Tgid,
+ Site = sys.Site
+ };
+ peerHandler.SendMessage(release.GetData());
+ });
+ }
+ }
+ }
+ }
+ else
+ {
+ systemStatusBox.Background = new SolidColorBrush(Colors.Red);
+ systemStatusBox.ConnectionState = "Disconnected";
}
+ });
+ };
- if (!_settingsManager.ShowSystemStatus)
- systemStatusBox.Visibility = Visibility.Collapsed;
+ handler.OnClose += () =>
+ {
+ Dispatcher.Invoke(() =>
+ {
+ systemStatusBox.Background = new SolidColorBrush(Colors.Red);
+ systemStatusBox.ConnectionState = "Disconnected";
+ });
+ };
- }
- }
+ handler.OnOpen += () =>
+ {
+ Console.WriteLine("Peer connected");
+ U_REG_REQ release = new U_REG_REQ
+ {
+ SrcId = system.Rid,
+ Site = system.Site
+ };
+ handler.SendMessage(release.GetData());
+ };
+ handler.OnReconnecting += () => Console.WriteLine("Peer reconnecting");
- if (_settingsManager.ShowChannels && Codeplug != null)
+ Task.Run(() =>
{
- foreach (var zone in Codeplug.Zones)
+ handler.Connect(system.Address, system.Port, system.AuthKey);
+ if (!handler.IsConnected)
{
- foreach (var channel in zone.Channels)
+ Dispatcher.Invoke(() =>
{
- var channelBox = new ChannelBox(_selectedChannelsManager, _audioManager, channel.Name, channel.System, channel.Tgid);
+ systemStatusBox.Background = new SolidColorBrush(Colors.Red);
+ systemStatusBox.ConnectionState = "Disconnected";
+ });
+ }
+ });
+ }
- //channelBox.crypter.AddKey(channel.GetKeyId(), channel.GetAlgoId(), channel.GetEncryptionKey());
+ private void SetupFneSystem(Codeplug.System system, SystemStatusBox systemStatusBox)
+ {
+ _fneSystemManager.AddFneSystem(system.Name, system, this);
- systemStatuses.Add(channel.Name, new SlotStatus());
+ PeerSystem peer = _fneSystemManager.GetFneSystem(system.Name);
- if (_settingsManager.ChannelPositions.TryGetValue(channel.Name, out var position))
- {
- Canvas.SetLeft(channelBox, position.X);
- Canvas.SetTop(channelBox, position.Y);
- }
- else
- {
- Canvas.SetLeft(channelBox, offsetX);
- Canvas.SetTop(channelBox, offsetY);
- }
+ peer.peer.PeerConnected += (sender, response) =>
+ {
+ Dispatcher.Invoke(() =>
+ {
+ systemStatusBox.Background = (Brush)new BrushConverter().ConvertFrom("#FF00BC48");
+ systemStatusBox.ConnectionState = "Connected";
+ });
+ };
- channelBox.PTTButtonClicked += ChannelBox_PTTButtonClicked;
- channelBox.PageButtonClicked += ChannelBox_PageButtonClicked;
- channelBox.HoldChannelButtonClicked += ChannelBox_HoldChannelButtonClicked;
+ peer.peer.PeerDisconnected += (response) =>
+ {
+ Dispatcher.Invoke(() =>
+ {
+ systemStatusBox.Background = new SolidColorBrush(Colors.Red);
+ systemStatusBox.ConnectionState = "Disconnected";
+ });
+ };
- channelBox.MouseLeftButtonDown += ChannelBox_MouseLeftButtonDown;
- channelBox.MouseMove += ChannelBox_MouseMove;
- channelBox.MouseRightButtonDown += ChannelBox_MouseRightButtonDown;
- ChannelsCanvas.Children.Add(channelBox);
+ Task.Run(() => peer.Start());
+ }
- offsetX += 225;
+ private void AddChannelBox(Codeplug.Channel channel, Canvas canvas, ref double offsetX, ref double offsetY)
+ {
+ var channelBox = new ChannelBox(_selectedChannelsManager, _audioManager, channel.Name, channel.System, channel.Tgid);
+ systemStatuses.Add(channel.Name, new SlotStatus());
- if (offsetX + 220 > ChannelsCanvas.ActualWidth)
- {
- offsetX = 20;
- offsetY += 106;
- }
- }
- }
+ if (_settingsManager.ChannelPositions.TryGetValue(channel.Name, out var pos))
+ {
+ Canvas.SetLeft(channelBox, pos.X);
+ Canvas.SetTop(channelBox, pos.Y);
}
-
- if (_settingsManager.ShowAlertTones && Codeplug != null)
+ else
{
- foreach (var alertPath in _settingsManager.AlertToneFilePaths)
- {
- var alertTone = new AlertTone(alertPath)
- {
- IsEditMode = isEditMode
- };
+ Canvas.SetLeft(channelBox, offsetX);
+ Canvas.SetTop(channelBox, offsetY);
+ }
- alertTone.OnAlertTone += SendAlertTone;
+ channelBox.PTTButtonClicked += ChannelBox_PTTButtonClicked;
+ channelBox.PageButtonClicked += ChannelBox_PageButtonClicked;
+ channelBox.HoldChannelButtonClicked += ChannelBox_HoldChannelButtonClicked;
- if (_settingsManager.AlertTonePositions.TryGetValue(alertPath, out var position))
- {
- Canvas.SetLeft(alertTone, position.X);
- Canvas.SetTop(alertTone, position.Y);
- }
- else
- {
- Canvas.SetLeft(alertTone, 20);
- Canvas.SetTop(alertTone, 20);
- }
+ channelBox.MouseLeftButtonDown += ChannelBox_MouseLeftButtonDown;
+ channelBox.MouseMove += ChannelBox_MouseMove;
+ channelBox.MouseRightButtonDown += ChannelBox_MouseRightButtonDown;
- alertTone.MouseRightButtonUp += AlertTone_MouseRightButtonUp;
+ canvas.Children.Add(channelBox);
- ChannelsCanvas.Children.Add(alertTone);
- }
+ offsetX += 225;
+ if (offsetX + 220 > canvas.ActualWidth)
+ {
+ offsetX = 20;
+ offsetY += 106;
}
- // Add ToneSet boxes if enabled
- if (_settingsManager.ShowQCTones && Codeplug != null && Codeplug?.Tones != null)
+ AdjustCanvasHeight();
+ }
+
+ private void AddPlaybackChannel(double offsetX, double offsetY)
+ {
+ playbackChannelBox = new ChannelBox(_selectedChannelsManager, _audioManager, PLAYBACKCHNAME, PLAYBACKSYS, PLAYBACKTG);
+
+ if (_settingsManager.ChannelPositions.TryGetValue(PLAYBACKCHNAME, out var pos))
{
- foreach (var tone in Codeplug.Tones)
- {
+ Canvas.SetLeft(playbackChannelBox, pos.X);
+ Canvas.SetTop(playbackChannelBox, pos.Y);
+ }
+ else
+ {
+ Canvas.SetLeft(playbackChannelBox, offsetX);
+ Canvas.SetTop(playbackChannelBox, offsetY);
+ }
- var toneSetControl = new ToneSet(tone.Name, tone.ToneA, tone.ToneB);
+ playbackChannelBox.PTTButtonClicked += ChannelBox_PTTButtonClicked;
+ playbackChannelBox.PageButtonClicked += ChannelBox_PageButtonClicked;
+ playbackChannelBox.HoldChannelButtonClicked += ChannelBox_HoldChannelButtonClicked;
- // Hook up events
- toneSetControl.PlayClicked += async (s, e) =>
- {
- if (_selectedToneSets.Count > 0)
- {
- var selectedTones = _selectedToneSets.ToList();
- var initiallyActiveChannels = _selectedChannelsManager
- .GetSelectedChannels()
- .Where(c => c.PageState)
- .ToList();
+ playbackChannelBox.MouseLeftButtonDown += ChannelBox_MouseLeftButtonDown;
+ playbackChannelBox.MouseMove += ChannelBox_MouseMove;
+ playbackChannelBox.MouseRightButtonDown += ChannelBox_MouseRightButtonDown;
- for (int i = 0; i < selectedTones.Count; i++)
- {
- var selected = selectedTones[i];
- var key = (selected.ToneA, selected.ToneB);
+ ChannelsCanvas.Children.Add(playbackChannelBox);
+ }
- // ✅ Restore PageState for each tone before playback
- foreach (var ch in initiallyActiveChannels)
- {
- ch.PageState = true;
- Dispatcher.Invoke(() =>
- {
- ch.PageSelectButton.Background = ch.orangeGradient;
- });
+ private void AddAlertTones(double offsetX, double offsetY)
+ {
+ if (!_settingsManager.ShowAlertTones) return;
- // This ensures PageState on the *server* is activated
- ChannelBox_PageButtonClicked(ch, ch);
- }
+ foreach (var alertPath in _settingsManager.AlertToneFilePaths)
+ {
+ var alertTone = new AlertTone(alertPath) { IsEditMode = isEditMode };
+ alertTone.OnAlertTone += SendAlertTone;
- if (i > 0)
- {
- await Task.Delay(1750); // delay only after the first tone
- }
+ if (_settingsManager.AlertTonePositions.TryGetValue(alertPath, out var pos))
+ {
+ Canvas.SetLeft(alertTone, pos.X);
+ Canvas.SetTop(alertTone, pos.Y);
+ }
+ else
+ {
+ Canvas.SetLeft(alertTone, 20);
+ Canvas.SetTop(alertTone, 20);
+ }
+ alertTone.MouseRightButtonUp += AlertTone_MouseRightButtonUp;
+ ChannelsCanvas.Children.Add(alertTone);
+ }
+ }
- // ▶️ Play the tone
- await PlayTone(selected.ToneA.ToString(), selected.ToneB.ToString());
+ private void AddToneSets(Canvas zoneCanvas, double offsetX, double offsetY, string zone)
+ {
+ if (!_settingsManager.ShowQCTones || Codeplug?.Tones == null) return;
+ Console.WriteLine($"Zone Name: {zone}");
+ Console.WriteLine($"ShowQCTones: {_settingsManager.ShowQCTones}");
+ Console.WriteLine($"Loaded tones: {Codeplug.Tones?.Count ?? 0}");
+ Console.WriteLine($"Zone canvas size: {zoneCanvas.ActualWidth}x{zoneCanvas.ActualHeight}");
+ Console.WriteLine($"Adding tones for zone: {zone}");
- // ❌ Remove from selected set
- _selectedToneSets.Remove(key);
+ foreach (var tone in Codeplug.Tones)
+ {
+ Console.WriteLine($"Tone {tone.Name}, AllowedZones: {string.Join(",", tone.AllowedZones)}");
- // 🔄 Deselect tone visually
- foreach (var child in ChannelsCanvas.Children)
- {
- if (child is ToneSet ts && ts.ToneA == key.ToneA && ts.ToneB == key.ToneB)
- {
- ts.SetSelected(false);
- break;
- }
- }
- }
+ // Only add tones allowed in this zone
+ if (tone.AllowedZones == null || !tone.AllowedZones.Any(z => string.Equals(z.Trim(), zone.Trim(), StringComparison.OrdinalIgnoreCase)))
+ {
+ continue;
+ }
+
+ var toneSetControl = new ToneSet(tone.Name, tone.ToneA, tone.ToneB);
+
+ // --- Play logic ---
+ toneSetControl.PlayClicked += async (s, e) =>
+ {
+ if (_selectedToneSets.Count > 0)
+ {
+ var selectedTones = _selectedToneSets.ToList();
+ var initiallyActiveChannels = _selectedChannelsManager
+ .GetSelectedChannels()
+ .Where(c => c.PageState)
+ .ToList();
+
+ for (int i = 0; i < selectedTones.Count; i++)
+ {
+ var selected = selectedTones[i];
+ var key = (selected.ToneA, selected.ToneB);
- // 🧹 Final cleanup: clear PageState on all initially active channels
foreach (var ch in initiallyActiveChannels)
{
- ch.PageState = false;
+ ch.PageState = true;
Dispatcher.Invoke(() =>
{
- ch.PageSelectButton.Background = ch.grayGradient;
+ ch.PageSelectButton.Background = ch.orangeGradient;
});
+ ChannelBox_PageButtonClicked(ch, ch);
}
- }
- else
- {
- // 🔁 Single tone fallback
- var hasActivePage = _selectedChannelsManager.GetSelectedChannels().Any(c => c.PageState);
- if (hasActivePage)
- {
- await PlayTone(tone.ToneA.ToString(), tone.ToneB.ToString());
- foreach (var channel in _selectedChannelsManager.GetSelectedChannels())
+ await Task.Delay(2000);
+ await PlayTone(selected.ToneA.ToString(), selected.ToneB.ToString());
+
+ _selectedToneSets.Remove(key);
+
+ foreach (var child in zoneCanvas.Children)
+ {
+ if (child is ToneSet ts && ts.ToneA == key.ToneA && ts.ToneB == key.ToneB)
{
- channel.PageState = false;
- channel.PageSelectButton.Background = channel.grayGradient;
+ ts.SetSelected(false);
+ break;
}
}
}
- };
-
- toneSetControl.SelectToggled += (s, e) =>
- {
- var key = (tone.ToneA, tone.ToneB);
- if (_selectedToneSets.Contains(key))
+ foreach (var ch in initiallyActiveChannels)
{
- _selectedToneSets.Remove(key);
- toneSetControl.SetSelected(false);
+ ch.PageState = false;
+ Dispatcher.Invoke(() =>
+ {
+ ch.PageSelectButton.Background = ch.grayGradient;
+ });
}
- else
+ }
+ else
+ {
+ var hasActivePage = _selectedChannelsManager.GetSelectedChannels().Any(c => c.PageState);
+ if (hasActivePage)
{
- _selectedToneSets.Add(key);
- toneSetControl.SetSelected(true);
+ await PlayTone(tone.ToneA.ToString(), tone.ToneB.ToString());
+
+ foreach (var channel in _selectedChannelsManager.GetSelectedChannels())
+ {
+ channel.PageState = false;
+ channel.PageSelectButton.Background = channel.grayGradient;
+ }
}
- };
+ }
+ };
- // Position like ToneSet Boxes using same layout logic
- if (_settingsManager.QCToneSetPositions.TryGetValue(tone.Name, out var position))
+ // --- Select/Toggle logic ---
+ toneSetControl.SelectToggled += (s, e) =>
+ {
+ var key = (tone.ToneA, tone.ToneB);
+ if (_selectedToneSets.Contains(key))
{
- Canvas.SetLeft(toneSetControl, position.X);
- Canvas.SetTop(toneSetControl, position.Y);
+ _selectedToneSets.Remove(key);
+ toneSetControl.SetSelected(false);
}
else
{
- Canvas.SetLeft(toneSetControl, offsetX);
- Canvas.SetTop(toneSetControl, offsetY);
+ _selectedToneSets.Add(key);
+ toneSetControl.SetSelected(true);
}
+ };
- toneSetControl.MouseLeftButtonDown += ToneSet_MouseLeftButtonDown;
- toneSetControl.MouseMove += ToneSet_MouseMove;
- toneSetControl.MouseRightButtonDown += ToneSet_MouseRightButtonDown;
-
- ChannelsCanvas.Children.Add(toneSetControl);
-
- offsetX += 225;
-
- if (offsetX + 220 > ChannelsCanvas.ActualWidth)
- {
- offsetX = 20;
- offsetY += 106;
- }
+ // --- Positioning ---
+ if (_settingsManager.QCToneSetPositions.TryGetValue(tone.Name, out var pos))
+ {
+ Canvas.SetLeft(toneSetControl, pos.X);
+ Canvas.SetTop(toneSetControl, pos.Y);
+ }
+ else
+ {
+ Canvas.SetLeft(toneSetControl, offsetX);
+ Canvas.SetTop(toneSetControl, offsetY);
}
- }
-
+ toneSetControl.MouseLeftButtonDown += ToneSet_MouseLeftButtonDown;
+ toneSetControl.MouseMove += ToneSet_MouseMove;
+ toneSetControl.MouseRightButtonDown += ToneSet_MouseRightButtonDown;
- playbackChannelBox = new ChannelBox(_selectedChannelsManager, _audioManager, PLAYBACKCHNAME, PLAYBACKSYS, PLAYBACKTG);
+ // Add to current zone canvas
+ zoneCanvas.Children.Add(toneSetControl);
- if (_settingsManager.ChannelPositions.TryGetValue(PLAYBACKCHNAME, out var pos))
- {
- Canvas.SetLeft(playbackChannelBox, pos.X);
- Canvas.SetTop(playbackChannelBox, pos.Y);
- }
- else
- {
- Canvas.SetLeft(playbackChannelBox, offsetX);
- Canvas.SetTop(playbackChannelBox, offsetY);
+ // --- Layout increment ---
+ offsetX += 225;
+ if (offsetX + 220 > zoneCanvas.ActualWidth)
+ {
+ offsetX = 20;
+ offsetY += 106;
+ }
}
+ }
- playbackChannelBox.PTTButtonClicked += ChannelBox_PTTButtonClicked;
- playbackChannelBox.PageButtonClicked += ChannelBox_PageButtonClicked;
- playbackChannelBox.HoldChannelButtonClicked += ChannelBox_HoldChannelButtonClicked;
-
- playbackChannelBox.MouseLeftButtonDown += ChannelBox_MouseLeftButtonDown;
- playbackChannelBox.MouseMove += ChannelBox_MouseMove;
- playbackChannelBox.MouseRightButtonDown += ChannelBox_MouseRightButtonDown;
- ChannelsCanvas.Children.Add(playbackChannelBox);
-
- //offsetX += 225;
- //if (offsetX + 220 > ChannelsCanvas.ActualWidth)
- //{
- // offsetX = 20;
- // offsetY += 106;
- //}
-
- AdjustCanvasHeight();
- }
+ //private void AddToneSets(Canvas zoneCanvas, double offsetX, double offsetY, string zone)
+ //{
+ // if (!_settingsManager.ShowQCTones || Codeplug?.Tones == null) return;
+
+ // foreach (var tone in Codeplug.Tones)
+ // {
+ // var toneSetControl = new ToneSet(tone.Name, tone.ToneA, tone.ToneB);
+
+ // toneSetControl.PlayClicked += async (s, e) =>
+ // {
+ // if (_selectedToneSets.Count > 0)
+ // {
+ // var selectedTones = _selectedToneSets.ToList();
+ // var initiallyActiveChannels = _selectedChannelsManager
+ // .GetSelectedChannels()
+ // .Where(c => c.PageState)
+ // .ToList();
+
+ // for (int i = 0; i < selectedTones.Count; i++)
+ // {
+ // var selected = selectedTones[i];
+ // var key = (selected.ToneA, selected.ToneB);
+
+ // foreach (var ch in initiallyActiveChannels)
+ // {
+ // ch.PageState = true;
+ // Dispatcher.Invoke(() =>
+ // {
+ // ch.PageSelectButton.Background = ch.orangeGradient;
+ // });
+ // ChannelBox_PageButtonClicked(ch, ch);
+ // }
+
+ // await Task.Delay(2000);
+
+ // await PlayTone(selected.ToneA.ToString(), selected.ToneB.ToString());
+
+ // _selectedToneSets.Remove(key);
+
+ // foreach (var child in ChannelsCanvas.Children)
+ // {
+ // if (child is ToneSet ts && ts.ToneA == key.ToneA && ts.ToneB == key.ToneB)
+ // {
+ // ts.SetSelected(false);
+ // break;
+ // }
+ // }
+ // }
+
+ // foreach (var ch in initiallyActiveChannels)
+ // {
+ // ch.PageState = false;
+ // Dispatcher.Invoke(() =>
+ // {
+ // ch.PageSelectButton.Background = ch.grayGradient;
+ // });
+ // }
+ // }
+ // else
+ // {
+ // var hasActivePage = _selectedChannelsManager.GetSelectedChannels().Any(c => c.PageState);
+ // if (hasActivePage)
+ // {
+ // await PlayTone(tone.ToneA.ToString(), tone.ToneB.ToString());
+
+ // foreach (var channel in _selectedChannelsManager.GetSelectedChannels())
+ // {
+ // channel.PageState = false;
+ // channel.PageSelectButton.Background = channel.grayGradient;
+ // }
+ // }
+ // }
+ // };
+
+ // toneSetControl.SelectToggled += (s, e) =>
+ // {
+ // var key = (tone.ToneA, tone.ToneB);
+ // if (_selectedToneSets.Contains(key))
+ // {
+ // _selectedToneSets.Remove(key);
+ // toneSetControl.SetSelected(false);
+ // }
+ // else
+ // {
+ // _selectedToneSets.Add(key);
+ // toneSetControl.SetSelected(true);
+ // }
+ // };
+
+ // if (_settingsManager.QCToneSetPositions.TryGetValue(tone.Name, out var pos))
+ // {
+ // Canvas.SetLeft(toneSetControl, pos.X);
+ // Canvas.SetTop(toneSetControl, pos.Y);
+ // }
+ // else
+ // {
+ // Canvas.SetLeft(toneSetControl, offsetX);
+ // Canvas.SetTop(toneSetControl, offsetY);
+ // }
+
+ // toneSetControl.MouseLeftButtonDown += ToneSet_MouseLeftButtonDown;
+ // toneSetControl.MouseMove += ToneSet_MouseMove;
+ // toneSetControl.MouseRightButtonDown += ToneSet_MouseRightButtonDown;
+
+ // //ChannelsCanvas.Children.Add(toneSetControl);
+
+ // zoneCanvas.Children.Add(toneSetControl);
+
+ // offsetX += 225;
+ // if (offsetX + 220 > zoneCanvas.ActualWidth)
+ // {
+ // offsetX = 20;
+ // offsetY += 106;
+ // }
+ // }
+ //}
private const int GridSize = 5;
@@ -1522,53 +1768,106 @@ private void ChannelBox_PTTButtonClicked(object sender, ChannelBox e)
private void ChannelBox_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
- if (!isEditMode || !(sender is UIElement element)) return;
+ if (!(sender is UIElement element)) return;
- _draggedElement = element;
- _startPoint = e.GetPosition(ChannelsCanvas);
- _offsetX = _startPoint.X - Canvas.GetLeft(_draggedElement);
- _offsetY = _startPoint.Y - Canvas.GetTop(_draggedElement);
- _isDragging = true;
+ if (isEditMode)
+ {
+ _draggedElement = element;
+ _startPoint = e.GetPosition((UIElement)VisualTreeHelper.GetParent(element));
+ _offsetX = _startPoint.X - Canvas.GetLeft(_draggedElement);
+ _offsetY = _startPoint.Y - Canvas.GetTop(_draggedElement);
+ _isDragging = true;
- element.CaptureMouse();
+ element.CaptureMouse();
+ e.Handled = true; // prevent click from toggling state
+ }
+ else
+ {
+ // Normal channel click behavior (toggle state)
+ // You can leave your ChannelBox click handler here
+ }
}
+
+ //private void ChannelBox_MouseMove(object sender, MouseEventArgs e)
+ //{
+ // if (!isEditMode || !_isDragging || _draggedElement == null) return;
+
+ // Point currentPosition = e.GetPosition(ChannelsCanvas);
+
+ // // Calculate the new position with snapping to the grid
+ // double newLeft = Math.Round((currentPosition.X - _offsetX) / GridSize) * GridSize;
+ // double newTop = Math.Round((currentPosition.Y - _offsetY) / GridSize) * GridSize;
+
+ // // Ensure the box stays within canvas bounds
+ // newLeft = Math.Max(0, Math.Min(newLeft, ChannelsCanvas.ActualWidth - _draggedElement.RenderSize.Width));
+ // newTop = Math.Max(0, Math.Min(newTop, ChannelsCanvas.ActualHeight - _draggedElement.RenderSize.Height));
+
+ // // Apply snapped position
+ // Canvas.SetLeft(_draggedElement, newLeft);
+ // Canvas.SetTop(_draggedElement, newTop);
+
+ // // Save the new position if it's a ChannelBox
+ // if (_draggedElement is ChannelBox channelBox)
+ // {
+ // _settingsManager.UpdateChannelPosition(channelBox.ChannelName, newLeft, newTop);
+ // }
+
+ // AdjustCanvasHeight();
+ //}
+
private void ChannelBox_MouseMove(object sender, MouseEventArgs e)
{
- if (!isEditMode || !_isDragging || _draggedElement == null) return;
+ if (!_isDragging || _draggedElement == null) return;
- Point currentPosition = e.GetPosition(ChannelsCanvas);
+ Point currentPosition = e.GetPosition((UIElement)VisualTreeHelper.GetParent(_draggedElement));
- // Calculate the new position with snapping to the grid
double newLeft = Math.Round((currentPosition.X - _offsetX) / GridSize) * GridSize;
double newTop = Math.Round((currentPosition.Y - _offsetY) / GridSize) * GridSize;
- // Ensure the box stays within canvas bounds
newLeft = Math.Max(0, Math.Min(newLeft, ChannelsCanvas.ActualWidth - _draggedElement.RenderSize.Width));
newTop = Math.Max(0, Math.Min(newTop, ChannelsCanvas.ActualHeight - _draggedElement.RenderSize.Height));
- // Apply snapped position
Canvas.SetLeft(_draggedElement, newLeft);
Canvas.SetTop(_draggedElement, newTop);
- // Save the new position if it's a ChannelBox
if (_draggedElement is ChannelBox channelBox)
- {
_settingsManager.UpdateChannelPosition(channelBox.ChannelName, newLeft, newTop);
- }
AdjustCanvasHeight();
}
- private void ChannelBox_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
+ private void ChannelBox_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
- if (!isEditMode || !_isDragging || _draggedElement == null) return;
+ if (_isDragging && _draggedElement != null)
+ {
+ _isDragging = false;
+ _draggedElement.ReleaseMouseCapture();
+ _draggedElement = null;
+ e.Handled = true;
+ }
+ }
- _isDragging = false;
- _draggedElement.ReleaseMouseCapture();
- _draggedElement = null;
+ private void ChannelBox_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
+ {
+ if (_isDragging && _draggedElement != null)
+ {
+ _isDragging = false;
+ _draggedElement.ReleaseMouseCapture();
+ _draggedElement = null;
+ e.Handled = true;
+ }
}
+ //private void ChannelBox_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
+ //{
+ // if (!isEditMode || !_isDragging || _draggedElement == null) return;
+
+ // _isDragging = false;
+ // _draggedElement.ReleaseMouseCapture();
+ // _draggedElement = null;
+ //}
+
private void SystemStatusBox_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) => ChannelBox_MouseLeftButtonDown(sender, e);
private void SystemStatusBox_MouseMove(object sender, MouseEventArgs e) => ChannelBox_MouseMove(sender, e);
@@ -1697,6 +1996,8 @@ private async Task PlayTone(string ToneA, string ToneB)
{
var selectedChannels = _selectedChannelsManager.GetSelectedChannels();
+ Console.WriteLine($"Playing Tones A={ToneA}, B={ToneB}");
+
// Check if any selected channel has PageState = true
if (!selectedChannels.Any(ch => ch.PageState))
{
@@ -2655,5 +2956,10 @@ private void CallHist_Click(object sender, RoutedEventArgs e)
{
callHistoryWindow.Show();
}
+
+ private void MenuItem_Click(object sender, RoutedEventArgs e)
+ {
+
+ }
}
}
diff --git a/WhackerLinkConsoleV2/SettingsManager.cs b/WhackerLinkConsoleV2/SettingsManager.cs
index 5e3f682..71bd526 100644
--- a/WhackerLinkConsoleV2/SettingsManager.cs
+++ b/WhackerLinkConsoleV2/SettingsManager.cs
@@ -32,6 +32,8 @@ public class SettingsManager
public bool ShowAlertTones { get; set; } = true;
public bool ShowQCTones { get; set; } = true;
+ public bool DebugMode { get; set; } = false;
+
public string LastCodeplugPath { get; set; } = null;
public Dictionary ChannelPositions { get; set; } = new Dictionary();
@@ -52,6 +54,7 @@ public void LoadSettings()
if (loadedSettings != null)
{
+ DebugMode = loadedSettings.DebugMode;
ShowSystemStatus = loadedSettings.ShowSystemStatus;
ShowChannels = loadedSettings.ShowChannels;
ShowAlertTones = loadedSettings.ShowAlertTones;
@@ -71,6 +74,12 @@ public void LoadSettings()
}
}
+
+ public void ToggleDebugMode()
+ {
+ DebugMode = !DebugMode;
+ SaveSettings();
+ }
public void UpdateAlertTonePaths(string newFilePath)
{
if (!AlertToneFilePaths.Contains(newFilePath))
From 76ed6f4c3b7b579fd860c929068ce31cdbf2c2ac Mon Sep 17 00:00:00 2001
From: Devin G <149845346+DeathSoulYTDev@users.noreply.github.com>
Date: Sun, 17 Aug 2025 18:50:29 -0400
Subject: [PATCH 08/17] Updated WLLib
---
WhackerLinkLib | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/WhackerLinkLib b/WhackerLinkLib
index 9178526..f1dc49c 160000
--- a/WhackerLinkLib
+++ b/WhackerLinkLib
@@ -1 +1 @@
-Subproject commit 9178526bf07a73374f2bfe016bc0b48602922d3a
+Subproject commit f1dc49c761c53db840becf99188924147d514137
From 33bda3ba334b16d28da0bc39b315ccc9d1d9da8b Mon Sep 17 00:00:00 2001
From: Devin G <149845346+DeathSoulYTDev@users.noreply.github.com>
Date: Thu, 21 Aug 2025 14:45:54 -0400
Subject: [PATCH 09/17] Comments Removal & Tone Delay
- Removed All Unused/Old Code that was Commented Out
- Fixed the Stacked Tones Delay so its not delaying the First Tone
---
WhackerLinkConsoleV2/App.xaml.cs | 15 --
WhackerLinkConsoleV2/ChannelBox.xaml.cs | 1 -
WhackerLinkConsoleV2/MainWindow.xaml.cs | 192 ------------------------
3 files changed, 208 deletions(-)
diff --git a/WhackerLinkConsoleV2/App.xaml.cs b/WhackerLinkConsoleV2/App.xaml.cs
index af25b44..6f584cf 100644
--- a/WhackerLinkConsoleV2/App.xaml.cs
+++ b/WhackerLinkConsoleV2/App.xaml.cs
@@ -1,7 +1,6 @@
using System.Configuration;
using System.Data;
using System.Windows;
-using WhackerLinkLib.Models.Radio;
namespace WhackerLinkConsoleV2
{
@@ -10,20 +9,6 @@ namespace WhackerLinkConsoleV2
///
public partial class App : Application
{
- public Codeplug Codeplug { get; set; }
-
- [System.Runtime.InteropServices.DllImport("kernel32.dll")]
- private static extern bool AllocConsole();
-
- public App()
- {
- //AllocConsole();
- //Console.WriteLine($"Debug: {Codeplug}");
- //if (Codeplug.Systems[0].debug)
- //{
- // Console.WriteLine("Hello, world!");
- //}
- }
}
}
diff --git a/WhackerLinkConsoleV2/ChannelBox.xaml.cs b/WhackerLinkConsoleV2/ChannelBox.xaml.cs
index b60f7cf..cf90922 100644
--- a/WhackerLinkConsoleV2/ChannelBox.xaml.cs
+++ b/WhackerLinkConsoleV2/ChannelBox.xaml.cs
@@ -301,7 +301,6 @@ private void PageSelectButton_Click(object sender, RoutedEventArgs e)
{
if (!IsSelected) return;
-
PageState = !PageState;
PageButtonClicked.Invoke(sender, this);
}
diff --git a/WhackerLinkConsoleV2/MainWindow.xaml.cs b/WhackerLinkConsoleV2/MainWindow.xaml.cs
index a3d28e5..7744313 100644
--- a/WhackerLinkConsoleV2/MainWindow.xaml.cs
+++ b/WhackerLinkConsoleV2/MainWindow.xaml.cs
@@ -202,75 +202,6 @@ private void LoadCodeplug(string filePath)
MessageBox.Show($"Error loading codeplug: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
-
- //private void GenerateChannelWidgets()
- //{
- // // Clear previous UI
- // ChannelsTabControl.Items.Clear();
- // ChannelsCanvas.Children.Clear();
-
- // double globalOffsetX = 20;
- // double globalOffsetY = 20;
-
- // if (Codeplug != null)
- // {
- // // 1. Add System Status Boxes (global)
- // foreach (var system in Codeplug.Systems)
- // {
- // AddSystemBox(system, ref globalOffsetX, ref globalOffsetY);
- // }
-
- // // 2. Add Zone Tabs (each tab gets its own ScrollViewer + Canvas)
- // foreach (var zone in Codeplug.Zones)
- // {
- // var tabItem = new TabItem { Header = zone.Name };
-
- // var scrollViewer = new ScrollViewer
- // {
- // VerticalScrollBarVisibility = ScrollBarVisibility.Auto,
- // HorizontalScrollBarVisibility = ScrollBarVisibility.Auto
- // };
-
- // var zoneCanvas = new Canvas { Background = Brushes.Transparent };
-
- // scrollViewer.Content = zoneCanvas;
- // tabItem.Content = scrollViewer;
- // ChannelsTabControl.Items.Add(tabItem);
-
- // double offsetX = 20;
- // double offsetY = 20;
-
- // foreach (var channel in zone.Channels)
- // {
- // // Add the channel box without capturing a return value
- // AddChannelBox(channel, zoneCanvas, ref offsetX, ref offsetY);
-
- // // Dynamically expand canvas as needed
- // double requiredWidth = offsetX + 40;
- // double requiredHeight = offsetY + 40;
- // if (zoneCanvas.Width < requiredWidth)
- // zoneCanvas.Width = requiredWidth;
- // if (zoneCanvas.Height < requiredHeight)
- // zoneCanvas.Height = requiredHeight;
- // }
- // }
-
-
-
-
- // // 3. Add Playback Channel (global)
- // AddPlaybackChannel(globalOffsetX, globalOffsetY);
-
- // // 4. Add Alert Tones (global)
- // AddAlertTones(globalOffsetX, globalOffsetY);
-
- // // 5. Add QC ToneSets (global)
- // AddToneSets(globalOffsetX, globalOffsetY);
- // }
-
- // AdjustCanvasHeight();
- //}
-
private void GenerateChannelWidgets()
{
// Clear previous UI
@@ -718,124 +649,6 @@ private void AddToneSets(Canvas zoneCanvas, double offsetX, double offsetY, stri
}
}
-
- //private void AddToneSets(Canvas zoneCanvas, double offsetX, double offsetY, string zone)
- //{
- // if (!_settingsManager.ShowQCTones || Codeplug?.Tones == null) return;
-
- // foreach (var tone in Codeplug.Tones)
- // {
- // var toneSetControl = new ToneSet(tone.Name, tone.ToneA, tone.ToneB);
-
- // toneSetControl.PlayClicked += async (s, e) =>
- // {
- // if (_selectedToneSets.Count > 0)
- // {
- // var selectedTones = _selectedToneSets.ToList();
- // var initiallyActiveChannels = _selectedChannelsManager
- // .GetSelectedChannels()
- // .Where(c => c.PageState)
- // .ToList();
-
- // for (int i = 0; i < selectedTones.Count; i++)
- // {
- // var selected = selectedTones[i];
- // var key = (selected.ToneA, selected.ToneB);
-
- // foreach (var ch in initiallyActiveChannels)
- // {
- // ch.PageState = true;
- // Dispatcher.Invoke(() =>
- // {
- // ch.PageSelectButton.Background = ch.orangeGradient;
- // });
- // ChannelBox_PageButtonClicked(ch, ch);
- // }
-
- // await Task.Delay(2000);
-
- // await PlayTone(selected.ToneA.ToString(), selected.ToneB.ToString());
-
- // _selectedToneSets.Remove(key);
-
- // foreach (var child in ChannelsCanvas.Children)
- // {
- // if (child is ToneSet ts && ts.ToneA == key.ToneA && ts.ToneB == key.ToneB)
- // {
- // ts.SetSelected(false);
- // break;
- // }
- // }
- // }
-
- // foreach (var ch in initiallyActiveChannels)
- // {
- // ch.PageState = false;
- // Dispatcher.Invoke(() =>
- // {
- // ch.PageSelectButton.Background = ch.grayGradient;
- // });
- // }
- // }
- // else
- // {
- // var hasActivePage = _selectedChannelsManager.GetSelectedChannels().Any(c => c.PageState);
- // if (hasActivePage)
- // {
- // await PlayTone(tone.ToneA.ToString(), tone.ToneB.ToString());
-
- // foreach (var channel in _selectedChannelsManager.GetSelectedChannels())
- // {
- // channel.PageState = false;
- // channel.PageSelectButton.Background = channel.grayGradient;
- // }
- // }
- // }
- // };
-
- // toneSetControl.SelectToggled += (s, e) =>
- // {
- // var key = (tone.ToneA, tone.ToneB);
- // if (_selectedToneSets.Contains(key))
- // {
- // _selectedToneSets.Remove(key);
- // toneSetControl.SetSelected(false);
- // }
- // else
- // {
- // _selectedToneSets.Add(key);
- // toneSetControl.SetSelected(true);
- // }
- // };
-
- // if (_settingsManager.QCToneSetPositions.TryGetValue(tone.Name, out var pos))
- // {
- // Canvas.SetLeft(toneSetControl, pos.X);
- // Canvas.SetTop(toneSetControl, pos.Y);
- // }
- // else
- // {
- // Canvas.SetLeft(toneSetControl, offsetX);
- // Canvas.SetTop(toneSetControl, offsetY);
- // }
-
- // toneSetControl.MouseLeftButtonDown += ToneSet_MouseLeftButtonDown;
- // toneSetControl.MouseMove += ToneSet_MouseMove;
- // toneSetControl.MouseRightButtonDown += ToneSet_MouseRightButtonDown;
-
- // //ChannelsCanvas.Children.Add(toneSetControl);
-
- // zoneCanvas.Children.Add(toneSetControl);
-
- // offsetX += 225;
- // if (offsetX + 220 > zoneCanvas.ActualWidth)
- // {
- // offsetX = 20;
- // offsetY += 106;
- // }
- // }
- //}
-
private const int GridSize = 5;
private void ToneSet_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
@@ -2956,10 +2769,5 @@ private void CallHist_Click(object sender, RoutedEventArgs e)
{
callHistoryWindow.Show();
}
-
- private void MenuItem_Click(object sender, RoutedEventArgs e)
- {
-
- }
}
}
From 087a8e13d6173e5edbf348df4eb7adf49d48377b Mon Sep 17 00:00:00 2001
From: Devin G <149845346+DeathSoulYTDev@users.noreply.github.com>
Date: Thu, 21 Aug 2025 17:36:51 -0400
Subject: [PATCH 10/17] UI Fix
- Fixed Canvas Sizing When Window Resizes
- Auto Added A SYSTEMS Tab for Show a Playback Channel and SystemBoxes
---
WhackerLinkConsoleV2/ChannelBox.xaml | 6 +-
WhackerLinkConsoleV2/MainWindow.xaml | 2 +-
WhackerLinkConsoleV2/MainWindow.xaml.cs | 324 +++++++++++++++++-------
WhackerLinkConsoleV2/ToneSet.xaml | 16 +-
4 files changed, 247 insertions(+), 101 deletions(-)
diff --git a/WhackerLinkConsoleV2/ChannelBox.xaml b/WhackerLinkConsoleV2/ChannelBox.xaml
index 705ec69..c5c8576 100644
--- a/WhackerLinkConsoleV2/ChannelBox.xaml
+++ b/WhackerLinkConsoleV2/ChannelBox.xaml
@@ -4,7 +4,7 @@
Width="220" Height="100" Background="#FF0B004B">
-
+
@@ -34,7 +34,7 @@
-
+
@@ -100,7 +100,7 @@
-
+
-
-
-
+
+
From c414ef0aa621c1fce015fb26c24ec92f0cfabd43 Mon Sep 17 00:00:00 2001
From: Devin G <149845346+DeathSoulYTDev@users.noreply.github.com>
Date: Thu, 21 Aug 2025 17:39:23 -0400
Subject: [PATCH 11/17] Comments Removal
- Removed Forgotten Commented out Code
---
WhackerLinkConsoleV2/MainWindow.xaml.cs | 57 -------------------------
1 file changed, 57 deletions(-)
diff --git a/WhackerLinkConsoleV2/MainWindow.xaml.cs b/WhackerLinkConsoleV2/MainWindow.xaml.cs
index 6378555..5934994 100644
--- a/WhackerLinkConsoleV2/MainWindow.xaml.cs
+++ b/WhackerLinkConsoleV2/MainWindow.xaml.cs
@@ -1724,33 +1724,6 @@ private void ChannelBox_MouseLeftButtonDown(object sender, MouseButtonEventArgs
element.CaptureMouse();
}
-
- //private void ChannelBox_MouseMove(object sender, MouseEventArgs e)
- //{
- // if (!isEditMode || !_isDragging || _draggedElement == null) return;
-
- // //Point current = e.GetPosition(ChannelsCanvas);
- // Point currentPosition = e.GetPosition(ChannelsCanvas);
-
- // // snap to grid
- // double newLeft = Math.Round((currentPosition.X - _offsetX) / GridSize) * GridSize;
- // double newTop = Math.Round((currentPosition.Y - _offsetY) / GridSize) * GridSize;
-
- // // keep inside canvas
- // //newLeft = Math.Max(0, Math.Min(newLeft, ChannelsCanvas.ActualWidth - _draggedElement.RenderSize.Width));
- // newLeft = Math.Max(0, Math.Min(newLeft, ChannelsCanvas.ActualWidth - _draggedElement.RenderSize.Width));
- // newTop = Math.Max(0, Math.Min(newTop, ChannelsCanvas.ActualHeight - _draggedElement.RenderSize.Height));
- // //newTop = Math.Max(0, Math.Min(newTop, ChannelsCanvas.ActualHeight - _draggedElement.RenderSize.Height));
-
- // Canvas.SetLeft(_draggedElement, newLeft);
- // Canvas.SetTop(_draggedElement, newTop);
-
- // if (_draggedElement is ChannelBox cb)
- // _settingsManager.UpdateChannelPosition(cb.ChannelName, newLeft, newTop);
-
- // AdjustCanvasHeight();
- //}
-
private void ChannelBox_MouseMove(object sender, MouseEventArgs e)
{
if (!isEditMode || !_isDragging || _draggedElement == null) return;
@@ -1794,36 +1767,6 @@ private void ChannelBox_MouseMove(object sender, MouseEventArgs e)
AdjustCanvasHeight();
}
-
- //private void ChannelBox_MouseMove(object sender, MouseEventArgs e)
- //{
- // if (!isEditMode || !_isDragging || _draggedElement == null) return;
-
- // // current mouse position relative to canvas
- // Point currentPosition = e.GetPosition(ChannelsCanvas);
-
- // // calculate new pos using element-relative offset
- // double newLeft = Math.Round((currentPosition.X - _offsetX) / GridSize) * GridSize;
- // double newTop = Math.Round((currentPosition.Y - _offsetY) / GridSize) * GridSize;
-
- // // snap to grid
- // //newLeft = Math.Round(newLeft / GridSize) * GridSize;
- // //newTop = Math.Round(newTop / GridSize) * GridSize;
-
- // // clamp to canvas bounds
- // newLeft = Math.Max(0, Math.Min(newLeft, ChannelsCanvas.ActualWidth - _draggedElement.RenderSize.Width));
- // newTop = Math.Max(0, Math.Min(newTop, ChannelsCanvas.ActualHeight - _draggedElement.RenderSize.Height));
-
- // Canvas.SetLeft(_draggedElement, newLeft);
- // Canvas.SetTop(_draggedElement, newTop);
-
- // if (_draggedElement is ChannelBox cb)
- // _settingsManager.UpdateChannelPosition(cb.ChannelName, newLeft, newTop);
-
- // AdjustCanvasHeight();
- //}
-
-
private void ChannelBox_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
if (!isEditMode || !_isDragging || _draggedElement == null) return;
From d12efcba32d8d235aa950487f32dc467fae8278a Mon Sep 17 00:00:00 2001
From: Devin G <149845346+DeathSoulYTDev@users.noreply.github.com>
Date: Sun, 21 Sep 2025 16:09:40 -0400
Subject: [PATCH 12/17] Update/Bug Fixes
- Fixed The Global Ptt & Alert Tone 3 Buttons as they where previously swaped Somehow
- Updated Styling Of the Channels Groups/ZoneTabs
- Updated Styling to a Dark Mode
---
WhackerLinkConsoleV2/ChannelBox.xaml | 2 +-
WhackerLinkConsoleV2/ChannelBox.xaml.cs | 6 +-
WhackerLinkConsoleV2/MainWindow.xaml | 291 ++++++++++++++++++------
WhackerLinkConsoleV2/MainWindow.xaml.cs | 61 ++++-
WhackerLinkConsoleV2/Themes.xaml | 29 +++
WhackerLinkConsoleV2/ToneSet.xaml | 8 +-
WhackerLinkConsoleV2/ToneSet.xaml.cs | 4 +-
WhackerLinkLib | 2 +-
8 files changed, 323 insertions(+), 80 deletions(-)
create mode 100644 WhackerLinkConsoleV2/Themes.xaml
diff --git a/WhackerLinkConsoleV2/ChannelBox.xaml b/WhackerLinkConsoleV2/ChannelBox.xaml
index c5c8576..544c98c 100644
--- a/WhackerLinkConsoleV2/ChannelBox.xaml
+++ b/WhackerLinkConsoleV2/ChannelBox.xaml
@@ -4,7 +4,7 @@
Width="220" Height="100" Background="#FF0B004B">
-
+
diff --git a/WhackerLinkConsoleV2/ChannelBox.xaml.cs b/WhackerLinkConsoleV2/ChannelBox.xaml.cs
index cf90922..e3bfebc 100644
--- a/WhackerLinkConsoleV2/ChannelBox.xaml.cs
+++ b/WhackerLinkConsoleV2/ChannelBox.xaml.cs
@@ -232,7 +232,7 @@ private void ChannelBox_MouseLeftButtonDown(object sender, MouseButtonEventArgs
if (IsEditMode) return;
IsSelected = !IsSelected;
- Background = IsSelected ? (Brush)new BrushConverter().ConvertFrom("#FF0B004B") : Brushes.Gray;
+ Background = IsSelected ? (Brush)new BrushConverter().ConvertFrom("#FF0B004B") : (Brush)new BrushConverter().ConvertFrom("#888888");
if (IsSelected)
{
@@ -278,11 +278,11 @@ private void UpdateBackground()
{
if (SystemName == MainWindow.PLAYBACKSYS || ChannelName == MainWindow.PLAYBACKCHNAME || DstId == MainWindow.PLAYBACKTG)
{
- Background = IsSelected ? (Brush)new BrushConverter().ConvertFrom("#FFC90000") : Brushes.DarkGray;
+ Background = IsSelected ? (Brush)new BrushConverter().ConvertFrom("#FFC90000") : (Brush)new BrushConverter().ConvertFrom("#888888");
return;
}
- Background = IsSelected ? (Brush)new BrushConverter().ConvertFrom("#FF0B004B") : Brushes.DarkGray;
+ Background = IsSelected ? (Brush)new BrushConverter().ConvertFrom("#FF0B004B") : (Brush)new BrushConverter().ConvertFrom("#888888");
}
private async void PTTButton_Click(object sender, RoutedEventArgs e)
diff --git a/WhackerLinkConsoleV2/MainWindow.xaml b/WhackerLinkConsoleV2/MainWindow.xaml
index 8cb7429..8c85a7a 100644
--- a/WhackerLinkConsoleV2/MainWindow.xaml
+++ b/WhackerLinkConsoleV2/MainWindow.xaml
@@ -2,11 +2,197 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WhackerLinkConsoleV2" Height="600" Width="1000" Background="#FFF2F2F2">
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
@@ -17,85 +203,54 @@
-
+
-
-
+
+
-
diff --git a/WhackerLinkConsoleV2/MainWindow.xaml.cs b/WhackerLinkConsoleV2/MainWindow.xaml.cs
index 5934994..e0ff649 100644
--- a/WhackerLinkConsoleV2/MainWindow.xaml.cs
+++ b/WhackerLinkConsoleV2/MainWindow.xaml.cs
@@ -50,6 +50,8 @@
using static WhackerLinkConsoleV2.P25Crypto;
//using static WhackerLinkLib.Models.Radio.Codeplug;
using System.Threading.Channels;
+using System.Windows.Controls.Primitives;
+using System.Windows.Media.Animation;
namespace WhackerLinkConsoleV2
{
@@ -108,6 +110,9 @@ public MainWindow()
ConsoleNative.ShowConsole();
#endif
InitializeComponent();
+ ChannelsTabControl.Loaded += (s, e) => UpdateIndicator(false);
+ ChannelsTabControl.SelectionChanged += (s, e) => UpdateIndicator(true);
+ //ChannelsTabControl.SelectionChanged += ChannelsTabControl_SelectionChanged;
_settingsManager.LoadSettings();
@@ -140,6 +145,59 @@ public MainWindow()
Loaded += MainWindow_Loaded;
}
+
+ private void ChannelsTabControl_Loaded(object sender, RoutedEventArgs e)
+ {
+ UpdateIndicator(false);
+ }
+
+ private void ChannelsTabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ UpdateIndicator(true);
+ }
+
+ private void UpdateIndicator(bool animate)
+ {
+ var indicator = ChannelsTabControl.Template.FindName("SelectionIndicator", ChannelsTabControl) as Border;
+ var transform = indicator?.RenderTransform as TranslateTransform;
+ if (indicator == null || transform == null) return;
+
+ if (ChannelsTabControl.SelectedItem is TabItem selectedTab)
+ {
+ // Force layout update to get correct ActualWidth
+ selectedTab.UpdateLayout();
+ ChannelsTabControl.UpdateLayout();
+
+ // Measure tab position relative to TabControl (not TabPanel)
+ var tabPos = selectedTab.TransformToAncestor(ChannelsTabControl).Transform(new Point(0, 0));
+ double targetX = tabPos.X;
+ double targetWidth = selectedTab.ActualWidth;
+
+ if (animate)
+ {
+ // Animate horizontal position
+ var animX = new DoubleAnimation(transform.X, targetX, TimeSpan.FromMilliseconds(250))
+ {
+ EasingFunction = new CubicEase { EasingMode = EasingMode.EaseInOut }
+ };
+ transform.BeginAnimation(TranslateTransform.XProperty, animX);
+
+ // Animate width
+ var animW = new DoubleAnimation(indicator.Width, targetWidth, TimeSpan.FromMilliseconds(250))
+ {
+ EasingFunction = new CubicEase { EasingMode = EasingMode.EaseInOut }
+ };
+ indicator.BeginAnimation(FrameworkElement.WidthProperty, animW);
+ }
+ else
+ {
+ // Snap immediately (for startup)
+ transform.X = targetX;
+ indicator.Width = targetWidth;
+ }
+ }
+ }
+
private class YamlConfig
{
public List Tones { get; set; }
@@ -1767,6 +1825,7 @@ private void ChannelBox_MouseMove(object sender, MouseEventArgs e)
AdjustCanvasHeight();
}
+
private void ChannelBox_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
if (!isEditMode || !_isDragging || _draggedElement == null) return;
@@ -2150,7 +2209,7 @@ private async void btnGlobalPtt_Click(object sender, RoutedEventArgs e)
{
Dispatcher.Invoke(() =>
{
- btnGlobalPtt.Background = channel.grayGradient;
+ btnGlobalPtt.Background = (Brush)new BrushConverter().ConvertFrom("#888888");
});
GRP_VCH_RLS release = new GRP_VCH_RLS
diff --git a/WhackerLinkConsoleV2/Themes.xaml b/WhackerLinkConsoleV2/Themes.xaml
new file mode 100644
index 0000000..745f8a2
--- /dev/null
+++ b/WhackerLinkConsoleV2/Themes.xaml
@@ -0,0 +1,29 @@
+
+
+
+
+ #3A86FF
+ #FFFFFF
+ #F2F2F2
+ #E6E6E6
+ #FFFFFF
+ #222222
+ #999999
+
+
+
+
+ #3A86FF
+ #1E1E1E
+ #2C2C2C
+ #333333
+ #2C2C2C
+ #FFFFFF
+ #777777
+
+
+
+
+
diff --git a/WhackerLinkConsoleV2/ToneSet.xaml b/WhackerLinkConsoleV2/ToneSet.xaml
index 584e105..8fea962 100644
--- a/WhackerLinkConsoleV2/ToneSet.xaml
+++ b/WhackerLinkConsoleV2/ToneSet.xaml
@@ -2,11 +2,11 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="220" Height="100" Background="#FF0B004B">
-
+
-
+
-
+