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>
46#include "observable/global_var.h"
59#define wxFULL_PAINT_ON_RESIZE 0x00010000
61#define wxFULL_PAINT_ON_RESIZE 0
68 Wrap(win, text, widthMax);
70 wxString
const& GetWrapped()
const {
return m_wrapped; }
71 int const GetLineCount()
const {
return m_lineCount; }
72 wxArrayString GetLineArray() {
return m_array; }
75 virtual void OnOutputLine(
const wxString& line) {
79 virtual void OnNewLine() {
87 wxArrayString m_array;
92EVT_PAINT(NotificationPanel::OnPaint)
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;
102 wxBoxSizer* topSizer =
new wxBoxSizer(wxVERTICAL);
105 wxBoxSizer* itemBoxSizer01 =
new wxBoxSizer(wxHORIZONTAL);
106 topSizer->Add(itemBoxSizer01, 1, wxEXPAND);
108 double iconSize = GetCharWidth() * 3;
110 int icon_scale = iconSize * dpi_mult;
112 wxImage notification_icon;
116 if (notification->GetSeverity() == NotificationSeverity::kInformational) {
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");
123 path = wxFileName(g_Platform->GetSharedDataDir(),
124 "notification-critical-2.svg");
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);
131 itemBoxSizer01->Add(m_itemStaticBitmap, 0, wxEXPAND | wxALL, 10);
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);
140 wxDateTime act_time = wxDateTime(notification->GetActivateTime());
141 wxString stime = wxString::Format(
144 .SetFormatString(
"$short_date")
145 .SetShowTimezone(
false)));
146 stime = stime.BeforeFirst(
' ');
147 wxString stime1 = wxString::Format(
148 "%s", ocpn::toUsrDateTimeFormat(act_time,
150 "$24_hour_minutes_seconds")));
154 auto timetextbox =
new wxStaticText(
this, wxID_ANY, stime);
155 itemBoxSizer01->Add(timetextbox, 0,
156 wxALL | wxALIGN_CENTER_VERTICAL, 5);
159 GetSize().x * 50 / 100);
161 auto textbox =
new wxStaticText(
this, wxID_ANY, wrapper.GetWrapped());
162 itemBoxSizer01->Add(textbox, 0, wxALIGN_CENTER_VERTICAL | wxALL, 10);
164 itemBoxSizer01->AddStretchSpacer();
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);
172 DimeControl(m_ack_button);
175NotificationPanel::~NotificationPanel() {}
177void NotificationPanel::OnAckButton(wxCommandEvent& event) {
182void NotificationPanel::OnPaint(wxPaintEvent& event) {
185 wxBrush bg(back_color, wxBRUSHSTYLE_SOLID);
186 dc.SetBackground(bg);
191 wxColour box_border = GetGlobalColor(
"GREY3");
193 wxBrush b(box_color, wxBRUSHSTYLE_SOLID);
195 dc.SetPen(wxPen(box_border, penWidth));
197 dc.DrawRoundedRectangle(5, 2, GetSize().x - 10, GetSize().y - 4, 5);
200NotificationListPanel::NotificationListPanel(wxWindow* parent, wxWindowID
id,
203 : wxScrolledWindow(parent, id, pos, size,
204 wxTAB_TRAVERSAL | wxVSCROLL | wxHSCROLL) {
205 SetSizer(
new wxBoxSizer(wxVERTICAL));
207 ShowScrollbars(wxSHOW_SB_NEVER, wxSHOW_SB_DEFAULT);
209 ShowScrollbars(wxSHOW_SB_NEVER, wxSHOW_SB_NEVER);
212 ReloadNotificationPanels();
216NotificationListPanel::~NotificationListPanel() {}
218void NotificationListPanel::ReloadNotificationPanels() {
219 wxWindowList kids = GetChildren();
220 for (
unsigned int i = 0; i < kids.GetCount(); i++) {
221 wxWindowListNode* node = kids.Item(i);
222 wxWindow* win = node->GetData();
224 if (pp) win->Destroy();
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);
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) {
248 for (
auto tpanel : panels) {
249 auto note = tpanel->GetNotification();
251 if ((note->GetStringHash() == this_hash) && (repeat_count > 1)) {
259 notification, repeat_count);
260 panels.push_back(panel);
263 for (
auto panel : panels) {
264 AddNotificationPanel(panel);
275 GetSizer()->Add(_panel, 0);
283EVT_CLOSE(NotificationsList::OnClose)
290 long mstyle = wxRESIZE_BORDER | wxCAPTION |
wxCLOSE_BOX | wxFRAME_NO_TASKBAR;
292 mstyle |= wxSTAY_ON_TOP;
295 wxDialog::Create(parent, wxID_ANY, _(
"OpenCPN Notifications"),
296 wxDefaultPosition, wxDefaultSize, mstyle);
298 wxBoxSizer* topsizer =
new wxBoxSizer(wxVERTICAL);
302 int group_item_spacing = 0;
303 int interGroupSpace = border_size * 2;
305 auto acksizer =
new wxBoxSizer(wxHORIZONTAL);
306 topsizer->Add(acksizer, 0, wxEXPAND);
309 acksizer->AddStretchSpacer(1);
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);
318 topsizer->Add(0, interGroupSpace);
321 this, wxID_ANY, wxDefaultPosition, wxSize(-1, parent->GetSize().y));
322 topsizer->Add(m_notifications_list_panel, 0, wxALL | wxEXPAND, border_size);
326void NotificationsList::SetColorScheme() { DimeControl(
this); }
328void NotificationsList::ReloadNotificationList() {
329 m_notifications_list_panel->ReloadNotificationPanels();
330 if (!m_notifications_list_panel->GetPanels().size()) {
332 GetParent()->Refresh();
336void NotificationsList::OnAckAllButton(wxCommandEvent& event) {
338 noteman.AcknowledgeAllNotifications();
341void NotificationsList::RecalculateSize() {
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();
351 wxMax(size_y, 8 * GetCharHeight());
353 wxSize target_size = wxSize(GetCharWidth() * 80, size_y);
355 wxPoint targetNLPos = GetParent()->ClientToScreen(
356 wxPoint(parent_size.x / 2, 3 * GetParent()->GetCharHeight()));
360 if (target_size.x * 2 > display_size.x) {
362 display_size.x * 85 / 100 - (2 * GetParent()->GetCharWidth());
364 GetParent()->ClientToScreen(wxPoint(display_size.x * 15 / 100, 0)).x;
367 SetSize(target_size);
372void NotificationsList::OnClose(wxCloseEvent& event) { Hide(); }
380extern int g_SatsInView;
381extern bool g_bopengl;
382extern bool g_btenhertz;
385#define GL_RGBA8 0x8058
388NotificationButton::NotificationButton(
ChartCanvas* parent) {
392 _img_gpsRed = style->GetIcon(
"gpsRed");
394 m_pStatBoxToolStaticBmp = NULL;
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());
408 m_cs = GLOBAL_COLOR_SCHEME_RGB;
409 m_NoteIconName =
"notification-info-2";
412NotificationButton::~NotificationButton() {
415 glDeleteTextures(1, &m_texobj);
420 delete m_pStatBoxToolStaticBmp;
423void NotificationButton::SetIconSeverity(NotificationSeverity _severity) {
425 if (_severity == NotificationSeverity::kInformational) {
426 icon_name =
"notification-info-2";
427 }
else if (_severity == NotificationSeverity::kWarning) {
428 icon_name =
"notification-warning-2";
430 icon_name =
"notification-critical-2";
433 SetIconName(icon_name);
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) {
442 glGenTextures(1, &m_texobj);
446 if (g_bopengl && m_texobj ) {
447 glBindTexture(GL_TEXTURE_2D, m_texobj);
448 glEnable(GL_TEXTURE_2D);
450#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
457 uv[2] = (float)m_image_width / m_tex_w;
459 uv[4] = (float)m_image_width / m_tex_w;
460 uv[5] = (float)m_image_height / m_tex_h;
462 uv[7] = (float)m_image_height / m_tex_h;
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;
474 m_parent->GetglCanvas()->RenderTextures(dc, coords, uv, 4,
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);
493 glDisable(GL_TEXTURE_2D);
499 double scale = m_parent->GetContentScaleFactor();
501 wxImage image = m_StatBmp.ConvertToImage();
502 image.Rescale(image.GetWidth() *
scale, image.GetHeight() *
scale);
504 dc.DrawBitmap(bmp, m_rect.x, m_rect.y,
true);
506 dc.DrawBitmap(m_StatBmp, m_rect.x, m_rect.y,
true);
508 dc.DrawBitmap(m_StatBmp, m_rect.x, m_rect.y,
true);
510 dc.DrawBitmap(m_StatBmp, m_rect.x, m_rect.y,
true);
515 dc.DrawBitmap(m_StatBmp, m_rect.x, m_rect.y,
true);
520void NotificationButton::SetColorScheme(ColorScheme cs) {
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()));
531 double scaleFactor = m_parent->GetContentScaleFactor();
533 wxPoint(m_rect.GetX() / scaleFactor, m_rect.GetY() / scaleFactor),
534 wxSize(m_rect.GetWidth() / scaleFactor,
535 m_rect.GetHeight() / scaleFactor));
539 wxRect logicalRect = m_rect;
544bool NotificationButton::UpdateStatus(
bool bnew) {
546 if (bnew) m_lastNoteIconName.Clear();
547 if (m_lastNoteIconName != m_NoteIconName) {
553 if (g_bopengl && m_texobj) CreateTexture();
558void NotificationButton::SetScaleFactor(
float factor) {
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);
576 if (fabs(m_scale - 1.0) > 0.1) {
583 int width = noteBg.GetWidth() + style->GetCompassLeftMargin();
584 if (!style->marginsInvisible)
585 width += style->GetCompassLeftMargin() + style->GetToolSeparation();
587 m_rect = wxRect(style->GetCompassXOffset(), style->GetCompassYOffset(), width,
588 noteBg.GetHeight() + style->GetCompassTopMargin() +
589 style->GetCompassBottomMargin());
592void NotificationButton::CreateBmp(
bool newColorScheme) {
600 static wxBitmap noteBg;
601 static wxSize toolsize;
602 static int topmargin, leftmargin, radius;
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);
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);
620 leftmargin = style->GetCompassLeftMargin();
621 topmargin = style->GetCompassTopMargin();
622 radius = style->GetCompassCornerRadius();
624 if (orient == wxTB_VERTICAL) style->SetOrientation(wxTB_VERTICAL);
628 int height = noteBg.GetHeight() + topmargin + style->GetCompassBottomMargin();
633 m_StatBmp.Create(width, height);
635 m_rect.width = m_StatBmp.GetWidth();
636 m_rect.height = m_StatBmp.GetHeight();
638 m_MaskBmp = wxBitmap(m_StatBmp.GetWidth(), m_StatBmp.GetHeight());
639 if (style->marginsInvisible) {
640 wxMemoryDC sdc(m_MaskBmp);
641 sdc.SetBackground(*wxWHITE_BRUSH);
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);
650 wxMemoryDC sdc(m_MaskBmp);
651 sdc.SetBackground(*wxWHITE_BRUSH);
653 sdc.SetBrush(*wxBLACK_BRUSH);
654 sdc.SetPen(*wxBLACK_PEN);
655 sdc.DrawRoundedRectangle(0, 0, m_MaskBmp.GetWidth(), m_MaskBmp.GetHeight(),
657 sdc.SelectObject(wxNullBitmap);
659#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
660 m_StatBmp.SetMask(
new wxMask(m_MaskBmp, *wxWHITE));
664 mdc.SelectObject(m_StatBmp);
665 mdc.SetBackground(wxBrush(GetGlobalColor(
"COMP1"), wxBRUSHSTYLE_SOLID));
668 mdc.SetPen(wxPen(GetGlobalColor(
"UITX1"), 1));
669 mdc.SetBrush(wxBrush(GetGlobalColor(
"UITX1"), wxBRUSHSTYLE_TRANSPARENT));
671 if (!style->marginsInvisible)
672 mdc.DrawRoundedRectangle(0, 0, m_StatBmp.GetWidth(), m_StatBmp.GetHeight(),
674 wxPoint offset(leftmargin, topmargin);
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);
682 swidth = swidth * 45 / 50;
683 sheight = sheight * 45 / 50;
685 offset.x = ((m_StatBmp.GetWidth() - swidth) / 2);
686 offset.y = ((m_StatBmp.GetHeight() - sheight) / 2);
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);
698 mdc.DrawBitmap(iconBm, offset);
699 mdc.SelectObject(wxNullBitmap);
701 m_lastNoteIconName = m_NoteIconName;
704void NotificationButton::CreateTexture() {
705#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
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;
718 int width_pot = m_tex_w;
719 int height_pot = m_tex_h;
721 int xp = image.GetWidth();
722 if (((xp != 0) && !(xp & (xp - 1))))
733 xp = image.GetHeight();
734 if (((xp != 0) && !(xp & (xp - 1))))
746 m_tex_h = height_pot;
748 GLuint format = GL_RGBA;
749 GLuint internalformat = GL_RGBA8;
753 unsigned char* teximage =
754 (
unsigned char*)malloc(stride * m_tex_w * m_tex_h);
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);
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;
768 glBindTexture(GL_TEXTURE_2D, m_texobj);
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,
774 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
776 glTexImage2D(GL_TEXTURE_2D, 0, internalformat, m_tex_w, m_tex_h, 0,
777 format, GL_UNSIGNED_BYTE, teximage);
780 glBindTexture(GL_TEXTURE_2D, 0);
787void NotificationButton::UpdateTexture() {
788#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
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;
801 int width_pot = m_tex_w;
802 int height_pot = m_tex_h;
804 int xp = image.GetWidth();
805 if (((xp != 0) && !(xp & (xp - 1))))
816 xp = image.GetHeight();
817 if (((xp != 0) && !(xp & (xp - 1))))
829 m_tex_h = height_pot;
831 GLuint format = GL_RGBA;
832 GLuint internalformat = GL_RGBA8;
836 unsigned char* teximage =
837 (
unsigned char*)malloc(stride * m_tex_w * m_tex_h);
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);
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;
851 glBindTexture(GL_TEXTURE_2D, m_texobj);
852 glEnable(GL_TEXTURE_2D);
854 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_tex_w, m_tex_h, format,
855 GL_UNSIGNED_BYTE, teximage);
858 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.
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...