35#include <wx/statline.h>
40#include "OCPNPlatform.h"
41#include "CanvasOptions.h"
42#include "DetailSlider.h"
43#include "GoToPositionDialog.h"
47#include "model/idents.h"
48#include "color_handler.h"
50#include "pluginmanager.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, _T(
""), wxDefaultPosition, wxDefaultSize, 0);
181 itemStaticBoxSizer4->Add(itemStaticText5, 0,
182 wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, 5);
184 m_ScaleCtl =
new wxTextCtrl(itemDialog1, ID_SCALECTRL, _T(
""),
185 wxDefaultPosition, wxSize(180, -1), 0);
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(_T(
"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);
823 if (g_focusCanvas) g_focusCanvas->TriggerDeferredFocus();
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);
833 if (g_focusCanvas) g_focusCanvas->TriggerDeferredFocus();
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);
845 if (g_focusCanvas) g_focusCanvas->TriggerDeferredFocus();
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)) {
854 if (g_focusCanvas) g_focusCanvas->TriggerDeferredFocus();
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->DoZoomCanvas(factor,
false);
892 displayScaleNow = pcc->GetScaleValue();
893 factor = displayScaleNow / dScale;
894 pcc->DoZoomCanvas(factor,
false);
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() + _T(
"uidata/MUI_flat/");
909 iconDir + _T(
"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 + _T(
"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 + _T(
"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;
944 m_followButton =
new MUIButton(m_parentCanvas, ID_FOLLOW, m_scaleFactor,
945 iconDir + _T(
"MUI_follow.svg"),
946 iconDir + _T(
"MUI_follow_active.svg"),
947 iconDir + _T(
"MUI_follow_ahead.svg"));
948 m_followButton->m_position = wxPoint(xoff, 0);
949 xoff += m_followButton->m_size.x;
952 m_menuButton =
new MUIButton(m_parentCanvas, ID_MUI_MENU, m_scaleFactor,
953 iconDir + _T(
"MUI_menu.svg"));
954 m_menuButton->m_position = wxPoint(xoff, 0);
955 xoff += m_menuButton->m_size.x;
957 m_size.y = button_size.y;
963 if (g_bShowMuiZoomButtons) {
964 m_zinButton =
new MUIButton(m_parentCanvas, ID_ZOOMIN, m_scaleFactor,
965 iconDir + _T(
"MUI_zoom-in.svg"));
966 m_zinButton->m_position = wxPoint(0, yoff);
967 yoff += m_zinButton->m_size.y;
969 m_zoutButton =
new MUIButton(m_parentCanvas, ID_ZOOMOUT, m_scaleFactor,
970 iconDir + _T(
"MUI_zoom-out.svg"));
971 m_zoutButton->m_position = wxPoint(0, yoff);
972 yoff += m_zoutButton->m_size.y;
975#ifndef __OCPN__ANDROID__
976 m_followButton =
new MUIButton(m_parentCanvas, ID_FOLLOW, m_scaleFactor,
977 iconDir + _T(
"MUI_follow.svg"),
978 iconDir + _T(
"MUI_follow_active.svg"),
979 iconDir + _T(
"MUI_follow_ahead.svg"));
980 m_followButton->m_position = wxPoint(0, yoff);
981 yoff += m_followButton->m_size.y;
984 m_menuButton =
new MUIButton(m_parentCanvas, ID_MUI_MENU, m_scaleFactor,
985 iconDir + _T(
"MUI_menu.svg"));
986 m_menuButton->m_position = wxPoint(0, yoff);
987 yoff += m_menuButton->m_size.y;
990 m_size.x = button_size.x;
994void MUIBar::SetBestPosition(
void) {
996 (m_parentCanvas->GetClientSize().x - (m_size.x + (m_end_margin) * 2.00));
998 int bottomOffset = 6;
1000 int y = m_parentCanvas->GetClientSize().y - m_size.y - bottomOffset;
1005 wxPoint position = wxPoint(x, y);
1006 m_screenPos = position;
1008 if (m_canvasOptions) {
1009 m_canvasOptions->Destroy();
1010 m_canvasOptions = 0;
1014void MUIBar::UpdateDynamicValues() {
1015 if (!m_scaleButton)
return;
1017 wxString scaleString;
1018 int scale = m_parentCanvas->GetScaleValue();
1020 if (
scale != m_scale) InvalidateBitmap();
1024 scaleString.Printf(_T(
"1:%d"),
scale);
1026 scaleString.Printf(_T(
"1:%4.1f M"),
scale / 1e6);
1028 if (m_scaleButton) m_scaleButton->SetText(scaleString);
1031void MUIBar::SetFollowButtonState(
int state) {
1032 if (m_followButton && m_followButton->GetState() != state) {
1033 m_followButton->SetState(state);
1038void MUIBar::HandleMenuClick() {
1039 if (!m_canvasOptions) {
1044 wxPoint parentClientUpperRight =
1045 m_parentCanvas->ClientToScreen(wxPoint(m_parentCanvas->GetSize().x, 0));
1046 wxPoint muibar_top = m_parentCanvas->ClientToScreen(m_screenPos);
1047 int size_y = muibar_top.y - (parentClientUpperRight.y + m_COTopOffset);
1048 size_y -= m_parentCanvas->GetCharHeight();
1049 size_y = wxMax(size_y, 100);
1051 m_canvasOptions->SetSize(wxSize(-1, size_y));
1052 m_canvasOptionsFullSize = m_canvasOptions->GetSize();
1053 m_canvasOptionsFullSize.x +=
1054 m_canvasOptions->GetCharWidth();
1057 m_currentCOPos = m_parentCanvas->ClientToScreen(
1058 wxPoint(m_parentCanvas->GetSize().x, m_COTopOffset));
1060 m_canvasOptions->Move(m_currentCOPos);
1061 m_canvasOptions->Hide();
1064 m_canvasOptions->SetENCAvailable(m_CanvasENCAvail);
1066 if (m_canvasOptions->IsShown())
1067 PushCanvasOptions();
1071 if (m_coAnimateByBitmaps && m_capture_size_y) {
1072 int overShoot_x = m_canvasOptions->GetSize().x * 2 / 10;
1074 wxPoint(m_capturePoint.x - overShoot_x, m_capturePoint.y);
1076 m_backingBitmap = wxBitmap(m_canvasOptionsFullSize.x + overShoot_x,
1077 m_capture_size_y, -1);
1079 mdcb.SelectObject(m_backingBitmap);
1081 mdcb.Blit(0, 0, m_canvasOptionsFullSize.x + overShoot_x, m_capture_size_y,
1082 &sdc, m_capturePoint.x - overShoot_x, m_capturePoint.y, wxCOPY);
1083 mdcb.SelectObject(wxNullBitmap);
1085 PullCanvasOptions();
1089void MUIBar::CaptureCanvasOptionsBitmap() {
1091 CanvasOptionTimer.Start(100, wxTIMER_ONE_SHOT);
1094void MUIBar::CaptureCanvasOptionsBitmapChain(wxTimerEvent& event) {
1095 if (m_coSequence == 0) {
1096 if (!m_canvasOptions) m_canvasOptions =
new CanvasOptions(m_parentCanvas);
1098 wxPoint parentClientUpperRight =
1099 m_parentCanvas->ClientToScreen(wxPoint(m_parentCanvas->GetSize().x, 0));
1100 wxRect rmui = m_parentCanvas->GetMUIBarRect();
1101 int size_y = rmui.y - (parentClientUpperRight.y + m_COTopOffset);
1102 size_y -= m_parentCanvas->GetCharHeight();
1103 size_y = wxMax(size_y, 100);
1104 m_capture_size_y = size_y;
1106 m_canvasOptions->SetSize(wxSize(-1, size_y));
1108 m_capturePoint = m_parentCanvas->ClientToScreen(
1109 wxPoint(m_parentCanvas->GetSize().x, m_COTopOffset));
1110 m_canvasOptions->Move(m_capturePoint);
1111 m_canvasOptions->Show();
1114 CanvasOptionTimer.Start(1, wxTIMER_ONE_SHOT);
1117 else if (m_coSequence == 1) {
1118 m_capturePoint = m_parentCanvas->ClientToScreen(
1119 wxPoint(m_parentCanvas->GetSize().x - m_canvasOptionsFullSize.x,
1121 m_canvasOptions->Move(m_capturePoint);
1124 CanvasOptionTimer.Start(1, wxTIMER_ONE_SHOT);
1127 else if (m_coSequence == 2) {
1129 wxBitmap(m_canvasOptions->GetSize().x, m_capture_size_y, -1);
1130 wxMemoryDC mdc(m_animateBitmap);
1134 mdc.Blit(0, 0, m_canvasOptions->GetSize().x, m_capture_size_y, &sdc,
1135 m_capturePoint.x, m_capturePoint.y, wxCOPY);
1136 mdc.SelectObject(wxNullBitmap);
1143wxBitmap& MUIBar::CreateBitmap(
double displayScale) {
1144 if (m_bitmap.IsOk())
return m_bitmap;
1147 int width = m_size.x;
1148 int height = m_size.y;
1151 wxBitmap bm(width, height);
1152 mdc.SelectObject(bm);
1153 mdc.SetBackground(wxBrush(GetBackgroundColor()));
1158 if (g_bShowMuiZoomButtons) {
1159 wxBitmap bmd = m_zinButton->GetButtonBitmap();
1161 mdc.DrawBitmap(bmd, m_zinButton->m_position.x, m_zinButton->m_position.y,
1164 bmd = m_zoutButton->GetButtonBitmap();
1166 mdc.DrawBitmap(bmd, m_zoutButton->m_position.x,
1167 m_zoutButton->m_position.y,
false);
1170 if (m_scaleButton) {
1171 bmd = m_scaleButton->GetButtonBitmap();
1173 int bm_pos_y = (m_scaleButton->GetSize().y - bmd.GetHeight()) / 2;
1174 int bm_pos_x = m_scaleButton->m_position.x +
1175 (m_scaleButton->GetSize().x - bmd.GetWidth()) / 2;
1177 mdc.DrawBitmap(bmd, bm_pos_x, bm_pos_y,
false);
1181 if (m_followButton) {
1182 bmd = m_followButton->GetButtonBitmap();
1184 mdc.DrawBitmap(bmd, m_followButton->m_position.x,
1185 m_followButton->m_position.y,
false);
1189 bmd = m_menuButton->GetButtonBitmap();
1191 mdc.DrawBitmap(bmd, m_menuButton->m_position.x,
1192 m_menuButton->m_position.y,
false);
1195 mdc.SelectObject(wxNullBitmap);
1201void MUIBar::DrawGL(
ocpnDC& gldc,
double displayScale) {
1204 wxColour backColor = GetBackgroundColor();
1205 gldc.SetBrush(wxBrush(backColor));
1206 gldc.SetPen(wxPen(backColor));
1208 wxRect r = wxRect(m_screenPos, m_size);
1209 if (m_orientation == wxHORIZONTAL)
1210 gldc.DrawRoundedRectangle(
1211 (r.x - m_end_margin / 2) * displayScale, (r.y - 1) * displayScale,
1212 (r.width + m_end_margin) * displayScale, (r.height + 2) * displayScale,
1213 (m_end_margin * 1) * displayScale);
1215 gldc.DrawRoundedRectangle((r.x - 1) * displayScale,
1216 (r.y - m_end_margin / 2) * displayScale,
1217 (r.width + 2) * displayScale,
1218 (r.height + 2 * m_end_margin) * displayScale,
1219 (m_end_margin * 1.5) * displayScale);
1221 int width = m_size.x;
1222 int height = m_size.y;
1224 CreateBitmap(displayScale);
1228 glGenTextures(1, &m_texture);
1230 glBindTexture(g_texture_rectangle_format, m_texture);
1231 glTexParameterf(g_texture_rectangle_format, GL_TEXTURE_MIN_FILTER,
1233 glTexParameteri(g_texture_rectangle_format, GL_TEXTURE_MAG_FILTER,
1235 glTexParameteri(g_texture_rectangle_format, GL_TEXTURE_WRAP_S,
1237 glTexParameteri(g_texture_rectangle_format, GL_TEXTURE_WRAP_T,
1240 glBindTexture(g_texture_rectangle_format, m_texture);
1244 if (m_bitmap.IsOk()) {
1245 wxImage image = m_bitmap.ConvertToImage();
1247 unsigned char* d = image.GetData();
1249 unsigned char* e =
new unsigned char[4 * width * height];
1250 for (
int y = 0; y < height; y++)
1251 for (
int x = 0; x < width; x++) {
1252 int i = y * width + x;
1253 memcpy(e + 4 * i, d + 3 * i, 3);
1257 glTexImage2D(g_texture_rectangle_format, 0, GL_RGBA, width, height, 0,
1258 GL_RGBA, GL_UNSIGNED_BYTE, e);
1260 glDisable(g_texture_rectangle_format);
1261 glDisable(GL_BLEND);
1268 glEnable(g_texture_rectangle_format);
1269 glBindTexture(g_texture_rectangle_format, m_texture);
1272 int x0 = m_screenPos.x, x1 = x0 + width;
1273 int y0 = m_screenPos.y - 0, y1 = y0 + height;
1280 if (GL_TEXTURE_RECTANGLE_ARB == g_texture_rectangle_format)
1281 tx = width, ty = height;
1308 m_parentCanvas->GetglCanvas()->RenderTextures(gldc, coords, uv, 4,
1309 m_parentCanvas->GetpVP());
1311 glDisable(g_texture_rectangle_format);
1312 glBindTexture(g_texture_rectangle_format, 0);
1313 glDisable(GL_BLEND);
1320void MUIBar::DrawDC(
ocpnDC& dc,
double displayScale) {
1322 dc.DrawBitmap(m_bitmap, m_screenPos.x, m_screenPos.y,
false);
1325void MUIBar::ResetCanvasOptions() {
1326 delete m_canvasOptions;
1327 m_canvasOptions = NULL;
1330void MUIBar::PullCanvasOptions() {
1332 int cox = m_parentCanvas->GetSize().x - m_canvasOptionsFullSize.x;
1333 int coy = m_COTopOffset;
1334 m_targetCOPos = m_parentCanvas->ClientToScreen(wxPoint(cox, coy));
1337 m_canvasOptions->Move(m_targetCOPos);
1338 m_canvasOptions->Show();
1344 if (m_coAnimateByBitmaps && !m_animateBitmap.IsOk()) {
1345 m_canvasOptions->Move(m_targetCOPos);
1346 m_canvasOptions->Show();
1347 CaptureCanvasOptionsBitmap();
1353 m_startCOPos = m_canvasOptions->GetPosition();
1356 m_currentCOPos = m_startCOPos;
1358 m_animationType = CO_ANIMATION_CUBIC_REVERSE;
1359 m_animateSteps = 10;
1360 m_animationTotalTime = 200;
1362 m_pushPull = CO_PULL;
1363 auto pcc =
dynamic_cast<ChartCanvas*
>(m_parentCanvas);
1364 pcc->m_b_paint_enable =
false;
1368 m_canvasOptionsAnimationTimer.Start(10,
true);
1369 m_canvasOptions->Move(m_targetCOPos);
1370 m_canvasOptions->Hide();
1373void MUIBar::PushCanvasOptions() {
1375 m_canvasOptions->Hide();
1382 int cox = m_parentCanvas->GetSize().x;
1386 m_targetCOPos = m_parentCanvas->ClientToScreen(wxPoint(cox, coy));
1388 m_targetCOPos = wxPoint(cox, coy);
1391 m_startCOPos = m_canvasOptions->GetPosition();
1394 m_currentCOPos = m_startCOPos;
1397 m_animationType = CO_ANIMATION_LINEAR;
1399 m_animationTotalTime = 100;
1400 m_pushPull = CO_PUSH;
1401 auto pcc =
dynamic_cast<ChartCanvas*
>(m_parentCanvas);
1405 m_canvasOptionsAnimationTimer.Start(10,
true);
1406 m_canvasOptions->Show();
1409void MUIBar::onCanvasOptionsAnimationTimerEvent(wxTimerEvent& event) {
1410 double progress = m_animateStep / (double)m_animateSteps;
1411 double valueX = getValue(m_animationType, progress);
1413 double dx = (m_targetCOPos.x - m_startCOPos.x) * valueX;
1415 wxPoint newPos = wxPoint(m_startCOPos.x + dx, m_currentCOPos.y);
1418 if (m_pushPull == CO_PULL)
1421 size_x = (m_targetCOPos.x - m_startCOPos.x) - abs(dx);
1423 if (!m_coAnimateByBitmaps) {
1424 m_canvasOptions->SetSize(newPos.x, newPos.y, size_x, wxDefaultCoord,
1425 wxSIZE_USE_EXISTING);
1427 m_canvasOptions->Show();
1429 m_canvasOptions->Hide();
1434 if (m_backingBitmap.IsOk()) {
1435 wxMemoryDC mdc_back(m_backingBitmap);
1436 sdc.Blit(m_backingPoint.x, m_backingPoint.y,
1437 m_backingBitmap.GetWidth() - size_x,
1438 m_backingBitmap.GetHeight(), &mdc_back, 0, 0, wxCOPY);
1442 wxMemoryDC mdc(m_animateBitmap);
1443 sdc.Blit(newPos.x, newPos.y, size_x, m_animateBitmap.GetHeight(), &mdc, 0,
1445 mdc.SelectObject(wxNullBitmap);
1448 m_currentCOPos = newPos;
1450 double dt = m_animationTotalTime / m_animateSteps;
1452 if (m_animateStep++ < m_animateSteps + 1) {
1453 m_canvasOptionsAnimationTimer.Start(dt,
true);
1455 m_currentCOPos = m_targetCOPos;
1456 m_canvasOptions->Show(m_pushPull == CO_PULL);
1458 auto pcc =
dynamic_cast<ChartCanvas*
>(m_parentCanvas);
1460 pcc->m_b_paint_enable =
true;
1462 if (m_pushPull == CO_PUSH) {
1463 delete m_canvasOptions;
1464 m_canvasOptions = NULL;
1467 if (m_pushPull == CO_PUSH) pcc->TriggerDeferredFocus();
1469 pcc->TriggerDeferredFocus();
1479double bounceMaker(
double t,
double c,
double a) {
1480 if (t == 1.0)
return c;
1481 if (t < (4 / 11.0)) {
1482 return c * (7.5625 * t * t);
1483 }
else if (t < (8 / 11.0)) {
1485 return -a * (1. - (7.5625 * t * t + .75)) + c;
1486 }
else if (t < (10 / 11.0)) {
1488 return -a * (1. - (7.5625 * t * t + .9375)) + c;
1491 return -a * (1. - (7.5625 * t * t + .984375)) + c;
1495double getValue(
int animationType,
double t) {
1499 switch (animationType) {
1500 case CO_ANIMATION_LINEAR:
1504 case CO_ANIMATION_CUBIC:
1506 value = tp * tp * tp + 1;
1509 case CO_ANIMATION_CUBIC_REVERSE:
1511 value = tp * tp * tp + 1;
1513 case CO_ANIMATION_CUBIC_BOUNCE_IN:
1514 value = bounceMaker(t, 1, s);
1517 case CO_ANIMATION_CUBIC_BACK_IN:
1519 value = tp * tp * ((s + 1) * tp + s) + 1;
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.