30#include <wx/datetime.h>
31#include <wx/filename.h>
34#include <wx/statline.h>
35#include <wx/stattext.h>
37#include <wx/textwrapper.h>
58#define wxFULL_PAINT_ON_RESIZE 0x00010000
60#define wxFULL_PAINT_ON_RESIZE 0
67 Wrap(win, text, widthMax);
69 wxString
const& GetWrapped()
const {
return m_wrapped; }
70 int const GetLineCount()
const {
return m_lineCount; }
71 wxArrayString GetLineArray() {
return m_array; }
74 virtual void OnOutputLine(
const wxString& line) {
78 virtual void OnNewLine() {
86 wxArrayString m_array;
91EVT_PAINT(NotificationPanel::OnPaint)
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;
101 wxBoxSizer* topSizer =
new wxBoxSizer(wxVERTICAL);
104 wxBoxSizer* itemBoxSizer01 =
new wxBoxSizer(wxHORIZONTAL);
105 topSizer->Add(itemBoxSizer01, 1, wxEXPAND);
107 double iconSize = GetCharWidth() * 3;
109 int icon_scale = iconSize * dpi_mult;
111 wxImage notification_icon;
115 if (notification->GetSeverity() == NotificationSeverity::kInformational) {
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");
122 path = wxFileName(g_Platform->GetSharedDataDir(),
123 "notification-critical-2.svg");
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);
130 itemBoxSizer01->Add(m_itemStaticBitmap, 0, wxEXPAND | wxALL, 10);
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);
139 wxDateTime act_time = wxDateTime(notification->GetActivateTime());
140 wxString stime = wxString::Format(
143 .SetFormatString(
"$short_date")
144 .SetShowTimezone(
false)));
145 stime = stime.BeforeFirst(
' ');
146 wxString stime1 = wxString::Format(
147 "%s", ocpn::toUsrDateTimeFormat(act_time,
149 "$24_hour_minutes_seconds")));
153 auto timetextbox =
new wxStaticText(
this, wxID_ANY, stime);
154 itemBoxSizer01->Add(timetextbox, 0,
155 wxALL | wxALIGN_CENTER_VERTICAL, 5);
158 GetSize().x * 50 / 100);
160 auto textbox =
new wxStaticText(
this, wxID_ANY, wrapper.GetWrapped());
161 itemBoxSizer01->Add(textbox, 0, wxALIGN_CENTER_VERTICAL | wxALL, 10);
163 itemBoxSizer01->AddStretchSpacer();
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);
171 DimeControl(m_ack_button);
174NotificationPanel::~NotificationPanel() {}
176void NotificationPanel::OnAckButton(wxCommandEvent& event) {
181void NotificationPanel::OnPaint(wxPaintEvent& event) {
184 wxBrush bg(back_color, wxBRUSHSTYLE_SOLID);
185 dc.SetBackground(bg);
190 wxColour box_border = GetGlobalColor(
"GREY3");
192 wxBrush b(box_color, wxBRUSHSTYLE_SOLID);
194 dc.SetPen(wxPen(box_border, penWidth));
196 dc.DrawRoundedRectangle(5, 2, GetSize().x - 10, GetSize().y - 4, 5);
199NotificationListPanel::NotificationListPanel(wxWindow* parent, wxWindowID
id,
202 : wxScrolledWindow(parent, id, pos, size,
203 wxTAB_TRAVERSAL | wxVSCROLL | wxHSCROLL) {
204 SetSizer(
new wxBoxSizer(wxVERTICAL));
206 ShowScrollbars(wxSHOW_SB_NEVER, wxSHOW_SB_DEFAULT);
208 ShowScrollbars(wxSHOW_SB_NEVER, wxSHOW_SB_NEVER);
211 ReloadNotificationPanels();
215NotificationListPanel::~NotificationListPanel() {}
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();
223 if (pp) win->Destroy();
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);
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) {
247 for (
auto tpanel : panels) {
248 auto note = tpanel->GetNotification();
250 if ((note->GetStringHash() == this_hash) && (repeat_count > 1)) {
258 notification, repeat_count);
259 panels.push_back(panel);
262 for (
auto panel : panels) {
263 AddNotificationPanel(panel);
274 GetSizer()->Add(_panel, 0);
282EVT_CLOSE(NotificationsList::OnClose)
289 long mstyle = wxRESIZE_BORDER | wxCAPTION | wxCLOSE_BOX | wxFRAME_NO_TASKBAR;
291 mstyle |= wxSTAY_ON_TOP;
294 wxDialog::Create(parent, wxID_ANY, _(
"OpenCPN Notifications"),
295 wxDefaultPosition, wxDefaultSize, mstyle);
297 wxBoxSizer* topsizer =
new wxBoxSizer(wxVERTICAL);
301 int group_item_spacing = 0;
302 int interGroupSpace = border_size * 2;
304 auto acksizer =
new wxBoxSizer(wxHORIZONTAL);
305 topsizer->Add(acksizer, 0, wxEXPAND);
308 acksizer->AddStretchSpacer(1);
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);
317 topsizer->Add(0, interGroupSpace);
320 this, wxID_ANY, wxDefaultPosition, wxSize(-1, parent->GetSize().y));
321 topsizer->Add(m_notifications_list_panel, 0, wxALL | wxEXPAND, border_size);
325void NotificationsList::SetColorScheme() { DimeControl(
this); }
327void NotificationsList::ReloadNotificationList() {
328 m_notifications_list_panel->ReloadNotificationPanels();
329 if (!m_notifications_list_panel->GetPanels().size()) {
331 GetParent()->Refresh();
335void NotificationsList::OnAckAllButton(wxCommandEvent& event) {
337 noteman.AcknowledgeAllNotifications();
340void NotificationsList::RecalculateSize() {
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();
350 wxMax(size_y, 8 * GetCharHeight());
352 wxSize target_size = wxSize(GetCharWidth() * 80, size_y);
354 wxPoint targetNLPos = GetParent()->ClientToScreen(
355 wxPoint(parent_size.x / 2, 3 * GetParent()->GetCharHeight()));
359 if (target_size.x * 2 > display_size.x) {
361 display_size.x * 85 / 100 - (2 * GetParent()->GetCharWidth());
363 GetParent()->ClientToScreen(wxPoint(display_size.x * 15 / 100, 0)).x;
366 SetSize(target_size);
371void NotificationsList::OnClose(wxCloseEvent& event) { Hide(); }
379extern int g_SatsInView;
380extern bool g_bopengl;
381extern bool g_btenhertz;
384#define GL_RGBA8 0x8058
387NotificationButton::NotificationButton(
ChartCanvas* parent) {
391 _img_gpsRed = style->GetIcon(
"gpsRed");
393 m_pStatBoxToolStaticBmp = NULL;
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());
407 m_cs = GLOBAL_COLOR_SCHEME_RGB;
408 m_NoteIconName =
"notification-info-2";
411NotificationButton::~NotificationButton() {
414 glDeleteTextures(1, &m_texobj);
419 delete m_pStatBoxToolStaticBmp;
422void NotificationButton::SetIconSeverity(NotificationSeverity _severity) {
424 if (_severity == NotificationSeverity::kInformational) {
425 icon_name =
"notification-info-2";
426 }
else if (_severity == NotificationSeverity::kWarning) {
427 icon_name =
"notification-warning-2";
429 icon_name =
"notification-critical-2";
432 SetIconName(icon_name);
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) {
441 glGenTextures(1, &m_texobj);
445 if (g_bopengl && m_texobj ) {
446 glBindTexture(GL_TEXTURE_2D, m_texobj);
447 glEnable(GL_TEXTURE_2D);
449#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
456 uv[2] = (float)m_image_width / m_tex_w;
458 uv[4] = (float)m_image_width / m_tex_w;
459 uv[5] = (float)m_image_height / m_tex_h;
461 uv[7] = (float)m_image_height / m_tex_h;
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;
473 m_parent->GetglCanvas()->RenderTextures(dc, coords, uv, 4,
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);
492 glDisable(GL_TEXTURE_2D);
498 double scale = m_parent->GetContentScaleFactor();
500 wxImage image = m_StatBmp.ConvertToImage();
501 image.Rescale(image.GetWidth() *
scale, image.GetHeight() *
scale);
503 dc.DrawBitmap(bmp, m_rect.x, m_rect.y,
true);
505 dc.DrawBitmap(m_StatBmp, m_rect.x, m_rect.y,
true);
507 dc.DrawBitmap(m_StatBmp, m_rect.x, m_rect.y,
true);
509 dc.DrawBitmap(m_StatBmp, m_rect.x, m_rect.y,
true);
514 dc.DrawBitmap(m_StatBmp, m_rect.x, m_rect.y,
true);
519void NotificationButton::SetColorScheme(ColorScheme cs) {
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()));
530 double scaleFactor = m_parent->GetContentScaleFactor();
532 wxPoint(m_rect.GetX() / scaleFactor, m_rect.GetY() / scaleFactor),
533 wxSize(m_rect.GetWidth() / scaleFactor,
534 m_rect.GetHeight() / scaleFactor));
538 wxRect logicalRect = m_rect;
543bool NotificationButton::UpdateStatus(
bool bnew) {
545 if (bnew) m_lastNoteIconName.Clear();
546 if (m_lastNoteIconName != m_NoteIconName) {
552 if (g_bopengl && m_texobj) CreateTexture();
557void NotificationButton::SetScaleFactor(
float factor) {
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);
575 if (fabs(m_scale - 1.0) > 0.1) {
582 int width = noteBg.GetWidth() + style->GetCompassLeftMargin();
583 if (!style->marginsInvisible)
584 width += style->GetCompassLeftMargin() + style->GetToolSeparation();
586 m_rect = wxRect(style->GetCompassXOffset(), style->GetCompassYOffset(), width,
587 noteBg.GetHeight() + style->GetCompassTopMargin() +
588 style->GetCompassBottomMargin());
591void NotificationButton::CreateBmp(
bool newColorScheme) {
599 static wxBitmap noteBg;
600 static wxSize toolsize;
601 static int topmargin, leftmargin, radius;
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);
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);
619 leftmargin = style->GetCompassLeftMargin();
620 topmargin = style->GetCompassTopMargin();
621 radius = style->GetCompassCornerRadius();
623 if (orient == wxTB_VERTICAL) style->SetOrientation(wxTB_VERTICAL);
627 int height = noteBg.GetHeight() + topmargin + style->GetCompassBottomMargin();
632 m_StatBmp.Create(width, height);
634 m_rect.width = m_StatBmp.GetWidth();
635 m_rect.height = m_StatBmp.GetHeight();
637 m_MaskBmp = wxBitmap(m_StatBmp.GetWidth(), m_StatBmp.GetHeight());
638 if (style->marginsInvisible) {
639 wxMemoryDC sdc(m_MaskBmp);
640 sdc.SetBackground(*wxWHITE_BRUSH);
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);
649 wxMemoryDC sdc(m_MaskBmp);
650 sdc.SetBackground(*wxWHITE_BRUSH);
652 sdc.SetBrush(*wxBLACK_BRUSH);
653 sdc.SetPen(*wxBLACK_PEN);
654 sdc.DrawRoundedRectangle(0, 0, m_MaskBmp.GetWidth(), m_MaskBmp.GetHeight(),
656 sdc.SelectObject(wxNullBitmap);
658#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
659 m_StatBmp.SetMask(
new wxMask(m_MaskBmp, *wxWHITE));
663 mdc.SelectObject(m_StatBmp);
664 mdc.SetBackground(wxBrush(GetGlobalColor(
"COMP1"), wxBRUSHSTYLE_SOLID));
667 mdc.SetPen(wxPen(GetGlobalColor(
"UITX1"), 1));
668 mdc.SetBrush(wxBrush(GetGlobalColor(
"UITX1"), wxBRUSHSTYLE_TRANSPARENT));
670 if (!style->marginsInvisible)
671 mdc.DrawRoundedRectangle(0, 0, m_StatBmp.GetWidth(), m_StatBmp.GetHeight(),
673 wxPoint offset(leftmargin, topmargin);
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);
681 swidth = swidth * 45 / 50;
682 sheight = sheight * 45 / 50;
684 offset.x = ((m_StatBmp.GetWidth() - swidth) / 2);
685 offset.y = ((m_StatBmp.GetHeight() - sheight) / 2);
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);
697 mdc.DrawBitmap(iconBm, offset);
698 mdc.SelectObject(wxNullBitmap);
700 m_lastNoteIconName = m_NoteIconName;
703void NotificationButton::CreateTexture() {
704#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
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;
717 int width_pot = m_tex_w;
718 int height_pot = m_tex_h;
720 int xp = image.GetWidth();
721 if (((xp != 0) && !(xp & (xp - 1))))
732 xp = image.GetHeight();
733 if (((xp != 0) && !(xp & (xp - 1))))
745 m_tex_h = height_pot;
747 GLuint format = GL_RGBA;
748 GLuint internalformat = GL_RGBA8;
752 unsigned char* teximage =
753 (
unsigned char*)malloc(stride * m_tex_w * m_tex_h);
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);
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;
767 glBindTexture(GL_TEXTURE_2D, m_texobj);
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,
773 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
775 glTexImage2D(GL_TEXTURE_2D, 0, internalformat, m_tex_w, m_tex_h, 0,
776 format, GL_UNSIGNED_BYTE, teximage);
779 glBindTexture(GL_TEXTURE_2D, 0);
786void NotificationButton::UpdateTexture() {
787#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
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;
800 int width_pot = m_tex_w;
801 int height_pot = m_tex_h;
803 int xp = image.GetWidth();
804 if (((xp != 0) && !(xp & (xp - 1))))
815 xp = image.GetHeight();
816 if (((xp != 0) && !(xp & (xp - 1))))
828 m_tex_h = height_pot;
830 GLuint format = GL_RGBA;
831 GLuint internalformat = GL_RGBA8;
835 unsigned char* teximage =
836 (
unsigned char*)malloc(stride * m_tex_w * m_tex_h);
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);
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;
850 glBindTexture(GL_TEXTURE_2D, m_texobj);
851 glEnable(GL_TEXTURE_2D);
853 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_tex_w, m_tex_h, format,
854 GL_UNSIGNED_BYTE, teximage);
857 glBindTexture(GL_TEXTURE_2D, 0);
Generic Chart canvas base.
ChartCanvas - Main chart display and interaction component.
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.
Device context class that can use either wxDC or OpenGL for drawing.
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.
OpenGL chart rendering canvas.
wxFont * GetOCPNScaledFont(wxString item, int default_size)
Retrieves a font from FontMgr, optionally scaled for physical readability.
General purpose GUI support.
User notification container.
User notifications manager.
bool g_bSatValid
Indicates valid GNSS reception status based on satellite visibility and successful parsing of NMEA018...
Notification Manager GUI.
Global variables Listen()/Notify() wrapper.
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...