@@ -42701,7 +42701,7 @@ const String Button::getTooltip() | |||
const String key (keyPresses.getReference(i).getTextDescription()); | |||
if (key.length() == 1) | |||
tt << " [shortcut: '" << key << "']"; | |||
tt << " [" << TRANS("shortcut") << ": '" << key << "']"; | |||
else | |||
tt << " [" << key << ']'; | |||
} | |||
@@ -51610,8 +51610,6 @@ END_JUCE_NAMESPACE | |||
BEGIN_JUCE_NAMESPACE | |||
#define SHOULD_WRAP(x, wrapwidth) (((x) - 0.0001f) >= (wrapwidth)) | |||
// a word or space that can't be broken down any further | |||
struct TextAtom | |||
{ | |||
@@ -51976,12 +51974,11 @@ public: | |||
jassert (wordWrapWidth_ > 0); | |||
if (sections.size() > 0) | |||
{ | |||
currentSection = (const UniformTextSection*) sections.getUnchecked (sectionIndex); | |||
if (currentSection != 0) | |||
{ | |||
lineHeight = currentSection->font.getHeight(); | |||
maxDescent = currentSection->font.getDescent(); | |||
if (currentSection != 0) | |||
beginNewLine(); | |||
} | |||
} | |||
@@ -52029,7 +52026,7 @@ public: | |||
int split; | |||
for (split = 0; split < g.getNumGlyphs(); ++split) | |||
if (SHOULD_WRAP (g.getGlyph (split).getRight(), wordWrapWidth)) | |||
if (shouldWrap (g.getGlyph (split).getRight())) | |||
break; | |||
if (split > 0 && split <= numRemaining) | |||
@@ -52061,9 +52058,6 @@ public: | |||
atomIndex = 0; | |||
currentSection = (const UniformTextSection*) sections.getUnchecked (sectionIndex); | |||
lineHeight = jmax (lineHeight, currentSection->font.getHeight()); | |||
maxDescent = jmax (maxDescent, currentSection->font.getDescent()); | |||
} | |||
else | |||
{ | |||
@@ -52094,7 +52088,7 @@ public: | |||
lineHeight2 = jmax (lineHeight2, s->font.getHeight()); | |||
maxDescent2 = jmax (maxDescent2, s->font.getDescent()); | |||
if (SHOULD_WRAP (right, wordWrapWidth)) | |||
if (shouldWrap (right)) | |||
{ | |||
lineHeight = lineHeight2; | |||
maxDescent = maxDescent2; | |||
@@ -52116,17 +52110,14 @@ public: | |||
indexInText += atom->numChars; | |||
if (atom->isNewLine()) | |||
{ | |||
atomX = 0; | |||
lineY += lineHeight; | |||
} | |||
beginNewLine(); | |||
} | |||
atom = currentSection->getAtom (atomIndex); | |||
atomRight = atomX + atom->width; | |||
++atomIndex; | |||
if (SHOULD_WRAP (atomRight, wordWrapWidth) || forceNewLine) | |||
if (shouldWrap (atomRight) || forceNewLine) | |||
{ | |||
if (atom->isWhitespace()) | |||
{ | |||
@@ -52135,36 +52126,78 @@ public: | |||
} | |||
else | |||
{ | |||
return wrapCurrentAtom(); | |||
atomRight = atom->width; | |||
if (shouldWrap (atomRight)) // atom too big to fit on a line, so break it up.. | |||
{ | |||
tempAtom = *atom; | |||
tempAtom.width = 0; | |||
tempAtom.numChars = 0; | |||
atom = &tempAtom; | |||
if (atomX > 0) | |||
beginNewLine(); | |||
return next(); | |||
} | |||
beginNewLine(); | |||
return true; | |||
} | |||
} | |||
return true; | |||
} | |||
bool wrapCurrentAtom() throw() | |||
void beginNewLine() throw() | |||
{ | |||
atomRight = atom->width; | |||
atomX = 0; | |||
lineY += lineHeight; | |||
int tempSectionIndex = sectionIndex; | |||
int tempAtomIndex = atomIndex; | |||
const UniformTextSection* section = (const UniformTextSection*) sections.getUnchecked (tempSectionIndex); | |||
lineHeight = section->font.getHeight(); | |||
maxDescent = section->font.getDescent(); | |||
if (SHOULD_WRAP (atomRight, wordWrapWidth)) // atom too big to fit on a line, so break it up.. | |||
float x = (atom != 0) ? atom->width : 0; | |||
while (! shouldWrap (x)) | |||
{ | |||
tempAtom = *atom; | |||
tempAtom.width = 0; | |||
tempAtom.numChars = 0; | |||
atom = &tempAtom; | |||
if (tempSectionIndex >= sections.size()) | |||
break; | |||
if (atomX > 0) | |||
bool checkSize = false; | |||
if (tempAtomIndex >= section->getNumAtoms()) | |||
{ | |||
atomX = 0; | |||
lineY += lineHeight; | |||
if (++tempSectionIndex >= sections.size()) | |||
break; | |||
tempAtomIndex = 0; | |||
section = (const UniformTextSection*) sections.getUnchecked (tempSectionIndex); | |||
checkSize = true; | |||
} | |||
return next(); | |||
} | |||
const TextAtom* const nextAtom = section->getAtom (tempAtomIndex); | |||
atomX = 0; | |||
lineY += lineHeight; | |||
return true; | |||
if (nextAtom == 0) | |||
break; | |||
x += nextAtom->width; | |||
if (shouldWrap (x) || nextAtom->isNewLine()) | |||
break; | |||
if (checkSize) | |||
{ | |||
lineHeight = jmax (lineHeight, section->font.getHeight()); | |||
maxDescent = jmax (maxDescent, section->font.getDescent()); | |||
} | |||
++tempAtomIndex; | |||
} | |||
} | |||
void draw (Graphics& g, const UniformTextSection*& lastSection) const throw() | |||
@@ -52280,66 +52313,16 @@ public: | |||
return indexInText + j; | |||
} | |||
void updateLineHeight() throw() | |||
{ | |||
float x = atomRight; | |||
int tempSectionIndex = sectionIndex; | |||
int tempAtomIndex = atomIndex; | |||
const UniformTextSection* currentSection = (const UniformTextSection*) sections.getUnchecked (tempSectionIndex); | |||
while (! SHOULD_WRAP (x, wordWrapWidth)) | |||
{ | |||
if (tempSectionIndex >= sections.size()) | |||
break; | |||
bool checkSize = false; | |||
if (tempAtomIndex >= currentSection->getNumAtoms()) | |||
{ | |||
if (++tempSectionIndex >= sections.size()) | |||
break; | |||
tempAtomIndex = 0; | |||
currentSection = (const UniformTextSection*) sections.getUnchecked (tempSectionIndex); | |||
checkSize = true; | |||
} | |||
const TextAtom* const atom = currentSection->getAtom (tempAtomIndex); | |||
if (atom == 0) | |||
break; | |||
x += atom->width; | |||
if (SHOULD_WRAP (x, wordWrapWidth) || atom->isNewLine()) | |||
break; | |||
if (checkSize) | |||
{ | |||
lineHeight = jmax (lineHeight, currentSection->font.getHeight()); | |||
maxDescent = jmax (maxDescent, currentSection->font.getDescent()); | |||
} | |||
++tempAtomIndex; | |||
} | |||
} | |||
bool getCharPosition (const int index, float& cx, float& cy, float& lineHeight_) throw() | |||
{ | |||
while (next()) | |||
{ | |||
if (indexInText + atom->numChars >= index) | |||
if (indexInText + atom->numChars > index) | |||
{ | |||
updateLineHeight(); | |||
if (indexInText + atom->numChars > index) | |||
{ | |||
cx = indexToX (index); | |||
cy = lineY; | |||
lineHeight_ = lineHeight; | |||
return true; | |||
} | |||
cx = indexToX (index); | |||
cy = lineY; | |||
lineHeight_ = lineHeight; | |||
return true; | |||
} | |||
} | |||
@@ -52379,6 +52362,11 @@ private: | |||
} | |||
} | |||
} | |||
bool shouldWrap (const float x) const throw() | |||
{ | |||
return (x - 0.0001f) >= wordWrapWidth; | |||
} | |||
}; | |||
class TextEditorInsertAction : public UndoableAction | |||
@@ -53263,8 +53251,6 @@ void TextEditor::drawContent (Graphics& g) | |||
while (i2.next() && i2.lineY < clip.getBottom()) | |||
{ | |||
i2.updateLineHeight(); | |||
if (i2.lineY + i2.lineHeight >= clip.getY() | |||
&& selectionEnd >= i2.indexInText | |||
&& selectionStart <= i2.indexInText + i2.atom->numChars) | |||
@@ -53278,8 +53264,6 @@ void TextEditor::drawContent (Graphics& g) | |||
while (i.next() && i.lineY < clip.getBottom()) | |||
{ | |||
i.updateLineHeight(); | |||
if (i.lineY + i.lineHeight >= clip.getY()) | |||
{ | |||
if (selectionEnd >= i.indexInText | |||
@@ -54093,9 +54077,6 @@ int TextEditor::indexAtPosition (const float x, const float y) throw() | |||
while (i.next()) | |||
{ | |||
if (i.lineY + getHeight() > y) | |||
i.updateLineHeight(); | |||
if (i.lineY + i.lineHeight > y) | |||
{ | |||
if (i.lineY > y) | |||
@@ -74576,6 +74557,7 @@ MidiKeyboardComponent::MidiKeyboardComponent (MidiKeyboardState& state_, | |||
firstKey (12 * 4), | |||
canScroll (true), | |||
mouseDragging (false), | |||
useMousePositionForVelocity (true), | |||
keyPresses (4), | |||
keyPressNotes (16), | |||
keyMappingOctave (6), | |||
@@ -74678,9 +74660,10 @@ void MidiKeyboardComponent::setMidiChannelsToDisplay (const int midiChannelMask) | |||
triggerAsyncUpdate(); | |||
} | |||
void MidiKeyboardComponent::setVelocity (const float velocity_) | |||
void MidiKeyboardComponent::setVelocity (const float velocity_, const bool useMousePositionForVelocity_) | |||
{ | |||
velocity = jlimit (0.0f, 1.0f, velocity_); | |||
useMousePositionForVelocity = useMousePositionForVelocity_; | |||
} | |||
void MidiKeyboardComponent::getKeyPosition (int midiNoteNumber, const float keyWidth, int& x, int& w) const | |||
@@ -74730,7 +74713,7 @@ int MidiKeyboardComponent::getKeyStartPosition (const int midiNoteNumber) const | |||
static const uint8 whiteNotes[] = { 0, 2, 4, 5, 7, 9, 11 }; | |||
static const uint8 blackNotes[] = { 1, 3, 6, 8, 10 }; | |||
int MidiKeyboardComponent::xyToNote (int x, int y) | |||
int MidiKeyboardComponent::xyToNote (int x, int y, float& mousePositionVelocity) | |||
{ | |||
if (! reallyContains (x, y, false)) | |||
return -1; | |||
@@ -74745,10 +74728,10 @@ int MidiKeyboardComponent::xyToNote (int x, int y) | |||
x = getHeight() - x; | |||
} | |||
return remappedXYToNote (x + xOffset, y); | |||
return remappedXYToNote (x + xOffset, y, mousePositionVelocity); | |||
} | |||
int MidiKeyboardComponent::remappedXYToNote (int x, int y) const | |||
int MidiKeyboardComponent::remappedXYToNote (int x, int y, float& mousePositionVelocity) const | |||
{ | |||
if (y < blackNoteLength) | |||
{ | |||
@@ -74765,7 +74748,10 @@ int MidiKeyboardComponent::remappedXYToNote (int x, int y) const | |||
kx += xOffset; | |||
if (x >= kx && x < kx + kw) | |||
{ | |||
mousePositionVelocity = y / (float) blackNoteLength; | |||
return note; | |||
} | |||
} | |||
} | |||
} | |||
@@ -74784,11 +74770,15 @@ int MidiKeyboardComponent::remappedXYToNote (int x, int y) const | |||
kx += xOffset; | |||
if (x >= kx && x < kx + kw) | |||
{ | |||
mousePositionVelocity = y / (float) getHeight(); | |||
return note; | |||
} | |||
} | |||
} | |||
} | |||
mousePositionVelocity = 0; | |||
return -1; | |||
} | |||
@@ -75111,8 +75101,9 @@ void MidiKeyboardComponent::resized() | |||
getKeyPos (rangeEnd, endOfLastKey, kw); | |||
endOfLastKey += kw; | |||
float mousePositionVelocity; | |||
const int spaceAvailable = w - scrollButtonW * 2; | |||
const int lastStartKey = remappedXYToNote (endOfLastKey - spaceAvailable, 0) + 1; | |||
const int lastStartKey = remappedXYToNote (endOfLastKey - spaceAvailable, 0, mousePositionVelocity) + 1; | |||
if (lastStartKey >= 0 && firstKey > lastStartKey) | |||
{ | |||
@@ -75168,8 +75159,9 @@ void MidiKeyboardComponent::resetAnyKeysInUse() | |||
void MidiKeyboardComponent::updateNoteUnderMouse (int x, int y) | |||
{ | |||
float mousePositionVelocity; | |||
const int newNote = (mouseDragging || isMouseOver()) | |||
? xyToNote (x, y) : -1; | |||
? xyToNote (x, y, mousePositionVelocity) : -1; | |||
if (noteUnderMouse != newNote) | |||
{ | |||
@@ -75181,7 +75173,10 @@ void MidiKeyboardComponent::updateNoteUnderMouse (int x, int y) | |||
if (mouseDragging && newNote >= 0) | |||
{ | |||
state.noteOn (midiChannel, newNote, velocity); | |||
if (! useMousePositionForVelocity) | |||
mousePositionVelocity = 1.0f; | |||
state.noteOn (midiChannel, newNote, mousePositionVelocity * velocity); | |||
mouseDownNote = newNote; | |||
} | |||
@@ -75204,7 +75199,8 @@ void MidiKeyboardComponent::mouseMove (const MouseEvent& e) | |||
void MidiKeyboardComponent::mouseDrag (const MouseEvent& e) | |||
{ | |||
const int newNote = xyToNote (e.x, e.y); | |||
float mousePositionVelocity; | |||
const int newNote = xyToNote (e.x, e.y, mousePositionVelocity); | |||
if (newNote >= 0) | |||
mouseDraggedToKey (newNote, e); | |||
@@ -75223,7 +75219,8 @@ void MidiKeyboardComponent::mouseDraggedToKey (int /*midiNoteNumber*/, const Mou | |||
void MidiKeyboardComponent::mouseDown (const MouseEvent& e) | |||
{ | |||
const int newNote = xyToNote (e.x, e.y); | |||
float mousePositionVelocity; | |||
const int newNote = xyToNote (e.x, e.y, mousePositionVelocity); | |||
mouseDragging = false; | |||
if (newNote >= 0 && mouseDownOnKey (newNote, e)) | |||
@@ -53878,7 +53878,7 @@ public: | |||
@see setMidiChannel | |||
*/ | |||
void setVelocity (const float velocity); | |||
void setVelocity (const float velocity, const bool useMousePositionForVelocity); | |||
/** Changes the midi channel number that will be used for events triggered by clicking | |||
on the component. | |||
@@ -54178,7 +54178,7 @@ private: | |||
BitArray keysPressed, keysCurrentlyDrawnDown; | |||
int rangeStart, rangeEnd, firstKey; | |||
bool canScroll, mouseDragging; | |||
bool canScroll, mouseDragging, useMousePositionForVelocity; | |||
Button* scrollDown; | |||
Button* scrollUp; | |||
@@ -54188,8 +54188,8 @@ private: | |||
int octaveNumForMiddleC; | |||
void getKeyPos (int midiNoteNumber, int& x, int& w) const; | |||
int xyToNote (int x, int y); | |||
int remappedXYToNote (int x, int y) const; | |||
int xyToNote (int x, int y, float& mousePositionVelocity); | |||
int remappedXYToNote (int x, int y, float& mousePositionVelocity) const; | |||
void resetAnyKeysInUse(); | |||
void updateNoteUnderMouse (int x, int y); | |||
void repaintNote (const int midiNoteNumber); | |||
@@ -30,6 +30,7 @@ BEGIN_JUCE_NAMESPACE | |||
#include "juce_Button.h" | |||
#include "../juce_ComponentDeletionWatcher.h" | |||
#include "../keyboard/juce_KeyPressMappingSet.h" | |||
#include "../../../text/juce_LocalisedStrings.h" | |||
//============================================================================== | |||
@@ -99,7 +100,7 @@ const String Button::getTooltip() | |||
const String key (keyPresses.getReference(i).getTextDescription()); | |||
if (key.length() == 1) | |||
tt << " [shortcut: '" << key << "']"; | |||
tt << " [" << TRANS("shortcut") << ": '" << key << "']"; | |||
else | |||
tt << " [" << key << ']'; | |||
} | |||
@@ -34,7 +34,6 @@ BEGIN_JUCE_NAMESPACE | |||
#include "../../../text/juce_LocalisedStrings.h" | |||
#include "../lookandfeel/juce_LookAndFeel.h" | |||
#define SHOULD_WRAP(x, wrapwidth) (((x) - 0.0001f) >= (wrapwidth)) | |||
//============================================================================== | |||
// a word or space that can't be broken down any further | |||
@@ -406,12 +405,11 @@ public: | |||
jassert (wordWrapWidth_ > 0); | |||
if (sections.size() > 0) | |||
{ | |||
currentSection = (const UniformTextSection*) sections.getUnchecked (sectionIndex); | |||
if (currentSection != 0) | |||
{ | |||
lineHeight = currentSection->font.getHeight(); | |||
maxDescent = currentSection->font.getDescent(); | |||
if (currentSection != 0) | |||
beginNewLine(); | |||
} | |||
} | |||
@@ -460,7 +458,7 @@ public: | |||
int split; | |||
for (split = 0; split < g.getNumGlyphs(); ++split) | |||
if (SHOULD_WRAP (g.getGlyph (split).getRight(), wordWrapWidth)) | |||
if (shouldWrap (g.getGlyph (split).getRight())) | |||
break; | |||
if (split > 0 && split <= numRemaining) | |||
@@ -492,9 +490,6 @@ public: | |||
atomIndex = 0; | |||
currentSection = (const UniformTextSection*) sections.getUnchecked (sectionIndex); | |||
lineHeight = jmax (lineHeight, currentSection->font.getHeight()); | |||
maxDescent = jmax (maxDescent, currentSection->font.getDescent()); | |||
} | |||
else | |||
{ | |||
@@ -525,7 +520,7 @@ public: | |||
lineHeight2 = jmax (lineHeight2, s->font.getHeight()); | |||
maxDescent2 = jmax (maxDescent2, s->font.getDescent()); | |||
if (SHOULD_WRAP (right, wordWrapWidth)) | |||
if (shouldWrap (right)) | |||
{ | |||
lineHeight = lineHeight2; | |||
maxDescent = maxDescent2; | |||
@@ -547,17 +542,14 @@ public: | |||
indexInText += atom->numChars; | |||
if (atom->isNewLine()) | |||
{ | |||
atomX = 0; | |||
lineY += lineHeight; | |||
} | |||
beginNewLine(); | |||
} | |||
atom = currentSection->getAtom (atomIndex); | |||
atomRight = atomX + atom->width; | |||
++atomIndex; | |||
if (SHOULD_WRAP (atomRight, wordWrapWidth) || forceNewLine) | |||
if (shouldWrap (atomRight) || forceNewLine) | |||
{ | |||
if (atom->isWhitespace()) | |||
{ | |||
@@ -566,36 +558,78 @@ public: | |||
} | |||
else | |||
{ | |||
return wrapCurrentAtom(); | |||
atomRight = atom->width; | |||
if (shouldWrap (atomRight)) // atom too big to fit on a line, so break it up.. | |||
{ | |||
tempAtom = *atom; | |||
tempAtom.width = 0; | |||
tempAtom.numChars = 0; | |||
atom = &tempAtom; | |||
if (atomX > 0) | |||
beginNewLine(); | |||
return next(); | |||
} | |||
beginNewLine(); | |||
return true; | |||
} | |||
} | |||
return true; | |||
} | |||
bool wrapCurrentAtom() throw() | |||
void beginNewLine() throw() | |||
{ | |||
atomRight = atom->width; | |||
atomX = 0; | |||
lineY += lineHeight; | |||
int tempSectionIndex = sectionIndex; | |||
int tempAtomIndex = atomIndex; | |||
const UniformTextSection* section = (const UniformTextSection*) sections.getUnchecked (tempSectionIndex); | |||
if (SHOULD_WRAP (atomRight, wordWrapWidth)) // atom too big to fit on a line, so break it up.. | |||
lineHeight = section->font.getHeight(); | |||
maxDescent = section->font.getDescent(); | |||
float x = (atom != 0) ? atom->width : 0; | |||
while (! shouldWrap (x)) | |||
{ | |||
tempAtom = *atom; | |||
tempAtom.width = 0; | |||
tempAtom.numChars = 0; | |||
atom = &tempAtom; | |||
if (tempSectionIndex >= sections.size()) | |||
break; | |||
if (atomX > 0) | |||
bool checkSize = false; | |||
if (tempAtomIndex >= section->getNumAtoms()) | |||
{ | |||
atomX = 0; | |||
lineY += lineHeight; | |||
if (++tempSectionIndex >= sections.size()) | |||
break; | |||
tempAtomIndex = 0; | |||
section = (const UniformTextSection*) sections.getUnchecked (tempSectionIndex); | |||
checkSize = true; | |||
} | |||
return next(); | |||
} | |||
const TextAtom* const nextAtom = section->getAtom (tempAtomIndex); | |||
atomX = 0; | |||
lineY += lineHeight; | |||
return true; | |||
if (nextAtom == 0) | |||
break; | |||
x += nextAtom->width; | |||
if (shouldWrap (x) || nextAtom->isNewLine()) | |||
break; | |||
if (checkSize) | |||
{ | |||
lineHeight = jmax (lineHeight, section->font.getHeight()); | |||
maxDescent = jmax (maxDescent, section->font.getDescent()); | |||
} | |||
++tempAtomIndex; | |||
} | |||
} | |||
//============================================================================== | |||
@@ -714,66 +748,16 @@ public: | |||
} | |||
//============================================================================== | |||
void updateLineHeight() throw() | |||
{ | |||
float x = atomRight; | |||
int tempSectionIndex = sectionIndex; | |||
int tempAtomIndex = atomIndex; | |||
const UniformTextSection* currentSection = (const UniformTextSection*) sections.getUnchecked (tempSectionIndex); | |||
while (! SHOULD_WRAP (x, wordWrapWidth)) | |||
{ | |||
if (tempSectionIndex >= sections.size()) | |||
break; | |||
bool checkSize = false; | |||
if (tempAtomIndex >= currentSection->getNumAtoms()) | |||
{ | |||
if (++tempSectionIndex >= sections.size()) | |||
break; | |||
tempAtomIndex = 0; | |||
currentSection = (const UniformTextSection*) sections.getUnchecked (tempSectionIndex); | |||
checkSize = true; | |||
} | |||
const TextAtom* const atom = currentSection->getAtom (tempAtomIndex); | |||
if (atom == 0) | |||
break; | |||
x += atom->width; | |||
if (SHOULD_WRAP (x, wordWrapWidth) || atom->isNewLine()) | |||
break; | |||
if (checkSize) | |||
{ | |||
lineHeight = jmax (lineHeight, currentSection->font.getHeight()); | |||
maxDescent = jmax (maxDescent, currentSection->font.getDescent()); | |||
} | |||
++tempAtomIndex; | |||
} | |||
} | |||
bool getCharPosition (const int index, float& cx, float& cy, float& lineHeight_) throw() | |||
{ | |||
while (next()) | |||
{ | |||
if (indexInText + atom->numChars >= index) | |||
if (indexInText + atom->numChars > index) | |||
{ | |||
updateLineHeight(); | |||
if (indexInText + atom->numChars > index) | |||
{ | |||
cx = indexToX (index); | |||
cy = lineY; | |||
lineHeight_ = lineHeight; | |||
return true; | |||
} | |||
cx = indexToX (index); | |||
cy = lineY; | |||
lineHeight_ = lineHeight; | |||
return true; | |||
} | |||
} | |||
@@ -814,6 +798,11 @@ private: | |||
} | |||
} | |||
} | |||
bool shouldWrap (const float x) const throw() | |||
{ | |||
return (x - 0.0001f) >= wordWrapWidth; | |||
} | |||
}; | |||
@@ -1716,8 +1705,6 @@ void TextEditor::drawContent (Graphics& g) | |||
while (i2.next() && i2.lineY < clip.getBottom()) | |||
{ | |||
i2.updateLineHeight(); | |||
if (i2.lineY + i2.lineHeight >= clip.getY() | |||
&& selectionEnd >= i2.indexInText | |||
&& selectionStart <= i2.indexInText + i2.atom->numChars) | |||
@@ -1731,8 +1718,6 @@ void TextEditor::drawContent (Graphics& g) | |||
while (i.next() && i.lineY < clip.getBottom()) | |||
{ | |||
i.updateLineHeight(); | |||
if (i.lineY + i.lineHeight >= clip.getY()) | |||
{ | |||
if (selectionEnd >= i.indexInText | |||
@@ -2553,9 +2538,6 @@ int TextEditor::indexAtPosition (const float x, const float y) throw() | |||
while (i.next()) | |||
{ | |||
if (i.lineY + getHeight() > y) | |||
i.updateLineHeight(); | |||
if (i.lineY + i.lineHeight > y) | |||
{ | |||
if (i.lineY > y) | |||
@@ -94,6 +94,7 @@ MidiKeyboardComponent::MidiKeyboardComponent (MidiKeyboardState& state_, | |||
firstKey (12 * 4), | |||
canScroll (true), | |||
mouseDragging (false), | |||
useMousePositionForVelocity (true), | |||
keyPresses (4), | |||
keyPressNotes (16), | |||
keyMappingOctave (6), | |||
@@ -198,9 +199,10 @@ void MidiKeyboardComponent::setMidiChannelsToDisplay (const int midiChannelMask) | |||
triggerAsyncUpdate(); | |||
} | |||
void MidiKeyboardComponent::setVelocity (const float velocity_) | |||
void MidiKeyboardComponent::setVelocity (const float velocity_, const bool useMousePositionForVelocity_) | |||
{ | |||
velocity = jlimit (0.0f, 1.0f, velocity_); | |||
useMousePositionForVelocity = useMousePositionForVelocity_; | |||
} | |||
//============================================================================== | |||
@@ -251,7 +253,7 @@ int MidiKeyboardComponent::getKeyStartPosition (const int midiNoteNumber) const | |||
static const uint8 whiteNotes[] = { 0, 2, 4, 5, 7, 9, 11 }; | |||
static const uint8 blackNotes[] = { 1, 3, 6, 8, 10 }; | |||
int MidiKeyboardComponent::xyToNote (int x, int y) | |||
int MidiKeyboardComponent::xyToNote (int x, int y, float& mousePositionVelocity) | |||
{ | |||
if (! reallyContains (x, y, false)) | |||
return -1; | |||
@@ -266,10 +268,10 @@ int MidiKeyboardComponent::xyToNote (int x, int y) | |||
x = getHeight() - x; | |||
} | |||
return remappedXYToNote (x + xOffset, y); | |||
return remappedXYToNote (x + xOffset, y, mousePositionVelocity); | |||
} | |||
int MidiKeyboardComponent::remappedXYToNote (int x, int y) const | |||
int MidiKeyboardComponent::remappedXYToNote (int x, int y, float& mousePositionVelocity) const | |||
{ | |||
if (y < blackNoteLength) | |||
{ | |||
@@ -286,7 +288,10 @@ int MidiKeyboardComponent::remappedXYToNote (int x, int y) const | |||
kx += xOffset; | |||
if (x >= kx && x < kx + kw) | |||
{ | |||
mousePositionVelocity = y / (float) blackNoteLength; | |||
return note; | |||
} | |||
} | |||
} | |||
} | |||
@@ -305,11 +310,15 @@ int MidiKeyboardComponent::remappedXYToNote (int x, int y) const | |||
kx += xOffset; | |||
if (x >= kx && x < kx + kw) | |||
{ | |||
mousePositionVelocity = y / (float) getHeight(); | |||
return note; | |||
} | |||
} | |||
} | |||
} | |||
mousePositionVelocity = 0; | |||
return -1; | |||
} | |||
@@ -633,8 +642,9 @@ void MidiKeyboardComponent::resized() | |||
getKeyPos (rangeEnd, endOfLastKey, kw); | |||
endOfLastKey += kw; | |||
float mousePositionVelocity; | |||
const int spaceAvailable = w - scrollButtonW * 2; | |||
const int lastStartKey = remappedXYToNote (endOfLastKey - spaceAvailable, 0) + 1; | |||
const int lastStartKey = remappedXYToNote (endOfLastKey - spaceAvailable, 0, mousePositionVelocity) + 1; | |||
if (lastStartKey >= 0 && firstKey > lastStartKey) | |||
{ | |||
@@ -692,8 +702,9 @@ void MidiKeyboardComponent::resetAnyKeysInUse() | |||
void MidiKeyboardComponent::updateNoteUnderMouse (int x, int y) | |||
{ | |||
float mousePositionVelocity; | |||
const int newNote = (mouseDragging || isMouseOver()) | |||
? xyToNote (x, y) : -1; | |||
? xyToNote (x, y, mousePositionVelocity) : -1; | |||
if (noteUnderMouse != newNote) | |||
{ | |||
@@ -705,7 +716,10 @@ void MidiKeyboardComponent::updateNoteUnderMouse (int x, int y) | |||
if (mouseDragging && newNote >= 0) | |||
{ | |||
state.noteOn (midiChannel, newNote, velocity); | |||
if (! useMousePositionForVelocity) | |||
mousePositionVelocity = 1.0f; | |||
state.noteOn (midiChannel, newNote, mousePositionVelocity * velocity); | |||
mouseDownNote = newNote; | |||
} | |||
@@ -728,7 +742,8 @@ void MidiKeyboardComponent::mouseMove (const MouseEvent& e) | |||
void MidiKeyboardComponent::mouseDrag (const MouseEvent& e) | |||
{ | |||
const int newNote = xyToNote (e.x, e.y); | |||
float mousePositionVelocity; | |||
const int newNote = xyToNote (e.x, e.y, mousePositionVelocity); | |||
if (newNote >= 0) | |||
mouseDraggedToKey (newNote, e); | |||
@@ -747,7 +762,8 @@ void MidiKeyboardComponent::mouseDraggedToKey (int /*midiNoteNumber*/, const Mou | |||
void MidiKeyboardComponent::mouseDown (const MouseEvent& e) | |||
{ | |||
const int newNote = xyToNote (e.x, e.y); | |||
float mousePositionVelocity; | |||
const int newNote = xyToNote (e.x, e.y, mousePositionVelocity); | |||
mouseDragging = false; | |||
if (newNote >= 0 && mouseDownOnKey (newNote, e)) | |||
@@ -89,7 +89,7 @@ public: | |||
@see setMidiChannel | |||
*/ | |||
void setVelocity (const float velocity); | |||
void setVelocity (const float velocity, const bool useMousePositionForVelocity); | |||
/** Changes the midi channel number that will be used for events triggered by clicking | |||
on the component. | |||
@@ -395,7 +395,7 @@ private: | |||
BitArray keysPressed, keysCurrentlyDrawnDown; | |||
int rangeStart, rangeEnd, firstKey; | |||
bool canScroll, mouseDragging; | |||
bool canScroll, mouseDragging, useMousePositionForVelocity; | |||
Button* scrollDown; | |||
Button* scrollUp; | |||
@@ -405,8 +405,8 @@ private: | |||
int octaveNumForMiddleC; | |||
void getKeyPos (int midiNoteNumber, int& x, int& w) const; | |||
int xyToNote (int x, int y); | |||
int remappedXYToNote (int x, int y) const; | |||
int xyToNote (int x, int y, float& mousePositionVelocity); | |||
int remappedXYToNote (int x, int y, float& mousePositionVelocity) const; | |||
void resetAnyKeysInUse(); | |||
void updateNoteUnderMouse (int x, int y); | |||
void repaintNote (const int midiNoteNumber); | |||