Lay the digital window out the way N1MM does

The window now carries what N1MM's does. Across the top: the TX indicator, the
Letters/Figs box showing the word under the pointer in the other Baudot case,
and the MouseOver box showing the word itself. Down the left of the receive
pane is the pause bar, green while text prints and yellow while it is held.
Under the panes are the message buttons and the row of engine controls, in
N1MM's order.

Both of N1MM's window scroll types are here. The scrolling pane adds lines at
the bottom; the non-scrolling pane keeps text where it was printed, underlines
the line being printed to and walks it down the window, so a call does not move
as the pointer reaches it.

A callsign is checked before it is coloured: anything shaped like a call under
the generic routine, only calls in the files that are ticked under the
resources routine, and under the combination everything, with a call the files
do not hold marked in yellow. The garbage text search finds a Master.scp call
inside a run of noise.

The mouse and the keys are N1MM's. Shift+click converts the case before the
word is transferred, Ctrl+click puts it in the box the cursor is in, Alt+click
puts a call on the stack, a double click replaces the call in the box. The
receive pane and the grab list have their right click menus. Alt+T and Ctrl+K
turn the transmitter on with the cursor in the transmit pane, Alt+G grabs, Esc
aborts.

New from the menus: hover mode, the callsign pause routines, sending a text
file, a date-stamped copy of the receive pane in ExportFiles, clearing the grab
list on a CQ or a QSY, the ham default and AFC off on a run to S&P change, AFC
on with CQ, and raising the window with the entry window it belongs to. The
grab list holds ten calls, as N1MM's does.

The setup window is N1MM's dialog: a General / MMTTY Setup tab, an Engine tab
for the Wine and bridge paths, and a Message Setup tab with the buttons laid
out as they sit in the window, a caption and text box for the one being edited,
the macro list, the count of buttons to show, and import and save of a .mc
file. Right-clicking a message button opens that tab, so the free-form macro
dialog is gone.

