Add CW keying, function key messages and the documentation
The function keys send through cwdaemon or a WinKeyer, with N1MM's message macros. Escape stops sending. Settings are read with the reflection serializer rather than a generated one: the generated one hands back null for every property the file leaves out instead of the value the property is declared with, which crashed the program the first time a new setting was added. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -254,6 +254,27 @@ public sealed partial class EntryWindow
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnKeyerSettings(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
KeyerDialog dialog = new(session.Settings);
|
||||
Settings? updated = await dialog.ShowDialog<Settings?>(this);
|
||||
if (updated is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
session.Save(updated);
|
||||
try
|
||||
{
|
||||
session.ApplyKeyerSettings();
|
||||
Status(updated.KeyerKind == "none" ? "no keyer" : $"keyer: {updated.KeyerKind}");
|
||||
}
|
||||
catch (Exception error) when (error is InvalidOperationException or IOException or UnauthorizedAccessException)
|
||||
{
|
||||
Status($"could not open the keyer: {error.Message}");
|
||||
}
|
||||
BuildFunctionKeys();
|
||||
}
|
||||
|
||||
private async void OnDownloadCountryFile(object? sender, RoutedEventArgs e) =>
|
||||
await Download(
|
||||
downloader => downloader.DownloadCountryFileAsync(session.Paths.CountryFile),
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
<MenuItem Header="_Radio…" Click="OnRadioSettings" />
|
||||
<MenuItem Header="_Cluster…" Click="OnClusterSettings" />
|
||||
<MenuItem Header="_Network…" Click="OnNetworkSettings" />
|
||||
<MenuItem Header="_Keyer and messages…" Click="OnKeyerSettings" />
|
||||
<Separator />
|
||||
<MenuItem Header="Download Country _File" Click="OnDownloadCountryFile" />
|
||||
<MenuItem Header="Download Check _Partial File" Click="OnDownloadCallDatabase" />
|
||||
|
||||
@@ -55,9 +55,47 @@ public sealed partial class EntryWindow : Window
|
||||
{
|
||||
Status($"could not reopen the last log: {e.Message}");
|
||||
}
|
||||
StartConnections();
|
||||
BuildEntryBoxes();
|
||||
}
|
||||
|
||||
/// Brings up whatever the operator had connected last time. Each is
|
||||
/// reported on its own so one that fails does not stop the others.
|
||||
private void StartConnections()
|
||||
{
|
||||
foreach ((string what, Action start) in Connections())
|
||||
{
|
||||
try
|
||||
{
|
||||
start();
|
||||
}
|
||||
catch (Exception e) when (e is InvalidOperationException or IOException or UnauthorizedAccessException)
|
||||
{
|
||||
Status($"could not start the {what}: {e.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<(string What, Action Start)> Connections()
|
||||
{
|
||||
if (session.Settings.RadioEnabled)
|
||||
{
|
||||
yield return ("radio", session.ConnectRadio);
|
||||
}
|
||||
if (session.Settings.ClusterEnabled)
|
||||
{
|
||||
yield return ("cluster", session.ConnectCluster);
|
||||
}
|
||||
if (session.Settings.NetworkEnabled)
|
||||
{
|
||||
yield return ("station network", session.ApplyNetworkSettings);
|
||||
}
|
||||
if (session.Settings.KeyerKind != "none")
|
||||
{
|
||||
yield return ("keyer", session.ApplyKeyerSettings);
|
||||
}
|
||||
}
|
||||
|
||||
private void BuildEntryBoxes()
|
||||
{
|
||||
EntryGrid.Children.Clear();
|
||||
@@ -132,6 +170,7 @@ public sealed partial class EntryWindow : Window
|
||||
break;
|
||||
case Key.Escape:
|
||||
e.Handled = true;
|
||||
_ = session.Keyer?.AbortAsync();
|
||||
Logging.Wipe();
|
||||
SyncBoxes();
|
||||
boxes[0].Focus();
|
||||
@@ -144,6 +183,10 @@ public sealed partial class EntryWindow : Window
|
||||
e.Handled = true;
|
||||
ToggleRun();
|
||||
break;
|
||||
case >= Key.F1 and <= Key.F12:
|
||||
e.Handled = true;
|
||||
RunFunctionKey(e.Key - Key.F1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,11 +325,85 @@ public sealed partial class EntryWindow : Window
|
||||
private void BuildFunctionKeys()
|
||||
{
|
||||
FunctionKeys.Children.Clear();
|
||||
foreach ((string key, string label, _) in Messages.Defaults)
|
||||
for (int at = 0; at < Messages.Keys.Count; at++)
|
||||
{
|
||||
int index = at;
|
||||
(string key, string label) = Messages.Keys[at];
|
||||
Button button = new() { Content = $"{key} {label}" };
|
||||
button.Classes.Add("fkey");
|
||||
button.Click += (_, _) => RunFunctionKey(index);
|
||||
FunctionKeys.Children.Add(button);
|
||||
}
|
||||
}
|
||||
|
||||
/// F11 spots the station being worked, F12 wipes the entry, and the rest
|
||||
/// send their message.
|
||||
private void RunFunctionKey(int index)
|
||||
{
|
||||
if (Logging is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
switch (index)
|
||||
{
|
||||
case 10:
|
||||
SpotCurrentCall();
|
||||
return;
|
||||
case 11:
|
||||
Logging.Wipe();
|
||||
SyncBoxes();
|
||||
boxes[0].Focus();
|
||||
return;
|
||||
default:
|
||||
SendMessage(index);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void SendMessage(int index)
|
||||
{
|
||||
if (Logging is null || session.Keyer is null)
|
||||
{
|
||||
Status("no keyer — Config ▸ Keyer");
|
||||
return;
|
||||
}
|
||||
string template = Messages.For(
|
||||
Logging.Mode.Category,
|
||||
session.Settings.CwMessages,
|
||||
session.Settings.PhoneMessages)[index];
|
||||
if (template.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
string text = MessageExpander.Expand(template, Logging);
|
||||
Status($"sending {text}");
|
||||
_ = SendAsync(text);
|
||||
}
|
||||
|
||||
private async Task SendAsync(string text)
|
||||
{
|
||||
try
|
||||
{
|
||||
await session.Keyer!.SendAsync(text);
|
||||
}
|
||||
catch (InvalidOperationException e)
|
||||
{
|
||||
Status(e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private void SpotCurrentCall()
|
||||
{
|
||||
if (Logging is null || Logging.Entry.Call.Trim().Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
session.Bandmap.Add(new Nonemm.Spotting.Spot(
|
||||
Core.Callsign.Parse(Logging.Entry.Call.Trim()),
|
||||
Logging.Frequency,
|
||||
DateTime.UtcNow,
|
||||
Nonemm.Spotting.SpotSource.Operator,
|
||||
Logging.Me.Callsign));
|
||||
Status($"{Logging.Entry.Call.Trim()} put on the bandmap");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user