@@ -277,7 +277,7 @@ public: | |||
if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, ranges))) | |||
{ | |||
static const double possibleRates[] = { 44100.0, 48000.0, 88200.0, 96000.0, 176400.0, 192000.0 }; | |||
static const double possibleRates[] = { 44100.0, 48000.0, 88200.0, 96000.0, 176400.0, 192000.0, 384000.0 }; | |||
for (int i = 0; i < numElementsInArray (possibleRates); ++i) | |||
{ | |||
@@ -1005,6 +1005,8 @@ public: | |||
#if ! JucePlugin_SilenceInProducesSilenceOut | |||
ioActionFlags &= (AudioUnitRenderActionFlags) ~kAudioUnitRenderAction_OutputIsSilence; | |||
#else | |||
ignoreUnused (ioActionFlags); | |||
#endif | |||
} | |||
@@ -67,7 +67,6 @@ | |||
#include "includes/state.h" | |||
#include "includes/time.h" | |||
#include "includes/ui.h" | |||
#include "includes/units.h" | |||
#include "includes/urid.h" | |||
#include "includes/lv2_external_ui.h" | |||
#include "includes/lv2_programs.h" | |||
@@ -249,13 +248,12 @@ const String makePluginFile (AudioProcessor* const filter) | |||
String text; | |||
// Header | |||
text += "@prefix atom: <" LV2_ATOM_PREFIX "> .\n"; | |||
text += "@prefix doap: <http://usefulinc.com/ns/doap#> .\n"; | |||
text += "@prefix foaf: <http://xmlns.com/foaf/0.1/> .\n"; | |||
text += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n"; | |||
text += "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n"; | |||
text += "@prefix ui: <" LV2_UI_PREFIX "> .\n"; | |||
text += "@prefix units: <" LV2_UNITS_PREFIX "> .\n"; | |||
text += "@prefix atom: <" LV2_ATOM_PREFIX "> .\n"; | |||
text += "@prefix doap: <http://usefulinc.com/ns/doap#> .\n"; | |||
text += "@prefix foaf: <http://xmlns.com/foaf/0.1/> .\n"; | |||
text += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n"; | |||
text += "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n"; | |||
text += "@prefix ui: <" LV2_UI_PREFIX "> .\n"; | |||
text += "\n"; | |||
// Plugin | |||
@@ -399,18 +397,6 @@ const String makePluginFile (AudioProcessor* const filter) | |||
text += " lv2:minimum 0.0 ;\n"; | |||
text += " lv2:maximum 1.0 ;\n"; | |||
String label(filter->getParameterLabel(i)); | |||
if (label.isNotEmpty()) | |||
{ | |||
text += " units:unit [\n"; | |||
text += " a units:Unit ;\n"; | |||
text += " rdfs:label \"" + label + "\" ;\n"; | |||
text += " units:symbol \"" + label + "\" ;\n"; | |||
text += " units:render \"%f " + label + "\" ;\n"; | |||
text += " ] ;\n"; | |||
} | |||
if (! filter->isParameterAutomatable(i)) | |||
text += " lv2:portProperty <" LV2_PORT_PROPS__expensive "> ;\n"; | |||
@@ -871,7 +857,7 @@ public: | |||
#if JucePlugin_ProducesMidiOutput | |||
controlPortOffset += 1; | |||
#endif | |||
controlPortOffset += 2; // freewheel and sampleRate | |||
controlPortOffset += 2; // freewheel and latency | |||
controlPortOffset += JucePlugin_MaxNumInputChannels; | |||
controlPortOffset += JucePlugin_MaxNumOutputChannels; | |||
@@ -1119,6 +1105,7 @@ public: | |||
uridMap (nullptr), | |||
uridAtomBlank (0), | |||
uridAtomObject (0), | |||
uridAtomDouble (0), | |||
uridAtomFloat (0), | |||
uridAtomInt (0), | |||
uridAtomLong (0), | |||
@@ -1200,6 +1187,7 @@ public: | |||
uridAtomBlank = uridMap->map(uridMap->handle, LV2_ATOM__Blank); | |||
uridAtomObject = uridMap->map(uridMap->handle, LV2_ATOM__Object); | |||
uridAtomDouble = uridMap->map(uridMap->handle, LV2_ATOM__Double); | |||
uridAtomFloat = uridMap->map(uridMap->handle, LV2_ATOM__Float); | |||
uridAtomInt = uridMap->map(uridMap->handle, LV2_ATOM__Int); | |||
uridAtomLong = uridMap->map(uridMap->handle, LV2_ATOM__Long); | |||
@@ -1413,59 +1401,140 @@ public: | |||
LV2_Atom* bar = nullptr; | |||
LV2_Atom* barBeat = nullptr; | |||
LV2_Atom* beatsPerBar = nullptr; | |||
LV2_Atom* bpm = nullptr; | |||
LV2_Atom* beatUnit = nullptr; | |||
LV2_Atom* beatsPerBar = nullptr; | |||
LV2_Atom* beatsPerMinute = nullptr; | |||
LV2_Atom* frame = nullptr; | |||
LV2_Atom* speed = nullptr; | |||
lv2_atom_object_get (obj, | |||
uridTimeBar, &bar, | |||
uridTimeBarBeat, &barBeat, | |||
uridTimeBeatsPerBar, &beatsPerBar, | |||
uridTimeBeatsPerMinute, &bpm, | |||
uridTimeBeatUnit, &beatUnit, | |||
uridTimeBeatsPerBar, &beatsPerBar, | |||
uridTimeBeatsPerMinute, &beatsPerMinute, | |||
uridTimeFrame, &frame, | |||
uridTimeSpeed, &speed, | |||
nullptr); | |||
if (bpm != nullptr && bpm->type == uridAtomFloat) | |||
curPosInfo.bpm = ((LV2_Atom_Float*)bpm)->body; | |||
// need to handle this first as other values depend on it | |||
if (speed != nullptr) | |||
{ | |||
/**/ if (speed->type == uridAtomDouble) | |||
lastPositionData.speed = ((LV2_Atom_Double*)speed)->body; | |||
else if (speed->type == uridAtomFloat) | |||
lastPositionData.speed = ((LV2_Atom_Float*)speed)->body; | |||
else if (speed->type == uridAtomInt) | |||
lastPositionData.speed = ((LV2_Atom_Int*)speed)->body; | |||
else if (speed->type == uridAtomLong) | |||
lastPositionData.speed = ((LV2_Atom_Long*)speed)->body; | |||
curPosInfo.isPlaying = lastPositionData.speed != 0.0; | |||
} | |||
if (beatsPerBar != nullptr && beatsPerBar->type == uridAtomFloat) | |||
if (bar != nullptr) | |||
{ | |||
float beatsPerBarValue = ((LV2_Atom_Float*)beatsPerBar)->body; | |||
curPosInfo.timeSigNumerator = beatsPerBarValue; | |||
/**/ if (bar->type == uridAtomDouble) | |||
lastPositionData.bar = ((LV2_Atom_Double*)bar)->body; | |||
else if (bar->type == uridAtomFloat) | |||
lastPositionData.bar = ((LV2_Atom_Float*)bar)->body; | |||
else if (bar->type == uridAtomInt) | |||
lastPositionData.bar = ((LV2_Atom_Int*)bar)->body; | |||
else if (bar->type == uridAtomLong) | |||
lastPositionData.bar = ((LV2_Atom_Long*)bar)->body; | |||
} | |||
if (bar != nullptr && bar->type == uridAtomLong) | |||
{ | |||
float barValue = ((LV2_Atom_Long*)bar)->body; | |||
curPosInfo.ppqPositionOfLastBarStart = barValue * beatsPerBarValue; | |||
if (barBeat != nullptr && barBeat->type == uridAtomFloat) | |||
{ | |||
float barBeatValue = ((LV2_Atom_Float*)barBeat)->body; | |||
curPosInfo.ppqPosition = curPosInfo.ppqPositionOfLastBarStart + barBeatValue; | |||
} | |||
} | |||
if (barBeat != nullptr) | |||
{ | |||
/**/ if (barBeat->type == uridAtomDouble) | |||
lastPositionData.barBeat = ((LV2_Atom_Double*)barBeat)->body; | |||
else if (barBeat->type == uridAtomFloat) | |||
lastPositionData.barBeat = ((LV2_Atom_Float*)barBeat)->body; | |||
else if (barBeat->type == uridAtomInt) | |||
lastPositionData.barBeat = ((LV2_Atom_Int*)barBeat)->body; | |||
else if (barBeat->type == uridAtomLong) | |||
lastPositionData.barBeat = ((LV2_Atom_Long*)barBeat)->body; | |||
} | |||
if (beatUnit != nullptr) | |||
{ | |||
if (beatUnit->type == uridAtomInt) | |||
curPosInfo.timeSigDenominator = ((LV2_Atom_Int*)beatUnit)->body; | |||
/**/ if (beatUnit->type == uridAtomDouble) | |||
lastPositionData.beatUnit = ((LV2_Atom_Double*)beatUnit)->body; | |||
else if (beatUnit->type == uridAtomFloat) | |||
curPosInfo.timeSigDenominator = ((LV2_Atom_Float*)beatUnit)->body; | |||
lastPositionData.beatUnit = ((LV2_Atom_Float*)beatUnit)->body; | |||
else if (beatUnit->type == uridAtomInt) | |||
lastPositionData.beatUnit = ((LV2_Atom_Int*)beatUnit)->body; | |||
else if (beatUnit->type == uridAtomLong) | |||
lastPositionData.beatUnit = ((LV2_Atom_Long*)beatUnit)->body; | |||
if (lastPositionData.beatUnit > 0) | |||
curPosInfo.timeSigDenominator = lastPositionData.beatUnit; | |||
} | |||
if (beatsPerBar != nullptr) | |||
{ | |||
/**/ if (beatsPerBar->type == uridAtomDouble) | |||
lastPositionData.beatsPerBar = ((LV2_Atom_Double*)beatsPerBar)->body; | |||
else if (beatsPerBar->type == uridAtomFloat) | |||
lastPositionData.beatsPerBar = ((LV2_Atom_Float*)beatsPerBar)->body; | |||
else if (beatsPerBar->type == uridAtomInt) | |||
lastPositionData.beatsPerBar = ((LV2_Atom_Int*)beatsPerBar)->body; | |||
else if (beatsPerBar->type == uridAtomLong) | |||
lastPositionData.beatsPerBar = ((LV2_Atom_Long*)beatsPerBar)->body; | |||
if (lastPositionData.beatsPerBar > 0.0f) | |||
curPosInfo.timeSigNumerator = lastPositionData.beatsPerBar; | |||
} | |||
if (frame != nullptr && frame->type == uridAtomLong) | |||
if (beatsPerMinute != nullptr) | |||
{ | |||
curPosInfo.timeInSamples = ((LV2_Atom_Long*)frame)->body; | |||
curPosInfo.timeInSeconds = double(curPosInfo.timeInSamples)/sampleRate; | |||
/**/ if (beatsPerMinute->type == uridAtomDouble) | |||
lastPositionData.beatsPerMinute = ((LV2_Atom_Double*)beatsPerMinute)->body; | |||
else if (beatsPerMinute->type == uridAtomFloat) | |||
lastPositionData.beatsPerMinute = ((LV2_Atom_Float*)beatsPerMinute)->body; | |||
else if (beatsPerMinute->type == uridAtomInt) | |||
lastPositionData.beatsPerMinute = ((LV2_Atom_Int*)beatsPerMinute)->body; | |||
else if (beatsPerMinute->type == uridAtomLong) | |||
lastPositionData.beatsPerMinute = ((LV2_Atom_Long*)beatsPerMinute)->body; | |||
if (lastPositionData.beatsPerMinute > 0.0f) | |||
{ | |||
curPosInfo.bpm = lastPositionData.beatsPerMinute; | |||
if (lastPositionData.speed != 0) | |||
curPosInfo.bpm *= std::abs(lastPositionData.speed); | |||
} | |||
} | |||
if (speed != nullptr && speed->type == uridAtomFloat) | |||
curPosInfo.isPlaying = ((LV2_Atom_Float*)speed)->body == 1.0f; | |||
if (frame != nullptr) | |||
{ | |||
/**/ if (frame->type == uridAtomDouble) | |||
lastPositionData.frame = ((LV2_Atom_Double*)frame)->body; | |||
else if (frame->type == uridAtomFloat) | |||
lastPositionData.frame = ((LV2_Atom_Float*)frame)->body; | |||
else if (frame->type == uridAtomInt) | |||
lastPositionData.frame = ((LV2_Atom_Int*)frame)->body; | |||
else if (frame->type == uridAtomLong) | |||
lastPositionData.frame = ((LV2_Atom_Long*)frame)->body; | |||
if (lastPositionData.frame >= 0) | |||
{ | |||
curPosInfo.timeInSamples = lastPositionData.frame; | |||
curPosInfo.timeInSeconds = double(curPosInfo.timeInSamples)/sampleRate; | |||
} | |||
} | |||
if (lastPositionData.bar >= 0 && lastPositionData.beatsPerBar > 0.0f) | |||
{ | |||
curPosInfo.ppqPositionOfLastBarStart = lastPositionData.bar * lastPositionData.beatsPerBar; | |||
if (lastPositionData.barBeat >= 0.0f) | |||
curPosInfo.ppqPosition = curPosInfo.ppqPositionOfLastBarStart + lastPositionData.barBeat; | |||
} | |||
lastPositionData.extraValid = (lastPositionData.beatsPerMinute > 0.0 && | |||
lastPositionData.beatUnit > 0 && | |||
lastPositionData.beatsPerBar > 0.0f); | |||
} | |||
#endif | |||
} | |||
@@ -1478,6 +1547,52 @@ public: | |||
} | |||
} | |||
#if JucePlugin_WantsLV2TimePos | |||
// update timePos for next callback | |||
if (lastPositionData.speed != 0.0) | |||
{ | |||
if (lastPositionData.speed > 0.0) | |||
{ | |||
// playing forwards | |||
lastPositionData.frame += sampleCount; | |||
} | |||
else | |||
{ | |||
// playing backwards | |||
lastPositionData.frame -= sampleCount; | |||
if (lastPositionData.frame < 0) | |||
lastPositionData.frame = 0; | |||
} | |||
curPosInfo.timeInSamples = lastPositionData.frame; | |||
curPosInfo.timeInSeconds = double(curPosInfo.timeInSamples)/sampleRate; | |||
if (lastPositionData.extraValid) | |||
{ | |||
const double beatsPerMinute = lastPositionData.beatsPerMinute * lastPositionData.speed; | |||
const double framesPerBeat = 60.0 * sampleRate / beatsPerMinute; | |||
const double addedBarBeats = double(sampleCount) / framesPerBeat; | |||
if (lastPositionData.bar >= 0 && lastPositionData.barBeat >= 0.0f) | |||
{ | |||
lastPositionData.bar += std::floor((lastPositionData.barBeat+addedBarBeats)/ | |||
lastPositionData.beatsPerBar); | |||
lastPositionData.barBeat = std::fmod(lastPositionData.barBeat+addedBarBeats, | |||
lastPositionData.beatsPerBar); | |||
if (lastPositionData.bar < 0) | |||
lastPositionData.bar = 0; | |||
curPosInfo.ppqPositionOfLastBarStart = lastPositionData.bar * lastPositionData.beatsPerBar; | |||
curPosInfo.ppqPosition = curPosInfo.ppqPositionOfLastBarStart + lastPositionData.barBeat; | |||
} | |||
curPosInfo.bpm = std::abs(beatsPerMinute); | |||
} | |||
} | |||
#endif | |||
if (! midiEvents.isEmpty()) | |||
{ | |||
#if JucePlugin_ProducesMidiOutput | |||
@@ -1725,9 +1840,32 @@ private: | |||
Array<float> lastControlValues; | |||
AudioPlayHead::CurrentPositionInfo curPosInfo; | |||
struct Lv2PositionData { | |||
int64_t bar; | |||
float barBeat; | |||
uint32_t beatUnit; | |||
float beatsPerBar; | |||
float beatsPerMinute; | |||
int64_t frame; | |||
double speed; | |||
bool extraValid; | |||
Lv2PositionData() | |||
: bar(-1), | |||
barBeat(-1.0f), | |||
beatUnit(0), | |||
beatsPerBar(0.0f), | |||
beatsPerMinute(0.0f), | |||
frame(-1), | |||
speed(0.0), | |||
extraValid(false) {} | |||
}; | |||
Lv2PositionData lastPositionData; | |||
const LV2_URID_Map* uridMap; | |||
LV2_URID uridAtomBlank; | |||
LV2_URID uridAtomObject; | |||
LV2_URID uridAtomDouble; | |||
LV2_URID uridAtomFloat; | |||
LV2_URID uridAtomInt; | |||
LV2_URID uridAtomLong; | |||
@@ -31,7 +31,7 @@ using namespace juce; | |||
namespace juce | |||
{ | |||
#if JUCE_MAC && ! DOXYGEN | |||
#if JUCE_MAC | |||
#define Point juce::Point | |||
#define Component juce::Component | |||
@@ -45,6 +45,16 @@ struct ContainerDeletePolicy | |||
{ | |||
static void destroy (ObjectType* object) | |||
{ | |||
// If the line below triggers a compiler error, it means that you are using | |||
// an incomplete type for ObjectType (for example, a type that is declared | |||
// but not defined). This is a problem because then the following delete is | |||
// undefined behaviour. The purpose of the sizeof is to capture this situation. | |||
// If this was caused by a ScopedPointer to a forward-declared type, move the | |||
// implementation of all methods trying to use the ScopedPointer (e.g. the destructor | |||
// of the class owning it) into cpp files where they can see to the definition | |||
// of ObjectType. This should fix the error. | |||
ignoreUnused (sizeof (ObjectType)); | |||
delete object; | |||
} | |||
}; | |||
@@ -52,21 +52,21 @@ public: | |||
} | |||
/** Creates a rectangle with a given position and size. */ | |||
Rectangle (const ValueType initialX, const ValueType initialY, | |||
const ValueType width, const ValueType height) noexcept | |||
Rectangle (ValueType initialX, ValueType initialY, | |||
ValueType width, ValueType height) noexcept | |||
: pos (initialX, initialY), | |||
w (width), h (height) | |||
{ | |||
} | |||
/** Creates a rectangle with a given size, and a position of (0, 0). */ | |||
Rectangle (const ValueType width, const ValueType height) noexcept | |||
Rectangle (ValueType width, ValueType height) noexcept | |||
: w (width), h (height) | |||
{ | |||
} | |||
/** Creates a Rectangle from the positions of two opposite corners. */ | |||
Rectangle (const Point<ValueType> corner1, const Point<ValueType> corner2) noexcept | |||
Rectangle (Point<ValueType> corner1, Point<ValueType> corner2) noexcept | |||
: pos (jmin (corner1.x, corner2.x), | |||
jmin (corner1.y, corner2.y)), | |||
w (corner1.x - corner2.x), | |||
@@ -80,8 +80,8 @@ public: | |||
The right and bottom values must be larger than the left and top ones, or the resulting | |||
rectangle will have a negative size. | |||
*/ | |||
static Rectangle leftTopRightBottom (const ValueType left, const ValueType top, | |||
const ValueType right, const ValueType bottom) noexcept | |||
static Rectangle leftTopRightBottom (ValueType left, ValueType top, | |||
ValueType right, ValueType bottom) noexcept | |||
{ | |||
return Rectangle (left, top, right - left, bottom - top); | |||
} | |||
@@ -134,17 +134,17 @@ public: | |||
/** Returns the aspect ratio of the rectangle's width / height. | |||
If widthOverHeight is true, it returns width / height; if widthOverHeight is false, | |||
it returns height / width. */ | |||
ValueType getAspectRatio (const bool widthOverHeight = true) const noexcept { return widthOverHeight ? w / h : h / w; } | |||
ValueType getAspectRatio (bool widthOverHeight = true) const noexcept { return widthOverHeight ? w / h : h / w; } | |||
//============================================================================== | |||
/** Returns the rectangle's top-left position as a Point. */ | |||
inline Point<ValueType> getPosition() const noexcept { return pos; } | |||
/** Changes the position of the rectangle's top-left corner (leaving its size unchanged). */ | |||
inline void setPosition (const Point<ValueType> newPos) noexcept { pos = newPos; } | |||
inline void setPosition (Point<ValueType> newPos) noexcept { pos = newPos; } | |||
/** Changes the position of the rectangle's top-left corner (leaving its size unchanged). */ | |||
inline void setPosition (const ValueType newX, const ValueType newY) noexcept { pos.setXY (newX, newY); } | |||
inline void setPosition (ValueType newX, ValueType newY) noexcept { pos.setXY (newX, newY); } | |||
/** Returns the rectangle's top-left position as a Point. */ | |||
Point<ValueType> getTopLeft() const noexcept { return pos; } | |||
@@ -158,62 +158,75 @@ public: | |||
/** Returns the rectangle's bottom-right position as a Point. */ | |||
Point<ValueType> getBottomRight() const noexcept { return Point<ValueType> (pos.x + w, pos.y + h); } | |||
/** Returns the rectangle's left and right positions as a Range. */ | |||
Range<ValueType> getHorizontalRange() const noexcept { return Range<ValueType>::withStartAndLength (pos.x, w); } | |||
/** Returns the rectangle's top and bottom positions as a Range. */ | |||
Range<ValueType> getVerticalRange() const noexcept { return Range<ValueType>::withStartAndLength (pos.y, h); } | |||
/** Changes the rectangle's size, leaving the position of its top-left corner unchanged. */ | |||
void setSize (const ValueType newWidth, const ValueType newHeight) noexcept { w = newWidth; h = newHeight; } | |||
void setSize (ValueType newWidth, ValueType newHeight) noexcept { w = newWidth; h = newHeight; } | |||
/** Changes all the rectangle's coordinates. */ | |||
void setBounds (const ValueType newX, const ValueType newY, | |||
const ValueType newWidth, const ValueType newHeight) noexcept { pos.x = newX; pos.y = newY; w = newWidth; h = newHeight; } | |||
void setBounds (ValueType newX, ValueType newY, | |||
ValueType newWidth, ValueType newHeight) noexcept { pos.x = newX; pos.y = newY; w = newWidth; h = newHeight; } | |||
/** Changes the rectangle's X coordinate */ | |||
inline void setX (const ValueType newX) noexcept { pos.x = newX; } | |||
inline void setX (ValueType newX) noexcept { pos.x = newX; } | |||
/** Changes the rectangle's Y coordinate */ | |||
inline void setY (const ValueType newY) noexcept { pos.y = newY; } | |||
inline void setY (ValueType newY) noexcept { pos.y = newY; } | |||
/** Changes the rectangle's width */ | |||
inline void setWidth (const ValueType newWidth) noexcept { w = newWidth; } | |||
inline void setWidth (ValueType newWidth) noexcept { w = newWidth; } | |||
/** Changes the rectangle's height */ | |||
inline void setHeight (const ValueType newHeight) noexcept { h = newHeight; } | |||
inline void setHeight (ValueType newHeight) noexcept { h = newHeight; } | |||
/** Changes the position of the rectangle's centre (leaving its size unchanged). */ | |||
inline void setCentre (const ValueType newCentreX, const ValueType newCentreY) noexcept { pos.x = newCentreX - w / (ValueType) 2; pos.y = newCentreY - h / (ValueType) 2; } | |||
inline void setCentre (ValueType newCentreX, ValueType newCentreY) noexcept { pos.x = newCentreX - w / (ValueType) 2; | |||
pos.y = newCentreY - h / (ValueType) 2; } | |||
/** Changes the position of the rectangle's centre (leaving its size unchanged). */ | |||
inline void setCentre (const Point<ValueType> newCentre) noexcept { setCentre (newCentre.x, newCentre.y); } | |||
inline void setCentre (Point<ValueType> newCentre) noexcept { setCentre (newCentre.x, newCentre.y); } | |||
/** Changes the position of the rectangle's left and right edges. */ | |||
void setHorizontalRange (Range<ValueType> range) noexcept { pos.x = range.getStart(); w = range.getLength(); } | |||
/** Changes the position of the rectangle's top and bottom edges. */ | |||
void setVerticalRange (Range<ValueType> range) noexcept { pos.y = range.getStart(); h = range.getLength(); } | |||
/** Returns a rectangle which has the same size and y-position as this one, but with a different x-position. */ | |||
Rectangle withX (const ValueType newX) const noexcept { return Rectangle (newX, pos.y, w, h); } | |||
Rectangle withX (ValueType newX) const noexcept { return Rectangle (newX, pos.y, w, h); } | |||
/** Returns a rectangle which has the same size and x-position as this one, but with a different y-position. */ | |||
Rectangle withY (const ValueType newY) const noexcept { return Rectangle (pos.x, newY, w, h); } | |||
Rectangle withY (ValueType newY) const noexcept { return Rectangle (pos.x, newY, w, h); } | |||
/** Returns a rectangle with the same size as this one, but a new position. */ | |||
Rectangle withPosition (const ValueType newX, const ValueType newY) const noexcept { return Rectangle (newX, newY, w, h); } | |||
Rectangle withPosition (ValueType newX, ValueType newY) const noexcept { return Rectangle (newX, newY, w, h); } | |||
/** Returns a rectangle with the same size as this one, but a new position. */ | |||
Rectangle withPosition (const Point<ValueType> newPos) const noexcept { return Rectangle (newPos.x, newPos.y, w, h); } | |||
Rectangle withPosition (Point<ValueType> newPos) const noexcept { return Rectangle (newPos.x, newPos.y, w, h); } | |||
/** Returns a rectangle whose size is the same as this one, but whose top-left position is (0, 0). */ | |||
Rectangle withZeroOrigin() const noexcept { return Rectangle (w, h); } | |||
Rectangle withZeroOrigin() const noexcept { return Rectangle (w, h); } | |||
/** Returns a rectangle with the same size as this one, but a new centre position. */ | |||
Rectangle withCentre (const Point<ValueType> newCentre) const noexcept { return Rectangle (newCentre.x - w / (ValueType) 2, | |||
newCentre.y - h / (ValueType) 2, w, h); } | |||
Rectangle withCentre (Point<ValueType> newCentre) const noexcept { return Rectangle (newCentre.x - w / (ValueType) 2, | |||
newCentre.y - h / (ValueType) 2, w, h); } | |||
/** Returns a rectangle which has the same position and height as this one, but with a different width. */ | |||
Rectangle withWidth (ValueType newWidth) const noexcept { return Rectangle (pos.x, pos.y, newWidth, h); } | |||
Rectangle withWidth (ValueType newWidth) const noexcept { return Rectangle (pos.x, pos.y, newWidth, h); } | |||
/** Returns a rectangle which has the same position and width as this one, but with a different height. */ | |||
Rectangle withHeight (ValueType newHeight) const noexcept { return Rectangle (pos.x, pos.y, w, newHeight); } | |||
Rectangle withHeight (ValueType newHeight) const noexcept { return Rectangle (pos.x, pos.y, w, newHeight); } | |||
/** Returns a rectangle with the same top-left position as this one, but a new size. */ | |||
Rectangle withSize (ValueType newWidth, ValueType newHeight) const noexcept { return Rectangle (pos.x, pos.y, newWidth, newHeight); } | |||
Rectangle withSize (ValueType newWidth, ValueType newHeight) const noexcept { return Rectangle (pos.x, pos.y, newWidth, newHeight); } | |||
/** Returns a rectangle with the same centre position as this one, but a new size. */ | |||
Rectangle withSizeKeepingCentre (ValueType newWidth, ValueType newHeight) const noexcept { return Rectangle (pos.x + (w - newWidth) / (ValueType) 2, | |||
pos.y + (h - newHeight) / (ValueType) 2, newWidth, newHeight); } | |||
Rectangle withSizeKeepingCentre (ValueType newWidth, ValueType newHeight) const noexcept { return Rectangle (pos.x + (w - newWidth) / (ValueType) 2, | |||
pos.y + (h - newHeight) / (ValueType) 2, newWidth, newHeight); } | |||
/** Moves the x position, adjusting the width so that the right-hand edge remains in the same place. | |||
If the x is moved to be on the right of the current right-hand edge, the width will be set to zero. | |||
@@ -277,41 +290,41 @@ public: | |||
//============================================================================== | |||
/** Moves the rectangle's position by adding amount to its x and y coordinates. */ | |||
void translate (const ValueType deltaX, | |||
const ValueType deltaY) noexcept | |||
void translate (ValueType deltaX, | |||
ValueType deltaY) noexcept | |||
{ | |||
pos.x += deltaX; | |||
pos.y += deltaY; | |||
} | |||
/** Returns a rectangle which is the same as this one moved by a given amount. */ | |||
Rectangle translated (const ValueType deltaX, | |||
const ValueType deltaY) const noexcept | |||
Rectangle translated (ValueType deltaX, | |||
ValueType deltaY) const noexcept | |||
{ | |||
return Rectangle (pos.x + deltaX, pos.y + deltaY, w, h); | |||
} | |||
/** Returns a rectangle which is the same as this one moved by a given amount. */ | |||
Rectangle operator+ (const Point<ValueType> deltaPosition) const noexcept | |||
Rectangle operator+ (Point<ValueType> deltaPosition) const noexcept | |||
{ | |||
return Rectangle (pos.x + deltaPosition.x, pos.y + deltaPosition.y, w, h); | |||
} | |||
/** Moves this rectangle by a given amount. */ | |||
Rectangle& operator+= (const Point<ValueType> deltaPosition) noexcept | |||
Rectangle& operator+= (Point<ValueType> deltaPosition) noexcept | |||
{ | |||
pos += deltaPosition; | |||
return *this; | |||
} | |||
/** Returns a rectangle which is the same as this one moved by a given amount. */ | |||
Rectangle operator- (const Point<ValueType> deltaPosition) const noexcept | |||
Rectangle operator- (Point<ValueType> deltaPosition) const noexcept | |||
{ | |||
return Rectangle (pos.x - deltaPosition.x, pos.y - deltaPosition.y, w, h); | |||
} | |||
/** Moves this rectangle by a given amount. */ | |||
Rectangle& operator-= (const Point<ValueType> deltaPosition) noexcept | |||
Rectangle& operator-= (Point<ValueType> deltaPosition) noexcept | |||
{ | |||
pos -= deltaPosition; | |||
return *this; | |||
@@ -396,8 +409,8 @@ public: | |||
Effectively, its new size is (x - deltaX, y - deltaY, w + deltaX * 2, h + deltaY * 2). | |||
@see expanded, reduce, reduced | |||
*/ | |||
void expand (const ValueType deltaX, | |||
const ValueType deltaY) noexcept | |||
void expand (ValueType deltaX, | |||
ValueType deltaY) noexcept | |||
{ | |||
const ValueType nw = jmax (ValueType(), w + deltaX * 2); | |||
const ValueType nh = jmax (ValueType(), h + deltaY * 2); | |||
@@ -409,8 +422,8 @@ public: | |||
Effectively, the rectangle returned is (x - deltaX, y - deltaY, w + deltaX * 2, h + deltaY * 2). | |||
@see expand, reduce, reduced | |||
*/ | |||
Rectangle expanded (const ValueType deltaX, | |||
const ValueType deltaY) const noexcept | |||
Rectangle expanded (ValueType deltaX, | |||
ValueType deltaY) const noexcept | |||
{ | |||
const ValueType nw = jmax (ValueType(), w + deltaX * 2); | |||
const ValueType nh = jmax (ValueType(), h + deltaY * 2); | |||
@@ -422,7 +435,7 @@ public: | |||
Effectively, the rectangle returned is (x - delta, y - delta, w + delta * 2, h + delta * 2). | |||
@see expand, reduce, reduced | |||
*/ | |||
Rectangle expanded (const ValueType delta) const noexcept | |||
Rectangle expanded (ValueType delta) const noexcept | |||
{ | |||
return expanded (delta, delta); | |||
} | |||
@@ -432,8 +445,8 @@ public: | |||
Effectively, its new size is (x + deltaX, y + deltaY, w - deltaX * 2, h - deltaY * 2). | |||
@see reduced, expand, expanded | |||
*/ | |||
void reduce (const ValueType deltaX, | |||
const ValueType deltaY) noexcept | |||
void reduce (ValueType deltaX, | |||
ValueType deltaY) noexcept | |||
{ | |||
expand (-deltaX, -deltaY); | |||
} | |||
@@ -443,8 +456,8 @@ public: | |||
Effectively, the rectangle returned is (x + deltaX, y + deltaY, w - deltaX * 2, h - deltaY * 2). | |||
@see reduce, expand, expanded | |||
*/ | |||
Rectangle reduced (const ValueType deltaX, | |||
const ValueType deltaY) const noexcept | |||
Rectangle reduced (ValueType deltaX, | |||
ValueType deltaY) const noexcept | |||
{ | |||
return expanded (-deltaX, -deltaY); | |||
} | |||
@@ -454,7 +467,7 @@ public: | |||
Effectively, the rectangle returned is (x + delta, y + delta, w - delta * 2, h - delta * 2). | |||
@see reduce, expand, expanded | |||
*/ | |||
Rectangle reduced (const ValueType delta) const noexcept | |||
Rectangle reduced (ValueType delta) const noexcept | |||
{ | |||
return reduced (delta, delta); | |||
} | |||
@@ -468,7 +481,7 @@ public: | |||
If amountToRemove is greater than the height of this rectangle, it'll be clipped to | |||
that value. | |||
*/ | |||
Rectangle removeFromTop (const ValueType amountToRemove) noexcept | |||
Rectangle removeFromTop (ValueType amountToRemove) noexcept | |||
{ | |||
const Rectangle r (pos.x, pos.y, w, jmin (amountToRemove, h)); | |||
pos.y += r.h; h -= r.h; | |||
@@ -484,7 +497,7 @@ public: | |||
If amountToRemove is greater than the width of this rectangle, it'll be clipped to | |||
that value. | |||
*/ | |||
Rectangle removeFromLeft (const ValueType amountToRemove) noexcept | |||
Rectangle removeFromLeft (ValueType amountToRemove) noexcept | |||
{ | |||
const Rectangle r (pos.x, pos.y, jmin (amountToRemove, w), h); | |||
pos.x += r.w; w -= r.w; | |||
@@ -533,13 +546,13 @@ public: | |||
bool operator!= (const Rectangle& other) const noexcept { return pos != other.pos || w != other.w || h != other.h; } | |||
/** Returns true if this coordinate is inside the rectangle. */ | |||
bool contains (const ValueType xCoord, const ValueType yCoord) const noexcept | |||
bool contains (ValueType xCoord, ValueType yCoord) const noexcept | |||
{ | |||
return xCoord >= pos.x && yCoord >= pos.y && xCoord < pos.x + w && yCoord < pos.y + h; | |||
} | |||
/** Returns true if this coordinate is inside the rectangle. */ | |||
bool contains (const Point<ValueType> point) const noexcept | |||
bool contains (Point<ValueType> point) const noexcept | |||
{ | |||
return point.x >= pos.x && point.y >= pos.y && point.x < pos.x + w && point.y < pos.y + h; | |||
} | |||
@@ -552,7 +565,7 @@ public: | |||
} | |||
/** Returns the nearest point to the specified point that lies within this rectangle. */ | |||
Point<ValueType> getConstrainedPoint (const Point<ValueType> point) const noexcept | |||
Point<ValueType> getConstrainedPoint (Point<ValueType> point) const noexcept | |||
{ | |||
return Point<ValueType> (jlimit (pos.x, pos.x + w, point.x), | |||
jlimit (pos.y, pos.y + h, point.y)); | |||
@@ -59,6 +59,7 @@ void DrawableButton::setImages (const Drawable* normal, | |||
overImageOn = copyDrawableIfNotNull (overOn); | |||
downImageOn = copyDrawableIfNotNull (downOn); | |||
disabledImageOn = copyDrawableIfNotNull (disabledOn); | |||
currentImage = nullptr; | |||
buttonStateChanged(); | |||
} | |||
@@ -43,7 +43,7 @@ public: | |||
imageOffset (offset), | |||
hasCheckedForExternalDrag (false) | |||
{ | |||
setSize (im.getWidth(), im.getHeight()); | |||
updateSize(); | |||
if (mouseDragSource == nullptr) | |||
mouseDragSource = sourceComponent; | |||
@@ -160,6 +160,13 @@ public: | |||
forceMouseCursorUpdate(); | |||
} | |||
void updateImage (const Image& newImage) | |||
{ | |||
image = newImage; | |||
updateSize(); | |||
repaint(); | |||
} | |||
void timerCallback() override | |||
{ | |||
forceMouseCursorUpdate(); | |||
@@ -207,6 +214,11 @@ private: | |||
bool hasCheckedForExternalDrag; | |||
Time lastTimeOverTarget; | |||
void updateSize() | |||
{ | |||
setSize (image.getWidth(), image.getHeight()); | |||
} | |||
void forceMouseCursorUpdate() | |||
{ | |||
Desktop::getInstance().getMainMouseSource().forceMouseCursorUpdate(); | |||
@@ -472,6 +484,12 @@ var DragAndDropContainer::getCurrentDragDescription() const | |||
: var(); | |||
} | |||
void DragAndDropContainer::setCurrentDragImage (const Image& newImage) | |||
{ | |||
if (dragImageComponent != nullptr) | |||
dragImageComponent->updateImage (newImage); | |||
} | |||
DragAndDropContainer* DragAndDropContainer::findParentDragContainerFor (Component* c) | |||
{ | |||
return c != nullptr ? c->findParentComponentOfClass<DragAndDropContainer>() : nullptr; | |||
@@ -102,6 +102,9 @@ public: | |||
*/ | |||
var getCurrentDragDescription() const; | |||
/** If a drag is in progress, this allows the image being shown to be dynamically updated. */ | |||
void setCurrentDragImage (const Image& newImage); | |||
/** Utility to find the DragAndDropContainer for a given Component. | |||
This will search up this component's parent hierarchy looking for the first | |||
@@ -50,34 +50,57 @@ namespace MouseCursorHelpers | |||
} | |||
} | |||
static void* fromWebKitFile (const char* filename, float hx, float hy) | |||
static NSCursor* fromNSImage (NSImage* im, NSPoint hotspot) | |||
{ | |||
FileInputStream fileStream (File ("/System/Library/Frameworks/WebKit.framework/Frameworks/WebCore.framework/Resources") | |||
.getChildFile (filename)); | |||
NSCursor* c = [[NSCursor alloc] initWithImage: im | |||
hotSpot: hotspot]; | |||
[im release]; | |||
return c; | |||
} | |||
if (fileStream.openedOk()) | |||
static void* fromHIServices (const char* filename) | |||
{ | |||
JUCE_AUTORELEASEPOOL | |||
{ | |||
BufferedInputStream buf (fileStream, 4096); | |||
const String cursorPath (String ("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/" | |||
"HIServices.framework/Versions/A/Resources/cursors/") | |||
+ filename); | |||
PNGImageFormat pngFormat; | |||
Image im (pngFormat.decodeImage (buf)); | |||
NSImage* originalImage = [[NSImage alloc] initByReferencingFile: juceStringToNS (cursorPath + "/cursor.pdf")]; | |||
NSSize originalSize = [originalImage size]; | |||
NSImage* resultImage = [[NSImage alloc] initWithSize: originalSize]; | |||
if (im.isValid()) | |||
return CustomMouseCursorInfo (im, (int) (hx * im.getWidth()), | |||
(int) (hy * im.getHeight())).create(); | |||
} | |||
for (int scale = 1; scale <= 4; ++scale) | |||
{ | |||
NSAffineTransform* scaleTransform = [NSAffineTransform transform]; | |||
[scaleTransform scaleBy: (float) scale]; | |||
CGImageRef rasterCGImage = [originalImage CGImageForProposedRect: nil | |||
context: nil | |||
hints: [NSDictionary dictionaryWithObjectsAndKeys: | |||
NSImageHintCTM, scaleTransform, nil]]; | |||
return nullptr; | |||
NSBitmapImageRep* imageRep = [[NSBitmapImageRep alloc] initWithCGImage: rasterCGImage]; | |||
[imageRep setSize: originalSize]; | |||
[resultImage addRepresentation: imageRep]; | |||
[imageRep release]; | |||
} | |||
NSDictionary* info = [NSDictionary dictionaryWithContentsOfFile: juceStringToNS (cursorPath + "/info.plist")]; | |||
const float hotspotX = (float) [[info valueForKey: nsStringLiteral ("hotx")] doubleValue]; | |||
const float hotspotY = (float) [[info valueForKey: nsStringLiteral ("hoty")] doubleValue]; | |||
return fromNSImage (resultImage, NSMakePoint (hotspotX, hotspotY)); | |||
} | |||
} | |||
} | |||
void* CustomMouseCursorInfo::create() const | |||
{ | |||
NSImage* im = MouseCursorHelpers::createNSImage (image); | |||
NSCursor* c = [[NSCursor alloc] initWithImage: im | |||
hotSpot: NSMakePoint (hotspot.x, hotspot.y)]; | |||
[im release]; | |||
return c; | |||
return MouseCursorHelpers::fromNSImage (MouseCursorHelpers::createNSImage (image), | |||
NSMakePoint (hotspot.x, hotspot.y)); | |||
} | |||
void* MouseCursor::createStandardMouseCursor (MouseCursor::StandardCursorType type) | |||
@@ -102,9 +125,10 @@ void* MouseCursor::createStandardMouseCursor (MouseCursor::StandardCursorType ty | |||
case CopyingCursor: | |||
{ | |||
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_6 | |||
if (void* m = MouseCursorHelpers::fromWebKitFile ("copyCursor.png", 0, 0)) | |||
if (void* m = MouseCursorHelpers::fromHIServices ("copy")) | |||
return m; | |||
#endif | |||
c = [NSCursor dragCopyCursor]; // added in 10.6 | |||
break; | |||
} | |||
@@ -112,10 +136,10 @@ void* MouseCursor::createStandardMouseCursor (MouseCursor::StandardCursorType ty | |||
case UpDownResizeCursor: | |||
case TopEdgeResizeCursor: | |||
case BottomEdgeResizeCursor: | |||
return MouseCursorHelpers::fromWebKitFile ("northSouthResizeCursor.png", 0.5f, 0.5f); | |||
return MouseCursorHelpers::fromHIServices ("resizenorthsouth"); | |||
case LeftRightResizeCursor: | |||
if (void* m = MouseCursorHelpers::fromWebKitFile ("eastWestResizeCursor.png", 0.5f, 0.5f)) | |||
if (void* m = MouseCursorHelpers::fromHIServices ("resizeeastwest")) | |||
return m; | |||
c = [NSCursor resizeLeftRightCursor]; | |||
@@ -123,14 +147,14 @@ void* MouseCursor::createStandardMouseCursor (MouseCursor::StandardCursorType ty | |||
case TopLeftCornerResizeCursor: | |||
case BottomRightCornerResizeCursor: | |||
return MouseCursorHelpers::fromWebKitFile ("northWestSouthEastResizeCursor.png", 0.5f, 0.5f); | |||
return MouseCursorHelpers::fromHIServices ("resizenorthwestsoutheast"); | |||
case TopRightCornerResizeCursor: | |||
case BottomLeftCornerResizeCursor: | |||
return MouseCursorHelpers::fromWebKitFile ("northEastSouthWestResizeCursor.png", 0.5f, 0.5f); | |||
return MouseCursorHelpers::fromHIServices ("resizenortheastsouthwest"); | |||
case UpDownLeftRightResizeCursor: | |||
return MouseCursorHelpers::fromWebKitFile ("moveCursor.png", 0.5f, 0.5f); | |||
return MouseCursorHelpers::fromHIServices ("move"); | |||
default: | |||
jassertfalse; | |||
@@ -2968,24 +2968,27 @@ bool KeyPress::isKeyCurrentlyDown (const int keyCode) | |||
{ | |||
SHORT k = (SHORT) keyCode; | |||
if ((keyCode & extendedKeyModifier) == 0 | |||
&& (k >= (SHORT) 'a' && k <= (SHORT) 'z')) | |||
k += (SHORT) 'A' - (SHORT) 'a'; | |||
const SHORT translatedValues[] = { (SHORT) ',', VK_OEM_COMMA, | |||
(SHORT) '+', VK_OEM_PLUS, | |||
(SHORT) '-', VK_OEM_MINUS, | |||
(SHORT) '.', VK_OEM_PERIOD, | |||
(SHORT) ';', VK_OEM_1, | |||
(SHORT) ':', VK_OEM_1, | |||
(SHORT) '/', VK_OEM_2, | |||
(SHORT) '?', VK_OEM_2, | |||
(SHORT) '[', VK_OEM_4, | |||
(SHORT) ']', VK_OEM_6 }; | |||
for (int i = 0; i < numElementsInArray (translatedValues); i += 2) | |||
if (k == translatedValues [i]) | |||
k = translatedValues [i + 1]; | |||
if ((keyCode & extendedKeyModifier) == 0) | |||
{ | |||
if (k >= (SHORT) 'a' && k <= (SHORT) 'z') | |||
k += (SHORT) 'A' - (SHORT) 'a'; | |||
// Only translate if extendedKeyModifier flag is not set | |||
const SHORT translatedValues[] = { (SHORT) ',', VK_OEM_COMMA, | |||
(SHORT) '+', VK_OEM_PLUS, | |||
(SHORT) '-', VK_OEM_MINUS, | |||
(SHORT) '.', VK_OEM_PERIOD, | |||
(SHORT) ';', VK_OEM_1, | |||
(SHORT) ':', VK_OEM_1, | |||
(SHORT) '/', VK_OEM_2, | |||
(SHORT) '?', VK_OEM_2, | |||
(SHORT) '[', VK_OEM_4, | |||
(SHORT) ']', VK_OEM_6 }; | |||
for (int i = 0; i < numElementsInArray (translatedValues); i += 2) | |||
if (k == translatedValues [i]) | |||
k = translatedValues [i + 1]; | |||
} | |||
return HWNDComponentPeer::isKeyDown (k); | |||
} | |||