28#include <wx/statline.h>
29#include <wx/textwrapper.h>
31#include "notification_manager_gui.h"
34#include "observable_globvar.h"
35#include "color_handler.h"
37#include "OCPNPlatform.h"
39#include "glChartCanvas.h"
42#include "model/datetime.h"
52 Wrap(win, text, widthMax);
54 wxString
const& GetWrapped()
const {
return m_wrapped; }
55 int const GetLineCount()
const {
return m_lineCount; }
56 wxArrayString GetLineArray() {
return m_array; }
59 virtual void OnOutputLine(
const wxString& line) {
63 virtual void OnNewLine() {
71 wxArrayString m_array;
76EVT_PAINT(NotificationPanel::OnPaint)
80 wxPanel* parent, wxWindowID
id, const wxPoint& pos, const wxSize& size,
81 std::shared_ptr<
Notification> _notification,
int _repeat_count)
82 : wxPanel(parent,
id, pos, size, wxBORDER_NONE),
83 repeat_count(_repeat_count) {
84 notification = _notification;
86 wxBoxSizer* topSizer =
new wxBoxSizer(wxVERTICAL);
89 wxBoxSizer* itemBoxSizer01 =
new wxBoxSizer(wxHORIZONTAL);
90 topSizer->Add(itemBoxSizer01, 0, wxEXPAND);
92 double iconSize = GetCharWidth() * 3;
94 int icon_scale = iconSize * dpi_mult;
96 wxImage notification_icon;
100 if (notification->GetSeverity() == NotificationSeverity::kInformational) {
102 wxFileName(g_Platform->GetSharedDataDir(),
"notification-info-2.svg");
103 }
else if (notification->GetSeverity() == NotificationSeverity::kWarning) {
104 path = wxFileName(g_Platform->GetSharedDataDir(),
105 "notification-warning-2.svg");
107 path = wxFileName(g_Platform->GetSharedDataDir(),
108 "notification-critical-2.svg");
110 path.AppendDir(
"uidata");
111 path.AppendDir(
"MUI_flat");
112 bitmap = LoadSVG(path.GetFullPath(), icon_scale, icon_scale);
113 m_itemStaticBitmap =
new wxStaticBitmap(
this, wxID_ANY, bitmap);
115 itemBoxSizer01->Add(m_itemStaticBitmap, 0, wxEXPAND | wxALL, 10);
118 wxString rp = _(
"Repeat:");
119 wxString sCount = rp + wxString::Format(
"\n %d", repeat_count);
120 auto counttextbox =
new wxStaticText(
this, wxID_ANY, sCount);
121 itemBoxSizer01->Add(counttextbox, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
124 wxDateTime act_time = wxDateTime(notification->GetActivateTime());
125 wxString stime = wxString::Format(
126 "%s", ocpn::toUsrDateTimeFormat(
128 "$short_date\n$24_hour_minutes_seconds")));
129 auto timetextbox =
new wxStaticText(
this, wxID_ANY, stime);
130 itemBoxSizer01->Add(timetextbox, 0,
131 wxALL | wxALIGN_CENTER_VERTICAL, 5);
134 GetSize().x * 5 / 10);
136 auto textbox =
new wxStaticText(
this, wxID_ANY, wrapper.GetWrapped());
137 itemBoxSizer01->Add(textbox, 0, wxALIGN_CENTER_VERTICAL | wxALL, 10);
139 itemBoxSizer01->AddStretchSpacer(1);
142 m_ack_button =
new wxButton(
this, wxID_OK);
143 itemBoxSizer01->Add(m_ack_button, 0, wxALIGN_CENTER_VERTICAL | wxALL, 10);
144 m_ack_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
145 &NotificationPanel::OnAckButton,
this);
147 DimeControl(m_ack_button);
153NotificationPanel::~NotificationPanel() {}
155void NotificationPanel::OnAckButton(wxCommandEvent& event) {
160void NotificationPanel::OnPaint(wxPaintEvent& event) {
162 wxColor back_color = GetDialogColor(DLG_UNSELECTED_BACKGROUND);
163 wxBrush bg(back_color, wxBRUSHSTYLE_SOLID);
164 dc.SetBackground(bg);
168 wxColour box_color = GetDialogColor(DLG_UNSELECTED_BACKGROUND);
169 wxColour box_border = GetGlobalColor(
"GREY3");
171 wxBrush b(box_color, wxBRUSHSTYLE_SOLID);
173 dc.SetPen(wxPen(box_border, penWidth));
175 dc.DrawRoundedRectangle(5, 5, GetSize().x - 10, GetSize().y - 10, 5);
178NotificationListPanel::NotificationListPanel(wxWindow* parent, wxWindowID
id,
181 : wxScrolledWindow(parent, id, pos, size, wxTAB_TRAVERSAL | wxVSCROLL) {
182 SetSizer(
new wxBoxSizer(wxVERTICAL));
184 ReloadNotificationPanels();
188NotificationListPanel::~NotificationListPanel() {}
190void NotificationListPanel::ReloadNotificationPanels() {
191 wxWindowList kids = GetChildren();
192 for (
unsigned int i = 0; i < kids.GetCount(); i++) {
193 wxWindowListNode* node = kids.Item(i);
194 wxWindow* win = node->GetData();
196 if (pp) win->Destroy();
205 wxSize panel_size = GetParent()->GetClientSize();
208 for (
auto notification : notifications) {
209 size_t this_hash = notification->GetStringHash();
210 int repeat_count = 0;
211 for (
auto hash_test : notifications) {
212 if (hash_test->GetStringHash() == this_hash) {
219 for (
auto tpanel : panels) {
220 auto note = tpanel->GetNotification();
222 if ((note->GetStringHash() == this_hash) && (repeat_count > 1)) {
230 notification, repeat_count);
231 panels.push_back(panel);
234 for (
auto panel : panels) {
235 AddNotificationPanel(panel);
239 GetSizer()->FitInside(
this);
248 GetSizer()->Add(_panel, 0, wxEXPAND);
256EVT_CLOSE(NotificationsList::OnClose)
263 long mstyle = wxRESIZE_BORDER | wxCAPTION | wxCLOSE_BOX | wxFRAME_NO_TASKBAR;
265 mstyle |= wxSTAY_ON_TOP;
268 wxDialog::Create(parent, wxID_ANY, _(
"OpenCPN Notifications"),
269 wxDefaultPosition, wxDefaultSize, mstyle);
271 wxBoxSizer* topsizer =
new wxBoxSizer(wxVERTICAL);
275 int group_item_spacing = 0;
276 int interGroupSpace = border_size * 2;
278 auto acksizer =
new wxBoxSizer(wxHORIZONTAL);
279 topsizer->Add(acksizer, 0, wxEXPAND);
282 acksizer->AddStretchSpacer(1);
284 m_ackall_button =
new wxButton(
this, wxID_ANY, _(
"Acknowledge All"));
285 acksizer->Add(m_ackall_button, 0, wxALIGN_CENTER_VERTICAL | wxALL, 2);
286 m_ackall_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
287 &NotificationsList::OnAckAllButton,
this);
288 acksizer->AddSpacer(10);
291 topsizer->Add(0, interGroupSpace);
294 this, wxID_ANY, wxDefaultPosition, wxSize(-1, 300));
295 topsizer->Add(m_notifications_list_panel, 0, wxALL | wxEXPAND, border_size);
299void NotificationsList::SetColorScheme() { DimeControl(
this); }
301void NotificationsList::ReloadNotificationList() {
302 m_notifications_list_panel->ReloadNotificationPanels();
303 if (!m_notifications_list_panel->GetPanels().size()) {
305 GetParent()->Refresh();
309void NotificationsList::OnAckAllButton(wxCommandEvent& event) {
311 noteman.AcknowledgeAllNotifications();
314void NotificationsList::OnClose(wxCloseEvent& event) { Hide(); }
321extern bool g_bSatValid;
322extern int g_SatsInView;
323extern bool g_bopengl;
324extern bool g_btenhertz;
327#define GL_RGBA8 0x8058
330NotificationButton::NotificationButton(
ChartCanvas* parent) {
334 _img_gpsRed = style->GetIcon(_T(
"gpsRed"));
336 m_pStatBoxToolStaticBmp = NULL;
338 m_rect = wxRect(style->GetCompassXOffset(), style->GetCompassYOffset(),
339 _img_gpsRed.GetWidth() + style->GetCompassLeftMargin() * 2 +
340 style->GetToolSeparation(),
341 _img_gpsRed.GetHeight() + style->GetCompassTopMargin() +
342 style->GetCompassBottomMargin());
350 m_cs = GLOBAL_COLOR_SCHEME_RGB;
351 m_NoteIconName =
"notification-info-2";
354NotificationButton::~NotificationButton() {
357 glDeleteTextures(1, &m_texobj);
362 delete m_pStatBoxToolStaticBmp;
365void NotificationButton::SetIconSeverity(NotificationSeverity _severity) {
367 if (_severity == NotificationSeverity::kInformational) {
368 icon_name =
"notification-info-2";
369 }
else if (_severity == NotificationSeverity::kWarning) {
370 icon_name =
"notification-warning-2";
372 icon_name =
"notification-critical-2";
375 SetIconName(icon_name);
378void NotificationButton::Paint(
ocpnDC& dc) {
379 if (m_shown && m_StatBmp.IsOk()) {
380#if defined(ocpnUSE_GLES) || defined(ocpnUSE_GL)
381 if (g_bopengl && !m_texobj) {
384 glGenTextures(1, &m_texobj);
388 if (g_bopengl && m_texobj ) {
389 glBindTexture(GL_TEXTURE_2D, m_texobj);
390 glEnable(GL_TEXTURE_2D);
392#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
399 uv[2] = (float)m_image_width / m_tex_w;
401 uv[4] = (float)m_image_width / m_tex_w;
402 uv[5] = (float)m_image_height / m_tex_h;
404 uv[7] = (float)m_image_height / m_tex_h;
407 coords[0] = m_rect.x;
408 coords[1] = m_rect.y;
409 coords[2] = m_rect.x + m_rect.width;
410 coords[3] = m_rect.y;
411 coords[4] = m_rect.x + m_rect.width;
412 coords[5] = m_rect.y + m_rect.height;
413 coords[6] = m_rect.x;
414 coords[7] = m_rect.y + m_rect.height;
416 m_parent->GetglCanvas()->RenderTextures(dc, coords, uv, 4,
423 glVertex2i(m_rect.x, m_rect.y);
424 glTexCoord2f((
float)m_image_width / m_tex_w, 0);
425 glVertex2i(m_rect.x + m_rect.width, m_rect.y);
426 glTexCoord2f((
float)m_image_width / m_tex_w,
427 (
float)m_image_height / m_tex_h);
428 glVertex2i(m_rect.x + m_rect.width, m_rect.y + m_rect.height);
429 glTexCoord2f(0, (
float)m_image_height / m_tex_h);
430 glVertex2i(m_rect.x, m_rect.y + m_rect.height);
435 glDisable(GL_TEXTURE_2D);
441 double scale = m_parent->GetContentScaleFactor();
443 wxImage image = m_StatBmp.ConvertToImage();
444 image.Rescale(image.GetWidth() *
scale, image.GetHeight() *
scale);
446 dc.DrawBitmap(bmp, m_rect.x, m_rect.y,
true);
448 dc.DrawBitmap(m_StatBmp, m_rect.x, m_rect.y,
true);
450 dc.DrawBitmap(m_StatBmp, m_rect.x, m_rect.y,
true);
452 dc.DrawBitmap(m_StatBmp, m_rect.x, m_rect.y,
true);
457 dc.DrawBitmap(m_StatBmp, m_rect.x, m_rect.y,
true);
462void NotificationButton::SetColorScheme(ColorScheme cs) {
468#ifdef wxHAS_DPI_INDEPENDENT_PIXELS
469#if wxCHECK_VERSION(3, 1, 6)
470 wxRect logicalRect = wxRect(m_parent->FromPhys(m_rect.GetPosition()),
471 m_parent->FromPhys(m_rect.GetSize()));
473 double scaleFactor = m_parent->GetContentScaleFactor();
475 wxPoint(m_rect.GetX() / scaleFactor, m_rect.GetY() / scaleFactor),
476 wxSize(m_rect.GetWidth() / scaleFactor,
477 m_rect.GetHeight() / scaleFactor));
481 wxRect logicalRect = m_rect;
486bool NotificationButton::UpdateStatus(
bool bnew) {
488 if (bnew) m_lastNoteIconName.Clear();
489 if (m_lastNoteIconName != m_NoteIconName) {
495 if (g_bopengl && m_texobj) CreateTexture();
500void NotificationButton::SetScaleFactor(
float factor) {
510 int orient = style->GetOrientation();
511 style->SetOrientation(wxTB_HORIZONTAL);
512 if (style->HasBackground()) {
513 noteBg = style->GetNormalBG();
514 style->DrawToolbarLineEnd(noteBg);
515 noteBg = style->SetBitmapBrightness(noteBg, m_cs);
518 if (fabs(m_scale - 1.0) > 0.1) {
525 int width = noteBg.GetWidth() + style->GetCompassLeftMargin();
526 if (!style->marginsInvisible)
527 width += style->GetCompassLeftMargin() + style->GetToolSeparation();
529 m_rect = wxRect(style->GetCompassXOffset(), style->GetCompassYOffset(), width,
530 noteBg.GetHeight() + style->GetCompassTopMargin() +
531 style->GetCompassBottomMargin());
534void NotificationButton::CreateBmp(
bool newColorScheme) {
542 static wxBitmap noteBg;
543 static wxSize toolsize;
544 static int topmargin, leftmargin, radius;
546 if (!noteBg.IsOk() || newColorScheme) {
547 int orient = style->GetOrientation();
548 style->SetOrientation(wxTB_HORIZONTAL);
549 if (style->HasBackground()) {
550 noteBg = style->GetNormalBG();
551 style->DrawToolbarLineEnd(noteBg);
552 noteBg = style->SetBitmapBrightness(noteBg, m_cs);
555 if (fabs(m_scale - 1.0) > 0.1) {
556 wxImage bg_img = noteBg.ConvertToImage();
557 bg_img.Rescale(noteBg.GetWidth() * m_scale, noteBg.GetHeight() * m_scale,
558 wxIMAGE_QUALITY_NORMAL);
559 noteBg = wxBitmap(bg_img);
562 leftmargin = style->GetCompassLeftMargin();
563 topmargin = style->GetCompassTopMargin();
564 radius = style->GetCompassCornerRadius();
566 if (orient == wxTB_VERTICAL) style->SetOrientation(wxTB_VERTICAL);
570 int height = noteBg.GetHeight() + topmargin + style->GetCompassBottomMargin();
575 m_StatBmp.Create(width, height);
577 m_rect.width = m_StatBmp.GetWidth();
578 m_rect.height = m_StatBmp.GetHeight();
580 m_MaskBmp = wxBitmap(m_StatBmp.GetWidth(), m_StatBmp.GetHeight());
581 if (style->marginsInvisible) {
582 wxMemoryDC sdc(m_MaskBmp);
583 sdc.SetBackground(*wxWHITE_BRUSH);
585 sdc.SetBrush(*wxBLACK_BRUSH);
586 sdc.SetPen(*wxBLACK_PEN);
587 wxSize maskSize = wxSize(m_MaskBmp.GetWidth() - leftmargin,
588 m_MaskBmp.GetHeight() - (2 * topmargin));
589 sdc.DrawRoundedRectangle(wxPoint(leftmargin, topmargin), maskSize, radius);
590 sdc.SelectObject(wxNullBitmap);
592 wxMemoryDC sdc(m_MaskBmp);
593 sdc.SetBackground(*wxWHITE_BRUSH);
595 sdc.SetBrush(*wxBLACK_BRUSH);
596 sdc.SetPen(*wxBLACK_PEN);
597 sdc.DrawRoundedRectangle(0, 0, m_MaskBmp.GetWidth(), m_MaskBmp.GetHeight(),
599 sdc.SelectObject(wxNullBitmap);
601#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
602 m_StatBmp.SetMask(
new wxMask(m_MaskBmp, *wxWHITE));
606 mdc.SelectObject(m_StatBmp);
607 mdc.SetBackground(wxBrush(GetGlobalColor(_T(
"COMP1")), wxBRUSHSTYLE_SOLID));
610 mdc.SetPen(wxPen(GetGlobalColor(_T(
"UITX1")), 1));
611 mdc.SetBrush(wxBrush(GetGlobalColor(_T(
"UITX1")), wxBRUSHSTYLE_TRANSPARENT));
613 if (!style->marginsInvisible)
614 mdc.DrawRoundedRectangle(0, 0, m_StatBmp.GetWidth(), m_StatBmp.GetHeight(),
616 wxPoint offset(leftmargin, topmargin);
619 int twidth = style->GetToolSize().x * m_scale;
620 int theight = style->GetToolSize().y * m_scale;
621 int swidth = wxMax(twidth, theight);
622 int sheight = wxMin(twidth, theight);
624 swidth = swidth * 45 / 50;
625 sheight = sheight * 45 / 50;
627 offset.x = ((m_StatBmp.GetWidth() - swidth) / 2);
628 offset.y = ((m_StatBmp.GetHeight() - sheight) / 2);
630 wxFileName icon_path;
631 wxString file_name = m_NoteIconName +
".svg";
632 icon_path = wxFileName(g_Platform->GetSharedDataDir(), file_name);
633 icon_path.AppendDir(
"uidata");
634 icon_path.AppendDir(
"MUI_flat");
635 wxBitmap gicon = LoadSVG(icon_path.GetFullPath(), swidth, sheight);
640 mdc.DrawBitmap(iconBm, offset);
641 mdc.SelectObject(wxNullBitmap);
643 m_lastNoteIconName = m_NoteIconName;
646void NotificationButton::CreateTexture() {
647#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
651 wxImage image = m_StatBmp.ConvertToImage();
652 unsigned char* imgdata = image.GetData();
653 unsigned char* imgalpha = image.GetAlpha();
654 m_tex_w = image.GetWidth();
655 m_tex_h = image.GetHeight();
656 m_image_width = m_tex_w;
657 m_image_height = m_tex_h;
660 int width_pot = m_tex_w;
661 int height_pot = m_tex_h;
663 int xp = image.GetWidth();
664 if (((xp != 0) && !(xp & (xp - 1))))
675 xp = image.GetHeight();
676 if (((xp != 0) && !(xp & (xp - 1))))
688 m_tex_h = height_pot;
690 GLuint format = GL_RGBA;
691 GLuint internalformat = GL_RGBA8;
695 unsigned char* teximage =
696 (
unsigned char*)malloc(stride * m_tex_w * m_tex_h);
698 for (
int i = 0; i < m_image_height; i++) {
699 for (
int j = 0; j < m_image_width; j++) {
700 int s = (i * 3 * m_image_width) + (j * 3);
701 int d = (i * stride * m_tex_w) + (j * stride);
703 teximage[d + 0] = imgdata[s + 0];
704 teximage[d + 1] = imgdata[s + 1];
705 teximage[d + 2] = imgdata[s + 2];
706 teximage[d + 3] = 255;
710 glBindTexture(GL_TEXTURE_2D, m_texobj);
712 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
713 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
714 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
716 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
718 glTexImage2D(GL_TEXTURE_2D, 0, internalformat, m_tex_w, m_tex_h, 0,
719 format, GL_UNSIGNED_BYTE, teximage);
722 glBindTexture(GL_TEXTURE_2D, 0);
729void NotificationButton::UpdateTexture() {
730#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
734 wxImage image = m_StatBmp.ConvertToImage();
735 unsigned char* imgdata = image.GetData();
736 unsigned char* imgalpha = image.GetAlpha();
737 m_tex_w = image.GetWidth();
738 m_tex_h = image.GetHeight();
739 m_image_width = m_tex_w;
740 m_image_height = m_tex_h;
743 int width_pot = m_tex_w;
744 int height_pot = m_tex_h;
746 int xp = image.GetWidth();
747 if (((xp != 0) && !(xp & (xp - 1))))
758 xp = image.GetHeight();
759 if (((xp != 0) && !(xp & (xp - 1))))
771 m_tex_h = height_pot;
773 GLuint format = GL_RGBA;
774 GLuint internalformat = GL_RGBA8;
778 unsigned char* teximage =
779 (
unsigned char*)malloc(stride * m_tex_w * m_tex_h);
781 for (
int i = 0; i < m_image_height; i++) {
782 for (
int j = 0; j < m_image_width; j++) {
783 int s = (i * 3 * m_image_width) + (j * 3);
784 int d = (i * stride * m_tex_w) + (j * stride);
786 teximage[d + 0] = imgdata[s + 0];
787 teximage[d + 1] = imgdata[s + 1];
788 teximage[d + 2] = imgdata[s + 2];
789 teximage[d + 3] = 255;
793 glBindTexture(GL_TEXTURE_2D, m_texobj);
794 glEnable(GL_TEXTURE_2D);
796 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_tex_w, m_tex_h, format,
797 GL_UNSIGNED_BYTE, teximage);
800 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.