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"
51 Wrap(win, text, widthMax);
53 wxString
const& GetWrapped()
const {
return m_wrapped; }
54 int const GetLineCount()
const {
return m_lineCount; }
55 wxArrayString GetLineArray() {
return m_array; }
58 virtual void OnOutputLine(
const wxString& line) {
62 virtual void OnNewLine() {
70 wxArrayString m_array;
75EVT_PAINT(NotificationPanel::OnPaint)
79 wxPanel* parent, wxWindowID
id, const wxPoint& pos, const wxSize& size,
80 std::shared_ptr<
Notification> _notification,
int _repeat_count)
81 : wxPanel(parent,
id, pos, size, wxBORDER_NONE),
82 repeat_count(_repeat_count) {
83 notification = _notification;
85 wxBoxSizer* topSizer =
new wxBoxSizer(wxVERTICAL);
88 wxBoxSizer* itemBoxSizer01 =
new wxBoxSizer(wxHORIZONTAL);
89 topSizer->Add(itemBoxSizer01, 0, wxEXPAND);
91 double iconSize = GetCharWidth() * 3;
93 int icon_scale = iconSize * dpi_mult;
95 wxImage notification_icon;
99 if (notification->GetSeverity() == NotificationSeverity::kInformational) {
101 wxFileName(g_Platform->GetSharedDataDir(),
"notification-info-1.svg");
102 }
else if (notification->GetSeverity() == NotificationSeverity::kWarning) {
103 path = wxFileName(g_Platform->GetSharedDataDir(),
104 "notification-warning-1.svg");
106 path = wxFileName(g_Platform->GetSharedDataDir(),
107 "notification-critical-1.svg");
109 path.AppendDir(
"uidata");
110 path.AppendDir(
"MUI_flat");
111 bitmap = LoadSVG(path.GetFullPath(), icon_scale, icon_scale);
112 m_itemStaticBitmap =
new wxStaticBitmap(
this, wxID_ANY, bitmap);
114 itemBoxSizer01->Add(m_itemStaticBitmap, 0, wxEXPAND | wxALL, 10);
117 wxString rp = _(
"Repeat:");
118 wxString sCount = rp + wxString::Format(
"\n %d", repeat_count);
119 auto counttextbox =
new wxStaticText(
this, wxID_ANY, sCount);
120 itemBoxSizer01->Add(counttextbox, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
123 wxDateTime act_time = wxDateTime(notification->GetActivateTime());
124 wxString stime = wxString::Format(
125 "%s", ocpn::toUsrDateTimeFormat(
127 "$short_date\n$24_hour_minutes_seconds")));
128 auto timetextbox =
new wxStaticText(
this, wxID_ANY, stime);
129 itemBoxSizer01->Add(timetextbox, 0,
130 wxALL | wxALIGN_CENTER_VERTICAL, 5);
133 GetSize().x * 5 / 10);
135 auto textbox =
new wxStaticText(
this, wxID_ANY, wrapper.GetWrapped());
136 itemBoxSizer01->Add(textbox, 0, wxALIGN_CENTER_VERTICAL | wxALL, 10);
138 itemBoxSizer01->AddStretchSpacer(1);
141 m_ack_button =
new wxButton(
this, wxID_ANY,
"ACK");
142 itemBoxSizer01->Add(m_ack_button, 0, wxALIGN_CENTER_VERTICAL | wxALL, 10);
143 m_ack_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
144 &NotificationPanel::OnAckButton,
this);
150NotificationPanel::~NotificationPanel() {}
152void NotificationPanel::OnAckButton(wxCommandEvent& event) {
157void NotificationPanel::OnPaint(wxPaintEvent& event) {
161 wxColour color = GetDialogColor(DLG_UNSELECTED_BACKGROUND);
162 wxColour border = GetDialogColor(DLG_UNSELECTED_ACCENT);
164 wxBrush b(color, wxBRUSHSTYLE_SOLID);
166 dc.SetPen(wxPen(border, penWidth));
168 dc.DrawRoundedRectangle(5, 5, GetSize().x - 10, GetSize().y - 10, 5);
171NotificationListPanel::NotificationListPanel(wxWindow* parent, wxWindowID
id,
174 : wxScrolledWindow(parent, id, pos, size, wxTAB_TRAVERSAL | wxVSCROLL) {
175 SetSizer(
new wxBoxSizer(wxVERTICAL));
177 ReloadNotificationPanels();
180NotificationListPanel::~NotificationListPanel() {}
182void NotificationListPanel::ReloadNotificationPanels() {
183 wxWindowList kids = GetChildren();
184 for (
unsigned int i = 0; i < kids.GetCount(); i++) {
185 wxWindowListNode* node = kids.Item(i);
186 wxWindow* win = node->GetData();
188 if (pp) win->Destroy();
197 wxSize panel_size = GetParent()->GetClientSize();
200 for (
auto notification : notifications) {
201 size_t this_hash = notification->GetStringHash();
202 int repeat_count = 0;
203 for (
auto hash_test : notifications) {
204 if (hash_test->GetStringHash() == this_hash) {
211 for (
auto tpanel : panels) {
212 auto note = tpanel->GetNotification();
214 if ((note->GetStringHash() == this_hash) && (repeat_count > 1)) {
222 notification, repeat_count);
223 panels.push_back(panel);
226 for (
auto panel : panels) {
227 AddNotificationPanel(panel);
230 GetSizer()->FitInside(
this);
239 GetSizer()->Add(_panel, 0, wxEXPAND);
247EVT_CLOSE(NotificationsList::OnClose)
254 long mstyle = wxRESIZE_BORDER | wxCAPTION | wxCLOSE_BOX | wxFRAME_NO_TASKBAR;
256 mstyle |= wxSTAY_ON_TOP;
259 wxDialog::Create(parent, wxID_ANY, _(
"OpenCPN Notifications"),
260 wxDefaultPosition, wxDefaultSize, mstyle);
262 wxBoxSizer* topsizer =
new wxBoxSizer(wxVERTICAL);
266 int group_item_spacing = 0;
267 int interGroupSpace = border_size * 2;
269 auto acksizer =
new wxBoxSizer(wxHORIZONTAL);
270 topsizer->Add(acksizer, 0, wxEXPAND);
273 acksizer->AddStretchSpacer(1);
275 m_ackall_button =
new wxButton(
this, wxID_ANY,
"ACK All");
276 acksizer->Add(m_ackall_button, 0, wxALIGN_CENTER_VERTICAL | wxALL, 2);
277 m_ackall_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
278 &NotificationsList::OnAckAllButton,
this);
279 acksizer->AddSpacer(10);
282 topsizer->Add(0, interGroupSpace);
285 this, wxID_ANY, wxDefaultPosition, wxSize(-1, 300));
286 topsizer->Add(m_notifications_list_panel, 0, wxALL | wxEXPAND, border_size);
293void NotificationsList::ReloadNotificationList() {
294 m_notifications_list_panel->ReloadNotificationPanels();
295 if (!m_notifications_list_panel->GetPanels().size()) {
297 GetParent()->Refresh();
301void NotificationsList::OnAckAllButton(wxCommandEvent& event) {
303 noteman.AcknowledgeAllNotifications();
306void NotificationsList::OnClose(wxCloseEvent& event) { Hide(); }
313extern bool g_bSatValid;
314extern int g_SatsInView;
315extern bool g_bopengl;
316extern bool g_btenhertz;
319#define GL_RGBA8 0x8058
322NotificationButton::NotificationButton(
ChartCanvas* parent) {
326 _img_gpsRed = style->GetIcon(_T(
"gpsRed"));
328 m_pStatBoxToolStaticBmp = NULL;
330 m_rect = wxRect(style->GetCompassXOffset(), style->GetCompassYOffset(),
331 _img_gpsRed.GetWidth() + style->GetCompassLeftMargin() * 2 +
332 style->GetToolSeparation(),
333 _img_gpsRed.GetHeight() + style->GetCompassTopMargin() +
334 style->GetCompassBottomMargin());
342 m_cs = GLOBAL_COLOR_SCHEME_RGB;
343 m_NoteIconName =
"notification-info-1";
346NotificationButton::~NotificationButton() {
349 glDeleteTextures(1, &m_texobj);
354 delete m_pStatBoxToolStaticBmp;
357void NotificationButton::SetIconSeverity(NotificationSeverity _severity) {
359 if (_severity == NotificationSeverity::kInformational) {
360 icon_name =
"notification-info-1";
361 }
else if (_severity == NotificationSeverity::kWarning) {
362 icon_name =
"notification-warning-1";
364 icon_name =
"notification-critical-1";
367 SetIconName(icon_name);
370void NotificationButton::Paint(
ocpnDC& dc) {
371 if (m_shown && m_StatBmp.IsOk()) {
372#if defined(ocpnUSE_GLES) || defined(ocpnUSE_GL)
373 if (g_bopengl && !m_texobj) {
376 glGenTextures(1, &m_texobj);
380 if (g_bopengl && m_texobj ) {
381 glBindTexture(GL_TEXTURE_2D, m_texobj);
382 glEnable(GL_TEXTURE_2D);
384#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
391 uv[2] = (float)m_image_width / m_tex_w;
393 uv[4] = (float)m_image_width / m_tex_w;
394 uv[5] = (float)m_image_height / m_tex_h;
396 uv[7] = (float)m_image_height / m_tex_h;
399 coords[0] = m_rect.x;
400 coords[1] = m_rect.y;
401 coords[2] = m_rect.x + m_rect.width;
402 coords[3] = m_rect.y;
403 coords[4] = m_rect.x + m_rect.width;
404 coords[5] = m_rect.y + m_rect.height;
405 coords[6] = m_rect.x;
406 coords[7] = m_rect.y + m_rect.height;
408 m_parent->GetglCanvas()->RenderTextures(dc, coords, uv, 4,
415 glVertex2i(m_rect.x, m_rect.y);
416 glTexCoord2f((
float)m_image_width / m_tex_w, 0);
417 glVertex2i(m_rect.x + m_rect.width, m_rect.y);
418 glTexCoord2f((
float)m_image_width / m_tex_w,
419 (
float)m_image_height / m_tex_h);
420 glVertex2i(m_rect.x + m_rect.width, m_rect.y + m_rect.height);
421 glTexCoord2f(0, (
float)m_image_height / m_tex_h);
422 glVertex2i(m_rect.x, m_rect.y + m_rect.height);
427 glDisable(GL_TEXTURE_2D);
433 double scale = m_parent->GetContentScaleFactor();
435 wxImage image = m_StatBmp.ConvertToImage();
436 image.Rescale(image.GetWidth() *
scale, image.GetHeight() *
scale);
438 dc.DrawBitmap(bmp, m_rect.x, m_rect.y,
true);
440 dc.DrawBitmap(m_StatBmp, m_rect.x, m_rect.y,
true);
442 dc.DrawBitmap(m_StatBmp, m_rect.x, m_rect.y,
true);
444 dc.DrawBitmap(m_StatBmp, m_rect.x, m_rect.y,
true);
449 dc.DrawBitmap(m_StatBmp, m_rect.x, m_rect.y,
true);
454void NotificationButton::SetColorScheme(ColorScheme cs) {
460#ifdef wxHAS_DPI_INDEPENDENT_PIXELS
461#if wxCHECK_VERSION(3, 1, 6)
462 wxRect logicalRect = wxRect(m_parent->FromPhys(m_rect.GetPosition()),
463 m_parent->FromPhys(m_rect.GetSize()));
465 double scaleFactor = m_parent->GetContentScaleFactor();
467 wxPoint(m_rect.GetX() / scaleFactor, m_rect.GetY() / scaleFactor),
468 wxSize(m_rect.GetWidth() / scaleFactor,
469 m_rect.GetHeight() / scaleFactor));
473 wxRect logicalRect = m_rect;
478bool NotificationButton::UpdateStatus(
bool bnew) {
480 if (bnew) m_lastNoteIconName.Clear();
481 if (m_lastNoteIconName != m_NoteIconName) {
487 if (g_bopengl && m_texobj) CreateTexture();
492void NotificationButton::SetScaleFactor(
float factor) {
502 int orient = style->GetOrientation();
503 style->SetOrientation(wxTB_HORIZONTAL);
504 if (style->HasBackground()) {
505 noteBg = style->GetNormalBG();
506 style->DrawToolbarLineEnd(noteBg);
507 noteBg = style->SetBitmapBrightness(noteBg, m_cs);
510 if (fabs(m_scale - 1.0) > 0.1) {
517 int width = noteBg.GetWidth() + style->GetCompassLeftMargin();
518 if (!style->marginsInvisible)
519 width += style->GetCompassLeftMargin() + style->GetToolSeparation();
521 m_rect = wxRect(style->GetCompassXOffset(), style->GetCompassYOffset(), width,
522 noteBg.GetHeight() + style->GetCompassTopMargin() +
523 style->GetCompassBottomMargin());
526void NotificationButton::CreateBmp(
bool newColorScheme) {
534 static wxBitmap noteBg;
535 static wxSize toolsize;
536 static int topmargin, leftmargin, radius;
538 if (!noteBg.IsOk() || newColorScheme) {
539 int orient = style->GetOrientation();
540 style->SetOrientation(wxTB_HORIZONTAL);
541 if (style->HasBackground()) {
542 noteBg = style->GetNormalBG();
543 style->DrawToolbarLineEnd(noteBg);
544 noteBg = style->SetBitmapBrightness(noteBg, m_cs);
547 if (fabs(m_scale - 1.0) > 0.1) {
548 wxImage bg_img = noteBg.ConvertToImage();
549 bg_img.Rescale(noteBg.GetWidth() * m_scale, noteBg.GetHeight() * m_scale,
550 wxIMAGE_QUALITY_NORMAL);
551 noteBg = wxBitmap(bg_img);
554 leftmargin = style->GetCompassLeftMargin();
555 topmargin = style->GetCompassTopMargin();
556 radius = style->GetCompassCornerRadius();
558 if (orient == wxTB_VERTICAL) style->SetOrientation(wxTB_VERTICAL);
562 int height = noteBg.GetHeight() + topmargin + style->GetCompassBottomMargin();
567 m_StatBmp.Create(width, height);
569 m_rect.width = m_StatBmp.GetWidth();
570 m_rect.height = m_StatBmp.GetHeight();
572 m_MaskBmp = wxBitmap(m_StatBmp.GetWidth(), m_StatBmp.GetHeight());
573 if (style->marginsInvisible) {
574 wxMemoryDC sdc(m_MaskBmp);
575 sdc.SetBackground(*wxWHITE_BRUSH);
577 sdc.SetBrush(*wxBLACK_BRUSH);
578 sdc.SetPen(*wxBLACK_PEN);
579 wxSize maskSize = wxSize(m_MaskBmp.GetWidth() - leftmargin,
580 m_MaskBmp.GetHeight() - (2 * topmargin));
581 sdc.DrawRoundedRectangle(wxPoint(leftmargin, topmargin), maskSize, radius);
582 sdc.SelectObject(wxNullBitmap);
584 wxMemoryDC sdc(m_MaskBmp);
585 sdc.SetBackground(*wxWHITE_BRUSH);
587 sdc.SetBrush(*wxBLACK_BRUSH);
588 sdc.SetPen(*wxBLACK_PEN);
589 sdc.DrawRoundedRectangle(0, 0, m_MaskBmp.GetWidth(), m_MaskBmp.GetHeight(),
591 sdc.SelectObject(wxNullBitmap);
593#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
594 m_StatBmp.SetMask(
new wxMask(m_MaskBmp, *wxWHITE));
598 mdc.SelectObject(m_StatBmp);
599 mdc.SetBackground(wxBrush(GetGlobalColor(_T(
"COMP1")), wxBRUSHSTYLE_SOLID));
602 mdc.SetPen(wxPen(GetGlobalColor(_T(
"UITX1")), 1));
603 mdc.SetBrush(wxBrush(GetGlobalColor(_T(
"UITX1")), wxBRUSHSTYLE_TRANSPARENT));
605 if (!style->marginsInvisible)
606 mdc.DrawRoundedRectangle(0, 0, m_StatBmp.GetWidth(), m_StatBmp.GetHeight(),
608 wxPoint offset(leftmargin, topmargin);
611 int twidth = style->GetToolSize().x * m_scale;
612 int theight = style->GetToolSize().y * m_scale;
613 int swidth = wxMax(twidth, theight);
614 int sheight = wxMin(twidth, theight);
616 swidth = swidth * 45 / 50;
617 sheight = sheight * 45 / 50;
619 offset.x = ((m_StatBmp.GetWidth() - swidth) / 2);
620 offset.y = ((m_StatBmp.GetHeight() - sheight) / 2);
622 wxFileName icon_path;
623 wxString file_name = m_NoteIconName +
".svg";
624 icon_path = wxFileName(g_Platform->GetSharedDataDir(), file_name);
625 icon_path.AppendDir(
"uidata");
626 icon_path.AppendDir(
"MUI_flat");
627 wxBitmap gicon = LoadSVG(icon_path.GetFullPath(), swidth, sheight);
632 mdc.DrawBitmap(iconBm, offset);
633 mdc.SelectObject(wxNullBitmap);
635 m_lastNoteIconName = m_NoteIconName;
638void NotificationButton::CreateTexture() {
639#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
643 wxImage image = m_StatBmp.ConvertToImage();
644 unsigned char* imgdata = image.GetData();
645 unsigned char* imgalpha = image.GetAlpha();
646 m_tex_w = image.GetWidth();
647 m_tex_h = image.GetHeight();
648 m_image_width = m_tex_w;
649 m_image_height = m_tex_h;
652 int width_pot = m_tex_w;
653 int height_pot = m_tex_h;
655 int xp = image.GetWidth();
656 if (((xp != 0) && !(xp & (xp - 1))))
667 xp = image.GetHeight();
668 if (((xp != 0) && !(xp & (xp - 1))))
680 m_tex_h = height_pot;
682 GLuint format = GL_RGBA;
683 GLuint internalformat = GL_RGBA8;
687 unsigned char* teximage =
688 (
unsigned char*)malloc(stride * m_tex_w * m_tex_h);
690 for (
int i = 0; i < m_image_height; i++) {
691 for (
int j = 0; j < m_image_width; j++) {
692 int s = (i * 3 * m_image_width) + (j * 3);
693 int d = (i * stride * m_tex_w) + (j * stride);
695 teximage[d + 0] = imgdata[s + 0];
696 teximage[d + 1] = imgdata[s + 1];
697 teximage[d + 2] = imgdata[s + 2];
698 teximage[d + 3] = 255;
702 glBindTexture(GL_TEXTURE_2D, m_texobj);
704 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
705 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
706 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
708 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
710 glTexImage2D(GL_TEXTURE_2D, 0, internalformat, m_tex_w, m_tex_h, 0,
711 format, GL_UNSIGNED_BYTE, teximage);
714 glBindTexture(GL_TEXTURE_2D, 0);
721void NotificationButton::UpdateTexture() {
722#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
726 wxImage image = m_StatBmp.ConvertToImage();
727 unsigned char* imgdata = image.GetData();
728 unsigned char* imgalpha = image.GetAlpha();
729 m_tex_w = image.GetWidth();
730 m_tex_h = image.GetHeight();
731 m_image_width = m_tex_w;
732 m_image_height = m_tex_h;
735 int width_pot = m_tex_w;
736 int height_pot = m_tex_h;
738 int xp = image.GetWidth();
739 if (((xp != 0) && !(xp & (xp - 1))))
750 xp = image.GetHeight();
751 if (((xp != 0) && !(xp & (xp - 1))))
763 m_tex_h = height_pot;
765 GLuint format = GL_RGBA;
766 GLuint internalformat = GL_RGBA8;
770 unsigned char* teximage =
771 (
unsigned char*)malloc(stride * m_tex_w * m_tex_h);
773 for (
int i = 0; i < m_image_height; i++) {
774 for (
int j = 0; j < m_image_width; j++) {
775 int s = (i * 3 * m_image_width) + (j * 3);
776 int d = (i * stride * m_tex_w) + (j * stride);
778 teximage[d + 0] = imgdata[s + 0];
779 teximage[d + 1] = imgdata[s + 1];
780 teximage[d + 2] = imgdata[s + 2];
781 teximage[d + 3] = 255;
785 glBindTexture(GL_TEXTURE_2D, m_texobj);
786 glEnable(GL_TEXTURE_2D);
788 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_tex_w, m_tex_h, format,
789 GL_UNSIGNED_BYTE, teximage);
792 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.