OpenCPN Partial API docs
Loading...
Searching...
No Matches
ais_target_list_dlg.cpp
1/**************************************************************************
2 * Copyright (C) 2010 by David S. Register *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, see <https://www.gnu.org/licenses/>. *
16 ***************************************************************************/
17
18#include "gl_headers.h" // Must be included before anything using GL stuff
19
20#include <wx/textctrl.h>
21#include <wx/sizer.h>
22#include <wx/tokenzr.h>
23#include <wx/clipbrd.h>
24
25#ifdef __ANDROID__
26#include "androidUTIL.h"
27#endif
28
29#include "model/ais_decoder.h"
32#include "model/navobj_db.h"
33#include "model/route_point.h"
34#include "model/select.h"
35
36#include "ais.h"
37#include "ais_target_list_dlg.h"
38#include "chcanv.h"
39#include "ocpn_list_ctrl.h"
40#include "ocpn_platform.h"
41#include "routemanagerdialog.h"
42#include "styles.h"
43#include "top_frame.h"
44
45AISTargetListDialog *g_pAISTargetList; // Global instance
46
47static int g_AisTargetList_count;
48static AisDecoder *s_p_sort_decoder;
49static bool g_bAisTargetList_autosort;
50
51IMPLEMENT_CLASS(AISTargetListDialog, wxPanel)
52
53BEGIN_EVENT_TABLE(AISTargetListDialog, wxPanel)
54EVT_CLOSE(AISTargetListDialog::OnClose)
55END_EVENT_TABLE()
56
57static bool g_bsort_once;
58
59static int ItemCompare(AisTargetData *pAISTarget1, AisTargetData *pAISTarget2) {
60 wxString s1, s2;
61 double n1 = 0.;
62 double n2 = 0.;
63 bool b_cmptype_num = false;
64
65 // Don't sort unless requested
66 if (!g_bAisTargetList_autosort && !g_bsort_once) return 0;
67
68 AisTargetData *t1 = pAISTarget1;
69 AisTargetData *t2 = pAISTarget2;
70
71 if (t1->Class == AIS_SART) {
72 if (t2->Class == AIS_DSC)
73 return 0;
74 else
75 return -1;
76 }
77
78 if (t2->Class == AIS_SART) {
79 if (t1->Class == AIS_DSC)
80 return 0;
81 else
82 return 1;
83 }
84
85 switch (g_AisTargetList_sortColumn) {
86 case tlTRK:
87 n1 = t1->b_show_track;
88 n2 = t2->b_show_track;
89 b_cmptype_num = true;
90 break;
91
92 case tlNAME:
93 s1 = trimAISField(t1->ShipName);
94 if ((!t1->b_nameValid && (t1->Class == AIS_BASE)) ||
95 (t1->Class == AIS_SART))
96 s1 = "-";
97
98 s2 = trimAISField(t2->ShipName);
99 if ((!t2->b_nameValid && (t2->Class == AIS_BASE)) ||
100 (t2->Class == AIS_SART))
101 s2 = "-";
102 break;
103
104 case tlCALL:
105 s1 = trimAISField(t1->CallSign);
106 s2 = trimAISField(t2->CallSign);
107 break;
108
109 case tlMMSI:
110 n1 = t1->MMSI;
111 n2 = t2->MMSI;
112 b_cmptype_num = true;
113 break;
114
115 case tlCLASS:
116 s1 = t1->Get_class_string(true);
117 s2 = t2->Get_class_string(true);
118 break;
119
120 case tlTYPE:
121 s1 = t1->Get_vessel_type_string(false);
122 if ((t1->Class == AIS_BASE) ||
123 (t1->Class == AIS_SART || (t1->Class == AIS_METEO)))
124 s1 = "-";
125
126 s2 = t2->Get_vessel_type_string(false);
127 if ((t1->Class == AIS_BASE) || (t1->Class == AIS_SART) ||
128 (t1->Class == AIS_METEO))
129 s2 = "-";
130 break;
131
132 case tlFLAG:
133 s1 = t1->GetCountryCode(true);
134 s2 = t2->GetCountryCode(true);
135 break;
136 case tlNAVSTATUS: {
137 if ((t1->NavStatus <= 15) && (t1->NavStatus >= 0)) {
138 if (t1->Class == AIS_SART) {
139 if (t1->NavStatus == RESERVED_14)
140 s1 = _("Active");
141 else if (t1->NavStatus == UNDEFINED)
142 s1 = _("Testing");
143 } else
144 s1 = ais_get_status(t1->NavStatus);
145 } else
146 s1 = _("-");
147
148 if ((t1->Class == AIS_ATON) || (t1->Class == AIS_BASE) ||
149 (t1->Class == AIS_CLASS_B) || (t1->Class == AIS_METEO))
150 s1 = "-";
151
152 if ((t2->NavStatus <= 15) && (t2->NavStatus >= 0)) {
153 if (t2->Class == AIS_SART) {
154 if (t2->NavStatus == RESERVED_14)
155 s2 = _("Active");
156 else if (t2->NavStatus == UNDEFINED)
157 s2 = _("Testing");
158 } else
159 s2 = ais_get_status(t2->NavStatus);
160 } else
161 s2 = _("-");
162
163 if ((t2->Class == AIS_ATON) || (t2->Class == AIS_BASE) ||
164 (t2->Class == AIS_CLASS_B) || (t2->Class == AIS_METEO))
165 s2 = "-";
166
167 break;
168 }
169
170 case tlBRG: {
171 int brg1 = wxRound(t1->Brg);
172 if (brg1 == 360)
173 n1 = 0.;
174 else
175 n1 = brg1;
176
177 int brg2 = wxRound(t2->Brg);
178 if (brg2 == 360)
179 n2 = 0.;
180 else
181 n2 = brg2;
182
183 b_cmptype_num = true;
184 break;
185 }
186
187 case tlCOG: {
188 if ((t1->COG >= 360.0) || (t1->Class == AIS_ATON) ||
189 (t1->Class == AIS_BASE) || (t1->Class == AIS_METEO))
190 n1 = -1.0;
191 else {
192 int crs = wxRound(t1->COG);
193 if (crs == 360)
194 n1 = 0.;
195 else
196 n1 = crs;
197 }
198
199 if ((t2->COG >= 360.0) || (t2->Class == AIS_ATON) ||
200 (t2->Class == AIS_BASE) || (t2->Class == AIS_METEO))
201 n2 = -1.0;
202 else {
203 int crs = wxRound(t2->COG);
204 if (crs == 360)
205 n2 = 0.;
206 else
207 n2 = crs;
208 }
209
210 b_cmptype_num = true;
211 break;
212 }
213
214 case tlSOG: {
215 if ((t1->SOG > 100.) || (t1->Class == AIS_ATON) ||
216 (t1->Class == AIS_BASE) || (t1->Class == AIS_METEO))
217 n1 = -1.0;
218 else
219 n1 = t1->SOG;
220
221 if ((t2->SOG > 100.) || (t2->Class == AIS_ATON) ||
222 (t2->Class == AIS_BASE) || (t2->Class == AIS_METEO))
223 n2 = -1.0;
224 else
225 n2 = t2->SOG;
226
227 b_cmptype_num = true;
228 break;
229 }
230 case tlCPA: {
231 if ((!t1->bCPA_Valid) || (t1->Class == AIS_ATON) ||
232 (t1->Class == AIS_BASE) || (t1->Class == AIS_METEO))
233 n1 = 99999.0;
234 else
235 n1 = t1->CPA;
236
237 if ((!t2->bCPA_Valid) || (t2->Class == AIS_ATON) ||
238 (t2->Class == AIS_BASE))
239 n2 = 99999.0;
240 else
241 n2 = t2->CPA;
242
243 b_cmptype_num = true;
244 break;
245 }
246 case tlTCPA: {
247 if ((!t1->bCPA_Valid) || (t1->Class == AIS_ATON) ||
248 (t1->Class == AIS_BASE) || (t1->Class == AIS_METEO))
249 n1 = 99999.0;
250 else
251 n1 = t1->TCPA;
252
253 if ((!t2->bCPA_Valid) || (t2->Class == AIS_ATON) ||
254 (t2->Class == AIS_BASE) || (t2->Class == AIS_METEO))
255 n2 = 99999.0;
256 else
257 n2 = t2->TCPA;
258
259 b_cmptype_num = true;
260 break;
261 }
262 case tlRNG: {
263 n1 = t1->Range_NM;
264 n2 = t2->Range_NM;
265 b_cmptype_num = true;
266 break;
267 }
268
269 default:
270 break;
271 }
272
273 if (!b_cmptype_num) {
274 if (g_bAisTargetList_sortReverse) return s2.Cmp(s1);
275 return s1.Cmp(s2);
276 } else {
277 // If numeric sort values are equal, secondary sort is on Range_NM
278 if (g_bAisTargetList_sortReverse) {
279 if (n2 > n1)
280 return 1;
281 else if (n2 < n1)
282 return -1;
283 else
284 return (t1->Range_NM > t2->Range_NM); // 0;
285 } else {
286 if (n2 > n1)
287 return -1;
288 else if (n2 < n1)
289 return 1;
290 else
291 return (t1->Range_NM > t2->Range_NM); // 0;
292 }
293 }
294}
295
296static int ArrayItemCompareMMSI(int MMSI1, int MMSI2) {
297 if (s_p_sort_decoder) {
298 std::shared_ptr<AisTargetData> pAISTarget1 =
299 s_p_sort_decoder->Get_Target_Data_From_MMSI(MMSI1);
300 std::shared_ptr<AisTargetData> pAISTarget2 =
301 s_p_sort_decoder->Get_Target_Data_From_MMSI(MMSI2);
302
303 if (pAISTarget1 && pAISTarget2)
304 return ItemCompare(pAISTarget1.get(), pAISTarget2.get());
305 else
306 return 0;
307 } else
308 return 0;
309}
310
311AISTargetListDialog::AISTargetListDialog(wxWindow *parent, wxAuiManager *auimgr,
312 AisDecoder *pdecoder)
313 : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxSize(-1, -1 /*780, 250*/),
314 wxBORDER_NONE) {
315 m_pparent = parent;
316 m_pAuiManager = auimgr;
317 m_pdecoder = pdecoder;
318 g_bsort_once = false;
319 m_bautosort_force = false;
320
321 wxFont *qFont = GetOCPNScaledFont(_("Dialog"));
322 SetFont(*qFont);
323
324 s_p_sort_decoder = pdecoder;
325 m_pMMSI_array = new ArrayOfMMSI(ArrayItemCompareMMSI);
326
327 CreateControls();
328
329 // Set default color for panel, respecting Dark mode if enabled
330 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
331 SetColorScheme();
332
333 UpdateButtons();
334
335 if (m_pAuiManager) {
336 wxAuiPaneInfo paneproto = wxAuiPaneInfo()
337 .Name("AISTargetList")
338 .CaptionVisible(true)
339 .Float()
340 .FloatingPosition(50, 50)
341 .FloatingSize(400, 200)
342 .BestSize(700, GetCharHeight() * 10);
343
344 // Force and/or override any perspective information that is not
345 // applicable
346 paneproto.Caption(wxGetTranslation(_("AIS target list")));
347 paneproto.Name("AISTargetList");
348 paneproto.DestroyOnClose(true);
349 paneproto.TopDockable(false)
350 .BottomDockable(true)
351 .LeftDockable(false)
352 .RightDockable(false);
353 paneproto.Show(true);
354
355 m_pAuiManager->AddPane(this, paneproto);
356
357 wxAuiPaneInfo &pane = m_pAuiManager->GetPane("AISTargetList");
358
359 if (g_AisTargetList_perspective.IsEmpty()) {
360 if (!g_btouch) RecalculateSize();
361 } else {
362 m_pAuiManager->LoadPaneInfo(g_AisTargetList_perspective, pane);
363 m_pAuiManager->Update();
364 }
365
366 pane = m_pAuiManager->GetPane("AISTargetList"); // Refresh the reference
367
368 // Some special setup for touch screens
369 if (g_btouch) {
370 pane.Float();
371 pane.Dockable(false);
372
373 wxSize screen_size = top_frame::Get()->GetClientSize();
374 pane.FloatingSize(screen_size.x * 8 / 10, screen_size.y * 8 / 10);
375 pane.FloatingPosition(screen_size.x * 1 / 10, screen_size.y * 1 / 10);
376 m_pAuiManager->Update();
377 }
378
379 bool b_reset_pos = false;
380 if ((pane.floating_size.x != -1) && (pane.floating_size.y != -1)) {
381#ifdef __WXMSW__
382 // Support MultiMonitor setups which an allow negative window positions.
383 // If the requested window title bar does not intersect any installed
384 // monitor, then default to simple primary monitor positioning.
385 RECT frame_title_rect;
386 frame_title_rect.left = pane.floating_pos.x;
387 frame_title_rect.top = pane.floating_pos.y;
388 frame_title_rect.right = pane.floating_pos.x + pane.floating_size.x;
389 frame_title_rect.bottom = pane.floating_pos.y + 30;
390
391 if (NULL == MonitorFromRect(&frame_title_rect, MONITOR_DEFAULTTONULL))
392 b_reset_pos = true;
393#else
394
395 // Make sure drag bar (title bar) of window intersects wxClient Area of
396 // screen, with a little slop...
397 wxRect window_title_rect; // conservative estimate
398 window_title_rect.x = pane.floating_pos.x;
399 window_title_rect.y = pane.floating_pos.y;
400 window_title_rect.width = pane.floating_size.x;
401 window_title_rect.height = 30;
402
403 wxRect ClientRect = wxGetClientDisplayRect();
404 ClientRect.Deflate(
405 60, 60); // Prevent the new window from being too close to the edge
406 if (!ClientRect.Intersects(window_title_rect)) b_reset_pos = true;
407
408#endif
409
410 if (b_reset_pos) {
411 pane.FloatingPosition(50, 50);
412 m_pAuiManager->Update();
413 }
414 }
415
416 // If the list got accidentally dropped on top of the chart bar, move it
417 // away....
418 if (pane.IsDocked() && (pane.dock_row == 0)) {
419 pane.Float();
420 pane.Row(1);
421 pane.Position(0);
422 m_pAuiManager->Update();
423 }
424
425 pane.Show(true);
426 m_pAuiManager->Update();
427
428 g_AisTargetList_perspective = m_pAuiManager->SavePaneInfo(pane);
429 pConfig->UpdateSettings();
430
431 m_pAuiManager->Connect(
432 wxEVT_AUI_PANE_CLOSE,
433 wxAuiManagerEventHandler(AISTargetListDialog::OnPaneClose), NULL, this);
434
435 } else {
436 // Make an estimate of the default dialog size
437 // for the case when the AUI Perspective for this dialog is undefined
438 wxSize esize;
439 esize.x = 700;
440 esize.y = GetCharHeight() * 10; // 18;
441 SetSize(esize);
442 }
443
444 // Connect Events
445 Connect(wxEVT_CONTEXT_MENU,
446 wxCommandEventHandler(AISTargetListDialog::OnRightClickContext), NULL,
447 this);
448}
449
450AISTargetListDialog::~AISTargetListDialog() {
451 Disconnect_decoder();
452 g_pAISTargetList = NULL;
453}
454
455void AISTargetListDialog::RecalculateSize() {
456 // Make an estimate of the dialog size
457
458 wxSize esize;
459 esize.x = GetCharWidth() * 110;
460 esize.y = GetCharHeight() * 40;
461
462 wxSize dsize = top_frame::Get()->GetClientSize();
463 esize.y = wxMin(esize.y, dsize.y - (4 * GetCharHeight()));
464 esize.x = wxMin(esize.x, dsize.x - (2 * GetCharHeight()));
465 SetClientSize(esize);
466
467 wxSize fsize = GetSize();
468 fsize.y = wxMin(fsize.y, dsize.y - (2 * GetCharHeight()));
469 fsize.x = wxMin(fsize.x, dsize.x - (2 * GetCharHeight()));
470 SetSize(fsize);
471
472 if (m_pAuiManager) {
473 wxAuiPaneInfo &pane = m_pAuiManager->GetPane("AISTargetList");
474
475 if (pane.IsOk()) {
476 pane.FloatingSize(fsize.x, fsize.y);
477 wxPoint pos = top_frame::Get()->GetScreenPosition();
478 pane.FloatingPosition(pos.x + (dsize.x - fsize.x) / 2,
479 pos.y + (dsize.y - fsize.y) / 2);
480 }
481
482 m_pAuiManager->Update();
483 }
484}
485
486void AISTargetListDialog::CreateControls() {
487 wxBoxSizer *topSizer = new wxBoxSizer(wxHORIZONTAL);
488 SetSizer(topSizer);
489#ifdef __ANDROID__
490 this->GetHandle()->setStyleSheet(getQtStyleSheet());
491#endif
492
493 // Parse the global column width string as read from config file
494 wxStringTokenizer tkz(g_AisTargetList_column_spec, ";");
495 wxString s_width = tkz.GetNextToken();
496 int width;
497 long lwidth;
498
499 long flags = wxLC_REPORT | wxLC_SINGLE_SEL | wxLC_HRULES | wxLC_VRULES |
500 wxBORDER_SUNKEN;
501#ifndef __WXQT__
502 flags |= wxLC_VIRTUAL;
503#endif
504
505 m_pListCtrlAISTargets = new OCPNListCtrl(
506 this, ID_AIS_TARGET_LIST, wxDefaultPosition, wxDefaultSize, flags);
507
508 wxImageList *imglist = new wxImageList(16, 16, true, 2);
509
510 ocpnStyle::Style *style = g_StyleManager->GetCurrentStyle();
511 imglist->Add(style->GetIcon("sort_asc"));
512 imglist->Add(style->GetIcon("sort_desc"));
513
514 m_pListCtrlAISTargets->AssignImageList(imglist, wxIMAGE_LIST_SMALL);
515 m_pListCtrlAISTargets->Connect(
516 wxEVT_COMMAND_LIST_ITEM_SELECTED,
517 wxListEventHandler(AISTargetListDialog::OnTargetSelected), NULL, this);
518 m_pListCtrlAISTargets->Connect(
519 wxEVT_COMMAND_LIST_ITEM_DESELECTED,
520 wxListEventHandler(AISTargetListDialog::OnTargetSelected), NULL, this);
521 m_pListCtrlAISTargets->Connect(
522 wxEVT_COMMAND_LIST_ITEM_ACTIVATED,
523 wxListEventHandler(AISTargetListDialog::OnTargetDefaultAction), NULL,
524 this);
525 m_pListCtrlAISTargets->Connect(
526 wxEVT_COMMAND_LIST_COL_CLICK,
527 wxListEventHandler(AISTargetListDialog::OnTargetListColumnClicked), NULL,
528 this);
529
530 int dx = GetCharWidth();
531
532 width = dx * 4;
533 if (s_width.ToLong(&lwidth)) {
534 width = wxMax(dx * 2, lwidth);
535 width = wxMin(width, dx * 30);
536 }
537 m_pListCtrlAISTargets->InsertColumn(tlTRK, _("Trk"), wxLIST_FORMAT_LEFT,
538 width);
539 s_width = tkz.GetNextToken();
540
541 width = dx * 12;
542 if (s_width.ToLong(&lwidth)) {
543 width = wxMax(dx * 2, lwidth);
544 width = wxMin(width, dx * 30);
545 }
546 m_pListCtrlAISTargets->InsertColumn(tlNAME, _("Name"), wxLIST_FORMAT_LEFT,
547 width);
548 s_width = tkz.GetNextToken();
549
550 width = dx * 7;
551 if (s_width.ToLong(&lwidth)) {
552 width = wxMax(dx * 2, lwidth);
553 width = wxMin(width, dx * 30);
554 }
555 m_pListCtrlAISTargets->InsertColumn(tlCALL, _("Call"), wxLIST_FORMAT_LEFT,
556 width);
557 s_width = tkz.GetNextToken();
558
559 width = dx * 10;
560 if (s_width.ToLong(&lwidth)) {
561 width = wxMax(dx * 2, lwidth);
562 width = wxMin(width, dx * 30);
563 }
564 m_pListCtrlAISTargets->InsertColumn(tlMMSI, _("MMSI"), wxLIST_FORMAT_LEFT,
565 width);
566 s_width = tkz.GetNextToken();
567
568 width = dx * 7;
569 if (s_width.ToLong(&lwidth)) {
570 width = wxMax(dx * 2, lwidth);
571 width = wxMin(width, dx * 30);
572 }
573 m_pListCtrlAISTargets->InsertColumn(tlCLASS, _("Class"), wxLIST_FORMAT_CENTER,
574 width);
575 s_width = tkz.GetNextToken();
576
577 width = dx * 10;
578 if (s_width.ToLong(&lwidth)) {
579 width = wxMax(dx * 2, lwidth);
580 width = wxMin(width, dx * 30);
581 }
582 m_pListCtrlAISTargets->InsertColumn(tlTYPE, _("Type"), wxLIST_FORMAT_LEFT,
583 width);
584 s_width = tkz.GetNextToken();
585
586 width = dx * 12;
587 if (s_width.ToLong(&lwidth)) {
588 width = wxMax(dx * 2, lwidth);
589 width = wxMin(width, dx * 30);
590 }
591 m_pListCtrlAISTargets->InsertColumn(tlNAVSTATUS, _("Flag"),
592 wxLIST_FORMAT_LEFT, width);
593 s_width = tkz.GetNextToken();
594
595 width = dx * 12;
596 if (s_width.ToLong(&lwidth)) {
597 width = wxMax(dx * 2, lwidth);
598 width = wxMin(width, dx * 30);
599 }
600 m_pListCtrlAISTargets->InsertColumn(tlNAVSTATUS, _("Nav Status"),
601 wxLIST_FORMAT_LEFT, width);
602 s_width = tkz.GetNextToken();
603
604 width = dx * 6;
605 if (s_width.ToLong(&lwidth)) {
606 width = wxMax(dx * 2, lwidth);
607 width = wxMin(width, dx * 30);
608 }
609 m_pListCtrlAISTargets->InsertColumn(tlBRG, _("Brg"), wxLIST_FORMAT_RIGHT,
610 width);
611 s_width = tkz.GetNextToken();
612
613 width = dx * 8;
614 if (s_width.ToLong(&lwidth)) {
615 width = wxMax(dx * 2, lwidth);
616 width = wxMin(width, dx * 30);
617 }
618 m_pListCtrlAISTargets->InsertColumn(tlRNG, _("Range"), wxLIST_FORMAT_RIGHT,
619 width);
620 s_width = tkz.GetNextToken();
621
622 width = dx * 6;
623 if (s_width.ToLong(&lwidth)) {
624 width = wxMax(dx * 2, lwidth);
625 width = wxMin(width, dx * 30);
626 }
627 m_pListCtrlAISTargets->InsertColumn(tlCOG, _("CoG"), wxLIST_FORMAT_RIGHT,
628 width);
629 s_width = tkz.GetNextToken();
630
631 width = dx * 6;
632 if (s_width.ToLong(&lwidth)) {
633 width = wxMax(dx * 2, lwidth);
634 width = wxMin(width, dx * 30);
635 }
636 m_pListCtrlAISTargets->InsertColumn(tlSOG, _("SoG"), wxLIST_FORMAT_RIGHT,
637 width);
638
639 width = dx * 7;
640 if (s_width.ToLong(&lwidth)) {
641 width = wxMax(dx * 2, lwidth);
642 width = wxMin(width, dx * 30);
643 }
644 m_pListCtrlAISTargets->InsertColumn(tlCPA, _("CPA"), wxLIST_FORMAT_RIGHT,
645 width);
646
647 width = dx * 8;
648 if (s_width.ToLong(&lwidth)) {
649 width = wxMax(dx * 2, lwidth);
650 width = wxMin(width, dx * 30);
651 }
652 m_pListCtrlAISTargets->InsertColumn(tlTCPA, _("TCPA"), wxLIST_FORMAT_RIGHT,
653 width);
654 wxListItem item;
655 item.SetMask(wxLIST_MASK_IMAGE);
656 item.SetImage(g_bAisTargetList_sortReverse ? 1 : 0);
657 g_AisTargetList_sortColumn = wxMax(g_AisTargetList_sortColumn, 0);
658 m_pListCtrlAISTargets->SetColumn(g_AisTargetList_sortColumn, item);
659
660#ifdef wxHAS_LISTCTRL_COLUMN_ORDER
661 wxStringTokenizer tkz_order(g_AisTargetList_column_order, ";");
662 wxString s_order = tkz_order.GetNextToken();
663 int i_columns = m_pListCtrlAISTargets->GetColumnCount();
664 wxArrayInt a_order(i_columns);
665 for (int i = 0; i < i_columns; i++) {
666 long l_order = (long)i;
667 s_order.ToLong(&l_order);
668 if (l_order < 0 || l_order > i_columns) {
669 l_order = i;
670 }
671 a_order[i] = l_order;
672 s_order = tkz_order.GetNextToken();
673 }
674
675 m_pListCtrlAISTargets->SetColumnsOrder(a_order);
676#endif
677
678 topSizer->Add(m_pListCtrlAISTargets, 1, wxEXPAND | wxALL, 0);
679
680 wxBoxSizer *boxSizer02 = new wxBoxSizer(wxVERTICAL);
681 boxSizer02->AddSpacer(22);
682 topSizer->Add(boxSizer02, 0, wxEXPAND | wxALL, 2);
683
684 wxScrolledWindow *winr =
685 new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
686 wxNO_BORDER | wxTAB_TRAVERSAL | wxVSCROLL);
687 winr->SetScrollRate(0, 5);
688
689 boxSizer02->Add(winr, 1, wxALL | wxEXPAND, 3);
690
691 wxBoxSizer *bsRouteButtonsInner = new wxBoxSizer(wxVERTICAL);
692 winr->SetSizer(bsRouteButtonsInner);
693
694 m_pButtonInfo = new wxButton(winr, wxID_ANY, _("Target info"),
695 wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW);
696 m_pButtonInfo->Connect(
697 wxEVT_COMMAND_BUTTON_CLICKED,
698 wxCommandEventHandler(AISTargetListDialog::OnTargetQuery), NULL, this);
699 bsRouteButtonsInner->Add(m_pButtonInfo, 0, wxEXPAND | wxALL, 2);
700 bsRouteButtonsInner->AddSpacer(5);
701
702 m_pButtonJumpTo =
703 new wxButton(winr, wxID_ANY, _("Center view"), wxDefaultPosition,
704 wxDefaultSize, wxBU_AUTODRAW);
705 m_pButtonJumpTo->Connect(
706 wxEVT_COMMAND_BUTTON_CLICKED,
707 wxCommandEventHandler(AISTargetListDialog::OnTargetScrollTo), NULL, this);
708 bsRouteButtonsInner->Add(m_pButtonJumpTo, 0, wxEXPAND | wxALL, 2);
709
710 m_pButtonJumpTo_Close =
711 new wxButton(winr, wxID_ANY, _("Center-Info-Close"), wxDefaultPosition,
712 wxDefaultSize, wxBU_AUTODRAW);
713 m_pButtonJumpTo_Close->Connect(
714 wxEVT_COMMAND_BUTTON_CLICKED,
715 wxCommandEventHandler(AISTargetListDialog::OnTargetScrollToClose), NULL,
716 this);
717 bsRouteButtonsInner->Add(m_pButtonJumpTo_Close, 0, wxEXPAND | wxALL, 2);
718
719 m_pButtonCreateWpt =
720 new wxButton(winr, wxID_ANY, _("Create WPT"), wxDefaultPosition,
721 wxDefaultSize, wxBU_AUTODRAW);
722 m_pButtonCreateWpt->Connect(
723 wxEVT_COMMAND_BUTTON_CLICKED,
724 wxCommandEventHandler(AISTargetListDialog::OnTargetCreateWpt), NULL,
725 this);
726 bsRouteButtonsInner->Add(m_pButtonCreateWpt, 0, wxEXPAND | wxALL, 0);
727
728 m_pButtonHideAllTracks =
729 new wxButton(winr, wxID_ANY, _("Hide All Tracks"), wxDefaultPosition,
730 wxDefaultSize, wxBU_AUTODRAW);
731 m_pButtonHideAllTracks->Connect(
732 wxEVT_COMMAND_BUTTON_CLICKED,
733 wxCommandEventHandler(AISTargetListDialog::OnHideAllTracks), NULL, this);
734 bsRouteButtonsInner->Add(m_pButtonHideAllTracks, 0, wxEXPAND | wxALL, 2);
735
736 m_pButtonShowAllTracks =
737 new wxButton(winr, wxID_ANY, _("Show All Tracks"), wxDefaultPosition,
738 wxDefaultSize, wxBU_AUTODRAW);
739 m_pButtonShowAllTracks->Connect(
740 wxEVT_COMMAND_BUTTON_CLICKED,
741 wxCommandEventHandler(AISTargetListDialog::OnShowAllTracks), NULL, this);
742 bsRouteButtonsInner->Add(m_pButtonShowAllTracks, 0, wxEXPAND | wxALL, 2);
743
744 m_pButtonToggleTrack =
745 new wxButton(winr, wxID_ANY, _("Toggle track"), wxDefaultPosition,
746 wxDefaultSize, wxBU_AUTODRAW);
747 m_pButtonToggleTrack->Connect(
748 wxEVT_COMMAND_BUTTON_CLICKED,
749 wxCommandEventHandler(AISTargetListDialog::OnToggleTrack), NULL, this);
750 bsRouteButtonsInner->Add(m_pButtonToggleTrack, 0, wxEXPAND | wxALL, 2);
751
752 m_pButtonCopyMMSI =
753 new wxButton(winr, wxID_ANY, _("Copy MMSI"), wxDefaultPosition,
754 wxDefaultSize, wxBU_AUTODRAW);
755 m_pButtonCopyMMSI->Connect(
756 wxEVT_COMMAND_BUTTON_CLICKED,
757 wxCommandEventHandler(AISTargetListDialog::OnCopyMMSI), NULL, this);
758 bsRouteButtonsInner->Add(m_pButtonCopyMMSI, 0, wxEXPAND | wxALL, 2);
759
760 bool enable_find = true;
761#ifdef __WXMSW__
762 // Check for app MenuBar. Disable "find" funtion if present.
763 auto *frame = wxDynamicCast(wxTheApp->GetTopWindow(), wxFrame);
764 if (frame) {
765 wxMenuBar *mb = frame->GetMenuBar();
766 if (mb != nullptr) enable_find = false;
767 }
768#endif
769
770 m_pStaticTextFind = new wxStaticText(winr, wxID_ANY, _("Find target name"),
771 wxDefaultPosition, wxDefaultSize, 0);
772 bsRouteButtonsInner->Add(m_pStaticTextFind, 0, wxALL, 2);
773
774 m_pFindTargetName =
775 new wxTextCtrl(winr, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, 0);
776 m_pFindTargetName->SetMinSize(wxSize(15 * GetCharWidth(), -1));
777 bsRouteButtonsInner->Add(m_pFindTargetName, 0, wxALL, 2);
778 if (enable_find) {
779 m_pFindTargetName->Enable();
780 m_pFindTargetName->Connect(
781 wxEVT_TEXT,
782 wxCommandEventHandler(AISTargetListDialog::OnEditFindTarget), NULL,
783 this);
784 } else {
785 m_pFindTargetName->Disable();
786 m_pFindTargetName->SetDefaultStyle(wxTextAttr(wxNullColour, *wxLIGHT_GREY));
787 m_pStaticTextFind->SetToolTip(
788 _("Disable OpenCPN MenuBar to search for AIS targets"));
789 }
790
791 m_pCBAutosort =
792 new wxCheckBox(winr, wxID_ANY, _("AutoSort"), wxDefaultPosition,
793 wxDefaultSize, wxBU_AUTODRAW);
794 m_pCBAutosort->Connect(
795 wxEVT_COMMAND_CHECKBOX_CLICKED,
796 wxCommandEventHandler(AISTargetListDialog::OnAutosortCB), NULL, this);
797 bsRouteButtonsInner->Add(m_pCBAutosort, 0, wxEXPAND | wxALL, 2);
798 g_bAisTargetList_autosort = true;
799 m_pCBAutosort->SetValue(g_bAisTargetList_autosort);
800
801 bsRouteButtonsInner->AddSpacer(10);
802
803 m_pStaticTextRange = new wxStaticText(winr, wxID_ANY, _("Limit range: NM"),
804 wxDefaultPosition, wxDefaultSize, 0);
805 bsRouteButtonsInner->Add(m_pStaticTextRange, 0, wxALL, 2);
806 bsRouteButtonsInner->AddSpacer(2);
807 m_pSpinCtrlRange = new wxSpinCtrl(
808 winr, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(50, -1),
809 wxSP_ARROW_KEYS, 1, 20000, g_AisTargetList_range);
810 m_pSpinCtrlRange->Connect(
811 wxEVT_COMMAND_SPINCTRL_UPDATED,
812 wxCommandEventHandler(AISTargetListDialog::OnLimitRange), NULL, this);
813 m_pSpinCtrlRange->Connect(
814 wxEVT_COMMAND_TEXT_UPDATED,
815 wxCommandEventHandler(AISTargetListDialog::OnLimitRange), NULL, this);
816 bsRouteButtonsInner->Add(m_pSpinCtrlRange, 0, wxEXPAND | wxALL, 0);
817
818 bsRouteButtonsInner->AddSpacer(10);
819 m_pStaticTextCount = new wxStaticText(winr, wxID_ANY, _("Target Count"),
820 wxDefaultPosition, wxDefaultSize, 0);
821 bsRouteButtonsInner->Add(m_pStaticTextCount, 0, wxALL, 2);
822
823 bsRouteButtonsInner->AddSpacer(2);
824 m_pTextTargetCount = new wxTextCtrl(winr, wxID_ANY, "", wxDefaultPosition,
825 wxDefaultSize, wxTE_READONLY);
826 m_pTextTargetCount->SetMinSize(wxSize(6 * GetCharWidth(), -1));
827 bsRouteButtonsInner->Add(m_pTextTargetCount, 0, wxALL, 2);
828
829 bsRouteButtonsInner->AddSpacer(10);
830 m_pButtonOK = new wxButton(winr, wxID_ANY, _("Close"), wxDefaultPosition,
831 wxDefaultSize, wxBU_AUTODRAW);
832 m_pButtonOK->Connect(
833 wxEVT_COMMAND_BUTTON_CLICKED,
834 wxCommandEventHandler(AISTargetListDialog::OnCloseButton), NULL, this);
835 bsRouteButtonsInner->Add(m_pButtonOK, 0, wxEXPAND | wxALL, 0);
836
837 topSizer->Layout();
838
839 // This is silly, but seems to be required for __WXMSW__ build
840 // If not done, the SECOND invocation of AISTargetList fails to expand the
841 // list to the full wxSizer size....
842 SetSize(GetSize().x, GetSize().y - 1);
843}
844
845void AISTargetListDialog::OnClose(wxCloseEvent &event) {
846 Disconnect_decoder();
847 Hide();
848 g_pAISTargetList = NULL;
849}
850
851void AISTargetListDialog::Disconnect_decoder() { m_pdecoder = NULL; }
852
853void AISTargetListDialog::SetColorScheme() { DimeControl(this); }
854
855void AISTargetListDialog::OnPaneClose(wxAuiManagerEvent &event) {
856 if (event.pane->name == "AISTargetList") {
857 g_AisTargetList_perspective = m_pAuiManager->SavePaneInfo(*event.pane);
858 }
859 event.Skip();
860}
861
862void AISTargetListDialog::OnCloseButton(wxCommandEvent &event) { Shutdown(); }
863
864void AISTargetListDialog::Shutdown() {
865 if (m_pAuiManager) {
866 wxAuiPaneInfo pane = m_pAuiManager->GetPane(this);
867 g_AisTargetList_perspective = m_pAuiManager->SavePaneInfo(pane);
868 m_pAuiManager->DetachPane(this);
869 Disconnect_decoder();
870 pane.Show(false);
871 m_pAuiManager->Update();
872#ifdef __ANDROID__
873 GetParent()->Refresh(true);
874#endif
875 Destroy();
876 }
877}
878
879void AISTargetListDialog::UpdateButtons() {
880 long item = -1;
881 item = m_pListCtrlAISTargets->GetNextItem(item, wxLIST_NEXT_ALL,
882 wxLIST_STATE_SELECTED);
883 bool enable = (item != -1);
884
885 m_pButtonInfo->Enable(enable);
886
887 if (m_pdecoder && item != -1) {
888 auto pAISTargetSel =
889 m_pdecoder->Get_Target_Data_From_MMSI(m_pMMSI_array->Item(item));
890 if (pAISTargetSel && (!pAISTargetSel->b_positionOnceValid)) enable = false;
891 }
892 m_pButtonJumpTo->Enable(enable);
893 m_pButtonJumpTo_Close->Enable(enable);
894 m_pButtonCreateWpt->Enable(enable);
895 m_pButtonToggleTrack->Enable(enable);
896 m_pButtonCopyMMSI->Enable(enable);
897}
898
899void AISTargetListDialog::OnTargetSelected(wxListEvent &event) {
900 UpdateButtons();
901}
902
903void AISTargetListDialog::DoTargetQuery(int mmsi) {
904 ShowAISTargetQueryDialog(m_pparent, mmsi);
905}
906
907/*
908 ** When an item is activated in AIS TArget List then opens the AIS Target Query
909 *Dialog
910 */
911void AISTargetListDialog::OnTargetDefaultAction(wxListEvent &event) {
912 long mmsi_no;
913 if ((mmsi_no = event.GetData())) DoTargetQuery(mmsi_no);
914}
915
916void AISTargetListDialog::OnTargetQuery(wxCommandEvent &event) {
917 long selItemID = -1;
918 selItemID = m_pListCtrlAISTargets->GetNextItem(selItemID, wxLIST_NEXT_ALL,
919 wxLIST_STATE_SELECTED);
920 if (selItemID == -1) return;
921
922 if (m_pdecoder) {
923 auto pAISTarget =
924 m_pdecoder->Get_Target_Data_From_MMSI(m_pMMSI_array->Item(selItemID));
925 if (pAISTarget) DoTargetQuery(pAISTarget->MMSI);
926 }
927}
928
929void AISTargetListDialog::OnAutosortCB(wxCommandEvent &event) {
930 g_bAisTargetList_autosort = m_pCBAutosort->GetValue();
931
932 m_bautosort_force = g_bAisTargetList_autosort;
933
934 if (!g_bAisTargetList_autosort) {
935 wxListItem item;
936 item.SetMask(wxLIST_MASK_IMAGE);
937 item.SetImage(-1);
938 g_AisTargetList_sortColumn = wxMax(g_AisTargetList_sortColumn, 0);
939 m_pListCtrlAISTargets->SetColumn(g_AisTargetList_sortColumn, item);
940 } else {
941 wxListItem item;
942 item.SetMask(wxLIST_MASK_IMAGE);
943 item.SetImage(g_bAisTargetList_sortReverse ? 1 : 0);
944
945 if (g_AisTargetList_sortColumn >= 0) {
946 m_pListCtrlAISTargets->SetColumn(g_AisTargetList_sortColumn, item);
947 UpdateAISTargetList();
948 }
949 }
950}
951
952void AISTargetListDialog::OnTargetListColumnClicked(wxListEvent &event) {
953 int key = event.GetColumn();
954 wxListItem item;
955 item.SetMask(wxLIST_MASK_IMAGE);
956 if (key == g_AisTargetList_sortColumn)
957 g_bAisTargetList_sortReverse = !g_bAisTargetList_sortReverse;
958 else {
959 item.SetImage(-1);
960 m_pListCtrlAISTargets->SetColumn(g_AisTargetList_sortColumn, item);
961 g_bAisTargetList_sortReverse = false;
962 g_AisTargetList_sortColumn = key;
963 }
964 item.SetImage(g_bAisTargetList_sortReverse ? 1 : 0);
965
966 if (!g_bAisTargetList_autosort) g_bsort_once = true;
967
968 if (g_AisTargetList_sortColumn >= 0) {
969 m_pListCtrlAISTargets->SetColumn(g_AisTargetList_sortColumn, item);
970 UpdateAISTargetList();
971 }
972}
973
974void AISTargetListDialog::OnTargetScrollTo(wxCommandEvent &event) {
975 CenterToTarget(false);
976}
977
978void AISTargetListDialog::OnTargetScrollToClose(wxCommandEvent &event) {
979 CenterToTarget(true);
980}
981
982void AISTargetListDialog::OnTargetCreateWpt(wxCommandEvent &event) {
983 long selItemID = -1;
984 selItemID = m_pListCtrlAISTargets->GetNextItem(selItemID, wxLIST_NEXT_ALL,
985 wxLIST_STATE_SELECTED);
986 if (selItemID == -1) return;
987
988 std::shared_ptr<AisTargetData> pAISTarget = NULL;
989 if (m_pdecoder)
990 pAISTarget =
991 m_pdecoder->Get_Target_Data_From_MMSI(m_pMMSI_array->Item(selItemID));
992
993 if (pAISTarget) {
994 RoutePoint *pWP =
995 new RoutePoint(pAISTarget->Lat, pAISTarget->Lon, g_default_wp_icon,
996 wxEmptyString, wxEmptyString);
997 pWP->m_bIsolatedMark = true; // This is an isolated mark
998 pSelect->AddSelectableRoutePoint(pAISTarget->Lat, pAISTarget->Lon, pWP);
999 // pConfig->AddNewWayPoint(pWP, -1); // use auto next num
1000 NavObj_dB::GetInstance().InsertRoutePoint(pWP);
1001
1002 if (pRouteManagerDialog && pRouteManagerDialog->IsShown())
1003 pRouteManagerDialog->UpdateWptListCtrl();
1004 top_frame::Get()->BeforeUndoableAction(Undo_CreateWaypoint, pWP,
1005 Undo_HasParent, NULL);
1006 top_frame::Get()->AfterUndoableAction(NULL);
1007 Refresh(false);
1008 }
1009}
1010
1011void AISTargetListDialog::OnShowAllTracks(wxCommandEvent &event) {
1012 if (m_pdecoder) {
1013 for (const auto &it : m_pdecoder->GetTargetList()) {
1014 auto pAISTarget = it.second;
1015 if (NULL != pAISTarget) {
1016 pAISTarget->b_show_track = true;
1017
1018 // Check for any persistently tracked target, force b_show_track_old ON
1019 std::map<int, Track *>::iterator it;
1020 it = g_pAIS->m_persistent_tracks.find(pAISTarget->MMSI);
1021 if (it != g_pAIS->m_persistent_tracks.end()) {
1022 pAISTarget->b_show_track_old = true;
1023 }
1024 }
1025 }
1026 UpdateAISTargetList();
1027 }
1028}
1029
1030void AISTargetListDialog::OnHideAllTracks(wxCommandEvent &event) {
1031 if (m_pdecoder) {
1032 for (const auto &it : m_pdecoder->GetTargetList()) {
1033 auto pAISTarget = it.second;
1034 if (NULL != pAISTarget) {
1035 pAISTarget->b_show_track = false;
1036
1037 // Check for any persistently tracked target, force b_show_track ON
1038 std::map<int, Track *>::iterator it;
1039 it = g_pAIS->m_persistent_tracks.find(pAISTarget->MMSI);
1040 if (it != g_pAIS->m_persistent_tracks.end()) {
1041 pAISTarget->b_show_track = true;
1042 pAISTarget->b_show_track_old = false;
1043 }
1044 }
1045 }
1046 UpdateAISTargetList();
1047 }
1048}
1049
1050void AISTargetListDialog::OnToggleTrack(wxCommandEvent &event) {
1051 long selItemID = -1;
1052 selItemID = m_pListCtrlAISTargets->GetNextItem(selItemID, wxLIST_NEXT_ALL,
1053 wxLIST_STATE_SELECTED);
1054 if (selItemID == -1) return;
1055
1056 std::shared_ptr<AisTargetData> pAISTarget = NULL;
1057 if (m_pdecoder)
1058 pAISTarget =
1059 m_pdecoder->Get_Target_Data_From_MMSI(m_pMMSI_array->Item(selItemID));
1060
1061 if (pAISTarget) {
1062 pAISTarget->b_show_track_old =
1063 pAISTarget->b_show_track; // Store current state before toggling
1064 pAISTarget->b_show_track = !pAISTarget->b_show_track; // Toggle visibility
1065 UpdateAISTargetList();
1066 }
1067}
1068
1069void AISTargetListDialog::OnCopyMMSI(wxCommandEvent &event) {
1070 long selItemID = -1;
1071 selItemID = m_pListCtrlAISTargets->GetNextItem(selItemID, wxLIST_NEXT_ALL,
1072 wxLIST_STATE_SELECTED);
1073 if (selItemID == -1) return;
1074 CopyMMSItoClipBoard((int)m_pMMSI_array->Item(selItemID));
1075}
1076
1077void AISTargetListDialog::OnEditFindTarget(wxCommandEvent &event) {
1078 wxString name = m_pFindTargetName->GetValue().MakeUpper();
1079 if (name.size() < 2 || name == " ") {
1080 event.StopPropagation(); // Don't send the key upstream
1081 return;
1082 }
1083 if (m_pdecoder) {
1084 bool found = false;
1085 long item_sel = 0;
1086
1087 // Loop to find the exact match of the searched target name.
1088 for (const auto &it : m_pdecoder->GetTargetList()) {
1089 auto pAISTarget = it.second;
1090 if (NULL != pAISTarget) {
1091 wxString s = pAISTarget->GetFullName();
1092 if (name == s) {
1093 found = true;
1094 int selMMSI = pAISTarget->MMSI;
1095 if (selMMSI != -1) {
1096 // Loop the display list to find position for the MMSI
1097 for (unsigned int i = 0; i < m_pMMSI_array->GetCount(); i++) {
1098 if (m_pMMSI_array->Item(i) == selMMSI) {
1099 item_sel = i;
1100 break;
1101 }
1102 }
1103 }
1104 break;
1105 }
1106 }
1107 }
1108 if (!found) {
1109 // Loop again to find parts of the searched target name.
1110 for (const auto &it : m_pdecoder->GetTargetList()) {
1111 auto pAISTarget = it.second;
1112 if (NULL != pAISTarget) {
1113 wxString s = pAISTarget->GetFullName();
1114 if (s.Find(name) != wxNOT_FOUND) {
1115 found = true;
1116 int selMMSI = pAISTarget->MMSI;
1117 if (selMMSI != -1) {
1118 // Loop the display list to find position for the MMSI
1119 for (unsigned int i = 0; i < m_pMMSI_array->GetCount(); i++) {
1120 if (m_pMMSI_array->Item(i) == selMMSI) {
1121 item_sel = i;
1122 break;
1123 }
1124 }
1125 }
1126 break;
1127 }
1128 }
1129 }
1130 }
1131 if (found) {
1132 if (m_pMMSI_array->GetCount())
1133 m_pListCtrlAISTargets->SetItemState(
1134 item_sel, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED,
1135 wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED);
1136
1137 m_pListCtrlAISTargets->EnsureVisible(item_sel);
1138 UpdateAISTargetList();
1139 }
1140 }
1141}
1142
1143void AISTargetListDialog::CenterToTarget(bool close) {
1144 long selItemID = -1;
1145 selItemID = m_pListCtrlAISTargets->GetNextItem(selItemID, wxLIST_NEXT_ALL,
1146 wxLIST_STATE_SELECTED);
1147 if (selItemID == -1) return;
1148
1149 std::shared_ptr<AisTargetData> pAISTarget = NULL;
1150 if (m_pdecoder)
1151 pAISTarget =
1152 m_pdecoder->Get_Target_Data_From_MMSI(m_pMMSI_array->Item(selItemID));
1153
1154 if (pAISTarget) {
1155 double scale = top_frame::Get()->GetAbstractFocusCanvas()->GetVPScale();
1156 if (!close) {
1157 top_frame::Get()->JumpToPosition(
1158 top_frame::Get()->GetAbstractFocusCanvas(), pAISTarget->Lat,
1159 pAISTarget->Lon, scale);
1160 } else {
1161 // Set a resonable (1:5000) chart scale to see the target.
1162 double factor = 1.;
1163 if (scale < 0.7) { // Don't zoom if already close.
1164 AbstractChartCanvas *acc = top_frame::Get()->GetAbstractFocusCanvas();
1165 factor = acc->GetScaleValue() / 5000.0;
1166 }
1167 top_frame::Get()->JumpToPosition(
1168 top_frame::Get()->GetAbstractFocusCanvas(), pAISTarget->Lat,
1169 pAISTarget->Lon, scale * factor);
1170 DoTargetQuery(pAISTarget->MMSI);
1171 // Close AIS target list
1172 Shutdown();
1173 }
1174 }
1175}
1176
1177void AISTargetListDialog::CopyMMSItoClipBoard(int mmsi) {
1178 // Write MMSI # as text to the clipboard
1179 if (wxTheClipboard->Open()) {
1180 wxTheClipboard->SetData(
1181 new wxTextDataObject(wxString::Format("%09d", mmsi)));
1182 wxTheClipboard->Close();
1183 }
1184}
1185void AISTargetListDialog::OnLimitRange(wxCommandEvent &event) {
1186 g_AisTargetList_range = m_pSpinCtrlRange->GetValue();
1187 UpdateAISTargetList();
1188}
1189
1190std::shared_ptr<AisTargetData> AISTargetListDialog::GetpTarget(
1191 unsigned int list_item) {
1192 if (m_pdecoder)
1193 return m_pdecoder->Get_Target_Data_From_MMSI(
1194 m_pMMSI_array->Item(list_item));
1195 else
1196 return NULL;
1197}
1198
1199void AISTargetListDialog::UpdateAISTargetList() {
1200 if (m_pListCtrlAISTargets && !m_pListCtrlAISTargets->IsVirtual())
1201 return UpdateNVAISTargetList();
1202
1203 if (m_pdecoder && m_pListCtrlAISTargets) {
1204 // Capture the MMSI of the curently selected list item
1205 long selItemID = -1;
1206 selItemID = m_pListCtrlAISTargets->GetNextItem(selItemID, wxLIST_NEXT_ALL,
1207 wxLIST_STATE_SELECTED);
1208
1209 int selMMSI = -1;
1210 if (selItemID != -1) selMMSI = m_pMMSI_array->Item(selItemID);
1211
1212 const auto &current_targets = m_pdecoder->GetTargetList();
1213 wxListItem item;
1214
1215 int index = 0;
1216 m_pMMSI_array->Clear();
1217
1218 for (auto it = current_targets.begin(); it != current_targets.end();
1219 ++it, ++index) {
1220 auto pAISTarget = it->second;
1221 item.SetId(index);
1222
1223 if (NULL != pAISTarget) {
1224 bool b_add = false;
1225 if ((pAISTarget->b_positionOnceValid) &&
1226 (pAISTarget->Range_NM <= g_AisTargetList_range))
1227 b_add = true;
1228 else if (!pAISTarget->b_positionOnceValid)
1229 b_add = true;
1230
1231 // Do not show any "lost" targets in the list.
1232 if (pAISTarget->b_lost) b_add = false;
1233
1234 if (b_add) {
1235 m_pMMSI_array->Add(pAISTarget->MMSI);
1236 }
1237 }
1238 }
1239
1240 g_bsort_once = false;
1241
1242 m_pListCtrlAISTargets->SetItemCount(m_pMMSI_array->GetCount());
1243
1244 g_AisTargetList_count = m_pMMSI_array->GetCount();
1245
1246 if ((g_AisTargetList_count > 1000) && !m_bautosort_force)
1247 g_bAisTargetList_autosort = false;
1248
1249 m_pCBAutosort->SetValue(g_bAisTargetList_autosort);
1250
1251 // Restore selected item
1252 long item_sel = 0;
1253 if ((selItemID != -1) && (selMMSI != -1)) {
1254 for (unsigned int i = 0; i < m_pMMSI_array->GetCount(); i++) {
1255 if (m_pMMSI_array->Item(i) == selMMSI) {
1256 item_sel = i;
1257 break;
1258 }
1259 }
1260 }
1261
1262 if (m_pMMSI_array->GetCount())
1263 m_pListCtrlAISTargets->SetItemState(
1264 item_sel, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED,
1265 wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED);
1266 else
1267 m_pListCtrlAISTargets->DeleteAllItems();
1268
1269 wxString count;
1270 count.Printf("%lu", (unsigned long)m_pMMSI_array->GetCount());
1271 m_pTextTargetCount->ChangeValue(count);
1272
1273#ifdef __WXMSW__
1274 m_pListCtrlAISTargets->Refresh(false);
1275#endif
1276 }
1277}
1278
1279void AISTargetListDialog::UpdateNVAISTargetList() {
1280 if (m_pdecoder) {
1281 // Capture the MMSI of the curently selected list item
1282 long selItemID = -1;
1283 selItemID = m_pListCtrlAISTargets->GetNextItem(selItemID, wxLIST_NEXT_ALL,
1284 wxLIST_STATE_SELECTED);
1285
1286 int selMMSI = -1;
1287 if (selItemID != -1) selMMSI = m_pMMSI_array->Item(selItemID);
1288
1289 const auto &current_targets = m_pdecoder->GetTargetList();
1290 wxListItem item;
1291
1292 int index = 0;
1293 m_pMMSI_array->Clear();
1294
1295 for (auto it = current_targets.begin(); it != current_targets.end();
1296 ++it, ++index) {
1297 auto pAISTarget = it->second;
1298 item.SetId(index);
1299
1300 if (NULL != pAISTarget) {
1301 bool b_add = false;
1302 if ((pAISTarget->b_positionOnceValid) &&
1303 (pAISTarget->Range_NM <= g_AisTargetList_range))
1304 b_add = true;
1305 else if (!pAISTarget->b_positionOnceValid)
1306 b_add = true;
1307
1308 if (b_add) {
1309 m_pMMSI_array->Add(pAISTarget->MMSI);
1310 }
1311 }
1312 }
1313
1314 g_bsort_once = false;
1315
1316 g_AisTargetList_count = m_pMMSI_array->GetCount();
1317
1318 m_pListCtrlAISTargets->DeleteAllItems();
1319
1320 for (int i = 0; i < g_AisTargetList_count; i++) {
1321 wxListItem item;
1322 item.SetId(i);
1323 m_pListCtrlAISTargets->InsertItem(item);
1324 for (int j = 0; j < tlTCPA + 1; j++) {
1325 item.SetColumn(j);
1326 item.SetText(m_pListCtrlAISTargets->OnGetItemText(i, j));
1327 m_pListCtrlAISTargets->SetItem(item);
1328 }
1329 }
1330
1331 if ((g_AisTargetList_count > 1000) && !m_bautosort_force)
1332 g_bAisTargetList_autosort = false;
1333
1334 m_pCBAutosort->SetValue(g_bAisTargetList_autosort);
1335
1336 // Restore selected item
1337 long item_sel = 0;
1338 if ((selItemID != -1) && (selMMSI != -1)) {
1339 for (unsigned int i = 0; i < m_pMMSI_array->GetCount(); i++) {
1340 if (m_pMMSI_array->Item(i) == selMMSI) {
1341 item_sel = i;
1342 break;
1343 }
1344 }
1345 }
1346
1347 if (m_pMMSI_array->GetCount())
1348 m_pListCtrlAISTargets->SetItemState(
1349 item_sel, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED,
1350 wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED);
1351 else
1352 m_pListCtrlAISTargets->DeleteAllItems();
1353
1354 wxString count;
1355 count.Printf("%lu", (unsigned long)m_pMMSI_array->GetCount());
1356 m_pTextTargetCount->ChangeValue(count);
1357
1358#ifdef __WXMSW__
1359 m_pListCtrlAISTargets->Refresh(false);
1360#endif
1361 }
1362}
1363
1364void AISTargetListDialog::OnRightClickContext(wxCommandEvent &event) {
1365 wxAuiPaneInfo &pane = m_pAuiManager->GetPane("AISTargetList");
1366 if (pane.IsDocked()) {
1367 wxMenu *popup = new wxMenu();
1368 popup->Append(ID_RCLK_UNDOCK, _("Undock Target List"));
1369 popup->Connect(wxEVT_COMMAND_MENU_SELECTED,
1370 wxCommandEventHandler(AISTargetListDialog::OnContextUndock),
1371 NULL, this);
1372
1373 PopupMenu(popup);
1374 delete popup;
1375 }
1376}
1377
1378void AISTargetListDialog::OnContextUndock(wxCommandEvent &event) {
1379 wxAuiPaneInfo &pane = m_pAuiManager->GetPane("AISTargetList");
1380 pane.Float();
1381 m_pAuiManager->Update();
1382}
AisDecoder * g_pAIS
Global instance.
Class AisDecoder and helpers.
Global state for AIS decoder.
AIS target definitions.
Class AISTargetListDialog.
AISTargetListDialog * g_pAISTargetList
Global instance.
Generic Chart canvas base.
Dialog for displaying a list of AIS targets.
Minimal ChartCAnvas interface with very little dependencies.
A custom list control for displaying AIS target information.
Represents a waypoint or mark within the navigation system.
Definition route_point.h:71
bool m_bIsolatedMark
Flag indicating if the waypoint is a standalone mark.
Platform independent GL includes.
wxFont * GetOCPNScaledFont(wxString item, int default_size)
Retrieves a font from FontMgr, optionally scaled for physical readability.
Definition gui_lib.cpp:61
MySQL based storage for routes, tracks, etc.
MyConfig * pConfig
Global instance.
Definition navutil.cpp:118
AIS info display component.
OpenCPN Platform specific support utilities.
Waypoint or mark abstraction.
RouteManagerDialog * pRouteManagerDialog
Global instance.
Manage routes dialog.
Select * pSelect
Global instance.
Definition select.cpp:36
Selected route, segment, waypoint, etc.
Chart Symbols.
Abstract gFrame/MyFrame interface.