Add the Avalonia application and the station network

The entry window types contacts and colours them as they are typed; the log,
check, bandmap, score and packet windows read the same session. Contacts are
shared with the other stations of a multi-operator entry in N1MM's contact
message, so an N1MM station on the same network sees them too.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Erik
2026-08-27 10:56:35 +00:00
co-authored by Claude Opus 5
parent ffeeb2cdc1
commit 2834f63c8e
45 changed files with 2667 additions and 0 deletions
@@ -0,0 +1,77 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
using Nonemm.Contests;
using Nonemm.Core;
using Nonemm.Storage;
namespace Nonemm.App.Dialogs;
/// Picks the contest and the entry categories the sponsor asks for in the
/// Cabrillo header.
public sealed partial class ContestSetupDialog : Window
{
private readonly AppSession session;
public ContestSetupDialog(AppSession session)
{
this.session = session;
InitializeComponent();
ContestBox.ItemsSource = session.Registry.Choices.Select(c => c.DisplayName).ToList();
ContestBox.SelectedIndex = 0;
ContestBox.SelectionChanged += (_, _) => ShowChosen();
ModeBox.ItemsSource = new[] { "CW", "SSB", "RTTY", "MIXED" };
ModeBox.SelectedIndex = 0;
OperatorBox.ItemsSource = new[] { "SINGLE-OP", "MULTI-OP", "CHECKLIST" };
OperatorBox.SelectedIndex = 0;
BandBox.ItemsSource = new[] { "ALL", "160M", "80M", "40M", "20M", "15M", "10M" };
BandBox.SelectedIndex = 0;
PowerBox.ItemsSource = new[] { "HIGH", "LOW", "QRP" };
PowerBox.SelectedIndex = 0;
AssistedBox.ItemsSource = new[] { "NON-ASSISTED", "ASSISTED" };
AssistedBox.SelectedIndex = 0;
TransmitterBox.ItemsSource = new[] { "ONE", "TWO", "LIMITED", "UNLIMITED", "SWL" };
TransmitterBox.SelectedIndex = 0;
OperatorsBox.Text = session.Settings.Station.Callsign;
ShowChosen();
}
private ContestChoice Chosen =>
session.Registry.Choices[Math.Max(0, ContestBox.SelectedIndex)];
private void ShowChosen()
{
Contest contest = Chosen.Create(ModeOf());
ExchangeBox.Text = contest.SentExchangeFor(session.Settings.Station.ToStationInfo());
HintText.Text = $"Cabrillo name {contest.CabrilloName} · exchange " +
string.Join(", ", contest.ExchangeFieldsFor(session.Settings.Station.ToStationInfo())
.Select(f => f.Label));
}
private ModeCategory ModeOf() => (ModeBox.SelectedItem as string) switch
{
"SSB" => ModeCategory.Phone,
"RTTY" => ModeCategory.Digital,
_ => ModeCategory.Cw,
};
private void OnStart(object? sender, RoutedEventArgs e) => Close(new ContestInstance
{
ContestNumber = 0,
ContestName = Chosen.Name,
StartDate = DateTime.UtcNow,
SentExchange = ExchangeBox.Text ?? "",
OperatorCategory = Text(OperatorBox),
BandCategory = Text(BandBox),
PowerCategory = Text(PowerBox),
ModeCategory = Text(ModeBox),
AssistedCategory = Text(AssistedBox),
TransmitterCategory = Text(TransmitterBox),
OverlayCategory = OverlayBox.Text ?? "",
Operators = OperatorsBox.Text ?? "",
});
private void OnCancel(object? sender, RoutedEventArgs e) => Close(null);
private static string Text(ComboBox box) => box.SelectedItem as string ?? "";
}