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