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