diff --git a/README.md b/README.md index 6c2f1d2..12d5bc7 100644 --- a/README.md +++ b/README.md @@ -400,6 +400,29 @@ to a tenth of a degree: `src/Nonemm.App/Assets/coastline.txt`. The sun's position is the standard low-precision solar formula, good to about a hundredth of a degree. +### Operators + +**Ctrl+O**, or **Config → Change Operator…**, asks who is at the radio. The +callsign goes on every contact logged from then on, in the log's Op column and +in the ADIF `OPERATOR` field, and `{OPERATOR}` in a message sends it. + +Each operator keeps three things of his own, which is what makes the key worth +pressing at a multi-operator station: + +| Kept | What happens on the way in and out | +|---|---| +| Window positions | where every open window sits is stored against the operator leaving, and the operator taking over gets his own back. A window opened later is placed as it appears | +| Colours | the theme he last had | +| Recordings folder | the folder his wav files are in, since a voice keyer plays the recordings of whoever is at the microphone | + +An operator whose callsign is not in the list is made by typing it, and starts +with the windows and colours on the screen. **Remove** drops one from the list; +the contacts he logged keep his callsign, because those are in the log rather +than in the settings. + +Nothing plays the recordings yet — there is no voice keyer — so the folder is +stored and passed on rather than used. + ### The log window The pane under the log lists what has already been worked with the call being diff --git a/docs/unfinished.md b/docs/unfinished.md index dc8994a..ced78ba 100644 --- a/docs/unfinished.md +++ b/docs/unfinished.md @@ -35,7 +35,8 @@ The other interfaces N1MM offers — fldigi, MMVARI, the hardware TNCs — are n here either, though `DigitalEngine` is the place to add them. **Voice keying.** `MessageSender` was written to cover a voice keyer playing a -recording, and nothing implements it. No DVK support either, so the second radio +recording, and nothing implements it. Each operator's recordings folder is +stored with the rest of his settings and waits on the same thing. No DVK support either, so the second radio of an SO2R station cannot call CQ by voice. Alternating CQ therefore works on CW only, though nothing in it is CW-specific: a voice keyer that reports when the recording has finished would drive it as it stands. diff --git a/src/Nonemm.App/AppSession.cs b/src/Nonemm.App/AppSession.cs index b3e1a1f..8b14508 100644 --- a/src/Nonemm.App/AppSession.cs +++ b/src/Nonemm.App/AppSession.cs @@ -196,7 +196,12 @@ public sealed class AppSession : IDisposable Contest contest = Registry.Create(instance.ContestName, ModeCategoryOf(instance)); SaveDefinition(contest); Logging = new ContestSession( - Store, contest, instance, Settings.Station.ToStationInfo(), Countries, History); + Store, contest, instance, Settings.Station.ToStationInfo(), Countries, History) + { + // whoever was at the radio when the last contest was open is still + // there, and every contact he logs carries his callsign + Operator = Settings.CurrentOperator, + }; positions.Clear(); for (int number = 1; number <= PositionCount; number++) { diff --git a/src/Nonemm.App/Configuration/Settings.cs b/src/Nonemm.App/Configuration/Settings.cs index 4a9b396..aaeedf1 100644 --- a/src/Nonemm.App/Configuration/Settings.cs +++ b/src/Nonemm.App/Configuration/Settings.cs @@ -186,6 +186,14 @@ public sealed record Settings public IReadOnlyList PhoneMessages { get; init; } = []; + /// The operators at this station, and their windows and colours. N1MM + /// keeps an operator's callsign against every contact he logs; this keeps + /// what he wants the program to look like as well. + public IReadOnlyList Operators { get; init; } = []; + + /// Whoever is at the radio now, or empty at a station with one operator. + public string CurrentOperator { get; init; } = ""; + /// The digital window's twenty-four macro buttons, in the same /// `label,message` lines as a function key file. Empty means the built-in /// set. diff --git a/src/Nonemm.App/Configuration/StoredOperator.cs b/src/Nonemm.App/Configuration/StoredOperator.cs new file mode 100644 index 0000000..e4ef4f3 --- /dev/null +++ b/src/Nonemm.App/Configuration/StoredOperator.cs @@ -0,0 +1,21 @@ +namespace Nonemm.App.Configuration; + +/// One operator's own settings. A multi-operator station changes hands every +/// few hours, and each operator wants the windows where he left them and the +/// colours he can read; N1MM asks for the operator's callsign the same way, and +/// records it against every contact he logs. +public sealed record StoredOperator +{ + public string Callsign { get; init; } = ""; + + /// Empty leaves the colours as they are, which is what a new operator gets + /// until he changes them. + public string Theme { get; init; } = ""; + + /// The folder this operator's recordings are in. A voice keyer plays the + /// recordings of whoever is at the microphone, so the path is the + /// operator's rather than the station's. + public string WavPath { get; init; } = ""; + + public IReadOnlyList Windows { get; init; } = []; +} diff --git a/src/Nonemm.App/Configuration/StoredWindow.cs b/src/Nonemm.App/Configuration/StoredWindow.cs new file mode 100644 index 0000000..818d4aa --- /dev/null +++ b/src/Nonemm.App/Configuration/StoredWindow.cs @@ -0,0 +1,17 @@ +namespace Nonemm.App.Configuration; + +/// Where one window sits, in screen pixels. `Name` is the window's class name, +/// which is what tells the entry window from the bandmap when a layout is put +/// back. +public sealed record StoredWindow +{ + public string Name { get; init; } = ""; + + public int X { get; init; } + + public int Y { get; init; } + + public double Width { get; init; } + + public double Height { get; init; } +} diff --git a/src/Nonemm.App/Dialogs/OperatorDialog.axaml b/src/Nonemm.App/Dialogs/OperatorDialog.axaml new file mode 100644 index 0000000..5475446 --- /dev/null +++ b/src/Nonemm.App/Dialogs/OperatorDialog.axaml @@ -0,0 +1,40 @@ + + + + + + +