OpenCPN Partial API docs
Loading...
Searching...
No Matches
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"
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
45extern AISTargetQueryDialog *g_pais_query_dialog_active;
46extern ColorScheme global_color_scheme;
47extern wxString g_default_wp_icon;
48extern MyConfig *pConfig;
49extern RouteManagerDialog *pRouteManagerDialog;
50extern std::vector<Track *> g_TrackList;
51extern OCPNPlatform *g_Platform;
52extern MyFrame *gFrame;
53
54#define xID_OK 10009
55#define xID_WPT_CREATE 10010
56#define xID_TRK_CREATE 10011
57IMPLEMENT_CLASS(AISTargetQueryDialog, wxDialog)
58// AISTargetQueryDialog event table definition
59BEGIN_EVENT_TABLE(AISTargetQueryDialog, wxFrame)
60EVT_BUTTON(xID_OK, AISTargetQueryDialog::OnIdOKClick)
61EVT_BUTTON(xID_WPT_CREATE, AISTargetQueryDialog::OnIdWptCreateClick)
62EVT_BUTTON(xID_TRK_CREATE, AISTargetQueryDialog::OnIdTrkCreateClick)
63EVT_CLOSE(AISTargetQueryDialog::OnClose)
64EVT_MOVE(AISTargetQueryDialog::OnMove)
65EVT_SIZE(AISTargetQueryDialog::OnSize)
66EVT_CHAR_HOOK(AISTargetQueryDialog::OnKey)
67END_EVENT_TABLE()
68
70
71AISTargetQueryDialog::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
79AISTargetQueryDialog::~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}
90void AISTargetQueryDialog::OnClose(wxCloseEvent &event) {
91 Destroy();
92 g_pais_query_dialog_active = NULL;
93}
94
95void AISTargetQueryDialog::OnIdOKClick(wxCommandEvent &event) { Close(); }
96
97void AISTargetQueryDialog::OnKey(wxKeyEvent &ke) {
98 if (ke.GetKeyCode() == WXK_ESCAPE)
99 Close(true);
100 else
101 ke.Skip();
102}
103
104void 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
132void 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 ==
173 OCPNMessageBox(
174 gFrame,
175 _("The recently captured track of this target has been "
176 "recorded.\nDo you want to continue recording until the end "
177 "of the current OpenCPN session?"),
178 _("OpenCPN Info"), wxYES_NO | wxCENTER, 60)) {
179 td->b_PersistTrack = true;
180 g_pAIS->m_persistent_tracks[td->MMSI] = t;
181 td->b_show_track = true;
182 }
183 }
184 }
185 }
186}
187
188bool AISTargetQueryDialog::Create(wxWindow *parent, wxWindowID id,
189 const wxString &caption, const wxPoint &pos,
190 const wxSize &size, long style) {
191 long wstyle = AIS_TARGET_QUERY_STYLE;
192
193 if (!wxFrame::Create(parent, id, caption, pos, size, wstyle)) return false;
194
195 m_parent = parent;
196
197 wxFont *dFont = FontMgr::Get().GetFont(_("AISTargetQuery"));
198 int font_size = wxMax(8, dFont->GetPointSize());
199 wxString face = dFont->GetFaceName();
200#ifdef __WXGTK__
201 face = _T("Monospace");
202#endif
203 m_basefont = FontMgr::Get().FindOrCreateFont(font_size, wxFONTFAMILY_MODERN,
204 wxFONTSTYLE_NORMAL,
205 dFont->GetWeight(), false, face);
206
207 m_adjustedFontSize = dFont->GetPointSize();
208 m_control_font_size = dFont->GetPointSize();
209
210 CreateControls();
211
212 SetColorScheme(global_color_scheme);
213
214 // Set the maximum size of the entire settings dialog
215 wxSize sz = g_Platform->getDisplaySize();
216 SetSizeHints(50, 50, sz.x - 20, sz.y - 40);
217
218 if (!m_bautosize) {
219 Fit(); // Sets the horizontal size OK
220 Layout();
221 SetSize(-1, m_adjustedFontSize * 30); // Estimated vertical size
222 }
223
224 return true;
225}
226
227void AISTargetQueryDialog::SetMMSI(int mmsi) {
228 m_MMSI = mmsi;
229
230 auto td = g_pAIS->Get_Target_Data_From_MMSI(m_MMSI);
231 AdjustBestSize(td.get());
232}
233
234void AISTargetQueryDialog::RecalculateSize() {
235 auto td = g_pAIS->Get_Target_Data_From_MMSI(m_MMSI);
236 AdjustBestSize(td.get());
237 return;
238}
239
240void AISTargetQueryDialog::SetColorScheme(ColorScheme cs) {
241 DimeControl(this);
242 wxColor bg = GetBackgroundColour();
243 m_pQueryTextCtl->SetBackgroundColour(bg);
244 SetBackgroundColour(
245 bg); // This looks like non-sense, but is needed for __WXGTK__
246 // to get colours to propagate down the control's family tree.
247
248#ifdef __WXQT__
249 // wxQT has some trouble clearing the background of HTML window...
250 wxBitmap tbm(GetSize().x, GetSize().y, -1);
251 wxMemoryDC tdc(tbm);
252 // wxColour cback = GetGlobalColor( _T("YELO1") );
253 tdc.SetBackground(bg);
254 tdc.Clear();
255 m_pQueryTextCtl->SetBackgroundImage(tbm);
256#endif
257
258 if (cs != m_colorscheme) {
259 Refresh();
260 }
261 m_colorscheme = cs;
262}
263
264void AISTargetQueryDialog::CreateControls() {
265 wxBoxSizer *topSizer = new wxBoxSizer(wxVERTICAL);
266 SetSizer(topSizer);
267
268 m_pQueryTextCtl =
269 new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
270 wxHW_SCROLLBAR_AUTO | wxHW_NO_SELECTION);
271 m_pQueryTextCtl->SetBorders(1);
272 m_pQueryTextCtl->SetFont(*m_basefont);
273 topSizer->Add(m_pQueryTextCtl, 1, wxALL | wxEXPAND, 5);
274
275 wxSizer *opt = new wxBoxSizer(wxHORIZONTAL);
276 m_createWptBtn = new wxButton(this, xID_WPT_CREATE, _("Create Waypoint"),
277 wxDefaultPosition, wxDefaultSize, 0);
278 opt->Add(m_createWptBtn, 0, wxALL | wxEXPAND, 5);
279
280 m_createTrkBtn = new wxButton(this, xID_TRK_CREATE, _("Record Track"),
281 wxDefaultPosition, wxDefaultSize, 0);
282 opt->Add(m_createTrkBtn, 0, wxALL | wxEXPAND, 5);
283 topSizer->Add(opt, 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 5);
284
285 topSizer->Add(new wxButton(this, xID_OK, _("OK")), 0,
286 wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 5);
287
288 Fit();
289}
290
291void AISTargetQueryDialog::UpdateText() {
292 wxString html;
293
294 if (!m_pQueryTextCtl) return;
295
296 int scroll_x, scroll_y;
297 m_pQueryTextCtl->GetViewStart(&scroll_x, &scroll_y);
298
299 auto td = g_pAIS->Get_Target_Data_From_MMSI(m_MMSI);
300 // AdjustBestSize(td);
301
302 DimeControl(this);
303 wxColor bg = GetBackgroundColour();
304 m_pQueryTextCtl->SetBackgroundColour(bg);
305 SetBackgroundColour(bg);
306
307 if (td) {
308 if (td->b_PersistTrack)
309 m_createTrkBtn->SetLabel(_("Stop Tracking"));
310 else
311 m_createTrkBtn->SetLabel(_("Record Track"));
312
313 m_createWptBtn->Enable(td->b_positionOnceValid);
314
315 if (td->Class == AIS_METEO || td->Class == AIS_BASE)
316 m_createTrkBtn->Disable();
317 else
318 m_createTrkBtn->Enable();
319
320 RenderHTMLQuery(td.get());
321 }
322
323#ifdef __WXQT__
324 SetColorScheme(m_colorscheme);
325#endif
326
327 m_pQueryTextCtl->Scroll(scroll_x, scroll_y);
328}
329
330void AISTargetQueryDialog::OnMove(wxMoveEvent &event) {
331 // Record the dialog position
332 wxPoint p = event.GetPosition();
333 g_ais_query_dialog_x = p.x;
334 g_ais_query_dialog_y = p.y;
335
336 event.Skip();
337}
338
339void AISTargetQueryDialog::OnSize(wxSizeEvent &event) { event.Skip(); }
340
341void AISTargetQueryDialog::AdjustBestSize(AisTargetData *td) {
342 if (!td) return;
343
344 wxSize origSize = GetSize();
345
346 Fit();
347 RenderHTMLQuery(td);
348
349 int target_x = -1;
350 int target_y = -1;
351
352 // Width adjustments
353
354 if (m_bautosize) {
355 // Reduce the font size if necessary to eliminate horizontal scroll bars.
356 wxSize szv = m_pQueryTextCtl->GetVirtualSize();
357 if (szv.x > m_pQueryTextCtl->GetSize().x) {
358 while ((szv.x > m_pQueryTextCtl->GetSize().x) &&
359 (m_adjustedFontSize > 8)) { // fluff
360 m_adjustedFontSize--;
361
362 RenderHTMLQuery(td);
363 m_pQueryTextCtl->Refresh();
364 m_pQueryTextCtl->Update();
365 Layout();
366 szv = m_pQueryTextCtl->GetVirtualSize();
367 }
368
369 m_adjustedFontSize--;
370 }
371 target_x = szv.x * 12 /
372 10; // Making the winfow a bit wider than absolutely nesessary
373 // gives a little better results in real world
374 } else {
375 wxSize szv = m_pQueryTextCtl->GetVirtualSize();
376 int csz = g_Platform->getDisplaySize().x * 8 / 10;
377 if ((szv.x) < csz) {
378 if (szv.x > m_pQueryTextCtl->GetSize().x) target_x = szv.x; // * 11/10;
379 }
380 target_x = szv.x * 12 /
381 10; // Making the winfow a bit wider than absolutely nesessary
382 // gives a little better results in real world
383 }
384
385#ifdef __ANDROID__
386 // Now adjust the font size used for the control buttons.
387 // This adjustment makes sure that the two horizontal buttons are not wider
388 // than the platform display allows. This may be a problem on phones,
389 // but probably never on normal computer displays. some platforms also don't
390 // support this at all
391
392 if (m_createWptBtn && m_createTrkBtn) {
393 wxSize psz = g_Platform->getDisplaySize();
394
395 wxScreenDC dc;
396 wxFont *tFont = FontMgr::Get().FindOrCreateFont(
397 m_control_font_size, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL,
398 m_basefont->GetWeight(), false, m_basefont->GetFaceName());
399 dc.SetFont(*tFont);
400
401 wxSize tsz = dc.GetTextExtent(m_createWptBtn->GetLabel() +
402 m_createTrkBtn->GetLabel());
403
404 float totalButtonWidth = tsz.x;
405
406 if (totalButtonWidth * 1.5 > psz.x) {
407 float delta = (float)totalButtonWidth * 2. / psz.x;
408
409 float font_size = m_control_font_size / delta;
410
411 wxFont *fp_font = FontMgr::Get().FindOrCreateFont(
412 font_size, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL,
413 m_basefont->GetWeight(), false, m_basefont->GetFaceName());
414
415 m_createWptBtn->SetFont(*fp_font);
416 m_createTrkBtn->SetFont(*fp_font);
417
418 m_control_font_size = font_size;
419 }
420 }
421#endif
422
423 // Height adjustments
424 // Try to avoid vertical scroll bar if possible.
425
426 // Estimate the control button area height
427 int yb = 0;
428 if (m_createWptBtn) yb = m_createWptBtn->GetSize().y * 4;
429
430 wxSize szyv = m_pQueryTextCtl->GetVirtualSize();
431 int csz = g_Platform->getDisplaySize().y * 85 / 100;
432 if ((szyv.y + yb) < csz) {
433 if (szyv.y > m_pQueryTextCtl->GetSize().y) target_y = szyv.y * 12 / 10 + yb;
434 } else {
435 target_y = csz;
436 }
437 SetSize(target_x, target_y);
438
439 wxSize nowSize = GetSize();
440
441 if (nowSize != origSize) {
442 if (m_bautoCentre) Centre();
443 }
444}
445
446void AISTargetQueryDialog::RenderHTMLQuery(AisTargetData *td) {
447 int font_size = m_adjustedFontSize;
448 wxFont *fp_font = FontMgr::Get().FindOrCreateFont(
449 font_size, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL,
450 m_basefont->GetWeight(), false, m_basefont->GetFaceName());
451
452 SetFont(*fp_font);
453
454 int sizes[7];
455 for (int i = -2; i < 5; i++) {
456 sizes[i + 2] = fp_font->GetPointSize() + i + (i > 0 ? i : 0);
457 }
458
459 wxString html;
460 wxColor bg = GetBackgroundColour();
461 wxColor fg = GetForegroundColour();
462
463 html.Printf(
464 _T("<html><body bgcolor=#%02x%02x%02x><font ")
465 _T("color=#%02x%02x%02x><center>"),
466 bg.Red(), bg.Green(), bg.Blue(), fg.Red(), fg.Green(), fg.Blue());
467
468 html << td->BuildQueryResult();
469
470 html << _T("</center></font></body></html>");
471
472 m_pQueryTextCtl->SetFonts(fp_font->GetFaceName(), fp_font->GetFaceName(),
473 sizes);
474
475 wxCharBuffer buf = html.ToUTF8();
476 if (buf.data()) // string OK?
477 m_pQueryTextCtl->SetPage(html);
478}
Global state for AIS decoder.
Dialog for querying detailed information about an AIS target.
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.
wxFont * FindOrCreateFont(int point_size, wxFontFamily family, wxFontStyle style, wxFontWeight weight, bool underline=false, const wxString &facename=wxEmptyString, wxFontEncoding encoding=wxFONTENCODING_DEFAULT)
Creates or finds a matching font in the font cache.
Definition FontMgr.cpp:450
wxFont * GetFont(const wxString &TextElement, int requested_font_size=0)
Gets a font object for a UI element.
Definition FontMgr.cpp:186
Main application frame.
Definition ocpn_frame.h:136
Provides platform-specific support utilities for OpenCPN.
wxSize getDisplaySize()
Get the display size in logical pixels.
Represents a single point in a track.
Definition track.h:52
Represents a track, which is a series of connected track points.
Definition track.h:78