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