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