OpenCPN Partial API docs
AISTargetQueryDialog.cpp
1 /******************************************************************************
2  *
3  * Project: OpenCPN
4  *
5  ***************************************************************************
6  * Copyright (C) 2013 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/wxprec.h>
26 
27 #include <wx/html/htmlwin.h>
28 
29 #include "model/ais_decoder.h"
30 #include "model/ais_state_vars.h"
31 #include "model/ais_target_data.h"
32 #include "model/route_point.h"
33 #include "model/select.h"
34 #include "model/track.h"
35 
36 #include "ais.h"
37 #include "AISTargetQueryDialog.h"
38 #include "chcanv.h"
39 #include "FontMgr.h"
40 #include "navutil.h"
41 #include "ocpn_frame.h"
42 #include "OCPNPlatform.h"
43 #include "routemanagerdialog.h"
44 
45 extern AISTargetQueryDialog *g_pais_query_dialog_active;
46 extern ColorScheme global_color_scheme;
47 extern wxString g_default_wp_icon;
48 extern MyConfig *pConfig;
49 extern RouteManagerDialog *pRouteManagerDialog;
50 extern std::vector<Track*> g_TrackList;
51 extern OCPNPlatform *g_Platform;
52 extern MyFrame *gFrame;
53 
54 #define xID_OK 10009
55 #define xID_WPT_CREATE 10010
56 #define xID_TRK_CREATE 10011
57 IMPLEMENT_CLASS(AISTargetQueryDialog, wxDialog)
58 // AISTargetQueryDialog event table definition
59 BEGIN_EVENT_TABLE(AISTargetQueryDialog, wxFrame)
60 EVT_BUTTON(xID_OK, AISTargetQueryDialog::OnIdOKClick)
61 EVT_BUTTON(xID_WPT_CREATE, AISTargetQueryDialog::OnIdWptCreateClick)
62 EVT_BUTTON(xID_TRK_CREATE, AISTargetQueryDialog::OnIdTrkCreateClick)
63 EVT_CLOSE(AISTargetQueryDialog::OnClose)
64 EVT_MOVE(AISTargetQueryDialog::OnMove)
65 EVT_SIZE(AISTargetQueryDialog::OnSize)
66 EVT_CHAR_HOOK(AISTargetQueryDialog::OnKey)
67 END_EVENT_TABLE()
68 
70 
71 AISTargetQueryDialog::AISTargetQueryDialog(wxWindow *parent, wxWindowID id,
72  const wxString &caption,
73  const wxPoint &pos,
74  const wxSize &size, long style) {
75  Init();
76  Create(parent, id, caption, pos, size, style);
77 }
78 
79 AISTargetQueryDialog::~AISTargetQueryDialog() { delete m_pQueryTextCtl; }
80 
82  m_MMSI = -1;
83  m_pQueryTextCtl = NULL;
84  m_nl = 0;
85  m_colorscheme = (ColorScheme)(-1);
86  m_okButton = NULL;
87  m_bautoCentre = false;
88  m_bautosize = false;
89 }
90 void AISTargetQueryDialog::OnClose(wxCloseEvent &event) {
91  Destroy();
92  g_pais_query_dialog_active = NULL;
93 }
94 
95 void AISTargetQueryDialog::OnIdOKClick(wxCommandEvent &event) { Close(); }
96 
97 void AISTargetQueryDialog::OnKey(wxKeyEvent &ke) {
98  if (ke.GetKeyCode() == WXK_ESCAPE)
99  Close(true);
100  else
101  ke.Skip();
102 }
103 
104 void AISTargetQueryDialog::OnIdWptCreateClick(wxCommandEvent &event) {
105  if (m_MMSI != 0) { // Faulty MMSI could be reported as 0
106  auto td = g_pAIS->Get_Target_Data_From_MMSI(m_MMSI);
107  if (td) {
108  wxString n0 = wxString::Format(wxT("%s"), td->ShipName);
109  n0.Replace(_T("@"), _T(" "));
110  n0.Trim();
111  wxString mmsi = wxString::Format(wxT("%i "), td->MMSI);
112  wxString n = _T("\"") + n0 + _T("\" ") + mmsi;
113  n.append(wxDateTime::Now().Format(wxT("%H:%M")));
114  // wxString n = wxString::Format(wxT("\"%s\" %i "),td->ShipName,
115  // td->MMSI).append(wxDateTime::Now().Format(wxT("%H:%M")));
116  RoutePoint *pWP =
117  new RoutePoint(td->Lat, td->Lon, g_default_wp_icon, n, wxEmptyString);
118  pWP->m_bIsolatedMark = true; // This is an isolated mark
119  pSelect->AddSelectableRoutePoint(td->Lat, td->Lon, pWP);
120  pConfig->AddNewWayPoint(pWP, -1); // use auto next num
121 
122  if (pRouteManagerDialog && pRouteManagerDialog->IsShown())
123  pRouteManagerDialog->UpdateWptListCtrl();
124  gFrame->GetPrimaryCanvas()->undo->BeforeUndoableAction(
125  Undo_CreateWaypoint, pWP, Undo_HasParent, NULL);
126  gFrame->GetPrimaryCanvas()->undo->AfterUndoableAction(NULL);
127  Refresh(false);
128  }
129  }
130 }
131 
132 void AISTargetQueryDialog::OnIdTrkCreateClick(wxCommandEvent &event) {
133  if (m_MMSI != 0) { // Faulty MMSI could be reported as 0
134  auto td = g_pAIS->Get_Target_Data_From_MMSI(m_MMSI);
135  if (td) {
136  if (td->b_PersistTrack) // The target was tracked and the user wants to
137  // stop it
138  {
139  td->b_PersistTrack = false;
140  g_pAIS->m_persistent_tracks.erase(td->MMSI);
141  m_createTrkBtn->SetLabel(_("Record Track"));
142  td->b_show_track = false;
143  } else {
144  TrackPoint *tp = NULL;
145  TrackPoint *tp1 = NULL;
146 
147  Track *t = new Track();
148 
149  t->SetName(wxString::Format(_T("AIS %s (%u) %s %s"),
150  td->GetFullName().c_str(), td->MMSI,
151  wxDateTime::Now().FormatISODate().c_str(),
152  wxDateTime::Now().FormatISOTime().c_str()));
153  for (const AISTargetTrackPoint &ptrack_point : td->m_ptrack) {
154  vector2D point(ptrack_point.m_lon, ptrack_point.m_lat);
155  tp1 = t->AddNewPoint(point, wxDateTime(ptrack_point.m_time).ToUTC());
156  if (tp) {
157  pSelect->AddSelectableTrackSegment(tp->m_lat, tp->m_lon, tp1->m_lat,
158  tp1->m_lon, tp, tp1, t);
159  }
160  tp = tp1;
161  }
162 
163  g_TrackList.push_back(t);
164  pConfig->AddNewTrack(t);
165  // t->RebuildGUIDList(); // ensure the GUID list is
166  // intact and good
167 
168  if (pRouteManagerDialog && pRouteManagerDialog->IsShown())
169  pRouteManagerDialog->UpdateTrkListCtrl();
170  Refresh(false);
171 
172  if (wxID_YES == OCPNMessageBox(gFrame,
173  _("The recently captured track of this target has been "
174  "recorded.\nDo you want to continue recording until the end "
175  "of the current OpenCPN session?"),
176  _("OpenCPN Info"), wxYES_NO | wxCENTER, 60)) {
177  td->b_PersistTrack = true;
178  g_pAIS->m_persistent_tracks[td->MMSI] = t;
179  td->b_show_track = true;
180  }
181  }
182  }
183  }
184 }
185 
186 bool AISTargetQueryDialog::Create(wxWindow *parent, wxWindowID id,
187  const wxString &caption, const wxPoint &pos,
188  const wxSize &size, long style) {
189  long wstyle = AIS_TARGET_QUERY_STYLE;
190 
191  if (!wxFrame::Create(parent, id, caption, pos, size, wstyle)) return false;
192 
193  m_parent = parent;
194 
195  wxFont *dFont = FontMgr::Get().GetFont(_("AISTargetQuery"));
196  int font_size = wxMax(8, dFont->GetPointSize());
197  wxString face = dFont->GetFaceName();
198 #ifdef __WXGTK__
199  face = _T("Monospace");
200 #endif
201  m_basefont = FontMgr::Get().FindOrCreateFont(font_size, wxFONTFAMILY_MODERN,
202  wxFONTSTYLE_NORMAL,
203  dFont->GetWeight(), false, face);
204 
205  m_adjustedFontSize = dFont->GetPointSize();
206  m_control_font_size = dFont->GetPointSize();
207 
208  CreateControls();
209 
210  SetColorScheme(global_color_scheme);
211 
212  // Set the maximum size of the entire settings dialog
213  wxSize sz = g_Platform->getDisplaySize();
214  SetSizeHints(50, 50, sz.x - 20, sz.y - 40);
215 
216  if (!m_bautosize) {
217  Fit(); // Sets the horizontal size OK
218  Layout();
219  SetSize(-1, m_adjustedFontSize * 30); // Estimated vertical size
220  }
221 
222  return true;
223 }
224 
225 void AISTargetQueryDialog::SetMMSI(int mmsi) {
226  m_MMSI = mmsi;
227 
228  auto td = g_pAIS->Get_Target_Data_From_MMSI(m_MMSI);
229  AdjustBestSize(td.get());
230 }
231 
232 void AISTargetQueryDialog::RecalculateSize() {
233  auto td = g_pAIS->Get_Target_Data_From_MMSI(m_MMSI);
234  AdjustBestSize(td.get());
235  return;
236 }
237 
238 void AISTargetQueryDialog::SetColorScheme(ColorScheme cs) {
239  DimeControl(this);
240  wxColor bg = GetBackgroundColour();
241  m_pQueryTextCtl->SetBackgroundColour(bg);
242  SetBackgroundColour(
243  bg); // This looks like non-sense, but is needed for __WXGTK__
244  // to get colours to propagate down the control's family tree.
245 
246 #ifdef __WXQT__
247  // wxQT has some trouble clearing the background of HTML window...
248  wxBitmap tbm(GetSize().x, GetSize().y, -1);
249  wxMemoryDC tdc(tbm);
250  // wxColour cback = GetGlobalColor( _T("YELO1") );
251  tdc.SetBackground(bg);
252  tdc.Clear();
253  m_pQueryTextCtl->SetBackgroundImage(tbm);
254 #endif
255 
256  if (cs != m_colorscheme) {
257  Refresh();
258  }
259  m_colorscheme = cs;
260 }
261 
262 void AISTargetQueryDialog::CreateControls() {
263  wxBoxSizer *topSizer = new wxBoxSizer(wxVERTICAL);
264  SetSizer(topSizer);
265 
266  m_pQueryTextCtl =
267  new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
268  wxHW_SCROLLBAR_AUTO | wxHW_NO_SELECTION);
269  m_pQueryTextCtl->SetBorders(1);
270  m_pQueryTextCtl->SetFont(*m_basefont);
271  topSizer->Add(m_pQueryTextCtl, 1, wxALL | wxEXPAND, 5);
272 
273  wxSizer *opt = new wxBoxSizer(wxHORIZONTAL);
274  m_createWptBtn = new wxButton(this, xID_WPT_CREATE, _("Create Waypoint"),
275  wxDefaultPosition, wxDefaultSize, 0);
276  opt->Add(m_createWptBtn, 0, wxALL | wxEXPAND, 5);
277 
278  m_createTrkBtn = new wxButton(this, xID_TRK_CREATE, _("Record Track"),
279  wxDefaultPosition, wxDefaultSize, 0);
280  opt->Add(m_createTrkBtn, 0, wxALL | wxEXPAND, 5);
281  topSizer->Add(opt, 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 5);
282 
283  topSizer->Add(new wxButton(this, xID_OK, _("OK")), 0,
284  wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 5);
285 
286  Fit();
287 }
288 
289 void AISTargetQueryDialog::UpdateText() {
290  wxString html;
291 
292  if (!m_pQueryTextCtl) return;
293 
294  int scroll_x, scroll_y;
295  m_pQueryTextCtl->GetViewStart(&scroll_x, &scroll_y);
296 
297  auto td = g_pAIS->Get_Target_Data_From_MMSI(m_MMSI);
298  // AdjustBestSize(td);
299 
300  DimeControl(this);
301  wxColor bg = GetBackgroundColour();
302  m_pQueryTextCtl->SetBackgroundColour(bg);
303  SetBackgroundColour(bg);
304 
305  if (td) {
306  if (td->b_PersistTrack)
307  m_createTrkBtn->SetLabel(_("Stop Tracking"));
308  else
309  m_createTrkBtn->SetLabel(_("Record Track"));
310 
311  m_createWptBtn->Enable(td->b_positionOnceValid);
312 
313  if (td->Class == AIS_METEO || td->Class == AIS_BASE)
314  m_createTrkBtn->Disable();
315  else
316  m_createTrkBtn->Enable();
317 
318  RenderHTMLQuery(td.get());
319  }
320 
321 #ifdef __WXQT__
322  SetColorScheme(m_colorscheme);
323 #endif
324 
325  m_pQueryTextCtl->Scroll(scroll_x, scroll_y);
326 }
327 
328 void AISTargetQueryDialog::OnMove(wxMoveEvent &event) {
329  // Record the dialog position
330  wxPoint p = event.GetPosition();
331  g_ais_query_dialog_x = p.x;
332  g_ais_query_dialog_y = p.y;
333 
334  event.Skip();
335 }
336 
337 void AISTargetQueryDialog::OnSize(wxSizeEvent &event) { event.Skip(); }
338 
339 void AISTargetQueryDialog::AdjustBestSize(AisTargetData *td) {
340  if (!td) return;
341 
342  wxSize origSize = GetSize();
343 
344  Fit();
345  RenderHTMLQuery(td);
346 
347  int target_x = -1;
348  int target_y = -1;
349 
350  // Width adjustments
351 
352  if (m_bautosize) {
353  // Reduce the font size if necessary to eliminate horizontal scroll bars.
354  wxSize szv = m_pQueryTextCtl->GetVirtualSize();
355  if (szv.x > m_pQueryTextCtl->GetSize().x) {
356  while ((szv.x > m_pQueryTextCtl->GetSize().x) &&
357  (m_adjustedFontSize > 8)) { // fluff
358  m_adjustedFontSize--;
359 
360  RenderHTMLQuery(td);
361  m_pQueryTextCtl->Refresh();
362  m_pQueryTextCtl->Update();
363  Layout();
364  szv = m_pQueryTextCtl->GetVirtualSize();
365  }
366 
367  m_adjustedFontSize--;
368  }
369  target_x = szv.x * 12/10; // Making the winfow a bit wider than absolutely nesessary gives a little better results in real world
370  } else {
371  wxSize szv = m_pQueryTextCtl->GetVirtualSize();
372  int csz = g_Platform->getDisplaySize().x * 8 / 10;
373  if ((szv.x) < csz) {
374  if (szv.x > m_pQueryTextCtl->GetSize().x) target_x = szv.x; // * 11/10;
375  }
376  target_x = szv.x * 12/10; // Making the winfow a bit wider than absolutely nesessary gives a little better results in real world
377  }
378 
379 #ifdef __ANDROID__
380  // Now adjust the font size used for the control buttons.
381  // This adjustment makes sure that the two horizontal buttons are not wider
382  // than the platform display allows. This may be a problem on phones,
383  // but probably never on normal computer displays. some platforms also don't support this at all
384 
385  if (m_createWptBtn && m_createTrkBtn) {
386  wxSize psz = g_Platform->getDisplaySize();
387 
388  wxScreenDC dc;
389  wxFont *tFont = FontMgr::Get().FindOrCreateFont(
390  m_control_font_size, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL,
391  m_basefont->GetWeight(), false, m_basefont->GetFaceName());
392  dc.SetFont(*tFont);
393 
394  wxSize tsz = dc.GetTextExtent(m_createWptBtn->GetLabel() +
395  m_createTrkBtn->GetLabel());
396 
397  float totalButtonWidth = tsz.x;
398 
399  if (totalButtonWidth * 1.5 > psz.x) {
400  float delta = (float)totalButtonWidth * 2. / psz.x;
401 
402  float font_size = m_control_font_size / delta;
403 
404  wxFont *fp_font = FontMgr::Get().FindOrCreateFont(
405  font_size, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL,
406  m_basefont->GetWeight(), false, m_basefont->GetFaceName());
407 
408  m_createWptBtn->SetFont(*fp_font);
409  m_createTrkBtn->SetFont(*fp_font);
410 
411  m_control_font_size = font_size;
412  }
413  }
414 #endif
415 
416  // Height adjustments
417  // Try to avoid vertical scroll bar if possible.
418 
419  // Estimate the control button area height
420  int yb = 0;
421  if (m_createWptBtn) yb = m_createWptBtn->GetSize().y * 4;
422 
423  wxSize szyv = m_pQueryTextCtl->GetVirtualSize();
424  int csz = g_Platform->getDisplaySize().y * 85 / 100;
425  if ((szyv.y + yb) < csz) {
426  if (szyv.y > m_pQueryTextCtl->GetSize().y)
427  target_y = szyv.y * 12 / 10 + yb;
428  } else {
429  target_y = csz;
430  }
431  SetSize(target_x, target_y);
432 
433  wxSize nowSize = GetSize();
434 
435  if (nowSize != origSize) {
436  if (m_bautoCentre) Centre();
437  }
438 }
439 
440 void AISTargetQueryDialog::RenderHTMLQuery(AisTargetData *td) {
441  int font_size = m_adjustedFontSize;
442  wxFont *fp_font = FontMgr::Get().FindOrCreateFont(
443  font_size, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL,
444  m_basefont->GetWeight(), false, m_basefont->GetFaceName());
445 
446  SetFont(*fp_font);
447 
448  int sizes[7];
449  for (int i = -2; i < 5; i++) {
450  sizes[i + 2] = fp_font->GetPointSize() + i + (i > 0 ? i : 0);
451  }
452 
453  wxString html;
454  wxColor bg = GetBackgroundColour();
455  wxColor fg = GetForegroundColour();
456 
457  html.Printf(
458  _T("<html><body bgcolor=#%02x%02x%02x><font ")
459  _T("color=#%02x%02x%02x><center>"),
460  bg.Red(), bg.Green(), bg.Blue(), fg.Red(), fg.Green(), fg.Blue());
461 
462  html << td->BuildQueryResult();
463 
464  html << _T("</center></font></body></html>");
465 
466  m_pQueryTextCtl->SetFonts(fp_font->GetFaceName(), fp_font->GetFaceName(),
467  sizes);
468 
469  wxCharBuffer buf = html.ToUTF8();
470  if (buf.data()) // string OK?
471  m_pQueryTextCtl->SetPage(html);
472 }
Global state for AIS decoder.
AISTargetQueryDialog()
Constructors.
bool Create(wxWindow *parent, wxWindowID id=wxID_ANY, const wxString &caption=_("Object Query"), const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=AIS_TARGET_QUERY_STYLE)
Creation.
void Init()
Initialise our variables.
Definition: track.h:78