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