1 /**********************************************************************
2 
3   Audacity: A Digital Audio Editor
4 
5   Ruler.h
6 
7   Dominic Mazzoni
8 
9 **********************************************************************/
10 
11 #ifndef __AUDACITY_RULER__
12 #define __AUDACITY_RULER__
13 
14 #include "wxPanelWrapper.h" // to inherit
15 #include "NumberScale.h" // member variable
16 
17 #include <wx/colour.h> // member variable
18 #include <wx/pen.h> // member variable
19 
20 class wxDC;
21 class wxFont;
22 
23 class Envelope;
24 class ZoomInfo;
25 
26 class AUDACITY_DLL_API Ruler {
27  public:
28 
29    enum RulerFormat {
30       IntFormat,
31       RealFormat,
32       RealLogFormat,
33       TimeFormat,
34       LinearDBFormat,
35    };
36 
37    //
38    // Constructor / Destructor
39    //
40 
41    Ruler();
42    ~Ruler();
43 
44    //
45    // Required Ruler Parameters
46    //
47 
48    void SetBounds(int left, int top, int right, int bottom);
49 
50    // wxHORIZONTAL || wxVERTICAL
51    void SetOrientation(int orient);
52 
53    // min is the value at (x, y)
54    // max is the value at (x+width, y+height)
55    // (at the center of the pixel, in both cases)
56    void SetRange(double min, double max);
57 
58    // An overload needed for the special case of fisheye
59    // min is the value at (x, y)
60    // max is the value at (x+width, y+height)
61    // hiddenMin, hiddenMax are the values that would be shown without the fisheye.
62    // (at the center of the pixel, in both cases)
63    void SetRange(double min, double max, double hiddenMin, double hiddenMax);
64 
65    //
66    // Optional Ruler Parameters
67    //
68 
69    // If twoTone is true, cause zero and positive numbers to appear black, negative in another color.
70    void SetTwoTone(bool twoTone);
71 
72    // IntFormat, RealFormat, or TimeFormat
73    void SetFormat(RulerFormat format);
74 
75    // Specify the name of the units (like "dB") if you
76    // want numbers like "1.6" formatted as "1.6 dB".
77    void SetUnits(const TranslatableString &units);
78    void SetDbMirrorValue( const double d );
79 
80    // Logarithmic
81    void SetLog(bool log);
82 
83    // Minimum number of pixels between labels
84    void SetSpacing(int spacing);
85 
86    // If this is true, the edges of the ruler will always
87    // receive a label.  If not, the nearest round number is
88    // labeled (which may or may not be the edge).
89    void SetLabelEdges(bool labelEdges);
90 
91    // Makes a vertical ruler hug the left side (instead of right)
92    // and a horizontal ruler hug the top (instead of bottom)
93    void SetFlip(bool flip);
94 
95    // Set it to false if you don't want minor labels.
96    void SetMinor(bool value);
97 
98    // Good defaults are provided, but you can override here
99    void SetFonts(const wxFont &minorFont, const wxFont &majorFont, const wxFont &minorMinorFont);
100    struct Fonts {
101       wxFont major, minor, minorMinor;
102       int lead;
103    };
104    Fonts GetFonts() const;
105 
106    void SetNumberScale(const NumberScale &scale);
107 
108    // The ruler will not draw text within this (pixel) range.
109    // Use this if you have another graphic object obscuring part
110    // of the ruler's area.  The values start and end are interpreted
111    // relative to the Ruler's local coordinates.
112    void OfflimitsPixels(int start, int end);
113 
114    //
115    // Calculates and returns the maximum size required by the ruler
116    //
117    void GetMaxSize(wxCoord *width, wxCoord *height);
118 
119 
120    // The following functions should allow a custom ruler setup:
121    // autosize is a GREAT thing, but for some applications it's
122    // useful the definition of a label array and label step by
123    // the user.
124    void SetCustomMode(bool value);
125    // If this is the case, you should provide an array of labels, start
126    // label position, and labels step. The range eventually specified will be
127    // ignored.
128    void SetCustomMajorLabels(
129       const TranslatableStrings &labels, int start, int step);
130    void SetCustomMinorLabels(
131       const TranslatableStrings &labels, int start, int step);
132 
133    void SetUseZoomInfo(int leftOffset, const ZoomInfo *zoomInfo);
134 
135    //
136    // Drawing
137    //
138 
139    // Note that it will not erase for you...
140    void Draw(wxDC& dc) const;
141    void Draw(wxDC& dc, const Envelope* envelope) const;
142    // If length <> 0, draws lines perpendiculars to ruler corresponding
143    // to selected ticks (major, minor, or both), in an adjacent window.
144    // You may need to use the offsets if you are using part of the dc for rulers, borders etc.
145    void DrawGrid(wxDC& dc, int length, bool minor = true, bool major = true, int xOffset = 0, int yOffset = 0) const;
146 
147    // So we can have white ticks on black...
SetTickColour(const wxColour & colour)148    void SetTickColour( const wxColour & colour)
149    { mTickColour = colour; mPen.SetColour( colour );}
150 
151    // Force regeneration of labels at next draw time
152    void Invalidate();
153 
154  private:
155    struct TickSizes;
156 
157    class Label {
158     public:
159       double value;
160       int pos;
161       int lx, ly;
162       TranslatableString text;
163 
164       void Draw(wxDC &dc, bool twoTone, wxColour c) const;
165    };
166    using Labels = std::vector<Label>;
167 
168    using Bits = std::vector< bool >;
169 
170    void ChooseFonts( wxDC &dc ) const;
171 
172    void UpdateCache( wxDC &dc, const Envelope* envelope ) const;
173 
174    struct Updater;
175 
176 public:
177    bool mbTicksOnly; // true => no line the length of the ruler
178    bool mbTicksAtExtremes;
179 
180 private:
181    wxColour mTickColour;
182    wxPen mPen;
183 
184    int          mLeft, mTop, mRight, mBottom;
185    int          mLength;
186 
187    std::unique_ptr<Fonts> mpUserFonts;
188    mutable std::unique_ptr<Fonts> mpFonts;
189 
190    double       mMin, mMax;
191    double       mHiddenMin, mHiddenMax;
192 
193    Bits mUserBits;
194 
195    static std::pair< wxRect, Label > MakeTick(
196       Label lab,
197       wxDC &dc, wxFont font,
198       std::vector<bool> &bits,
199       int left, int top, int spacing, int lead,
200       bool flip, int orientation );
201 
202    struct Cache;
203    mutable std::unique_ptr<Cache> mpCache;
204 
205    // Returns 'zero' label coordinate (for grid drawing)
206    int FindZero( const Labels &labels ) const;
207 
208    int GetZeroPosition() const;
209 
210    int          mOrientation;
211    int          mSpacing;
212    double       mDbMirrorValue;
213    bool         mHasSetSpacing;
214    bool         mLabelEdges;
215    RulerFormat  mFormat;
216    bool         mLog;
217    bool         mFlip;
218    bool         mCustom;
219    bool         mbMinor;
220    TranslatableString mUnits;
221    bool         mTwoTone;
222    const ZoomInfo *mUseZoomInfo;
223    int          mLeftOffset;
224 
225    NumberScale mNumberScale;
226 };
227 
228 class AUDACITY_DLL_API RulerPanel final : public wxPanelWrapper {
229    DECLARE_DYNAMIC_CLASS(RulerPanel)
230 
231  public:
232    using Range = std::pair<double, double>;
233 
234    struct Options {
235       bool log { false };
236       bool flip { false };
237       bool labelEdges { false };
238       bool ticksAtExtremes { false };
239       bool hasTickColour{ false };
240       wxColour tickColour;
241 
OptionsOptions242       Options() {}
243 
LogOptions244       Options &Log( bool l )
245       { log = l; return *this; }
246 
FlipOptions247       Options &Flip( bool f )
248       { flip = f; return *this; }
249 
LabelEdgesOptions250       Options &LabelEdges( bool l )
251       { labelEdges = l; return *this; }
252 
TicksAtExtremesOptions253       Options &TicksAtExtremes( bool t )
254       { ticksAtExtremes = t; return *this; }
255 
TickColourOptions256       Options &TickColour( const wxColour c )
257       { tickColour = c; hasTickColour = true; return *this; }
258    };
259 
260    RulerPanel(wxWindow* parent, wxWindowID id,
261               wxOrientation orientation,
262               const wxSize &bounds,
263               const Range &range,
264               Ruler::RulerFormat format,
265               const TranslatableString &units,
266               const Options &options = {},
267               const wxPoint& pos = wxDefaultPosition,
268               const wxSize& size = wxDefaultSize);
269 
270    ~RulerPanel();
271 
272    void DoSetSize(int x, int y,
273                   int width, int height,
274                   int sizeFlags = wxSIZE_AUTO) override;
275 
276    void OnErase(wxEraseEvent &evt);
277    void OnPaint(wxPaintEvent &evt);
278    void OnSize(wxSizeEvent &evt);
SetTickColour(wxColour & c)279    void SetTickColour( wxColour & c){ ruler.SetTickColour( c );}
280 
281    // We don't need or want to accept focus.
AcceptsFocus()282    bool AcceptsFocus() const override  { return false; }
283    // So that wxPanel is not included in Tab traversal - see wxWidgets bug 15581
AcceptsFocusFromKeyboard()284    bool AcceptsFocusFromKeyboard() const override { return false; }
285 
286  public:
287 
288    Ruler  ruler;
289 
290 private:
291     DECLARE_EVENT_TABLE()
292 };
293 
294 #endif //define __AUDACITY_RULER__
295