From f2bea8969ce8741daea795075e4b31c5993f1fb9 Mon Sep 17 00:00:00 2001 From: Devin G <149845346+DeathSoulYTDev@users.noreply.github.com> Date: Mon, 26 May 2025 15:15:38 -0700 Subject: [PATCH 01/17] Add files via upload From 9b56619461313bd2c572cc546af627a0baa722d1 Mon Sep 17 00:00:00 2001 From: Devin G <149845346+DeathSoulYTDev@users.noreply.github.com> Date: Mon, 26 May 2025 15:16:15 -0700 Subject: [PATCH 02/17] Add files via upload --- WhackerLinkConsoleV2/ChannelBox.xaml.cs | 5 + WhackerLinkConsoleV2/MainWindow.xaml | 7 +- WhackerLinkConsoleV2/MainWindow.xaml.cs | 347 +++++++++++++++++- WhackerLinkConsoleV2/SettingsManager.cs | 10 + WhackerLinkConsoleV2/ToneSet.xaml | 30 ++ WhackerLinkConsoleV2/ToneSet.xaml.cs | 72 ++++ .../WhackerLinkConsoleV2.csproj | 4 + .../WhackerLinkConsoleV2.csproj.user | 4 + .../WidgetSelectionWindow.xaml | 1 + .../WidgetSelectionWindow.xaml.cs | 3 + 10 files changed, 477 insertions(+), 6 deletions(-) create mode 100644 WhackerLinkConsoleV2/ToneSet.xaml create mode 100644 WhackerLinkConsoleV2/ToneSet.xaml.cs create mode 100644 WhackerLinkConsoleV2/WhackerLinkConsoleV2.csproj.user diff --git a/WhackerLinkConsoleV2/ChannelBox.xaml.cs b/WhackerLinkConsoleV2/ChannelBox.xaml.cs index 18c9d2d..f6750c9 100644 --- a/WhackerLinkConsoleV2/ChannelBox.xaml.cs +++ b/WhackerLinkConsoleV2/ChannelBox.xaml.cs @@ -336,5 +336,10 @@ private void PttButton_MouseLeave(object sender, System.Windows.Input.MouseEvent ((Button)sender).Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FFDDDDDD")); } + + private void SetPageState(bool state) + { + PageState = state; + } } } diff --git a/WhackerLinkConsoleV2/MainWindow.xaml b/WhackerLinkConsoleV2/MainWindow.xaml index 3cc66f8..58b36fb 100644 --- a/WhackerLinkConsoleV2/MainWindow.xaml +++ b/WhackerLinkConsoleV2/MainWindow.xaml @@ -41,8 +41,11 @@ - - + + + + + diff --git a/WhackerLinkConsoleV2/MainWindow.xaml.cs b/WhackerLinkConsoleV2/MainWindow.xaml.cs index d9098ad..c8c75c0 100644 --- a/WhackerLinkConsoleV2/MainWindow.xaml.cs +++ b/WhackerLinkConsoleV2/MainWindow.xaml.cs @@ -49,6 +49,7 @@ using NWaves.Signals; using static WhackerLinkConsoleV2.P25Crypto; using static WhackerLinkLib.Models.Radio.Codeplug; +using System.Threading.Channels; namespace WhackerLinkConsoleV2 { @@ -67,6 +68,9 @@ public partial class MainWindow : Window private SettingsManager _settingsManager = new SettingsManager(); private SelectedChannelsManager _selectedChannelsManager; + + private List<(double ToneA, double ToneB)> _selectedToneSets = new List<(double, double)>(); + private FlashingBackgroundManager _flashingManager; private WaveFilePlaybackManager _emergencyAlertPlayback; private WebSocketManager _webSocketManager = new WebSocketManager(); @@ -124,6 +128,11 @@ public MainWindow() Loaded += MainWindow_Loaded; } + private class YamlConfig + { + public List Tones { get; set; } + } + private void OpenCodeplug_Click(object sender, RoutedEventArgs e) { OpenFileDialog openFileDialog = new OpenFileDialog @@ -303,8 +312,7 @@ private void GenerateChannelWidgets() systemStatusBox.ConnectionState = "Disconnected"; } }); - } else - { + } else { _fneSystemManager.AddFneSystem(system.Name, system, this); PeerSystem peer = _fneSystemManager.GetFneSystem(system.Name); @@ -344,6 +352,7 @@ private void GenerateChannelWidgets() } } + if (_settingsManager.ShowChannels && Codeplug != null) { foreach (var zone in Codeplug.Zones) @@ -415,6 +424,137 @@ private void GenerateChannelWidgets() } } + // Add ToneSet boxes if enabled + if (_settingsManager.ShowQCTones && Codeplug != null && Codeplug?.Tones != null) + { + foreach (var tone in Codeplug.Tones) + { + var toneSetControl = new ToneSet(tone.Name, tone.ToneA, tone.ToneB); + + // 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(); + + for (int i = 0; i < selectedTones.Count; i++) + { + 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()) + { + // Pause logic: restore PageState for affected channels + foreach (var ch in prematurelyCleared) + { + ch.PageState = true; + Dispatcher.Invoke(() => + { + ch.PageSelectButton.Background = ch.orangeGradient; + }); + } + + // Wait a moment to visually reflect the reset before continuing + await Task.Delay(300); + } + + // Play the tone + await PlayTone(selected.ToneA.ToString(), selected.ToneB.ToString()); + + // Remove from selected set + _selectedToneSets.Remove(key); + + // Deselect the 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; + } + } + } + + // Now that all tones are done, clean up page state + foreach (var ch in initiallyActiveChannels) + { + ch.PageState = false; + ch.PageSelectButton.Background = ch.grayGradient; + } + } + else + { + 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; + 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); + } + }; + + // Position like ToneSet Boxes using same layout logic + if (_settingsManager.QCToneSetPositions.TryGetValue(tone.Name, out var position)) + { + Canvas.SetLeft(toneSetControl, position.X); + Canvas.SetTop(toneSetControl, position.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); + + offsetX += 225; + + if (offsetX + 220 > ChannelsCanvas.ActualWidth) + { + offsetX = 20; + offsetY += 106; + } + } + } + + + playbackChannelBox = new ChannelBox(_selectedChannelsManager, _audioManager, PLAYBACKCHNAME, PLAYBACKSYS, PLAYBACKTG); if (_settingsManager.ChannelPositions.TryGetValue(PLAYBACKCHNAME, out var pos)) @@ -448,6 +588,95 @@ private void GenerateChannelWidgets() AdjustCanvasHeight(); } + private const int GridSize = 5; + + private void ToneSet_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) + { + if (!isEditMode || !(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; + + element.CaptureMouse(); + } + + private void ToneSet_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 ToneSet + if (_draggedElement is ToneSet toneSet) + { + _settingsManager.UpdateQCToneSetPosition(toneSet.ToneName, newLeft, newTop); + } + + AdjustCanvasHeight(); + } + + private void ToneSet_MouseRightButtonDown(object sender, MouseButtonEventArgs e) + { + if (!isEditMode || !_isDragging || _draggedElement == null) return; + + _isDragging = false; + _draggedElement.ReleaseMouseCapture(); + _draggedElement = null; + } + + + private async void ToneSet_PlayClicked(object sender, EventArgs e) + { + if (sender is ToneSet toneSet) + { + if (_selectedToneSets.Count > 0) + { + foreach (var selected in _selectedToneSets) + { + await PlayTone(selected.ToneA.ToString(), selected.ToneB.ToString()); + } + } + else + { + await PlayTone(toneSet.ToneA.ToString(), toneSet.ToneB.ToString()); + } + } + } + + private void ToneSet_SelectClicked(object sender, EventArgs e) + { + if (sender is ToneSet toneSet) + { + var key = (toneSet.ToneA, toneSet.ToneB); + + if (_selectedToneSets.Contains(key)) + { + _selectedToneSets.Remove(key); + toneSet.SetSelected(false); + } + else + { + _selectedToneSets.Add(key); + toneSet.SetSelected(true); + } + } + } + private void WaveIn_RecordingStopped(object sender, EventArgs e) { /* stub */ @@ -934,6 +1163,7 @@ private void SelectWidgets_Click(object sender, RoutedEventArgs e) _settingsManager.ShowSystemStatus = widgetSelectionWindow.ShowSystemStatus; _settingsManager.ShowChannels = widgetSelectionWindow.ShowChannels; _settingsManager.ShowAlertTones = widgetSelectionWindow.ShowAlertTones; + _settingsManager.ShowQCTones = widgetSelectionWindow.ShowQCTones; GenerateChannelWidgets(); _settingsManager.SaveSettings(); @@ -1301,8 +1531,6 @@ private void ChannelBox_MouseLeftButtonDown(object sender, MouseButtonEventArgs element.CaptureMouse(); } - private const int GridSize = 5; - private void ChannelBox_MouseMove(object sender, MouseEventArgs e) { if (!isEditMode || !_isDragging || _draggedElement == null) return; @@ -1455,6 +1683,7 @@ private void MainWindow_Loaded(object sender, RoutedEventArgs e) if (!string.IsNullOrEmpty(_settingsManager.LastCodeplugPath) && File.Exists(_settingsManager.LastCodeplugPath)) { LoadCodeplug(_settingsManager.LastCodeplugPath); + //LoadToneSets(); } else { @@ -1462,6 +1691,116 @@ private void MainWindow_Loaded(object sender, RoutedEventArgs e) } } + private async Task PlayTone(string ToneA, string ToneB) + { + var selectedChannels = _selectedChannelsManager.GetSelectedChannels(); + + // Check if any selected channel has PageState = true + if (!selectedChannels.Any(ch => ch.PageState)) + { + // No channels with active page state - do nothing + return; + } + + foreach (ChannelBox channel in selectedChannels) + { + if (!channel.PageState) + continue; // skip channels without page state + + Codeplug.System system = Codeplug.GetSystemForChannel(channel.ChannelName); + Codeplug.Channel cpgChannel = Codeplug.GetChannelByName(channel.ChannelName); + + ToneGenerator generator = new ToneGenerator(); + + double toneADuration = 1.0; + double toneBDuration = 3.0; + + byte[] toneA = generator.GenerateTone(double.Parse(ToneA), toneADuration); + byte[] toneB = generator.GenerateTone(double.Parse(ToneB), toneBDuration); + + byte[] combinedAudio = new byte[toneA.Length + toneB.Length]; + Buffer.BlockCopy(toneA, 0, combinedAudio, 0, toneA.Length); + Buffer.BlockCopy(toneB, 0, combinedAudio, toneA.Length, toneB.Length); + + int chunkSize = system.IsDvm ? 320 : 1600; + int totalChunks = (combinedAudio.Length + chunkSize - 1) / chunkSize; + + _audioManager.AddTalkgroupStream(cpgChannel.Tgid, combinedAudio); + + await Task.Run(() => + { + for (int i = 0; i < totalChunks; i++) + { + int offset = i * chunkSize; + int size = Math.Min(chunkSize, combinedAudio.Length - offset); + + byte[] chunk = new byte[chunkSize]; + Buffer.BlockCopy(combinedAudio, offset, chunk, 0, size); + + if (!system.IsDvm) + { + IPeer handler = _webSocketManager.GetWebSocketHandler(system.Name); + + AudioPacket voicePacket = new AudioPacket + { + Data = chunk, + VoiceChannel = new VoiceChannel + { + Frequency = channel.VoiceChannel, + DstId = cpgChannel.Tgid, + SrcId = system.Rid, + Site = system.Site + }, + Site = system.Site, + LopServerVocode = true + }; + + handler.SendMessage(voicePacket.GetData()); + } + else + { + PeerSystem handler = _fneSystemManager.GetFneSystem(system.Name); + + if (chunk.Length == 320) + { + P25EncodeAudioFrame(chunk, handler, channel, cpgChannel, system); + } + } + } + }); + + double totalDurationMs = (toneADuration + toneBDuration) * 1000 + 750; + await Task.Delay((int)totalDurationMs); + + if (!system.IsDvm) + { + IPeer handler = _webSocketManager.GetWebSocketHandler(system.Name); + + GRP_VCH_RLS release = new GRP_VCH_RLS + { + SrcId = system.Rid, + DstId = cpgChannel.Tgid, + Channel = channel.VoiceChannel, + Site = system.Site + }; + + handler.SendMessage(release.GetData()); + } + else + { + PeerSystem handler = _fneSystemManager.GetFneSystem(system.Name); + + await Task.Delay(4000); + handler.SendP25TDU(uint.Parse(system.Rid), uint.Parse(cpgChannel.Tgid), false); + } + + Dispatcher.Invoke(() => + { + channel.PageSelectButton.Background = channel.grayGradient; + }); + } + } + private async void OnHoldTimerElapsed(object sender, ElapsedEventArgs e) { foreach (ChannelBox channel in _selectedChannelsManager.GetSelectedChannels()) diff --git a/WhackerLinkConsoleV2/SettingsManager.cs b/WhackerLinkConsoleV2/SettingsManager.cs index 96690b9..5e3f682 100644 --- a/WhackerLinkConsoleV2/SettingsManager.cs +++ b/WhackerLinkConsoleV2/SettingsManager.cs @@ -30,6 +30,7 @@ public class SettingsManager public bool ShowSystemStatus { get; set; } = true; public bool ShowChannels { get; set; } = true; public bool ShowAlertTones { get; set; } = true; + public bool ShowQCTones { get; set; } = true; public string LastCodeplugPath { get; set; } = null; @@ -37,6 +38,7 @@ public class SettingsManager public Dictionary SystemStatusPositions { get; set; } = new Dictionary(); public List AlertToneFilePaths { get; set; } = new List(); public Dictionary AlertTonePositions { get; set; } = new Dictionary(); + public Dictionary QCToneSetPositions { get; set; } = new Dictionary(); public Dictionary ChannelOutputDevices { get; set; } = new Dictionary(); public void LoadSettings() @@ -53,11 +55,13 @@ public void LoadSettings() ShowSystemStatus = loadedSettings.ShowSystemStatus; ShowChannels = loadedSettings.ShowChannels; ShowAlertTones = loadedSettings.ShowAlertTones; + ShowQCTones = loadedSettings.ShowQCTones; LastCodeplugPath = loadedSettings.LastCodeplugPath; ChannelPositions = loadedSettings.ChannelPositions ?? new Dictionary(); SystemStatusPositions = loadedSettings.SystemStatusPositions ?? new Dictionary(); AlertToneFilePaths = loadedSettings.AlertToneFilePaths ?? new List(); AlertTonePositions = loadedSettings.AlertTonePositions ?? new Dictionary(); + QCToneSetPositions = loadedSettings.QCToneSetPositions ?? new Dictionary(); ChannelOutputDevices = loadedSettings.ChannelOutputDevices ?? new Dictionary(); } } @@ -88,6 +92,12 @@ public void UpdateChannelPosition(string channelName, double x, double y) SaveSettings(); } + public void UpdateQCToneSetPosition(string name, double x, double y) + { + QCToneSetPositions[name] = new ChannelPosition { X = x, Y = y }; + SaveSettings(); + } + public void UpdateSystemStatusPosition(string systemName, double x, double y) { SystemStatusPositions[systemName] = new ChannelPosition { X = x, Y = y }; diff --git a/WhackerLinkConsoleV2/ToneSet.xaml b/WhackerLinkConsoleV2/ToneSet.xaml new file mode 100644 index 0000000..b3cc0bc --- /dev/null +++ b/WhackerLinkConsoleV2/ToneSet.xaml @@ -0,0 +1,30 @@ + + + + + + + + + + + + diff --git a/WhackerLinkConsoleV2/ToneSet.xaml.cs b/WhackerLinkConsoleV2/ToneSet.xaml.cs new file mode 100644 index 0000000..66e2c14 --- /dev/null +++ b/WhackerLinkConsoleV2/ToneSet.xaml.cs @@ -0,0 +1,72 @@ +using System; +using System.ComponentModel; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; + +namespace WhackerLinkConsoleV2.Controls +{ + public partial class ToneSet : UserControl, INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + public string ToneName { get; set; } + public double ToneA { get; set; } + public double ToneB { get; set; } + + public bool IsEditMode { get; set; } + + public event EventHandler PlayClicked; + public event EventHandler SelectToggled; + + private bool _isSelected = false; + + public ToneSet(string toneName, double toneA, double toneB) + { + InitializeComponent(); + ToneName = toneName; + ToneA = toneA; + ToneB = toneB; + + MouseLeftButtonDown += ToneSet_MouseLeftButtonDown; + + DataContext = this; + } + + private void ToneSet_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) + { + if (IsEditMode) return; + } + + + private void ToneSetPlayBtn_Click(object sender, RoutedEventArgs e) + { + PlayClicked?.Invoke(this, EventArgs.Empty); + } + + private void ToneSetSelectBtn_Click(object sender, RoutedEventArgs e) + { + SelectToggled?.Invoke(this, EventArgs.Empty); + } + + public void SetSelected(bool selected) + { + _isSelected = selected; + UpdateSelectButton(); + if (selected) + this.Background = System.Windows.Media.Brushes.LightBlue; + else + this.Background = System.Windows.Media.Brushes.Transparent; + } + + private void UpdateSelectButton() + { + //if (ToneSetSelectBtn != null) + { + //ToneSetSelectBtn.Content = _isSelected ? "Deselect" : "Select"; + //ToneSetSelectBtn.Background = _isSelected ? Brushes.LightGreen : Brushes.LightGray; + } + } + } +} diff --git a/WhackerLinkConsoleV2/WhackerLinkConsoleV2.csproj b/WhackerLinkConsoleV2/WhackerLinkConsoleV2.csproj index 5441dd9..77f5a9e 100644 --- a/WhackerLinkConsoleV2/WhackerLinkConsoleV2.csproj +++ b/WhackerLinkConsoleV2/WhackerLinkConsoleV2.csproj @@ -13,6 +13,8 @@ + + @@ -71,6 +73,8 @@ + + diff --git a/WhackerLinkConsoleV2/WhackerLinkConsoleV2.csproj.user b/WhackerLinkConsoleV2/WhackerLinkConsoleV2.csproj.user new file mode 100644 index 0000000..0f14913 --- /dev/null +++ b/WhackerLinkConsoleV2/WhackerLinkConsoleV2.csproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/WhackerLinkConsoleV2/WidgetSelectionWindow.xaml b/WhackerLinkConsoleV2/WidgetSelectionWindow.xaml index d2cd7e3..183fa9a 100644 --- a/WhackerLinkConsoleV2/WidgetSelectionWindow.xaml +++ b/WhackerLinkConsoleV2/WidgetSelectionWindow.xaml @@ -7,6 +7,7 @@ + - + 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 @@ + + - + @@ -37,33 +42,60 @@ - - + + - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -73,151 +105,63 @@ - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - + 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 @@ - + - - + + - - - + + + - - - - - - - + + + + + + + - - - + + + - + - - - - - - - - - - - - - - - - - - - - + - + @@ -107,14 +262,14 @@ - + - - - - - - - - -