Let the operator pick, order and sort the log columns
The log showed the contest's columns and nothing else, with the country, points and multiplier columns fixed in the XAML. LogColumns now holds every field the log can show and the settings file keeps the names, left to right. A right click on a header adds or removes a column and offers the contest's own set back, a header can be dragged to move its column, and a click sorts on it. Sorting uses the field, not the text of the cell, so 9,000 kHz comes before 14,000 and the serial numbers do not run 1, 10, 2. Both grids get the same columns, so a value in the pane below sits under the column it belongs to. Removing the last column is refused, otherwise nothing could be selected. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015NZkFxHde1iifAQemFTHxf
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
using Nonemm.Core;
|
||||
|
||||
namespace Nonemm.Session;
|
||||
|
||||
/// The columns the log window can show, and how a set the operator picked is
|
||||
/// stored. The contest gives the default set and the labels for its own
|
||||
/// exchange fields; every other field can be added to it from the header menu.
|
||||
public static class LogColumns
|
||||
{
|
||||
/// Every field the log can show, in the order the header menu lists the
|
||||
/// ones the contest did not ask for. Labels and widths are the fallback
|
||||
/// for a field the contest has no column for.
|
||||
public static readonly IReadOnlyList<QsoColumn> All =
|
||||
[
|
||||
new QsoColumn("Time", QsoField.Time, 19),
|
||||
new QsoColumn("Freq", QsoField.Frequency, 8),
|
||||
new QsoColumn("QSX", QsoField.QsxFrequency, 8),
|
||||
new QsoColumn("Mode", QsoField.Mode, 5),
|
||||
new QsoColumn("Call", QsoField.Call, 12),
|
||||
new QsoColumn("Snt", QsoField.SentReport, 4),
|
||||
new QsoColumn("SntNr", QsoField.SentNumber, 5),
|
||||
new QsoColumn("Rcv", QsoField.ReceivedReport, 4),
|
||||
new QsoColumn("Nr", QsoField.ReceivedNumber, 5),
|
||||
new QsoColumn("Zone", QsoField.Zone, 4),
|
||||
new QsoColumn("Sect", QsoField.Section, 5),
|
||||
new QsoColumn("Prec", QsoField.Precedence, 4),
|
||||
new QsoColumn("Ck", QsoField.Check, 3),
|
||||
new QsoColumn("Exch", QsoField.Exchange1, 6),
|
||||
new QsoColumn("Misc", QsoField.MiscText, 6),
|
||||
new QsoColumn("Name", QsoField.Name, 10),
|
||||
new QsoColumn("QTH", QsoField.Qth, 10),
|
||||
new QsoColumn("Grid", QsoField.GridSquare, 6),
|
||||
new QsoColumn("Pwr", QsoField.Power, 5),
|
||||
new QsoColumn("Comment", QsoField.Comment, 16),
|
||||
new QsoColumn("Op", QsoField.Operator, 8),
|
||||
new QsoColumn("Cty", QsoField.CountryPrefix, 6),
|
||||
new QsoColumn("WPX", QsoField.WpxPrefix, 6),
|
||||
new QsoColumn("Radio", QsoField.RadioNumber, 5),
|
||||
new QsoColumn("Run", QsoField.RunPosition, 3),
|
||||
new QsoColumn("Pts", QsoField.Points, 4, IsEditable: false),
|
||||
new QsoColumn("Mult", QsoField.Multipliers, 5, IsEditable: false),
|
||||
];
|
||||
|
||||
/// What the header menu offers: the contest's own columns first, in the
|
||||
/// order the contest gives them, then the rest.
|
||||
public static IReadOnlyList<QsoColumn> Choices(IReadOnlyList<QsoColumn> contest) =>
|
||||
[
|
||||
.. contest,
|
||||
.. All.Where(c => !contest.Any(other => other.Field == c.Field)),
|
||||
];
|
||||
|
||||
/// The stored field names turned back into columns. A name that is not a
|
||||
/// field, or is repeated, is dropped. An empty or unusable list falls back
|
||||
/// to the contest's own columns, which is what a fresh log shows.
|
||||
public static IReadOnlyList<QsoColumn> Chosen(
|
||||
IReadOnlyList<QsoColumn> contest,
|
||||
IEnumerable<string> stored)
|
||||
{
|
||||
IReadOnlyList<QsoColumn> offered = Choices(contest);
|
||||
List<QsoColumn> chosen = [];
|
||||
foreach (string name in stored)
|
||||
{
|
||||
if (!Enum.TryParse(name, out QsoField field)
|
||||
|| chosen.Any(c => c.Field == field)
|
||||
|| offered.FirstOrDefault(c => c.Field == field) is not { } column)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
chosen.Add(column);
|
||||
}
|
||||
return chosen.Count > 0 ? chosen : contest;
|
||||
}
|
||||
|
||||
/// What a column sorts on. Numbers and times sort as numbers and times, not
|
||||
/// as the text the log shows, so 9,000 kHz comes before 14,000 kHz and the
|
||||
/// serial numbers do not run 1, 10, 2.
|
||||
public static IComparable SortKey(Qso qso, QsoField field) => field switch
|
||||
{
|
||||
QsoField.Time => qso.TimestampUtc,
|
||||
QsoField.Frequency => qso.Frequency.Hertz,
|
||||
QsoField.QsxFrequency => qso.QsxFrequency.Hertz,
|
||||
QsoField.SentNumber => qso.SentNumber,
|
||||
QsoField.ReceivedNumber => qso.ReceivedNumber,
|
||||
QsoField.Zone => qso.Zone,
|
||||
QsoField.Check => qso.Check,
|
||||
QsoField.RadioNumber => qso.RadioNumber,
|
||||
QsoField.RunPosition => qso.RunPosition,
|
||||
QsoField.Points => qso.Points,
|
||||
_ => QsoEditor.Read(qso, field),
|
||||
};
|
||||
|
||||
/// What goes in the settings file: the field names, left to right.
|
||||
public static IReadOnlyList<string> Store(IEnumerable<QsoColumn> columns) =>
|
||||
[.. columns.Select(c => c.Field.ToString())];
|
||||
}
|
||||
Reference in New Issue
Block a user