Add tab support (#12)

pull/15/head
Lorenzo L. Romero 1 month ago committed by GitHub
parent 1b90f403f2
commit 8eda11c88c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -29,12 +29,14 @@ systems:
#aliasPath: "Full/Path/To/alias.yml"
#
# Zones
# Zones (each zone becomes a tab)
#
zones:
# Textual name of the zone
- name: "Zone 1"
# List of channels
# Textual name of the zone (this becomes the tab name)
- name: "Primary"
# Background color of the tab in the tab bar, in hex
tabColor: "#FF0000"
# List of channels in this zone/tab
channels:
# Textual name of channel
- name: "Channel 1"
@ -57,7 +59,8 @@ zones:
system: "System 1"
tgid: "15003"
- name: "Zone 2"
- name: "Secondary"
tabColor: "#00FF00"
channels:
- name: "Channel A"
system: "System 1"
@ -65,6 +68,9 @@ zones:
- name: "Channel B"
system: "System 1"
tgid: "16002"
- name: "Emergency"
channels:
- name: "Channel C"
system: "System 1"
tgid: "16002"

@ -10,6 +10,7 @@
* Copyright (C) 2024-2025 Caleb, K4PHP
* Copyright (C) 2025 Bryan Biedenkapp, N2PLL
* Copyright (C) 2025 Steven Jennison, KD8RHO
* Copyright (C) 2025 Lorenzo L Romero, K2LLR
*
*/
@ -45,7 +46,7 @@ namespace dvmconsole
/// </summary>
public List<System> Systems { get; set; }
/// <summary>
/// List of zones.
/// List of zones (each zone becomes a tab).
/// </summary>
public List<Zone> Zones { get; set; }
@ -140,6 +141,10 @@ namespace dvmconsole
/// </summary>
public string Name { get; set; }
/// <summary>
/// Tab Color in Hex (#RRGGBB)
/// </summary>
public string TabColor { get; set; }
/// <summary>
/// List of channels in the zone.
/// </summary>
public List<Channel> Channels { get; set; }

@ -751,9 +751,14 @@ namespace dvmconsole.Controls
await Task.Delay(500);
if (pttToggleMode)
PttButton_Click(sender, e);
{
// Toggle mode: toggle PttState and invoke clicked event
PttState = !PttState;
PTTButtonClicked?.Invoke(sender, this);
}
else
{
// Normal mode: set PttState to true and invoke pressed event
PTTButtonPressed?.Invoke(sender, this);
PttState = true;
}

@ -8,6 +8,7 @@
* @license AGPLv3 License (https://opensource.org/licenses/AGPL-3.0)
*
* Copyright (C) 2025 Caleb, K4PHP
* Copyright (C) 2025 Lorenzo L Romero, K2LLR
*
*/
@ -91,51 +92,56 @@ namespace dvmconsole
{
KeyStatusItems.Clear();
foreach (var child in mainWindow.channelsCanvas.Children)
// Iterate through all canvases (all tabs plus the original) so we see
// keys for channels on every tab, not just the original canvas.
foreach (var canvas in mainWindow.GetAllCanvases())
{
if (child == null)
foreach (var child in canvas.Children)
{
Log.WriteLine("A child in ChannelsCanvas.Children is null.");
continue;
if (child == null)
{
Log.WriteLine("A child in canvas.Children is null.");
continue;
}
if (!(child is ChannelBox channelBox))
{
continue;
}
Codeplug.System system = Codeplug.GetSystemForChannel(channelBox.ChannelName);
if (system == null)
{
Log.WriteLine($"System not found for {channelBox.ChannelName}");
continue;
}
Codeplug.Channel cpgChannel = Codeplug.GetChannelByName(channelBox.ChannelName);
if (cpgChannel == null)
{
Log.WriteLine($"Channel not found for {channelBox.ChannelName}");
continue;
}
if (cpgChannel.GetKeyId() == 0 || cpgChannel.GetAlgoId() == 0)
continue;
if (channelBox.Crypter == null)
{
Log.WriteLine($"Crypter is null for channel {channelBox.ChannelName}");
continue;
}
bool hasKey = channelBox.Crypter.HasKey();
KeyStatusItems.Add(new KeyStatusItem
{
ChannelName = channelBox.ChannelName,
AlgId = $"0x{cpgChannel.GetAlgoId():X2}",
KeyId = $"0x{cpgChannel.GetKeyId():X4}",
KeyStatus = hasKey ? "Key Available" : "No Key"
});
}
if (!(child is ChannelBox channelBox))
{
continue;
}
Codeplug.System system = Codeplug.GetSystemForChannel(channelBox.ChannelName);
if (system == null)
{
Log.WriteLine($"System not found for {channelBox.ChannelName}");
continue;
}
Codeplug.Channel cpgChannel = Codeplug.GetChannelByName(channelBox.ChannelName);
if (cpgChannel == null)
{
Log.WriteLine($"Channel not found for {channelBox.ChannelName}");
continue;
}
if (cpgChannel.GetKeyId() == 0 || cpgChannel.GetAlgoId() == 0)
continue;
if (channelBox.Crypter == null)
{
Log.WriteLine($"Crypter is null for channel {channelBox.ChannelName}");
continue;
}
bool hasKey = channelBox.Crypter.HasKey();
KeyStatusItems.Add(new KeyStatusItem
{
ChannelName = channelBox.ChannelName,
AlgId = $"0x{cpgChannel.GetAlgoId():X2}",
KeyId = $"0x{cpgChannel.GetKeyId():X4}",
KeyStatus = hasKey ? "Key Available" : "No Key"
});
}
});
}

