OpenCPN Partial API docs
gui_lib.cpp
Go to the documentation of this file.
1  /**************************************************************************
2  * Copyright (C) 2010 by David S. Register *
3  * *
4  * This program is free software; you can redistribute it and/or modify *
5  * it under the terms of the GNU General Public License as published by *
6  * the Free Software Foundation; either version 2 of the License, or *
7  * (at your option) any later version. *
8  * *
9  * This program is distributed in the hope that it will be useful, *
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12  * GNU General Public License for more details. *
13  * *
14  * You should have received a copy of the GNU General Public License *
15  * along with this program; if not, write to the *
16  * Free Software Foundation, Inc., *
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
18  **************************************************************************/
19 
22 #include <wx/artprov.h>
23 #include <wx/dialog.h>
24 #include <wx/sizer.h>
25 #include <wx/statbmp.h>
26 
27 #include "gui_lib.h"
28 #include "timers.h"
29 #include "FontMgr.h"
30 #include "OCPNPlatform.h"
31 #include "ocpn_plugin.h"
32 #include "displays.h"
33 
34 #ifdef __ANDROID__
35 #include "androidUTIL.h"
36 #include "qdebug.h"
37 #endif
38 
39 extern bool g_bresponsive;
40 extern OCPNPlatform* g_Platform;
41 extern int g_GUIScaleFactor;
42 
43 CopyableText::CopyableText(wxWindow* parent, const char* text)
44  : wxTextCtrl(parent, wxID_ANY, text, wxDefaultPosition,
45  wxDefaultSize, wxBORDER_NONE) {
46  SetEditable(false);
47  wxStaticText tmp(parent, wxID_ANY, text);
48  SetBackgroundColour(tmp.GetBackgroundColour());
49 }
50 
51 
52 wxFont* GetOCPNScaledFont(wxString item, int default_size) {
53  wxFont* dFont = FontMgr::Get().GetFont(item, default_size);
54  int req_size = dFont->GetPointSize();
55 
56  if (g_bresponsive) {
57  // Adjust font size to be no smaller than xx mm actual size
58  double scaled_font_size = dFont->GetPointSize();
59 
60  {
61  double points_per_mm =
62  g_Platform->getFontPointsperPixel() * g_Platform->GetDisplayDPmm();
63  double min_scaled_font_size =
64  3 * points_per_mm; // smaller than 3 mm is unreadable
65  int nscaled_font_size =
66  wxMax(wxRound(scaled_font_size), min_scaled_font_size);
67 
68  if (req_size >= nscaled_font_size)
69  return dFont;
70  else {
71  wxFont* qFont = FontMgr::Get().FindOrCreateFont(
72  nscaled_font_size, dFont->GetFamily(), dFont->GetStyle(),
73  dFont->GetWeight());
74  return qFont;
75  }
76  }
77  }
78  return dFont;
79 }
80 
81 wxFont GetOCPNGUIScaledFont(wxString item) {
82  wxFont* dFont = FontMgr::Get().GetFont(item, 0);
83  int req_size = dFont->GetPointSize();
84  wxFont qFont = *dFont;
85 
86  if (g_bresponsive) {
87  double postmult = exp(g_GUIScaleFactor * (0.693 / 5.0)); // exp(2)
88  double scaled_font_size = dFont->GetPointSize() * postmult;
89 
90  double points_per_mm =
91  g_Platform->getFontPointsperPixel() * g_Platform->GetDisplayDPmm();
92  double min_scaled_font_size =
93  3 * points_per_mm; // smaller than 3 mm is unreadable
94  int nscaled_font_size =
95  wxMax(wxRound(scaled_font_size), min_scaled_font_size);
96 
97  // wxFont *qFont = wxTheFontList->FindOrCreateFont(
98  // nscaled_font_size,
99  // dFont->GetFamily(),
100  // dFont->GetStyle(),
101  // dFont->GetWeight());
102  qFont.SetPointSize(nscaled_font_size);
103  }
104 
105  return qFont;
106 }
107 
108 int OCPNMessageBox( wxWindow *parent, const wxString& message, const wxString& caption, int style,
109  int timeout_sec, int x, int y )
110 {
111 #ifdef __OCPN__ANDROID__
112  androidDisableRotation();
113  int style_mod = style;
114 
115  auto dlg = new wxMessageDialog(parent, message, caption, style_mod);
116  int ret = dlg->ShowModal();
117  qDebug() << "OCPNMB-1 ret" << ret;
118 
119  //int ret= dlg->GetReturnCode();
120 
121  // Not sure why we need this, maybe on wx3?
122  if( ((style & wxYES_NO) == wxYES_NO) && (ret == wxID_OK))
123  ret = wxID_YES;
124 
125  dlg->Destroy();
126 
127  androidEnableRotation();
128  qDebug() << "OCPNMB-2 ret" << ret;
129  return ret;
130 
131 #else
132  int ret = wxID_OK;
133 
134  TimedMessageBox tbox(parent, message, caption, style, timeout_sec, wxPoint( x, y ) );
135  ret = tbox.GetRetVal() ;
136 #endif
137 
138  return ret;
139 }
140 
141 
142 BEGIN_EVENT_TABLE(OCPNMessageDialog, wxDialog)
143 EVT_BUTTON(wxID_YES, OCPNMessageDialog::OnYes)
144 EVT_BUTTON(wxID_NO, OCPNMessageDialog::OnNo)
145 EVT_BUTTON(wxID_CANCEL, OCPNMessageDialog::OnCancel)
146 EVT_CLOSE(OCPNMessageDialog::OnClose)
147 END_EVENT_TABLE()
148 
149 OCPNMessageDialog::OCPNMessageDialog(wxWindow* parent, const wxString& message,
150  const wxString& caption, long style,
151  const wxPoint& pos)
152  : wxDialog(parent, wxID_ANY, caption, pos, wxDefaultSize,
153  wxDEFAULT_DIALOG_STYLE | wxSTAY_ON_TOP) {
154  m_style = style;
155  wxFont* qFont = GetOCPNScaledFont(_("Dialog"));
156  SetFont(*qFont);
157 
158  wxBoxSizer* topsizer = new wxBoxSizer(wxVERTICAL);
159 
160  wxBoxSizer* icon_text = new wxBoxSizer(wxHORIZONTAL);
161 
162 #if wxUSE_STATBMP
163  // 1) icon
164  if (style & wxICON_MASK) {
165  wxBitmap bitmap;
166  switch (style & wxICON_MASK) {
167  default:
168  wxFAIL_MSG(_T("incorrect log style"));
169  // fall through
170 
171  case wxICON_ERROR:
172  bitmap = wxArtProvider::GetIcon(wxART_ERROR, wxART_MESSAGE_BOX);
173  break;
174 
175  case wxICON_INFORMATION:
176  bitmap = wxArtProvider::GetIcon(wxART_INFORMATION, wxART_MESSAGE_BOX);
177  break;
178 
179  case wxICON_WARNING:
180  bitmap = wxArtProvider::GetIcon(wxART_WARNING, wxART_MESSAGE_BOX);
181  break;
182 
183  case wxICON_QUESTION:
184  bitmap = wxArtProvider::GetIcon(wxART_QUESTION, wxART_MESSAGE_BOX);
185  break;
186  }
187  wxStaticBitmap* icon = new wxStaticBitmap(this, wxID_ANY, bitmap);
188  icon_text->Add(icon, 0, wxCENTER);
189  }
190 #endif // wxUSE_STATBMP
191 
192 #if wxUSE_STATTEXT
193  // 2) text
194  icon_text->Add(CreateTextSizer(message), 0, wxALIGN_CENTER | wxLEFT, 10);
195 
196  topsizer->Add(icon_text, 1, wxCENTER | wxLEFT | wxRIGHT | wxTOP, 10);
197 #endif // wxUSE_STATTEXT
198 
199  // 3) buttons
200  int AllButtonSizerFlags =
201  wxOK | wxCANCEL | wxYES | wxNO | wxHELP | wxNO_DEFAULT;
202  int center_flag = wxEXPAND;
203  if (style & wxYES_NO) center_flag = wxALIGN_CENTRE;
204  wxSizer* sizerBtn = CreateSeparatedButtonSizer(style & AllButtonSizerFlags);
205  if (sizerBtn) topsizer->Add(sizerBtn, 0, center_flag | wxALL, 10);
206 
207  SetAutoLayout(true);
208  SetSizer(topsizer);
209 
210  topsizer->SetSizeHints(this);
211  topsizer->Fit(this);
212  wxSize size(GetSize());
213  if (size.x < size.y * 3 / 2) {
214  size.x = size.y * 3 / 2;
215  SetSize(size);
216  }
217 
218  Centre(wxBOTH | wxCENTER_FRAME);
219 }
220 
221 void OCPNMessageDialog::OnYes(wxCommandEvent& WXUNUSED(event)) {
222  SetReturnCode(wxID_YES);
223  EndModal(wxID_YES);
224 }
225 
226 void OCPNMessageDialog::OnNo(wxCommandEvent& WXUNUSED(event)) {
227  SetReturnCode(wxID_NO);
228  EndModal(wxID_NO);
229 }
230 
231 void OCPNMessageDialog::OnCancel(wxCommandEvent& WXUNUSED(event)) {
232  // Allow cancellation via ESC/Close button except if
233  // only YES and NO are specified.
234  if ((m_style & wxYES_NO) != wxYES_NO || (m_style & wxCANCEL)) {
235  SetReturnCode(wxID_CANCEL);
236  EndModal(wxID_CANCEL);
237  }
238 }
239 
240 void OCPNMessageDialog::OnClose(wxCloseEvent& event) {
241  SetReturnCode(wxID_CANCEL);
242  EndModal(wxID_CANCEL);
243 }
244 
245 BEGIN_EVENT_TABLE(TimedMessageBox, wxEvtHandler)
246 EVT_TIMER(-1, TimedMessageBox::OnTimer)
247 END_EVENT_TABLE()
248 
249 TimedMessageBox::TimedMessageBox(wxWindow* parent, const wxString& message,
250  const wxString& caption, long style,
251  int timeout_sec, const wxPoint& pos) {
252  ret_val = 0;
253  m_timer.SetOwner(this, -1);
254 
255  if (timeout_sec > 0) m_timer.Start(timeout_sec * 1000, wxTIMER_ONE_SHOT);
256 
257  dlg = new OCPNMessageDialog(parent, message, caption, style, pos);
258  dlg->ShowModal();
259 
260  int ret = dlg->GetReturnCode();
261 
262  // Not sure why we need this, maybe on wx3?
263  if (((style & wxYES_NO) == wxYES_NO) && (ret == wxID_OK)) ret = wxID_YES;
264 
265  delete dlg;
266  dlg = NULL;
267 
268  ret_val = ret;
269 }
270 
271 TimedMessageBox::~TimedMessageBox() {}
272 
273 void TimedMessageBox::OnTimer(wxTimerEvent& evt) {
274  if (dlg) dlg->EndModal(wxID_CANCEL);
275 }
276 
277 BEGIN_EVENT_TABLE(OCPN_TimedHTMLMessageDialog, wxDialog)
278 EVT_BUTTON(wxID_YES, OCPN_TimedHTMLMessageDialog::OnYes)
279 EVT_BUTTON(wxID_NO, OCPN_TimedHTMLMessageDialog::OnNo)
280 EVT_BUTTON(wxID_CANCEL, OCPN_TimedHTMLMessageDialog::OnCancel)
281 EVT_CLOSE(OCPN_TimedHTMLMessageDialog::OnClose)
282 EVT_TIMER(-1, OCPN_TimedHTMLMessageDialog::OnTimer)
283 EVT_HTML_LINK_CLICKED( wxID_ANY, OCPN_TimedHTMLMessageDialog::OnHtmlLinkClicked )
284 END_EVENT_TABLE()
285 
287  wxWindow* parent, const wxString& message, const wxString& caption,
288  int tSeconds, long style, bool bFixedFont, const wxPoint& pos)
289  : wxDialog(parent, wxID_ANY, caption, pos, wxDefaultSize,
290  wxDEFAULT_DIALOG_STYLE | wxSTAY_ON_TOP) {
291  m_style = style;
292  if (bFixedFont) {
293  wxFont* dFont = GetOCPNScaledFont_PlugIn(_("Dialog"));
294  double font_size = dFont->GetPointSize();
295  wxFont* qFont =
296  wxTheFontList->FindOrCreateFont(font_size, wxFONTFAMILY_TELETYPE,
297  dFont->GetStyle(), dFont->GetWeight());
298  SetFont(*qFont);
299  }
300 
301  wxBoxSizer* topsizer = new wxBoxSizer(wxVERTICAL);
302 
303  msgWindow = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
304  wxHW_SCROLLBAR_AUTO | wxHW_NO_SELECTION);
305  msgWindow->SetBorders(30);
306 
307  topsizer->Add(msgWindow, 1, wxEXPAND, 5);
308 
309  wxString html;
310  html << message;
311 
312  wxCharBuffer buf = html.ToUTF8();
313  if (buf.data()) // string OK?
314  msgWindow->SetPage(html);
315 
316  // 3) buttons
317  int AllButtonSizerFlags =
318  wxOK | wxCANCEL | wxYES | wxNO | wxHELP | wxNO_DEFAULT;
319  int center_flag = wxEXPAND;
320  if (style & wxYES_NO) center_flag = wxALIGN_CENTRE;
321  wxSizer* sizerBtn = CreateSeparatedButtonSizer(style & AllButtonSizerFlags);
322  if (sizerBtn) topsizer->Add(sizerBtn, 0, center_flag | wxALL, 10);
323 
324  SetSizer(topsizer);
325 
326  topsizer->Fit(this);
327 
328  RecalculateSize();
329  // wxSize szyv = msgWindow->GetVirtualSize();
330 
331  // SetClientSize(szyv.x + 20, szyv.y + 20);
332 
333  CentreOnScreen();
334 
335  // msgWindow->SetBackgroundColour(wxColour(191, 183, 180));
336  msgWindow->SetBackgroundColour(GetBackgroundColour());
337 
338  m_timer.SetOwner(this, -1);
339 
340  if (tSeconds > 0) m_timer.Start(tSeconds * 1000, wxTIMER_ONE_SHOT);
341 }
342 
343 void OCPN_TimedHTMLMessageDialog::RecalculateSize(void) {
344  wxSize esize;
345  esize.x = GetCharWidth() * 60;
346  int sx, sy;
347  sx = g_monitor_info[g_current_monitor].width;
348  esize.x = wxMin(esize.x, sx * 6 / 10);
349  esize.y = -1;
350  SetClientSize(esize); // This will force a recalc of internal representation
351 
352  int height1 = msgWindow->GetInternalRepresentation()->GetHeight();
353 
354  int client_size_y =
355  wxMin(::wxGetDisplaySize().y - 100, height1 + 70); // Must fit on screen
356 
357  SetClientSize(wxSize(
358  esize.x, client_size_y)); // constant is 2xBorders + a little slop.
359 }
360 
361 void OCPN_TimedHTMLMessageDialog::OnYes(wxCommandEvent& WXUNUSED(event)) {
362  SetReturnCode(wxID_YES);
363  if (IsModal())
364  EndModal(wxID_YES);
365  else
366  Hide();
367 }
368 
369 void OCPN_TimedHTMLMessageDialog::OnNo(wxCommandEvent& WXUNUSED(event)) {
370  SetReturnCode(wxID_NO);
371  if (IsModal())
372  EndModal(wxID_NO);
373  else
374  Hide();
375 }
376 
377 void OCPN_TimedHTMLMessageDialog::OnCancel(wxCommandEvent& WXUNUSED(event)) {
378  // Allow cancellation via ESC/Close button except if
379  // only YES and NO are specified.
380  if ((m_style & wxYES_NO) != wxYES_NO || (m_style & wxCANCEL)) {
381  SetReturnCode(wxID_CANCEL);
382  EndModal(wxID_CANCEL);
383  }
384 }
385 
386 void OCPN_TimedHTMLMessageDialog::OnClose(wxCloseEvent& event) {
387  SetReturnCode(wxID_CANCEL);
388  if (IsModal())
389  EndModal(wxID_CANCEL);
390  else
391  Hide();
392 }
393 
394 void OCPN_TimedHTMLMessageDialog::OnTimer(wxTimerEvent& evt) {
395  if (IsModal())
396  EndModal(m_style & wxNO_DEFAULT ? wxID_NO : wxID_YES);
397  else
398  Hide();
399 }
400 
401 
402 // Auto timed popup Window implementation
403 
404 BEGIN_EVENT_TABLE(TimedPopupWin, wxWindow)
405 EVT_PAINT(TimedPopupWin::OnPaint)
406 EVT_TIMER(POPUP_TIMER, TimedPopupWin::OnTimer)
407 
408 END_EVENT_TABLE()
409 
410 // Define a constructor
411 TimedPopupWin::TimedPopupWin(wxWindow *parent, int timeout)
412  : wxWindow(parent, wxID_ANY, wxPoint(0, 0), wxSize(1, 1), wxNO_BORDER) {
413  m_pbm = NULL;
414 
415  m_timer_timeout.SetOwner(this, POPUP_TIMER);
416  m_timeout_sec = timeout;
417  isActive = false;
418  Hide();
419 }
420 
421 TimedPopupWin::~TimedPopupWin() { delete m_pbm; }
422 void TimedPopupWin::OnTimer(wxTimerEvent &event) {
423  if (IsShown()) Hide();
424 }
425 
426 void TimedPopupWin::SetBitmap(wxBitmap &bmp) {
427  delete m_pbm;
428  m_pbm = new wxBitmap(bmp);
429 
430  // Retrigger the auto timeout
431  if (m_timeout_sec > 0)
432  m_timer_timeout.Start(m_timeout_sec * 1000, wxTIMER_ONE_SHOT);
433 }
434 
435 void TimedPopupWin::OnPaint(wxPaintEvent &event) {
436  int width, height;
437  GetClientSize(&width, &height);
438  wxPaintDC dc(this);
439 
440  wxMemoryDC mdc;
441  mdc.SelectObject(*m_pbm);
442  dc.Blit(0, 0, width, height, &mdc, 0, 0);
443 }
General purpose GUI support.