Put the wiped entry boxes back on Alt+W
N1MM wipes on Ctrl+W and puts the boxes back on Alt+W, which this had no key for. A wipe now keeps the call, the exchange, the name, the comment and the sent time, and Alt+W fills them in again. Wiping empty boxes keeps what the wipe before it held, and the boxes are only filled in again when they are empty, so nothing typed since the wipe is thrown away. A call moved onto the call stack is not kept: it has not gone anywhere and putting it back would make a second copy of it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015NZkFxHde1iifAQemFTHxf
This commit is contained in:
@@ -71,6 +71,7 @@
|
|||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem Header="_Edit">
|
<MenuItem Header="_Edit">
|
||||||
<MenuItem Header="Wipe Out Entry Fields" Click="OnWipe" InputGesture="Ctrl+W" />
|
<MenuItem Header="Wipe Out Entry Fields" Click="OnWipe" InputGesture="Ctrl+W" />
|
||||||
|
<MenuItem Header="Restore Wiped Entry Fields" Click="OnRestoreWiped" InputGesture="Alt+W" />
|
||||||
<Separator />
|
<Separator />
|
||||||
<MenuItem Header="Edit Last Contact" Click="OnEditLastContact" InputGesture="Ctrl+Y" />
|
<MenuItem Header="Edit Last Contact" Click="OnEditLastContact" InputGesture="Ctrl+Y" />
|
||||||
<MenuItem Header="Add a Note to Last/Current Contact" Click="OnAddNote" InputGesture="Ctrl+N" />
|
<MenuItem Header="Add a Note to Last/Current Contact" Click="OnAddNote" InputGesture="Ctrl+N" />
|
||||||
|
|||||||
@@ -388,6 +388,10 @@ public sealed partial class EntryWindow : Window
|
|||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
OnWipe(this, new RoutedEventArgs());
|
OnWipe(this, new RoutedEventArgs());
|
||||||
break;
|
break;
|
||||||
|
case Key.W when e.KeyModifiers.HasFlag(KeyModifiers.Alt):
|
||||||
|
e.Handled = true;
|
||||||
|
OnRestoreWiped(this, new RoutedEventArgs());
|
||||||
|
break;
|
||||||
case Key.L when e.KeyModifiers.HasFlag(KeyModifiers.Control):
|
case Key.L when e.KeyModifiers.HasFlag(KeyModifiers.Control):
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
OnShowLog(this, new RoutedEventArgs());
|
OnShowLog(this, new RoutedEventArgs());
|
||||||
@@ -682,6 +686,26 @@ public sealed partial class EntryWindow : Window
|
|||||||
Refresh();
|
Refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// N1MM's Alt+W: the boxes the last wipe cleared are filled in again. With
|
||||||
|
/// something typed in them there is nowhere to put it back, so the status
|
||||||
|
/// line says so rather than throwing the typing away.
|
||||||
|
private void OnRestoreWiped(object? sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (Logging is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!Logging.RestoreWiped())
|
||||||
|
{
|
||||||
|
Status(Logging.Entry.IsEmpty ? "no wiped contact to put back" : "wipe the boxes first");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ResetEsm();
|
||||||
|
SyncBoxes();
|
||||||
|
boxes[0].Focus();
|
||||||
|
Refresh();
|
||||||
|
}
|
||||||
|
|
||||||
private void OnLogIt(object? sender, RoutedEventArgs e) => LogContact();
|
private void OnLogIt(object? sender, RoutedEventArgs e) => LogContact();
|
||||||
|
|
||||||
/// Opens the callsign's page in a browser, as N1MM's QRZ button does.
|
/// Opens the callsign's page in a browser, as N1MM's QRZ button does.
|
||||||
|
|||||||
@@ -89,6 +89,12 @@ public sealed class EntryFields
|
|||||||
private static bool IsFilledIn(ExchangeField field) =>
|
private static bool IsFilledIn(ExchangeField field) =>
|
||||||
field.IsSent || field.Kind == ExchangeFieldKind.Report;
|
field.IsSent || field.Kind == ExchangeFieldKind.Report;
|
||||||
|
|
||||||
|
/// True when the operator has typed nothing. The boxes holding what this
|
||||||
|
/// station sends are filled in by the program, so they do not count.
|
||||||
|
public bool IsEmpty =>
|
||||||
|
Call.Trim().Length == 0 &&
|
||||||
|
Exchange.Where((f, at) => !f.IsSent && values[at].Trim().Length > 0).Any() == false;
|
||||||
|
|
||||||
/// True when everything the contest asks for has been typed.
|
/// True when everything the contest asks for has been typed.
|
||||||
public bool IsComplete =>
|
public bool IsComplete =>
|
||||||
Call.Trim().Length > 0 &&
|
Call.Trim().Length > 0 &&
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ public sealed class OperatingPosition
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Stack.Add(Entry.Call.Trim(), worth.IsNewMultiplier);
|
Stack.Add(Entry.Call.Trim(), worth.IsNewMultiplier);
|
||||||
Wipe();
|
ClearEntry();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,7 +162,7 @@ public sealed class OperatingPosition
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
string call = Stack.Next();
|
string call = Stack.Next();
|
||||||
Wipe();
|
ClearEntry();
|
||||||
Entry.Call = call;
|
Entry.Call = call;
|
||||||
Entry.FocusOn(0);
|
Entry.FocusOn(0);
|
||||||
Session.NotifyChanged();
|
Session.NotifyChanged();
|
||||||
@@ -194,7 +194,7 @@ public sealed class OperatingPosition
|
|||||||
bool isMultiplier = worth.IsNewMultiplier;
|
bool isMultiplier = worth.IsNewMultiplier;
|
||||||
string next = Stack.Next();
|
string next = Stack.Next();
|
||||||
Stack.Add(call, isMultiplier);
|
Stack.Add(call, isMultiplier);
|
||||||
Wipe();
|
ClearEntry();
|
||||||
Entry.Call = next;
|
Entry.Call = next;
|
||||||
Entry.FocusOn(0);
|
Entry.FocusOn(0);
|
||||||
Session.NotifyChanged();
|
Session.NotifyChanged();
|
||||||
@@ -341,7 +341,8 @@ public sealed class OperatingPosition
|
|||||||
/// log, Ctrl+A forward, Enter writes the changes back and Esc leaves.
|
/// log, Ctrl+A forward, Enter writes the changes back and Esc leaves.
|
||||||
public Qso? Editing { get; private set; }
|
public Qso? Editing { get; private set; }
|
||||||
|
|
||||||
private (string Call, string[] Values, string Name, string Comment)? held;
|
private TypedEntry? held;
|
||||||
|
private TypedEntry? wiped;
|
||||||
|
|
||||||
/// Steps one contact back or forward and loads it into the boxes. False
|
/// Steps one contact back or forward and loads it into the boxes. False
|
||||||
/// when the log has nothing that way; stepping forward past the newest
|
/// when the log has nothing that way; stepping forward past the newest
|
||||||
@@ -364,7 +365,7 @@ public sealed class OperatingPosition
|
|||||||
LeaveQuickEdit();
|
LeaveQuickEdit();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
held ??= (Entry.Call, [.. Enumerable.Range(1, Entry.FieldCount - 1).Select(f => Entry[f])], OtherName, Comment);
|
held ??= Typed();
|
||||||
Editing = qsos[wanted];
|
Editing = qsos[wanted];
|
||||||
LoadFrom(Editing);
|
LoadFrom(Editing);
|
||||||
Session.NotifyChanged();
|
Session.NotifyChanged();
|
||||||
@@ -381,13 +382,7 @@ public sealed class OperatingPosition
|
|||||||
Editing = null;
|
Editing = null;
|
||||||
if (held is { } typed)
|
if (held is { } typed)
|
||||||
{
|
{
|
||||||
Entry.Call = typed.Call;
|
PutBack(typed);
|
||||||
for (int field = 1; field < Entry.FieldCount; field++)
|
|
||||||
{
|
|
||||||
Entry[field] = typed.Values[field - 1];
|
|
||||||
}
|
|
||||||
OtherName = typed.Name;
|
|
||||||
Comment = typed.Comment;
|
|
||||||
held = null;
|
held = null;
|
||||||
}
|
}
|
||||||
Session.NotifyChanged();
|
Session.NotifyChanged();
|
||||||
@@ -441,7 +436,37 @@ public sealed class OperatingPosition
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Clears the boxes, keeping what was in them for `RestoreWiped`. An entry
|
||||||
|
/// with nothing typed in it is not worth keeping, so a second wipe does not
|
||||||
|
/// throw the first one away.
|
||||||
public void Wipe()
|
public void Wipe()
|
||||||
|
{
|
||||||
|
if (!Entry.IsEmpty)
|
||||||
|
{
|
||||||
|
wiped = Typed();
|
||||||
|
}
|
||||||
|
ClearEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// N1MM's Alt+W: puts back what the last wipe cleared. False when no wipe
|
||||||
|
/// has anything to put back, or when the boxes have something in them
|
||||||
|
/// again — N1MM restores into an empty entry only, so nothing typed since
|
||||||
|
/// the wipe is lost.
|
||||||
|
public bool RestoreWiped()
|
||||||
|
{
|
||||||
|
if (wiped is not { } typed || !Entry.IsEmpty)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
PutBack(typed);
|
||||||
|
Entry.FocusOn(0);
|
||||||
|
Session.NotifyChanged();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wipes without keeping anything: the call has gone somewhere else, such
|
||||||
|
/// as onto the call stack, so there is nothing to put back.
|
||||||
|
private void ClearEntry()
|
||||||
{
|
{
|
||||||
Editing = null;
|
Editing = null;
|
||||||
held = null;
|
held = null;
|
||||||
@@ -453,6 +478,26 @@ public sealed class OperatingPosition
|
|||||||
Session.NotifyChanged();
|
Session.NotifyChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private TypedEntry Typed() => new(
|
||||||
|
Entry.Call,
|
||||||
|
[.. Enumerable.Range(1, Entry.FieldCount - 1).Select(f => Entry[f])],
|
||||||
|
OtherName,
|
||||||
|
Comment,
|
||||||
|
SentTime);
|
||||||
|
|
||||||
|
private void PutBack(TypedEntry typed)
|
||||||
|
{
|
||||||
|
Entry.Call = typed.Call;
|
||||||
|
// the contest can have changed since, and with it the boxes there are
|
||||||
|
for (int field = 1; field < Entry.FieldCount && field <= typed.Values.Count; field++)
|
||||||
|
{
|
||||||
|
Entry[field] = typed.Values[field - 1];
|
||||||
|
}
|
||||||
|
OtherName = typed.Name;
|
||||||
|
Comment = typed.Comment;
|
||||||
|
SentTime = typed.SentTime;
|
||||||
|
}
|
||||||
|
|
||||||
/// The name and comment boxes fill the log columns of the same names. A
|
/// The name and comment boxes fill the log columns of the same names. A
|
||||||
/// contest that exchanges a name has already filled that column, and what
|
/// contest that exchanges a name has already filled that column, and what
|
||||||
/// was exchanged wins.
|
/// was exchanged wins.
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
namespace Nonemm.Session;
|
||||||
|
|
||||||
|
/// What is in the entry boxes, kept so it can be put back: by leaving quick
|
||||||
|
/// edit, or by Alt+W after a wipe. `Values` holds the exchange boxes in order,
|
||||||
|
/// so `Values[0]` is box 1.
|
||||||
|
public sealed record TypedEntry(
|
||||||
|
string Call,
|
||||||
|
IReadOnlyList<string> Values,
|
||||||
|
string Name,
|
||||||
|
string Comment,
|
||||||
|
string SentTime);
|
||||||
@@ -648,4 +648,67 @@ public class OperatingPositionTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void EmptyBoxesGiveNoProblem() =>
|
public void EmptyBoxesGiveNoProblem() =>
|
||||||
Assert.Null(Session().EntryProblem());
|
Assert.Null(Session().EntryProblem());
|
||||||
|
|
||||||
|
/// N1MM's Alt+W: a wipe keeps what was in the boxes and Alt+W puts it back.
|
||||||
|
[Fact]
|
||||||
|
public void WhatAWipeClearedComesBack()
|
||||||
|
{
|
||||||
|
OperatingPosition session = Session();
|
||||||
|
Type(session, "JA1XYZ", "25");
|
||||||
|
session.Comment = "loud";
|
||||||
|
|
||||||
|
session.Wipe();
|
||||||
|
Assert.Equal("", session.Entry.Call);
|
||||||
|
|
||||||
|
Assert.True(session.RestoreWiped());
|
||||||
|
Assert.Equal("JA1XYZ", session.Entry.Call);
|
||||||
|
Assert.Equal("25", session.Entry.ValueOf(ExchangeSlot.Zone));
|
||||||
|
Assert.Equal("loud", session.Comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ThereIsNothingToPutBackBeforeAWipe() =>
|
||||||
|
Assert.False(Session().RestoreWiped());
|
||||||
|
|
||||||
|
/// Wiping empty boxes would otherwise throw away what the wipe before it
|
||||||
|
/// kept.
|
||||||
|
[Fact]
|
||||||
|
public void ASecondWipeKeepsTheFirstOne()
|
||||||
|
{
|
||||||
|
OperatingPosition session = Session();
|
||||||
|
Type(session, "JA1XYZ", "25");
|
||||||
|
|
||||||
|
session.Wipe();
|
||||||
|
session.Wipe();
|
||||||
|
|
||||||
|
Assert.True(session.RestoreWiped());
|
||||||
|
Assert.Equal("JA1XYZ", session.Entry.Call);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// N1MM puts the boxes back only when they are empty, so typing is never
|
||||||
|
/// overwritten by a restore.
|
||||||
|
[Fact]
|
||||||
|
public void NothingIsPutBackOverSomethingTyped()
|
||||||
|
{
|
||||||
|
OperatingPosition session = Session();
|
||||||
|
Type(session, "JA1XYZ", "25");
|
||||||
|
session.Wipe();
|
||||||
|
session.Entry.Call = "DL2XYZ";
|
||||||
|
|
||||||
|
Assert.False(session.RestoreWiped());
|
||||||
|
Assert.Equal("DL2XYZ", session.Entry.Call);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A call put on the stack has not gone anywhere, so there is nothing to
|
||||||
|
/// put back and Alt+W would only make a second copy of it.
|
||||||
|
[Fact]
|
||||||
|
public void AStackedCallIsNotWaitingToBePutBack()
|
||||||
|
{
|
||||||
|
OperatingPosition session = Session();
|
||||||
|
session.IsRunning = true;
|
||||||
|
Type(session, "JA1XYZ", "25");
|
||||||
|
|
||||||
|
Assert.True(session.StackAnother());
|
||||||
|
Assert.False(session.RestoreWiped());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user