Walk the entry boxes the way N1MM walks them
Space and tab were moving through every box in turn, and since the name and comment boxes arrived, into those as well. N1MM does neither. Its EntryWindow.NextTab walks the boxes the contest declares plus the callsign, skips SNTText and RCVText, and comes round to the callsign from the last one. For CQ WW that means the callsign and the zone, and nothing else: the entry items are SNTText, RCVText and CQZoneText, and the first two are stepped over. So this steps over report boxes as well, and the name and comment boxes are out of the walk, as they are out of N1MM's. Skipping the reports only works because they are filled in. N1MM's TabPressed puts the report for the mode into an empty Snt or Rcv box when the callsign box is left, and FillReports does the same here: 599 on CW and digital, 59 on phone. A report the operator typed is left alone, and a report box is still there to click when a contact is not 599. The test that said space walks every box in turn now says it steps over the report, which is the behaviour that was asked for. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
18
README.md
18
README.md
@@ -177,10 +177,20 @@ to `SM3ABC`, and the key sends `SM3ABC TU DL1ABC`.
|
|||||||
|
|
||||||
Beside the exchange are a Name box and a Comment box, as N1MM has them. Neither
|
Beside the exchange are a Name box and a Comment box, as N1MM has them. Neither
|
||||||
is part of the exchange, so neither holds up a contact: they fill the log's Name
|
is part of the exchange, so neither holds up a contact: they fill the log's Name
|
||||||
and Comment columns, and a contest that exchanges a name uses that instead. Tab
|
and Comment columns, and a contest that exchanges a name uses that instead.
|
||||||
walks the callsign, the exchange, the name and the comment, and round again. The
|
Neither is in the space and tab walk, as neither is in N1MM's — they are there
|
||||||
name is filled from the call history file when there is one, and what the call
|
for the mouse. The name is filled from the call history file when there is one,
|
||||||
history says in its UserText column appears under the entry boxes.
|
and what the call history says in its UserText column appears under the entry
|
||||||
|
boxes.
|
||||||
|
|
||||||
|
### Space and tab
|
||||||
|
|
||||||
|
Space and tab move between the callsign box and the exchange boxes and round
|
||||||
|
again, which is N1MM's walk. The report boxes are stepped over, because nobody
|
||||||
|
types 599: leaving the callsign box fills them with the report for the mode,
|
||||||
|
599 on CW and digital, 59 on phone. So in CQ WW, space takes you from the
|
||||||
|
callsign straight to the zone, and space again brings you back to the callsign.
|
||||||
|
Click a report box to change one.
|
||||||
|
|
||||||
Two lights sit beside the run switch: the left one is green while a radio is
|
Two lights sit beside the run switch: the left one is green while a radio is
|
||||||
answering, the right one red while something is going out on the air.
|
answering, the right one red while something is going out on the air.
|
||||||
|
|||||||
@@ -311,7 +311,7 @@ public sealed partial class EntryWindow : Window
|
|||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
session.SwapRadio();
|
session.SwapRadio();
|
||||||
break;
|
break;
|
||||||
case Key.Tab when FocusedIsEntryBox() || FocusedIsNoteBox():
|
case Key.Tab when FocusedIsEntryBox():
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
MoveFocus(forward: !e.KeyModifiers.HasFlag(KeyModifiers.Shift));
|
MoveFocus(forward: !e.KeyModifiers.HasFlag(KeyModifiers.Shift));
|
||||||
break;
|
break;
|
||||||
@@ -394,40 +394,24 @@ public sealed partial class EntryWindow : Window
|
|||||||
boxes[0].Focus();
|
boxes[0].Focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tab and space walk the callsign box, the exchange boxes, then the name
|
/// Tab and space walk the callsign box and the exchange boxes, and round
|
||||||
/// and comment boxes, and round again.
|
/// again, as N1MM's do. The name and comment boxes are not in the walk,
|
||||||
|
/// because they are not part of the exchange; they are there for the mouse.
|
||||||
private void MoveFocus(bool forward)
|
private void MoveFocus(bool forward)
|
||||||
{
|
{
|
||||||
if (Logging is null || boxes.Count == 0)
|
if (Logging is null || boxes.Count == 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
List<TextBox> notes = [.. new[] { nameBox, commentBox }.OfType<TextBox>()];
|
|
||||||
int inNotes = notes.FindIndex(box => box.IsFocused);
|
|
||||||
if (inNotes >= 0)
|
|
||||||
{
|
|
||||||
int next = inNotes + (forward ? 1 : -1);
|
|
||||||
if (next >= 0 && next < notes.Count)
|
|
||||||
{
|
|
||||||
notes[next].Focus();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Logging.Entry.FocusOn(forward ? 0 : Logging.Entry.FieldCount - 1);
|
|
||||||
FocusEntryBox();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (forward && notes.Count > 0 && Logging.Entry.Focus == Logging.Entry.FieldCount - 1)
|
|
||||||
{
|
|
||||||
notes[0].Focus();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (forward)
|
if (forward)
|
||||||
{
|
{
|
||||||
// leaving the callsign box is the moment the call is settled, which
|
// leaving the callsign box is the moment the call is settled, which
|
||||||
// is when the call history can say what the exchange will be
|
// is when the call history can say what the exchange will be and
|
||||||
|
// the reports can be filled in
|
||||||
if (Logging.Entry.Focus == 0)
|
if (Logging.Entry.Focus == 0)
|
||||||
{
|
{
|
||||||
bool filled = Logging.FillFromHistory();
|
bool filled = Logging.FillReports();
|
||||||
|
filled |= Logging.FillFromHistory();
|
||||||
filled |= FillNameFromHistory();
|
filled |= FillNameFromHistory();
|
||||||
if (filled)
|
if (filled)
|
||||||
{
|
{
|
||||||
@@ -472,8 +456,6 @@ public sealed partial class EntryWindow : Window
|
|||||||
|
|
||||||
private bool FocusedIsEntryBox() => boxes.Any(b => b.IsFocused);
|
private bool FocusedIsEntryBox() => boxes.Any(b => b.IsFocused);
|
||||||
|
|
||||||
private bool FocusedIsNoteBox() => nameBox?.IsFocused == true || commentBox?.IsFocused == true;
|
|
||||||
|
|
||||||
/// The buttons N1MM puts under its function keys. Each one does what its
|
/// The buttons N1MM puts under its function keys. Each one does what its
|
||||||
/// key does, so there is one place deciding what happens.
|
/// key does, so there is one place deciding what happens.
|
||||||
private void OnStopSending(object? sender, RoutedEventArgs e)
|
private void OnStopSending(object? sender, RoutedEventArgs e)
|
||||||
|
|||||||
@@ -64,11 +64,27 @@ public sealed class EntryFields
|
|||||||
|
|
||||||
public void FocusOn(int field) => Focus = Math.Clamp(field, 0, FieldCount - 1);
|
public void FocusOn(int field) => Focus = Math.Clamp(field, 0, FieldCount - 1);
|
||||||
|
|
||||||
/// What space does: move to the next box, and round to the callsign again
|
/// What space and tab do: move to the next box the operator types in, and
|
||||||
/// from the last one.
|
/// round to the callsign again from the last one. The report boxes are
|
||||||
public void Advance() => Focus = (Focus + 1) % FieldCount;
|
/// stepped over, because nobody types 599 — N1MM steps over them too, and
|
||||||
|
/// fills them in instead.
|
||||||
|
public void Advance() => Focus = Next(1);
|
||||||
|
|
||||||
public void Retreat() => Focus = (Focus + FieldCount - 1) % FieldCount;
|
public void Retreat() => Focus = Next(-1);
|
||||||
|
|
||||||
|
private int Next(int by)
|
||||||
|
{
|
||||||
|
int at = Focus;
|
||||||
|
for (int step = 0; step < FieldCount; step++)
|
||||||
|
{
|
||||||
|
at = (at + by + FieldCount) % FieldCount;
|
||||||
|
if (at == 0 || Exchange[at - 1].Kind != ExchangeFieldKind.Report)
|
||||||
|
{
|
||||||
|
return at;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Focus;
|
||||||
|
}
|
||||||
|
|
||||||
/// 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 =>
|
||||||
|
|||||||
@@ -97,6 +97,28 @@ public sealed class RadioPosition
|
|||||||
return stored;
|
return stored;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Puts the report the operator would send anyway into the report boxes
|
||||||
|
/// that are empty: 599 on CW and digital modes, 59 on phone. N1MM does the
|
||||||
|
/// same when the callsign box is left, which is why neither program stops
|
||||||
|
/// on a report box.
|
||||||
|
public bool FillReports()
|
||||||
|
{
|
||||||
|
bool filled = false;
|
||||||
|
for (int at = 0; at < Entry.Exchange.Count; at++)
|
||||||
|
{
|
||||||
|
if (Entry.Exchange[at].Kind == ExchangeFieldKind.Report && Entry[at + 1].Trim().Length == 0)
|
||||||
|
{
|
||||||
|
Entry[at + 1] = DefaultReport();
|
||||||
|
filled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (filled)
|
||||||
|
{
|
||||||
|
Session.NotifyChanged();
|
||||||
|
}
|
||||||
|
return filled;
|
||||||
|
}
|
||||||
|
|
||||||
/// Fills what the call history knows into the exchange boxes that are still
|
/// Fills what the call history knows into the exchange boxes that are still
|
||||||
/// empty. What the other station actually sends wins, so a box with
|
/// empty. What the other station actually sends wins, so a box with
|
||||||
/// something in it is left alone. Returns true when anything was filled.
|
/// something in it is left alone. Returns true when anything was filled.
|
||||||
|
|||||||
@@ -118,15 +118,48 @@ public class RadioPositionTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void SpaceMovesThroughTheBoxesAndBackToTheCall()
|
public void SpaceStepsOverTheReportAndComesBackToTheCall()
|
||||||
|
{
|
||||||
|
// CQ WW's boxes are the call, the report and the zone
|
||||||
|
RadioPosition session = Session();
|
||||||
|
|
||||||
|
Assert.Equal(0, session.Entry.Focus);
|
||||||
|
session.Entry.Advance();
|
||||||
|
Assert.Equal(2, session.Entry.Focus);
|
||||||
|
session.Entry.Advance();
|
||||||
|
Assert.Equal(0, session.Entry.Focus);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GoingBackStepsOverTheReportToo()
|
||||||
{
|
{
|
||||||
RadioPosition session = Session();
|
RadioPosition session = Session();
|
||||||
|
session.Entry.FocusOn(2);
|
||||||
|
|
||||||
|
session.Entry.Retreat();
|
||||||
|
|
||||||
Assert.Equal(0, session.Entry.Focus);
|
Assert.Equal(0, session.Entry.Focus);
|
||||||
session.Entry.Advance();
|
}
|
||||||
Assert.Equal(1, session.Entry.Focus);
|
|
||||||
session.Entry.Advance();
|
[Fact]
|
||||||
session.Entry.Advance();
|
public void TheReportBoxesAreFilledInWithWhatWouldBeSentAnyway()
|
||||||
Assert.Equal(0, session.Entry.Focus);
|
{
|
||||||
|
RadioPosition session = Session();
|
||||||
|
|
||||||
|
Assert.True(session.FillReports());
|
||||||
|
|
||||||
|
Assert.Equal("599", session.Entry.ValueOf(ExchangeSlot.ReceivedReport));
|
||||||
|
Assert.False(session.FillReports());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AReportTheOperatorTypedIsLeftAlone()
|
||||||
|
{
|
||||||
|
RadioPosition session = Session();
|
||||||
|
session.Entry.Set(ExchangeSlot.ReceivedReport, "559");
|
||||||
|
|
||||||
|
Assert.False(session.FillReports());
|
||||||
|
Assert.Equal("559", session.Entry.ValueOf(ExchangeSlot.ReceivedReport));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
|||||||
Reference in New Issue
Block a user