Audacity 3.2.0
RulerUpdater.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 RulerUpdater.cpp
6
7 Dominic Mazzoni
8 Michael Papadopoulos split from Ruler.cpp
9
10*******************************************************************//***************************************************************//******************************************************************/
27
28#include "RulerUpdater.h"
29
30#include "AllThemeResources.h"
31#include "Theme.h"
32
33#include <wx/dc.h>
34
36 double UPP, int orientation, const RulerFormat *format, bool log)
37 {
38 //TODO: better dynamic digit computation for the log case
39 (void)log;
40
41 // Given the dimensions of the ruler, the range of values it
42 // has to display, and the format (i.e. Int, Real, Time),
43 // figure out how many units are in one Minor tick, and
44 // in one Major tick.
45 //
46 // The goal is to always put tick marks on nice round numbers
47 // that are easy for humans to grok. This is the most tricky
48 // with time.
49
50 // As a heuristic, we want at least 22 pixels between each
51 // minor tick. We want to show numbers like "-48"
52 // in that space.
53 // If vertical, we don't need as much space.
54 mUnits = ((orientation == wxHORIZONTAL) ? 22 : 16) * fabs(UPP);
55
56 mDigits = 0;
57
58 if (format)
59 format->SetTickSizes(mUnits, mMajor, mMinor, mMinorMinor, mDigits);
60 }
61
63 double d, const RulerFormat *format) const
64 {
65 // Given a value, turn it into a string according
66 // to the current ruler format. The number of digits of
67 // accuracy depends on the resolution of the ruler,
68 // i.e. how far zoomed in or out you are.
69
70 // Should not be called unless TickSizes is instantiated
71 wxASSERT(mUnits > 0);
72
73 wxString s;
74
75 // PRL Todo: are all these cases properly localized? (Decimal points,
76 // hour-minute-second, etc.?)
77
78 if (format)
79 format->SetLabelString(s, d, mUnits, mMinor, mDigits, tickType);
80
81 auto result = Verbatim(s);
82
83 return result;
84 }
85
87 wxDC& dc, bool twoTone, wxColour c,
88 std::unique_ptr<RulerStruct::Fonts>& fonts) const
89{
90 if (text.has_value() && !text->empty()) {
91 bool altColor = twoTone && value < 0.0;
92
93#ifdef EXPERIMENTAL_THEMING
94 dc.SetTextForeground(altColor ? theTheme.Colour(clrTextNegativeNumbers) : c);
95#else
96 dc.SetTextForeground(altColor ? *wxBLUE : *wxBLACK);
97#endif
98 dc.SetBackgroundMode(wxTRANSPARENT);
99 if (dc.GetFont() == fonts->major) {
100 // Do not draw units as bolded
101 dc.DrawText(text->Translation(), lx, ly);
102 wxSize textSize = dc.GetTextExtent(text->Translation());
103 dc.SetFont(fonts->minor);
104 int unitX = lx + textSize.GetWidth();
105 dc.DrawText(units.Translation(), unitX, ly);
106 dc.SetFont(fonts->major);
107 }
108 else {
109 auto str = *text + units;
110 dc.DrawText(str.Translation(), lx, ly);
111 }
112 }
113}
114
116 Label lab,
117 wxDC& dc, wxFont font,
118 std::vector<bool>& bits,
119 int left, int top, int spacing, int lead,
120 bool flip, int orientation)
121 -> std::pair< wxRect, Label >
122{
123 lab.lx = left - 1000; // don't display
124 lab.ly = top - 1000; // don't display
125
126 auto length = bits.size() - 1;
127 auto pos = lab.pos;
128
129 dc.SetFont(font);
130
131 wxCoord strW, strH, strD, strL;
132 auto strText = lab.text;
133 auto strUnits = lab.units;
134 auto str = (strText ? *strText : TranslatableString{}) + strUnits;
135 // Do not put the text into results until we are sure it does not overlap
136 lab.text = {};
137 lab.units = {};
138 dc.GetTextExtent(str.Translation(), &strW, &strH, &strD, &strL);
139
140 int strPos, strLen, strLeft, strTop;
141 if (orientation == wxHORIZONTAL) {
142 strLen = strW;
143 strPos = pos - strW / 2;
144 if (strPos < 0)
145 strPos = 0;
146 if (strPos + strW >= length)
147 strPos = length - strW;
148 strLeft = left + strPos;
149 if (flip)
150 strTop = top + 4;
151 else
152 strTop = -strH - lead;
153 // strTop = top - lead + 4;// More space was needed...
154 }
155 else {
156 strLen = strH;
157 strPos = pos - strH / 2;
158 if (strPos < 0)
159 strPos = 0;
160 if (strPos + strH >= length)
161 strPos = length - strH;
162 strTop = top + strPos;
163 if (flip)
164 strLeft = left + 5;
165 else
166 strLeft = -strW - 6;
167 }
168
169 // FIXME: we shouldn't even get here if strPos < 0.
170 // Ruler code currently does not handle very small or
171 // negative sized windows (i.e. don't draw) properly.
172 if (strPos < 0)
173 return { {}, lab };
174
175 // See if any of the pixels we need to draw this
176 // label is already covered
177
178 int i;
179 for (i = 0; i < strLen; i++)
180 if (bits[strPos + i])
181 return { {}, lab };
182
183 // If not, position the label
184
185 lab.lx = strLeft;
186 lab.ly = strTop;
187
188 // And mark these pixels, plus some surrounding
189 // ones (the spacing between labels), as covered
190 int leftMargin = spacing;
191 if (strPos < leftMargin)
192 leftMargin = strPos;
193 strPos -= leftMargin;
194 strLen += leftMargin;
195
196 int rightMargin = spacing;
197 if (strPos + strLen > length - spacing)
198 rightMargin = length - strPos - strLen;
199 strLen += rightMargin;
200
201 for (i = 0; i < strLen; i++)
202 bits[strPos + i] = true;
203
204 // Good to display the text
205 lab.text = strText;
206 lab.units = strUnits;
207 return { { strLeft, strTop, strW, strH }, lab };
208}
209
210// Formerly the ending part of Ruler::Updater::Update after a switch
211// on mCustom and mLog; now a base class function used in
212// overrides of RulerUpdater::Update as the common ending step
214 UpdateOutputs& allOutputs,
215 const RulerStruct& context
216)
217const
218{
219 const int mLeft = context.mLeft;
220 const int mTop = context.mTop;
221 const int mBottom = context.mBottom;
222 const int mRight = context.mRight;
223 const int mOrientation = context.mOrientation;
224 const bool mFlip = context.mFlip;
225
226 int displacementx = 0, displacementy = 0;
227 auto& box = allOutputs.box;
228 if (!mFlip) {
229 if (mOrientation == wxHORIZONTAL) {
230 int d = mTop + box.GetHeight() + 5;
231 box.Offset(0, d);
232 box.Inflate(0, 5);
233 displacementx = 0;
234 displacementy = d;
235 }
236 else {
237 int d = mLeft - box.GetLeft() + 5;
238 box.Offset(d, 0);
239 box.Inflate(5, 0);
240 displacementx = d;
241 displacementy = 0;
242 }
243 }
244 else {
245 if (mOrientation == wxHORIZONTAL) {
246 box.Inflate(0, 5);
247 displacementx = 0;
248 displacementy = 0;
249 }
250 }
251 auto update = [=](Label& label) {
252 label.lx += displacementx;
253 label.ly += displacementy;
254 };
255 for (auto& label : allOutputs.majorLabels)
256 update(label);
257 for (auto& label : allOutputs.minorLabels)
258 update(label);
259 for (auto& label : allOutputs.minorMinorLabels)
260 update(label);
261}
262
#define str(a)
TranslatableString label
Definition: TagsEditor.cpp:165
THEME_API Theme theTheme
Definition: Theme.cpp:82
TranslatableString Verbatim(wxString str)
Require calls to the one-argument constructor to go through this distinct global function name.
void BoxAdjust(UpdateOutputs &allOutputs, const RulerStruct &context) const
virtual ~RulerUpdater()=0
static std::pair< wxRect, Label > MakeTick(RulerUpdater::Label lab, wxDC &dc, wxFont font, std::vector< bool > &bits, int left, int top, int spacing, int lead, bool flip, int orientation)
wxColour & Colour(int iIndex)
Holds a msgid for the translation catalog; may also bind format arguments.
int mOrientation
Definition: RulerUpdater.h:37
An array of these created by the Updater is used to determine what and where text annotations to the ...
Definition: RulerUpdater.h:60
void Draw(wxDC &dc, bool twoTone, wxColour c, std::unique_ptr< RulerStruct::Fonts > &fonts) const
TranslatableString LabelString(double d, const RulerFormat *format) const
TickSizes(double UPP, int orientation, const RulerFormat *format, bool log)