mirror of
https://github.com/cookiengineer/audacity
synced 2025-06-23 07:40:05 +02:00
final but it's a big improvement. Reversed a couple changes from original indication scheme that are no longer necessary. Unfortunately it's really hard to remove colors from AllThemeResources.h.
2057 lines
52 KiB
C++
2057 lines
52 KiB
C++
/**********************************************************************
|
|
|
|
Audacity: A Digital Audio Editor
|
|
|
|
Ruler.cpp
|
|
|
|
Dominic Mazzoni
|
|
|
|
*******************************************************************//**
|
|
|
|
\class Ruler
|
|
\brief Used to display a Ruler.
|
|
|
|
This is a generic class which can be used to display just about
|
|
any kind of ruler.
|
|
|
|
At a minimum, the user must specify the dimensions of the
|
|
ruler, its orientation (horizontal or vertical), and the
|
|
values displayed at the two ends of the ruler (min and max).
|
|
By default, this class will display tick marks at reasonable
|
|
round numbers and fractions, for example, 100, 50, 10, 5, 1,
|
|
0.5, 0.1, etc.
|
|
|
|
The class is designed to display a small handful of
|
|
labeled Major ticks, and a few Minor ticks between each of
|
|
these. Minor ticks are labeled if there is enough space.
|
|
Labels will never run into each other.
|
|
|
|
In addition to Real numbers, the Ruler currently supports
|
|
two other formats for its display:
|
|
|
|
Integer - never shows tick marks for fractions of an integer
|
|
|
|
Time - Assumes values represent seconds, and labels the tick
|
|
marks in "HH:MM:SS" format, e.g. 4000 seconds becomes
|
|
"1:06:40", for example. Will display fractions of
|
|
a second, and tick marks are all reasonable round
|
|
numbers for time (i.e. 15 seconds, 30 seconds, etc.)
|
|
*//***************************************************************//**
|
|
|
|
\class RulerPanel
|
|
\brief RulerPanel class allows you to work with a Ruler like
|
|
any other wxWindow.
|
|
|
|
*//***************************************************************//**
|
|
|
|
\class Ruler::Label
|
|
\brief An array of these created by the Ruler is used to determine
|
|
what and where text annotations to the numbers on the Ruler get drawn.
|
|
|
|
\todo Check whether Ruler is costing too much time in malloc/free of
|
|
array of Ruler::Label.
|
|
|
|
*//******************************************************************/
|
|
|
|
#include "../Audacity.h"
|
|
|
|
#include <math.h>
|
|
|
|
#include <wx/dcscreen.h>
|
|
#include <wx/dcmemory.h>
|
|
#include <wx/dcbuffer.h>
|
|
#include <wx/settings.h>
|
|
|
|
#include "../Internat.h"
|
|
#include "../Project.h"
|
|
#include "Ruler.h"
|
|
#include "../toolbars/ControlToolBar.h"
|
|
#include "../Theme.h"
|
|
#include "../AllThemeResources.h"
|
|
#include "../Experimental.h"
|
|
|
|
#define max(a,b) ( (a<b)?b:a )
|
|
|
|
#define SELECT_TOLERANCE_PIXEL 10
|
|
|
|
#define PLAY_REGION_TRIANGLE_SIZE 6
|
|
#define PLAY_REGION_RECT_WIDTH 1
|
|
#define PLAY_REGION_RECT_HEIGHT 3
|
|
#define PLAY_REGION_GLOBAL_OFFSET_Y 7
|
|
|
|
//
|
|
// Ruler
|
|
//
|
|
|
|
Ruler::Ruler()
|
|
{
|
|
mMin = 0.0;
|
|
mMax = 100.0;
|
|
mOrientation = wxHORIZONTAL;
|
|
mSpacing = 6;
|
|
mHasSetSpacing = false;
|
|
mFormat = RealFormat;
|
|
mFlip = false;
|
|
mLog = false;
|
|
mLabelEdges = false;
|
|
mUnits = wxT("");
|
|
|
|
mLeft = -1;
|
|
mTop = -1;
|
|
mRight = -1;
|
|
mBottom = -1;
|
|
mbTicksOnly = true;
|
|
mbTicksAtExtremes = false;
|
|
mTickColour = wxColour(153,153,153);
|
|
mPen.SetColour(mTickColour);
|
|
|
|
// Note: the font size is now adjusted automatically whenever
|
|
// Invalidate is called on a horizontal Ruler, unless the user
|
|
// calls SetFonts manually. So the defaults here are not used
|
|
// often.
|
|
|
|
int fontSize = 10;
|
|
#ifdef __WXMSW__
|
|
fontSize = 8;
|
|
#endif
|
|
|
|
mMinorMinorFont = new wxFont(fontSize-1, wxSWISS, wxNORMAL, wxNORMAL);
|
|
mMinorFont = new wxFont(fontSize, wxSWISS, wxNORMAL, wxNORMAL);
|
|
mMajorFont = new wxFont(fontSize, wxSWISS, wxNORMAL, wxBOLD);
|
|
mUserFonts = false;
|
|
|
|
#ifdef __WXMAC__
|
|
mMinorMinorFont->SetNoAntiAliasing(true);
|
|
mMinorFont->SetNoAntiAliasing(true);
|
|
mMajorFont->SetNoAntiAliasing(true);
|
|
#endif
|
|
|
|
mMajorLabels = 0;
|
|
mMinorLabels = 0;
|
|
mMinorMinorLabels = 0;
|
|
mLengthOld = 0;
|
|
mLength = 0;
|
|
mBits = NULL;
|
|
mUserBits = NULL;
|
|
mUserBitLen = 0;
|
|
|
|
mValid = false;
|
|
|
|
mCustom = false;
|
|
mbMinor = true;
|
|
|
|
mGridLineLength = 0;
|
|
mMajorGrid = false;
|
|
mMinorGrid = false;
|
|
}
|
|
|
|
Ruler::~Ruler()
|
|
{
|
|
Invalidate(); // frees up our arrays
|
|
if( mUserBits )
|
|
delete [] mUserBits;//JKC
|
|
delete mMinorFont;
|
|
delete mMajorFont;
|
|
delete mMinorMinorFont;
|
|
|
|
if (mMajorLabels)
|
|
delete[] mMajorLabels;
|
|
if (mMinorLabels)
|
|
delete[] mMinorLabels;
|
|
if (mMinorMinorLabels)
|
|
delete[] mMinorMinorLabels;
|
|
}
|
|
|
|
void Ruler::SetFormat(RulerFormat format)
|
|
{
|
|
// IntFormat, RealFormat, RealLogFormat, TimeFormat, or LinearDBFormat
|
|
|
|
if (mFormat != format) {
|
|
mFormat = format;
|
|
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
void Ruler::SetLog(bool log)
|
|
{
|
|
// Logarithmic
|
|
|
|
if (mLog != log) {
|
|
mLog = log;
|
|
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
void Ruler::SetUnits(wxString units)
|
|
{
|
|
// Specify the name of the units (like "dB") if you
|
|
// want numbers like "1.6" formatted as "1.6 dB".
|
|
|
|
if (mUnits != units) {
|
|
mUnits = units;
|
|
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
void Ruler::SetOrientation(int orient)
|
|
{
|
|
// wxHORIZONTAL || wxVERTICAL
|
|
|
|
if (mOrientation != orient) {
|
|
mOrientation = orient;
|
|
|
|
if (mOrientation == wxVERTICAL && !mHasSetSpacing)
|
|
mSpacing = 2;
|
|
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
void Ruler::SetRange(double min, double max)
|
|
{
|
|
// For a horizontal ruler,
|
|
// min is the value in the center of pixel "left",
|
|
// max is the value in the center of pixel "right".
|
|
|
|
if (mMin != min || mMax != max) {
|
|
mMin = min;
|
|
mMax = max;
|
|
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
void Ruler::SetSpacing(int spacing)
|
|
{
|
|
mHasSetSpacing = true;
|
|
|
|
if (mSpacing != spacing) {
|
|
mSpacing = spacing;
|
|
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
void Ruler::SetLabelEdges(bool labelEdges)
|
|
{
|
|
// If this is true, the edges of the ruler will always
|
|
// receive a label. If not, the nearest round number is
|
|
// labeled (which may or may not be the edge).
|
|
|
|
if (mLabelEdges != labelEdges) {
|
|
mLabelEdges = labelEdges;
|
|
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
void Ruler::SetFlip(bool flip)
|
|
{
|
|
// If this is true, the orientation of the tick marks
|
|
// is reversed from the default; eg. above the line
|
|
// instead of below
|
|
|
|
if (mFlip != flip) {
|
|
mFlip = flip;
|
|
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
void Ruler::SetMinor(bool value)
|
|
{
|
|
mbMinor = value;
|
|
}
|
|
|
|
void Ruler::SetFonts(const wxFont &minorFont, const wxFont &majorFont, const wxFont &minorMinorFont)
|
|
{
|
|
*mMinorMinorFont = minorMinorFont;
|
|
*mMinorFont = minorFont;
|
|
*mMajorFont = majorFont;
|
|
|
|
#ifdef __WXMAC__
|
|
mMinorMinorFont->SetNoAntiAliasing(true);
|
|
mMinorFont->SetNoAntiAliasing(true);
|
|
mMajorFont->SetNoAntiAliasing(true);
|
|
#endif
|
|
|
|
// Won't override these fonts
|
|
mUserFonts = true;
|
|
|
|
Invalidate();
|
|
}
|
|
|
|
void Ruler::OfflimitsPixels(int start, int end)
|
|
{
|
|
int i;
|
|
|
|
if (!mUserBits) {
|
|
if (mOrientation == wxHORIZONTAL)
|
|
mLength = mRight-mLeft;
|
|
else
|
|
mLength = mBottom-mTop;
|
|
mUserBits = new int[mLength+1];
|
|
for(i=0; i<=mLength; i++)
|
|
mUserBits[i] = 0;
|
|
mUserBitLen = mLength+1;
|
|
}
|
|
|
|
if (end < start) {
|
|
i = end;
|
|
end = start;
|
|
start = i;
|
|
}
|
|
|
|
if (start < 0)
|
|
start = 0;
|
|
if (end > mLength)
|
|
end = mLength;
|
|
|
|
for(i=start; i<=end; i++)
|
|
mUserBits[i] = 1;
|
|
}
|
|
|
|
void Ruler::SetBounds(int left, int top, int right, int bottom)
|
|
{
|
|
if (mLeft != left || mTop != top ||
|
|
mRight != right || mBottom != bottom) {
|
|
mLeft = left;
|
|
mTop = top;
|
|
mRight = right;
|
|
mBottom = bottom;
|
|
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
void Ruler::Invalidate()
|
|
{
|
|
mValid = false;
|
|
|
|
if (mOrientation == wxHORIZONTAL)
|
|
mLength = mRight-mLeft;
|
|
else
|
|
mLength = mBottom-mTop;
|
|
|
|
if (mBits) {
|
|
delete [] mBits;
|
|
mBits = NULL;
|
|
}
|
|
if (mUserBits && mLength+1 != mUserBitLen) {
|
|
delete[] mUserBits;
|
|
mUserBits = NULL;
|
|
mUserBitLen = 0;
|
|
}
|
|
}
|
|
|
|
void Ruler::FindLinearTickSizes(double UPP)
|
|
{
|
|
// Given the dimensions of the ruler, the range of values it
|
|
// has to display, and the format (i.e. Int, Real, Time),
|
|
// figure out how many units are in one Minor tick, and
|
|
// in one Major tick.
|
|
//
|
|
// The goal is to always put tick marks on nice round numbers
|
|
// that are easy for humans to grok. This is the most tricky
|
|
// with time.
|
|
|
|
double d;
|
|
|
|
// As a heuristic, we want at least 16 pixels
|
|
// between each minor tick
|
|
double units = 16 * fabs(UPP);
|
|
|
|
mDigits = 0;
|
|
|
|
switch(mFormat) {
|
|
case LinearDBFormat:
|
|
if (units < 0.1) {
|
|
mMinor = 0.1;
|
|
mMajor = 0.5;
|
|
return;
|
|
}
|
|
if (units < 1.0) {
|
|
mMinor = 1.0;
|
|
mMajor = 6.0;
|
|
return;
|
|
}
|
|
if (units < 3.0) {
|
|
mMinor = 3.0;
|
|
mMajor = 12.0;
|
|
return;
|
|
}
|
|
if (units < 6.0) {
|
|
mMinor = 6.0;
|
|
mMajor = 24.0;
|
|
return;
|
|
}
|
|
if (units < 12.0) {
|
|
mMinor = 12.0;
|
|
mMajor = 48.0;
|
|
return;
|
|
}
|
|
if (units < 24.0) {
|
|
mMinor = 24.0;
|
|
mMajor = 96.0;
|
|
return;
|
|
}
|
|
d = 20.0;
|
|
for(;;) {
|
|
if (units < d) {
|
|
mMinor = d;
|
|
mMajor = d*5.0;
|
|
return;
|
|
}
|
|
d *= 5.0;
|
|
if (units < d) {
|
|
mMinor = d;
|
|
mMajor = d*5.0;
|
|
return;
|
|
}
|
|
d *= 2.0;
|
|
}
|
|
break;
|
|
|
|
case IntFormat:
|
|
d = 1.0;
|
|
for(;;) {
|
|
if (units < d) {
|
|
mMinor = d;
|
|
mMajor = d*5.0;
|
|
return;
|
|
}
|
|
d *= 5.0;
|
|
if (units < d) {
|
|
mMinor = d;
|
|
mMajor = d*2.0;
|
|
return;
|
|
}
|
|
d *= 2.0;
|
|
}
|
|
break;
|
|
|
|
case TimeFormat:
|
|
if (units > 0.5) {
|
|
if (units < 1.0) { // 1 sec
|
|
mMinor = 1.0;
|
|
mMajor = 5.0;
|
|
return;
|
|
}
|
|
if (units < 5.0) { // 5 sec
|
|
mMinor = 5.0;
|
|
mMajor = 15.0;
|
|
return;
|
|
}
|
|
if (units < 10.0) {
|
|
mMinor = 10.0;
|
|
mMajor = 30.0;
|
|
return;
|
|
}
|
|
if (units < 15.0) {
|
|
mMinor = 15.0;
|
|
mMajor = 60.0;
|
|
return;
|
|
}
|
|
if (units < 30.0) {
|
|
mMinor = 30.0;
|
|
mMajor = 60.0;
|
|
return;
|
|
}
|
|
if (units < 60.0) { // 1 min
|
|
mMinor = 60.0;
|
|
mMajor = 300.0;
|
|
return;
|
|
}
|
|
if (units < 300.0) { // 5 min
|
|
mMinor = 300.0;
|
|
mMajor = 900.0;
|
|
return;
|
|
}
|
|
if (units < 600.0) { // 10 min
|
|
mMinor = 600.0;
|
|
mMajor = 1800.0;
|
|
return;
|
|
}
|
|
if (units < 900.0) { // 15 min
|
|
mMinor = 900.0;
|
|
mMajor = 3600.0;
|
|
return;
|
|
}
|
|
if (units < 1800.0) { // 30 min
|
|
mMinor = 1800.0;
|
|
mMajor = 3600.0;
|
|
return;
|
|
}
|
|
if (units < 3600.0) { // 1 hr
|
|
mMinor = 3600.0;
|
|
mMajor = 6*3600.0;
|
|
return;
|
|
}
|
|
if (units < 6*3600.0) { // 6 hrs
|
|
mMinor = 6*3600.0;
|
|
mMajor = 24*3600.0;
|
|
return;
|
|
}
|
|
if (units < 24*3600.0) { // 1 day
|
|
mMinor = 24*3600.0;
|
|
mMajor = 7*24*3600.0;
|
|
return;
|
|
}
|
|
|
|
mMinor = 24.0 * 7.0 * 3600.0; // 1 week
|
|
mMajor = 24.0 * 7.0 * 3600.0;
|
|
}
|
|
|
|
// Otherwise fall through to RealFormat
|
|
// (fractions of a second should be dealt with
|
|
// the same way as for RealFormat)
|
|
|
|
case RealFormat:
|
|
d = 0.000001;
|
|
// mDigits is number of digits after the decimal point.
|
|
mDigits = 6;
|
|
for(;;) {
|
|
if (units < d) {
|
|
mMinor = d;
|
|
mMajor = d*5.0;
|
|
return;
|
|
}
|
|
d *= 5.0;
|
|
if (units < d) {
|
|
mMinor = d;
|
|
mMajor = d*2.0;
|
|
return;
|
|
}
|
|
d *= 2.0;
|
|
mDigits--;
|
|
// More than 10 digit numbers? Something is badly wrong.
|
|
// Probably units is coming in with too high a value.
|
|
wxASSERT( mDigits >= -10 );
|
|
}
|
|
mMinor = d;
|
|
mMajor = d * 2.0;
|
|
break;
|
|
|
|
case RealLogFormat:
|
|
d = 0.000001;
|
|
// mDigits is number of digits after the decimal point.
|
|
mDigits = 6;
|
|
for(;;) {
|
|
if (units < d) {
|
|
mMinor = d;
|
|
mMajor = d*5.0;
|
|
return;
|
|
}
|
|
d *= 5.0;
|
|
if (units < d) {
|
|
mMinor = d;
|
|
mMajor = d*2.0;
|
|
return;
|
|
}
|
|
d *= 2.0;
|
|
mDigits--;
|
|
// More than 10 digit numbers? Something is badly wrong.
|
|
// Probably units is coming in with too high a value.
|
|
wxASSERT( mDigits >= -10 );
|
|
}
|
|
mDigits++;
|
|
mMinor = d;
|
|
mMajor = d * 2.0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
wxString Ruler::LabelString(double d, bool major)
|
|
{
|
|
// Given a value, turn it into a string according
|
|
// to the current ruler format. The number of digits of
|
|
// accuracy depends on the resolution of the ruler,
|
|
// i.e. how far zoomed in or out you are.
|
|
|
|
wxString s;
|
|
|
|
// Replace -0 with 0
|
|
if (d < 0.0 && d+mMinor > 0.0)
|
|
d = 0.0;
|
|
|
|
switch(mFormat) {
|
|
case IntFormat:
|
|
s.Printf(wxT("%d"), (int)floor(d+0.5));
|
|
break;
|
|
case LinearDBFormat:
|
|
if (mMinor >= 1.0)
|
|
s.Printf(wxT("%d"), (int)floor(d+0.5));
|
|
else {
|
|
s.Printf(wxT("%.1f"), d);
|
|
}
|
|
break;
|
|
case RealFormat:
|
|
if (mMinor >= 1.0)
|
|
s.Printf(wxT("%d"), (int)floor(d+0.5));
|
|
else {
|
|
s.Printf(wxString::Format(wxT("%%.%df"), mDigits), d);
|
|
}
|
|
break;
|
|
case RealLogFormat:
|
|
if (mMinor >= 1.0)
|
|
s.Printf(wxT("%d"), (int)floor(d+0.5));
|
|
else {
|
|
s.Printf(wxString::Format(wxT("%%.%df"), mDigits), d);
|
|
}
|
|
break;
|
|
case TimeFormat:
|
|
if (major) {
|
|
if (d < 0) {
|
|
s = wxT("-");
|
|
d = -d;
|
|
}
|
|
|
|
#if ALWAYS_HH_MM_SS
|
|
int secs = (int)(d + 0.5);
|
|
if (mMinor >= 1.0) {
|
|
s.Printf(wxT("%d:%02d:%02d"), secs/3600, (secs/60)%60, secs%60);
|
|
}
|
|
else {
|
|
wxString t1, t2, format;
|
|
t1.Printf(wxT("%d:%02d:"), secs/3600, (secs/60)%60);
|
|
format.Printf(wxT("%%0%d.%dlf"), mDigits+3, mDigits);
|
|
t2.Printf(format.c_str(), fmod(d, 60.0));
|
|
s += t1 + t2;
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
if (mMinor >= 3600.0) {
|
|
int hrs = (int)(d / 3600.0 + 0.5);
|
|
wxString h;
|
|
h.Printf(wxT("%d:00:00"), hrs);
|
|
s += h;
|
|
}
|
|
else if (mMinor >= 60.0) {
|
|
int minutes = (int)(d / 60.0 + 0.5);
|
|
wxString m;
|
|
if (minutes >= 60)
|
|
m.Printf(wxT("%d:%02d:00"), minutes/60, minutes%60);
|
|
else
|
|
m.Printf(wxT("%d:00"), minutes);
|
|
s += m;
|
|
}
|
|
else if (mMinor >= 1.0) {
|
|
int secs = (int)(d + 0.5);
|
|
wxString t;
|
|
if (secs >= 3600)
|
|
t.Printf(wxT("%d:%02d:%02d"), secs/3600, (secs/60)%60, secs%60);
|
|
else if (secs >= 60)
|
|
t.Printf(wxT("%d:%02d"), secs/60, secs%60);
|
|
else
|
|
t.Printf(wxT("%d"), secs);
|
|
s += t;
|
|
}
|
|
else {
|
|
// The casting to float is working around an issue where 59 seconds
|
|
// would show up as 60 when using g++ (Ubuntu 4.3.3-5ubuntu4) 4.3.3.
|
|
int secs = (int)(float)(d);
|
|
wxString t1, t2, format;
|
|
|
|
if (secs >= 3600)
|
|
t1.Printf(wxT("%d:%02d:"), secs/3600, (secs/60)%60);
|
|
else if (secs >= 60)
|
|
t1.Printf(wxT("%d:"), secs/60);
|
|
|
|
if (secs >= 60)
|
|
format.Printf(wxT("%%0%d.%dlf"), mDigits+3, mDigits);
|
|
else
|
|
format.Printf(wxT("%%%d.%dlf"), mDigits+3, mDigits);
|
|
// The casting to float is working around an issue where 59 seconds
|
|
// would show up as 60 when using g++ (Ubuntu 4.3.3-5ubuntu4) 4.3.3.
|
|
t2.Printf(format.c_str(), fmod((float)d, (float)60.0));
|
|
|
|
s += t1 + t2;
|
|
}
|
|
}
|
|
else {
|
|
}
|
|
}
|
|
|
|
if (mUnits != wxT(""))
|
|
s = (s + mUnits);
|
|
|
|
return s;
|
|
}
|
|
|
|
void Ruler::Tick(int pos, double d, bool major, bool minor)
|
|
{
|
|
wxString l;
|
|
wxCoord strW, strH, strD, strL;
|
|
int strPos, strLen, strLeft, strTop;
|
|
|
|
// FIXME: We don't draw a tick if of end of our label arrays
|
|
// But we shouldn't have an array of labels.
|
|
if( mNumMinorMinor >= mLength )
|
|
return;
|
|
if( mNumMinor >= mLength )
|
|
return;
|
|
if( mNumMajor >= mLength )
|
|
return;
|
|
|
|
Label *label;
|
|
if (major)
|
|
label = &mMajorLabels[mNumMajor++];
|
|
else if (minor)
|
|
label = &mMinorLabels[mNumMinor++];
|
|
else
|
|
label = &mMinorMinorLabels[mNumMinorMinor++];
|
|
|
|
label->pos = pos;
|
|
label->lx = mLeft - 1000; // don't display
|
|
label->ly = mTop - 1000; // don't display
|
|
label->text = wxT("");
|
|
|
|
mDC->SetFont(major? *mMajorFont: minor? *mMinorFont : *mMinorMinorFont);
|
|
l = LabelString(d, major);
|
|
mDC->GetTextExtent(l, &strW, &strH, &strD, &strL);
|
|
|
|
if (mOrientation == wxHORIZONTAL) {
|
|
strLen = strW;
|
|
strPos = pos - strW/2;
|
|
if (strPos < 0)
|
|
strPos = 0;
|
|
if (strPos + strW >= mLength)
|
|
strPos = mLength - strW;
|
|
strLeft = mLeft + strPos;
|
|
if (mFlip) {
|
|
strTop = mTop + 4;
|
|
mMaxHeight = max(mMaxHeight, strH + 4);
|
|
}
|
|
else {
|
|
strTop =-strH-mLead;
|
|
mMaxHeight = max(mMaxHeight, strH + 6);
|
|
}
|
|
}
|
|
else {
|
|
strLen = strH;
|
|
strPos = pos - strH/2;
|
|
if (strPos < 0)
|
|
strPos = 0;
|
|
if (strPos + strH >= mLength)
|
|
strPos = mLength - strH;
|
|
strTop = mTop + strPos;
|
|
if (mFlip) {
|
|
strLeft = mLeft + 5;
|
|
mMaxWidth = max(mMaxWidth, strW + 5);
|
|
}
|
|
else
|
|
strLeft =-strW-6;
|
|
}
|
|
|
|
|
|
// FIXME: we shouldn't even get here if strPos < 0.
|
|
// Ruler code currently does not handle very small or
|
|
// negative sized windows (i.e. don't draw) properly.
|
|
if( strPos < 0 )
|
|
return;
|
|
|
|
// See if any of the pixels we need to draw this
|
|
// label is already covered
|
|
|
|
int i;
|
|
for(i=0; i<strLen; i++)
|
|
if (mBits[strPos+i])
|
|
return;
|
|
|
|
// If not, position the label and give it text
|
|
|
|
label->lx = strLeft;
|
|
label->ly = strTop;
|
|
label->text = l;
|
|
|
|
// And mark these pixels, plus some surrounding
|
|
// ones (the spacing between labels), as covered
|
|
int leftMargin = mSpacing;
|
|
if (strPos < leftMargin)
|
|
leftMargin = strPos;
|
|
strPos -= leftMargin;
|
|
strLen += leftMargin;
|
|
|
|
int rightMargin = mSpacing;
|
|
if (strPos + strLen > mLength - mSpacing)
|
|
rightMargin = mLength - strPos - strLen;
|
|
strLen += rightMargin;
|
|
|
|
for(i=0; i<strLen; i++)
|
|
mBits[strPos+i] = 1;
|
|
|
|
wxRect r(strLeft, strTop, strW, strH);
|
|
mRect.Union(r);
|
|
|
|
}
|
|
|
|
void Ruler::TickCustom(int labelIdx, bool major, bool minor)
|
|
{
|
|
//This should only used in the mCustom case
|
|
// Many code comes from 'Tick' method: this should
|
|
// be optimized.
|
|
|
|
int pos;
|
|
wxString l;
|
|
wxCoord strW, strH, strD, strL;
|
|
int strPos, strLen, strLeft, strTop;
|
|
|
|
// FIXME: We don't draw a tick if of end of our label arrays
|
|
// But we shouldn't have an array of labels.
|
|
if( mNumMinor >= mLength )
|
|
return;
|
|
if( mNumMajor >= mLength )
|
|
return;
|
|
|
|
Label *label;
|
|
if (major)
|
|
label = &mMajorLabels[labelIdx];
|
|
else if (minor)
|
|
label = &mMinorLabels[labelIdx];
|
|
else
|
|
label = &mMinorMinorLabels[labelIdx];
|
|
|
|
pos = label->pos; // already stored in label class
|
|
l = label->text;
|
|
label->lx = mLeft - 1000; // don't display
|
|
label->ly = mTop - 1000; // don't display
|
|
|
|
mDC->SetFont(major? *mMajorFont: minor? *mMinorFont : *mMinorMinorFont);
|
|
|
|
mDC->GetTextExtent(l, &strW, &strH, &strD, &strL);
|
|
|
|
if (mOrientation == wxHORIZONTAL) {
|
|
strLen = strW;
|
|
strPos = pos - strW/2;
|
|
if (strPos < 0)
|
|
strPos = 0;
|
|
if (strPos + strW >= mLength)
|
|
strPos = mLength - strW;
|
|
strLeft = mLeft + strPos;
|
|
if (mFlip) {
|
|
strTop = mTop + 4;
|
|
mMaxHeight = max(mMaxHeight, strH + 4);
|
|
}
|
|
else {
|
|
|
|
strTop =-strH-mLead;
|
|
strTop = mTop- mLead+4;// More space was needed...
|
|
mMaxHeight = max(mMaxHeight, strH + 6);
|
|
}
|
|
}
|
|
else {
|
|
strLen = strH;
|
|
strPos = pos - strH/2;
|
|
if (strPos < 0)
|
|
strPos = 0;
|
|
if (strPos + strH >= mLength)
|
|
strPos = mLength - strH;
|
|
strTop = mTop + strPos;
|
|
if (mFlip) {
|
|
strLeft = mLeft + 5;
|
|
mMaxWidth = max(mMaxWidth, strW + 5);
|
|
}
|
|
else {
|
|
|
|
strLeft =-strW-6;
|
|
}
|
|
}
|
|
|
|
|
|
// FIXME: we shouldn't even get here if strPos < 0.
|
|
// Ruler code currently does not handle very small or
|
|
// negative sized windows (i.e. don't draw) properly.
|
|
if( strPos < 0 )
|
|
return;
|
|
|
|
// See if any of the pixels we need to draw this
|
|
// label is already covered
|
|
|
|
int i;
|
|
for(i=0; i<strLen; i++)
|
|
if (mBits[strPos+i])
|
|
return;
|
|
|
|
// If not, position the label
|
|
|
|
label->lx = strLeft;
|
|
label->ly = strTop;
|
|
|
|
// And mark these pixels, plus some surrounding
|
|
// ones (the spacing between labels), as covered
|
|
int leftMargin = mSpacing;
|
|
if (strPos < leftMargin)
|
|
leftMargin = strPos;
|
|
strPos -= leftMargin;
|
|
strLen += leftMargin;
|
|
|
|
int rightMargin = mSpacing;
|
|
if (strPos + strLen > mLength - mSpacing)
|
|
rightMargin = mLength - strPos - strLen;
|
|
strLen += rightMargin;
|
|
|
|
for(i=0; i<strLen; i++)
|
|
mBits[strPos+i] = 1;
|
|
|
|
|
|
wxRect r(strLeft, strTop, strW, strH);
|
|
mRect.Union(r);
|
|
|
|
}
|
|
|
|
void Ruler::Update()
|
|
{
|
|
Update(NULL, 0, 0);
|
|
}
|
|
|
|
void Ruler::Update( Envelope *speedEnv, long minSpeed, long maxSpeed )
|
|
{
|
|
// This gets called when something has been changed
|
|
// (i.e. we've been invalidated). Recompute all
|
|
// tick positions and font size.
|
|
|
|
int i;
|
|
int j;
|
|
|
|
if (!mUserFonts) {
|
|
int fontSize = 4;
|
|
wxCoord strW, strH, strD, strL;
|
|
wxString exampleText = wxT("0.9"); //ignored for height calcs on all platforms
|
|
int desiredPixelHeight;
|
|
|
|
if (mOrientation == wxHORIZONTAL)
|
|
desiredPixelHeight = mBottom-mTop-5; // height less ticks and 1px gap
|
|
else
|
|
desiredPixelHeight = 12; // why 12? 10 -> 12 seems to be max/min
|
|
|
|
if (desiredPixelHeight < 10)//8)
|
|
desiredPixelHeight = 10;//8;
|
|
if (desiredPixelHeight > 12)
|
|
desiredPixelHeight = 12;
|
|
|
|
// Keep making the font bigger until it's too big, then subtract one.
|
|
mDC->SetFont(wxFont(fontSize, wxSWISS, wxNORMAL, wxBOLD));
|
|
mDC->GetTextExtent(exampleText, &strW, &strH, &strD, &strL);
|
|
while( (strH-strD-strL) <= desiredPixelHeight && fontSize < 40) {
|
|
fontSize++;
|
|
mDC->SetFont(wxFont(fontSize, wxSWISS, wxNORMAL, wxBOLD));
|
|
mDC->GetTextExtent(exampleText, &strW, &strH, &strD, & strL);
|
|
}
|
|
fontSize--;
|
|
mDC->SetFont(wxFont(fontSize, wxSWISS, wxNORMAL, wxNORMAL));
|
|
mDC->GetTextExtent(exampleText, &strW, &strH, &strD, &strL);
|
|
mLead = strL;
|
|
|
|
if (mMajorFont)
|
|
delete mMajorFont;
|
|
mMajorFont = new wxFont(fontSize, wxSWISS, wxNORMAL, wxBOLD);
|
|
|
|
if (mMinorFont)
|
|
delete mMinorFont;
|
|
mMinorFont = new wxFont(fontSize, wxSWISS, wxNORMAL, wxNORMAL);
|
|
|
|
if (mMinorMinorFont)
|
|
delete mMinorMinorFont;
|
|
mMinorMinorFont = new wxFont(fontSize-1, wxSWISS, wxNORMAL, wxNORMAL);
|
|
}
|
|
|
|
// If ruler is being resized, we could end up with it being too small.
|
|
// Values of mLength of zero or below cause bad array allocations and
|
|
// division by zero. So...
|
|
// IF too small THEN bail out and don't draw.
|
|
if( mLength <= 0 )
|
|
return;
|
|
|
|
if (mOrientation == wxHORIZONTAL) {
|
|
mMaxWidth = mLength;
|
|
mMaxHeight = 0;
|
|
mRect = wxRect(0,0, mLength,0);
|
|
}
|
|
else {
|
|
mMaxWidth = 0;
|
|
mMaxHeight = mLength;
|
|
mRect = wxRect(0,0, 0,mLength);
|
|
}
|
|
|
|
// FIXME: Surely we do not need to allocate storage for the labels?
|
|
// We can just recompute them as we need them? Yes, but only if
|
|
// mCustom is false!!!!
|
|
|
|
if(!mCustom) {
|
|
mNumMajor = 0;
|
|
mNumMinor = 0;
|
|
mNumMinorMinor = 0;
|
|
if (mLength!=mLengthOld) {
|
|
if (mMajorLabels)
|
|
delete[] mMajorLabels;
|
|
mMajorLabels = new Label[mLength+1];
|
|
if (mMinorLabels)
|
|
delete[] mMinorLabels;
|
|
mMinorLabels = new Label[mLength+1];
|
|
if (mMinorMinorLabels)
|
|
delete[] mMinorMinorLabels;
|
|
mMinorMinorLabels = new Label[mLength+1];
|
|
mLengthOld = mLength;
|
|
}
|
|
}
|
|
|
|
if (mBits)
|
|
delete[] mBits;
|
|
mBits = new int[mLength+1];
|
|
if (mUserBits)
|
|
for(i=0; i<=mLength; i++)
|
|
mBits[i] = mUserBits[i];
|
|
else
|
|
for(i=0; i<=mLength; i++)
|
|
mBits[i] = 0;
|
|
|
|
// *************** Label calculation routine **************
|
|
if(mCustom == true) {
|
|
|
|
// SET PARAMETER IN MCUSTOM CASE
|
|
// Works only with major labels
|
|
|
|
int numLabel = mNumMajor;
|
|
|
|
i = 0;
|
|
while((i<numLabel) && (i<=mLength)) {
|
|
|
|
TickCustom(i, true, false);
|
|
i++;
|
|
}
|
|
|
|
} else if(mLog==false) {
|
|
|
|
double UPP = (mMax-mMin)/mLength; // Units per pixel
|
|
FindLinearTickSizes(UPP);
|
|
|
|
// Left and Right Edges
|
|
if (mLabelEdges) {
|
|
Tick(0, mMin, true, false);
|
|
Tick(mLength, mMax, true, false);
|
|
}
|
|
|
|
// Zero (if it's in the middle somewhere)
|
|
if (mMin * mMax < 0.0) {
|
|
int mid = (int)(mLength*(mMin/(mMin-mMax)) + 0.5);
|
|
Tick(mid, 0.0, true, false);
|
|
}
|
|
|
|
double sg = UPP > 0.0? 1.0: -1.0;
|
|
|
|
// Major ticks
|
|
double d = mMin - UPP/2;
|
|
double lastD = d;
|
|
int majorInt = (int)floor(sg * d / mMajor);
|
|
i = -1;
|
|
while(i <= mLength) {
|
|
double warpfactor;
|
|
if( d>0 && speedEnv != NULL ) {
|
|
warpfactor = speedEnv->Average( lastD, d );
|
|
// Now we re-scale so that 0.5 is normal speed and
|
|
// 0 and 1.0 are min% and max% of normal speed
|
|
warpfactor = (maxSpeed * (1 - warpfactor) +
|
|
warpfactor * minSpeed) / 100.0;
|
|
}
|
|
else
|
|
warpfactor = 1.0;
|
|
|
|
i++;
|
|
lastD = d;
|
|
d += UPP*warpfactor;
|
|
|
|
if ((int)floor(sg * d / mMajor) > majorInt) {
|
|
majorInt = (int)floor(sg * d / mMajor);
|
|
Tick(i, sg * majorInt * mMajor, true, false);
|
|
}
|
|
}
|
|
|
|
// Minor ticks
|
|
d = mMin - UPP/2;
|
|
lastD = d;
|
|
int minorInt = (int)floor(sg * d / mMinor);
|
|
i = -1;
|
|
while(i <= mLength) {
|
|
double warpfactor;
|
|
if( d>0 && speedEnv != NULL ) {
|
|
warpfactor = speedEnv->Average( lastD, d );
|
|
// Now we re-scale so that 0.5 is normal speed and
|
|
// 0 and 1.0 are min% and max% of normal speed
|
|
warpfactor = (maxSpeed * (1 - warpfactor) +
|
|
warpfactor * minSpeed) / 100.0;
|
|
}
|
|
else
|
|
warpfactor = 1.0;
|
|
|
|
i++;
|
|
lastD = d;
|
|
d += UPP*warpfactor;
|
|
|
|
if ((int)floor(sg * d / mMinor) > minorInt) {
|
|
minorInt = (int)floor(sg * d / mMinor);
|
|
Tick(i, sg * minorInt * mMinor, false, true);
|
|
}
|
|
}
|
|
|
|
// Left and Right Edges
|
|
if (mLabelEdges) {
|
|
Tick(0, mMin, true, false);
|
|
Tick(mLength, mMax, true, false);
|
|
}
|
|
|
|
}
|
|
else {
|
|
// log case
|
|
mDigits=2; //TODO: implement dynamic digit computation
|
|
double loLog = log10(mMin);
|
|
double hiLog = log10(mMax);
|
|
double scale = mLength/(hiLog - loLog);
|
|
int loDecade = (int) floor(loLog);
|
|
|
|
int pos;
|
|
double val;
|
|
double startDecade = pow(10., (double)loDecade);
|
|
|
|
// Major ticks are the decades
|
|
double decade = startDecade;
|
|
double delta=hiLog-loLog, steps=fabs(delta);
|
|
double step = delta>=0 ? 10 : 0.1;
|
|
double rMin=wxMin(mMin, mMax), rMax=wxMax(mMin, mMax);
|
|
for(i=0; i<=steps; i++)
|
|
{ // if(i!=0)
|
|
{ val = decade;
|
|
if(val > rMin && val < rMax) {
|
|
pos = (int)(((log10(val) - loLog)*scale)+0.5);
|
|
Tick(pos, val, true, false);
|
|
}
|
|
}
|
|
decade *= step;
|
|
}
|
|
|
|
// Minor ticks are multiples of decades
|
|
decade = startDecade;
|
|
float start, end, mstep;
|
|
if (delta > 0)
|
|
{ start=2; end=10; mstep=1;
|
|
}else
|
|
{ start=9; end=1; mstep=-1;
|
|
}
|
|
steps++;
|
|
for(i=0; i<=steps; i++) {
|
|
for(j=start; j!=end; j+=mstep) {
|
|
val = decade * j;
|
|
if(val >= rMin && val < rMax) {
|
|
pos = (int)(((log10(val) - loLog)*scale)+0.5);
|
|
Tick(pos, val, false, true);
|
|
}
|
|
}
|
|
decade *= step;
|
|
}
|
|
|
|
// MinorMinor ticks are multiples of decades
|
|
decade = startDecade;
|
|
if (delta > 0)
|
|
{ start= 10; end=100; mstep= 1;
|
|
}else
|
|
{ start=100; end= 10; mstep=-1;
|
|
}
|
|
steps++;
|
|
for(i=0; i<=steps; i++) {
|
|
for(int f=start; f!=int(end); f+=mstep) {
|
|
if (int(f/10)!=f/10.0f) {
|
|
val = decade * f/10;
|
|
if(val >= rMin && val < rMax) {
|
|
pos = (int)(((log10(val) - loLog)*scale)+0.5);
|
|
Tick(pos, val, false, false);
|
|
}
|
|
}
|
|
}
|
|
decade *= step;
|
|
}
|
|
}
|
|
|
|
int displacementx=0, displacementy=0;
|
|
if (!mFlip) {
|
|
if (mOrientation==wxHORIZONTAL) {
|
|
int d=mTop+mRect.GetHeight()+5;
|
|
mRect.Offset(0,d);
|
|
mRect.Inflate(0,5);
|
|
displacementx=0;
|
|
displacementy=d;
|
|
}
|
|
else {
|
|
int d=mLeft-mRect.GetLeft()+5;
|
|
mRect.Offset(d,0);
|
|
mRect.Inflate(5,0);
|
|
displacementx=d;
|
|
displacementy=0;
|
|
}
|
|
}
|
|
else {
|
|
if (mOrientation==wxHORIZONTAL) {
|
|
mRect.Inflate(0,5);
|
|
displacementx=0;
|
|
displacementy=0;
|
|
}
|
|
}
|
|
for(i=0; i<mNumMajor; i++) {
|
|
mMajorLabels[i].lx+= displacementx;
|
|
mMajorLabels[i].ly+= displacementy;
|
|
}
|
|
for(i=0; i<mNumMinor; i++) {
|
|
mMinorLabels[i].lx+= displacementx;
|
|
mMinorLabels[i].ly+= displacementy;
|
|
}
|
|
for(i=0; i<mNumMinorMinor; i++) {
|
|
mMinorMinorLabels[i].lx+= displacementx;
|
|
mMinorMinorLabels[i].ly+= displacementy;
|
|
}
|
|
mMaxWidth = mRect.GetWidth ();
|
|
mMaxHeight= mRect.GetHeight();
|
|
mValid = true;
|
|
}
|
|
|
|
void Ruler::Draw(wxDC& dc)
|
|
{
|
|
Draw( dc, NULL, 0, 0);
|
|
}
|
|
|
|
void Ruler::Draw(wxDC& dc, Envelope *speedEnv, long minSpeed, long maxSpeed)
|
|
{
|
|
mDC = &dc;
|
|
if( mLength <=0 )
|
|
return;
|
|
|
|
if (!mValid)
|
|
Update( speedEnv, minSpeed, maxSpeed );
|
|
|
|
#ifdef EXPERIMENTAL_THEMING
|
|
mDC->SetPen(mPen);
|
|
mDC->SetTextForeground(mTickColour);
|
|
#else
|
|
mDC->SetPen(*wxBLACK_PEN);
|
|
mDC->SetTextForeground(*wxBLACK);
|
|
#endif
|
|
|
|
// Draws a long line the length of the ruler.
|
|
if( !mbTicksOnly )
|
|
{
|
|
if (mOrientation == wxHORIZONTAL) {
|
|
if (mFlip)
|
|
mDC->DrawLine(mLeft, mTop, mRight+1, mTop);
|
|
else
|
|
mDC->DrawLine(mLeft, mBottom, mRight+1, mBottom);
|
|
}
|
|
else {
|
|
if (mFlip)
|
|
mDC->DrawLine(mLeft, mTop, mLeft, mBottom+1);
|
|
else
|
|
{
|
|
// These calculations tappear to be wrong, and to never have been used (so not tested) prior to MixerBoard.
|
|
// mDC->DrawLine(mRect.x-mRect.width, mTop, mRect.x-mRect.width, mBottom+1);
|
|
const int nLineX = mRight - 1;
|
|
mDC->DrawLine(nLineX, mTop, nLineX, mBottom+1);
|
|
}
|
|
}
|
|
}
|
|
|
|
int i;
|
|
|
|
mDC->SetFont(*mMajorFont);
|
|
|
|
// We may want to not show the ticks at the extremes,
|
|
// though still showing the labels.
|
|
// This gives a better look when the ruler is on a bevelled
|
|
// button, since otherwise the tick is drawn on the bevel.
|
|
int iMaxPos = (mOrientation==wxHORIZONTAL)? mRight : mBottom-5;
|
|
|
|
for(i=0; i<mNumMajor; i++) {
|
|
int pos = mMajorLabels[i].pos;
|
|
|
|
if( mbTicksAtExtremes || ((pos!=0)&&(pos!=iMaxPos)))
|
|
{
|
|
if (mOrientation == wxHORIZONTAL) {
|
|
if (mFlip)
|
|
mDC->DrawLine(mLeft + pos, mTop,
|
|
mLeft + pos, mTop + 4);
|
|
else
|
|
mDC->DrawLine(mLeft + pos, mBottom - 4,
|
|
mLeft + pos, mBottom);
|
|
}
|
|
else {
|
|
if (mFlip)
|
|
mDC->DrawLine(mLeft, mTop + pos,
|
|
mLeft + 4, mTop + pos);
|
|
else
|
|
mDC->DrawLine(mRight - 4, mTop + pos,
|
|
mRight, mTop + pos);
|
|
}
|
|
}
|
|
|
|
if (mMajorLabels[i].text != wxT(""))
|
|
mDC->DrawText(mMajorLabels[i].text,
|
|
mMajorLabels[i].lx,
|
|
mMajorLabels[i].ly);
|
|
}
|
|
|
|
if(mbMinor == true) {
|
|
mDC->SetFont(*mMinorFont);
|
|
for(i=0; i<mNumMinor; i++) {
|
|
int pos = mMinorLabels[i].pos;
|
|
if( mbTicksAtExtremes || ((pos!=0)&&(pos!=iMaxPos)))
|
|
{
|
|
if (mOrientation == wxHORIZONTAL)
|
|
{
|
|
if (mFlip)
|
|
mDC->DrawLine(mLeft + pos, mTop,
|
|
mLeft + pos, mTop + 2);
|
|
else
|
|
mDC->DrawLine(mLeft + pos, mBottom - 2,
|
|
mLeft + pos, mBottom);
|
|
}
|
|
else
|
|
{
|
|
if (mFlip)
|
|
mDC->DrawLine(mLeft, mTop + pos,
|
|
mLeft + 2, mTop + pos);
|
|
else
|
|
mDC->DrawLine(mRight - 2, mTop + pos,
|
|
mRight, mTop + pos);
|
|
}
|
|
}
|
|
if (mMinorLabels[i].text != wxT(""))
|
|
mDC->DrawText(mMinorLabels[i].text,
|
|
mMinorLabels[i].lx,
|
|
mMinorLabels[i].ly);
|
|
}
|
|
}
|
|
|
|
mDC->SetFont(*mMinorMinorFont);
|
|
|
|
for(i=0; i<mNumMinorMinor; i++) {
|
|
if (mMinorMinorLabels[i].text != wxT(""))
|
|
{
|
|
int pos = mMinorMinorLabels[i].pos;
|
|
|
|
if( mbTicksAtExtremes || ((pos!=0)&&(pos!=iMaxPos)))
|
|
{
|
|
if (mOrientation == wxHORIZONTAL)
|
|
{
|
|
if (mFlip)
|
|
mDC->DrawLine(mLeft + pos, mTop,
|
|
mLeft + pos, mTop + 2);
|
|
else
|
|
mDC->DrawLine(mLeft + pos, mBottom - 2,
|
|
mLeft + pos, mBottom);
|
|
}
|
|
else
|
|
{
|
|
if (mFlip)
|
|
mDC->DrawLine(mLeft, mTop + pos,
|
|
mLeft + 2, mTop + pos);
|
|
else
|
|
mDC->DrawLine(mRight - 2, mTop + pos,
|
|
mRight, mTop + pos);
|
|
}
|
|
}
|
|
mDC->DrawText(mMinorMinorLabels[i].text,
|
|
mMinorMinorLabels[i].lx,
|
|
mMinorMinorLabels[i].ly);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ********** Draw grid ***************************
|
|
void Ruler::DrawGrid(wxDC& dc, int length, bool minor, bool major, int xOffset, int yOffset)
|
|
{
|
|
mGridLineLength = length;
|
|
mMajorGrid = major;
|
|
mMinorGrid = minor;
|
|
mDC = &dc;
|
|
|
|
Update();
|
|
|
|
int gridPos;
|
|
wxPen gridPen;
|
|
|
|
if(mbMinor && (mMinorGrid && (mGridLineLength != 0 ))) {
|
|
gridPen.SetColour(178, 178, 178); // very light grey
|
|
mDC->SetPen(gridPen);
|
|
for(int i=0; i<mNumMinor; i++) {
|
|
gridPos = mMinorLabels[i].pos;
|
|
if(mOrientation == wxHORIZONTAL) {
|
|
if((gridPos != 0) && (gridPos != mGridLineLength))
|
|
mDC->DrawLine(gridPos+xOffset, yOffset, gridPos+xOffset, mGridLineLength);
|
|
}
|
|
else {
|
|
if((gridPos != 0) && (gridPos != mGridLineLength))
|
|
mDC->DrawLine(xOffset, gridPos+yOffset, mGridLineLength+xOffset, gridPos+yOffset);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(mMajorGrid && (mGridLineLength != 0 )) {
|
|
gridPen.SetColour(127, 127, 127); // light grey
|
|
mDC->SetPen(gridPen);
|
|
for(int i=0; i<mNumMajor; i++) {
|
|
gridPos = mMajorLabels[i].pos;
|
|
if(mOrientation == wxHORIZONTAL) {
|
|
if((gridPos != 0) && (gridPos != mGridLineLength))
|
|
mDC->DrawLine(gridPos+xOffset, yOffset, gridPos+xOffset, mGridLineLength+yOffset);
|
|
}
|
|
else {
|
|
if((gridPos != 0) && (gridPos != mGridLineLength))
|
|
mDC->DrawLine(xOffset, gridPos+yOffset, mGridLineLength+xOffset, gridPos+yOffset);
|
|
}
|
|
}
|
|
|
|
int zeroPosition = GetZeroPosition();
|
|
if(zeroPosition > 0) {
|
|
// Draw 'zero' grid line in black
|
|
mDC->SetPen(*wxBLACK_PEN);
|
|
if(mOrientation == wxHORIZONTAL) {
|
|
if(zeroPosition != mGridLineLength)
|
|
mDC->DrawLine(zeroPosition+xOffset, yOffset, zeroPosition+xOffset, mGridLineLength+yOffset);
|
|
}
|
|
else {
|
|
if(zeroPosition != mGridLineLength)
|
|
mDC->DrawLine(xOffset, zeroPosition+yOffset, mGridLineLength+xOffset, zeroPosition+yOffset);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int Ruler::FindZero(Label * label, const int len)
|
|
{
|
|
int i = 0;
|
|
double d = 1.0; // arbitrary
|
|
wxString s;
|
|
|
|
do {
|
|
s = label[i].text;
|
|
if(!s.IsEmpty())
|
|
s.ToDouble(&d);
|
|
else
|
|
d = 1.0; // arbitrary, looking for some text here
|
|
i++;
|
|
} while( (i < len) && (d != 0.0) );
|
|
|
|
if(d == 0.0)
|
|
return (label[i - 1].pos) ;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
int Ruler::GetZeroPosition()
|
|
{
|
|
int zero;
|
|
if((zero = FindZero(mMajorLabels, mNumMajor)) < 0)
|
|
zero = FindZero(mMinorLabels, mNumMinor);
|
|
return zero;
|
|
}
|
|
|
|
void Ruler::GetMaxSize(wxCoord *width, wxCoord *height)
|
|
{
|
|
|
|
if (!mValid) {
|
|
wxMemoryDC tmpDC;
|
|
wxBitmap tmpBM(1, 1);
|
|
tmpDC.SelectObject(tmpBM);
|
|
mDC = &tmpDC;
|
|
Update( NULL, 0, 0 );
|
|
}
|
|
|
|
if (width)
|
|
*width = mRect.GetWidth(); //mMaxWidth;
|
|
|
|
if (height)
|
|
*height = mRect.GetHeight(); //mMaxHeight;
|
|
}
|
|
|
|
|
|
void Ruler::SetCustomMode(bool value) { mCustom = value; }
|
|
|
|
void Ruler::SetCustomMajorLabels(wxArrayString *label, int numLabel, int start, int step)
|
|
{
|
|
int i;
|
|
|
|
mNumMajor = numLabel;
|
|
mMajorLabels = new Label[numLabel];
|
|
|
|
for(i=0; i<numLabel; i++) {
|
|
mMajorLabels[i].text = label->Item(i);
|
|
mMajorLabels[i].pos = start + i*step;
|
|
}
|
|
//Remember: delete majorlabels....
|
|
}
|
|
|
|
void Ruler::SetCustomMinorLabels(wxArrayString *label, int numLabel, int start, int step)
|
|
{
|
|
int i;
|
|
|
|
mNumMinor = numLabel;
|
|
mMinorLabels = new Label[numLabel];
|
|
|
|
for(i=0; i<numLabel; i++) {
|
|
mMinorLabels[i].text = label->Item(i);
|
|
mMinorLabels[i].pos = start + i*step;
|
|
}
|
|
//Remember: delete majorlabels....
|
|
}
|
|
|
|
//
|
|
// RulerPanel
|
|
//
|
|
|
|
BEGIN_EVENT_TABLE(RulerPanel, wxPanel)
|
|
EVT_ERASE_BACKGROUND(RulerPanel::OnErase)
|
|
EVT_PAINT(RulerPanel::OnPaint)
|
|
EVT_SIZE(RulerPanel::OnSize)
|
|
END_EVENT_TABLE()
|
|
|
|
IMPLEMENT_CLASS(RulerPanel, wxPanel)
|
|
|
|
RulerPanel::RulerPanel(wxWindow* parent, wxWindowID id,
|
|
const wxPoint& pos /*= wxDefaultPosition*/,
|
|
const wxSize& size /*= wxDefaultSize*/):
|
|
wxPanel(parent, id, pos, size)
|
|
{
|
|
}
|
|
|
|
RulerPanel::~RulerPanel()
|
|
{
|
|
}
|
|
|
|
void RulerPanel::OnErase(wxEraseEvent &evt)
|
|
{
|
|
// Ignore it to prevent flashing
|
|
}
|
|
|
|
void RulerPanel::OnPaint(wxPaintEvent &evt)
|
|
{
|
|
wxPaintDC dc(this);
|
|
|
|
#if defined(__WXGTK__)
|
|
dc.SetBackground(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE)));
|
|
#endif
|
|
|
|
dc.Clear();
|
|
ruler.Draw(dc);
|
|
}
|
|
|
|
void RulerPanel::OnSize(wxSizeEvent &evt)
|
|
{
|
|
Refresh(false);
|
|
}
|
|
|
|
// LL: We're overloading DoSetSize so that we can update the ruler bounds immediately
|
|
// instead of waiting for a wxEVT_SIZE to come through. This is needed by (at least)
|
|
// FreqWindow since it needs to have an updated ruler before RulerPanel gets the
|
|
// size event.
|
|
void RulerPanel::DoSetSize(int x, int y,
|
|
int width, int height,
|
|
int sizeFlags)
|
|
{
|
|
wxPanel::DoSetSize(x, y, width, height, sizeFlags);
|
|
|
|
int w, h;
|
|
GetClientSize(&w, &h);
|
|
|
|
ruler.SetBounds(0, 0, w-1, h-1);
|
|
}
|
|
|
|
/**********************************************************************
|
|
|
|
Implementation of AdornedRulerPanel.
|
|
Either we find a way to make this more generic, Or it will move
|
|
out of the widgets subdirectory into its own source file.
|
|
|
|
**********************************************************************/
|
|
|
|
#include "../ViewInfo.h"
|
|
#include "../AColor.h"
|
|
|
|
BEGIN_EVENT_TABLE(AdornedRulerPanel, wxPanel)
|
|
EVT_ERASE_BACKGROUND(AdornedRulerPanel::OnErase)
|
|
EVT_PAINT(AdornedRulerPanel::OnPaint)
|
|
EVT_SIZE(AdornedRulerPanel::OnSize)
|
|
EVT_MOUSE_EVENTS(AdornedRulerPanel::OnMouseEvents)
|
|
EVT_MOUSE_CAPTURE_LOST(AdornedRulerPanel::OnCaptureLost)
|
|
END_EVENT_TABLE()
|
|
|
|
AdornedRulerPanel::AdornedRulerPanel(wxWindow* parent,
|
|
wxWindowID id,
|
|
const wxPoint& pos,
|
|
const wxSize& size,
|
|
ViewInfo *viewinfo):
|
|
wxPanel( parent, id, pos, size )
|
|
{
|
|
SetLabel( _("Vertical Ruler") );
|
|
SetName( _("Vertical Ruler") );
|
|
|
|
mLeftOffset = 0;
|
|
mCurPos = -1;
|
|
mIndPos = -1;
|
|
mIndType = -1;
|
|
mPlayRegionStart = -1;
|
|
mPlayRegionEnd = -1;
|
|
mMouseEventState = mesNone;
|
|
|
|
mBuffer = new wxBitmap( 1, 1 );
|
|
mViewInfo = viewinfo;
|
|
|
|
mOuter = GetClientRect();
|
|
|
|
mInner = mOuter;
|
|
mInner.x += 1; // +1 for left bevel
|
|
mInner.y += 1; // +1 for top bevel
|
|
mInner.width -= 2; // -2 for left and right bevels
|
|
mInner.height -= 3; // -3 for top and bottom bevels and bottom line
|
|
|
|
ruler.SetBounds( mInner.GetLeft(),
|
|
mInner.GetTop(),
|
|
mInner.GetRight(),
|
|
mInner.GetBottom() );
|
|
ruler.SetLabelEdges( false );
|
|
ruler.SetFormat( Ruler::TimeFormat );
|
|
}
|
|
|
|
AdornedRulerPanel::~AdornedRulerPanel()
|
|
{
|
|
delete mBuffer;
|
|
}
|
|
|
|
void AdornedRulerPanel::OnErase(wxEraseEvent &evt)
|
|
{
|
|
// Ignore it to prevent flashing
|
|
}
|
|
|
|
void AdornedRulerPanel::OnPaint(wxPaintEvent &evt)
|
|
{
|
|
#if defined(__WXMAC__)
|
|
wxPaintDC dc(this);
|
|
#else
|
|
wxBufferedPaintDC dc(this);
|
|
#endif
|
|
|
|
DoDrawBorder(&dc);
|
|
|
|
if (mViewInfo->sel0 < mViewInfo->sel1)
|
|
{
|
|
DoDrawSelection(&dc);
|
|
}
|
|
|
|
if (mIndType >= 0)
|
|
{
|
|
DoDrawIndicator(&dc);
|
|
}
|
|
|
|
DoDrawMarks(&dc, true);
|
|
|
|
if (mViewInfo->sel0 == mViewInfo->sel1)
|
|
{
|
|
DoDrawCursor(&dc);
|
|
}
|
|
|
|
DoDrawPlayRegion(&dc);
|
|
}
|
|
|
|
void AdornedRulerPanel::OnSize(wxSizeEvent &evt)
|
|
{
|
|
mOuter = GetClientRect();
|
|
|
|
mInner = mOuter;
|
|
mInner.x += 1; // +1 for left bevel
|
|
mInner.y += 1; // +1 for top bevel
|
|
mInner.width -= 2; // -2 for left and right bevels
|
|
mInner.height -= 3; // -3 for top and bottom bevels and bottom line
|
|
|
|
ruler.SetBounds( mInner.GetLeft(),
|
|
mInner.GetTop(),
|
|
mInner.GetRight(),
|
|
mInner.GetBottom() );
|
|
|
|
if( mBuffer )
|
|
{
|
|
delete mBuffer;
|
|
}
|
|
|
|
mBuffer = new wxBitmap( mOuter.GetWidth(), mOuter.GetHeight() );
|
|
|
|
Refresh( false );
|
|
}
|
|
|
|
double AdornedRulerPanel::Pos2Time(int p)
|
|
{
|
|
return (p-mLeftOffset) / mViewInfo->zoom + mViewInfo->h;
|
|
}
|
|
|
|
int AdornedRulerPanel::Time2Pos(double t)
|
|
{
|
|
return mLeftOffset + (int)((t-mViewInfo->h) * mViewInfo->zoom + 0.5);
|
|
}
|
|
|
|
bool AdornedRulerPanel::IsWithinMarker(int mousePosX, double markerTime)
|
|
{
|
|
if (markerTime < 0)
|
|
return false;
|
|
|
|
int pixelPos = Time2Pos(markerTime);
|
|
int boundLeft = pixelPos - SELECT_TOLERANCE_PIXEL;
|
|
int boundRight = pixelPos + SELECT_TOLERANCE_PIXEL;
|
|
|
|
return mousePosX >= boundLeft && mousePosX < boundRight;
|
|
}
|
|
|
|
void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt)
|
|
{
|
|
bool isWithinStart = IsWithinMarker(evt.GetX(), mPlayRegionStart);
|
|
bool isWithinEnd = IsWithinMarker(evt.GetX(), mPlayRegionEnd);
|
|
|
|
mLastMouseX = evt.GetX();
|
|
|
|
if (isWithinStart || isWithinEnd)
|
|
SetCursor(wxCursor(wxCURSOR_SIZEWE));
|
|
else
|
|
SetCursor(wxCursor(wxCURSOR_HAND));
|
|
|
|
double mouseTime = Pos2Time(evt.GetX());
|
|
if (mouseTime < 0.0) {
|
|
mouseTime = 0.0;
|
|
}
|
|
|
|
if (evt.LeftDown())
|
|
{
|
|
mButtonDownMousePos = evt.GetX();
|
|
|
|
if (isWithinStart && isWithinEnd)
|
|
{
|
|
// Both could be selected, check which marker is nearer
|
|
if (fabs(mouseTime-mPlayRegionStart) < fabs(mouseTime-mPlayRegionEnd))
|
|
mMouseEventState = mesDraggingPlayRegionStart;
|
|
else
|
|
mMouseEventState = mesDraggingPlayRegionEnd;
|
|
} else if (isWithinStart)
|
|
mMouseEventState = mesDraggingPlayRegionStart;
|
|
else if (isWithinEnd)
|
|
mMouseEventState = mesDraggingPlayRegionEnd;
|
|
else {
|
|
// First, we enter "click" mode to avoid selecting a small range
|
|
// accidentially
|
|
mMouseEventState = mesSelectingPlayRegionClick;
|
|
mPlayRegionStart = mouseTime;
|
|
mPlayRegionEnd = mouseTime;
|
|
Refresh();
|
|
}
|
|
|
|
CaptureMouse();
|
|
}
|
|
|
|
switch (mMouseEventState)
|
|
{
|
|
case mesNone:
|
|
// do nothing
|
|
break;
|
|
case mesDraggingPlayRegionStart:
|
|
mPlayRegionStart = mouseTime;
|
|
Refresh();
|
|
break;
|
|
case mesDraggingPlayRegionEnd:
|
|
mPlayRegionEnd = mouseTime;
|
|
Refresh();
|
|
break;
|
|
case mesSelectingPlayRegionClick:
|
|
if (abs(evt.GetX() - mButtonDownMousePos) > SELECT_TOLERANCE_PIXEL)
|
|
{
|
|
// User moved mouse at least SELECT_TOLERANCE_PIXEL, so change
|
|
// from "click" mode to "range" mode to allow selecting a range
|
|
mMouseEventState = mesSelectingPlayRegionRange;
|
|
mPlayRegionEnd = mouseTime;
|
|
Refresh();
|
|
}
|
|
break;
|
|
case mesSelectingPlayRegionRange:
|
|
mPlayRegionEnd = mouseTime;
|
|
Refresh();
|
|
break;
|
|
}
|
|
|
|
if (evt.LeftUp())
|
|
{
|
|
if (HasCapture())
|
|
ReleaseMouse();
|
|
|
|
if (mPlayRegionStart < 0 && mPlayRegionEnd >= 0)
|
|
{
|
|
// This makes no sense, remove play region
|
|
mPlayRegionStart = -1;
|
|
mPlayRegionEnd = -1;
|
|
}
|
|
|
|
if (mPlayRegionEnd < mPlayRegionStart)
|
|
{
|
|
// Swap values to make sure mPlayRegionStart > mPlayRegionEnd
|
|
double tmp = mPlayRegionStart;
|
|
mPlayRegionStart = mPlayRegionEnd;
|
|
mPlayRegionEnd = tmp;
|
|
}
|
|
|
|
bool startPlaying = mPlayRegionStart >= 0 &&
|
|
(mMouseEventState == mesSelectingPlayRegionClick ||
|
|
mMouseEventState == mesSelectingPlayRegionRange);
|
|
|
|
mMouseEventState = mesNone;
|
|
|
|
if (startPlaying)
|
|
{
|
|
ControlToolBar* ctb = mProject->GetControlToolBar();
|
|
ctb->StopPlaying();
|
|
ctb->PlayDefault();
|
|
}
|
|
}
|
|
}
|
|
|
|
void AdornedRulerPanel::OnCaptureLost(wxMouseCaptureLostEvent &evt)
|
|
{
|
|
wxMouseEvent e(wxEVT_LEFT_UP);
|
|
e.m_x = mLastMouseX;
|
|
OnMouseEvents(e);
|
|
}
|
|
|
|
void AdornedRulerPanel::DoDrawPlayRegion(wxDC * dc)
|
|
{
|
|
double start, end;
|
|
GetPlayRegion(&start, &end);
|
|
|
|
if (start >= 0)
|
|
{
|
|
int x1 = Time2Pos(start) + 1;
|
|
int x2 = Time2Pos(end);
|
|
int y = mInner.height/2;
|
|
|
|
bool isLocked = mProject->IsPlayRegionLocked();
|
|
AColor::PlayRegionColor(dc, isLocked);
|
|
|
|
wxPoint tri[3];
|
|
wxRect r;
|
|
|
|
tri[0].x = x1;
|
|
tri[0].y = y + PLAY_REGION_GLOBAL_OFFSET_Y;
|
|
tri[1].x = x1 + PLAY_REGION_TRIANGLE_SIZE;
|
|
tri[1].y = y - PLAY_REGION_TRIANGLE_SIZE + PLAY_REGION_GLOBAL_OFFSET_Y;
|
|
tri[2].x = x1 + PLAY_REGION_TRIANGLE_SIZE;
|
|
tri[2].y = y + PLAY_REGION_TRIANGLE_SIZE + PLAY_REGION_GLOBAL_OFFSET_Y;
|
|
dc->DrawPolygon(3, tri);
|
|
|
|
r.x = x1;
|
|
r.y = y - PLAY_REGION_TRIANGLE_SIZE + PLAY_REGION_GLOBAL_OFFSET_Y;
|
|
r.width = PLAY_REGION_RECT_WIDTH;
|
|
r.height = PLAY_REGION_TRIANGLE_SIZE*2 + 1;
|
|
dc->DrawRectangle(r);
|
|
|
|
if (end != start)
|
|
{
|
|
tri[0].x = x2;
|
|
tri[0].y = y + PLAY_REGION_GLOBAL_OFFSET_Y;
|
|
tri[1].x = x2 - PLAY_REGION_TRIANGLE_SIZE;
|
|
tri[1].y = y - PLAY_REGION_TRIANGLE_SIZE + PLAY_REGION_GLOBAL_OFFSET_Y;
|
|
tri[2].x = x2 - PLAY_REGION_TRIANGLE_SIZE;
|
|
tri[2].y = y + PLAY_REGION_TRIANGLE_SIZE + PLAY_REGION_GLOBAL_OFFSET_Y;
|
|
dc->DrawPolygon(3, tri);
|
|
|
|
r.x = x2 - PLAY_REGION_RECT_WIDTH + 1;
|
|
r.y = y - PLAY_REGION_TRIANGLE_SIZE + PLAY_REGION_GLOBAL_OFFSET_Y;
|
|
r.width = PLAY_REGION_RECT_WIDTH;
|
|
r.height = PLAY_REGION_TRIANGLE_SIZE*2 + 1;
|
|
dc->DrawRectangle(r);
|
|
|
|
r.x = x1 + PLAY_REGION_TRIANGLE_SIZE;
|
|
r.y = y - PLAY_REGION_RECT_HEIGHT/2 + PLAY_REGION_GLOBAL_OFFSET_Y;
|
|
r.width = x2-x1 - PLAY_REGION_TRIANGLE_SIZE*2;
|
|
r.height = PLAY_REGION_RECT_HEIGHT;
|
|
dc->DrawRectangle(r);
|
|
}
|
|
}
|
|
}
|
|
|
|
void AdornedRulerPanel::DoDrawBorder(wxDC * dc)
|
|
{
|
|
// Draw AdornedRulerPanel border
|
|
AColor::MediumTrackInfo( dc, false );
|
|
dc->DrawRectangle( mInner );
|
|
|
|
wxRect r = mOuter;
|
|
r.width -= 1; // -1 for bevel
|
|
r.height -= 2; // -2 for bevel and for bottom line
|
|
AColor::BevelTrackInfo( *dc, true, r );
|
|
|
|
dc->SetPen( *wxBLACK_PEN );
|
|
dc->DrawLine( mOuter.x,
|
|
mOuter.y + mOuter.height - 1,
|
|
mOuter.x + mOuter.width,
|
|
mOuter.y + mOuter.height - 1 );
|
|
}
|
|
|
|
void AdornedRulerPanel::DoDrawMarks(wxDC * dc, bool /*text */ )
|
|
{
|
|
double min = mViewInfo->h - mLeftOffset / mViewInfo->zoom;
|
|
double max = min + mInner.width / mViewInfo->zoom;
|
|
|
|
ruler.SetTickColour( theTheme.Colour( clrTrackPanelText ) );
|
|
ruler.SetRange( min, max );
|
|
ruler.Draw( *dc );
|
|
}
|
|
|
|
void AdornedRulerPanel::DrawSelection()
|
|
{
|
|
Refresh(false);
|
|
}
|
|
|
|
void AdornedRulerPanel::DoDrawSelection(wxDC * dc)
|
|
{
|
|
// Draw selection
|
|
double zoom = mViewInfo->zoom;
|
|
double sel0 = mViewInfo->sel0 - mViewInfo->h + mLeftOffset / zoom;
|
|
double sel1 = mViewInfo->sel1 - mViewInfo->h + mLeftOffset / zoom;
|
|
|
|
if( sel0 < 0.0 )
|
|
sel0 = 0.0;
|
|
|
|
if( sel1 > ( mInner.width / zoom ) )
|
|
sel1 = mInner.width / zoom;
|
|
|
|
int p0 = int ( sel0 * zoom + 1.5 );
|
|
int p1 = int ( sel1 * zoom + 2.5 );
|
|
|
|
dc->SetBrush( wxBrush( theTheme.Colour( clrRulerBackground )) );
|
|
dc->SetPen( wxPen( theTheme.Colour( clrRulerBackground )) );
|
|
|
|
wxRect r;
|
|
r.x = p0;
|
|
r.y = 1;
|
|
r.width = p1 - p0 - 1;
|
|
r.height = mInner.height;
|
|
dc->DrawRectangle( r );
|
|
}
|
|
|
|
void AdornedRulerPanel::DrawCursor(double pos)
|
|
{
|
|
mCurPos = pos;
|
|
|
|
Refresh(false);
|
|
}
|
|
|
|
void AdornedRulerPanel::DoDrawCursor(wxDC * dc)
|
|
{
|
|
int x = mLeftOffset + int ( ( mCurPos - mViewInfo->h ) * mViewInfo->zoom );
|
|
|
|
// Draw cursor in ruler
|
|
dc->DrawLine( x, 1, x, mInner.height );
|
|
}
|
|
|
|
//
|
|
//This draws the little triangular indicator on the
|
|
//AdornedRulerPanel.
|
|
//
|
|
void AdornedRulerPanel::ClearIndicator()
|
|
{
|
|
mIndType = -1;
|
|
|
|
Refresh(false);
|
|
}
|
|
|
|
void AdornedRulerPanel::DrawIndicator( double pos, bool rec )
|
|
{
|
|
mIndPos = pos;
|
|
|
|
if( mIndPos < 0 )
|
|
{
|
|
ClearIndicator();
|
|
return;
|
|
}
|
|
|
|
mIndType = ( rec ? 0 : 1 );
|
|
|
|
Refresh(false);
|
|
}
|
|
|
|
void AdornedRulerPanel::DoDrawIndicator(wxDC * dc)
|
|
{
|
|
if( mIndType < 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
int indsize = 6;
|
|
int x = mLeftOffset + int ( ( mIndPos - mViewInfo->h ) * mViewInfo->zoom );
|
|
|
|
wxPoint tri[ 3 ];
|
|
tri[ 0 ].x = x - indsize;
|
|
tri[ 0 ].y = 1;
|
|
tri[ 1 ].x = x + indsize;
|
|
tri[ 1 ].y = 1;
|
|
tri[ 2 ].x = x;
|
|
tri[ 2 ].y = ( indsize * 3 ) / 2 + 1;
|
|
|
|
AColor::IndicatorColor( dc, ( mIndType ? true : false ) );
|
|
dc->DrawPolygon( 3, tri );
|
|
}
|
|
|
|
void AdornedRulerPanel::SetPlayRegion(double playRegionStart,
|
|
double playRegionEnd)
|
|
{
|
|
// This is called by AudacityProject to make the play region follow
|
|
// the current selection. But while the user is selecting a play region
|
|
// with the mouse directly in the ruler, changes from outside are blocked.
|
|
if (mMouseEventState != mesNone)
|
|
return;
|
|
|
|
mPlayRegionStart = playRegionStart;
|
|
mPlayRegionEnd = playRegionEnd;
|
|
Refresh();
|
|
}
|
|
|
|
void AdornedRulerPanel::ClearPlayRegion()
|
|
{
|
|
mPlayRegionStart = -1;
|
|
mPlayRegionEnd = -1;
|
|
Refresh();
|
|
}
|
|
|
|
void AdornedRulerPanel::GetPlayRegion(double* playRegionStart,
|
|
double* playRegionEnd)
|
|
{
|
|
if (mPlayRegionStart >= 0 && mPlayRegionEnd >= 0 &&
|
|
mPlayRegionEnd < mPlayRegionStart)
|
|
{
|
|
// swap values to make sure end > start
|
|
*playRegionStart = mPlayRegionEnd;
|
|
*playRegionEnd = mPlayRegionStart;
|
|
} else
|
|
{
|
|
*playRegionStart = mPlayRegionStart;
|
|
*playRegionEnd = mPlayRegionEnd;
|
|
}
|
|
}
|
|
|
|
void AdornedRulerPanel::GetMaxSize(wxCoord *width, wxCoord *height)
|
|
{
|
|
ruler.GetMaxSize(width, height);
|
|
}
|
|
|
|
// Indentation settings for Vim and Emacs and unique identifier for Arch, a
|
|
// version control system. Please do not modify past this point.
|
|
//
|
|
// Local Variables:
|
|
// c-basic-offset: 3
|
|
// indent-tabs-mode: nil
|
|
// End:
|
|
//
|
|
// vim: et sts=3 sw=3
|
|
// arch-tag: 126e06c2-f0c8-490f-bdd6-12581013f13f
|
|
|