mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-26 21:57:32 +00:00
feat(dxcluster): споты DX-кластера на панадаптере
Telnet-клиент DX-кластера, база спотов и подписи позывных прямо на спектре на своих частотах. Разложено на три слоя, как бэндплан и лупа маяка. DXClusterClient.pas — один рабочий поток: резолв, неблокирующий connect с select квантами по 200 мс (Stop не ждёт таймаут соединения), логин позывным, чтение строк, реконнект с backoff. Приглашения логина И пароля ловятся в незавершённом хвосте буфера — типичный telnet-prompt приходит без CR/LF. Ошибки recv отличаются от таймаута кванта (EAGAIN/EINTR/WSAETIMEDOUT), иначе на ECONNRESET поток крутился бы в пустом цикле вместо реконнекта. Отправка дописывает частичный send. LCL-free. DXSpotStore.pas — потокобезопасная база: дедуп по позывному, TTL, потолок записей, монотонный Version. Единственная точка обмена потока с UI: никакого Synchronize, UI сам замечает правки по Version, как оверлеи — по ключам кэша. DXSpotOverlay.pas — рендер по модели BandPlanOverlay/VfoOverlay: кэшируется только полоса подписей (W × BandH) в key-color битмап, пересборка строго по dirty-ключу, на кадр — один keyed-композит. Штрихи от полосы до низа спектра рисует вызывающая сторона теми же примитивами, что и прочие маркеры: CPU — RawVLine внутри RawBegin/RawEnd, GL — DrawLine по готовому списку X/цвет. GL берёт тот же битмап текстурой и заливает её только при смене RenderVersion. Пересекающиеся подписи раскладываются лесенкой, цвет гаснет с возрастом, свой позывной выделен; палитра парная под тёмную и светлую тему. DXClusterForm.pas — окно списка: споты, лог соединения, строка команды кластеру (диалект set/filter у всех свой — не угадываем). Двойной клик или Enter = QSY. Данные тянутся поллингом по Version/LogVersion. Интеграция: кнопка DX в тулбаре (ЛКМ — подписи на спектре, ПКМ — окно), клик по подписи спота = QSY с автовыбором моды по комментарию кластера, страница SETUP → DX Cluster с персистом в секции "dxcluster". Правки SETUP прилетают посимвольно, поэтому запись конфига, TTL стора и переподключение откладываются до паузы в наборе — иначе набор позывного стоил бы шесть реконнектов, а промежуточный TTL «3» необратимо выбросил бы споты. Частота спота кладётся как есть и сравнивается с GetViewWindow: на QO-100 кластеры постят downlink 10489.xxx, что совпадает со шкалой пана само собой. Побочно в общих юнитах: WebUtils.SockSetRcvTimeout, FlatMemo.OnChange, FlatListBox.OnKeyDown, BlendBitmapKey вынесен в interface VfoOverlay (одна копия дворд-блендера на проект). Проверено на локальном фейковом кластере: логин и пароль по prompt без CR/LF, разбор спотов (включая QO-100), уход в RETRY по RST, Stop за 200 мс на висящем connect. На железе рендер не проверялся. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+258
-1
@@ -28,7 +28,7 @@ uses
|
||||
Forms, Controls, Graphics, Dialogs,
|
||||
StdCtrls, ExtCtrls, LCLType,
|
||||
FlatButton, FlatCheckBox, FlatComboBox, FlatEdit, FlatSpinEdit, FlatFloatSpinEdit,
|
||||
FlatRadioButton,
|
||||
FlatRadioButton, FlatMemo,
|
||||
EqualizerControl, OverlayScrollBar, AudioOutput, AudioInput, AppTheme, Settings,
|
||||
BoardUtils, DpiUtils;
|
||||
|
||||
@@ -86,6 +86,10 @@ type
|
||||
TOnThemeChange = procedure(LightTheme: Boolean) of object;
|
||||
TOnWfAGCNFChange = procedure(WfAGC, WfNF: Boolean) of object;
|
||||
TOnADCChange = procedure(Dither, Random: Boolean) of object;
|
||||
// Настройки DX-кластера уезжают наружу целиком записью: полей много и
|
||||
// добавлять их по одному в сигнатуру (как у веб-сервера) уже неудобно.
|
||||
TOnDXClusterChange = procedure(const D: TDXClusterSettings) of object;
|
||||
|
||||
TOnWebSettingsChange = procedure(Enabled: Boolean; Port: Integer;
|
||||
const BindAddr, User, Pass: string; SpecPixels: Integer) of object;
|
||||
TOnCATChange = procedure(
|
||||
@@ -124,6 +128,19 @@ type
|
||||
FPageWaterfall: TScrollBox;
|
||||
FPagePA: TScrollBox;
|
||||
FPageAdvanced: TScrollBox;
|
||||
FPageDXCluster: TScrollBox;
|
||||
// DX-кластер
|
||||
FChkDXEnabled: TFlatCheckBox;
|
||||
FChkDXShow: TFlatCheckBox;
|
||||
FEdDXHost: TFlatEdit;
|
||||
FEdDXPort: TFlatSpinEdit;
|
||||
FEdDXLogin: TFlatEdit;
|
||||
FEdDXPass: TFlatEdit;
|
||||
FEdDXPostLogin: TFlatMemo;
|
||||
FEdDXTTL: TFlatSpinEdit;
|
||||
FEdDXRows: TFlatSpinEdit;
|
||||
FChkDXMode: array[0..9] of TFlatCheckBox; // индекс = Ord(TDXMode)
|
||||
FDXCfg: TDXClusterSettings;
|
||||
FPageCAT: TScrollBox;
|
||||
FPageSlices: TScrollBox;
|
||||
FPageTransmit: TScrollBox;
|
||||
@@ -133,6 +150,7 @@ type
|
||||
FNavWaterfall: TFlatButton;
|
||||
FNavPA: TFlatButton;
|
||||
FNavAdvanced: TFlatButton;
|
||||
FNavDXCluster: TFlatButton;
|
||||
FNavCAT: TFlatButton;
|
||||
FNavSlices: TFlatButton;
|
||||
FNavTransmit: TFlatButton;
|
||||
@@ -465,6 +483,7 @@ type
|
||||
FOnVHFCalChange: TOnVHFCalChange;
|
||||
FOnADCChange: TOnADCChange;
|
||||
FOnWebSettingsChange: TOnWebSettingsChange;
|
||||
FOnDXClusterChange: TOnDXClusterChange;
|
||||
FOnDisplayChange: TOnDisplayParamChange;
|
||||
FOnWaterfallChange: TOnWaterfallParamChange;
|
||||
FOnWfRenderChange: TOnWfRenderChange;
|
||||
@@ -572,6 +591,8 @@ type
|
||||
OnChange: TNotifyEvent): TFlatComboBox;
|
||||
function MakeGroupPanel(AParent: TWinControl; const Cap: string;
|
||||
ALeft, ATop, AW, AH: Integer): TPanel;
|
||||
procedure BuildDXClusterTab;
|
||||
procedure OnDXAnyChange(Sender: TObject);
|
||||
function MakeScrollPage: TScrollBox;
|
||||
procedure AddPageBottomSpace(APage: TScrollBox);
|
||||
procedure UpdatePageBottomSpace(APage: TScrollBox);
|
||||
@@ -696,6 +717,7 @@ type
|
||||
ResLimit, SpeedDiv: Integer);
|
||||
procedure LoadSpecMSAA(Samples: Integer);
|
||||
procedure LoadADCSettings(Dither, Random: Boolean);
|
||||
procedure LoadDXClusterSettings(const D: TDXClusterSettings);
|
||||
procedure LoadWebSettings(Enabled: Boolean; Port: Integer;
|
||||
const BindAddr, User, Pass: string; SpecPixels: Integer);
|
||||
|
||||
@@ -734,6 +756,7 @@ type
|
||||
property OnWfAGCNFChange: TOnWfAGCNFChange read FOnWfAGCNFChange write FOnWfAGCNFChange;
|
||||
property OnADCChange: TOnADCChange read FOnADCChange write FOnADCChange;
|
||||
property OnWebSettingsChange: TOnWebSettingsChange read FOnWebSettingsChange write FOnWebSettingsChange;
|
||||
property OnDXClusterChange: TOnDXClusterChange read FOnDXClusterChange write FOnDXClusterChange;
|
||||
end;
|
||||
|
||||
implementation
|
||||
@@ -1113,6 +1136,7 @@ begin
|
||||
FNavCAT := MakeNavButton('CAT', 496);
|
||||
FNavSlices := MakeNavButton('Slices', 528);
|
||||
FNavAdvanced := MakeNavButton('Advanced', 560);
|
||||
FNavDXCluster := MakeNavButton('DX Cluster', 592);
|
||||
|
||||
FContentPanel := TPanel.Create(Self);
|
||||
FContentPanel.Parent := Self;
|
||||
@@ -1135,6 +1159,7 @@ begin
|
||||
FPagePA := MakeScrollPage;
|
||||
FPageCalib := MakeScrollPage;
|
||||
FPageAdvanced := MakeScrollPage;
|
||||
FPageDXCluster := MakeScrollPage;
|
||||
FPageCAT := MakeScrollPage;
|
||||
FPageSlices := MakeScrollPage;
|
||||
FPageAlex := MakeScrollPage;
|
||||
@@ -1154,6 +1179,7 @@ begin
|
||||
BuildPATab;
|
||||
BuildCalibrationTab;
|
||||
BuildAdvancedTab;
|
||||
BuildDXClusterTab;
|
||||
BuildCATTab;
|
||||
BuildSlicesTab;
|
||||
BuildAlexTab;
|
||||
@@ -1168,6 +1194,7 @@ begin
|
||||
AddPageBottomSpace(FPageWaterfall);
|
||||
AddPageBottomSpace(FPageCalib);
|
||||
AddPageBottomSpace(FPageCAT);
|
||||
AddPageBottomSpace(FPageDXCluster);
|
||||
AddPageBottomSpace(FPageOC);
|
||||
|
||||
FBtnClose := TFlatButton.Create(Self);
|
||||
@@ -1390,6 +1417,7 @@ begin
|
||||
ResizePage(FPagePA, CardWidth);
|
||||
ResizePage(FPageCalib, CardWidth);
|
||||
ResizePage(FPageAdvanced, CardWidth);
|
||||
ResizePage(FPageDXCluster, CardWidth);
|
||||
ResizePage(FPageCAT, CardWidth);
|
||||
ResizePage(FPageSlices, CardWidth);
|
||||
ResizePage(FPageAlex, CardWidth);
|
||||
@@ -1408,6 +1436,7 @@ begin
|
||||
else if Sender = FNavPA then SelectPage(FPagePA, FNavPA)
|
||||
else if Sender = FNavCalib then SelectPage(FPageCalib, FNavCalib)
|
||||
else if Sender = FNavAdvanced then SelectPage(FPageAdvanced, FNavAdvanced)
|
||||
else if Sender = FNavDXCluster then SelectPage(FPageDXCluster, FNavDXCluster)
|
||||
else if Sender = FNavCAT then SelectPage(FPageCAT, FNavCAT)
|
||||
else if Sender = FNavSlices then SelectPage(FPageSlices, FNavSlices)
|
||||
else if Sender = FNavAlex then SelectPage(FPageAlex, FNavAlex)
|
||||
@@ -1429,6 +1458,7 @@ begin
|
||||
FPagePA.Visible := APage = FPagePA;
|
||||
FPageCalib.Visible := APage = FPageCalib;
|
||||
FPageAdvanced.Visible := APage = FPageAdvanced;
|
||||
FPageDXCluster.Visible := APage = FPageDXCluster;
|
||||
FPageCAT.Visible := APage = FPageCAT;
|
||||
FPageSlices.Visible := APage = FPageSlices;
|
||||
FPageAlex.Visible := APage = FPageAlex;
|
||||
@@ -1444,6 +1474,7 @@ begin
|
||||
FNavPA.Active := ANav = FNavPA;
|
||||
FNavCalib.Active := ANav = FNavCalib;
|
||||
FNavAdvanced.Active := ANav = FNavAdvanced;
|
||||
FNavDXCluster.Active := ANav = FNavDXCluster;
|
||||
FNavCAT.Active := ANav = FNavCAT;
|
||||
FNavSlices.Active := ANav = FNavSlices;
|
||||
FNavAlex.Active := ANav = FNavAlex;
|
||||
@@ -3412,6 +3443,227 @@ begin
|
||||
Lbl.Font.Size := 8;
|
||||
end;
|
||||
|
||||
procedure TSettingsForm.BuildDXClusterTab;
|
||||
const
|
||||
MARGIN = 22;
|
||||
GRP_PAD = 18;
|
||||
LBL_W = 110;
|
||||
ED_W = 220;
|
||||
ROW_H = 42;
|
||||
R1 = 42;
|
||||
// Подписи для чекбоксов фильтра мод. Порядок = Ord(TDXMode), включая
|
||||
// dxmUnknown («без моды») — иначе фильтр молча съедал бы такие споты.
|
||||
MODE_CAPS: array[0..9] of string =
|
||||
('no mode', 'CW', 'SSB', 'DIGI', 'FT8', 'FT4', 'RTTY', 'PSK', 'FM', 'SSTV');
|
||||
var
|
||||
Grp: TPanel;
|
||||
Chk: TFlatCheckBox;
|
||||
Ed: TFlatEdit;
|
||||
Spin: TFlatSpinEdit;
|
||||
Lbl: TLabel;
|
||||
Y, i, CX, CY: Integer;
|
||||
begin
|
||||
TSettingsManager.DefaultDXCluster(FDXCfg);
|
||||
|
||||
MakePageHeader(FPageDXCluster, 'DX Cluster',
|
||||
'Telnet connection to a DX cluster and how spots look on the panadapter.');
|
||||
|
||||
// ── Соединение ────────────────────────────────────────────────────────────
|
||||
Grp := MakeGroupPanel(FPageDXCluster, 'Connection', MARGIN, 86, SETTINGS_CARD_W, 366);
|
||||
|
||||
Y := R1;
|
||||
Chk := TFlatCheckBox.Create(Self);
|
||||
Chk.Parent := Grp;
|
||||
Chk.Caption := 'Connect at startup';
|
||||
Chk.SetBounds(DpiScale(GRP_PAD), DpiScale(Y), DpiScale(300), DpiScale(22));
|
||||
Chk.Font.Size := 9;
|
||||
Chk.Font.Color := CLR_TEXT;
|
||||
Chk.OnChange := OnDXAnyChange;
|
||||
FChkDXEnabled := Chk;
|
||||
|
||||
Y := Y + ROW_H;
|
||||
MakeLbl(Grp, 'Host:', GRP_PAD, Y + 4, LBL_W);
|
||||
Ed := TFlatEdit.Create(Self);
|
||||
Ed.Parent := Grp;
|
||||
Ed.SetBounds(DpiScale(GRP_PAD + LBL_W + 12), DpiScale(Y), DpiScale(ED_W), DpiScale(BTN_H));
|
||||
Ed.Color := CLR_INPUT;
|
||||
Ed.Font.Color := CLR_INPUT_TEXT;
|
||||
Ed.Font.Size := 9;
|
||||
Ed.Text := FDXCfg.Host;
|
||||
Ed.OnChange := OnDXAnyChange;
|
||||
FEdDXHost := Ed;
|
||||
|
||||
Y := Y + ROW_H;
|
||||
MakeLbl(Grp, 'Port:', GRP_PAD, Y + 4, LBL_W);
|
||||
Spin := TFlatSpinEdit.Create(Self);
|
||||
Spin.Parent := Grp;
|
||||
Spin.SetBounds(DpiScale(GRP_PAD + LBL_W + 12), DpiScale(Y), DpiScale(90), DpiScale(BTN_H));
|
||||
Spin.Color := CLR_INPUT;
|
||||
Spin.Font.Color := CLR_INPUT_TEXT;
|
||||
Spin.Font.Size := 9;
|
||||
Spin.MinValue := 1;
|
||||
Spin.MaxValue := 65535;
|
||||
Spin.Value := FDXCfg.Port;
|
||||
Spin.OnChange := OnDXAnyChange;
|
||||
FEdDXPort := Spin;
|
||||
|
||||
Y := Y + ROW_H;
|
||||
MakeLbl(Grp, 'Callsign:', GRP_PAD, Y + 4, LBL_W);
|
||||
Ed := TFlatEdit.Create(Self);
|
||||
Ed.Parent := Grp;
|
||||
Ed.SetBounds(DpiScale(GRP_PAD + LBL_W + 12), DpiScale(Y), DpiScale(140), DpiScale(BTN_H));
|
||||
Ed.Color := CLR_INPUT;
|
||||
Ed.Font.Color := CLR_INPUT_TEXT;
|
||||
Ed.Font.Size := 9;
|
||||
Ed.OnChange := OnDXAnyChange;
|
||||
FEdDXLogin := Ed;
|
||||
Lbl := MakeLbl(Grp, 'used to log in; spots for this call are highlighted',
|
||||
GRP_PAD + LBL_W + 12 + 150, Y + 4, 340);
|
||||
Lbl.Font.Size := 8;
|
||||
|
||||
Y := Y + ROW_H;
|
||||
MakeLbl(Grp, 'Password:', GRP_PAD, Y + 4, LBL_W);
|
||||
Ed := TFlatEdit.Create(Self);
|
||||
Ed.Parent := Grp;
|
||||
Ed.SetBounds(DpiScale(GRP_PAD + LBL_W + 12), DpiScale(Y), DpiScale(140), DpiScale(BTN_H));
|
||||
Ed.Color := CLR_INPUT;
|
||||
Ed.Font.Color := CLR_INPUT_TEXT;
|
||||
Ed.Font.Size := 9;
|
||||
Ed.PasswordChar := '*';
|
||||
Ed.OnChange := OnDXAnyChange;
|
||||
FEdDXPass := Ed;
|
||||
Lbl := MakeLbl(Grp, 'usually not needed', GRP_PAD + LBL_W + 12 + 150, Y + 4, 200);
|
||||
Lbl.Font.Size := 8;
|
||||
|
||||
Y := Y + ROW_H;
|
||||
MakeLbl(Grp, 'After login:', GRP_PAD, Y + 4, LBL_W);
|
||||
FEdDXPostLogin := TFlatMemo.Create(Self);
|
||||
FEdDXPostLogin.Parent := Grp;
|
||||
FEdDXPostLogin.SetBounds(DpiScale(GRP_PAD + LBL_W + 12), DpiScale(Y),
|
||||
DpiScale(ED_W + 120), DpiScale(70));
|
||||
FEdDXPostLogin.Color := CLR_INPUT;
|
||||
FEdDXPostLogin.Font.Color := CLR_INPUT_TEXT;
|
||||
FEdDXPostLogin.Font.Size := 9;
|
||||
FEdDXPostLogin.WordWrap := False;
|
||||
FEdDXPostLogin.OnChange := OnDXAnyChange;
|
||||
Lbl := MakeLbl(Grp, 'one command per line (set/filter, sh/dx …) — dialects differ between clusters',
|
||||
GRP_PAD, Y + 76, 520);
|
||||
Lbl.Font.Size := 8;
|
||||
|
||||
// ── Отображение ───────────────────────────────────────────────────────────
|
||||
Grp := MakeGroupPanel(FPageDXCluster, 'Spots on panadapter', MARGIN, 468,
|
||||
SETTINGS_CARD_W, 292);
|
||||
|
||||
Y := R1;
|
||||
Chk := TFlatCheckBox.Create(Self);
|
||||
Chk.Parent := Grp;
|
||||
Chk.Caption := 'Show spots on the spectrum';
|
||||
Chk.SetBounds(DpiScale(GRP_PAD), DpiScale(Y), DpiScale(300), DpiScale(22));
|
||||
Chk.Font.Size := 9;
|
||||
Chk.Font.Color := CLR_TEXT;
|
||||
Chk.OnChange := OnDXAnyChange;
|
||||
FChkDXShow := Chk;
|
||||
|
||||
Y := Y + ROW_H;
|
||||
MakeLbl(Grp, 'Keep for, min:', GRP_PAD, Y + 4, LBL_W);
|
||||
Spin := TFlatSpinEdit.Create(Self);
|
||||
Spin.Parent := Grp;
|
||||
Spin.SetBounds(DpiScale(GRP_PAD + LBL_W + 12), DpiScale(Y), DpiScale(90), DpiScale(BTN_H));
|
||||
Spin.Color := CLR_INPUT;
|
||||
Spin.Font.Color := CLR_INPUT_TEXT;
|
||||
Spin.Font.Size := 9;
|
||||
Spin.MinValue := 1;
|
||||
Spin.MaxValue := 1440;
|
||||
Spin.Value := FDXCfg.TTLMinutes;
|
||||
Spin.OnChange := OnDXAnyChange;
|
||||
FEdDXTTL := Spin;
|
||||
Lbl := MakeLbl(Grp, 'also the fade horizon: a spot dims and then disappears',
|
||||
GRP_PAD + LBL_W + 114, Y + 4, 360);
|
||||
Lbl.Font.Size := 8;
|
||||
|
||||
Y := Y + ROW_H;
|
||||
MakeLbl(Grp, 'Label rows:', GRP_PAD, Y + 4, LBL_W);
|
||||
Spin := TFlatSpinEdit.Create(Self);
|
||||
Spin.Parent := Grp;
|
||||
Spin.SetBounds(DpiScale(GRP_PAD + LBL_W + 12), DpiScale(Y), DpiScale(90), DpiScale(BTN_H));
|
||||
Spin.Color := CLR_INPUT;
|
||||
Spin.Font.Color := CLR_INPUT_TEXT;
|
||||
Spin.Font.Size := 9;
|
||||
Spin.MinValue := 1;
|
||||
Spin.MaxValue := 6;
|
||||
Spin.Value := FDXCfg.MaxRows;
|
||||
Spin.OnChange := OnDXAnyChange;
|
||||
FEdDXRows := Spin;
|
||||
Lbl := MakeLbl(Grp, 'ladder height: more rows means fewer hidden spots',
|
||||
GRP_PAD + LBL_W + 114, Y + 4, 360);
|
||||
Lbl.Font.Size := 8;
|
||||
|
||||
Y := Y + ROW_H;
|
||||
MakeLbl(Grp, 'Modes:', GRP_PAD, Y + 4, LBL_W);
|
||||
CX := GRP_PAD + LBL_W + 12;
|
||||
CY := Y;
|
||||
for i := 0 to High(FChkDXMode) do
|
||||
begin
|
||||
Chk := TFlatCheckBox.Create(Self);
|
||||
Chk.Parent := Grp;
|
||||
Chk.Caption := MODE_CAPS[i];
|
||||
Chk.SetBounds(DpiScale(CX), DpiScale(CY), DpiScale(90), DpiScale(22));
|
||||
Chk.Font.Size := 9;
|
||||
Chk.Font.Color := CLR_TEXT;
|
||||
Chk.OnChange := OnDXAnyChange;
|
||||
FChkDXMode[i] := Chk;
|
||||
CX := CX + 96;
|
||||
if CX > 480 then begin CX := GRP_PAD + LBL_W + 12; CY := CY + 26; end;
|
||||
end;
|
||||
Lbl := MakeLbl(Grp, 'none checked — every spot is shown',
|
||||
GRP_PAD + LBL_W + 12, CY + 28, 360);
|
||||
Lbl.Font.Size := 8;
|
||||
end;
|
||||
|
||||
procedure TSettingsForm.OnDXAnyChange(Sender: TObject);
|
||||
var i: Integer;
|
||||
begin
|
||||
if FLoading then Exit;
|
||||
if not Assigned(FOnDXClusterChange) then Exit;
|
||||
FDXCfg.Enabled := FChkDXEnabled.Checked;
|
||||
FDXCfg.ShowSpots := FChkDXShow.Checked;
|
||||
FDXCfg.Host := Trim(FEdDXHost.Text);
|
||||
FDXCfg.Port := FEdDXPort.Value;
|
||||
FDXCfg.Login := Trim(FEdDXLogin.Text);
|
||||
FDXCfg.Password := FEdDXPass.Text;
|
||||
FDXCfg.PostLogin := FEdDXPostLogin.Text;
|
||||
FDXCfg.TTLMinutes := FEdDXTTL.Value;
|
||||
FDXCfg.MaxRows := FEdDXRows.Value;
|
||||
FDXCfg.ModeMask := 0;
|
||||
for i := 0 to High(FChkDXMode) do
|
||||
if (FChkDXMode[i] <> nil) and FChkDXMode[i].Checked then
|
||||
FDXCfg.ModeMask := FDXCfg.ModeMask or (LongWord(1) shl i);
|
||||
FOnDXClusterChange(FDXCfg);
|
||||
end;
|
||||
|
||||
procedure TSettingsForm.LoadDXClusterSettings(const D: TDXClusterSettings);
|
||||
var i: Integer;
|
||||
begin
|
||||
FLoading := True;
|
||||
try
|
||||
FDXCfg := D;
|
||||
FChkDXEnabled.Checked := D.Enabled;
|
||||
FChkDXShow.Checked := D.ShowSpots;
|
||||
FEdDXHost.Text := D.Host;
|
||||
FEdDXPort.Value := EnsureRange(D.Port, 1, 65535);
|
||||
FEdDXLogin.Text := D.Login;
|
||||
FEdDXPass.Text := D.Password;
|
||||
FEdDXPostLogin.Text := D.PostLogin;
|
||||
FEdDXTTL.Value := EnsureRange(D.TTLMinutes, 1, 1440);
|
||||
FEdDXRows.Value := EnsureRange(D.MaxRows, 1, 6);
|
||||
for i := 0 to High(FChkDXMode) do
|
||||
if FChkDXMode[i] <> nil then
|
||||
FChkDXMode[i].Checked := (D.ModeMask and (LongWord(1) shl i)) <> 0;
|
||||
finally
|
||||
FLoading := False;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TSettingsForm.OnADCChkChange(Sender: TObject);
|
||||
begin
|
||||
if FLoading then Exit;
|
||||
@@ -3540,6 +3792,11 @@ procedure TSettingsForm.ApplyTheme(const T: TAppTheme);
|
||||
TFlatFloatSpinEdit(Ctrl).SetAppTheme(T)
|
||||
else if Ctrl is TFlatRadioButton then
|
||||
TFlatRadioButton(Ctrl).SetAppTheme(T)
|
||||
// TFlatMemo — обёртка над TMemo со своим scrollbar: тему знает сама,
|
||||
// рекурсивный обход внутрь ей только навредил бы (внутренний TMemo не
|
||||
// подходит ни под одну ветку и остался бы серым).
|
||||
else if Ctrl is TFlatMemo then
|
||||
TFlatMemo(Ctrl).SetAppTheme(T)
|
||||
else if Ctrl is TListBox then
|
||||
begin
|
||||
TListBox(Ctrl).Color := T.BG;
|
||||
|
||||
Reference in New Issue
Block a user