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