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