OpenCPN Partial API docs
Loading...
Searching...
No Matches
notification_manager_gui.cpp
Go to the documentation of this file.
1/**************************************************************************
2 * Copyright (C) 2025 by David S. Register *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, see <https://www.gnu.org/licenses/>. *
16 **************************************************************************/
17
24#include <cmath>
25#include <memory>
26#include <vector>
27
28#include <wx/arrstr.h>
29#include <wx/colour.h>
30#include <wx/datetime.h>
31#include <wx/filename.h>
32#include <wx/image.h>
33#include <wx/sizer.h>
34#include <wx/statline.h>
35#include <wx/stattext.h>
36#include <wx/string.h>
37#include <wx/textwrapper.h>
38
40
41#include "model/datetime.h"
42#include "model/notification.h"
44#include "model/svg_utils.h"
45
46#include "observable/global_var.h"
47
48#include "chcanv.h"
49#include "color_handler.h"
50#include "gl_chart_canvas.h"
51#include "gui_lib.h"
52#include "navutil.h"
53#include "ocpn_platform.h"
54#include "styles.h"
55
56// This window style value has slipped from the headers, but is useful
57// on MSW...
58#ifdef __WXMSW__
59#define wxFULL_PAINT_ON_RESIZE 0x00010000
60#else
61#define wxFULL_PAINT_ON_RESIZE 0
62#endif
63
64class PanelHardBreakWrapper : public wxTextWrapper {
65public:
66 PanelHardBreakWrapper(wxWindow* win, const wxString& text, int widthMax) {
67 m_lineCount = 0;
68 Wrap(win, text, widthMax);
69 }
70 wxString const& GetWrapped() const { return m_wrapped; }
71 int const GetLineCount() const { return m_lineCount; }
72 wxArrayString GetLineArray() { return m_array; }
73
74protected:
75 virtual void OnOutputLine(const wxString& line) {
76 m_wrapped += line;
77 m_array.Add(line);
78 }
79 virtual void OnNewLine() {
80 m_wrapped += '\n';
81 m_lineCount++;
82 }
83
84private:
85 wxString m_wrapped;
86 int m_lineCount;
87 wxArrayString m_array;
88};
89
90#
91BEGIN_EVENT_TABLE(NotificationPanel, wxPanel)
92EVT_PAINT(NotificationPanel::OnPaint)
93END_EVENT_TABLE()
94
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;
101
102 wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL);
103 SetSizer(topSizer);
104
105 wxBoxSizer* itemBoxSizer01 = new wxBoxSizer(wxHORIZONTAL);
106 topSizer->Add(itemBoxSizer01, 1, wxEXPAND);
107
108 double iconSize = GetCharWidth() * 3;
109 double dpi_mult = g_Platform->GetDisplayDIPMult(this);
110 int icon_scale = iconSize * dpi_mult;
111
112 wxImage notification_icon;
113 ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
114 wxBitmap bitmap;
115 wxFileName path;
116 if (notification->GetSeverity() == NotificationSeverity::kInformational) {
117 path =
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");
122 } else {
123 path = wxFileName(g_Platform->GetSharedDataDir(),
124 "notification-critical-2.svg");
125 }
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);
130
131 itemBoxSizer01->Add(m_itemStaticBitmap, 0, wxEXPAND | wxALL, 10);
132
133 // Repeat Count
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);
138
139 // Time
140 wxDateTime act_time = wxDateTime(notification->GetActivateTime());
141 wxString stime = wxString::Format(
142 "%s",
143 ocpn::toUsrDateTimeFormat(act_time, DateTimeFormatOptions()
144 .SetFormatString("$short_date")
145 .SetShowTimezone(false)));
146 stime = stime.BeforeFirst(' ');
147 wxString stime1 = wxString::Format(
148 "%s", ocpn::toUsrDateTimeFormat(act_time,
149 DateTimeFormatOptions().SetFormatString(
150 "$24_hour_minutes_seconds")));
151 stime += "\n";
152 stime += stime1;
153
154 auto timetextbox = new wxStaticText(this, wxID_ANY, stime);
155 itemBoxSizer01->Add(timetextbox, 0,
156 /*wxEXPAND|*/ wxALL | wxALIGN_CENTER_VERTICAL, 5);
157
158 PanelHardBreakWrapper wrapper(this, notification->GetMessage(),
159 GetSize().x * 50 / 100);
160
161 auto textbox = new wxStaticText(this, wxID_ANY, wrapper.GetWrapped());
162 itemBoxSizer01->Add(textbox, 0, wxALIGN_CENTER_VERTICAL | wxALL, 10);
163
164 itemBoxSizer01->AddStretchSpacer();
165
166 // Ack button
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);
171
172 DimeControl(m_ack_button);
173}
174
175NotificationPanel::~NotificationPanel() {}
176
177void NotificationPanel::OnAckButton(wxCommandEvent& event) {
178 NotificationManager& noteman = NotificationManager::GetInstance();
179 noteman.AcknowledgeNotification(notification->GetGuid());
180}
181
182void NotificationPanel::OnPaint(wxPaintEvent& event) {
183 wxPaintDC dc(this);
184 wxColor back_color = GetDialogColor(DLG_UNSELECTED_BACKGROUND);
185 wxBrush bg(back_color, wxBRUSHSTYLE_SOLID);
186 dc.SetBackground(bg);
187 dc.Clear();
188
189 int penWidth = 2; // m_penWidthUnselected;
190 wxColour box_color = GetDialogColor(DLG_UNSELECTED_BACKGROUND);
191 wxColour box_border = GetGlobalColor("GREY3");
192
193 wxBrush b(box_color, wxBRUSHSTYLE_SOLID);
194 dc.SetBrush(b);
195 dc.SetPen(wxPen(box_border, penWidth));
196
197 dc.DrawRoundedRectangle(5, 2, GetSize().x - 10, GetSize().y - 4, 5);
198}
199
200NotificationListPanel::NotificationListPanel(wxWindow* parent, wxWindowID id,
201 const wxPoint& pos,
202 const wxSize& size)
203 : wxScrolledWindow(parent, id, pos, size,
204 wxTAB_TRAVERSAL | wxVSCROLL | wxHSCROLL) {
205 SetSizer(new wxBoxSizer(wxVERTICAL));
206 SetScrollRate(5, 5);
207 ShowScrollbars(wxSHOW_SB_NEVER, wxSHOW_SB_DEFAULT);
208 if (g_btouch) {
209 ShowScrollbars(wxSHOW_SB_NEVER, wxSHOW_SB_NEVER);
210 SetScrollRate(1, 1);
211 }
212 ReloadNotificationPanels();
213 DimeControl(this);
214}
215
216NotificationListPanel::~NotificationListPanel() {}
217
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();
223 NotificationPanel* pp = dynamic_cast<NotificationPanel*>(win);
224 if (pp) win->Destroy();
225 }
226 GetSizer()->Clear();
227 Hide();
228 panels.clear();
229
230 NotificationManager& noteman = NotificationManager::GetInstance();
231 auto notifications = noteman.GetNotifications();
232
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);
236
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) {
242 repeat_count++;
243 }
244 }
245
246 // Do not create duplicate panels
247 bool skip = false;
248 for (auto tpanel : panels) {
249 auto note = tpanel->GetNotification();
250
251 if ((note->GetStringHash() == this_hash) && (repeat_count > 1)) {
252 skip = true;
253 }
254 }
255 if (skip) continue;
256
257 NotificationPanel* panel =
258 new NotificationPanel(this, wxID_ANY, wxDefaultPosition, panel_size,
259 notification, repeat_count);
260 panels.push_back(panel);
261 }
262
263 for (auto panel : panels) {
264 AddNotificationPanel(panel);
265 DimeControl(panel);
266 }
267
268 Show();
269 Layout();
270 Refresh(true);
271 Scroll(0, 0);
272}
273
274void NotificationListPanel::AddNotificationPanel(NotificationPanel* _panel) {
275 GetSizer()->Add(_panel, 0); //| wxALL, 10);
276}
277
278//------------------------------------------------------------------------------
279// NotificationsList
280//------------------------------------------------------------------------------
281
282BEGIN_EVENT_TABLE(NotificationsList, wxDialog)
283EVT_CLOSE(NotificationsList::OnClose)
284END_EVENT_TABLE()
285
286NotificationsList::NotificationsList(wxWindow* parent) : wxDialog() {
287 wxFont* qFont = GetOCPNScaledFont(_("Dialog"));
288 SetFont(*qFont);
289
290 long mstyle = wxRESIZE_BORDER | wxCAPTION | wxCLOSE_BOX | wxFRAME_NO_TASKBAR;
291#ifdef __WXOSX__
292 mstyle |= wxSTAY_ON_TOP;
293#endif
294
295 wxDialog::Create(parent, wxID_ANY, _("OpenCPN Notifications"),
296 wxDefaultPosition, wxDefaultSize, mstyle);
297
298 wxBoxSizer* topsizer = new wxBoxSizer(wxVERTICAL);
299 SetSizer(topsizer);
300
301 int border_size = 2;
302 int group_item_spacing = 0;
303 int interGroupSpace = border_size * 2;
304
305 auto acksizer = new wxBoxSizer(wxHORIZONTAL);
306 topsizer->Add(acksizer, 0, wxEXPAND);
307
308 // Ack All button
309 acksizer->AddStretchSpacer(1);
310
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);
316
317 // spacer
318 topsizer->Add(0, interGroupSpace);
319
320 m_notifications_list_panel = new NotificationListPanel(
321 this, wxID_ANY, wxDefaultPosition, wxSize(-1, parent->GetSize().y));
322 topsizer->Add(m_notifications_list_panel, 0, wxALL | wxEXPAND, border_size);
323
324 DimeControl(this);
325}
326void NotificationsList::SetColorScheme() { DimeControl(this); }
327
328void NotificationsList::ReloadNotificationList() {
329 m_notifications_list_panel->ReloadNotificationPanels();
330 if (!m_notifications_list_panel->GetPanels().size()) {
331 Hide();
332 GetParent()->Refresh();
333 }
334}
335
336void NotificationsList::OnAckAllButton(wxCommandEvent& event) {
337 NotificationManager& noteman = NotificationManager::GetInstance();
338 noteman.AcknowledgeAllNotifications();
339}
340
341void NotificationsList::RecalculateSize() {
342 // calculate and set best size and position for Notification list
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();
350 size_y =
351 wxMax(size_y, 8 * GetCharHeight()); // ensure always big enough to see
352
353 wxSize target_size = wxSize(GetCharWidth() * 80, size_y);
354
355 wxPoint targetNLPos = GetParent()->ClientToScreen(
356 wxPoint(parent_size.x / 2, 3 * GetParent()->GetCharHeight()));
357
358 // Adjust the size for smaller devices
359 wxSize display_size = g_Platform->getDisplaySize();
360 if (target_size.x * 2 > display_size.x) {
361 target_size.x =
362 display_size.x * 85 / 100 - (2 * GetParent()->GetCharWidth());
363 targetNLPos.x =
364 GetParent()->ClientToScreen(wxPoint(display_size.x * 15 / 100, 0)).x;
365 }
366
367 SetSize(target_size);
368 Move(targetNLPos);
369 Layout();
370}
371
372void NotificationsList::OnClose(wxCloseEvent& event) { Hide(); }
373
374/*
375 * Notification Button Widget
376 */
377
378extern ocpnStyle::StyleManager* g_StyleManager;
379extern bool g_bSatValid;
380extern int g_SatsInView;
381extern bool g_bopengl;
382extern bool g_btenhertz;
383
384#ifndef GL_RGBA8
385#define GL_RGBA8 0x8058
386#endif
387
388NotificationButton::NotificationButton(ChartCanvas* parent) {
389 m_parent = parent;
390
391 ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
392 _img_gpsRed = style->GetIcon("gpsRed");
393
394 m_pStatBoxToolStaticBmp = NULL;
395
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());
401
402#ifdef ocpnUSE_GL
403 m_texobj = 0;
404#endif
405 m_texOK = false;
406
407 m_scale = 1.0;
408 m_cs = GLOBAL_COLOR_SCHEME_RGB;
409 m_NoteIconName = "notification-info-2";
410}
411
412NotificationButton::~NotificationButton() {
413#ifdef ocpnUSE_GL
414 if (m_texobj) {
415 glDeleteTextures(1, &m_texobj);
416 m_texobj = 0;
417 }
418#endif
419
420 delete m_pStatBoxToolStaticBmp;
421}
422
423void NotificationButton::SetIconSeverity(NotificationSeverity _severity) {
424 wxString icon_name;
425 if (_severity == NotificationSeverity::kInformational) {
426 icon_name = "notification-info-2";
427 } else if (_severity == NotificationSeverity::kWarning) {
428 icon_name = "notification-warning-2";
429 } else {
430 icon_name = "notification-critical-2";
431 }
432
433 SetIconName(icon_name);
434}
435
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) {
440 // The glContext is known active here,
441 // so safe to create a texture.
442 glGenTextures(1, &m_texobj);
443 CreateTexture();
444 }
445
446 if (g_bopengl && m_texobj /*&& m_texOK*/) {
447 glBindTexture(GL_TEXTURE_2D, m_texobj);
448 glEnable(GL_TEXTURE_2D);
449
450#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
451 float coords[8];
452 float uv[8];
453
454 // normal uv, normalized to POT
455 uv[0] = 0;
456 uv[1] = 0;
457 uv[2] = (float)m_image_width / m_tex_w;
458 uv[3] = 0;
459 uv[4] = (float)m_image_width / m_tex_w;
460 uv[5] = (float)m_image_height / m_tex_h;
461 uv[6] = 0;
462 uv[7] = (float)m_image_height / m_tex_h;
463
464 // pixels
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;
473
474 m_parent->GetglCanvas()->RenderTextures(dc, coords, uv, 4,
475 m_parent->GetpVP());
476#else
477
478 glBegin(GL_QUADS);
479
480 glTexCoord2f(0, 0);
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);
489
490 glEnd();
491#endif
492
493 glDisable(GL_TEXTURE_2D);
494
495 } else {
496#ifdef __WXOSX__
497 // Support MacBook Retina display
498 if (g_bopengl) {
499 double scale = m_parent->GetContentScaleFactor();
500 if (scale > 1) {
501 wxImage image = m_StatBmp.ConvertToImage();
502 image.Rescale(image.GetWidth() * scale, image.GetHeight() * scale);
503 wxBitmap bmp(image);
504 dc.DrawBitmap(bmp, m_rect.x, m_rect.y, true);
505 } else
506 dc.DrawBitmap(m_StatBmp, m_rect.x, m_rect.y, true);
507 } else
508 dc.DrawBitmap(m_StatBmp, m_rect.x, m_rect.y, true);
509#else
510 dc.DrawBitmap(m_StatBmp, m_rect.x, m_rect.y, true);
511#endif
512 }
513
514#else
515 dc.DrawBitmap(m_StatBmp, m_rect.x, m_rect.y, true);
516#endif
517 }
518}
519
520void NotificationButton::SetColorScheme(ColorScheme cs) {
521 m_cs = cs;
522 UpdateStatus(true);
523}
524
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()));
530#else
531 double scaleFactor = m_parent->GetContentScaleFactor();
532 wxRect logicalRect(
533 wxPoint(m_rect.GetX() / scaleFactor, m_rect.GetY() / scaleFactor),
534 wxSize(m_rect.GetWidth() / scaleFactor,
535 m_rect.GetHeight() / scaleFactor));
536#endif
537#else
538 // On platforms without DPI-independent pixels, logical = physical.
539 wxRect logicalRect = m_rect;
540#endif
541 return logicalRect;
542}
543
544bool NotificationButton::UpdateStatus(bool bnew) {
545 bool rv = false;
546 if (bnew) m_lastNoteIconName.Clear(); // force an update to occur
547 if (m_lastNoteIconName != m_NoteIconName) {
548 CreateBmp(bnew);
549 rv = true;
550 }
551
552#ifdef ocpnUSE_GL
553 if (g_bopengl && m_texobj) CreateTexture();
554#endif
555 return rv;
556}
557
558void NotificationButton::SetScaleFactor(float factor) {
559 ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
560
561 if (factor > 0.1)
562 m_scale = factor;
563 else
564 m_scale = 1.0;
565
566 // Precalculate the background sizes to get m_rect width/height
567 wxBitmap noteBg;
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);
574 }
575
576 if (fabs(m_scale - 1.0) > 0.1) {
577 // noteBg = noteBg.ConvertToImage();
578 // noteBg.Rescale(noteBg.GetWidth() * m_scale, noteBg.GetHeight() * m_scale,
579 // wxIMAGE_QUALITY_NORMAL);
580 // noteBg = wxBitmap(noteBg);
581 }
582
583 int width = noteBg.GetWidth() + style->GetCompassLeftMargin();
584 if (!style->marginsInvisible)
585 width += style->GetCompassLeftMargin() + style->GetToolSeparation();
586
587 m_rect = wxRect(style->GetCompassXOffset(), style->GetCompassYOffset(), width,
588 noteBg.GetHeight() + style->GetCompassTopMargin() +
589 style->GetCompassBottomMargin());
590}
591
592void NotificationButton::CreateBmp(bool newColorScheme) {
593 // wxString gpsIconName;
594 ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
595
596 // In order to draw a horizontal compass window when the toolbar is vertical,
597 // we need to save away the sizes and backgrounds for the two icons.
598
599 // static wxBitmap compassBg;
600 static wxBitmap noteBg;
601 static wxSize toolsize;
602 static int topmargin, leftmargin, radius;
603
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);
611 }
612
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);
618 }
619
620 leftmargin = style->GetCompassLeftMargin();
621 topmargin = style->GetCompassTopMargin();
622 radius = style->GetCompassCornerRadius();
623
624 if (orient == wxTB_VERTICAL) style->SetOrientation(wxTB_VERTICAL);
625 }
626
627 // int width = noteBg.GetWidth();// + leftmargin;
628 int height = noteBg.GetHeight() + topmargin + style->GetCompassBottomMargin();
629 int width = height; // + leftmargin;
630 // if (!style->marginsInvisible)
631 // width += leftmargin + style->GetToolSeparation();
632
633 m_StatBmp.Create(width, height);
634
635 m_rect.width = m_StatBmp.GetWidth();
636 m_rect.height = m_StatBmp.GetHeight();
637
638 m_MaskBmp = wxBitmap(m_StatBmp.GetWidth(), m_StatBmp.GetHeight());
639 if (style->marginsInvisible) {
640 wxMemoryDC sdc(m_MaskBmp);
641 sdc.SetBackground(*wxWHITE_BRUSH);
642 sdc.Clear();
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);
649 } else if (radius) {
650 wxMemoryDC sdc(m_MaskBmp);
651 sdc.SetBackground(*wxWHITE_BRUSH);
652 sdc.Clear();
653 sdc.SetBrush(*wxBLACK_BRUSH);
654 sdc.SetPen(*wxBLACK_PEN);
655 sdc.DrawRoundedRectangle(0, 0, m_MaskBmp.GetWidth(), m_MaskBmp.GetHeight(),
656 radius);
657 sdc.SelectObject(wxNullBitmap);
658 }
659#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
660 m_StatBmp.SetMask(new wxMask(m_MaskBmp, *wxWHITE));
661#endif
662
663 wxMemoryDC mdc;
664 mdc.SelectObject(m_StatBmp);
665 mdc.SetBackground(wxBrush(GetGlobalColor("COMP1"), wxBRUSHSTYLE_SOLID));
666 mdc.Clear();
667
668 mdc.SetPen(wxPen(GetGlobalColor("UITX1"), 1));
669 mdc.SetBrush(wxBrush(GetGlobalColor("UITX1"), wxBRUSHSTYLE_TRANSPARENT));
670
671 if (!style->marginsInvisible)
672 mdc.DrawRoundedRectangle(0, 0, m_StatBmp.GetWidth(), m_StatBmp.GetHeight(),
673 radius);
674 wxPoint offset(leftmargin, topmargin);
675
676 // Notification Icon
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);
681
682 swidth = swidth * 45 / 50;
683 sheight = sheight * 45 / 50;
684
685 offset.x = ((m_StatBmp.GetWidth() - swidth) / 2);
686 offset.y = ((m_StatBmp.GetHeight() - sheight) / 2);
687
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);
694
695 wxBitmap iconBm;
696 iconBm = gicon;
697
698 mdc.DrawBitmap(iconBm, offset);
699 mdc.SelectObject(wxNullBitmap);
700
701 m_lastNoteIconName = m_NoteIconName;
702}
703
704void NotificationButton::CreateTexture() {
705#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
706 // GLES does not do ocpnDC::DrawBitmap(), so use
707 // texture
708 if (g_bopengl) {
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;
716
717 // Make it POT
718 int width_pot = m_tex_w;
719 int height_pot = m_tex_h;
720
721 int xp = image.GetWidth();
722 if (((xp != 0) && !(xp & (xp - 1)))) // detect POT
723 width_pot = xp;
724 else {
725 int a = 0;
726 while (xp) {
727 xp = xp >> 1;
728 a++;
729 }
730 width_pot = 1 << a;
731 }
732
733 xp = image.GetHeight();
734 if (((xp != 0) && !(xp & (xp - 1))))
735 height_pot = xp;
736 else {
737 int a = 0;
738 while (xp) {
739 xp = xp >> 1;
740 a++;
741 }
742 height_pot = 1 << a;
743 }
744
745 m_tex_w = width_pot;
746 m_tex_h = height_pot;
747
748 GLuint format = GL_RGBA;
749 GLuint internalformat = GL_RGBA8; // format;
750 int stride = 4;
751
752 if (imgdata) {
753 unsigned char* teximage =
754 (unsigned char*)malloc(stride * m_tex_w * m_tex_h);
755
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);
760
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;
765 }
766 }
767
768 glBindTexture(GL_TEXTURE_2D, m_texobj);
769
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,
773 GL_NEAREST); // No mipmapping
774 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
775
776 glTexImage2D(GL_TEXTURE_2D, 0, internalformat, m_tex_w, m_tex_h, 0,
777 format, GL_UNSIGNED_BYTE, teximage);
778
779 free(teximage);
780 glBindTexture(GL_TEXTURE_2D, 0);
781 m_texOK = true;
782 }
783 }
784#endif
785}
786
787void NotificationButton::UpdateTexture() {
788#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
789 // GLES does not do ocpnDC::DrawBitmap(), so use
790 // texture
791 if (g_bopengl) {
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;
799
800 // Make it POT
801 int width_pot = m_tex_w;
802 int height_pot = m_tex_h;
803
804 int xp = image.GetWidth();
805 if (((xp != 0) && !(xp & (xp - 1)))) // detect POT
806 width_pot = xp;
807 else {
808 int a = 0;
809 while (xp) {
810 xp = xp >> 1;
811 a++;
812 }
813 width_pot = 1 << a;
814 }
815
816 xp = image.GetHeight();
817 if (((xp != 0) && !(xp & (xp - 1))))
818 height_pot = xp;
819 else {
820 int a = 0;
821 while (xp) {
822 xp = xp >> 1;
823 a++;
824 }
825 height_pot = 1 << a;
826 }
827
828 m_tex_w = width_pot;
829 m_tex_h = height_pot;
830
831 GLuint format = GL_RGBA;
832 GLuint internalformat = GL_RGBA8; // format;
833 int stride = 4;
834
835 if (imgdata) {
836 unsigned char* teximage =
837 (unsigned char*)malloc(stride * m_tex_w * m_tex_h);
838
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);
843
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;
848 }
849 }
850
851 glBindTexture(GL_TEXTURE_2D, m_texobj);
852 glEnable(GL_TEXTURE_2D);
853
854 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_tex_w, m_tex_h, format,
855 GL_UNSIGNED_BYTE, teximage);
856
857 free(teximage);
858 glBindTexture(GL_TEXTURE_2D, 0);
859 }
860 }
861#endif
862}
Generic Chart canvas base.
double GetDisplayDIPMult(wxWindow *win)
Get the display scaling factor for DPI-aware rendering.
ChartCanvas - Main chart display and interaction component.
Definition chcanv.h:174
wxRect GetLogicalRect(void) const
Return the coordinates of the widget, in logical pixels.
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.
wxSize getDisplaySize()
Get the display size in logical pixels.
Device context class that can use either wxDC or OpenGL for drawing.
Definition ocpndc.h:60
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.
Date and time utilities.
OpenGL chart rendering canvas.
wxFont * GetOCPNScaledFont(wxString item, int default_size)
Retrieves a font from FontMgr, optionally scaled for physical readability.
Definition gui_lib.cpp:61
General purpose GUI support.
#define wxCLOSE_BOX
Definition mark_info.h:133
Utility functions.
User notification container.
User notifications manager.
bool g_bSatValid
Indicates valid GNSS reception status based on satellite visibility and successful parsing of NMEA018...
Definition comm_vars.cpp:32
Notification Manager GUI.
OpenCPN Platform specific support utilities.
Configuration options for date and time formatting.
Chart Symbols.
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...
Definition svg_utils.cpp:59
SVG utilities.