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