OpenCPN Partial API docs
Loading...
Searching...
No Matches
notification_manager_gui.cpp
1/***************************************************************************
2 *
3 * Project: OpenCPN
4 * Purpose: Notification Manager GUI
5 * Author: David Register
6 *
7 ***************************************************************************
8 * Copyright (C) 2025 by David S. Register *
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 * This program is distributed in the hope that it will be useful, *
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18 * GNU General Public License for more details. *
19 * *
20 * You should have received a copy of the GNU General Public License *
21 * along with this program; if not, write to the *
22 * Free Software Foundation, Inc., *
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
24 **************************************************************************/
25#include <cmath>
26#include <memory>
27#include <vector>
28#include <wx/statline.h>
29#include <wx/textwrapper.h>
30
31#include "notification_manager_gui.h"
33#include "model/notification.h"
34#include "observable_globvar.h"
35#include "color_handler.h"
36#include "styles.h"
37#include "OCPNPlatform.h"
38#include "chcanv.h"
39#include "glChartCanvas.h"
40#include "gui_lib.h"
41#include "svg_utils.h"
42#include "model/datetime.h"
43#include "navutil.h"
44
45extern OCPNPlatform* g_Platform;
46extern ocpnStyle::StyleManager* g_StyleManager;
47
48class PanelHardBreakWrapper : public wxTextWrapper {
49public:
50 PanelHardBreakWrapper(wxWindow* win, const wxString& text, int widthMax) {
51 m_lineCount = 0;
52 Wrap(win, text, widthMax);
53 }
54 wxString const& GetWrapped() const { return m_wrapped; }
55 int const GetLineCount() const { return m_lineCount; }
56 wxArrayString GetLineArray() { return m_array; }
57
58protected:
59 virtual void OnOutputLine(const wxString& line) {
60 m_wrapped += line;
61 m_array.Add(line);
62 }
63 virtual void OnNewLine() {
64 m_wrapped += '\n';
65 m_lineCount++;
66 }
67
68private:
69 wxString m_wrapped;
70 int m_lineCount;
71 wxArrayString m_array;
72};
73
74#
75BEGIN_EVENT_TABLE(NotificationPanel, wxPanel)
76EVT_PAINT(NotificationPanel::OnPaint)
77END_EVENT_TABLE()
78
80 wxPanel* parent, wxWindowID id, const wxPoint& pos, const wxSize& size,
81 std::shared_ptr<Notification> _notification, int _repeat_count)
82 : wxPanel(parent, id, pos, size, wxBORDER_NONE),
83 repeat_count(_repeat_count) {
84 notification = _notification;
85
86 wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL);
87 SetSizer(topSizer);
88
89 wxBoxSizer* itemBoxSizer01 = new wxBoxSizer(wxHORIZONTAL);
90 topSizer->Add(itemBoxSizer01, 0, wxEXPAND);
91
92 double iconSize = GetCharWidth() * 3;
93 double dpi_mult = g_Platform->GetDisplayDIPMult(this);
94 int icon_scale = iconSize * dpi_mult;
95
96 wxImage notification_icon;
97 ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
98 wxBitmap bitmap;
99 wxFileName path;
100 if (notification->GetSeverity() == NotificationSeverity::kInformational) {
101 path =
102 wxFileName(g_Platform->GetSharedDataDir(), "notification-info-2.svg");
103 } else if (notification->GetSeverity() == NotificationSeverity::kWarning) {
104 path = wxFileName(g_Platform->GetSharedDataDir(),
105 "notification-warning-2.svg");
106 } else {
107 path = wxFileName(g_Platform->GetSharedDataDir(),
108 "notification-critical-2.svg");
109 }
110 path.AppendDir("uidata");
111 path.AppendDir("MUI_flat");
112 bitmap = LoadSVG(path.GetFullPath(), icon_scale, icon_scale);
113 m_itemStaticBitmap = new wxStaticBitmap(this, wxID_ANY, bitmap);
114
115 itemBoxSizer01->Add(m_itemStaticBitmap, 0, wxEXPAND | wxALL, 10);
116
117 // Repeat Count
118 wxString rp = _("Repeat:");
119 wxString sCount = rp + wxString::Format("\n %d", repeat_count);
120 auto counttextbox = new wxStaticText(this, wxID_ANY, sCount);
121 itemBoxSizer01->Add(counttextbox, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
122
123 // Time
124 wxDateTime act_time = wxDateTime(notification->GetActivateTime());
125 wxString stime = wxString::Format(
126 "%s",
127 ocpn::toUsrDateTimeFormat(act_time, DateTimeFormatOptions()
128 .SetFormatString("$short_date")
129 .SetShowTimezone(false)));
130 stime = stime.BeforeFirst(' ');
131 wxString stime1 = wxString::Format(
132 "%s", ocpn::toUsrDateTimeFormat(act_time,
133 DateTimeFormatOptions().SetFormatString(
134 "$24_hour_minutes_seconds")));
135 stime += "\n";
136 stime += stime1;
137
138 auto timetextbox = new wxStaticText(this, wxID_ANY, stime);
139 itemBoxSizer01->Add(timetextbox, 0,
140 /*wxEXPAND|*/ wxALL | wxALIGN_CENTER_VERTICAL, 5);
141
142 PanelHardBreakWrapper wrapper(this, notification->GetMessage(),
143 GetSize().x * 60 / 100);
144
145 auto textbox = new wxStaticText(this, wxID_ANY, wrapper.GetWrapped());
146 itemBoxSizer01->Add(textbox, 0, wxALIGN_CENTER_VERTICAL | wxALL, 10);
147
148 itemBoxSizer01->AddStretchSpacer(1);
149
150 // Ack button
151 m_ack_button = new wxButton(this, wxID_OK);
152 itemBoxSizer01->Add(m_ack_button, 0, wxALIGN_CENTER_VERTICAL | wxALL, 10);
153 m_ack_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
154 &NotificationPanel::OnAckButton, this);
155
156 DimeControl(m_ack_button);
157}
158
159NotificationPanel::~NotificationPanel() {}
160
161void NotificationPanel::OnAckButton(wxCommandEvent& event) {
162 NotificationManager& noteman = NotificationManager::GetInstance();
163 noteman.AcknowledgeNotification(notification->GetGuid());
164}
165
166void NotificationPanel::OnPaint(wxPaintEvent& event) {
167 wxPaintDC dc(this);
168 wxColor back_color = GetDialogColor(DLG_UNSELECTED_BACKGROUND);
169 wxBrush bg(back_color, wxBRUSHSTYLE_SOLID);
170 dc.SetBackground(bg);
171 dc.Clear();
172
173 int penWidth = 2; // m_penWidthUnselected;
174 wxColour box_color = GetDialogColor(DLG_UNSELECTED_BACKGROUND);
175 wxColour box_border = GetGlobalColor("GREY3");
176
177 wxBrush b(box_color, wxBRUSHSTYLE_SOLID);
178 dc.SetBrush(b);
179 dc.SetPen(wxPen(box_border, penWidth));
180
181 dc.DrawRoundedRectangle(5, 5, GetSize().x - 10, GetSize().y - 10, 5);
182}
183
184NotificationListPanel::NotificationListPanel(wxWindow* parent, wxWindowID id,
185 const wxPoint& pos,
186 const wxSize& size)
187 : wxScrolledWindow(parent, id, pos, size,
188 wxTAB_TRAVERSAL | wxVSCROLL | wxHSCROLL) {
189 SetSizer(new wxBoxSizer(wxVERTICAL));
190 SetScrollRate(5, 5);
191 ShowScrollbars(wxSHOW_SB_NEVER, wxSHOW_SB_DEFAULT);
192 if (g_btouch) {
193 ShowScrollbars(wxSHOW_SB_NEVER, wxSHOW_SB_NEVER);
194 SetScrollRate(1, 1);
195 }
196 ReloadNotificationPanels();
197 DimeControl(this);
198}
199
200NotificationListPanel::~NotificationListPanel() {}
201
202void NotificationListPanel::ReloadNotificationPanels() {
203 wxWindowList kids = GetChildren();
204 for (unsigned int i = 0; i < kids.GetCount(); i++) {
205 wxWindowListNode* node = kids.Item(i);
206 wxWindow* win = node->GetData();
207 NotificationPanel* pp = dynamic_cast<NotificationPanel*>(win);
208 if (pp) win->Destroy();
209 }
210 GetSizer()->Clear();
211 Hide();
212 panels.clear();
213
214 NotificationManager& noteman = NotificationManager::GetInstance();
215 auto notifications = noteman.GetNotifications();
216
217 int panel_size_x = 60 * GetCharWidth();
218 panel_size_x = wxMax(panel_size_x, GetParent()->GetClientSize().x);
219 wxSize panel_size = wxSize(panel_size_x, -1);
220
221 for (auto notification : notifications) {
222 size_t this_hash = notification->GetStringHash();
223 int repeat_count = 0;
224 for (auto hash_test : notifications) {
225 if (hash_test->GetStringHash() == this_hash) {
226 repeat_count++;
227 }
228 }
229
230 // Do not create duplicate panels
231 bool skip = false;
232 for (auto tpanel : panels) {
233 auto note = tpanel->GetNotification();
234
235 if ((note->GetStringHash() == this_hash) && (repeat_count > 1)) {
236 skip = true;
237 }
238 }
239 if (skip) continue;
240
241 NotificationPanel* panel =
242 new NotificationPanel(this, wxID_ANY, wxDefaultPosition, panel_size,
243 notification, repeat_count);
244 panels.push_back(panel);
245 }
246
247 for (auto panel : panels) {
248 AddNotificationPanel(panel);
249 DimeControl(panel);
250 }
251
252 Show();
253 Layout();
254 Refresh(true);
255 Scroll(0, 0);
256}
257
258void NotificationListPanel::AddNotificationPanel(NotificationPanel* _panel) {
259 GetSizer()->Add(_panel, 0); //| wxALL, 10);
260}
261
262//------------------------------------------------------------------------------
263// NotificationsList
264//------------------------------------------------------------------------------
265
266BEGIN_EVENT_TABLE(NotificationsList, wxDialog)
267EVT_CLOSE(NotificationsList::OnClose)
268END_EVENT_TABLE()
269
270NotificationsList::NotificationsList(wxWindow* parent) : wxDialog() {
271 wxFont* qFont = GetOCPNScaledFont(_("Dialog"));
272 SetFont(*qFont);
273
274 long mstyle = wxRESIZE_BORDER | wxCAPTION | wxCLOSE_BOX | wxFRAME_NO_TASKBAR;
275#ifdef __WXOSX__
276 mstyle |= wxSTAY_ON_TOP;
277#endif
278
279 wxDialog::Create(parent, wxID_ANY, _("OpenCPN Notifications"),
280 wxDefaultPosition, wxDefaultSize, mstyle);
281
282 wxBoxSizer* topsizer = new wxBoxSizer(wxVERTICAL);
283 SetSizer(topsizer);
284
285 int border_size = 2;
286 int group_item_spacing = 0;
287 int interGroupSpace = border_size * 2;
288
289 auto acksizer = new wxBoxSizer(wxHORIZONTAL);
290 topsizer->Add(acksizer, 0, wxEXPAND);
291
292 // Ack All button
293 acksizer->AddStretchSpacer(1);
294
295 m_ackall_button = new wxButton(this, wxID_ANY, _("Acknowledge All"));
296 acksizer->Add(m_ackall_button, 0, wxALIGN_CENTER_VERTICAL | wxALL, 2);
297 m_ackall_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
298 &NotificationsList::OnAckAllButton, this);
299 acksizer->AddSpacer(10);
300
301 // spacer
302 topsizer->Add(0, interGroupSpace);
303
304 m_notifications_list_panel = new NotificationListPanel(
305 this, wxID_ANY, wxDefaultPosition, wxSize(-1, parent->GetSize().y));
306 topsizer->Add(m_notifications_list_panel, 0, wxALL | wxEXPAND, border_size);
307
308 DimeControl(this);
309}
310void NotificationsList::SetColorScheme() { DimeControl(this); }
311
312void NotificationsList::ReloadNotificationList() {
313 m_notifications_list_panel->ReloadNotificationPanels();
314 if (!m_notifications_list_panel->GetPanels().size()) {
315 Hide();
316 GetParent()->Refresh();
317 }
318}
319
320void NotificationsList::OnAckAllButton(wxCommandEvent& event) {
321 NotificationManager& noteman = NotificationManager::GetInstance();
322 noteman.AcknowledgeAllNotifications();
323}
324
325void NotificationsList::RecalculateSize() {
326 // calculate and set best size and position for Notification list
327 wxSize parent_size = GetParent()->GetSize();
328 wxPoint ClientUpperRight =
329 GetParent()->ClientToScreen(wxPoint(parent_size.x, 0));
330 wxPoint list_bottom =
331 GetParent()->ClientToScreen(wxPoint(0, parent_size.y / 3));
332 int size_y = list_bottom.y - (ClientUpperRight.y + 5);
333 size_y -= GetParent()->GetCharHeight();
334 size_y =
335 wxMax(size_y, 8 * GetCharHeight()); // ensure always big enough to see
336
337 wxSize target_size = wxSize(GetCharWidth() * 80, size_y);
338
339 wxPoint targetNLPos = GetParent()->ClientToScreen(
340 wxPoint(parent_size.x / 2, 3 * GetParent()->GetCharHeight()));
341
342 // Adjust the size for smaller devices
343 wxSize display_size = g_Platform->getDisplaySize();
344 if (target_size.x * 2 > display_size.x) {
345 target_size.x =
346 display_size.x * 85 / 100 - (2 * GetParent()->GetCharWidth());
347 targetNLPos.x =
348 GetParent()->ClientToScreen(wxPoint(display_size.x * 15 / 100, 0)).x;
349 }
350
351 SetSize(target_size);
352 Move(targetNLPos);
353 Layout();
354}
355
356void NotificationsList::OnClose(wxCloseEvent& event) { Hide(); }
357
358/*
359 * Notification Button Widget
360 */
361
362extern ocpnStyle::StyleManager* g_StyleManager;
363extern bool g_bSatValid;
364extern int g_SatsInView;
365extern bool g_bopengl;
366extern bool g_btenhertz;
367
368#ifndef GL_RGBA8
369#define GL_RGBA8 0x8058
370#endif
371
372NotificationButton::NotificationButton(ChartCanvas* parent) {
373 m_parent = parent;
374
375 ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
376 _img_gpsRed = style->GetIcon(_T("gpsRed"));
377
378 m_pStatBoxToolStaticBmp = NULL;
379
380 m_rect = wxRect(style->GetCompassXOffset(), style->GetCompassYOffset(),
381 _img_gpsRed.GetWidth() + style->GetCompassLeftMargin() * 2 +
382 style->GetToolSeparation(),
383 _img_gpsRed.GetHeight() + style->GetCompassTopMargin() +
384 style->GetCompassBottomMargin());
385
386#ifdef ocpnUSE_GL
387 m_texobj = 0;
388#endif
389 m_texOK = false;
390
391 m_scale = 1.0;
392 m_cs = GLOBAL_COLOR_SCHEME_RGB;
393 m_NoteIconName = "notification-info-2";
394}
395
396NotificationButton::~NotificationButton() {
397#ifdef ocpnUSE_GL
398 if (m_texobj) {
399 glDeleteTextures(1, &m_texobj);
400 m_texobj = 0;
401 }
402#endif
403
404 delete m_pStatBoxToolStaticBmp;
405}
406
407void NotificationButton::SetIconSeverity(NotificationSeverity _severity) {
408 wxString icon_name;
409 if (_severity == NotificationSeverity::kInformational) {
410 icon_name = "notification-info-2";
411 } else if (_severity == NotificationSeverity::kWarning) {
412 icon_name = "notification-warning-2";
413 } else {
414 icon_name = "notification-critical-2";
415 }
416
417 SetIconName(icon_name);
418}
419
420void NotificationButton::Paint(ocpnDC& dc) {
421 if (m_shown && m_StatBmp.IsOk()) {
422#if defined(ocpnUSE_GLES) || defined(ocpnUSE_GL)
423 if (g_bopengl && !m_texobj) {
424 // The glContext is known active here,
425 // so safe to create a texture.
426 glGenTextures(1, &m_texobj);
427 CreateTexture();
428 }
429
430 if (g_bopengl && m_texobj /*&& m_texOK*/) {
431 glBindTexture(GL_TEXTURE_2D, m_texobj);
432 glEnable(GL_TEXTURE_2D);
433
434#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
435 float coords[8];
436 float uv[8];
437
438 // normal uv, normalized to POT
439 uv[0] = 0;
440 uv[1] = 0;
441 uv[2] = (float)m_image_width / m_tex_w;
442 uv[3] = 0;
443 uv[4] = (float)m_image_width / m_tex_w;
444 uv[5] = (float)m_image_height / m_tex_h;
445 uv[6] = 0;
446 uv[7] = (float)m_image_height / m_tex_h;
447
448 // pixels
449 coords[0] = m_rect.x;
450 coords[1] = m_rect.y;
451 coords[2] = m_rect.x + m_rect.width;
452 coords[3] = m_rect.y;
453 coords[4] = m_rect.x + m_rect.width;
454 coords[5] = m_rect.y + m_rect.height;
455 coords[6] = m_rect.x;
456 coords[7] = m_rect.y + m_rect.height;
457
458 m_parent->GetglCanvas()->RenderTextures(dc, coords, uv, 4,
459 m_parent->GetpVP());
460#else
461
462 glBegin(GL_QUADS);
463
464 glTexCoord2f(0, 0);
465 glVertex2i(m_rect.x, m_rect.y);
466 glTexCoord2f((float)m_image_width / m_tex_w, 0);
467 glVertex2i(m_rect.x + m_rect.width, m_rect.y);
468 glTexCoord2f((float)m_image_width / m_tex_w,
469 (float)m_image_height / m_tex_h);
470 glVertex2i(m_rect.x + m_rect.width, m_rect.y + m_rect.height);
471 glTexCoord2f(0, (float)m_image_height / m_tex_h);
472 glVertex2i(m_rect.x, m_rect.y + m_rect.height);
473
474 glEnd();
475#endif
476
477 glDisable(GL_TEXTURE_2D);
478
479 } else {
480#ifdef __WXOSX__
481 // Support MacBook Retina display
482 if (g_bopengl) {
483 double scale = m_parent->GetContentScaleFactor();
484 if (scale > 1) {
485 wxImage image = m_StatBmp.ConvertToImage();
486 image.Rescale(image.GetWidth() * scale, image.GetHeight() * scale);
487 wxBitmap bmp(image);
488 dc.DrawBitmap(bmp, m_rect.x, m_rect.y, true);
489 } else
490 dc.DrawBitmap(m_StatBmp, m_rect.x, m_rect.y, true);
491 } else
492 dc.DrawBitmap(m_StatBmp, m_rect.x, m_rect.y, true);
493#else
494 dc.DrawBitmap(m_StatBmp, m_rect.x, m_rect.y, true);
495#endif
496 }
497
498#else
499 dc.DrawBitmap(m_StatBmp, m_rect.x, m_rect.y, true);
500#endif
501 }
502}
503
504void NotificationButton::SetColorScheme(ColorScheme cs) {
505 m_cs = cs;
506 UpdateStatus(true);
507}
508
510#ifdef wxHAS_DPI_INDEPENDENT_PIXELS
511#if wxCHECK_VERSION(3, 1, 6)
512 wxRect logicalRect = wxRect(m_parent->FromPhys(m_rect.GetPosition()),
513 m_parent->FromPhys(m_rect.GetSize()));
514#else
515 double scaleFactor = m_parent->GetContentScaleFactor();
516 wxRect logicalRect(
517 wxPoint(m_rect.GetX() / scaleFactor, m_rect.GetY() / scaleFactor),
518 wxSize(m_rect.GetWidth() / scaleFactor,
519 m_rect.GetHeight() / scaleFactor));
520#endif
521#else
522 // On platforms without DPI-independent pixels, logical = physical.
523 wxRect logicalRect = m_rect;
524#endif
525 return logicalRect;
526}
527
528bool NotificationButton::UpdateStatus(bool bnew) {
529 bool rv = false;
530 if (bnew) m_lastNoteIconName.Clear(); // force an update to occur
531 if (m_lastNoteIconName != m_NoteIconName) {
532 CreateBmp(bnew);
533 rv = true;
534 }
535
536#ifdef ocpnUSE_GL
537 if (g_bopengl && m_texobj) CreateTexture();
538#endif
539 return rv;
540}
541
542void NotificationButton::SetScaleFactor(float factor) {
543 ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
544
545 if (factor > 0.1)
546 m_scale = factor;
547 else
548 m_scale = 1.0;
549
550 // Precalculate the background sizes to get m_rect width/height
551 wxBitmap noteBg;
552 int orient = style->GetOrientation();
553 style->SetOrientation(wxTB_HORIZONTAL);
554 if (style->HasBackground()) {
555 noteBg = style->GetNormalBG();
556 style->DrawToolbarLineEnd(noteBg);
557 noteBg = style->SetBitmapBrightness(noteBg, m_cs);
558 }
559
560 if (fabs(m_scale - 1.0) > 0.1) {
561 // noteBg = noteBg.ConvertToImage();
562 // noteBg.Rescale(noteBg.GetWidth() * m_scale, noteBg.GetHeight() * m_scale,
563 // wxIMAGE_QUALITY_NORMAL);
564 // noteBg = wxBitmap(noteBg);
565 }
566
567 int width = noteBg.GetWidth() + style->GetCompassLeftMargin();
568 if (!style->marginsInvisible)
569 width += style->GetCompassLeftMargin() + style->GetToolSeparation();
570
571 m_rect = wxRect(style->GetCompassXOffset(), style->GetCompassYOffset(), width,
572 noteBg.GetHeight() + style->GetCompassTopMargin() +
573 style->GetCompassBottomMargin());
574}
575
576void NotificationButton::CreateBmp(bool newColorScheme) {
577 // wxString gpsIconName;
578 ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
579
580 // In order to draw a horizontal compass window when the toolbar is vertical,
581 // we need to save away the sizes and backgrounds for the two icons.
582
583 // static wxBitmap compassBg;
584 static wxBitmap noteBg;
585 static wxSize toolsize;
586 static int topmargin, leftmargin, radius;
587
588 if (!noteBg.IsOk() || newColorScheme) {
589 int orient = style->GetOrientation();
590 style->SetOrientation(wxTB_HORIZONTAL);
591 if (style->HasBackground()) {
592 noteBg = style->GetNormalBG();
593 style->DrawToolbarLineEnd(noteBg);
594 noteBg = style->SetBitmapBrightness(noteBg, m_cs);
595 }
596
597 if (fabs(m_scale - 1.0) > 0.1) {
598 wxImage bg_img = noteBg.ConvertToImage();
599 bg_img.Rescale(noteBg.GetWidth() * m_scale, noteBg.GetHeight() * m_scale,
600 wxIMAGE_QUALITY_NORMAL);
601 noteBg = wxBitmap(bg_img);
602 }
603
604 leftmargin = style->GetCompassLeftMargin();
605 topmargin = style->GetCompassTopMargin();
606 radius = style->GetCompassCornerRadius();
607
608 if (orient == wxTB_VERTICAL) style->SetOrientation(wxTB_VERTICAL);
609 }
610
611 // int width = noteBg.GetWidth();// + leftmargin;
612 int height = noteBg.GetHeight() + topmargin + style->GetCompassBottomMargin();
613 int width = height; // + leftmargin;
614 // if (!style->marginsInvisible)
615 // width += leftmargin + style->GetToolSeparation();
616
617 m_StatBmp.Create(width, height);
618
619 m_rect.width = m_StatBmp.GetWidth();
620 m_rect.height = m_StatBmp.GetHeight();
621
622 m_MaskBmp = wxBitmap(m_StatBmp.GetWidth(), m_StatBmp.GetHeight());
623 if (style->marginsInvisible) {
624 wxMemoryDC sdc(m_MaskBmp);
625 sdc.SetBackground(*wxWHITE_BRUSH);
626 sdc.Clear();
627 sdc.SetBrush(*wxBLACK_BRUSH);
628 sdc.SetPen(*wxBLACK_PEN);
629 wxSize maskSize = wxSize(m_MaskBmp.GetWidth() - leftmargin,
630 m_MaskBmp.GetHeight() - (2 * topmargin));
631 sdc.DrawRoundedRectangle(wxPoint(leftmargin, topmargin), maskSize, radius);
632 sdc.SelectObject(wxNullBitmap);
633 } else if (radius) {
634 wxMemoryDC sdc(m_MaskBmp);
635 sdc.SetBackground(*wxWHITE_BRUSH);
636 sdc.Clear();
637 sdc.SetBrush(*wxBLACK_BRUSH);
638 sdc.SetPen(*wxBLACK_PEN);
639 sdc.DrawRoundedRectangle(0, 0, m_MaskBmp.GetWidth(), m_MaskBmp.GetHeight(),
640 radius);
641 sdc.SelectObject(wxNullBitmap);
642 }
643#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
644 m_StatBmp.SetMask(new wxMask(m_MaskBmp, *wxWHITE));
645#endif
646
647 wxMemoryDC mdc;
648 mdc.SelectObject(m_StatBmp);
649 mdc.SetBackground(wxBrush(GetGlobalColor(_T("COMP1")), wxBRUSHSTYLE_SOLID));
650 mdc.Clear();
651
652 mdc.SetPen(wxPen(GetGlobalColor(_T("UITX1")), 1));
653 mdc.SetBrush(wxBrush(GetGlobalColor(_T("UITX1")), wxBRUSHSTYLE_TRANSPARENT));
654
655 if (!style->marginsInvisible)
656 mdc.DrawRoundedRectangle(0, 0, m_StatBmp.GetWidth(), m_StatBmp.GetHeight(),
657 radius);
658 wxPoint offset(leftmargin, topmargin);
659
660 // Notification Icon
661 int twidth = style->GetToolSize().x * m_scale;
662 int theight = style->GetToolSize().y * m_scale;
663 int swidth = wxMax(twidth, theight);
664 int sheight = wxMin(twidth, theight);
665
666 swidth = swidth * 45 / 50;
667 sheight = sheight * 45 / 50;
668
669 offset.x = ((m_StatBmp.GetWidth() - swidth) / 2);
670 offset.y = ((m_StatBmp.GetHeight() - sheight) / 2);
671
672 wxFileName icon_path;
673 wxString file_name = m_NoteIconName + ".svg";
674 icon_path = wxFileName(g_Platform->GetSharedDataDir(), file_name);
675 icon_path.AppendDir("uidata");
676 icon_path.AppendDir("MUI_flat");
677 wxBitmap gicon = LoadSVG(icon_path.GetFullPath(), swidth, sheight);
678
679 wxBitmap iconBm;
680 iconBm = gicon;
681
682 mdc.DrawBitmap(iconBm, offset);
683 mdc.SelectObject(wxNullBitmap);
684
685 m_lastNoteIconName = m_NoteIconName;
686}
687
688void NotificationButton::CreateTexture() {
689#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
690 // GLES does not do ocpnDC::DrawBitmap(), so use
691 // texture
692 if (g_bopengl) {
693 wxImage image = m_StatBmp.ConvertToImage();
694 unsigned char* imgdata = image.GetData();
695 unsigned char* imgalpha = image.GetAlpha();
696 m_tex_w = image.GetWidth();
697 m_tex_h = image.GetHeight();
698 m_image_width = m_tex_w;
699 m_image_height = m_tex_h;
700
701 // Make it POT
702 int width_pot = m_tex_w;
703 int height_pot = m_tex_h;
704
705 int xp = image.GetWidth();
706 if (((xp != 0) && !(xp & (xp - 1)))) // detect POT
707 width_pot = xp;
708 else {
709 int a = 0;
710 while (xp) {
711 xp = xp >> 1;
712 a++;
713 }
714 width_pot = 1 << a;
715 }
716
717 xp = image.GetHeight();
718 if (((xp != 0) && !(xp & (xp - 1))))
719 height_pot = xp;
720 else {
721 int a = 0;
722 while (xp) {
723 xp = xp >> 1;
724 a++;
725 }
726 height_pot = 1 << a;
727 }
728
729 m_tex_w = width_pot;
730 m_tex_h = height_pot;
731
732 GLuint format = GL_RGBA;
733 GLuint internalformat = GL_RGBA8; // format;
734 int stride = 4;
735
736 if (imgdata) {
737 unsigned char* teximage =
738 (unsigned char*)malloc(stride * m_tex_w * m_tex_h);
739
740 for (int i = 0; i < m_image_height; i++) {
741 for (int j = 0; j < m_image_width; j++) {
742 int s = (i * 3 * m_image_width) + (j * 3);
743 int d = (i * stride * m_tex_w) + (j * stride);
744
745 teximage[d + 0] = imgdata[s + 0];
746 teximage[d + 1] = imgdata[s + 1];
747 teximage[d + 2] = imgdata[s + 2];
748 teximage[d + 3] = 255;
749 }
750 }
751
752 glBindTexture(GL_TEXTURE_2D, m_texobj);
753
754 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
755 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
756 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
757 GL_NEAREST); // No mipmapping
758 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
759
760 glTexImage2D(GL_TEXTURE_2D, 0, internalformat, m_tex_w, m_tex_h, 0,
761 format, GL_UNSIGNED_BYTE, teximage);
762
763 free(teximage);
764 glBindTexture(GL_TEXTURE_2D, 0);
765 m_texOK = true;
766 }
767 }
768#endif
769}
770
771void NotificationButton::UpdateTexture() {
772#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
773 // GLES does not do ocpnDC::DrawBitmap(), so use
774 // texture
775 if (g_bopengl) {
776 wxImage image = m_StatBmp.ConvertToImage();
777 unsigned char* imgdata = image.GetData();
778 unsigned char* imgalpha = image.GetAlpha();
779 m_tex_w = image.GetWidth();
780 m_tex_h = image.GetHeight();
781 m_image_width = m_tex_w;
782 m_image_height = m_tex_h;
783
784 // Make it POT
785 int width_pot = m_tex_w;
786 int height_pot = m_tex_h;
787
788 int xp = image.GetWidth();
789 if (((xp != 0) && !(xp & (xp - 1)))) // detect POT
790 width_pot = xp;
791 else {
792 int a = 0;
793 while (xp) {
794 xp = xp >> 1;
795 a++;
796 }
797 width_pot = 1 << a;
798 }
799
800 xp = image.GetHeight();
801 if (((xp != 0) && !(xp & (xp - 1))))
802 height_pot = xp;
803 else {
804 int a = 0;
805 while (xp) {
806 xp = xp >> 1;
807 a++;
808 }
809 height_pot = 1 << a;
810 }
811
812 m_tex_w = width_pot;
813 m_tex_h = height_pot;
814
815 GLuint format = GL_RGBA;
816 GLuint internalformat = GL_RGBA8; // format;
817 int stride = 4;
818
819 if (imgdata) {
820 unsigned char* teximage =
821 (unsigned char*)malloc(stride * m_tex_w * m_tex_h);
822
823 for (int i = 0; i < m_image_height; i++) {
824 for (int j = 0; j < m_image_width; j++) {
825 int s = (i * 3 * m_image_width) + (j * 3);
826 int d = (i * stride * m_tex_w) + (j * stride);
827
828 teximage[d + 0] = imgdata[s + 0];
829 teximage[d + 1] = imgdata[s + 1];
830 teximage[d + 2] = imgdata[s + 2];
831 teximage[d + 3] = 255;
832 }
833 }
834
835 glBindTexture(GL_TEXTURE_2D, m_texobj);
836 glEnable(GL_TEXTURE_2D);
837
838 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_tex_w, m_tex_h, format,
839 GL_UNSIGNED_BYTE, teximage);
840
841 free(teximage);
842 glBindTexture(GL_TEXTURE_2D, 0);
843 }
844 }
845#endif
846}
double GetDisplayDIPMult(wxWindow *win)
Get the display scaling factor for DPI-aware rendering.
ChartCanvas - Main chart display and interaction component.
Definition chcanv.h:151
wxRect GetLogicalRect(void) const
Return the coordinates of the widget, in logical pixels.
The global list of user notifications, a singleton.
const std::vector< std::shared_ptr< Notification > > & GetNotifications() const
Return current active notifications.
bool AcknowledgeNotification(const std::string &guid)
User ack on a notification which eventually will remove it.
User visible notification.
Provides platform-specific support utilities for OpenCPN.
wxSize getDisplaySize()
Get the display size in logical pixels.
Device context class that can use either wxDC or OpenGL for drawing.
Definition ocpndc.h:64
wxFont * GetOCPNScaledFont(wxString item, int default_size)
Retrieves a font from FontMgr, optionally scaled for physical readability.
Definition gui_lib.cpp:54
General purpose GUI support.
Class Notification.
Class NotificationManager.
Configuration options for date and time formatting.