| @@ -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); | |||