60#include <wx/dcclient.h>
61#include <wx/dcscreen.h>
194 (
double min,
double max,
double hiddenMin,
double hiddenMax)
259 wxCoord &height, wxCoord &lead, wxDC &dc,
const wxFont &font )
261 wxCoord strW, strH, strD, strL;
262 static const wxString exampleText =
wxT(
"0.9");
264 dc.GetTextExtent(exampleText, &strW, &strH, &strD, &strL);
265 height = strH - strD - strL;
270 wxCoord &height, wxCoord &lead,
271 wxDC &dc,
int fontSize, wxFontWeight weight = wxFONTWEIGHT_NORMAL )
273 const wxFont font{ fontSize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, weight };
278void Ruler::SetFonts(
const wxFont &minorFont,
const wxFont &majorFont,
const wxFont &minorMinorFont)
283 Fonts{ majorFont, minorFont, minorMinorFont, 0 } );
312 auto size =
static_cast<size_t>( length + 1 );
326 for(
int i = start; i <=
end; i++)
386 double units = ((orientation == wxHORIZONTAL) ? 22 : 16) * fabs(UPP);
514 if (units < 1800.0) {
519 if (units < 3600.0) {
524 if (units < 6*3600.0) {
529 if (units < 24*3600.0) {
535 mMinor = 24.0 * 7.0 * 3600.0;
536 mMajor = 24.0 * 7.0 * 3600.0;
622 s.Printf(
wxT(
"%d"), (
int)floor(d+0.5));
626 s.Printf(
wxT(
"%d"), (
int)floor(d+0.5));
628 int precision = -log10(
mMinor);
629 s.Printf(
wxT(
"%.*f"), precision, d);
634 s.Printf(
wxT(
"%d"), (
int)floor(d+0.5));
636 s.Printf(wxString::Format(
wxT(
"%%.%df"),
mDigits), d);
641 s.Printf(
wxT(
"%d"), (
int)floor(d+0.5));
643 s.Printf(wxString::Format(
wxT(
"%%.%df"),
mDigits), d);
654 int secs = (int)(d + 0.5);
656 s.Printf(
wxT(
"%d:%02d:%02d"), secs/3600, (secs/60)%60, secs%60);
660 t1.Printf(
wxT(
"%d:%02d:"), secs/3600, (secs/60)%60);
662 t2.Printf(
format, fmod(d, 60.0));
669 int hrs = (int)(d / 3600.0 + 0.5);
671 h.Printf(
wxT(
"%d:00:00"), hrs);
674 else if (
mMinor >= 60.0) {
675 int minutes = (int)(d / 60.0 + 0.5);
678 m.Printf(
wxT(
"%d:%02d:00"), minutes/60, minutes%60);
680 m.Printf(
wxT(
"%d:00"), minutes);
684 int secs = (int)(d + 0.5);
687 t.Printf(
wxT(
"%d:%02d:%02d"), secs/3600, (secs/60)%60, secs%60);
689 t.Printf(
wxT(
"%d:%02d"), secs/60, secs%60);
691 t.Printf(
wxT(
"%d"), secs);
701 int secs = (int)(
float)(d);
705 t1.Printf(
wxT(
"%d:%02d:"), secs/3600, (secs/60)%60);
707 t1.Printf(
wxT(
"%d:"), secs/60);
715 t2.Printf(
format, fmod((
float)d, (float)60.0));
721 double dd = d * 1.000000000000001;
722 int secs = (int)(dd);
726 t1.Printf(
wxT(
"%d:%02d:"), secs/3600, (secs/60)%60);
728 t1.Printf(
wxT(
"%d:"), secs/60);
735 dd = dd - secs + (secs%60);
738 double multiplier = pow( 10,
mDigits);
739 dd = ((int)(dd * multiplier))/multiplier;
760 wxDC &dc, wxFont font,
761 std::vector<bool> &bits,
762 int left,
int top,
int spacing,
int lead,
763 bool flip,
int orientation )
764 -> std::pair< wxRect, Label >
766 lab.lx = left - 1000;
769 auto length = bits.size() - 1;
774 wxCoord strW, strH, strD, strL;
778 dc.GetTextExtent(
str.Translation(), &strW, &strH, &strD, &strL);
780 int strPos, strLen, strLeft, strTop;
781 if ( orientation == wxHORIZONTAL ) {
783 strPos = pos - strW/2;
786 if (strPos + strW >= length)
787 strPos = length - strW;
788 strLeft = left + strPos;
792 strTop = -strH - lead;
797 strPos = pos - strH/2;
800 if (strPos + strH >= length)
801 strPos = length - strH;
802 strTop = top + strPos;
819 for(i=0; i<strLen; i++)
820 if ( bits[strPos+i] )
830 int leftMargin = spacing;
831 if (strPos < leftMargin)
833 strPos -= leftMargin;
834 strLen += leftMargin;
836 int rightMargin = spacing;
837 if (strPos + strLen > length - spacing)
838 rightMargin = length - strPos - strLen;
839 strLen += rightMargin;
841 for(i=0; i<strLen; i++)
842 bits[strPos+i] =
true;
846 return { { strLeft, strTop, strW, strH }, lab };
886 int pos,
double d,
const TickSizes &tickSizes, wxFont font,
891 bool TickCustom( wxDC &dc,
int labelIdx, wxFont font,
896 std::unique_ptr<Fonts> &pFonts,
const Fonts *pUserFonts,
897 wxDC &dc,
int desiredPixelHeight );
927 int pos,
double d,
const TickSizes &tickSizes, wxFont font,
953 auto &rect = result.first;
954 outputs.
box.Union( rect );
955 outputs.
labels.emplace_back( result.second );
956 return !rect.IsEmpty();
965 if( labelIdx >= outputs.
labels.size() )
982 auto &rect = result.first;
983 outputs.
box.Union( rect );
984 outputs.
labels[labelIdx] = ( result.second );
985 return !rect.IsEmpty();
1018 std::unique_ptr<Fonts> &pFonts,
const Fonts *pUserFonts,
1019 wxDC &dc,
int desiredPixelHeight )
1025 pFonts = std::make_unique<Fonts>( *pUserFonts );
1029 pFonts = std::make_unique<Fonts>(
Fonts{ {}, {}, {}, 0 } );
1030 auto &fonts = *pFonts;
1034 desiredPixelHeight =
1039 FindFontHeights( height, fonts.lead, dc, fontSize, wxFONTWEIGHT_BOLD );
1040 while (height <= desiredPixelHeight && fontSize < 40) {
1042 FindFontHeights( height, fonts.lead, dc, fontSize, wxFONTWEIGHT_BOLD );
1047 fonts.major = wxFont{ fontSize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD };
1048 fonts.minor = wxFont{ fontSize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL };
1049 fonts.minorMinor = wxFont{ fontSize - 1, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL };
1062 for(
int i = 0; (i<numLabel) && (i<=
mLength); ++i )
1063 TickCustom( dc, i, mFonts.major, majorOutputs );
1080 [
this, &tickSizes, &dc, &majorOutputs]
1081 (
double value ) ->
int {
1085 if ( value >= std::max(
mMin,
mMax ) )
1089 if (zoomInfo != NULL) {
1093 mid = (int)(zoomInfo->TimeToPosition(0.0,
mLeftOffset));
1099 if (mid >= 0 && mid < iMaxPos)
1100 Tick( dc, mid, value, tickSizes, mFonts.major, majorOutputs );
1123 Tick( dc, 0,
mMin, tickSizes, mFonts.major, majorOutputs );
1124 Tick( dc,
mLength,
mMax, tickSizes, mFonts.major, majorOutputs );
1132 double sg = UPP > 0.0? 1.0: -1.0;
1134 int nDroppedMinorLabels=0;
1136 for (
int jj = 0; jj < 2; ++jj) {
1137 const double denom = jj == 0 ? tickSizes.mMajor : tickSizes.mMinor;
1138 auto font = jj == 0 ? mFonts.major : mFonts.minor;
1141 allOutputs.
bits, allOutputs.
box
1144 double d, warpedD, nextD;
1146 double prevTime = 0.0, time = 0.0;
1147 if (zoomInfo != NULL) {
1148 j = zoomInfo->TimeToPosition(
mMin);
1149 prevTime = zoomInfo->PositionToTime(--j);
1150 time = zoomInfo->PositionToTime(++j);
1151 d = (prevTime + time) / 2.0;
1161 double step = floor(sg * warpedD / denom);
1167 time = zoomInfo->PositionToTime(++j);
1168 nextD = (prevTime + time) / 2.0;
1179 if (floor(sg * warpedD / denom) > step) {
1180 step = floor(sg * warpedD / denom);
1181 bool major = jj == 0;
1182 tickSizes.useMajor = major;
1183 bool ticked = Tick( dc, ii, sg * step * denom, tickSizes,
1185 if( !major && !ticked ){
1186 nDroppedMinorLabels++;
1192 tickSizes.useMajor =
true;
1197 if( nDroppedMinorLabels >
1208 Tick( dc, 0,
mMin, tickSizes, mFonts.major, majorOutputs );
1209 Tick( dc,
mLength,
mMax, tickSizes, mFonts.major, majorOutputs );
1226 tickSizes.mDigits = 2;
1228 double loLog = log10(
mMin);
1229 double hiLog = log10(
mMax);
1230 int loDecade = (int) floor(loLog);
1233 double startDecade = pow(10., (
double)loDecade);
1236 double decade = startDecade;
1237 double delta=hiLog-loLog, steps=fabs(delta);
1238 double step = delta>=0 ? 10 : 0.1;
1240 for(
int i=0; i<=steps; i++)
1243 if(val >= rMin && val < rMax) {
1244 const int pos(0.5 +
mLength * numberScale.ValueToPosition(val));
1245 Tick( dc, pos, val, tickSizes, mFonts.major, majorOutputs );
1252 decade = startDecade;
1253 float start,
end, mstep;
1255 { start=2;
end=10; mstep=1;
1257 { start=9;
end=1; mstep=-1;
1260 tickSizes.useMajor =
false;
1263 for(
int i=0; i<=steps; i++) {
1264 for(
int j=start; j!=
end; j+=mstep) {
1266 if(val >= rMin && val < rMax) {
1267 const int pos(0.5 +
mLength * numberScale.ValueToPosition(val));
1268 Tick( dc, pos, val, tickSizes, mFonts.minor, minorOutputs );
1275 decade = startDecade;
1277 { start= 10;
end=100; mstep= 1;
1279 { start=100;
end= 10; mstep=-1;
1284 for (
int i = 0; i <= steps; i++) {
1287 for (
int f = start; f != (int)(
end); f += mstep) {
1288 if ((
int)(f / 10) != f / 10.0f) {
1289 val = decade * f / 10;
1290 if (val >= rMin && val < rMax) {
1291 const int pos(0.5 +
mLength * numberScale.ValueToPosition(val));
1292 Tick( dc, pos, val, tickSizes,
1293 mFonts.minorMinor, minorMinorOutputs );
1303 wxDC &dc,
const Envelope* envelope,
1312 UpdateCustom( dc, allOutputs );
1314 UpdateLinear( dc, envelope, allOutputs );
1316 UpdateNonlinear( dc, allOutputs );
1318 int displacementx=0, displacementy=0;
1319 auto &box = allOutputs.
box;
1322 int d =
mTop + box.GetHeight() + 5;
1329 int d =
mLeft - box.GetLeft() + 5;
1344 label.lx += displacementx;
1345 label.ly += displacementy;
1365 wxDC &dc,
const Envelope* envelope )
1380 mpCache = std::make_unique< Cache >();
1391 cache.mRect = { 0, 0,
mLength, 0 };
1393 cache.mRect = { 0, 0, 0,
mLength };
1400 cache.mMajorLabels.clear();
1401 cache.mMinorLabels.clear();
1402 cache.mMinorMinorLabels.clear();
1406 cache.mBits.resize(
static_cast<size_t>(
mLength + 1),
false );
1410 const Updater updater{ *
this, zoomInfo };
1412 cache.mMajorLabels, cache.mMinorLabels, cache.mMinorMinorLabels,
1413 cache.mBits, cache.mRect
1415 updater.Update(dc, envelope, allOutputs);
1442#ifdef EXPERIMENTAL_THEMING
1445 dc.SetPen(*wxBLACK_PEN);
1464 const int nLineX =
mRight - 1;
1478 auto drawLabel = [
this, iMaxPos, &dc](
const Label &
label,
int length ){
1479 int pos =
label.pos;
1504 for(
const auto &
label : cache.mMajorLabels )
1505 drawLabel(
label, 4 );
1509 for(
const auto &
label : cache.mMinorLabels )
1510 drawLabel(
label, 2 );
1513 dc.SetFont(
mpFonts->minorMinor );
1515 for(
const auto &
label : cache.mMinorMinorLabels )
1517 drawLabel(
label, 2 );
1522 const int gridLineLength,
1523 const bool minorGrid,
const bool majorGrid,
int xOffset,
int yOffset)
1532 if(
mbMinor && (minorGrid && (gridLineLength != 0 ))) {
1533 gridPen.SetColour(178, 178, 178);
1535 for(
const auto &
label : cache.mMinorLabels ) {
1536 gridPos =
label.pos;
1538 if((gridPos != 0) && (gridPos != gridLineLength))
1539 AColor::Line(dc, gridPos+xOffset, yOffset, gridPos+xOffset, gridLineLength-1+yOffset);
1542 if((gridPos != 0) && (gridPos != gridLineLength))
1543 AColor::Line(dc, xOffset, gridPos+yOffset, gridLineLength-1+xOffset, gridPos+yOffset);
1548 if(majorGrid && (gridLineLength != 0 )) {
1549 gridPen.SetColour(127, 127, 127);
1551 for(
const auto &
label : cache.mMajorLabels ) {
1552 gridPos =
label.pos;
1554 if((gridPos != 0) && (gridPos != gridLineLength))
1555 AColor::Line(dc, gridPos+xOffset, yOffset, gridPos+xOffset, gridLineLength-1+yOffset);
1558 if((gridPos != 0) && (gridPos != gridLineLength))
1559 AColor::Line(dc, xOffset, gridPos+yOffset, gridLineLength-1+xOffset, gridPos+yOffset);
1564 if(zeroPosition > 0) {
1566 dc.SetPen(*wxBLACK_PEN);
1568 if(zeroPosition != gridLineLength)
1569 AColor::Line(dc, zeroPosition+xOffset, yOffset, zeroPosition+xOffset, gridLineLength-1+yOffset);
1572 if(zeroPosition != gridLineLength)
1573 AColor::Line(dc, xOffset, zeroPosition+yOffset, gridLineLength-1+xOffset, zeroPosition+yOffset);
1581 auto begin = labels.begin(),
end = labels.end(),
1583 return label.value == 0.0;
1597 if( (zero =
FindZero( cache.mMajorLabels ) ) < 0)
1598 zero =
FindZero( cache.mMinorLabels );
1612 *width = cache.mRect.GetWidth();
1615 *height = cache.mRect.GetHeight();
1634 mpCache = std::make_unique<Cache>();
1636 auto &mMajorLabels = cache.mMajorLabels;
1638 const auto numLabel = labels.size();
1639 mMajorLabels.resize( numLabel );
1641 for(
size_t i = 0; i<numLabel; i++) {
1642 mMajorLabels[i].text = labels[i];
1643 mMajorLabels[i].pos = start + i*step;
1651 mpCache = std::make_unique<Cache>();
1653 auto &mMinorLabels = cache.mMinorLabels;
1655 const auto numLabel = labels.size();
1656 mMinorLabels.resize( numLabel );
1658 for(
size_t i = 0; i<numLabel; i++) {
1659 mMinorLabels[i].text = labels[i];
1660 mMinorLabels[i].pos = start + i*step;
1667 if (!text.empty()) {
1668 bool altColor = twoTone && value < 0.0;
1670#ifdef EXPERIMENTAL_THEMING
1671 dc.SetTextForeground(altColor ?
theTheme.
Colour( clrTextNegativeNumbers) : c);
1673 dc.SetTextForeground(altColor ? *wxBLUE : *wxBLACK);
1675 dc.SetBackgroundMode(wxTRANSPARENT);
1676 dc.DrawText(text.Translation(), lx, ly);
1706 wxOrientation orientation,
1707 const wxSize &bounds,
1712 const wxPoint& pos ,
1713 const wxSize&
size ):
1725 if (orientation == wxVERTICAL) {
1728 SetMinSize(wxSize(w, 150));
1730 else if (orientation == wxHORIZONTAL) {
1733 SetMinSize(wxSize(wxDefaultCoord, h));
1735 if (options.hasTickColour)
1752#if defined(__WXMSW__)
1769 int width,
int height,
1772 wxPanelWrapper::DoSetSize(x, y, width, height, sizeFlags);
1775 GetClientSize(&w, &h);
static constexpr int MaxPixelHeight
static constexpr int MinPixelHeight
TranslatableString Verbatim(wxString str)
Require calls to the one-argument constructor to go through this distinct global function name.
std::vector< TranslatableString > TranslatableStrings
static void Line(wxDC &dc, wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
Piecewise linear or piecewise exponential function from double to double.
double SolveIntegralOfInverse(double t0, double area) const
double IntegralOfInverse(double t0, double t1) const
An array of these created by the Ruler is used to determine what and where text annotations to the nu...
void Draw(wxDC &dc, bool twoTone, wxColour c) const
void SetTickColour(const wxColour &colour)
TranslatableString mUnits
void OfflimitsPixels(int start, int end)
std::unique_ptr< Cache > mpCache
std::vector< Label > Labels
const ZoomInfo * mUseZoomInfo
void UpdateCache(wxDC &dc, const Envelope *envelope) const
void DrawGrid(wxDC &dc, int length, bool minor=true, bool major=true, int xOffset=0, int yOffset=0) const
void SetFonts(const wxFont &minorFont, const wxFont &majorFont, const wxFont &minorMinorFont)
void SetSpacing(int spacing)
void ChooseFonts(wxDC &dc) const
void SetNumberScale(const NumberScale &scale)
void SetOrientation(int orient)
int FindZero(const Labels &labels) const
void SetCustomMinorLabels(const TranslatableStrings &labels, int start, int step)
void SetUseZoomInfo(int leftOffset, const ZoomInfo *zoomInfo)
void Draw(wxDC &dc) const
void SetLabelEdges(bool labelEdges)
void SetCustomMode(bool value)
void GetMaxSize(wxCoord *width, wxCoord *height)
int GetZeroPosition() const
void SetBounds(int left, int top, int right, int bottom)
void SetUnits(const TranslatableString &units)
std::unique_ptr< Fonts > mpUserFonts
void SetRange(double min, double max)
void SetDbMirrorValue(const double d)
void SetFormat(RulerFormat format)
std::unique_ptr< Fonts > mpFonts
void SetCustomMajorLabels(const TranslatableStrings &labels, int start, int step)
void SetTwoTone(bool twoTone)
static std::pair< wxRect, Label > MakeTick(Label lab, wxDC &dc, wxFont font, std::vector< bool > &bits, int left, int top, int spacing, int lead, bool flip, int orientation)
void SetMinor(bool value)
RulerPanel class allows you to work with a Ruler like any other wxWindow.
void DoSetSize(int x, int y, int width, int height, int sizeFlags=wxSIZE_AUTO) override
void OnSize(wxSizeEvent &evt)
std::pair< double, double > Range
void OnPaint(wxPaintEvent &evt)
void OnErase(wxEraseEvent &evt)
wxColour & Colour(int iIndex)
Holds a msgid for the translation catalog; may also bind format arguments.
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
auto begin(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
void swap(std::unique_ptr< Alg_seq > &a, std::unique_ptr< Alg_seq > &b)
void FindFontHeights(wxCoord &height, wxCoord &lead, wxDC &dc, int fontSize, wxFontWeight weight=wxFONTWEIGHT_NORMAL)
double ComputeWarpedLength(const Envelope &env, double t0, double t1)
double SolveWarpedLength(const Envelope &env, double t0, double length)
TranslatableString LabelString(double d, RulerFormat format, const TranslatableString &units) const
TickSizes(double UPP, int orientation, RulerFormat format, bool log)
Labels & minorMinorLabels
bool TickCustom(wxDC &dc, int labelIdx, wxFont font, TickOutputs outputs) const
static void ChooseFonts(std::unique_ptr< Fonts > &pFonts, const Fonts *pUserFonts, wxDC &dc, int desiredPixelHeight)
bool Tick(wxDC &dc, int pos, double d, const TickSizes &tickSizes, wxFont font, TickOutputs outputs) const
const NumberScale mNumberScale
void UpdateNonlinear(wxDC &dc, UpdateOutputs &allOutputs) const
const RulerFormat mFormat
const double mDbMirrorValue
void UpdateCustom(wxDC &dc, UpdateOutputs &allOutputs) const
void UpdateLinear(wxDC &dc, const Envelope *envelope, UpdateOutputs &allOutputs) const
void Update(wxDC &dc, const Envelope *envelope, UpdateOutputs &allOutputs) const
Updater(const Ruler &ruler, const ZoomInfo *z)
const ZoomInfo * zoomInfo
const TranslatableString mUnits