mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 20:37:33 +00:00
add Alex setup
This commit is contained in:
+348
-11
@@ -54,6 +54,7 @@ type
|
||||
const SerStopBits: array of Integer;
|
||||
const SerParity: array of Integer;
|
||||
TcpEnabled: Boolean; TcpPort: Integer) of object;
|
||||
TOnAlexChange = procedure(const A: TAlexSettings) of object;
|
||||
|
||||
{ TSettingsForm }
|
||||
TSettingsForm = class(TForm)
|
||||
@@ -176,6 +177,29 @@ type
|
||||
FEdTXRange: TEdit;
|
||||
FCmbTXGridStep: TComboBox;
|
||||
|
||||
// ---- Alex / Antenna tab controls ----
|
||||
FPageAlex: TScrollBox;
|
||||
FNavAlex: TFlatButton;
|
||||
// Per-band antenna radio buttons (bands 0..10 = 160m..6m)
|
||||
FAlexRxAnt: array[0..10, 1..3] of TRadioButton;
|
||||
FAlexTxAnt: array[0..10, 1..3] of TRadioButton;
|
||||
// Per-band secondary RX (взаимно исключающие checkboxes)
|
||||
FAlexRxByps: array[0..10] of TCheckBox; // BYPS: Ext1+Bypass
|
||||
FAlexRxExt1: array[0..10] of TCheckBox; // EXT1: Ext2+Bypass
|
||||
FAlexRxXvtr: array[0..10] of TCheckBox; // XVTR: XvtrDDC+Bypass
|
||||
// Per-band Do Not TX
|
||||
FAlexDoNotTx: array[0..10] of TCheckBox;
|
||||
// Global TX routing flags
|
||||
FAlexRxBypassOnTx: TCheckBox;
|
||||
FAlexExt1OnTx: TCheckBox;
|
||||
FAlexGndBpf2OnTx: TCheckBox;
|
||||
FAlexEnableXvtrHf: TCheckBox;
|
||||
// Current Alex config state
|
||||
FAlexConfig: TAlexSettings;
|
||||
FAlexBoardType: Integer;
|
||||
// Callback
|
||||
FOnAlexChange: TOnAlexChange;
|
||||
|
||||
// ---- CAT tab controls ----
|
||||
FCATSerialEn: array[0..3] of TCheckBox;
|
||||
FCATSerialPort: array[0..3] of TEdit;
|
||||
@@ -211,6 +235,11 @@ type
|
||||
procedure BuildPATab;
|
||||
procedure BuildCATTab;
|
||||
procedure BuildTransmitTab;
|
||||
procedure BuildAlexTab;
|
||||
procedure CollectAlexFromUI;
|
||||
procedure FireAlexChange;
|
||||
procedure OnAlexAnyChange(Sender: TObject);
|
||||
procedure OnAlexRxOnlyChange(Sender: TObject);
|
||||
procedure CollectTXFromUI;
|
||||
procedure FireTXChange;
|
||||
procedure OnTXAnyChange(Sender: TObject);
|
||||
@@ -288,8 +317,10 @@ type
|
||||
const SerBaud, SerDataBits, SerStopBits, SerParity: array of Integer;
|
||||
TcpEnabled: Boolean; TcpPort: Integer);
|
||||
procedure LoadTXSettings(const T: TTXSettings);
|
||||
procedure LoadAlexSettings(const A: TAlexSettings; BoardType: Integer);
|
||||
|
||||
property OnCATChange: TOnCATChange read FOnCATChange write FOnCATChange;
|
||||
property OnAlexChange: TOnAlexChange read FOnAlexChange write FOnAlexChange;
|
||||
property OnPAChange: TOnPASettingsChange read FOnPAChange write FOnPAChange;
|
||||
property OnDisplayChange: TOnDisplayParamChange read FOnDisplayChange write FOnDisplayChange;
|
||||
property OnWaterfallChange: TOnWaterfallParamChange read FOnWaterfallChange write FOnWaterfallChange;
|
||||
@@ -508,6 +539,7 @@ begin
|
||||
FNavPA := MakeNavButton('Power Amplifier', 254);
|
||||
FNavAdvanced := MakeNavButton('Advanced', 290);
|
||||
FNavCAT := MakeNavButton('CAT', 326);
|
||||
FNavAlex := MakeNavButton('Antenna / Alex', 362);
|
||||
|
||||
FContentPanel := TPanel.Create(Self);
|
||||
FContentPanel.Parent := Self;
|
||||
@@ -523,6 +555,9 @@ begin
|
||||
FPagePA := MakeScrollPage;
|
||||
FPageAdvanced := MakeScrollPage;
|
||||
FPageCAT := MakeScrollPage;
|
||||
FPageAlex := MakeScrollPage;
|
||||
FAlexBoardType := 0;
|
||||
TSettingsManager.DefaultAlex(FAlexConfig);
|
||||
|
||||
BuildAudioTab;
|
||||
BuildDisplayTab;
|
||||
@@ -530,6 +565,7 @@ begin
|
||||
BuildTransmitTab;
|
||||
BuildPATab;
|
||||
BuildCATTab;
|
||||
BuildAlexTab;
|
||||
|
||||
FBtnClose := TFlatButton.Create(Self);
|
||||
FBtnClose.Parent := Self;
|
||||
@@ -558,28 +594,31 @@ begin
|
||||
else if Sender = FNavTransmit then SelectPage(FPageTransmit, FNavTransmit)
|
||||
else if Sender = FNavPA then SelectPage(FPagePA, FNavPA)
|
||||
else if Sender = FNavAdvanced then SelectPage(FPageAdvanced, FNavAdvanced)
|
||||
else if Sender = FNavCAT then SelectPage(FPageCAT, FNavCAT);
|
||||
else if Sender = FNavCAT then SelectPage(FPageCAT, FNavCAT)
|
||||
else if Sender = FNavAlex then SelectPage(FPageAlex, FNavAlex);
|
||||
end;
|
||||
|
||||
procedure TSettingsForm.SelectPage(APage: TWinControl; ANav: TFlatButton);
|
||||
begin
|
||||
FPageAudio.Visible := APage = FPageAudio;
|
||||
FPageDisplay.Visible := APage = FPageDisplay;
|
||||
FPageAudio.Visible := APage = FPageAudio;
|
||||
FPageDisplay.Visible := APage = FPageDisplay;
|
||||
FPageSpectrum.Visible := APage = FPageSpectrum;
|
||||
FPageWaterfall.Visible := APage = FPageWaterfall;
|
||||
FPageWaterfall.Visible:= APage = FPageWaterfall;
|
||||
FPageTransmit.Visible := APage = FPageTransmit;
|
||||
FPagePA.Visible := APage = FPagePA;
|
||||
FPagePA.Visible := APage = FPagePA;
|
||||
FPageAdvanced.Visible := APage = FPageAdvanced;
|
||||
FPageCAT.Visible := APage = FPageCAT;
|
||||
FPageCAT.Visible := APage = FPageCAT;
|
||||
FPageAlex.Visible := APage = FPageAlex;
|
||||
|
||||
FNavAudio.Active := ANav = FNavAudio;
|
||||
FNavDisplay.Active := ANav = FNavDisplay;
|
||||
FNavAudio.Active := ANav = FNavAudio;
|
||||
FNavDisplay.Active := ANav = FNavDisplay;
|
||||
FNavSpectrum.Active := ANav = FNavSpectrum;
|
||||
FNavWaterfall.Active := ANav = FNavWaterfall;
|
||||
FNavWaterfall.Active:= ANav = FNavWaterfall;
|
||||
FNavTransmit.Active := ANav = FNavTransmit;
|
||||
FNavPA.Active := ANav = FNavPA;
|
||||
FNavPA.Active := ANav = FNavPA;
|
||||
FNavAdvanced.Active := ANav = FNavAdvanced;
|
||||
FNavCAT.Active := ANav = FNavCAT;
|
||||
FNavCAT.Active := ANav = FNavCAT;
|
||||
FNavAlex.Active := ANav = FNavAlex;
|
||||
end;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -2194,4 +2233,302 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// BuildAlexTab
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
procedure TSettingsForm.BuildAlexTab;
|
||||
const
|
||||
MARGIN = 22;
|
||||
PAD = 12;
|
||||
HDR1_H = 22; // height of "RX Ant / TX Ant" header row
|
||||
HDR2_H = 20; // height of "1 2 3 / BYPS EXT1 XVTR" sub-header row
|
||||
ROW_H = 24;
|
||||
BANDS = 11;
|
||||
BAND_X = PAD;
|
||||
RX1_X = PAD + 50;
|
||||
RX2_X = PAD + 76;
|
||||
RX3_X = PAD + 102;
|
||||
BYPS_X = PAD + 136;
|
||||
EXT1_X = PAD + 192;
|
||||
XVTR_X = PAD + 248;
|
||||
TX1_X = PAD + 308;
|
||||
TX2_X = PAD + 334;
|
||||
TX3_X = PAD + 360;
|
||||
DNTX_X = PAD + 398;
|
||||
GRP_W = DNTX_X + 80;
|
||||
GRP_H = GRP_TITLE_H + HDR1_H + HDR2_H + BANDS * ROW_H + PAD;
|
||||
DATA_Y0 = GRP_TITLE_H + HDR1_H + HDR2_H + 4;
|
||||
TXGRP_H = GRP_TITLE_H + 4 * 28 + PAD;
|
||||
|
||||
BAND_NAMES: array[0..10] of string =
|
||||
('160m','80m','60m','40m','30m','20m','17m','15m','12m','10m','6m');
|
||||
|
||||
var
|
||||
Grp, GrpTX: TPanel;
|
||||
b: Integer;
|
||||
Y: Integer;
|
||||
Chk: TCheckBox;
|
||||
RxGrp, TxGrp: TPanel;
|
||||
|
||||
// In Lazarus, TRadioButton has no GroupIndex; buttons are grouped by parent.
|
||||
// We use small invisible panels as parents so each band's RX and TX groups
|
||||
// are independent of each other.
|
||||
function MkRbGroup(AParent: TWinControl; AX, AY, AW: Integer): TPanel;
|
||||
begin
|
||||
Result := TPanel.Create(Self);
|
||||
Result.Parent := AParent;
|
||||
Result.SetBounds(AX, AY, AW, 20);
|
||||
Result.BevelOuter := bvNone;
|
||||
Result.Color := CLR_PANEL;
|
||||
Result.Caption := '';
|
||||
end;
|
||||
|
||||
function MkRb(AParent: TWinControl; AX: Integer): TRadioButton;
|
||||
begin
|
||||
Result := TRadioButton.Create(Self);
|
||||
Result.Parent := AParent;
|
||||
Result.Caption := '';
|
||||
Result.SetBounds(AX, 0, 22, 20);
|
||||
Result.Font.Color := CLR_TEXT;
|
||||
Result.Font.Name := UI_FONT;
|
||||
Result.Font.Size := 9;
|
||||
Result.OnChange := OnAlexAnyChange;
|
||||
end;
|
||||
|
||||
function MkCh(AParent: TWinControl; const ACap: string;
|
||||
AX, AY, AW: Integer): TCheckBox;
|
||||
begin
|
||||
Result := TCheckBox.Create(Self);
|
||||
Result.Parent := AParent;
|
||||
Result.Caption := ACap;
|
||||
Result.SetBounds(AX, AY, AW, 20);
|
||||
Result.Font.Color := CLR_TEXT;
|
||||
Result.Font.Name := UI_FONT;
|
||||
Result.Font.Size := 9;
|
||||
Result.OnChange := OnAlexAnyChange;
|
||||
end;
|
||||
|
||||
begin
|
||||
FLoading := True;
|
||||
try
|
||||
with TLabel.Create(Self) do
|
||||
begin
|
||||
Parent := FPageAlex;
|
||||
Caption := 'Antenna / Alex';
|
||||
SetBounds(MARGIN, 18, 500, 28);
|
||||
Font.Name := UI_FONT; Font.Size := 16;
|
||||
Font.Style := [fsBold]; Font.Color := CLR_TEXT;
|
||||
end;
|
||||
with TLabel.Create(Self) do
|
||||
begin
|
||||
Parent := FPageAlex;
|
||||
Caption := 'Per-band RX/TX antenna and secondary path routing for the Alex filter board.';
|
||||
SetBounds(MARGIN, 48, 680, 20);
|
||||
Font.Name := UI_FONT; Font.Size := 9; Font.Color := CLR_TEXTDIM;
|
||||
end;
|
||||
|
||||
// ---- Antenna grid group ----
|
||||
Grp := MakeGroupPanel(FPageAlex, 'Antenna Selection', MARGIN, 86, GRP_W, GRP_H);
|
||||
|
||||
// Header row 1 — group labels
|
||||
MakeLbl(Grp, 'Band', BAND_X, GRP_TITLE_H + 2, 44);
|
||||
MakeLbl(Grp, 'RX Antenna', RX1_X, GRP_TITLE_H + 2, 80);
|
||||
MakeLbl(Grp, 'Secondary RX', BYPS_X, GRP_TITLE_H + 2, 170);
|
||||
MakeLbl(Grp, 'TX Antenna', TX1_X, GRP_TITLE_H + 2, 80);
|
||||
MakeLbl(Grp, 'No TX', DNTX_X, GRP_TITLE_H + 2, 56);
|
||||
|
||||
// Header row 2 — sub-column labels
|
||||
MakeLbl(Grp, '1', RX1_X + 3, GRP_TITLE_H + HDR1_H + 2, 20);
|
||||
MakeLbl(Grp, '2', RX2_X + 3, GRP_TITLE_H + HDR1_H + 2, 20);
|
||||
MakeLbl(Grp, '3', RX3_X + 3, GRP_TITLE_H + HDR1_H + 2, 20);
|
||||
MakeLbl(Grp, 'BYPS', BYPS_X, GRP_TITLE_H + HDR1_H + 2, 52);
|
||||
MakeLbl(Grp, 'EXT1', EXT1_X, GRP_TITLE_H + HDR1_H + 2, 52);
|
||||
MakeLbl(Grp, 'XVTR', XVTR_X, GRP_TITLE_H + HDR1_H + 2, 52);
|
||||
MakeLbl(Grp, '1', TX1_X + 3, GRP_TITLE_H + HDR1_H + 2, 20);
|
||||
MakeLbl(Grp, '2', TX2_X + 3, GRP_TITLE_H + HDR1_H + 2, 20);
|
||||
MakeLbl(Grp, '3', TX3_X + 3, GRP_TITLE_H + HDR1_H + 2, 20);
|
||||
|
||||
// Data rows (bands 0..10 = 160m..6m)
|
||||
for b := 0 to BANDS - 1 do
|
||||
begin
|
||||
Y := DATA_Y0 + b * ROW_H;
|
||||
MakeLbl(Grp, BAND_NAMES[b], BAND_X, Y + 2, 44);
|
||||
|
||||
// RX antenna — 3 radio buttons in a dedicated panel so they are mutually
|
||||
// exclusive only within that band row (Lazarus groups by parent, not GroupIndex)
|
||||
RxGrp := MkRbGroup(Grp, RX1_X, Y, RX3_X - RX1_X + 26);
|
||||
FAlexRxAnt[b, 1] := MkRb(RxGrp, 0);
|
||||
FAlexRxAnt[b, 2] := MkRb(RxGrp, RX2_X - RX1_X);
|
||||
FAlexRxAnt[b, 3] := MkRb(RxGrp, RX3_X - RX1_X);
|
||||
FAlexRxAnt[b, 1].Checked := True;
|
||||
|
||||
// Secondary RX checkboxes — mutually exclusive, handled in OnAlexRxOnlyChange
|
||||
// Tag encodes: b=BYPS, b+100=EXT1, b+200=XVTR
|
||||
Chk := MkCh(Grp, '', BYPS_X, Y, 22);
|
||||
Chk.Tag := b;
|
||||
Chk.OnChange := OnAlexRxOnlyChange;
|
||||
FAlexRxByps[b] := Chk;
|
||||
|
||||
Chk := MkCh(Grp, '', EXT1_X, Y, 22);
|
||||
Chk.Tag := b + 100;
|
||||
Chk.OnChange := OnAlexRxOnlyChange;
|
||||
FAlexRxExt1[b] := Chk;
|
||||
|
||||
Chk := MkCh(Grp, '', XVTR_X, Y, 22);
|
||||
Chk.Tag := b + 200;
|
||||
Chk.OnChange := OnAlexRxOnlyChange;
|
||||
FAlexRxXvtr[b] := Chk;
|
||||
|
||||
// TX antenna — same pattern: dedicated panel per band row
|
||||
TxGrp := MkRbGroup(Grp, TX1_X, Y, TX3_X - TX1_X + 26);
|
||||
FAlexTxAnt[b, 1] := MkRb(TxGrp, 0);
|
||||
FAlexTxAnt[b, 2] := MkRb(TxGrp, TX2_X - TX1_X);
|
||||
FAlexTxAnt[b, 3] := MkRb(TxGrp, TX3_X - TX1_X);
|
||||
FAlexTxAnt[b, 1].Checked := True;
|
||||
|
||||
// Do Not TX checkbox
|
||||
FAlexDoNotTx[b] := MkCh(Grp, '', DNTX_X, Y, 22);
|
||||
end;
|
||||
|
||||
// ---- TX Routing flags group ----
|
||||
GrpTX := MakeGroupPanel(FPageAlex, 'TX Routing',
|
||||
MARGIN, 86 + GRP_H + 12, 480, TXGRP_H);
|
||||
|
||||
FAlexRxBypassOnTx := MkCh(GrpTX, 'Rx BYPASS on TX',
|
||||
PAD, GRP_TITLE_H + 8, 440);
|
||||
FAlexExt1OnTx := MkCh(GrpTX, 'Ext 1 on TX',
|
||||
PAD, GRP_TITLE_H + 8 + 28, 440);
|
||||
FAlexGndBpf2OnTx := MkCh(GrpTX, 'Ground BPF2 on TX (ANAN-8000DLE)',
|
||||
PAD, GRP_TITLE_H + 8 + 56, 440);
|
||||
FAlexEnableXvtrHf := MkCh(GrpTX, 'Enable XVTR HF',
|
||||
PAD, GRP_TITLE_H + 8 + 84, 440);
|
||||
finally
|
||||
FLoading := False;
|
||||
end;
|
||||
end;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Alex event handlers and data collectors
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
procedure TSettingsForm.CollectAlexFromUI;
|
||||
var b, a: Integer;
|
||||
begin
|
||||
for b := 0 to 10 do
|
||||
begin
|
||||
FAlexConfig.RxAnt[b] := 1;
|
||||
for a := 1 to 3 do
|
||||
if (FAlexRxAnt[b, a] <> nil) and FAlexRxAnt[b, a].Checked then
|
||||
begin
|
||||
FAlexConfig.RxAnt[b] := a;
|
||||
Break;
|
||||
end;
|
||||
FAlexConfig.TxAnt[b] := 1;
|
||||
for a := 1 to 3 do
|
||||
if (FAlexTxAnt[b, a] <> nil) and FAlexTxAnt[b, a].Checked then
|
||||
begin
|
||||
FAlexConfig.TxAnt[b] := a;
|
||||
Break;
|
||||
end;
|
||||
if (FAlexRxXvtr[b] <> nil) and FAlexRxXvtr[b].Checked then
|
||||
FAlexConfig.RxOnly[b] := 3
|
||||
else if (FAlexRxExt1[b] <> nil) and FAlexRxExt1[b].Checked then
|
||||
FAlexConfig.RxOnly[b] := 2
|
||||
else if (FAlexRxByps[b] <> nil) and FAlexRxByps[b].Checked then
|
||||
FAlexConfig.RxOnly[b] := 1
|
||||
else
|
||||
FAlexConfig.RxOnly[b] := 0;
|
||||
FAlexConfig.DoNotTx[b] := (FAlexDoNotTx[b] <> nil) and FAlexDoNotTx[b].Checked;
|
||||
end;
|
||||
FAlexConfig.RxBypassOnTx := (FAlexRxBypassOnTx <> nil) and FAlexRxBypassOnTx.Checked;
|
||||
FAlexConfig.Ext1OnTx := (FAlexExt1OnTx <> nil) and FAlexExt1OnTx.Checked;
|
||||
FAlexConfig.GndBpf2OnTx := (FAlexGndBpf2OnTx <> nil) and FAlexGndBpf2OnTx.Checked;
|
||||
FAlexConfig.EnableXvtrHf := (FAlexEnableXvtrHf <> nil) and FAlexEnableXvtrHf.Checked;
|
||||
end;
|
||||
|
||||
procedure TSettingsForm.FireAlexChange;
|
||||
begin
|
||||
if FLoading then Exit;
|
||||
CollectAlexFromUI;
|
||||
if Assigned(FOnAlexChange) then
|
||||
FOnAlexChange(FAlexConfig);
|
||||
end;
|
||||
|
||||
procedure TSettingsForm.OnAlexAnyChange(Sender: TObject);
|
||||
begin
|
||||
FireAlexChange;
|
||||
end;
|
||||
|
||||
procedure TSettingsForm.OnAlexRxOnlyChange(Sender: TObject);
|
||||
var
|
||||
Tag, b: Integer;
|
||||
begin
|
||||
if FLoading then Exit;
|
||||
Tag := TCheckBox(Sender).Tag;
|
||||
if Tag >= 200 then
|
||||
begin
|
||||
b := Tag - 200; // XVTR
|
||||
if TCheckBox(Sender).Checked then
|
||||
begin
|
||||
FLoading := True;
|
||||
FAlexRxByps[b].Checked := False;
|
||||
FAlexRxExt1[b].Checked := False;
|
||||
FLoading := False;
|
||||
end;
|
||||
end
|
||||
else if Tag >= 100 then
|
||||
begin
|
||||
b := Tag - 100; // EXT1
|
||||
if TCheckBox(Sender).Checked then
|
||||
begin
|
||||
FLoading := True;
|
||||
FAlexRxByps[b].Checked := False;
|
||||
FAlexRxXvtr[b].Checked := False;
|
||||
FLoading := False;
|
||||
end;
|
||||
end
|
||||
else
|
||||
begin
|
||||
b := Tag; // BYPS
|
||||
if TCheckBox(Sender).Checked then
|
||||
begin
|
||||
FLoading := True;
|
||||
FAlexRxExt1[b].Checked := False;
|
||||
FAlexRxXvtr[b].Checked := False;
|
||||
FLoading := False;
|
||||
end;
|
||||
end;
|
||||
FireAlexChange;
|
||||
end;
|
||||
|
||||
procedure TSettingsForm.LoadAlexSettings(const A: TAlexSettings; BoardType: Integer);
|
||||
var b, ant: Integer;
|
||||
begin
|
||||
FLoading := True;
|
||||
try
|
||||
FAlexConfig := A;
|
||||
FAlexBoardType := BoardType;
|
||||
for b := 0 to 10 do
|
||||
begin
|
||||
for ant := 1 to 3 do
|
||||
if FAlexRxAnt[b, ant] <> nil then
|
||||
FAlexRxAnt[b, ant].Checked := (A.RxAnt[b] = ant);
|
||||
for ant := 1 to 3 do
|
||||
if FAlexTxAnt[b, ant] <> nil then
|
||||
FAlexTxAnt[b, ant].Checked := (A.TxAnt[b] = ant);
|
||||
if FAlexRxByps[b] <> nil then FAlexRxByps[b].Checked := (A.RxOnly[b] = 1);
|
||||
if FAlexRxExt1[b] <> nil then FAlexRxExt1[b].Checked := (A.RxOnly[b] = 2);
|
||||
if FAlexRxXvtr[b] <> nil then FAlexRxXvtr[b].Checked := (A.RxOnly[b] = 3);
|
||||
if FAlexDoNotTx[b] <> nil then FAlexDoNotTx[b].Checked := A.DoNotTx[b];
|
||||
end;
|
||||
if FAlexRxBypassOnTx <> nil then FAlexRxBypassOnTx.Checked := A.RxBypassOnTx;
|
||||
if FAlexExt1OnTx <> nil then FAlexExt1OnTx.Checked := A.Ext1OnTx;
|
||||
if FAlexGndBpf2OnTx <> nil then FAlexGndBpf2OnTx.Checked := A.GndBpf2OnTx;
|
||||
if FAlexEnableXvtrHf <> nil then FAlexEnableXvtrHf.Checked := A.EnableXvtrHf;
|
||||
finally
|
||||
FLoading := False;
|
||||
end;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
Reference in New Issue
Block a user