diff --git a/MainForm.pas b/MainForm.pas index 5a4d342..b3d20f9 100644 --- a/MainForm.pas +++ b/MainForm.pas @@ -520,7 +520,8 @@ type procedure ApplyDXClusterSettings(const D: TDXClusterSettings); procedure AttachPanDXSpots(P: TPanafallPanel); // свой оверлей спотов на пан procedure ApplyDXOverlayCfg(O: TDXSpotOverlay); // настройки вида → один оверлей - procedure DXTuneSpotOnPan(P: TPanafallPanel; FreqHz: Double; Mode: TDXMode); + function DXTuneSpotOnPan(P: TPanafallPanel; FreqHz: Double; + Mode: TDXMode): Integer; procedure UpdateDXClusterButton; procedure BtnDXClusterClick(Sender: TObject); procedure BtnDXClusterMouseDown(Sender: TObject; Button: TMouseButton; @@ -746,7 +747,7 @@ type procedure PanFloatResize(Sender: TObject); function PanFloating(PanId: Integer): Boolean; procedure ResetPanGLCaches(P: TPanafallPanel); // после смены родителя - procedure AddSliceAtFreqPan(P: TPanafallPanel; Hz: Double); + function AddSliceAtFreqPan(P: TPanafallPanel; Hz: Double): Integer; procedure PanClickTune(P: TPanafallPanel; X, W: Integer); // клик по фону пана N procedure PanViewWindow(P: TPanafallPanel; out VC, VS: Double); procedure SyncPanZoomUI(P: TPanafallPanel); // вьюха+зум-бар ← контроллер @@ -5303,17 +5304,19 @@ begin FController.SetMode(NewMode); end; -procedure TMainForm.DXTuneSpotOnPan(P: TPanafallPanel; FreqHz: Double; - Mode: TDXMode); +function TMainForm.DXTuneSpotOnPan(P: TPanafallPanel; FreqHz: Double; + Mode: TDXMode): Integer; // QSY по споту на доп. пане. Цель — слайс ЭТОГО пана (активный, если он здесь, // иначе первый; нет ни одного — создаём в точке), ровно как у клика по фону // пана: своего VFO у панов N нет, а ретюнить их DDC под спот нельзя — это -// увезло бы весь пан. +// увезло бы весь пан. Возвращает слайс, который увели на спот (0 — не увели): +// его номер и адресуют уведомления TCI. var Id, NewMode: Integer; Hz: Int64; S: TCtrlSlice; begin + Result := 0; if (P = nil) or (FreqHz <= 0) then Exit; Hz := Round(FreqHz); // Спот видим в окне пана, но окно может быть шире полосы захвата (зум) — @@ -5329,8 +5332,11 @@ begin NewMode := DXSpotRadioMode(FreqHz, Mode); if Id = 0 then begin - AddSliceAtFreqPan(P, Hz); // ставит FActiveSliceId - Id := FActiveSliceId; + // ★Берём то, что вернул сам вызов, а не FActiveSliceId: слоты могли + // кончиться, и тогда в нём лежит прежний активный слайс — возможно, вообще + // с другого пана. Раньше его и уводили на спот, а теперь ещё и объявляли + // клиентам TCI приёмником, который «встал на спот». + Id := AddSliceAtFreqPan(P, Hz); if Id = 0 then Exit; end else @@ -5344,6 +5350,7 @@ begin FController.FilterBWFor(NewMode, FilterDefIdx(NewMode))); FActiveSliceId := Id; + Result := Id; P.PushSliceFlagState(Id); P.LayoutFlags; MarkPanDirty(P); @@ -7015,18 +7022,23 @@ begin if P.PbPanZoom <> nil then P.PbPanZoom.Invalidate; // событийный путь — можно end; -procedure TMainForm.AddSliceAtFreqPan(P: TPanafallPanel; Hz: Double); +function TMainForm.AddSliceAtFreqPan(P: TPanafallPanel; Hz: Double): Integer; // Слайс в доп. пане (режим/полоса копируются с главного, звуковуха default). +// Возвращает созданный слайс, 0 — не создали (вне полосы захвата, слоты +// кончились). Спрашивать об этом FActiveSliceId нельзя: при неудаче там +// остаётся ПРЕЖНИЙ активный слайс, вообще говоря с другого пана. var Lo, Hi, Id: Integer; begin + Result := 0; if not FController.SliceFitsCapture(Hz, P.PanId) then Exit; TRadioController.FilterEdgesFor(FController.FMode, FController.FFilterBW, Lo, Hi); Id := FController.AddSlice(Hz, FController.FMode, Lo, Hi, TRadioController.AGCModeFromUI(FController.FAGCMode), 0.7, -1, '', P.PanId); - if Id < 0 then Exit; + if Id <= 0 then Exit; P.CreateSliceFlag(Id); FActiveSliceId := Id; + Result := Id; end; procedure TMainForm.OnPanRateClick(Sender: TObject); @@ -8031,9 +8043,18 @@ begin if IsSpec and (Button = mbLeft) and (P.DXOverlay <> nil) and P.DXOverlay.SpotAtPixel(X, Y, DXSpot) then begin - DXTuneSpotOnPan(P, DXSpot.FreqHz, DXSpot.Mode); - if FTCIAdapter <> nil then - FTCIAdapter.NotifySpotClicked(DXSpot.Call, DXSpot.FreqHz, P.PanId, 0); + Id := DXTuneSpotOnPan(P, DXSpot.FreqHz, DXSpot.Mode); + // ★Приёмник TCI — это слот СЛАЙСА (панадаптером приёмник быть перестал), + // поэтому спрашиваем номер того слайса, который на спот и увели. Номер + // пана здесь адресовал чужой приёмник, а то и несуществующий — клиенты с + // фильтром по rx уведомление просто теряли. + // ★Ничего не увели (спот вне полосы захвата зумнутого пана, слоты + // кончились) — молчим. Иначе уведомление уезжало бы под номером 0, то есть + // от имени ГЛАВНОГО приёмника, который никуда не двигался: логгер открывал + // бы карточку QSO и тянул за собой VFO на частоту, где радио не стоит. + if (Id <> 0) and (FTCIAdapter <> nil) then + FTCIAdapter.NotifySpotClicked(DXSpot.Call, DXSpot.FreqHz, + FTCIAdapter.RxOfSlice(Id), 0); Exit; end; W := TControl(Sender).Width; diff --git a/TCIAdapter.pas b/TCIAdapter.pas index df5fee6..e762c95 100644 --- a/TCIAdapter.pas +++ b/TCIAdapter.pas @@ -353,6 +353,10 @@ type { Уведомление о клике по споту на панораме (§4.4) — зовёт UI. } procedure NotifySpotClicked(const Call: string; FreqHz: Double; Rx: Integer = 0; Ch: Integer = 0); + { Номер TCI-приёмника, за которым стоит слайс Id. Нужен UI: уведомления + адресуются номерами TCI, а он знает только id слайса. 0 — слайса нет в + снимке (или Id = 0): адресуем главный тракт, он есть всегда. } + function RxOfSlice(Id: Integer): Integer; { Статус фокуса главного окна (APP_FOCUS) — зовёт UI. } procedure NotifyAppFocus(InFocus: Boolean); @@ -3790,6 +3794,12 @@ end; Уведомления, которые инициирует UI ═══════════════════════════════════════════════════════════════════════════ } +function TTCIAdapter.RxOfSlice(Id: Integer): Integer; +var Rx, Ch: Integer; +begin + if SliceRxCh(Id, Rx, Ch) then Result := Rx else Result := 0; +end; + procedure TTCIAdapter.NotifySpotClicked(const Call: string; FreqHz: Double; Rx, Ch: Integer); begin