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