diff --git a/README.md b/README.md
index e3d5596..4107ef6 100644
--- a/README.md
+++ b/README.md
@@ -173,6 +173,16 @@ in again while you are running, and `EsmSendsCorrectedCall` sends the call again
in front of the last message when you have corrected it — copy `SM3AB`, fix it
to `SM3ABC`, and the key sends `SM3ABC TU DL1ABC`.
+### The buttons and the title bar
+
+Under the function keys is the row N1MM has: Esc: Stop, Wipe, Log It, Edit,
+Spot It and QRZ, each doing what its key does. QRZ opens the callsign's page in
+a browser. N1MM's Mark and Store are not there, because call stacking is not.
+
+The title bar says what N1MM's says: the frequency, the mode, whether the
+frequency came from a radio or was typed, and which radio the window belongs to
+when there are two.
+
### The band buttons
Down the left of the entry window are the bands, CW in one column and phone in
diff --git a/src/Nonemm.App/Windows/EntryWindow.axaml b/src/Nonemm.App/Windows/EntryWindow.axaml
index 83e982f..81ff6df 100644
--- a/src/Nonemm.App/Windows/EntryWindow.axaml
+++ b/src/Nonemm.App/Windows/EntryWindow.axaml
@@ -90,7 +90,17 @@
-
+
+
+
+
+
+
+
+
+
+
+ Reopen();
if (radioNumber > 1)
{
- Title = "Nonemm — radio 2";
MainMenu.IsVisible = false;
}
AddHandler(KeyDownEvent, OnWindowKeyDown, RoutingStrategies.Tunnel);
@@ -97,7 +97,6 @@ public sealed partial class EntryWindow : Window
bool wanted = session.Positions.Count > 1;
if (wanted && secondRadio is null)
{
- Title = "Nonemm — radio 1";
secondRadio = new EntryWindow(session, 2);
secondRadio.Closed += (_, _) => secondRadio = null;
secondRadio.Show(this);
@@ -370,6 +369,51 @@ public sealed partial class EntryWindow : Window
private bool FocusedIsEntryBox() => boxes.Any(b => b.IsFocused);
+ /// The buttons N1MM puts under its function keys. Each one does what its
+ /// key does, so there is one place deciding what happens.
+ private void OnStopSending(object? sender, RoutedEventArgs e)
+ {
+ session.Alternating?.Stop();
+ _ = session.Keyer?.AbortAsync();
+ Status("stopped sending");
+ }
+
+ private void OnWipe(object? sender, RoutedEventArgs e)
+ {
+ if (Logging is null)
+ {
+ return;
+ }
+ Logging.Wipe();
+ ResetEsm();
+ SyncBoxes();
+ boxes[0].Focus();
+ Refresh();
+ }
+
+ private void OnLogIt(object? sender, RoutedEventArgs e) => LogContact();
+
+ /// Opens the callsign's page in a browser, as N1MM's QRZ button does.
+ private void OnLookUpCall(object? sender, RoutedEventArgs e)
+ {
+ if (Logging is null || Logging.Entry.Call.Trim() is not { Length: > 0 } call)
+ {
+ Status("no callsign to look up");
+ return;
+ }
+ try
+ {
+ Process.Start(new ProcessStartInfo($"https://www.qrz.com/db/{call}")
+ {
+ UseShellExecute = true,
+ });
+ }
+ catch (Exception error) when (error is System.ComponentModel.Win32Exception or IOException)
+ {
+ Status($"could not open a browser: {error.Message}");
+ }
+ }
+
private void ToggleRun()
{
if (Logging is null)
@@ -411,6 +455,7 @@ public sealed partial class EntryWindow : Window
RunBorder.Background = Logging.IsRunning ? Verdicts.Worth : new SolidColorBrush(Color.FromArgb(0x22, 0x80, 0x80, 0x80));
ContestText.Text = ContestLine();
+ Title = TitleLine();
PathText.Text = PathLine();
Verdict? verdict = Logging.Verdict();
@@ -438,6 +483,22 @@ public sealed partial class EntryWindow : Window
$"{Logging.Log.Tally.Points} pts · {mults} · {Logging.Log.TotalScore:N0}{radio}";
}
+ /// What N1MM writes in its title bar: the frequency, the mode, whether the
+ /// frequency came from a radio or was typed, and which radio this window
+ /// is.
+ private string TitleLine()
+ {
+ if (Logging is null)
+ {
+ return "Nonemm";
+ }
+ string source = session.Radios.Any(r => r.Number == radioNumber && r.IsConnected)
+ ? "rigctld"
+ : "manual";
+ string radio = session.Positions.Count > 1 ? $" — radio {radioNumber}" : "";
+ return $"{Logging.Frequency.Kilohertz:0.00} {Logging.Mode.Name} {source}{radio}";
+ }
+
/// Where the station being worked is: the beam heading, the heading the
/// long way round, the distance, and the sun there. N1MM writes the same
/// line under its entry window.