OpenCPN Partial API docs
AISTargetAlertDialog.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 // For compilers that support precompilation, includes "wx.h".
26 #include <wx/wxprec.h>
27 
28 #ifndef WX_PRECOMP
29 #include <wx/wx.h>
30 #endif // precompiled headers
31 
32 #include <wx/html/htmlwin.h>
33 
34 #include "model/ais_decoder.h"
35 #include "model/ais_state_vars.h"
36 #include "model/ais_target_data.h"
37 #include "model/ocpn_types.h"
38 #include "model/ocpn_types.h"
39 #include "model/route_point.h"
40 #include "model/select.h"
41 
42 #include "AISTargetAlertDialog.h"
43 #include "chcanv.h"
44 #include "FontMgr.h"
45 #include "navutil.h"
46 #include "ocpn_frame.h"
47 #include "OCPNPlatform.h"
48 #include "routemanagerdialog.h"
49 
50 #ifdef __ANDROID__
51 #include <QDebug>
52 #include "androidUTIL.h"
53 #endif
54 
55 extern ColorScheme global_color_scheme;
56 extern bool g_bopengl;
57 extern MyFrame *gFrame;
58 extern int g_ais_alert_dialog_y;
59 extern wxString g_default_wp_icon;
60 extern MyConfig *pConfig;
61 extern RouteManagerDialog *pRouteManagerDialog;
62 extern OCPNPlatform *g_Platform;
63 
64 //---------------------------------------------------------------------------------------------------------------------
65 //
66 // OCPN Alert Dialog Base Class implementation
67 //
68 //---------------------------------------------------------------------------------------------------------------------
69 
70 IMPLEMENT_CLASS(OCPN_AlertDialog, wxDialog)
71 
72 BEGIN_EVENT_TABLE(OCPN_AlertDialog, wxDialog)
73 END_EVENT_TABLE()
74 
76 
77 OCPN_AlertDialog::~OCPN_AlertDialog() {}
78 
79 void OCPN_AlertDialog::Init(void) { m_pparent = NULL; }
80 
81 bool OCPN_AlertDialog::Create(wxWindow *parent, wxWindowID id,
82  const wxString &caption, const wxPoint &pos,
83  const wxSize &size, long style)
84 
85 {
86  long wstyle = wxDEFAULT_FRAME_STYLE;
87 
88  wxSize size_min = size;
89  size_min.IncTo(wxSize(500, 600));
90  if (!wxDialog::Create(parent, id, caption, pos, size_min, wstyle))
91  return false;
92 
93  m_pparent = parent;
94 
95  if (!g_bopengl && CanSetTransparent()) SetTransparent(192);
96 
97  return true;
98 }
99 
100 //---------------------------------------------------------------------------------------------------------------------
101 //
102 // AIS Target Alert Dialog implementation
103 //
104 //---------------------------------------------------------------------------------------------------------------------
105 
106 IMPLEMENT_CLASS(AISTargetAlertDialog, wxDialog)
107 
108 BEGIN_EVENT_TABLE(AISTargetAlertDialog, wxDialog)
109 EVT_CLOSE(AISTargetAlertDialog::OnClose)
110 EVT_BUTTON(ID_ACKNOWLEDGE, AISTargetAlertDialog::OnIdAckClick)
111 EVT_BUTTON(ID_SILENCE, AISTargetAlertDialog::OnIdSilenceClick)
112 EVT_BUTTON(ID_JUMPTO, AISTargetAlertDialog::OnIdJumptoClick)
113 EVT_BUTTON(ID_WPT_CREATE, AISTargetAlertDialog::OnIdCreateWPClick)
114 EVT_MOVE(AISTargetAlertDialog::OnMove)
115 EVT_SIZE(AISTargetAlertDialog::OnSize)
116 END_EVENT_TABLE()
117 
119 
120 AISTargetAlertDialog::~AISTargetAlertDialog() {}
121 
122 void AISTargetAlertDialog::Init() {
123  m_target_mmsi = 0;
124  m_max_nline = 20;
125  m_adj_height = 0;
126  m_bsizeSet = false;
127  m_pParent = 0;
128 }
129 
130 bool AISTargetAlertDialog::Create(int target_mmsi, wxWindow *parent,
131  AisDecoder *pdecoder, bool b_jumpto,
132  bool b_createWP, bool b_ack, wxWindowID id,
133  const wxString &caption, const wxPoint &pos,
134  const wxSize &size, long style)
135 
136 {
137  OCPN_AlertDialog::Create(parent, id, caption, pos, size, style);
138  m_bjumpto = b_jumpto;
139  m_back = b_ack;
140  m_bcreateWP = b_createWP;
141 
142  m_target_mmsi = target_mmsi;
143  m_pdecoder = pdecoder;
144  m_pParent = parent;
145 
146  wxFont *dFont = FontMgr::Get().GetFont(_("AISTargetAlert"), 12);
147  int font_size = wxMax(8, dFont->GetPointSize());
148  wxString face = dFont->GetFaceName();
149 #ifdef __WXGTK__
150  face = _T("Monospace");
151 #endif
152  wxFont *fp_font = FontMgr::Get().FindOrCreateFont(
153  font_size, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, dFont->GetWeight(),
154  false, face);
155 
156  SetFont(*fp_font);
157 
158  CreateControls();
159  if (!g_bopengl && CanSetTransparent()) SetTransparent(192);
160  DimeControl(this);
161  wxColor bg = GetBackgroundColour();
162  m_pAlertTextCtl->SetBackgroundColour(bg);
163  SetBackgroundColour(bg);
164 
165  return true;
166 }
167 
168 void AISTargetAlertDialog::CreateControls() {
169  wxBoxSizer *topSizer = new wxBoxSizer(wxVERTICAL);
170  SetSizer(topSizer);
171  long style = wxHW_SCROLLBAR_AUTO;
172  if (g_btouch) style |= wxHW_NO_SELECTION;
173 
174  m_pAlertTextCtl =
175  new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, style);
176 #ifdef __ANDROID__
177  m_pAlertTextCtl->GetHandle()->setStyleSheet(getQtStyleSheet());
178 #endif
179 
180  m_pAlertTextCtl->SetBorders(5);
181  m_pAlertTextCtl->SetScrollRate(1, 1);
182 
183  topSizer->Add(m_pAlertTextCtl, 1, wxALL | wxEXPAND, 5);
184 
185  // A flex sizer to contain Ack and more buttons
186  wxFlexGridSizer *AckBox = new wxFlexGridSizer(2);
187  topSizer->Add(AckBox, 0, wxALL, 5);
188 
189  // The Silence button
190  if (g_bAIS_CPA_Alert_Audio) {
191  wxButton *silence = new wxButton(this, ID_SILENCE, _("&Silence Alert"),
192  wxDefaultPosition, wxDefaultSize, 0);
193  AckBox->Add(silence, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
194  }
195 
196  if (m_bjumpto) {
197  wxButton *jumpto = new wxButton(this, ID_JUMPTO, _("&Jump To"),
198  wxDefaultPosition, wxDefaultSize, 0);
199  AckBox->Add(jumpto, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
200  }
201 
202  if (m_bcreateWP) {
203  wxButton *createWptBtn =
204  new wxButton(this, ID_WPT_CREATE, _("Create Waypoint"),
205  wxDefaultPosition, wxDefaultSize, 0);
206  AckBox->Add(createWptBtn, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
207  }
208  // The Ack button
209  // Also used to close a DSC Alert
210  wxString acktext = _("&Acknowledge");
211  bool show_ack_button = false;
212  if (m_bjumpto && m_bcreateWP) { //DSC Alert only
213  acktext = _("&Close Alert");
214  show_ack_button = true;
215  }
216  if (m_back || show_ack_button) {
217  wxButton *ack = new wxButton(this, ID_ACKNOWLEDGE, acktext,
218  wxDefaultPosition, wxDefaultSize, 0);
219  AckBox->Add(ack, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
220  }
221 
222  UpdateText();
223 
224  RecalculateSize();
225 }
226 
227 bool AISTargetAlertDialog::GetAlertText() {
228  // Search the parent AisDecoder's target list for specified mmsi
229  if (m_pdecoder) {
230  auto td_found = m_pdecoder->Get_Target_Data_From_MMSI(Get_Dialog_MMSI());
231 
232  if (td_found) {
233  m_alert_text = td_found->BuildQueryResult();
234  return true;
235  } else
236  return false;
237  } else
238  return false;
239 }
240 
241 void AISTargetAlertDialog::UpdateText() {
242  if (GetAlertText()) {
243  // Capture current scroll position
244  int x, y;
245  m_pAlertTextCtl->GetViewStart( &x, &y);
246 
247  wxFont *dFont = FontMgr::Get().GetFont(_("AISTargetAlert"), 12);
248  wxString face = dFont->GetFaceName();
249  int sizes[7];
250  for (int i = -2; i < 5; i++) {
251  sizes[i + 2] = dFont->GetPointSize() + i + (i > 0 ? i : 0);
252  }
253 
254  wxString html;
255  wxColor bg = GetBackgroundColour();
256  wxColor fg = GetForegroundColour();
257 
258  html.Printf(
259  _T("<html><body bgcolor=#%02x%02x%02x><font ")
260  _T("color=#%02x%02x%02x><center>"),
261  bg.Red(), bg.Green(), bg.Blue(), fg.Red(), fg.Green(), fg.Blue());
262 
263  html << m_alert_text;
264  html << _T("</center></font></body></html>");
265 
266  m_pAlertTextCtl->SetFonts(face, face, sizes);
267  m_pAlertTextCtl->SetPage(html);
268 
269  RecalculateSize();
270 
271  // Restore scroll position
272  m_pAlertTextCtl->Scroll(x, y);
273  }
274 
275  SetColorScheme();
276  if (!g_bopengl && CanSetTransparent()) SetTransparent(192);
277 }
278 
279 void AISTargetAlertDialog::RecalculateSize(void) {
280  // Count the lines in the currently displayed text string
281  unsigned int i = 0;
282  int nline = 0;
283  while (i < m_alert_text.Length()) {
284  if (m_alert_text[i] == '\n') nline++;
285  i++;
286  }
287 
288  if (nline > m_max_nline) m_max_nline = nline;
289 
290  wxSize esize;
291  esize.x = GetCharWidth() * 45;
292  esize.y = GetCharHeight() * (m_max_nline + 4);
293  // SetSize(esize);
294 
295  int height = m_pAlertTextCtl->GetInternalRepresentation()->GetHeight();
296  int adj_height = height + (GetCharHeight() * 6);
297  m_adj_height = wxMax(m_adj_height, adj_height);
298 
299  esize.y = wxMin(esize.y, m_adj_height);
300 
301  Fit(); // Sets the horizontal size OK, with respect to the buttons
302 
303  // If there is only one button shown, the resulting Fit() size may be too
304  // narrow. Adjust it, considering the size of the rendered HTML text content.
305  int textWidth = m_pAlertTextCtl->GetInternalRepresentation()->GetWidth();
306  wxSize gSize = GetClientSize();
307  int adjustedWidth = wxMax(GetClientSize().x, textWidth + GetCharHeight() * 2);
308  SetClientSize(adjustedWidth, esize.y);
309 
310  // Constrain the vertical size to be no larger than parent window height
311  if (m_pParent) {
312  wxSize wsize = m_pParent->GetSize();
313  SetSize(-1, wxMin(esize.y, wsize.y));
314  }
315 
316  g_Platform->PositionAISAlert(this);
317 }
318 
319 void AISTargetAlertDialog::SetColorScheme(void) {
320  DimeControl(this);
321  wxColor bg = GetBackgroundColour();
322  m_pAlertTextCtl->SetBackgroundColour(bg);
323  SetBackgroundColour(
324  bg); // This looks like non-sense, but is needed for __WXGTK__
325  // to get colours to propagate down the control's family tree.
326 
327 #ifdef __WXQT__
328  // wxQT has some trouble clearing the background of HTML window...
329  wxBitmap tbm(GetSize().x, GetSize().y, -1);
330  wxMemoryDC tdc(tbm);
331  // wxColour cback = GetGlobalColor( _T("YELO1") );
332  tdc.SetBackground(bg);
333  tdc.Clear();
334  m_pAlertTextCtl->SetBackgroundImage(tbm);
335 #endif
336 }
337 
338 void AISTargetAlertDialog::OnClose(wxCloseEvent &event) {
339  // Acknowledge any existing Alert, and dismiss the dialog
340  if (m_pdecoder) {
341  auto td = m_pdecoder->Get_Target_Data_From_MMSI(Get_Dialog_MMSI());
342  if (td) {
343  if (AIS_ALERT_SET == td->n_alert_state) {
344  td->m_ack_time = wxDateTime::Now();
345  td->b_in_ack_timeout = true;
346  }
347  if (td->b_isDSCtarget) {
348  td->b_isDSCtarget = false;
349  if (td->n_alert_state) {
350  td->n_alert_state = AIS_NO_ALERT;
351  }
352  }
353  }
354  }
355 
356  Destroy();
357  g_pais_alert_dialog_active = NULL;
358 }
359 
360 void AISTargetAlertDialog::OnIdAckClick(wxCommandEvent &event) {
361  // Acknowledge the Alert, and dismiss the dialog
362  if (m_pdecoder) {
363  auto td = m_pdecoder->Get_Target_Data_From_MMSI(Get_Dialog_MMSI());
364  if (td) {
365  if (AIS_ALERT_SET == td->n_alert_state ) {
366  td->m_ack_time = wxDateTime::Now();
367  td->b_in_ack_timeout = true;
368  }
369  if (td->b_isDSCtarget) {
370  td->b_isDSCtarget = false;
371  if (td->n_alert_state) {
372  td->n_alert_state = AIS_NO_ALERT;
373  }
374  }
375  }
376  }
377  Destroy();
378  g_pais_alert_dialog_active = NULL;
379 }
380 void AISTargetAlertDialog::OnIdCreateWPClick(wxCommandEvent &event) {
381  if (m_pdecoder) {
382  auto td = m_pdecoder->Get_Target_Data_From_MMSI(Get_Dialog_MMSI());
383  if (td) {
384  RoutePoint *pWP = new RoutePoint(td->Lat, td->Lon, g_default_wp_icon,
385  wxEmptyString, wxEmptyString);
386  pWP->m_bIsolatedMark = true; // This is an isolated mark
387  pSelect->AddSelectableRoutePoint(td->Lat, td->Lon, pWP);
388  pConfig->AddNewWayPoint(pWP, -1); // use auto next num
389 
390  if (pRouteManagerDialog && pRouteManagerDialog->IsShown())
391  pRouteManagerDialog->UpdateWptListCtrl();
392  if (gFrame->GetPrimaryCanvas()) {
393  gFrame->GetPrimaryCanvas()->undo->BeforeUndoableAction(
394  Undo_CreateWaypoint, pWP, Undo_HasParent, NULL);
395  gFrame->GetPrimaryCanvas()->undo->AfterUndoableAction(NULL);
396  gFrame->InvalidateAllGL();
397  }
398  Refresh(false);
399  }
400  }
401 }
402 
403 void AISTargetAlertDialog::OnIdSilenceClick(wxCommandEvent &event) {
404  // Set the suppress audio flag
405  if (m_pdecoder) {
406  auto td =
407  m_pdecoder->Get_Target_Data_From_MMSI(Get_Dialog_MMSI());
408  if (td) td->b_suppress_audio = true;
409  }
410 }
411 
412 void AISTargetAlertDialog::OnIdJumptoClick(wxCommandEvent &event) {
413  if (m_pdecoder) {
414  auto td =
415  m_pdecoder->Get_Target_Data_From_MMSI(Get_Dialog_MMSI());
416  if (td)
417  gFrame->JumpToPosition(gFrame->GetFocusCanvas(), td->Lat, td->Lon,
418  gFrame->GetFocusCanvas()->GetVPScale());
419  }
420 }
421 
422 void AISTargetAlertDialog::OnMove(wxMoveEvent &event) {
423  // Record the dialog position
424  g_ais_alert_dialog_x = GetPosition().x;
425  g_ais_alert_dialog_y = GetPosition().y;
426 
427  event.Skip();
428 }
429 
430 void AISTargetAlertDialog::OnSize(wxSizeEvent &event) {
431  // Record the dialog size
432  wxSize p = event.GetSize();
433  g_ais_alert_dialog_sx = p.x;
434  g_ais_alert_dialog_sy = p.y;
435 
436  event.Skip();
437 }
Global state for AIS decoder.