35#include <wx/statline.h>
40#include "OCPNPlatform.h"
47#include "model/idents.h"
56#ifdef __OCPN__ANDROID__
57#include "androidUTIL.h"
62extern GLenum g_texture_rectangle_format;
72extern bool g_bShowMuiZoomButtons;
78double getValue(
int animationType,
double t);
82#define ID_SCALE_CANCEL 8301
83#define ID_SCALE_OK 8302
84#define ID_SCALECTRL 8303
86static inline void IgnoreRetval(
size_t n) { (void)n; }
94 SetScaleDialog(wxWindow* parent, wxWindowID
id = SYMBOL_GOTOPOS_IDNAME,
95 const wxString& caption = _(
"Set scale"),
96 const wxPoint& pos = wxDefaultPosition,
97 const wxSize& size = wxDefaultSize,
98 long style = wxDEFAULT_DIALOG_STYLE);
103 bool Create(wxWindow* parent, wxWindowID
id = wxID_ANY,
104 const wxString& caption = _(
"Set scale"),
105 const wxPoint& pos = wxDefaultPosition,
106 const wxSize& size = wxDefaultSize,
107 long style = wxDEFAULT_DIALOG_STYLE);
109 void SetColorScheme(ColorScheme cs);
113 void OnSetScaleCancelClick(wxCommandEvent& event);
114 void OnSetScaleOKClick(wxCommandEvent& event);
119 wxButton* m_CancelButton;
120 wxButton* m_OKButton;
124EVT_BUTTON(ID_GOTOPOS_CANCEL, SetScaleDialog::OnSetScaleCancelClick)
125EVT_BUTTON(ID_GOTOPOS_OK, SetScaleDialog::OnSetScaleOKClick)
135 const wxString& caption,
const wxPoint& pos,
136 const wxSize& size,
long style) {
138 wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxFRAME_FLOAT_ON_PARENT;
140 Create(parent,
id, caption, pos, size, wstyle);
143SetScaleDialog::~SetScaleDialog() {}
150 const wxString& caption,
const wxPoint& pos,
151 const wxSize& size,
long style) {
152 SetExtraStyle(GetExtraStyle() | wxWS_EX_BLOCK_EVENTS);
153 wxDialog::Create(parent,
id, caption, pos, size, style);
156 GetSizer()->SetSizeHints(
this);
169 wxBoxSizer* itemBoxSizer2 =
new wxBoxSizer(wxVERTICAL);
170 itemDialog1->SetSizer(itemBoxSizer2);
172 wxStaticBox* itemStaticBoxSizer4Static =
173 new wxStaticBox(itemDialog1, wxID_ANY, _(
"Chart Scale"));
175 wxStaticBoxSizer* itemStaticBoxSizer4 =
176 new wxStaticBoxSizer(itemStaticBoxSizer4Static, wxVERTICAL);
177 itemBoxSizer2->Add(itemStaticBoxSizer4, 0, wxEXPAND | wxALL, 5);
179 wxStaticText* itemStaticText5 =
new wxStaticText(
180 itemDialog1, wxID_STATIC,
"", wxDefaultPosition, wxDefaultSize, 0);
181 itemStaticBoxSizer4->Add(itemStaticText5, 0,
182 wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, 5);
184 m_ScaleCtl =
new wxTextCtrl(itemDialog1, ID_SCALECTRL,
"", wxDefaultPosition,
186 itemStaticBoxSizer4->Add(
187 m_ScaleCtl, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxBOTTOM | wxEXPAND, 5);
189 wxBoxSizer* itemBoxSizer16 =
new wxBoxSizer(wxHORIZONTAL);
190 itemBoxSizer2->Add(itemBoxSizer16, 0, wxALIGN_RIGHT | wxALL, 5);
192 m_CancelButton =
new wxButton(itemDialog1, ID_GOTOPOS_CANCEL, _(
"Cancel"),
193 wxDefaultPosition, wxDefaultSize, 0);
194 itemBoxSizer16->Add(m_CancelButton, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
196 m_OKButton =
new wxButton(itemDialog1, ID_GOTOPOS_OK, _(
"OK"),
197 wxDefaultPosition, wxDefaultSize, 0);
198 itemBoxSizer16->Add(m_OKButton, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
199 m_OKButton->SetDefault();
201 SetColorScheme((ColorScheme)0);
204void SetScaleDialog::SetColorScheme(ColorScheme cs) { DimeControl(
this); }
206void SetScaleDialog::OnSetScaleCancelClick(wxCommandEvent& event) {
211void SetScaleDialog::OnSetScaleOKClick(wxCommandEvent& event) {
212 SetReturnCode(wxID_OK);
222 wxSize DoGetBestSize()
const;
226 MUIButton(wxWindow* parent, wxWindowID
id = wxID_ANY,
227 float scale_factor = 1.0,
228 const wxString& bitmapState0 = wxEmptyString,
229 const wxString& bitmapState1 = wxEmptyString,
230 const wxString& bitmapState2 = wxEmptyString,
231 const wxPoint& pos = wxDefaultPosition,
232 const wxSize& size = wxDefaultSize,
long style = wxNO_BORDER);
234 bool Create(wxWindow* parent, wxWindowID
id = wxID_ANY,
235 float scale_factor = 1.0,
236 const wxString& bitmapState0 = wxEmptyString,
237 const wxString& bitmapState1 = wxEmptyString,
238 const wxString& bitmapState2 = wxEmptyString,
239 const wxPoint& pos = wxDefaultPosition,
240 const wxSize& size = wxDefaultSize,
long style = wxNO_BORDER);
245 void CreateControls();
247 void SetState(
int state);
248 int GetState() {
return mState; }
250 void SetColorScheme(ColorScheme cs);
251 void OnSize(wxSizeEvent& event);
252 void OnLeftDown(wxMouseEvent& event);
253 void OnLeftUp(wxMouseEvent& event);
255 wxBitmap GetBitmapResource(
const wxString& name);
257 wxIcon GetIconResource(
const wxString& name);
258 wxBitmap GetButtonBitmap() {
return m_bitmap; }
266 wxString m_bitmapFileState0;
267 wxString m_bitmapFileState1;
268 wxString m_bitmapFileState2;
270 wxBitmap m_bitmapState0;
271 wxBitmap m_bitmapState1;
272 wxBitmap m_bitmapState2;
276 wxSize m_styleToolSize;
280MUIButton::MUIButton() { Init(); }
282MUIButton::MUIButton(wxWindow* parent, wxWindowID
id,
float scale_factor,
283 const wxString& bitmap,
const wxString& bitmapState1,
284 const wxString& bitmapState2,
const wxPoint& pos,
285 const wxSize& size,
long style) {
288 Create(parent,
id, scale_factor, bitmap, bitmapState1, bitmapState2, pos,
292bool MUIButton::Create(wxWindow* parent, wxWindowID
id,
float scale_factor,
293 const wxString& bitmap,
const wxString& bitmapState1,
294 const wxString& bitmapState2,
const wxPoint& pos,
295 const wxSize& size,
long style) {
296 m_bitmapFileState0 = bitmap;
297 m_bitmapFileState1 = bitmapState1;
298 m_bitmapFileState2 = bitmapState2;
300 m_scaleFactor = scale_factor;
302 m_styleToolSize = g_StyleManager->GetCurrentStyle()->GetToolSize();
306 m_styleToolSize = wxSize(m_styleToolSize.x * 1.25, m_styleToolSize.y * 1.25);
308 m_size = wxSize(m_styleToolSize.x * m_scaleFactor,
309 m_styleToolSize.y * m_scaleFactor);
315MUIButton::~MUIButton() {}
317void MUIButton::Init() {
319 m_cs = (ColorScheme)-1;
322void MUIButton::CreateControls() {
332void MUIButton::SetColorScheme(ColorScheme cs) {
335 wxColour backColor = GetGlobalColor(
"GREY3");
340 wxBitmap bmp = LoadSVG(m_bitmapFileState0, m_size.x, m_size.y);
341 m_bitmapState0 = style->SetBitmapBrightness(bmp, cs);
343 bmp = LoadSVG(m_bitmapFileState1, m_size.x, m_size.y);
345 m_bitmapState1 = style->SetBitmapBrightness(bmp, cs);
347 m_bitmapState1 = m_bitmapState0;
349 bmp = LoadSVG(m_bitmapFileState2, m_size.x, m_size.y);
351 m_bitmapState2 = style->SetBitmapBrightness(bmp, cs);
353 m_bitmapState2 = m_bitmapState0;
358 m_bitmap = m_bitmapState0;
362 m_bitmap = m_bitmapState1;
366 m_bitmap = m_bitmapState2;
377void MUIButton::SetState(
int state) {
381 m_bitmap = m_bitmapState0;
385 m_bitmap = m_bitmapState1;
389 m_bitmap = m_bitmapState2;
396void MUIButton::OnSize(wxSizeEvent& event) {
397 if (m_bitmap.IsOk()) {
398 if (event.GetSize() == m_bitmap.GetSize())
return;
401 if (!m_bitmapFileState0.IsEmpty())
403 LoadSVG(m_bitmapFileState0, event.GetSize().x, event.GetSize().y);
405 if (!m_bitmapFileState1.IsEmpty())
407 LoadSVG(m_bitmapFileState1, event.GetSize().x, event.GetSize().y);
408 if (!m_bitmapState1.IsOk() || m_bitmapFileState1.IsEmpty())
409 m_bitmapState1 = m_bitmapState0;
411 if (!m_bitmapFileState2.IsEmpty())
413 LoadSVG(m_bitmapFileState2, event.GetSize().x, event.GetSize().y);
414 if (!m_bitmapState2.IsOk() || m_bitmapFileState2.IsEmpty())
415 m_bitmapState2 = m_bitmapState0;
420 m_bitmap = m_bitmapState0;
424 m_bitmap = m_bitmapState1;
428 m_bitmap = m_bitmapState2;
433wxSize MUIButton::DoGetBestSize()
const {
438 return wxSize(m_styleToolSize.x * m_scaleFactor,
439 m_styleToolSize.y * m_scaleFactor);
445static std::unordered_map<char, ssfn_glyph_t*> ssfn_glyph_map;
451 char* fontdata = NULL;
455 unsigned char hdr[2];
459 f = fopen(filename,
"rb");
461 fprintf(stderr,
"unable to load %s\n", filename);
466 fread(&hdr, 2, 1, f);
467 if (hdr[0] == 0x1f && hdr[1] == 0x8b) {
468 fseek(f, -4L, SEEK_END);
469 fread(&size, 4, 1, f);
471 fseek(f, 0, SEEK_END);
475 g = gzopen(filename,
"r");
477 fseek(f, 0, SEEK_END);
479 fseek(f, 0, SEEK_SET);
482 fprintf(stderr,
"unable to load %s\n", filename);
485 fontdata = (
char*)malloc(size);
487 fprintf(stderr,
"memory allocation error\n");
491 gzread(g, fontdata, size);
494 IgnoreRetval(fread(fontdata, size, 1, f));
501bool RenderGlyphToImageBuffer(
unsigned char* buffer,
ssfn_glyph_t* glyph,
502 int x_offset,
int w,
int h,
int nominal_baseline,
503 wxColour& color, wxColour& back_color) {
504 unsigned char* src = glyph->data;
505 for (
int i = 0; i < h; i++) {
506 for (
int j = 0; j < glyph->w; j++) {
507 size_t index = i * w + j + x_offset;
508 index += (nominal_baseline - glyph->baseline) * w;
509 if (index > (
size_t)h * (w - 1))
continue;
510 size_t didx = index * 3;
512 size_t sidx = i * glyph->pitch + j;
513 unsigned char d = src[sidx];
515 buffer[didx] = (color.Red() * dn) + (back_color.Red() * (1 - dn));
516 buffer[didx + 1] = (color.Green() * dn) + (back_color.Green() * (1 - dn));
517 buffer[didx + 2] = (color.Blue() * dn) + (back_color.Blue() * (1 - dn));
523bool RenderStringToBuffer(
unsigned char* buffer, std::string s,
int wbox,
524 int hbox,
int nominal_baseline, wxColour color,
525 wxColour& back_color) {
527 for (
unsigned int i = 0; i < s.size(); i++) {
531 if (
auto findit = ssfn_glyph_map.find(key);
532 findit != ssfn_glyph_map.end()) {
533 glyph = findit->second;
535 glyph = ssfn_render(&ctx, key);
536 ssfn_glyph_map[key] = glyph;
538 RenderGlyphToImageBuffer(buffer, glyph, xpos, wbox, hbox, nominal_baseline,
540 xpos += glyph->adv_x;
552 MUITextButton(wxWindow* parent, wxWindowID
id,
float scale_factor,
553 wxColor backColor,
const wxString& text = wxEmptyString,
554 const wxPoint& pos = wxDefaultPosition,
555 const wxSize& size = wxDefaultSize,
long style = wxNO_BORDER);
557 bool Create(wxWindow* parent, wxWindowID
id,
float scale_factor = 1.0,
558 const wxString& text = wxEmptyString,
559 const wxPoint& pos = wxDefaultPosition,
560 const wxSize& size = wxDefaultSize,
long style = wxNO_BORDER);
565 wxSize GetSize() {
return m_size; }
566 void SetState(
int state);
568 void SetColorScheme(ColorScheme cs);
569 void OnSize(wxSizeEvent& event);
570 void OnLeftDown(wxMouseEvent& event);
571 void OnLeftUp(wxMouseEvent& event);
572 void SetText(
const wxString& text);
574 wxBitmap GetButtonBitmap() {
return m_bitmap; }
586 wxSize m_styleToolSize;
590 wxColor m_backgrounColor;
593MUITextButton::MUITextButton() { Init(); }
595MUITextButton::MUITextButton(wxWindow* parent, wxWindowID
id,
596 float scale_factor, wxColor backColor,
597 const wxString& text,
const wxPoint& pos,
598 const wxSize& size,
long style) {
599 m_backgrounColor = backColor;
601 Create(parent,
id, scale_factor, text, pos, size, style);
604bool MUITextButton::Create(wxWindow* parent, wxWindowID
id,
float scale_factor,
605 const wxString& text,
const wxPoint& pos,
606 const wxSize& size,
long style) {
609 m_scaleFactor = scale_factor;
611 m_styleToolSize = g_StyleManager->GetCurrentStyle()->GetToolSize();
615 m_styleToolSize = wxSize(m_styleToolSize.x * 1.25, m_styleToolSize.y * 1.25);
617 int height_ref = m_styleToolSize.y;
620 memset(&ctx, 0,
sizeof(
ssfn_t));
621 wxString font_file = g_Platform->GetSharedDataDir() +
"ssfndata/FreeSans.sfn";
622 std::string sfont = font_file.ToStdString();
624 pssfn_font = load_font(sfont.c_str());
625 if (pssfn_font) ssfn_load(&ctx, pssfn_font);
627 m_pixel_height = height_ref / 2;
628 m_pixel_height *= m_scaleFactor;
632 ssfn_select(&ctx, SSFN_FAMILY_SANS, NULL,
633 SSFN_STYLE_REGULAR, m_pixel_height,
640 if (m_ssfn_status == SSFN_OK) {
641 std::string t =
"1:888888";
642 ssfn_bbox(&ctx, (
char*)t.c_str(), 0, &wbox, &hbox);
645 m_size = wxSize(hbox * 1.5, m_styleToolSize.y);
646 m_size.y *= m_scaleFactor;
650MUITextButton::~MUITextButton() {
651 for (
const auto& [key, value] : ssfn_glyph_map) {
654 ssfn_glyph_map.clear();
657void MUITextButton::Init() {
659 m_cs = (ColorScheme)-1;
662void MUITextButton::SetText(
const wxString& text) {
663 if (!m_text.IsSameAs(text)) {
669void MUITextButton::SetColorScheme(ColorScheme cs) {
679void MUITextButton::SetState(
int state) { mState = state; }
681void MUITextButton::OnSize(wxSizeEvent& event) {
686void MUITextButton::BuildBitmap() {
687 int width = m_size.x;
688 int height = m_size.y;
690 if (m_ssfn_status != SSFN_OK)
return;
694 std::string t = m_text.ToStdString();
695 ssfn_bbox(&ctx, (
char*)t.c_str(), 0, &wbox, &hbox);
698 int baseline = glyph->baseline;
701 unsigned char* image_data = (
unsigned char*)calloc(1, wbox * hbox * 3);
702 for (
int i = 0; i < wbox * hbox; i++) {
704 image_data[idx] = m_backgrounColor.Red();
705 image_data[idx + 1] = m_backgrounColor.Green();
706 image_data[idx + 2] = m_backgrounColor.Blue();
709 RenderStringToBuffer(image_data, t, wbox, hbox, baseline,
710 GetGlobalColor(
"CHWHT"), m_backgrounColor);
712 wxImage fimage = wxImage(wbox, hbox, image_data);
713 wxSize clip_size = wxSize(wbox, baseline + 2);
714 wxRect clip_rect = wxRect(0, 0, clip_size.x, clip_size.y);
715 wxImage clip_image = fimage.GetSubImage(clip_rect);
717 m_bitmap = wxBitmap(clip_image);
720#define CANVAS_OPTIONS_ANIMATION_TIMER_1 800
721#define CANVAS_OPTIONS_TIMER 801
730MUIBar::MUIBar(
ChartCanvas* parent,
int orientation,
float size_factor,
731 wxWindowID
id,
const wxPoint& pos,
const wxSize& size,
732 long style,
const wxString& name) {
733 m_parentCanvas = parent;
734 m_orientation = orientation;
736 m_scaleFactor = size_factor;
737 m_cs = (ColorScheme)-1;
738 wxColour backColor = wxColor(*wxBLACK);
745 if (m_canvasOptions) {
746 m_canvasOptions->Destroy();
751 delete m_followButton;
753 delete m_scaleButton;
760 m_followButton = NULL;
762 m_scaleButton = NULL;
764 m_canvasOptions = NULL;
765 m_canvasOptionsAnimationTimer.SetOwner(
this,
766 CANVAS_OPTIONS_ANIMATION_TIMER_1);
767 m_backcolor = GetGlobalColor(
"GREY3");
768 m_capture_size_y = 0;
772 CanvasOptionTimer.SetOwner(
this, CANVAS_OPTIONS_TIMER);
773 m_coAnimateByBitmaps =
false;
775#ifdef __OCPN__ANDROID__
779 m_end_margin = m_parentCanvas->GetCharWidth() / 2;
783void MUIBar::SetColorScheme(ColorScheme cs) {
785 if (m_zinButton) m_zinButton->SetColorScheme(cs);
786 if (m_zoutButton) m_zoutButton->SetColorScheme(cs);
787 if (m_followButton) m_followButton->SetColorScheme(cs);
788 if (m_menuButton) m_menuButton->SetColorScheme(cs);
790 if (m_scaleButton) m_scaleButton->SetColorScheme(cs);
796void MUIBar::InvalidateBitmap() {
797 m_bitmap = wxNullBitmap;
801 glDeleteTextures(1, &m_texture);
807bool MUIBar::MouseEvent(wxMouseEvent& event) {
809 event.GetPosition(&x, &y);
812 wxRect r = wxRect(m_screenPos, m_size);
813 if (r.Contains(x, y)) {
815 if (event.LeftDown()) {
816 if (g_bShowMuiZoomButtons) {
817 wxRect rzin(m_zinButton->m_position.x, m_zinButton->m_position.y,
818 m_zinButton->m_size.x, m_zinButton->m_size.y);
819 rzin.Offset(m_screenPos);
820 if (rzin.Contains(x, y)) {
821 wxCommandEvent evt(wxEVT_COMMAND_MENU_SELECTED, ID_ZOOMIN);
822 m_parentCanvas->GetEventHandler()->AddPendingEvent(evt);
827 wxRect rzout(m_zoutButton->m_position.x, m_zoutButton->m_position.y,
828 m_zoutButton->m_size.x, m_zoutButton->m_size.y);
829 rzout.Offset(m_screenPos);
830 if (rzout.Contains(x, y)) {
831 wxCommandEvent evt(wxEVT_COMMAND_MENU_SELECTED, ID_ZOOMOUT);
832 m_parentCanvas->GetEventHandler()->AddPendingEvent(evt);
837 if (m_followButton) {
838 wxRect rfollow(m_followButton->m_position.x,
839 m_followButton->m_position.y, m_followButton->m_size.x,
840 m_followButton->m_size.y);
841 rfollow.Offset(m_screenPos);
842 if (rfollow.Contains(x, y)) {
843 wxCommandEvent evt(wxEVT_COMMAND_MENU_SELECTED, ID_FOLLOW);
844 m_parentCanvas->GetEventHandler()->AddPendingEvent(evt);
849 wxRect rmenu(m_menuButton->m_position.x, m_menuButton->m_position.y,
850 m_menuButton->m_size.x, m_menuButton->m_size.y);
851 rmenu.Offset(m_screenPos);
852 if (rmenu.Contains(x, y)) {
856 }
else if (event.LeftUp()) {
858 wxRect rscale(m_scaleButton->m_position.x, m_scaleButton->m_position.y,
859 m_scaleButton->m_size.x, m_scaleButton->m_size.y);
860 rscale.Offset(m_screenPos);
861 if (rscale.Contains(x, y)) {
862 OnScaleSelected(event);
871void MUIBar::OnScaleSelected(wxMouseEvent& event) {
872 auto pcc =
dynamic_cast<ChartCanvas*
>(m_parentCanvas);
878 if (dlg.GetReturnCode() == wxID_OK) {
879 wxString newScale = dlg.m_ScaleCtl->GetValue();
880 if (newScale.Contains(
':')) newScale = newScale.AfterFirst(
':');
882 if (newScale.ToDouble(&dScale)) {
884 dScale = wxMin(dScale, 3e6);
885 dScale = wxMax(dScale, 1000);
886 double displayScaleNow = pcc->GetScaleValue();
887 double factor = displayScaleNow / dScale;
888 pcc->ZoomCanvasSimple(factor);
892 displayScaleNow = pcc->GetScaleValue();
893 factor = displayScaleNow / dScale;
894 pcc->ZoomCanvasSimple(factor);
899void MUIBar::SetCanvasENCAvailable(
bool avail) {
900 m_CanvasENCAvail = avail;
901 if (m_canvasOptions) m_canvasOptions->SetENCAvailable(avail);
904void MUIBar::CreateControls() {
905 wxString iconDir = g_Platform->GetSharedDataDir() +
"uidata/MUI_flat/";
909 iconDir +
"MUI_zoom-in.svg");
910 wxSize button_size = tb->m_size;
913 if (m_orientation == wxHORIZONTAL) {
917 if (g_bShowMuiZoomButtons) {
918 m_zinButton =
new MUIButton(m_parentCanvas, ID_ZOOMIN, m_scaleFactor,
919 iconDir +
"MUI_zoom-in.svg");
920 m_zinButton->m_position = wxPoint(xoff, 0);
921 xoff += m_zinButton->m_size.x;
923 m_zoutButton =
new MUIButton(m_parentCanvas, ID_ZOOMOUT, m_scaleFactor,
924 iconDir +
"MUI_zoom-out.svg");
925 m_zoutButton->m_position = wxPoint(xoff, 0);
926 xoff += m_zoutButton->m_size.x;
929#ifndef __OCPN__ANDROID__
932 m_scaleButton =
new MUITextButton(m_parentCanvas, wxID_ANY, m_scaleFactor,
933 GetBackgroundColor(),
"1:400000");
935 m_scaleButton->m_position = wxPoint(xoff, 0);
936 if (m_scaleButton->GetButtonBitmap().IsOk()) {
937 int bm_pos_y = (m_scaleButton->GetSize().y -
938 m_scaleButton->GetButtonBitmap().GetHeight()) /
940 m_scaleButton->m_position = wxPoint(xoff, bm_pos_y);
942 xoff += m_scaleButton->m_size.x;
945 m_parentCanvas, ID_FOLLOW, m_scaleFactor, iconDir +
"MUI_follow.svg",
946 iconDir +
"MUI_follow_active.svg", iconDir +
"MUI_follow_ahead.svg");
947 m_followButton->m_position = wxPoint(xoff, 0);
948 xoff += m_followButton->m_size.x;
951 m_menuButton =
new MUIButton(m_parentCanvas, ID_MUI_MENU, m_scaleFactor,
952 iconDir +
"MUI_menu.svg");
953 m_menuButton->m_position = wxPoint(xoff, 0);
954 xoff += m_menuButton->m_size.x;
956 m_size.y = button_size.y;
962 if (g_bShowMuiZoomButtons) {
963 m_zinButton =
new MUIButton(m_parentCanvas, ID_ZOOMIN, m_scaleFactor,
964 iconDir +
"MUI_zoom-in.svg");
965 m_zinButton->m_position = wxPoint(0, yoff);
966 yoff += m_zinButton->m_size.y;
968 m_zoutButton =
new MUIButton(m_parentCanvas, ID_ZOOMOUT, m_scaleFactor,
969 iconDir +
"MUI_zoom-out.svg");
970 m_zoutButton->m_position = wxPoint(0, yoff);
971 yoff += m_zoutButton->m_size.y;
974#ifndef __OCPN__ANDROID__
976 m_parentCanvas, ID_FOLLOW, m_scaleFactor, iconDir +
"MUI_follow.svg",
977 iconDir +
"MUI_follow_active.svg", iconDir +
"MUI_follow_ahead.svg");
978 m_followButton->m_position = wxPoint(0, yoff);
979 yoff += m_followButton->m_size.y;
982 m_menuButton =
new MUIButton(m_parentCanvas, ID_MUI_MENU, m_scaleFactor,
983 iconDir +
"MUI_menu.svg");
984 m_menuButton->m_position = wxPoint(0, yoff);
985 yoff += m_menuButton->m_size.y;
988 m_size.x = button_size.x;
992void MUIBar::SetBestPosition(
void) {
994 (m_parentCanvas->GetClientSize().x - (m_size.x + (m_end_margin) * 2.00));
996 int bottomOffset = 6;
998 int y = m_parentCanvas->GetClientSize().y - m_size.y - bottomOffset;
1003 wxPoint position = wxPoint(x, y);
1004 m_screenPos = position;
1006 if (m_canvasOptions) {
1007 m_canvasOptions->Destroy();
1008 m_canvasOptions = 0;
1012void MUIBar::UpdateDynamicValues() {
1013 if (!m_scaleButton)
return;
1015 wxString scaleString;
1016 int scale = m_parentCanvas->GetScaleValue();
1018 if (
scale != m_scale) InvalidateBitmap();
1022 scaleString.Printf(
"1:%d",
scale);
1024 scaleString.Printf(
"1:%4.1f M",
scale / 1e6);
1026 if (m_scaleButton) m_scaleButton->SetText(scaleString);
1029void MUIBar::SetFollowButtonState(
int state) {
1030 if (m_followButton && m_followButton->GetState() != state) {
1031 m_followButton->SetState(state);
1036void MUIBar::HandleMenuClick() {
1037 if (!m_canvasOptions) {
1042 wxPoint parentClientUpperRight =
1043 m_parentCanvas->ClientToScreen(wxPoint(m_parentCanvas->GetSize().x, 0));
1044 wxPoint muibar_top = m_parentCanvas->ClientToScreen(m_screenPos);
1045 int size_y = muibar_top.y - (parentClientUpperRight.y + m_COTopOffset);
1046 size_y -= m_parentCanvas->GetCharHeight();
1047 size_y = wxMax(size_y, 100);
1049 m_canvasOptions->SetSize(wxSize(-1, size_y));
1050 m_canvasOptionsFullSize = m_canvasOptions->GetSize();
1051 m_canvasOptionsFullSize.x +=
1052 m_canvasOptions->GetCharWidth();
1055 m_currentCOPos = m_parentCanvas->ClientToScreen(
1056 wxPoint(m_parentCanvas->GetSize().x, m_COTopOffset));
1058 m_canvasOptions->Move(m_currentCOPos);
1059 m_canvasOptions->Hide();
1062 m_canvasOptions->SetENCAvailable(m_CanvasENCAvail);
1064 if (m_canvasOptions->IsShown())
1065 PushCanvasOptions();
1069 if (m_coAnimateByBitmaps && m_capture_size_y) {
1070 int overShoot_x = m_canvasOptions->GetSize().x * 2 / 10;
1072 wxPoint(m_capturePoint.x - overShoot_x, m_capturePoint.y);
1074 m_backingBitmap = wxBitmap(m_canvasOptionsFullSize.x + overShoot_x,
1075 m_capture_size_y, -1);
1077 mdcb.SelectObject(m_backingBitmap);
1079 mdcb.Blit(0, 0, m_canvasOptionsFullSize.x + overShoot_x, m_capture_size_y,
1080 &sdc, m_capturePoint.x - overShoot_x, m_capturePoint.y, wxCOPY);
1081 mdcb.SelectObject(wxNullBitmap);
1083 PullCanvasOptions();
1087void MUIBar::CaptureCanvasOptionsBitmap() {
1089 CanvasOptionTimer.Start(100, wxTIMER_ONE_SHOT);
1092void MUIBar::CaptureCanvasOptionsBitmapChain(wxTimerEvent& event) {
1093 if (m_coSequence == 0) {
1094 if (!m_canvasOptions) m_canvasOptions =
new CanvasOptions(m_parentCanvas);
1096 wxPoint parentClientUpperRight =
1097 m_parentCanvas->ClientToScreen(wxPoint(m_parentCanvas->GetSize().x, 0));
1098 wxRect rmui = m_parentCanvas->GetMUIBarRect();
1099 int size_y = rmui.y - (parentClientUpperRight.y + m_COTopOffset);
1100 size_y -= m_parentCanvas->GetCharHeight();
1101 size_y = wxMax(size_y, 100);
1102 m_capture_size_y = size_y;
1104 m_canvasOptions->SetSize(wxSize(-1, size_y));
1106 m_capturePoint = m_parentCanvas->ClientToScreen(
1107 wxPoint(m_parentCanvas->GetSize().x, m_COTopOffset));
1108 m_canvasOptions->Move(m_capturePoint);
1109 m_canvasOptions->Show();
1112 CanvasOptionTimer.Start(1, wxTIMER_ONE_SHOT);
1115 else if (m_coSequence == 1) {
1116 m_capturePoint = m_parentCanvas->ClientToScreen(
1117 wxPoint(m_parentCanvas->GetSize().x - m_canvasOptionsFullSize.x,
1119 m_canvasOptions->Move(m_capturePoint);
1122 CanvasOptionTimer.Start(1, wxTIMER_ONE_SHOT);
1125 else if (m_coSequence == 2) {
1127 wxBitmap(m_canvasOptions->GetSize().x, m_capture_size_y, -1);
1128 wxMemoryDC mdc(m_animateBitmap);
1132 mdc.Blit(0, 0, m_canvasOptions->GetSize().x, m_capture_size_y, &sdc,
1133 m_capturePoint.x, m_capturePoint.y, wxCOPY);
1134 mdc.SelectObject(wxNullBitmap);
1141wxBitmap& MUIBar::CreateBitmap(
double displayScale) {
1142 if (m_bitmap.IsOk())
return m_bitmap;
1145 int width = m_size.x;
1146 int height = m_size.y;
1149 wxBitmap bm(width, height);
1150 mdc.SelectObject(bm);
1151 mdc.SetBackground(wxBrush(GetBackgroundColor()));
1156 if (g_bShowMuiZoomButtons) {
1157 wxBitmap bmd = m_zinButton->GetButtonBitmap();
1159 mdc.DrawBitmap(bmd, m_zinButton->m_position.x, m_zinButton->m_position.y,
1162 bmd = m_zoutButton->GetButtonBitmap();
1164 mdc.DrawBitmap(bmd, m_zoutButton->m_position.x,
1165 m_zoutButton->m_position.y,
false);
1168 if (m_scaleButton) {
1169 bmd = m_scaleButton->GetButtonBitmap();
1171 int bm_pos_y = (m_scaleButton->GetSize().y - bmd.GetHeight()) / 2;
1172 int bm_pos_x = m_scaleButton->m_position.x +
1173 (m_scaleButton->GetSize().x - bmd.GetWidth()) / 2;
1175 mdc.DrawBitmap(bmd, bm_pos_x, bm_pos_y,
false);
1179 if (m_followButton) {
1180 bmd = m_followButton->GetButtonBitmap();
1182 mdc.DrawBitmap(bmd, m_followButton->m_position.x,
1183 m_followButton->m_position.y,
false);
1187 bmd = m_menuButton->GetButtonBitmap();
1189 mdc.DrawBitmap(bmd, m_menuButton->m_position.x,
1190 m_menuButton->m_position.y,
false);
1193 mdc.SelectObject(wxNullBitmap);
1199void MUIBar::DrawGL(
ocpnDC& gldc,
double displayScale) {
1202 wxColour backColor = GetBackgroundColor();
1203 gldc.SetBrush(wxBrush(backColor));
1204 gldc.SetPen(wxPen(backColor));
1206 wxRect r = wxRect(m_screenPos, m_size);
1207 if (m_orientation == wxHORIZONTAL)
1208 gldc.DrawRoundedRectangle(
1209 (r.x - m_end_margin / 2) * displayScale, (r.y - 1) * displayScale,
1210 (r.width + m_end_margin) * displayScale, (r.height + 2) * displayScale,
1211 (m_end_margin * 1) * displayScale);
1213 gldc.DrawRoundedRectangle((r.x - 1) * displayScale,
1214 (r.y - m_end_margin / 2) * displayScale,
1215 (r.width + 2) * displayScale,
1216 (r.height + 2 * m_end_margin) * displayScale,
1217 (m_end_margin * 1.5) * displayScale);
1219 int width = m_size.x;
1220 int height = m_size.y;
1222 CreateBitmap(displayScale);
1226 glGenTextures(1, &m_texture);
1228 glBindTexture(g_texture_rectangle_format, m_texture);
1229 glTexParameterf(g_texture_rectangle_format, GL_TEXTURE_MIN_FILTER,
1231 glTexParameteri(g_texture_rectangle_format, GL_TEXTURE_MAG_FILTER,
1233 glTexParameteri(g_texture_rectangle_format, GL_TEXTURE_WRAP_S,
1235 glTexParameteri(g_texture_rectangle_format, GL_TEXTURE_WRAP_T,
1238 glBindTexture(g_texture_rectangle_format, m_texture);
1242 if (m_bitmap.IsOk()) {
1243 wxImage image = m_bitmap.ConvertToImage();
1245 unsigned char* d = image.GetData();
1247 unsigned char* e =
new unsigned char[4 * width * height];
1248 for (
int y = 0; y < height; y++)
1249 for (
int x = 0; x < width; x++) {
1250 int i = y * width + x;
1251 memcpy(e + 4 * i, d + 3 * i, 3);
1255 glTexImage2D(g_texture_rectangle_format, 0, GL_RGBA, width, height, 0,
1256 GL_RGBA, GL_UNSIGNED_BYTE, e);
1258 glDisable(g_texture_rectangle_format);
1259 glDisable(GL_BLEND);
1266 glEnable(g_texture_rectangle_format);
1267 glBindTexture(g_texture_rectangle_format, m_texture);
1270 int x0 = m_screenPos.x, x1 = x0 + width;
1271 int y0 = m_screenPos.y - 0, y1 = y0 + height;
1278 if (GL_TEXTURE_RECTANGLE_ARB == g_texture_rectangle_format)
1279 tx = width, ty = height;
1306 m_parentCanvas->GetglCanvas()->RenderTextures(gldc, coords, uv, 4,
1307 m_parentCanvas->GetpVP());
1309 glDisable(g_texture_rectangle_format);
1310 glBindTexture(g_texture_rectangle_format, 0);
1311 glDisable(GL_BLEND);
1318void MUIBar::DrawDC(
ocpnDC& dc,
double displayScale) {
1320 dc.DrawBitmap(m_bitmap, m_screenPos.x, m_screenPos.y,
false);
1323void MUIBar::ResetCanvasOptions() {
1324 delete m_canvasOptions;
1325 m_canvasOptions = NULL;
1328void MUIBar::PullCanvasOptions() {
1330 int cox = m_parentCanvas->GetSize().x - m_canvasOptionsFullSize.x;
1331 int coy = m_COTopOffset;
1332 m_targetCOPos = m_parentCanvas->ClientToScreen(wxPoint(cox, coy));
1335 m_canvasOptions->Move(m_targetCOPos);
1336 m_canvasOptions->Show();
1342 if (m_coAnimateByBitmaps && !m_animateBitmap.IsOk()) {
1343 m_canvasOptions->Move(m_targetCOPos);
1344 m_canvasOptions->Show();
1345 CaptureCanvasOptionsBitmap();
1351 m_startCOPos = m_canvasOptions->GetPosition();
1354 m_currentCOPos = m_startCOPos;
1356 m_animationType = CO_ANIMATION_CUBIC_REVERSE;
1357 m_animateSteps = 10;
1358 m_animationTotalTime = 200;
1360 m_pushPull = CO_PULL;
1361 auto pcc =
dynamic_cast<ChartCanvas*
>(m_parentCanvas);
1362 pcc->m_b_paint_enable =
false;
1366 m_canvasOptionsAnimationTimer.Start(10,
true);
1367 m_canvasOptions->Move(m_targetCOPos);
1368 m_canvasOptions->Hide();
1371void MUIBar::PushCanvasOptions() {
1373 m_canvasOptions->Hide();
1380 int cox = m_parentCanvas->GetSize().x;
1384 m_targetCOPos = m_parentCanvas->ClientToScreen(wxPoint(cox, coy));
1386 m_targetCOPos = wxPoint(cox, coy);
1389 m_startCOPos = m_canvasOptions->GetPosition();
1392 m_currentCOPos = m_startCOPos;
1395 m_animationType = CO_ANIMATION_LINEAR;
1397 m_animationTotalTime = 100;
1398 m_pushPull = CO_PUSH;
1399 auto pcc =
dynamic_cast<ChartCanvas*
>(m_parentCanvas);
1403 m_canvasOptionsAnimationTimer.Start(10,
true);
1404 m_canvasOptions->Show();
1407void MUIBar::onCanvasOptionsAnimationTimerEvent(wxTimerEvent& event) {
1408 double progress = m_animateStep / (double)m_animateSteps;
1409 double valueX = getValue(m_animationType, progress);
1411 double dx = (m_targetCOPos.x - m_startCOPos.x) * valueX;
1413 wxPoint newPos = wxPoint(m_startCOPos.x + dx, m_currentCOPos.y);
1416 if (m_pushPull == CO_PULL)
1419 size_x = (m_targetCOPos.x - m_startCOPos.x) - abs(dx);
1421 if (!m_coAnimateByBitmaps) {
1422 m_canvasOptions->SetSize(newPos.x, newPos.y, size_x, wxDefaultCoord,
1423 wxSIZE_USE_EXISTING);
1425 m_canvasOptions->Show();
1427 m_canvasOptions->Hide();
1432 if (m_backingBitmap.IsOk()) {
1433 wxMemoryDC mdc_back(m_backingBitmap);
1434 sdc.Blit(m_backingPoint.x, m_backingPoint.y,
1435 m_backingBitmap.GetWidth() - size_x,
1436 m_backingBitmap.GetHeight(), &mdc_back, 0, 0, wxCOPY);
1440 wxMemoryDC mdc(m_animateBitmap);
1441 sdc.Blit(newPos.x, newPos.y, size_x, m_animateBitmap.GetHeight(), &mdc, 0,
1443 mdc.SelectObject(wxNullBitmap);
1446 m_currentCOPos = newPos;
1448 double dt = m_animationTotalTime / m_animateSteps;
1450 if (m_animateStep++ < m_animateSteps + 1) {
1451 m_canvasOptionsAnimationTimer.Start(dt,
true);
1453 m_currentCOPos = m_targetCOPos;
1454 m_canvasOptions->Show(m_pushPull == CO_PULL);
1456 auto pcc =
dynamic_cast<ChartCanvas*
>(m_parentCanvas);
1458 pcc->m_b_paint_enable =
true;
1460 if (m_pushPull == CO_PUSH) {
1461 delete m_canvasOptions;
1462 m_canvasOptions = NULL;
1465 if (m_pushPull == CO_PUSH) pcc->TriggerDeferredFocus();
1467 pcc->TriggerDeferredFocus();
1477double bounceMaker(
double t,
double c,
double a) {
1478 if (t == 1.0)
return c;
1479 if (t < (4 / 11.0)) {
1480 return c * (7.5625 * t * t);
1481 }
else if (t < (8 / 11.0)) {
1483 return -a * (1. - (7.5625 * t * t + .75)) + c;
1484 }
else if (t < (10 / 11.0)) {
1486 return -a * (1. - (7.5625 * t * t + .9375)) + c;
1489 return -a * (1. - (7.5625 * t * t + .984375)) + c;
1493double getValue(
int animationType,
double t) {
1497 switch (animationType) {
1498 case CO_ANIMATION_LINEAR:
1502 case CO_ANIMATION_CUBIC:
1504 value = tp * tp * tp + 1;
1507 case CO_ANIMATION_CUBIC_REVERSE:
1509 value = tp * tp * tp + 1;
1511 case CO_ANIMATION_CUBIC_BOUNCE_IN:
1512 value = bounceMaker(t, 1, s);
1515 case CO_ANIMATION_CUBIC_BACK_IN:
1517 value = tp * tp * ((s + 1) * tp + s) + 1;
Class CanvasOptions and helpers – Canvas options Window/Dialog.
ChartCanvas * g_focusCanvas
Global instance.
Generic Chart canvas base.
Represents the Canvas Options dialog.
ChartCanvas - Main chart display and interaction component.
bool Create(wxWindow *parent, wxWindowID id=wxID_ANY, const wxString &caption=_("Set scale"), const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=wxDEFAULT_DIALOG_STYLE)
Creation.
wxTextCtrl * m_ScaleCtl
Should we show tooltips?
SetScaleDialog()
Constructors.
Device context class that can use either wxDC or OpenGL for drawing.
Global color handling by name.
Chart display details slider.
OpenGL chart rendering canvas.
PlugInManager and helper classes – Mostly gui parts (dialogs) and plugin API stuff.