The title carries the dial frequency with the mark tone added to it, or the
dial frequency alone when the radio already reads out what it transmits.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoGtneMQaz4M9w7Kk49AVD
This commit is contained in:
2026-09-01 00:03:30 +00:00
co-authored by Claude Opus 5
parent 1da35e6c45
commit f3908840b3
9 changed files with 1154 additions and 248 deletions
+177 -44
View File
@@ -1,7 +1,7 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Nonemm.App.Dialogs.DigitalDialog"
Title="Digital engine" Width="620" SizeToContent="Height"
Title="Digital Setup" Width="820" Height="640"
WindowStartupLocation="CenterOwner">
<Window.Styles>
<Style Selector="TextBlock.label">
@@ -9,51 +9,184 @@
<Setter Property="Opacity" Value="0.7" />
<Setter Property="Margin" Value="0,8,0,1" />
</Style>
<Style Selector="HeaderedContentControl">
<Setter Property="Margin" Value="0,0,0,8" />
</Style>
<Style Selector="CheckBox">
<Setter Property="FontSize" Value="12" />
<Setter Property="MinHeight" Value="22" />
</Style>
<Style Selector="RadioButton">
<Setter Property="FontSize" Value="12" />
<Setter Property="MinHeight" Value="22" />
</Style>
<Style Selector="Button.macro">
<Setter Property="FontSize" Value="11" />
<Setter Property="Padding" Value="2,3" />
<Setter Property="Margin" Value="1" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
</Style>
</Window.Styles>
<DockPanel Margin="14">
<StackPanel DockPanel.Dock="Top">
<TextBlock TextWrapping="Wrap" FontSize="11" Opacity="0.75"
Text="MMTTY and 2Tone are Windows programs. They run under Wine, driven through the same XMMT.ocx control N1MM uses, with a small bridge program in between. See docs/digital-bridge.md for setting the Wine prefix up." />
<TextBlock Classes="label" Text="Engine program (mmtty.exe or 2Tone.exe)" />
<Grid ColumnDefinitions="*,6,Auto">
<TextBox Name="EngineBox" />
<Button Grid.Column="2" Content="Browse…" Click="OnBrowseEngine" />
</Grid>
<TextBlock Classes="label" Text="Bridge program (nonemm-mmtty-bridge.exe)" />
<Grid ColumnDefinitions="*,6,Auto">
<TextBox Name="BridgeBox" />
<Button Grid.Column="2" Content="Browse…" Click="OnBrowseBridge" />
</Grid>
<Grid ColumnDefinitions="*,8,160" RowDefinitions="Auto,Auto">
<TextBlock Classes="label" Text="Wine prefix (empty for Wine's own)" />
<TextBox Name="PrefixBox" Grid.Row="1" />
<TextBlock Grid.Column="2" Classes="label" Text="Wine command" />
<TextBox Name="WineBox" Grid.Row="1" Grid.Column="2" />
</Grid>
<TextBlock Classes="label" Text="Control (XMMT.ocx), registered in the prefix above" />
<Grid ColumnDefinitions="*,6,Auto,6,Auto">
<TextBox Name="ControlBox" />
<Button Grid.Column="2" Content="Browse…" Click="OnBrowseControl" />
<Button Grid.Column="4" Name="RegisterButton" Content="Register" Click="OnRegister" />
</Grid>
<TextBlock Name="RegisterText" TextWrapping="Wrap" FontSize="11" Opacity="0.75"
Margin="0,4,0,0" IsVisible="False" />
<Grid ColumnDefinitions="160,8,160,8,*" RowDefinitions="Auto,Auto">
<TextBlock Classes="label" Text="Engine window" />
<ComboBox Name="WindowBox" Grid.Row="1" HorizontalAlignment="Stretch" />
<TextBlock Grid.Column="2" Classes="label" Text="PTT port (empty reads Mmtty.INI)" />
<TextBox Name="PttBox" Grid.Row="1" Grid.Column="2" />
<TextBlock Grid.Column="4" Classes="label" Text="Mark tone (Hz)" />
<TextBox Name="MarkBox" Grid.Row="1" Grid.Column="4" />
</Grid>
<CheckBox Name="OnTopBox" Content="Keep the engine window on top" Margin="0,10,0,0" />
<DockPanel Margin="12">
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" HorizontalAlignment="Right"
Spacing="6" Margin="0,10,0,0">
<Button Content="Cancel" Click="OnCancel" />
<Button Content="Save Settings" Click="OnSave" IsDefault="True" />
</StackPanel>
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" HorizontalAlignment="Right"
Spacing="6" Margin="0,12,0,0">
<Button Content="Cancel" Click="OnCancel" />
<Button Content="Save" Click="OnSave" IsDefault="True" />
</StackPanel>
<Panel />
<TabControl Name="Tabs">
<TabItem Header="General / MMTTY Setup">
<ScrollViewer>
<StackPanel Margin="4,8,10,4">
<HeaderedContentControl Header="General Settings">
<Border BorderThickness="1" BorderBrush="#40808080" Padding="8">
<StackPanel>
<CheckBox Name="RadioFrequencyBox"
Content="Display radio freq and not exact freq in DI caption" />
<CheckBox Name="ClickSpaceBox" Content="Send space on callsign mouse click" />
<CheckBox Name="GrabSpaceBox" Content="Send space on using grab" />
<CheckBox Name="HamOnSearchBox"
Content="(MMTTY) Send ham default on run to S&amp;P change" />
<CheckBox Name="AfcOnSearchBox" Content="(MMTTY) Turn AFC off on run to S&amp;P change" />
<CheckBox Name="AfcOnCqBox" Content="(MMTTY) Turn AFC on with CQ" />
<CheckBox Name="NoDupesBox" Content="Do not add dupes to grab window" />
<CheckBox Name="KnownOnlyBox"
Content="Only grab Master.scp calls and previously worked calls" />
<CheckBox Name="ClearOnCqBox" Content="Clear grab window on CQ" />
<CheckBox Name="ClearOnQsyBox" Content="QSY will clear grab window" />
<CheckBox Name="ForegroundBox" Content="Bring to foreground when made active" />
<CheckBox Name="TextOutputBox" Content="Output RX window to text file" />
</StackPanel>
</Border>
</HeaderedContentControl>
<Grid ColumnDefinitions="*,10,*">
<HeaderedContentControl Header="Window Scroll">
<Border BorderThickness="1" BorderBrush="#40808080" Padding="8">
<StackPanel>
<RadioButton Name="ScrollingBox" GroupName="scroll" Content="Scrolling" />
<RadioButton Name="NonScrollingBox" GroupName="scroll" Content="Non scrolling" />
</StackPanel>
</Border>
</HeaderedContentControl>
<HeaderedContentControl Grid.Column="2" Header="MMTTY Window Layout">
<Border BorderThickness="1" BorderBrush="#40808080" Padding="8">
<StackPanel>
<ComboBox Name="WindowBox" HorizontalAlignment="Stretch" />
<CheckBox Name="OnTopBox" Content="MMTTY always on top" Margin="0,6,0,0" />
</StackPanel>
</Border>
</HeaderedContentControl>
</Grid>
<HeaderedContentControl Header="Callsign Validity and Highlight Routines">
<Border BorderThickness="1" BorderBrush="#40808080" Padding="8">
<StackPanel>
<RadioButton Name="GenericBox" GroupName="routine" Content="Use generic routines" />
<RadioButton Name="ResourcesBox" GroupName="routine" Content="Use these resources:" />
<RadioButton Name="BothBox" GroupName="routine" Content="Use combination of both" />
<WrapPanel Margin="18,2,0,4">
<CheckBox Name="MasterScpBox" Content="Master.scp" Margin="0,0,12,0" />
<CheckBox Name="CallHistoryBox" Content="Call history" Margin="0,0,12,0" />
<CheckBox Name="TelnetBox" Content="Telnet calls" Margin="0,0,12,0" />
<CheckBox Name="LoggedBox" Content="Logged calls" />
</WrapPanel>
<CheckBox Name="GarbageBox"
Content="Use search routine to find Master.scp calls in garbage text" />
<RadioButton Name="ForegroundHighlightBox" GroupName="highlight"
Content="Highlight foreground text" />
<RadioButton Name="BackgroundHighlightBox" GroupName="highlight"
Content="Highlight background of text" />
</StackPanel>
</Border>
</HeaderedContentControl>
<HeaderedContentControl Header="Alignment Frequency">
<Border BorderThickness="1" BorderBrush="#40808080" Padding="8">
<StackPanel>
<TextBlock Classes="label" Text="MMTTY mark frequency (Hz)" Margin="0,0,0,1" />
<TextBox Name="MarkBox" Width="120" HorizontalAlignment="Left" />
<TextBlock Classes="label" TextWrapping="Wrap"
Text="The Align button retunes the radio so the signal that was clicked in the waterfall sits on this tone." />
</StackPanel>
</Border>
</HeaderedContentControl>
</StackPanel>
</ScrollViewer>
</TabItem>
<TabItem Header="Engine">
<ScrollViewer>
<StackPanel Margin="4,8,10,4">
<TextBlock TextWrapping="Wrap" FontSize="11" Opacity="0.75"
Text="MMTTY and 2Tone are Windows programs. They run under Wine, driven through the same XMMT.ocx control N1MM uses, with a small bridge program in between. See docs/digital-bridge.md for setting the Wine prefix up." />
<TextBlock Classes="label" Text="Engine program (mmtty.exe or 2Tone.exe)" />
<Grid ColumnDefinitions="*,6,Auto">
<TextBox Name="EngineBox" />
<Button Grid.Column="2" Content="Browse…" Click="OnBrowseEngine" />
</Grid>
<TextBlock Classes="label" Text="Bridge program (nonemm-mmtty-bridge.exe)" />
<Grid ColumnDefinitions="*,6,Auto">
<TextBox Name="BridgeBox" />
<Button Grid.Column="2" Content="Browse…" Click="OnBrowseBridge" />
</Grid>
<Grid ColumnDefinitions="*,8,160" RowDefinitions="Auto,Auto">
<TextBlock Classes="label" Text="Wine prefix (empty for Wine's own)" />
<TextBox Name="PrefixBox" Grid.Row="1" />
<TextBlock Grid.Column="2" Classes="label" Text="Wine command" />
<TextBox Name="WineBox" Grid.Row="1" Grid.Column="2" />
</Grid>
<TextBlock Classes="label" Text="Control (XMMT.ocx), registered in the prefix above" />
<Grid ColumnDefinitions="*,6,Auto,6,Auto">
<TextBox Name="ControlBox" />
<Button Grid.Column="2" Content="Browse…" Click="OnBrowseControl" />
<Button Grid.Column="4" Name="RegisterButton" Content="Register" Click="OnRegister" />
</Grid>
<TextBlock Name="RegisterText" TextWrapping="Wrap" FontSize="11" Opacity="0.75"
Margin="0,4,0,0" IsVisible="False" />
<TextBlock Classes="label" Text="PTT port (empty reads Mmtty.INI)" />
<TextBox Name="PttBox" Width="160" HorizontalAlignment="Left" />
</StackPanel>
</ScrollViewer>
</TabItem>
<TabItem Header="Message Setup">
<DockPanel Margin="4,8,4,4">
<Grid DockPanel.Dock="Top" ColumnDefinitions="Auto,Auto,*,Auto,Auto">
<TextBlock Classes="label" Text="Number of message buttons to use" Margin="0,0,6,0"
VerticalAlignment="Center" />
<ComboBox Name="ButtonCountBox" Grid.Column="1" Width="80" />
<Button Grid.Column="3" Content="Import…" Click="OnImportMessages" Margin="4,0" />
<Button Grid.Column="4" Content="Save…" Click="OnExportMessages" />
</Grid>
<TextBlock DockPanel.Dock="Top" Classes="label"
Text="Select a macro button to edit, or use Import to load messages from a .mc file." />
<Grid DockPanel.Dock="Top" Name="MacroRows" RowDefinitions="Auto,Auto,Auto" Margin="0,4" />
<Grid ColumnDefinitions="*,10,220" Margin="0,8,0,0">
<StackPanel>
<TextBlock Classes="label" Text="Message Caption" />
<TextBox Name="CaptionBox" />
<TextBlock Classes="label" Text="Message Text" />
<TextBox Name="MessageBox" AcceptsReturn="True" TextWrapping="Wrap" Height="120" />
<StackPanel Orientation="Horizontal" Spacing="6" Margin="0,8,0,0">
<Button Content="Save Message" Click="OnSaveMessage" />
<Button Content="Back to the defaults" Click="OnDefaults" />
</StackPanel>
<TextBlock Name="MessageStatus" Classes="label" />
</StackPanel>
<StackPanel Grid.Column="2">
<TextBlock Classes="label" Text="Available Macros" />
<Border BorderThickness="1" BorderBrush="#40808080">
<ListBox Name="MacroList" Height="200" SelectionChanged="OnMacroPicked" />
</Border>
</StackPanel>
</Grid>
</DockPanel>
</TabItem>
</TabControl>
</DockPanel>
</Window>
+236 -9
View File
@@ -1,35 +1,237 @@
using System.Globalization;
using System.Text;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Interactivity;
using Avalonia.Platform.Storage;
using Nonemm.App.Configuration;
using Nonemm.App.Windows;
using Nonemm.Digital;
using Nonemm.Session;
namespace Nonemm.App.Dialogs;
/// Where the digital engine is and how it is started. It closes with the
/// settings, or with null when nothing is to be changed.
/// The tabs of the digital setup window, for the callers that open it on one.
public static class DigitalDialogTabs
{
public const int General = 0;
public const int Engine = 1;
public const int Messages = 2;
}
/// N1MM's digital setup window: how the receive pane and the grab list behave,
/// where the engine is and how it is started, and the twenty-four message
/// buttons. It closes with the settings, or with null when nothing is to be
/// changed.
public sealed partial class DigitalDialog : Window
{
private const int MacroColumns = 8;
private static readonly string[] WindowSizes = ["Normal", "Small", "Medium1", "Medium2"];
private readonly Settings settings;
/// The macros the message buttons take, for the list beside the message
/// text. They are the ones the entry window's function keys expand.
private static readonly string[] Macros =
[
"{TX}", "{RX}", "{ENTER}", "{MYCALL}", "{CALL}", "{EXCH}", "{SENTRST}", "{SENTRSTCUT}",
"{SERIAL}", "{NAME}", "{NAMEANDSPACE}", "{GRID}", "{GRIDSQUARE}", "{OPERATOR}",
"{LASTCALL}", "{PREVNR}", "{FREQ}", "{FREQROUND}", "{TIME2}", "{TIMESTAMP}",
"{GRAB}", "{LOG}", "{LOGTHENPOP}", "{WIPE}", "{RUN}", "{S&P}", "{SPACE}", "{SPOTME}",
"{EXCHSENT}", "{STOPTX}", "{END}",
];
public DigitalDialog(Settings settings)
private readonly Settings settings;
private readonly List<Button> macroButtons = [];
private readonly List<FunctionKey> messages;
private int editing;
public DigitalDialog(Settings settings, int tab = DigitalDialogTabs.General)
{
this.settings = settings;
InitializeComponent();
messages = Messages.Digital(settings.DigitalMessageFile).Buttons.ToList();
ShowGeneral();
ShowEngine();
BuildMacroButtons();
MacroList.ItemsSource = Macros;
Tabs.SelectedIndex = tab;
}
private void ShowGeneral()
{
RadioFrequencyBox.IsChecked = settings.DigitalRadioFrequencyInTitle;
ClickSpaceBox.IsChecked = settings.DigitalClickSendsSpace;
GrabSpaceBox.IsChecked = settings.DigitalGrabSendsSpace;
HamOnSearchBox.IsChecked = settings.DigitalHamDefaultOnSearch;
AfcOnSearchBox.IsChecked = settings.DigitalAfcOffOnSearch;
AfcOnCqBox.IsChecked = settings.DigitalAfcOnCq;
NoDupesBox.IsChecked = settings.DigitalGrabFilter.ToLowerInvariant() == "notdupes";
KnownOnlyBox.IsChecked = settings.DigitalGrabFilter.ToLowerInvariant() == "knowncalls";
ClearOnCqBox.IsChecked = settings.DigitalClearGrabOnCq;
ClearOnQsyBox.IsChecked = settings.DigitalClearGrabOnQsy;
ForegroundBox.IsChecked = settings.DigitalBringToForeground;
TextOutputBox.IsChecked = settings.DigitalOutputToTextFile;
ScrollingBox.IsChecked = settings.DigitalScrolling;
NonScrollingBox.IsChecked = !settings.DigitalScrolling;
string routine = settings.DigitalHighlightRoutine.ToLowerInvariant();
GenericBox.IsChecked = routine is not ("resources" or "both");
ResourcesBox.IsChecked = routine == "resources";
BothBox.IsChecked = routine == "both";
MasterScpBox.IsChecked = settings.DigitalUseMasterScp;
CallHistoryBox.IsChecked = settings.DigitalUseCallHistory;
TelnetBox.IsChecked = settings.DigitalUseTelnetCalls;
LoggedBox.IsChecked = settings.DigitalUseLoggedCalls;
GarbageBox.IsChecked = settings.DigitalSearchGarbage;
ForegroundHighlightBox.IsChecked = !settings.DigitalHighlightBackground;
BackgroundHighlightBox.IsChecked = settings.DigitalHighlightBackground;
MarkBox.Text = settings.DigitalMarkHertz.ToString(CultureInfo.InvariantCulture);
WindowBox.ItemsSource = WindowSizes;
WindowBox.SelectedItem =
WindowSizes.FirstOrDefault(s => s == settings.DigitalEngineWindow) ?? WindowSizes[0];
OnTopBox.IsChecked = settings.DigitalEngineOnTop;
ButtonCountBox.ItemsSource = DigitalWindow.MacroCounts;
ButtonCountBox.SelectedItem = DigitalWindow.MacroCounts.Contains(settings.DigitalMessageButtons)
? settings.DigitalMessageButtons
: DigitalMacros.ButtonCount;
}
private void ShowEngine()
{
EngineBox.Text = settings.DigitalEnginePath;
BridgeBox.Text = settings.DigitalBridgePath;
ControlBox.Text = settings.DigitalControlPath;
PrefixBox.Text = settings.DigitalWinePrefix;
WineBox.Text = settings.DigitalWineCommand;
PttBox.Text = settings.DigitalPttPort;
MarkBox.Text = settings.DigitalMarkHertz.ToString(CultureInfo.InvariantCulture);
WindowBox.ItemsSource = WindowSizes;
WindowBox.SelectedItem =
WindowSizes.FirstOrDefault(s => s == settings.DigitalEngineWindow) ?? WindowSizes[0];
OnTopBox.IsChecked = settings.DigitalEngineOnTop;
}
/// The twenty-four buttons as they sit in the digital window. Clicking one
/// brings its caption and its text down into the boxes below.
private void BuildMacroButtons()
{
for (int row = 0; row < DigitalMacros.ButtonCount / MacroColumns; row++)
{
UniformGrid grid = new() { Columns = MacroColumns, Rows = 1 };
Grid.SetRow(grid, row);
for (int column = 0; column < MacroColumns; column++)
{
int index = (row * MacroColumns) + column;
Button button = new() { Classes = { "macro" } };
button.Click += (_, _) => Edit(index);
macroButtons.Add(button);
grid.Children.Add(button);
}
MacroRows.Children.Add(grid);
}
LabelMacroButtons();
Edit(0);
}
private void LabelMacroButtons()
{
for (int index = 0; index < macroButtons.Count; index++)
{
macroButtons[index].Content = messages[index].Label.Length > 0
? messages[index].Label
: $"{index + 1}";
}
}
private void Edit(int index)
{
editing = index;
CaptionBox.Text = messages[index].Label;
MessageBox.Text = messages[index].Message;
MessageStatus.Text = $"button {index + 1}";
}
private void OnSaveMessage(object? sender, RoutedEventArgs e)
{
messages[editing] = new FunctionKey(CaptionBox.Text?.Trim() ?? "", MessageBox.Text?.Trim() ?? "");
LabelMacroButtons();
MessageStatus.Text = $"button {editing + 1} saved";
}
private void OnDefaults(object? sender, RoutedEventArgs e)
{
messages.Clear();
messages.AddRange(DigitalMacros.Parse(Messages.DefaultDigital).Buttons);
LabelMacroButtons();
Edit(editing);
}
/// A macro picked from the list is typed into the message at the cursor,
/// which is what N1MM's list does.
private void OnMacroPicked(object? sender, SelectionChangedEventArgs e)
{
if (MacroList.SelectedItem is not string macro)
{
return;
}
string text = MessageBox.Text ?? "";
int at = Math.Clamp(MessageBox.CaretIndex, 0, text.Length);
MessageBox.Text = text[..at] + macro + text[at..];
MessageBox.CaretIndex = at + macro.Length;
MacroList.SelectedItem = null;
MessageBox.Focus();
}
private async void OnImportMessages(object? sender, RoutedEventArgs e)
{
if (await PickAsync("The message file", "Message files", "*.mc") is not { } path)
{
return;
}
try
{
messages.Clear();
messages.AddRange(DigitalMacros.Parse(await File.ReadAllTextAsync(path)).Buttons);
LabelMacroButtons();
Edit(editing);
MessageStatus.Text = $"read {Path.GetFileName(path)}";
}
catch (IOException failure)
{
MessageStatus.Text = failure.Message;
}
}
private async void OnExportMessages(object? sender, RoutedEventArgs e)
{
IStorageFile? file = await StorageProvider.SaveFilePickerAsync(
new FilePickerSaveOptions
{
Title = "Save the messages",
DefaultExtension = "mc",
SuggestedFileName = "DigitalMessages.mc",
});
if (file is null)
{
return;
}
try
{
await File.WriteAllTextAsync(file.Path.LocalPath, MessageLines());
MessageStatus.Text = $"wrote {Path.GetFileName(file.Path.LocalPath)}";
}
catch (IOException failure)
{
MessageStatus.Text = failure.Message;
}
}
/// The buttons as the `label,message` lines the rest of the program reads.
private string MessageLines()
{
StringBuilder lines = new();
foreach (FunctionKey message in messages)
{
lines.Append(message.Label.Replace("&", "&&")).Append(',').AppendLine(message.Message);
}
return lines.ToString();
}
private async void OnBrowseEngine(object? sender, RoutedEventArgs e) =>
@@ -101,5 +303,30 @@ public sealed partial class DigitalDialog : Window
DigitalEngineWindow = WindowBox.SelectedItem as string ?? "Normal",
DigitalEngineOnTop = OnTopBox.IsChecked == true,
DigitalEnabled = (EngineBox.Text?.Trim().Length ?? 0) > 0,
DigitalRadioFrequencyInTitle = RadioFrequencyBox.IsChecked == true,
DigitalClickSendsSpace = ClickSpaceBox.IsChecked == true,
DigitalGrabSendsSpace = GrabSpaceBox.IsChecked == true,
DigitalHamDefaultOnSearch = HamOnSearchBox.IsChecked == true,
DigitalAfcOffOnSearch = AfcOnSearchBox.IsChecked == true,
DigitalAfcOnCq = AfcOnCqBox.IsChecked == true,
DigitalGrabFilter = KnownOnlyBox.IsChecked == true ? "knowncalls"
: NoDupesBox.IsChecked == true ? "notdupes" : "everything",
DigitalClearGrabOnCq = ClearOnCqBox.IsChecked == true,
DigitalClearGrabOnQsy = ClearOnQsyBox.IsChecked == true,
DigitalBringToForeground = ForegroundBox.IsChecked == true,
DigitalOutputToTextFile = TextOutputBox.IsChecked == true,
DigitalScrolling = NonScrollingBox.IsChecked != true,
DigitalHighlightRoutine = ResourcesBox.IsChecked == true ? "resources"
: BothBox.IsChecked == true ? "both" : "generic",
DigitalUseMasterScp = MasterScpBox.IsChecked == true,
DigitalUseCallHistory = CallHistoryBox.IsChecked == true,
DigitalUseTelnetCalls = TelnetBox.IsChecked == true,
DigitalUseLoggedCalls = LoggedBox.IsChecked == true,
DigitalSearchGarbage = GarbageBox.IsChecked == true,
DigitalHighlightBackground = BackgroundHighlightBox.IsChecked == true,
DigitalMessageButtons = ButtonCountBox.SelectedItem is int count
? count
: settings.DigitalMessageButtons,
DigitalMessageFile = MessageLines(),
});
}
@@ -1,20 +0,0 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Nonemm.App.Dialogs.DigitalMacrosDialog"
Title="Digital macros" Width="620" Height="520"
WindowStartupLocation="CenterOwner">
<DockPanel Margin="14">
<TextBlock DockPanel.Dock="Top" TextWrapping="Wrap" FontSize="11" Opacity="0.75"
Text="One line per button: the label, a comma, then the message. Twenty-four lines fill the three rows of eight. {TX} starts the transmission and {RX} ends it; {ENTER} sends a carriage return. The text macros are the same ones the function keys use." />
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" Spacing="6" Margin="0,10,0,0">
<Button Content="Back to the defaults" Click="OnDefaults" />
<Panel Width="180" />
<Button Content="Cancel" Click="OnCancel" />
<Button Content="Save" Click="OnSave" IsDefault="True" />
</StackPanel>
<Border BorderThickness="1" BorderBrush="#40808080" CornerRadius="3" Margin="0,8,0,0">
<TextBox Name="TextArea" AcceptsReturn="True" FontFamily="monospace" FontSize="12"
BorderThickness="0" />
</Border>
</DockPanel>
</Window>
@@ -1,22 +0,0 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
namespace Nonemm.App.Dialogs;
/// The digital window's macro buttons, edited as their lines. It closes with
/// the text, or with null when nothing is to be changed.
public sealed partial class DigitalMacrosDialog : Window
{
public DigitalMacrosDialog(string text)
{
InitializeComponent();
TextArea.Text = text.Trim().Length > 0 ? text : Messages.DefaultDigital;
}
private void OnDefaults(object? sender, RoutedEventArgs e) =>
TextArea.Text = Messages.DefaultDigital;
private void OnCancel(object? sender, RoutedEventArgs e) => Close(null);
private void OnSave(object? sender, RoutedEventArgs e) => Close(TextArea.Text ?? "");
}