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