28#include <wx/statline.h>
29#include <wx/textwrapper.h>
31#include "notification_manager_gui.h"
35#include "color_handler.h"
37#include "OCPNPlatform.h"
39#include "glChartCanvas.h"
42#include "model/datetime.h"
48#define wxFULL_PAINT_ON_RESIZE 0x00010000
50#define wxFULL_PAINT_ON_RESIZE 0
60 Wrap(win, text, widthMax);
62 wxString
const& GetWrapped()
const {
return m_wrapped; }
63 int const GetLineCount()
const {
return m_lineCount; }
64 wxArrayString GetLineArray() {
return m_array; }
67 virtual void OnOutputLine(
const wxString& line) {
71 virtual void OnNewLine() {
79 wxArrayString m_array;
84EVT_PAINT(NotificationPanel::OnPaint)
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;
94 wxBoxSizer* topSizer =
new wxBoxSizer(wxVERTICAL);
97 wxBoxSizer* itemBoxSizer01 =
new wxBoxSizer(wxHORIZONTAL);
98 topSizer->Add(itemBoxSizer01, 1, wxEXPAND);
100 double iconSize = GetCharWidth() * 3;
102 int icon_scale = iconSize * dpi_mult;
104 wxImage notification_icon;
108 if (notification->GetSeverity() == NotificationSeverity::kInformational) {
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");
115 path = wxFileName(g_Platform->GetSharedDataDir(),
116 "notification-critical-2.svg");
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);
123 itemBoxSizer01->Add(m_itemStaticBitmap, 0, wxEXPAND | wxALL, 10);
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);
132 wxDateTime act_time = wxDateTime(notification->GetActivateTime());
133 wxString stime = wxString::Format(
136 .SetFormatString(
"$short_date")
137 .SetShowTimezone(
false)));
138 stime = stime.BeforeFirst(
' ');
139 wxString stime1 = wxString::Format(
140 "%s", ocpn::toUsrDateTimeFormat(act_time,
142 "$24_hour_minutes_seconds")));
146 auto timetextbox =
new wxStaticText(
this, wxID_ANY, stime);
147 itemBoxSizer01->Add(timetextbox, 0,
148 wxALL | wxALIGN_CENTER_VERTICAL, 5);
151 GetSize().x * 50 / 100);
153 auto textbox =
new wxStaticText(
this, wxID_ANY, wrapper.GetWrapped());
154 itemBoxSizer01->Add(textbox, 0, wxALIGN_CENTER_VERTICAL | wxALL, 10);
156 itemBoxSizer01->AddStretchSpacer();
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);
164 DimeControl(m_ack_button);
167NotificationPanel::~NotificationPanel() {}
169void NotificationPanel::OnAckButton(wxCommandEvent& event) {
174void NotificationPanel::OnPaint(wxPaintEvent& event) {
176 wxColor back_color = GetDialogColor(DLG_UNSELECTED_BACKGROUND);
177 wxBrush bg(back_color, wxBRUSHSTYLE_SOLID);
178 dc.SetBackground(bg);
182 wxColour box_color = GetDialogColor(DLG_UNSELECTED_BACKGROUND);
183 wxColour box_border = GetGlobalColor(
"GREY3");
185 wxBrush b(box_color, wxBRUSHSTYLE_SOLID);
187 dc.SetPen(wxPen(box_border, penWidth));
189 dc.DrawRoundedRectangle(5, 2, GetSize().x - 10, GetSize().y - 4, 5);
192NotificationListPanel::NotificationListPanel(wxWindow* parent, wxWindowID
id,
195 : wxScrolledWindow(parent, id, pos, size,
196 wxTAB_TRAVERSAL | wxVSCROLL | wxHSCROLL) {
197 SetSizer(
new wxBoxSizer(wxVERTICAL));
199 ShowScrollbars(wxSHOW_SB_NEVER, wxSHOW_SB_DEFAULT);
201 ShowScrollbars(wxSHOW_SB_NEVER, wxSHOW_SB_NEVER);
204 ReloadNotificationPanels();
208NotificationListPanel::~NotificationListPanel() {}
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();
216 if (pp) win->Destroy();
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);
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) {
240 for (
auto tpanel : panels) {
241 auto note = tpanel->GetNotification();
243 if ((note->GetStringHash() == this_hash) && (repeat_count > 1)) {
251 notification, repeat_count);
252 panels.push_back(panel);
255 for (
auto panel : panels) {
256 AddNotificationPanel(panel);
267 GetSizer()->Add(_panel, 0);
275EVT_CLOSE(NotificationsList::OnClose)
282 long mstyle = wxRESIZE_BORDER | wxCAPTION | wxCLOSE_BOX | wxFRAME_NO_TASKBAR;
284 mstyle |= wxSTAY_ON_TOP;
287 wxDialog::Create(parent, wxID_ANY, _(
"OpenCPN Notifications"),
288 wxDefaultPosition, wxDefaultSize, mstyle);
290 wxBoxSizer* topsizer =
new wxBoxSizer(wxVERTICAL);
294 int group_item_spacing = 0;
295 int interGroupSpace = border_size * 2;
297 auto acksizer =
new wxBoxSizer(wxHORIZONTAL);
298 topsizer->Add(acksizer, 0, wxEXPAND);
301 acksizer->AddStretchSpacer(1);
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);
310 topsizer->Add(0, interGroupSpace);
313 this, wxID_ANY, wxDefaultPosition, wxSize(-1, parent->GetSize().y));
314 topsizer->Add(m_notifications_list_panel, 0, wxALL | wxEXPAND, border_size);
318void NotificationsList::SetColorScheme() { DimeControl(
this); }
320void NotificationsList::ReloadNotificationList() {
321 m_notifications_list_panel->ReloadNotificationPanels();
322 if (!m_notifications_list_panel->GetPanels().size()) {
324 GetParent()->Refresh();
328void NotificationsList::OnAckAllButton(wxCommandEvent& event) {
330 noteman.AcknowledgeAllNotifications();
333void NotificationsList::RecalculateSize() {
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();
343 wxMax(size_y, 8 * GetCharHeight());
345 wxSize target_size = wxSize(GetCharWidth() * 80, size_y);
347 wxPoint targetNLPos = GetParent()->ClientToScreen(
348 wxPoint(parent_size.x / 2, 3 * GetParent()->GetCharHeight()));
352 if (target_size.x * 2 > display_size.x) {
354 display_size.x * 85 / 100 - (2 * GetParent()->GetCharWidth());
356 GetParent()->ClientToScreen(wxPoint(display_size.x * 15 / 100, 0)).x;
359 SetSize(target_size);
364void NotificationsList::OnClose(wxCloseEvent& event) { Hide(); }
371extern bool g_bSatValid;
372extern int g_SatsInView;
373extern bool g_bopengl;
374extern bool g_btenhertz;
377#define GL_RGBA8 0x8058
380NotificationButton::NotificationButton(
ChartCanvas* parent) {
384 _img_gpsRed = style->GetIcon(_T(
"gpsRed"));
386 m_pStatBoxToolStaticBmp = NULL;
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());
400 m_cs = GLOBAL_COLOR_SCHEME_RGB;
401 m_NoteIconName =
"notification-info-2";
404NotificationButton::~NotificationButton() {
407 glDeleteTextures(1, &m_texobj);
412 delete m_pStatBoxToolStaticBmp;
415void NotificationButton::SetIconSeverity(NotificationSeverity _severity) {
417 if (_severity == NotificationSeverity::kInformational) {
418 icon_name =
"notification-info-2";
419 }
else if (_severity == NotificationSeverity::kWarning) {
420 icon_name =
"notification-warning-2";
422 icon_name =
"notification-critical-2";
425 SetIconName(icon_name);
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) {
434 glGenTextures(1, &m_texobj);
438 if (g_bopengl && m_texobj ) {
439 glBindTexture(GL_TEXTURE_2D, m_texobj);
440 glEnable(GL_TEXTURE_2D);
442#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
449 uv[2] = (float)m_image_width / m_tex_w;
451 uv[4] = (float)m_image_width / m_tex_w;
452 uv[5] = (float)m_image_height / m_tex_h;
454 uv[7] = (float)m_image_height / m_tex_h;
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;
466 m_parent->GetglCanvas()->RenderTextures(dc, coords, uv, 4,
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);
485 glDisable(GL_TEXTURE_2D);
491 double scale = m_parent->GetContentScaleFactor();
493 wxImage image = m_StatBmp.ConvertToImage();
494 image.Rescale(image.GetWidth() *
scale, image.GetHeight() *
scale);
496 dc.DrawBitmap(bmp, m_rect.x, m_rect.y,
true);
498 dc.DrawBitmap(m_StatBmp, m_rect.x, m_rect.y,
true);
500 dc.DrawBitmap(m_StatBmp, m_rect.x, m_rect.y,
true);
502 dc.DrawBitmap(m_StatBmp, m_rect.x, m_rect.y,
true);
507 dc.DrawBitmap(m_StatBmp, m_rect.x, m_rect.y,
true);
512void NotificationButton::SetColorScheme(ColorScheme cs) {
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()));
523 double scaleFactor = m_parent->GetContentScaleFactor();
525 wxPoint(m_rect.GetX() / scaleFactor, m_rect.GetY() / scaleFactor),
526 wxSize(m_rect.GetWidth() / scaleFactor,
527 m_rect.GetHeight() / scaleFactor));
531 wxRect logicalRect = m_rect;
536bool NotificationButton::UpdateStatus(
bool bnew) {
538 if (bnew) m_lastNoteIconName.Clear();
539 if (m_lastNoteIconName != m_NoteIconName) {
545 if (g_bopengl && m_texobj) CreateTexture();
550void NotificationButton::SetScaleFactor(
float factor) {
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);
568 if (fabs(m_scale - 1.0) > 0.1) {
575 int width = noteBg.GetWidth() + style->GetCompassLeftMargin();
576 if (!style->marginsInvisible)
577 width += style->GetCompassLeftMargin() + style->GetToolSeparation();
579 m_rect = wxRect(style->GetCompassXOffset(), style->GetCompassYOffset(), width,
580 noteBg.GetHeight() + style->GetCompassTopMargin() +
581 style->GetCompassBottomMargin());
584void NotificationButton::CreateBmp(
bool newColorScheme) {
592 static wxBitmap noteBg;
593 static wxSize toolsize;
594 static int topmargin, leftmargin, radius;
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);
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);
612 leftmargin = style->GetCompassLeftMargin();
613 topmargin = style->GetCompassTopMargin();
614 radius = style->GetCompassCornerRadius();
616 if (orient == wxTB_VERTICAL) style->SetOrientation(wxTB_VERTICAL);
620 int height = noteBg.GetHeight() + topmargin + style->GetCompassBottomMargin();
625 m_StatBmp.Create(width, height);
627 m_rect.width = m_StatBmp.GetWidth();
628 m_rect.height = m_StatBmp.GetHeight();
630 m_MaskBmp = wxBitmap(m_StatBmp.GetWidth(), m_StatBmp.GetHeight());
631 if (style->marginsInvisible) {
632 wxMemoryDC sdc(m_MaskBmp);
633 sdc.SetBackground(*wxWHITE_BRUSH);
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);
642 wxMemoryDC sdc(m_MaskBmp);
643 sdc.SetBackground(*wxWHITE_BRUSH);
645 sdc.SetBrush(*wxBLACK_BRUSH);
646 sdc.SetPen(*wxBLACK_PEN);
647 sdc.DrawRoundedRectangle(0, 0, m_MaskBmp.GetWidth(), m_MaskBmp.GetHeight(),
649 sdc.SelectObject(wxNullBitmap);
651#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
652 m_StatBmp.SetMask(
new wxMask(m_MaskBmp, *wxWHITE));
656 mdc.SelectObject(m_StatBmp);
657 mdc.SetBackground(wxBrush(GetGlobalColor(_T(
"COMP1")), wxBRUSHSTYLE_SOLID));
660 mdc.SetPen(wxPen(GetGlobalColor(_T(
"UITX1")), 1));
661 mdc.SetBrush(wxBrush(GetGlobalColor(_T(
"UITX1")), wxBRUSHSTYLE_TRANSPARENT));
663 if (!style->marginsInvisible)
664 mdc.DrawRoundedRectangle(0, 0, m_StatBmp.GetWidth(), m_StatBmp.GetHeight(),
666 wxPoint offset(leftmargin, topmargin);
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);
674 swidth = swidth * 45 / 50;
675 sheight = sheight * 45 / 50;
677 offset.x = ((m_StatBmp.GetWidth() - swidth) / 2);
678 offset.y = ((m_StatBmp.GetHeight() - sheight) / 2);
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);
690 mdc.DrawBitmap(iconBm, offset);
691 mdc.SelectObject(wxNullBitmap);
693 m_lastNoteIconName = m_NoteIconName;
696void NotificationButton::CreateTexture() {
697#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
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;
710 int width_pot = m_tex_w;
711 int height_pot = m_tex_h;
713 int xp = image.GetWidth();
714 if (((xp != 0) && !(xp & (xp - 1))))
725 xp = image.GetHeight();
726 if (((xp != 0) && !(xp & (xp - 1))))
738 m_tex_h = height_pot;
740 GLuint format = GL_RGBA;
741 GLuint internalformat = GL_RGBA8;
745 unsigned char* teximage =
746 (
unsigned char*)malloc(stride * m_tex_w * m_tex_h);
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);
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;
760 glBindTexture(GL_TEXTURE_2D, m_texobj);
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,
766 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
768 glTexImage2D(GL_TEXTURE_2D, 0, internalformat, m_tex_w, m_tex_h, 0,
769 format, GL_UNSIGNED_BYTE, teximage);
772 glBindTexture(GL_TEXTURE_2D, 0);
779void NotificationButton::UpdateTexture() {
780#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
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;
793 int width_pot = m_tex_w;
794 int height_pot = m_tex_h;
796 int xp = image.GetWidth();
797 if (((xp != 0) && !(xp & (xp - 1))))
808 xp = image.GetHeight();
809 if (((xp != 0) && !(xp & (xp - 1))))
821 m_tex_h = height_pot;
823 GLuint format = GL_RGBA;
824 GLuint internalformat = GL_RGBA8;
828 unsigned char* teximage =
829 (
unsigned char*)malloc(stride * m_tex_w * m_tex_h);
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);
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;
843 glBindTexture(GL_TEXTURE_2D, m_texobj);
844 glEnable(GL_TEXTURE_2D);
846 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_tex_w, m_tex_h, format,
847 GL_UNSIGNED_BYTE, teximage);
850 glBindTexture(GL_TEXTURE_2D, 0);
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.
wxFont * GetOCPNScaledFont(wxString item, int default_size)
Retrieves a font from FontMgr, optionally scaled for physical readability.
General purpose GUI support.
Class NotificationManager.
Global variables Listen()/Notify() wrapper.