35#include <wx/statline.h>
40#include "OCPNPlatform.h"
42#include "DetailSlider.h"
43#include "GoToPositionDialog.h"
47#include "model/idents.h"
53#include "glChartCanvas.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.
PlugInManager and helper classes – Mostly gui parts (dialogs) and plugin API stuff.