@ -9,6 +9,7 @@
*
* Copyright (C) 2024-2025 Caleb, K4PHP
* Copyright (C) 2025 Bryan Biedenkapp, N2PLL
* Copyright (C) 2025 Lorenzo L Romero, K2LLR
*
*/
@ -306,6 +307,9 @@ namespace dvmconsole
channel.PeerId = e.PeerId;
channel.RxStreamId = e.StreamId;
// Update tab audio indicator
Dispatcher.Invoke(() => UpdateTabAudioIndicatorForChannel(channel));
systemStatuses[cpgChannel.Name + e.Slot].RxStart = pktTime;
Log.WriteLine($"({system.Name}) DMRD: Traffic *CALL START * PEER {e.PeerId} SYS {system.Name} SRC_ID {e.SrcId} TGID {e.DstId} TS {e.Slot} [STREAM ID {e.StreamId}]");
@ -355,6 +359,9 @@ namespace dvmconsole
channel.PeerId = 0;
channel.RxStreamId = 0;
// Update tab audio indicator
Dispatcher.Invoke(() => UpdateTabAudioIndicatorForChannel(channel));
TimeSpan callDuration = pktTime - systemStatuses[cpgChannel.Name + e.Slot].RxStart;
Log.WriteLine($"({system.Name}) DMRD: Traffic *CALL END * PEER {e.PeerId} SYS {system.Name} SRC_ID {e.SrcId} TGID {e.DstId} TS {e.Slot} DUR {callDuration} [STREAM ID {e.StreamId}]");
channel.Background = ChannelBox.BLUE_GRADIENT;

@ -9,6 +9,7 @@
*
* Copyright (C) 2024-2025 Caleb, K4PHP
* Copyright (C) 2025 Bryan Biedenkapp, N2PLL
* Copyright (C) 2025 Lorenzo L Romero, K2LLR
*
*/
@ -522,6 +523,9 @@ namespace dvmconsole
channel.PeerId = e.PeerId;
channel.RxStreamId = e.StreamId;
// Update tab audio indicator
Dispatcher.Invoke(() => UpdateTabAudioIndicatorForChannel(channel));
slot.RxStart = pktTime;
Log.WriteLine($"({system.Name}) P25D: Traffic *CALL START * PEER {e.PeerId} SYS {system.Name} SRC_ID {e.SrcId} TGID {e.DstId} ALGID {channel.algId} KID {channel.kId} [STREAM ID {e.StreamId}]");
@ -554,6 +558,9 @@ namespace dvmconsole
channel.PeerId = 0;
channel.RxStreamId = 0;
// Update tab audio indicator
Dispatcher.Invoke(() => UpdateTabAudioIndicatorForChannel(channel));
TimeSpan callDuration = pktTime - slot.RxStart;
Log.WriteLine($"({system.Name}) P25D: Traffic *CALL END * PEER {e.PeerId} SYS {system.Name} SRC_ID {e.SrcId} TGID {e.DstId} DUR {callDuration} [STREAM ID {e.StreamId}]");
channel.Background = ChannelBox.BLUE_GRADIENT;

@ -15,10 +15,11 @@
<Grid.RowDefinitions>
<RowDefinition Height="24"/>
<RowDefinition Height="52" />
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Menu materialDesign:MenuAssist.TopLevelMenuItemHeight="24" Grid.ColumnSpan="2">
<Menu materialDesign:MenuAssist.TopLevelMenuItemHeight="24" Grid.ColumnSpan="2" Background="{DynamicResource MaterialDesignPaper}">
<MenuItem Header="_File">
<MenuItem Header="_Open Codeplug..." Click="OpenCodeplug_Click" />
<Separator />
@ -63,8 +64,8 @@
</MenuItem>
</Menu>
<ToolBarTray Grid.Row="1">
<ToolBar ClipToBounds="False" Style="{StaticResource MaterialDesignToolBar}">
<ToolBarTray Grid.Row="1" Background="{DynamicResource MaterialDesignPaper}">
<ToolBar ClipToBounds="False" Style="{StaticResource MaterialDesignToolBar}" Background="{DynamicResource MaterialDesignPaper}">
<!-- Application Banner -->
<Image Width="224" Height="50" VerticalAlignment="Center" HorizontalAlignment="Left" Source="/dvmconsole;component/Assets/logo.png" Stretch="UniformToFill">
<Image.RenderTransform>
@ -167,8 +168,13 @@
</ToolBar>
</ToolBarTray>
<!-- Channel Widget Canvas -->
<ScrollViewer x:Name="canvasScrollViewer" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" Grid.Row="3" Grid.ColumnSpan="2">
<!-- Tab Control for Resource Views -->
<TabControl x:Name="resourceTabs" Grid.Row="2" Grid.ColumnSpan="2" Grid.RowSpan="2" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
materialDesign:ColorZoneAssist.Mode="PrimaryDark" Style="{StaticResource MaterialDesignFilledTabControl}">
</TabControl>
<!-- Channel Widget Canvas (will be moved to first tab) -->
<ScrollViewer x:Name="canvasScrollViewer" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" Visibility="Collapsed">
<Canvas x:Name="channelsCanvas" VerticalAlignment="Top">
<Canvas.Background>
<ImageBrush x:Name="channelsCanvasBg" ImageSource="/dvmconsole;component/Assets/bg_main_hd_light.png" Stretch="UniformToFill" />

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save

Powered by TurnKey Linux.