| @@ -833,7 +833,7 @@ public: | |||
| AudioIODeviceCallback* const oldCallback = currentCallback; | |||
| close(); | |||
| open (currentChansIn, currentChansOut, | |||
| open (BitArray (currentChansIn), BitArray (currentChansOut), | |||
| currentSampleRate, currentBlockSizeSamples); | |||
| if (oldCallback != 0) | |||
| @@ -63,7 +63,6 @@ int MidiMessage::getMessageLengthFromFirstByte (const uint8 firstByte) throw() | |||
| { | |||
| // this method only works for valid starting bytes of a short midi message | |||
| jassert (firstByte >= 0x80 | |||
| && firstByte != 0xff | |||
| && firstByte != 0xf0 | |||
| && firstByte != 0xf7); | |||
| @@ -110,7 +110,7 @@ void MidiMessageCollector::removeNextBlockOfMessages (MidiBuffer& destBuffer, | |||
| { | |||
| // if our list of events is longer than the buffer we're being | |||
| // asked for, scale them down to squeeze them all in.. | |||
| const int maxBlockLengthToUse = numSamples << 3; | |||
| const int maxBlockLengthToUse = numSamples << 5; | |||
| if (numSourceSamples > maxBlockLengthToUse) | |||
| { | |||
| @@ -150,7 +150,7 @@ void MidiMessageCollector::removeNextBlockOfMessages (MidiBuffer& destBuffer, | |||
| void MidiMessageCollector::handleNoteOn (MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity) | |||
| { | |||
| MidiMessage m (MidiMessage::noteOn (midiChannel, midiNoteNumber, velocity)); | |||
| m.setTimeStamp (Time::getMillisecondCounter() * 0.001); | |||
| m.setTimeStamp (Time::getMillisecondCounterHiRes() * 0.001); | |||
| addMessageToQueue (m); | |||
| } | |||
| @@ -158,7 +158,7 @@ void MidiMessageCollector::handleNoteOn (MidiKeyboardState*, int midiChannel, in | |||
| void MidiMessageCollector::handleNoteOff (MidiKeyboardState*, int midiChannel, int midiNoteNumber) | |||
| { | |||
| MidiMessage m (MidiMessage::noteOff (midiChannel, midiNoteNumber)); | |||
| m.setTimeStamp (Time::getMillisecondCounter() * 0.001); | |||
| m.setTimeStamp (Time::getMillisecondCounterHiRes() * 0.001); | |||
| addMessageToQueue (m); | |||
| } | |||
| @@ -1,444 +1,456 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-7 by Raw Material Software ltd. | |||
| ------------------------------------------------------------------------------ | |||
| JUCE can be redistributed and/or modified under the terms of the | |||
| GNU General Public License, as published by the Free Software Foundation; | |||
| either version 2 of the License, or (at your option) any later version. | |||
| JUCE is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU General Public License for more details. | |||
| You should have received a copy of the GNU General Public License | |||
| along with JUCE; if not, visit www.gnu.org/licenses or write to the | |||
| Free Software Foundation, Inc., 59 Temple Place, Suite 330, | |||
| Boston, MA 02111-1307 USA | |||
| ------------------------------------------------------------------------------ | |||
| If you'd like to release a closed-source product which uses JUCE, commercial | |||
| licenses are also available: visit www.rawmaterialsoftware.com/juce for | |||
| more information. | |||
| ============================================================================== | |||
| */ | |||
| #include "../../../../juce_core/basics/juce_StandardHeader.h" | |||
| BEGIN_JUCE_NAMESPACE | |||
| #include "juce_Label.h" | |||
| //============================================================================== | |||
| Label::Label (const String& componentName, | |||
| const String& labelText) | |||
| : Component (componentName), | |||
| text (labelText), | |||
| font (15.0f), | |||
| justification (Justification::centredLeft), | |||
| editor (0), | |||
| listeners (2), | |||
| ownerComponent (0), | |||
| deletionWatcher (0), | |||
| editSingleClick (false), | |||
| editDoubleClick (false), | |||
| lossOfFocusDiscardsChanges (false) | |||
| { | |||
| setColour (TextEditor::textColourId, Colours::black); | |||
| setColour (TextEditor::backgroundColourId, Colours::transparentBlack); | |||
| setColour (TextEditor::outlineColourId, Colours::transparentBlack); | |||
| } | |||
| Label::~Label() | |||
| { | |||
| if (ownerComponent != 0 && ! deletionWatcher->hasBeenDeleted()) | |||
| ownerComponent->removeComponentListener (this); | |||
| deleteAndZero (deletionWatcher); | |||
| if (editor != 0) | |||
| delete editor; | |||
| } | |||
| //============================================================================== | |||
| void Label::setText (const String& newText, | |||
| const bool broadcastChangeMessage) | |||
| { | |||
| hideEditor (true); | |||
| if (text != newText) | |||
| { | |||
| text = newText; | |||
| if (broadcastChangeMessage) | |||
| triggerAsyncUpdate(); | |||
| repaint(); | |||
| if (ownerComponent != 0 && ! deletionWatcher->hasBeenDeleted()) | |||
| componentMovedOrResized (*ownerComponent, true, true); | |||
| } | |||
| } | |||
| const String Label::getText (const bool returnActiveEditorContents) const throw() | |||
| { | |||
| return (returnActiveEditorContents && isBeingEdited()) | |||
| ? editor->getText() | |||
| : text; | |||
| } | |||
| void Label::setFont (const Font& newFont) throw() | |||
| { | |||
| font = newFont; | |||
| repaint(); | |||
| } | |||
| const Font& Label::getFont() const throw() | |||
| { | |||
| return font; | |||
| } | |||
| void Label::setEditable (const bool editOnSingleClick, | |||
| const bool editOnDoubleClick, | |||
| const bool lossOfFocusDiscardsChanges_) throw() | |||
| { | |||
| editSingleClick = editOnSingleClick; | |||
| editDoubleClick = editOnDoubleClick; | |||
| lossOfFocusDiscardsChanges = lossOfFocusDiscardsChanges_; | |||
| setWantsKeyboardFocus (editOnSingleClick || editOnDoubleClick); | |||
| setFocusContainer (editOnSingleClick || editOnDoubleClick); | |||
| } | |||
| void Label::setJustificationType (const Justification& justification_) throw() | |||
| { | |||
| justification = justification_; | |||
| repaint(); | |||
| } | |||
| //============================================================================== | |||
| void Label::attachToComponent (Component* owner, | |||
| const bool onLeft) | |||
| { | |||
| if (ownerComponent != 0 && ! deletionWatcher->hasBeenDeleted()) | |||
| ownerComponent->removeComponentListener (this); | |||
| deleteAndZero (deletionWatcher); | |||
| ownerComponent = owner; | |||
| leftOfOwnerComp = onLeft; | |||
| if (ownerComponent != 0) | |||
| { | |||
| deletionWatcher = new ComponentDeletionWatcher (owner); | |||
| setVisible (owner->isVisible()); | |||
| ownerComponent->addComponentListener (this); | |||
| componentParentHierarchyChanged (*ownerComponent); | |||
| componentMovedOrResized (*ownerComponent, true, true); | |||
| } | |||
| } | |||
| void Label::componentMovedOrResized (Component& component, | |||
| bool /*wasMoved*/, | |||
| bool /*wasResized*/) | |||
| { | |||
| if (leftOfOwnerComp) | |||
| { | |||
| setSize (jmin (getFont().getStringWidth (text) + 8, component.getX()), | |||
| component.getHeight()); | |||
| setTopRightPosition (component.getX(), component.getY()); | |||
| } | |||
| else | |||
| { | |||
| setSize (component.getWidth(), | |||
| 8 + roundFloatToInt (getFont().getHeight())); | |||
| setTopLeftPosition (component.getX(), component.getY() - getHeight()); | |||
| } | |||
| } | |||
| void Label::componentParentHierarchyChanged (Component& component) | |||
| { | |||
| if (component.getParentComponent() != 0) | |||
| component.getParentComponent()->addChildComponent (this); | |||
| } | |||
| void Label::componentVisibilityChanged (Component& component) | |||
| { | |||
| setVisible (component.isVisible()); | |||
| } | |||
| //============================================================================== | |||
| void Label::textWasEdited() | |||
| { | |||
| } | |||
| void Label::showEditor() | |||
| { | |||
| if (editor == 0) | |||
| { | |||
| addAndMakeVisible (editor = createEditorComponent()); | |||
| editor->setText (getText()); | |||
| editor->addListener (this); | |||
| editor->grabKeyboardFocus(); | |||
| editor->setHighlightedRegion (0, text.length()); | |||
| editor->addListener (this); | |||
| resized(); | |||
| repaint(); | |||
| enterModalState(); | |||
| editor->grabKeyboardFocus(); | |||
| } | |||
| } | |||
| bool Label::updateFromTextEditorContents() | |||
| { | |||
| jassert (editor != 0); | |||
| const String newText (editor->getText()); | |||
| if (text != newText) | |||
| { | |||
| text = newText; | |||
| triggerAsyncUpdate(); | |||
| repaint(); | |||
| if (ownerComponent != 0 && ! deletionWatcher->hasBeenDeleted()) | |||
| componentMovedOrResized (*ownerComponent, true, true); | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| void Label::hideEditor (const bool discardCurrentEditorContents) | |||
| { | |||
| if (editor != 0) | |||
| { | |||
| const bool changed = (! discardCurrentEditorContents) | |||
| && updateFromTextEditorContents(); | |||
| deleteAndZero (editor); | |||
| repaint(); | |||
| if (changed) | |||
| textWasEdited(); | |||
| exitModalState (0); | |||
| } | |||
| } | |||
| void Label::inputAttemptWhenModal() | |||
| { | |||
| if (editor != 0) | |||
| { | |||
| if (lossOfFocusDiscardsChanges) | |||
| textEditorEscapeKeyPressed (*editor); | |||
| else | |||
| textEditorReturnKeyPressed (*editor); | |||
| } | |||
| } | |||
| bool Label::isBeingEdited() const throw() | |||
| { | |||
| return editor != 0; | |||
| } | |||
| TextEditor* Label::createEditorComponent() | |||
| { | |||
| TextEditor* const ed = new TextEditor (getName()); | |||
| ed->setFont (font); | |||
| // copy these colours from our own settings.. | |||
| const int cols[] = { TextEditor::backgroundColourId, | |||
| TextEditor::textColourId, | |||
| TextEditor::highlightColourId, | |||
| TextEditor::highlightedTextColourId, | |||
| TextEditor::caretColourId, | |||
| TextEditor::outlineColourId, | |||
| TextEditor::focusedOutlineColourId, | |||
| TextEditor::shadowColourId }; | |||
| for (int i = 0; i < numElementsInArray (cols); ++i) | |||
| ed->setColour (cols[i], findColour (cols[i])); | |||
| return ed; | |||
| } | |||
| //============================================================================== | |||
| void Label::paint (Graphics& g) | |||
| { | |||
| g.fillAll (findColour (backgroundColourId)); | |||
| if (editor == 0) | |||
| { | |||
| const float alpha = isEnabled() ? 1.0f : 0.5f; | |||
| g.setColour (findColour (textColourId).withMultipliedAlpha (alpha)); | |||
| g.setFont (font); | |||
| g.drawFittedText (text, | |||
| 3, 1, getWidth() - 6, getHeight() - 2, | |||
| justification, | |||
| jmax (1, (int) (getHeight() / font.getHeight()))); | |||
| g.setColour (findColour (outlineColourId).withMultipliedAlpha (alpha)); | |||
| g.drawRect (0, 0, getWidth(), getHeight()); | |||
| } | |||
| else if (isEnabled()) | |||
| { | |||
| g.setColour (editor->findColour (TextEditor::backgroundColourId) | |||
| .overlaidWith (findColour (outlineColourId))); | |||
| g.drawRect (0, 0, getWidth(), getHeight()); | |||
| } | |||
| } | |||
| void Label::mouseUp (const MouseEvent& e) | |||
| { | |||
| if (editSingleClick | |||
| && e.mouseWasClicked() | |||
| && contains (e.x, e.y) | |||
| && ! e.mods.isPopupMenu()) | |||
| { | |||
| showEditor(); | |||
| } | |||
| } | |||
| void Label::mouseDoubleClick (const MouseEvent& e) | |||
| { | |||
| if (editDoubleClick && ! e.mods.isPopupMenu()) | |||
| showEditor(); | |||
| } | |||
| void Label::resized() | |||
| { | |||
| if (editor != 0) | |||
| editor->setBoundsInset (BorderSize (0)); | |||
| } | |||
| void Label::focusGained (FocusChangeType cause) | |||
| { | |||
| if (editSingleClick && cause == focusChangedByTabKey) | |||
| showEditor(); | |||
| } | |||
| void Label::enablementChanged() | |||
| { | |||
| repaint(); | |||
| } | |||
| void Label::colourChanged() | |||
| { | |||
| repaint(); | |||
| } | |||
| //============================================================================== | |||
| // We'll use a custom focus traverser here to make sure focus goes from the | |||
| // text editor to another component rather than back to the label itself. | |||
| class LabelKeyboardFocusTraverser : public KeyboardFocusTraverser | |||
| { | |||
| public: | |||
| LabelKeyboardFocusTraverser() {} | |||
| Component* getNextComponent (Component* current) | |||
| { | |||
| return KeyboardFocusTraverser::getNextComponent (dynamic_cast <TextEditor*> (current) != 0 | |||
| ? current->getParentComponent() : current); | |||
| } | |||
| Component* getPreviousComponent (Component* current) | |||
| { | |||
| return KeyboardFocusTraverser::getPreviousComponent (dynamic_cast <TextEditor*> (current) != 0 | |||
| ? current->getParentComponent() : current); | |||
| } | |||
| }; | |||
| KeyboardFocusTraverser* Label::createFocusTraverser() | |||
| { | |||
| return new LabelKeyboardFocusTraverser(); | |||
| } | |||
| //============================================================================== | |||
| void Label::addListener (LabelListener* const listener) throw() | |||
| { | |||
| jassert (listener != 0); | |||
| if (listener != 0) | |||
| listeners.add (listener); | |||
| } | |||
| void Label::removeListener (LabelListener* const listener) throw() | |||
| { | |||
| listeners.removeValue (listener); | |||
| } | |||
| void Label::handleAsyncUpdate() | |||
| { | |||
| for (int i = listeners.size(); --i >= 0;) | |||
| { | |||
| ((LabelListener*) listeners.getUnchecked (i))->labelTextChanged (this); | |||
| i = jmin (i, listeners.size()); | |||
| } | |||
| } | |||
| //============================================================================== | |||
| void Label::textEditorTextChanged (TextEditor& ed) | |||
| { | |||
| if (editor != 0) | |||
| { | |||
| jassert (&ed == editor); | |||
| if (! (hasKeyboardFocus (true) || isCurrentlyBlockedByAnotherModalComponent())) | |||
| { | |||
| if (lossOfFocusDiscardsChanges) | |||
| textEditorEscapeKeyPressed (ed); | |||
| else | |||
| textEditorReturnKeyPressed (ed); | |||
| } | |||
| } | |||
| } | |||
| void Label::textEditorReturnKeyPressed (TextEditor& ed) | |||
| { | |||
| if (editor != 0) | |||
| { | |||
| jassert (&ed == editor); | |||
| (void) ed; | |||
| const bool changed = updateFromTextEditorContents(); | |||
| hideEditor (true); | |||
| if (changed) | |||
| textWasEdited(); | |||
| } | |||
| } | |||
| void Label::textEditorEscapeKeyPressed (TextEditor& ed) | |||
| { | |||
| if (editor != 0) | |||
| { | |||
| jassert (&ed == editor); | |||
| (void) ed; | |||
| editor->setText (text, false); | |||
| hideEditor (true); | |||
| } | |||
| } | |||
| void Label::textEditorFocusLost (TextEditor& ed) | |||
| { | |||
| textEditorTextChanged (ed); | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-7 by Raw Material Software ltd. | |||
| ------------------------------------------------------------------------------ | |||
| JUCE can be redistributed and/or modified under the terms of the | |||
| GNU General Public License, as published by the Free Software Foundation; | |||
| either version 2 of the License, or (at your option) any later version. | |||
| JUCE is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU General Public License for more details. | |||
| You should have received a copy of the GNU General Public License | |||
| along with JUCE; if not, visit www.gnu.org/licenses or write to the | |||
| Free Software Foundation, Inc., 59 Temple Place, Suite 330, | |||
| Boston, MA 02111-1307 USA | |||
| ------------------------------------------------------------------------------ | |||
| If you'd like to release a closed-source product which uses JUCE, commercial | |||
| licenses are also available: visit www.rawmaterialsoftware.com/juce for | |||
| more information. | |||
| ============================================================================== | |||
| */ | |||
| #include "../../../../juce_core/basics/juce_StandardHeader.h" | |||
| BEGIN_JUCE_NAMESPACE | |||
| #include "juce_Label.h" | |||
| //============================================================================== | |||
| Label::Label (const String& componentName, | |||
| const String& labelText) | |||
| : Component (componentName), | |||
| text (labelText), | |||
| font (15.0f), | |||
| justification (Justification::centredLeft), | |||
| editor (0), | |||
| listeners (2), | |||
| ownerComponent (0), | |||
| deletionWatcher (0), | |||
| horizontalBorderSize (3), | |||
| verticalBorderSize (1), | |||
| editSingleClick (false), | |||
| editDoubleClick (false), | |||
| lossOfFocusDiscardsChanges (false) | |||
| { | |||
| setColour (TextEditor::textColourId, Colours::black); | |||
| setColour (TextEditor::backgroundColourId, Colours::transparentBlack); | |||
| setColour (TextEditor::outlineColourId, Colours::transparentBlack); | |||
| } | |||
| Label::~Label() | |||
| { | |||
| if (ownerComponent != 0 && ! deletionWatcher->hasBeenDeleted()) | |||
| ownerComponent->removeComponentListener (this); | |||
| deleteAndZero (deletionWatcher); | |||
| if (editor != 0) | |||
| delete editor; | |||
| } | |||
| //============================================================================== | |||
| void Label::setText (const String& newText, | |||
| const bool broadcastChangeMessage) | |||
| { | |||
| hideEditor (true); | |||
| if (text != newText) | |||
| { | |||
| text = newText; | |||
| if (broadcastChangeMessage) | |||
| triggerAsyncUpdate(); | |||
| repaint(); | |||
| if (ownerComponent != 0 && ! deletionWatcher->hasBeenDeleted()) | |||
| componentMovedOrResized (*ownerComponent, true, true); | |||
| } | |||
| } | |||
| const String Label::getText (const bool returnActiveEditorContents) const throw() | |||
| { | |||
| return (returnActiveEditorContents && isBeingEdited()) | |||
| ? editor->getText() | |||
| : text; | |||
| } | |||
| void Label::setFont (const Font& newFont) throw() | |||
| { | |||
| font = newFont; | |||
| repaint(); | |||
| } | |||
| const Font& Label::getFont() const throw() | |||
| { | |||
| return font; | |||
| } | |||
| void Label::setEditable (const bool editOnSingleClick, | |||
| const bool editOnDoubleClick, | |||
| const bool lossOfFocusDiscardsChanges_) throw() | |||
| { | |||
| editSingleClick = editOnSingleClick; | |||
| editDoubleClick = editOnDoubleClick; | |||
| lossOfFocusDiscardsChanges = lossOfFocusDiscardsChanges_; | |||
| setWantsKeyboardFocus (editOnSingleClick || editOnDoubleClick); | |||
| setFocusContainer (editOnSingleClick || editOnDoubleClick); | |||
| } | |||
| void Label::setJustificationType (const Justification& justification_) throw() | |||
| { | |||
| justification = justification_; | |||
| repaint(); | |||
| } | |||
| void Label::setBorderSize (int h, int v) | |||
| { | |||
| horizontalBorderSize = h; | |||
| verticalBorderSize = v; | |||
| repaint(); | |||
| } | |||
| //============================================================================== | |||
| void Label::attachToComponent (Component* owner, | |||
| const bool onLeft) | |||
| { | |||
| if (ownerComponent != 0 && ! deletionWatcher->hasBeenDeleted()) | |||
| ownerComponent->removeComponentListener (this); | |||
| deleteAndZero (deletionWatcher); | |||
| ownerComponent = owner; | |||
| leftOfOwnerComp = onLeft; | |||
| if (ownerComponent != 0) | |||
| { | |||
| deletionWatcher = new ComponentDeletionWatcher (owner); | |||
| setVisible (owner->isVisible()); | |||
| ownerComponent->addComponentListener (this); | |||
| componentParentHierarchyChanged (*ownerComponent); | |||
| componentMovedOrResized (*ownerComponent, true, true); | |||
| } | |||
| } | |||
| void Label::componentMovedOrResized (Component& component, | |||
| bool /*wasMoved*/, | |||
| bool /*wasResized*/) | |||
| { | |||
| if (leftOfOwnerComp) | |||
| { | |||
| setSize (jmin (getFont().getStringWidth (text) + 8, component.getX()), | |||
| component.getHeight()); | |||
| setTopRightPosition (component.getX(), component.getY()); | |||
| } | |||
| else | |||
| { | |||
| setSize (component.getWidth(), | |||
| 8 + roundFloatToInt (getFont().getHeight())); | |||
| setTopLeftPosition (component.getX(), component.getY() - getHeight()); | |||
| } | |||
| } | |||
| void Label::componentParentHierarchyChanged (Component& component) | |||
| { | |||
| if (component.getParentComponent() != 0) | |||
| component.getParentComponent()->addChildComponent (this); | |||
| } | |||
| void Label::componentVisibilityChanged (Component& component) | |||
| { | |||
| setVisible (component.isVisible()); | |||
| } | |||
| //============================================================================== | |||
| void Label::textWasEdited() | |||
| { | |||
| } | |||
| void Label::showEditor() | |||
| { | |||
| if (editor == 0) | |||
| { | |||
| addAndMakeVisible (editor = createEditorComponent()); | |||
| editor->setText (getText()); | |||
| editor->addListener (this); | |||
| editor->grabKeyboardFocus(); | |||
| editor->setHighlightedRegion (0, text.length()); | |||
| editor->addListener (this); | |||
| resized(); | |||
| repaint(); | |||
| enterModalState(); | |||
| editor->grabKeyboardFocus(); | |||
| } | |||
| } | |||
| bool Label::updateFromTextEditorContents() | |||
| { | |||
| jassert (editor != 0); | |||
| const String newText (editor->getText()); | |||
| if (text != newText) | |||
| { | |||
| text = newText; | |||
| triggerAsyncUpdate(); | |||
| repaint(); | |||
| if (ownerComponent != 0 && ! deletionWatcher->hasBeenDeleted()) | |||
| componentMovedOrResized (*ownerComponent, true, true); | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| void Label::hideEditor (const bool discardCurrentEditorContents) | |||
| { | |||
| if (editor != 0) | |||
| { | |||
| const bool changed = (! discardCurrentEditorContents) | |||
| && updateFromTextEditorContents(); | |||
| deleteAndZero (editor); | |||
| repaint(); | |||
| if (changed) | |||
| textWasEdited(); | |||
| exitModalState (0); | |||
| } | |||
| } | |||
| void Label::inputAttemptWhenModal() | |||
| { | |||
| if (editor != 0) | |||
| { | |||
| if (lossOfFocusDiscardsChanges) | |||
| textEditorEscapeKeyPressed (*editor); | |||
| else | |||
| textEditorReturnKeyPressed (*editor); | |||
| } | |||
| } | |||
| bool Label::isBeingEdited() const throw() | |||
| { | |||
| return editor != 0; | |||
| } | |||
| TextEditor* Label::createEditorComponent() | |||
| { | |||
| TextEditor* const ed = new TextEditor (getName()); | |||
| ed->setFont (font); | |||
| // copy these colours from our own settings.. | |||
| const int cols[] = { TextEditor::backgroundColourId, | |||
| TextEditor::textColourId, | |||
| TextEditor::highlightColourId, | |||
| TextEditor::highlightedTextColourId, | |||
| TextEditor::caretColourId, | |||
| TextEditor::outlineColourId, | |||
| TextEditor::focusedOutlineColourId, | |||
| TextEditor::shadowColourId }; | |||
| for (int i = 0; i < numElementsInArray (cols); ++i) | |||
| ed->setColour (cols[i], findColour (cols[i])); | |||
| return ed; | |||
| } | |||
| //============================================================================== | |||
| void Label::paint (Graphics& g) | |||
| { | |||
| g.fillAll (findColour (backgroundColourId)); | |||
| if (editor == 0) | |||
| { | |||
| const float alpha = isEnabled() ? 1.0f : 0.5f; | |||
| g.setColour (findColour (textColourId).withMultipliedAlpha (alpha)); | |||
| g.setFont (font); | |||
| g.drawFittedText (text, | |||
| horizontalBorderSize, | |||
| verticalBorderSize, | |||
| getWidth() - 2 * horizontalBorderSize, | |||
| getHeight() - 2 * verticalBorderSize, | |||
| justification, | |||
| jmax (1, (int) (getHeight() / font.getHeight()))); | |||
| g.setColour (findColour (outlineColourId).withMultipliedAlpha (alpha)); | |||
| g.drawRect (0, 0, getWidth(), getHeight()); | |||
| } | |||
| else if (isEnabled()) | |||
| { | |||
| g.setColour (editor->findColour (TextEditor::backgroundColourId) | |||
| .overlaidWith (findColour (outlineColourId))); | |||
| g.drawRect (0, 0, getWidth(), getHeight()); | |||
| } | |||
| } | |||
| void Label::mouseUp (const MouseEvent& e) | |||
| { | |||
| if (editSingleClick | |||
| && e.mouseWasClicked() | |||
| && contains (e.x, e.y) | |||
| && ! e.mods.isPopupMenu()) | |||
| { | |||
| showEditor(); | |||
| } | |||
| } | |||
| void Label::mouseDoubleClick (const MouseEvent& e) | |||
| { | |||
| if (editDoubleClick && ! e.mods.isPopupMenu()) | |||
| showEditor(); | |||
| } | |||
| void Label::resized() | |||
| { | |||
| if (editor != 0) | |||
| editor->setBoundsInset (BorderSize (0)); | |||
| } | |||
| void Label::focusGained (FocusChangeType cause) | |||
| { | |||
| if (editSingleClick && cause == focusChangedByTabKey) | |||
| showEditor(); | |||
| } | |||
| void Label::enablementChanged() | |||
| { | |||
| repaint(); | |||
| } | |||
| void Label::colourChanged() | |||
| { | |||
| repaint(); | |||
| } | |||
| //============================================================================== | |||
| // We'll use a custom focus traverser here to make sure focus goes from the | |||
| // text editor to another component rather than back to the label itself. | |||
| class LabelKeyboardFocusTraverser : public KeyboardFocusTraverser | |||
| { | |||
| public: | |||
| LabelKeyboardFocusTraverser() {} | |||
| Component* getNextComponent (Component* current) | |||
| { | |||
| return KeyboardFocusTraverser::getNextComponent (dynamic_cast <TextEditor*> (current) != 0 | |||
| ? current->getParentComponent() : current); | |||
| } | |||
| Component* getPreviousComponent (Component* current) | |||
| { | |||
| return KeyboardFocusTraverser::getPreviousComponent (dynamic_cast <TextEditor*> (current) != 0 | |||
| ? current->getParentComponent() : current); | |||
| } | |||
| }; | |||
| KeyboardFocusTraverser* Label::createFocusTraverser() | |||
| { | |||
| return new LabelKeyboardFocusTraverser(); | |||
| } | |||
| //============================================================================== | |||
| void Label::addListener (LabelListener* const listener) throw() | |||
| { | |||
| jassert (listener != 0); | |||
| if (listener != 0) | |||
| listeners.add (listener); | |||
| } | |||
| void Label::removeListener (LabelListener* const listener) throw() | |||
| { | |||
| listeners.removeValue (listener); | |||
| } | |||
| void Label::handleAsyncUpdate() | |||
| { | |||
| for (int i = listeners.size(); --i >= 0;) | |||
| { | |||
| ((LabelListener*) listeners.getUnchecked (i))->labelTextChanged (this); | |||
| i = jmin (i, listeners.size()); | |||
| } | |||
| } | |||
| //============================================================================== | |||
| void Label::textEditorTextChanged (TextEditor& ed) | |||
| { | |||
| if (editor != 0) | |||
| { | |||
| jassert (&ed == editor); | |||
| if (! (hasKeyboardFocus (true) || isCurrentlyBlockedByAnotherModalComponent())) | |||
| { | |||
| if (lossOfFocusDiscardsChanges) | |||
| textEditorEscapeKeyPressed (ed); | |||
| else | |||
| textEditorReturnKeyPressed (ed); | |||
| } | |||
| } | |||
| } | |||
| void Label::textEditorReturnKeyPressed (TextEditor& ed) | |||
| { | |||
| if (editor != 0) | |||
| { | |||
| jassert (&ed == editor); | |||
| (void) ed; | |||
| const bool changed = updateFromTextEditorContents(); | |||
| hideEditor (true); | |||
| if (changed) | |||
| textWasEdited(); | |||
| } | |||
| } | |||
| void Label::textEditorEscapeKeyPressed (TextEditor& ed) | |||
| { | |||
| if (editor != 0) | |||
| { | |||
| jassert (&ed == editor); | |||
| (void) ed; | |||
| editor->setText (text, false); | |||
| hideEditor (true); | |||
| } | |||
| } | |||
| void Label::textEditorFocusLost (TextEditor& ed) | |||
| { | |||
| textEditorTextChanged (ed); | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -148,6 +148,12 @@ public: | |||
| /** Returns the type of justification, as set in setJustificationType(). */ | |||
| const Justification getJustificationType() const throw() { return justification; } | |||
| /** Changes the gap that is left between the edge of the component and the text. | |||
| By default there's a small gap left at the sides of the component to allow for | |||
| the drawing of the border, but you can change this if necessary. | |||
| */ | |||
| void setBorderSize (int horizontalBorder, int verticalBorder); | |||
| /** Makes this label "stick to" another component. | |||
| This will cause the label to follow another component around, staying | |||
| @@ -294,7 +300,7 @@ private: | |||
| SortedSet <void*> listeners; | |||
| Component* ownerComponent; | |||
| ComponentDeletionWatcher* deletionWatcher; | |||
| int horizontalBorderSize, verticalBorderSize; | |||
| bool editSingleClick : 1; | |||
| bool editDoubleClick : 1; | |||
| bool lossOfFocusDiscardsChanges : 1; | |||
| @@ -1,421 +1,442 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-7 by Raw Material Software ltd. | |||
| ------------------------------------------------------------------------------ | |||
| JUCE can be redistributed and/or modified under the terms of the | |||
| GNU General Public License, as published by the Free Software Foundation; | |||
| either version 2 of the License, or (at your option) any later version. | |||
| JUCE is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU General Public License for more details. | |||
| You should have received a copy of the GNU General Public License | |||
| along with JUCE; if not, visit www.gnu.org/licenses or write to the | |||
| Free Software Foundation, Inc., 59 Temple Place, Suite 330, | |||
| Boston, MA 02111-1307 USA | |||
| ------------------------------------------------------------------------------ | |||
| If you'd like to release a closed-source product which uses JUCE, commercial | |||
| licenses are also available: visit www.rawmaterialsoftware.com/juce for | |||
| more information. | |||
| ============================================================================== | |||
| */ | |||
| #include "../../../../juce_core/basics/juce_StandardHeader.h" | |||
| BEGIN_JUCE_NAMESPACE | |||
| #include "juce_PropertyPanel.h" | |||
| #include "../lookandfeel/juce_LookAndFeel.h" | |||
| #include "../../../../juce_core/text/juce_LocalisedStrings.h" | |||
| //============================================================================== | |||
| class PropertyHolderComponent : public Component | |||
| { | |||
| public: | |||
| PropertyHolderComponent() | |||
| { | |||
| } | |||
| ~PropertyHolderComponent() | |||
| { | |||
| deleteAllChildren(); | |||
| } | |||
| void paint (Graphics&) | |||
| { | |||
| } | |||
| void updateLayout (const int width); | |||
| void refreshAll() const; | |||
| }; | |||
| //============================================================================== | |||
| class PropertySectionComponent : public Component | |||
| { | |||
| public: | |||
| PropertySectionComponent (const String& sectionTitle, | |||
| const Array <PropertyComponent*>& newProperties, | |||
| const bool open) | |||
| : Component (sectionTitle), | |||
| titleHeight (sectionTitle.isNotEmpty() ? 22 : 0), | |||
| isOpen_ (open) | |||
| { | |||
| for (int i = newProperties.size(); --i >= 0;) | |||
| { | |||
| addAndMakeVisible (newProperties.getUnchecked(i)); | |||
| newProperties.getUnchecked(i)->refresh(); | |||
| } | |||
| } | |||
| ~PropertySectionComponent() | |||
| { | |||
| deleteAllChildren(); | |||
| } | |||
| void paint (Graphics& g) | |||
| { | |||
| if (titleHeight > 0) | |||
| getLookAndFeel().drawPropertyPanelSectionHeader (g, getName(), isOpen(), getWidth(), titleHeight); | |||
| } | |||
| void resized() | |||
| { | |||
| int y = titleHeight; | |||
| for (int i = getNumChildComponents(); --i >= 0;) | |||
| { | |||
| PropertyComponent* const pec = dynamic_cast <PropertyComponent*> (getChildComponent (i)); | |||
| if (pec != 0) | |||
| { | |||
| const int prefH = pec->getPreferredHeight(); | |||
| pec->setBounds (1, y, getWidth() - 2, prefH); | |||
| y += prefH; | |||
| } | |||
| } | |||
| } | |||
| int getPreferredHeight() const | |||
| { | |||
| int y = titleHeight; | |||
| if (isOpen()) | |||
| { | |||
| for (int i = 0; i < getNumChildComponents(); ++i) | |||
| { | |||
| PropertyComponent* pec = dynamic_cast <PropertyComponent*> (getChildComponent (i)); | |||
| if (pec != 0) | |||
| y += pec->getPreferredHeight(); | |||
| } | |||
| } | |||
| return y; | |||
| } | |||
| void setOpen (const bool open) | |||
| { | |||
| if (isOpen_ != open) | |||
| { | |||
| isOpen_ = open; | |||
| for (int i = 0; i < getNumChildComponents(); ++i) | |||
| { | |||
| PropertyComponent* pec = dynamic_cast <PropertyComponent*> (getChildComponent (i)); | |||
| if (pec != 0) | |||
| pec->setVisible (open); | |||
| } | |||
| // (unable to use the syntax findParentComponentOfClass <DragAndDropContainer> () because of a VC6 compiler bug) | |||
| PropertyPanel* const pp = findParentComponentOfClass ((PropertyPanel*) 0); | |||
| if (pp != 0) | |||
| pp->resized(); | |||
| } | |||
| } | |||
| bool isOpen() const throw() | |||
| { | |||
| return isOpen_; | |||
| } | |||
| void refreshAll() const | |||
| { | |||
| for (int i = 0; i < getNumChildComponents(); ++i) | |||
| { | |||
| PropertyComponent* pec = dynamic_cast <PropertyComponent*> (getChildComponent (i)); | |||
| if (pec != 0) | |||
| pec->refresh(); | |||
| } | |||
| } | |||
| void mouseDown (const MouseEvent&) | |||
| { | |||
| } | |||
| void mouseUp (const MouseEvent& e) | |||
| { | |||
| if (e.getMouseDownX() < titleHeight | |||
| && e.x < titleHeight | |||
| && e.y < titleHeight | |||
| && e.getNumberOfClicks() != 2) | |||
| { | |||
| setOpen (! isOpen()); | |||
| } | |||
| } | |||
| void mouseDoubleClick (const MouseEvent& e) | |||
| { | |||
| if (e.y < titleHeight) | |||
| setOpen (! isOpen()); | |||
| } | |||
| private: | |||
| int titleHeight; | |||
| bool isOpen_; | |||
| }; | |||
| void PropertyHolderComponent::updateLayout (const int width) | |||
| { | |||
| int y = 0; | |||
| for (int i = getNumChildComponents(); --i >= 0;) | |||
| { | |||
| PropertySectionComponent* const section | |||
| = dynamic_cast <PropertySectionComponent*> (getChildComponent (i)); | |||
| if (section != 0) | |||
| { | |||
| const int prefH = section->getPreferredHeight(); | |||
| section->setBounds (0, y, width, prefH); | |||
| y += prefH; | |||
| } | |||
| } | |||
| setSize (width, y); | |||
| repaint(); | |||
| } | |||
| void PropertyHolderComponent::refreshAll() const | |||
| { | |||
| for (int i = getNumChildComponents(); --i >= 0;) | |||
| { | |||
| PropertySectionComponent* const section | |||
| = dynamic_cast <PropertySectionComponent*> (getChildComponent (i)); | |||
| if (section != 0) | |||
| section->refreshAll(); | |||
| } | |||
| } | |||
| //============================================================================== | |||
| PropertyPanel::PropertyPanel() | |||
| { | |||
| messageWhenEmpty = TRANS("(nothing selected)"); | |||
| addAndMakeVisible (viewport = new Viewport()); | |||
| viewport->setViewedComponent (propertyHolderComponent = new PropertyHolderComponent()); | |||
| viewport->setFocusContainer (true); | |||
| } | |||
| PropertyPanel::~PropertyPanel() | |||
| { | |||
| clear(); | |||
| deleteAllChildren(); | |||
| } | |||
| //============================================================================== | |||
| void PropertyPanel::paint (Graphics& g) | |||
| { | |||
| if (propertyHolderComponent->getNumChildComponents() == 0) | |||
| { | |||
| g.setColour (Colours::black.withAlpha (0.5f)); | |||
| g.setFont (14.0f); | |||
| g.drawText (messageWhenEmpty, 0, 0, getWidth(), 30, | |||
| Justification::centred, true); | |||
| } | |||
| } | |||
| void PropertyPanel::resized() | |||
| { | |||
| viewport->setBounds (0, 0, getWidth(), getHeight()); | |||
| updatePropHolderLayout(); | |||
| } | |||
| //============================================================================== | |||
| void PropertyPanel::clear() | |||
| { | |||
| if (propertyHolderComponent->getNumChildComponents() > 0) | |||
| { | |||
| propertyHolderComponent->deleteAllChildren(); | |||
| repaint(); | |||
| } | |||
| } | |||
| void PropertyPanel::addProperties (const Array <PropertyComponent*>& newProperties) | |||
| { | |||
| if (propertyHolderComponent->getNumChildComponents() == 0) | |||
| repaint(); | |||
| propertyHolderComponent->addAndMakeVisible (new PropertySectionComponent (String::empty, | |||
| newProperties, | |||
| true), 0); | |||
| updatePropHolderLayout(); | |||
| } | |||
| void PropertyPanel::addSection (const String& sectionTitle, | |||
| const Array <PropertyComponent*>& newProperties, | |||
| const bool shouldBeOpen) | |||
| { | |||
| jassert (sectionTitle.isNotEmpty()); | |||
| if (propertyHolderComponent->getNumChildComponents() == 0) | |||
| repaint(); | |||
| propertyHolderComponent->addAndMakeVisible (new PropertySectionComponent (sectionTitle, | |||
| newProperties, | |||
| shouldBeOpen), 0); | |||
| updatePropHolderLayout(); | |||
| } | |||
| void PropertyPanel::updatePropHolderLayout() const | |||
| { | |||
| const int maxWidth = viewport->getMaximumVisibleWidth(); | |||
| ((PropertyHolderComponent*) propertyHolderComponent)->updateLayout (maxWidth); | |||
| const int newMaxWidth = viewport->getMaximumVisibleWidth(); | |||
| if (maxWidth != newMaxWidth) | |||
| { | |||
| // need to do this twice because of scrollbars changing the size, etc. | |||
| ((PropertyHolderComponent*) propertyHolderComponent)->updateLayout (newMaxWidth); | |||
| } | |||
| } | |||
| void PropertyPanel::refreshAll() const | |||
| { | |||
| ((PropertyHolderComponent*) propertyHolderComponent)->refreshAll(); | |||
| } | |||
| //============================================================================== | |||
| const StringArray PropertyPanel::getSectionNames() const | |||
| { | |||
| StringArray s; | |||
| for (int i = 0; i < propertyHolderComponent->getNumChildComponents(); ++i) | |||
| { | |||
| PropertySectionComponent* const section = dynamic_cast <PropertySectionComponent*> (propertyHolderComponent->getChildComponent (i)); | |||
| if (section != 0 && section->getName().isNotEmpty()) | |||
| s.add (section->getName()); | |||
| } | |||
| return s; | |||
| } | |||
| bool PropertyPanel::isSectionOpen (const int sectionIndex) const | |||
| { | |||
| int index = 0; | |||
| for (int i = 0; i < propertyHolderComponent->getNumChildComponents(); ++i) | |||
| { | |||
| PropertySectionComponent* const section = dynamic_cast <PropertySectionComponent*> (propertyHolderComponent->getChildComponent (i)); | |||
| if (section != 0 && section->getName().isNotEmpty()) | |||
| { | |||
| if (index == sectionIndex) | |||
| return section->isOpen(); | |||
| ++index; | |||
| } | |||
| } | |||
| return false; | |||
| } | |||
| void PropertyPanel::setSectionOpen (const int sectionIndex, const bool shouldBeOpen) | |||
| { | |||
| int index = 0; | |||
| for (int i = 0; i < propertyHolderComponent->getNumChildComponents(); ++i) | |||
| { | |||
| PropertySectionComponent* const section = dynamic_cast <PropertySectionComponent*> (propertyHolderComponent->getChildComponent (i)); | |||
| if (section != 0 && section->getName().isNotEmpty()) | |||
| { | |||
| if (index == sectionIndex) | |||
| { | |||
| section->setOpen (shouldBeOpen); | |||
| break; | |||
| } | |||
| ++index; | |||
| } | |||
| } | |||
| } | |||
| //============================================================================== | |||
| XmlElement* PropertyPanel::getOpennessState() const | |||
| { | |||
| XmlElement* const xml = new XmlElement (T("PROPERTYPANELSTATE")); | |||
| const StringArray sections (getSectionNames()); | |||
| for (int i = 0; i < sections.size(); ++i) | |||
| { | |||
| if (sections[i].isNotEmpty()) | |||
| { | |||
| XmlElement* const e = new XmlElement (T("SECTION")); | |||
| e->setAttribute (T("name"), sections[i]); | |||
| e->setAttribute (T("open"), isSectionOpen (i) ? 1 : 0); | |||
| xml->addChildElement (e); | |||
| } | |||
| } | |||
| return xml; | |||
| } | |||
| void PropertyPanel::restoreOpennessState (const XmlElement& xml) | |||
| { | |||
| if (xml.hasTagName (T("PROPERTYPANELSTATE"))) | |||
| { | |||
| const StringArray sections (getSectionNames()); | |||
| forEachXmlChildElementWithTagName (xml, e, T("SECTION")) | |||
| { | |||
| setSectionOpen (sections.indexOf (e->getStringAttribute (T("name"))), | |||
| e->getBoolAttribute (T("open"))); | |||
| } | |||
| } | |||
| } | |||
| //============================================================================== | |||
| void PropertyPanel::setMessageWhenEmpty (const String& newMessage) | |||
| { | |||
| if (messageWhenEmpty != newMessage) | |||
| { | |||
| messageWhenEmpty = newMessage; | |||
| repaint(); | |||
| } | |||
| } | |||
| const String& PropertyPanel::getMessageWhenEmpty() const throw() | |||
| { | |||
| return messageWhenEmpty; | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-7 by Raw Material Software ltd. | |||
| ------------------------------------------------------------------------------ | |||
| JUCE can be redistributed and/or modified under the terms of the | |||
| GNU General Public License, as published by the Free Software Foundation; | |||
| either version 2 of the License, or (at your option) any later version. | |||
| JUCE is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU General Public License for more details. | |||
| You should have received a copy of the GNU General Public License | |||
| along with JUCE; if not, visit www.gnu.org/licenses or write to the | |||
| Free Software Foundation, Inc., 59 Temple Place, Suite 330, | |||
| Boston, MA 02111-1307 USA | |||
| ------------------------------------------------------------------------------ | |||
| If you'd like to release a closed-source product which uses JUCE, commercial | |||
| licenses are also available: visit www.rawmaterialsoftware.com/juce for | |||
| more information. | |||
| ============================================================================== | |||
| */ | |||
| #include "../../../../juce_core/basics/juce_StandardHeader.h" | |||
| BEGIN_JUCE_NAMESPACE | |||
| #include "juce_PropertyPanel.h" | |||
| #include "../lookandfeel/juce_LookAndFeel.h" | |||
| #include "../../../../juce_core/text/juce_LocalisedStrings.h" | |||
| //============================================================================== | |||
| class PropertyHolderComponent : public Component | |||
| { | |||
| public: | |||
| PropertyHolderComponent() | |||
| { | |||
| } | |||
| ~PropertyHolderComponent() | |||
| { | |||
| deleteAllChildren(); | |||
| } | |||
| void paint (Graphics&) | |||
| { | |||
| } | |||
| void updateLayout (const int width); | |||
| void refreshAll() const; | |||
| }; | |||
| //============================================================================== | |||
| class PropertySectionComponent : public Component | |||
| { | |||
| public: | |||
| PropertySectionComponent (const String& sectionTitle, | |||
| const Array <PropertyComponent*>& newProperties, | |||
| const bool open) | |||
| : Component (sectionTitle), | |||
| titleHeight (sectionTitle.isNotEmpty() ? 22 : 0), | |||
| isOpen_ (open) | |||
| { | |||
| for (int i = newProperties.size(); --i >= 0;) | |||
| { | |||
| addAndMakeVisible (newProperties.getUnchecked(i)); | |||
| newProperties.getUnchecked(i)->refresh(); | |||
| } | |||
| } | |||
| ~PropertySectionComponent() | |||
| { | |||
| deleteAllChildren(); | |||
| } | |||
| void paint (Graphics& g) | |||
| { | |||
| if (titleHeight > 0) | |||
| getLookAndFeel().drawPropertyPanelSectionHeader (g, getName(), isOpen(), getWidth(), titleHeight); | |||
| } | |||
| void resized() | |||
| { | |||
| int y = titleHeight; | |||
| for (int i = getNumChildComponents(); --i >= 0;) | |||
| { | |||
| PropertyComponent* const pec = dynamic_cast <PropertyComponent*> (getChildComponent (i)); | |||
| if (pec != 0) | |||
| { | |||
| const int prefH = pec->getPreferredHeight(); | |||
| pec->setBounds (1, y, getWidth() - 2, prefH); | |||
| y += prefH; | |||
| } | |||
| } | |||
| } | |||
| int getPreferredHeight() const | |||
| { | |||
| int y = titleHeight; | |||
| if (isOpen()) | |||
| { | |||
| for (int i = 0; i < getNumChildComponents(); ++i) | |||
| { | |||
| PropertyComponent* pec = dynamic_cast <PropertyComponent*> (getChildComponent (i)); | |||
| if (pec != 0) | |||
| y += pec->getPreferredHeight(); | |||
| } | |||
| } | |||
| return y; | |||
| } | |||
| void setOpen (const bool open) | |||
| { | |||
| if (isOpen_ != open) | |||
| { | |||
| isOpen_ = open; | |||
| for (int i = 0; i < getNumChildComponents(); ++i) | |||
| { | |||
| PropertyComponent* pec = dynamic_cast <PropertyComponent*> (getChildComponent (i)); | |||
| if (pec != 0) | |||
| pec->setVisible (open); | |||
| } | |||
| // (unable to use the syntax findParentComponentOfClass <DragAndDropContainer> () because of a VC6 compiler bug) | |||
| PropertyPanel* const pp = findParentComponentOfClass ((PropertyPanel*) 0); | |||
| if (pp != 0) | |||
| pp->resized(); | |||
| } | |||
| } | |||
| bool isOpen() const throw() | |||
| { | |||
| return isOpen_; | |||
| } | |||
| void refreshAll() const | |||
| { | |||
| for (int i = 0; i < getNumChildComponents(); ++i) | |||
| { | |||
| PropertyComponent* pec = dynamic_cast <PropertyComponent*> (getChildComponent (i)); | |||
| if (pec != 0) | |||
| pec->refresh(); | |||
| } | |||
| } | |||
| void mouseDown (const MouseEvent&) | |||
| { | |||
| } | |||
| void mouseUp (const MouseEvent& e) | |||
| { | |||
| if (e.getMouseDownX() < titleHeight | |||
| && e.x < titleHeight | |||
| && e.y < titleHeight | |||
| && e.getNumberOfClicks() != 2) | |||
| { | |||
| setOpen (! isOpen()); | |||
| } | |||
| } | |||
| void mouseDoubleClick (const MouseEvent& e) | |||
| { | |||
| if (e.y < titleHeight) | |||
| setOpen (! isOpen()); | |||
| } | |||
| private: | |||
| int titleHeight; | |||
| bool isOpen_; | |||
| }; | |||
| void PropertyHolderComponent::updateLayout (const int width) | |||
| { | |||
| int y = 0; | |||
| for (int i = getNumChildComponents(); --i >= 0;) | |||
| { | |||
| PropertySectionComponent* const section | |||
| = dynamic_cast <PropertySectionComponent*> (getChildComponent (i)); | |||
| if (section != 0) | |||
| { | |||
| const int prefH = section->getPreferredHeight(); | |||
| section->setBounds (0, y, width, prefH); | |||
| y += prefH; | |||
| } | |||
| } | |||
| setSize (width, y); | |||
| repaint(); | |||
| } | |||
| void PropertyHolderComponent::refreshAll() const | |||
| { | |||
| for (int i = getNumChildComponents(); --i >= 0;) | |||
| { | |||
| PropertySectionComponent* const section | |||
| = dynamic_cast <PropertySectionComponent*> (getChildComponent (i)); | |||
| if (section != 0) | |||
| section->refreshAll(); | |||
| } | |||
| } | |||
| //============================================================================== | |||
| PropertyPanel::PropertyPanel() | |||
| { | |||
| messageWhenEmpty = TRANS("(nothing selected)"); | |||
| addAndMakeVisible (viewport = new Viewport()); | |||
| viewport->setViewedComponent (propertyHolderComponent = new PropertyHolderComponent()); | |||
| viewport->setFocusContainer (true); | |||
| } | |||
| PropertyPanel::~PropertyPanel() | |||
| { | |||
| clear(); | |||
| deleteAllChildren(); | |||
| } | |||
| //============================================================================== | |||
| void PropertyPanel::paint (Graphics& g) | |||
| { | |||
| if (propertyHolderComponent->getNumChildComponents() == 0) | |||
| { | |||
| g.setColour (Colours::black.withAlpha (0.5f)); | |||
| g.setFont (14.0f); | |||
| g.drawText (messageWhenEmpty, 0, 0, getWidth(), 30, | |||
| Justification::centred, true); | |||
| } | |||
| } | |||
| void PropertyPanel::resized() | |||
| { | |||
| viewport->setBounds (0, 0, getWidth(), getHeight()); | |||
| updatePropHolderLayout(); | |||
| } | |||
| //============================================================================== | |||
| void PropertyPanel::clear() | |||
| { | |||
| if (propertyHolderComponent->getNumChildComponents() > 0) | |||
| { | |||
| propertyHolderComponent->deleteAllChildren(); | |||
| repaint(); | |||
| } | |||
| } | |||
| void PropertyPanel::addProperties (const Array <PropertyComponent*>& newProperties) | |||
| { | |||
| if (propertyHolderComponent->getNumChildComponents() == 0) | |||
| repaint(); | |||
| propertyHolderComponent->addAndMakeVisible (new PropertySectionComponent (String::empty, | |||
| newProperties, | |||
| true), 0); | |||
| updatePropHolderLayout(); | |||
| } | |||
| void PropertyPanel::addSection (const String& sectionTitle, | |||
| const Array <PropertyComponent*>& newProperties, | |||
| const bool shouldBeOpen) | |||
| { | |||
| jassert (sectionTitle.isNotEmpty()); | |||
| if (propertyHolderComponent->getNumChildComponents() == 0) | |||
| repaint(); | |||
| propertyHolderComponent->addAndMakeVisible (new PropertySectionComponent (sectionTitle, | |||
| newProperties, | |||
| shouldBeOpen), 0); | |||
| updatePropHolderLayout(); | |||
| } | |||
| void PropertyPanel::updatePropHolderLayout() const | |||
| { | |||
| const int maxWidth = viewport->getMaximumVisibleWidth(); | |||
| ((PropertyHolderComponent*) propertyHolderComponent)->updateLayout (maxWidth); | |||
| const int newMaxWidth = viewport->getMaximumVisibleWidth(); | |||
| if (maxWidth != newMaxWidth) | |||
| { | |||
| // need to do this twice because of scrollbars changing the size, etc. | |||
| ((PropertyHolderComponent*) propertyHolderComponent)->updateLayout (newMaxWidth); | |||
| } | |||
| } | |||
| void PropertyPanel::refreshAll() const | |||
| { | |||
| ((PropertyHolderComponent*) propertyHolderComponent)->refreshAll(); | |||
| } | |||
| //============================================================================== | |||
| const StringArray PropertyPanel::getSectionNames() const | |||
| { | |||
| StringArray s; | |||
| for (int i = 0; i < propertyHolderComponent->getNumChildComponents(); ++i) | |||
| { | |||
| PropertySectionComponent* const section = dynamic_cast <PropertySectionComponent*> (propertyHolderComponent->getChildComponent (i)); | |||
| if (section != 0 && section->getName().isNotEmpty()) | |||
| s.add (section->getName()); | |||
| } | |||
| return s; | |||
| } | |||
| bool PropertyPanel::isSectionOpen (const int sectionIndex) const | |||
| { | |||
| int index = 0; | |||
| for (int i = 0; i < propertyHolderComponent->getNumChildComponents(); ++i) | |||
| { | |||
| PropertySectionComponent* const section = dynamic_cast <PropertySectionComponent*> (propertyHolderComponent->getChildComponent (i)); | |||
| if (section != 0 && section->getName().isNotEmpty()) | |||
| { | |||
| if (index == sectionIndex) | |||
| return section->isOpen(); | |||
| ++index; | |||
| } | |||
| } | |||
| return false; | |||
| } | |||
| void PropertyPanel::setSectionOpen (const int sectionIndex, const bool shouldBeOpen) | |||
| { | |||
| int index = 0; | |||
| for (int i = 0; i < propertyHolderComponent->getNumChildComponents(); ++i) | |||
| { | |||
| PropertySectionComponent* const section = dynamic_cast <PropertySectionComponent*> (propertyHolderComponent->getChildComponent (i)); | |||
| if (section != 0 && section->getName().isNotEmpty()) | |||
| { | |||
| if (index == sectionIndex) | |||
| { | |||
| section->setOpen (shouldBeOpen); | |||
| break; | |||
| } | |||
| ++index; | |||
| } | |||
| } | |||
| } | |||
| void PropertyPanel::setSectionEnabled (const int sectionIndex, const bool shouldBeEnabled) | |||
| { | |||
| int index = 0; | |||
| for (int i = 0; i < propertyHolderComponent->getNumChildComponents(); ++i) | |||
| { | |||
| PropertySectionComponent* const section = dynamic_cast <PropertySectionComponent*> (propertyHolderComponent->getChildComponent (i)); | |||
| if (section != 0 && section->getName().isNotEmpty()) | |||
| { | |||
| if (index == sectionIndex) | |||
| { | |||
| section->setEnabled (shouldBeEnabled); | |||
| break; | |||
| } | |||
| ++index; | |||
| } | |||
| } | |||
| } | |||
| //============================================================================== | |||
| XmlElement* PropertyPanel::getOpennessState() const | |||
| { | |||
| XmlElement* const xml = new XmlElement (T("PROPERTYPANELSTATE")); | |||
| const StringArray sections (getSectionNames()); | |||
| for (int i = 0; i < sections.size(); ++i) | |||
| { | |||
| if (sections[i].isNotEmpty()) | |||
| { | |||
| XmlElement* const e = new XmlElement (T("SECTION")); | |||
| e->setAttribute (T("name"), sections[i]); | |||
| e->setAttribute (T("open"), isSectionOpen (i) ? 1 : 0); | |||
| xml->addChildElement (e); | |||
| } | |||
| } | |||
| return xml; | |||
| } | |||
| void PropertyPanel::restoreOpennessState (const XmlElement& xml) | |||
| { | |||
| if (xml.hasTagName (T("PROPERTYPANELSTATE"))) | |||
| { | |||
| const StringArray sections (getSectionNames()); | |||
| forEachXmlChildElementWithTagName (xml, e, T("SECTION")) | |||
| { | |||
| setSectionOpen (sections.indexOf (e->getStringAttribute (T("name"))), | |||
| e->getBoolAttribute (T("open"))); | |||
| } | |||
| } | |||
| } | |||
| //============================================================================== | |||
| void PropertyPanel::setMessageWhenEmpty (const String& newMessage) | |||
| { | |||
| if (messageWhenEmpty != newMessage) | |||
| { | |||
| messageWhenEmpty = newMessage; | |||
| repaint(); | |||
| } | |||
| } | |||
| const String& PropertyPanel::getMessageWhenEmpty() const throw() | |||
| { | |||
| return messageWhenEmpty; | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -109,6 +109,12 @@ public: | |||
| */ | |||
| void setSectionOpen (const int sectionIndex, const bool shouldBeOpen); | |||
| /** Enables or disables one of the sections. | |||
| The index is from 0 up to the number of items returned by getSectionNames(). | |||
| */ | |||
| void setSectionEnabled (const int sectionIndex, const bool shouldBeEnabled); | |||
| //============================================================================== | |||
| /** Saves the current state of open/closed sections so it can be restored later. | |||
| @@ -1,376 +1,378 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-7 by Raw Material Software ltd. | |||
| ------------------------------------------------------------------------------ | |||
| JUCE can be redistributed and/or modified under the terms of the | |||
| GNU General Public License, as published by the Free Software Foundation; | |||
| either version 2 of the License, or (at your option) any later version. | |||
| JUCE is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU General Public License for more details. | |||
| You should have received a copy of the GNU General Public License | |||
| along with JUCE; if not, visit www.gnu.org/licenses or write to the | |||
| Free Software Foundation, Inc., 59 Temple Place, Suite 330, | |||
| Boston, MA 02111-1307 USA | |||
| ------------------------------------------------------------------------------ | |||
| If you'd like to release a closed-source product which uses JUCE, commercial | |||
| licenses are also available: visit www.rawmaterialsoftware.com/juce for | |||
| more information. | |||
| ============================================================================== | |||
| */ | |||
| #include "../../../../../juce_core/basics/juce_StandardHeader.h" | |||
| #if JUCE_MSVC | |||
| #pragma warning (push) | |||
| #endif | |||
| namespace jpeglibNamespace | |||
| { | |||
| extern "C" | |||
| { | |||
| #define JPEG_INTERNALS | |||
| #undef FAR | |||
| #include "jpglib/jpeglib.h" | |||
| #include "jpglib/jcapimin.c" | |||
| #include "jpglib/jcapistd.c" | |||
| #include "jpglib/jccoefct.c" | |||
| #include "jpglib/jccolor.c" | |||
| #undef FIX | |||
| #include "jpglib/jcdctmgr.c" | |||
| #undef CONST_BITS | |||
| #include "jpglib/jchuff.c" | |||
| #undef emit_byte | |||
| #include "jpglib/jcinit.c" | |||
| #include "jpglib/jcmainct.c" | |||
| #include "jpglib/jcmarker.c" | |||
| #include "jpglib/jcmaster.c" | |||
| #include "jpglib/jcomapi.c" | |||
| #include "jpglib/jcparam.c" | |||
| #include "jpglib/jcphuff.c" | |||
| #include "jpglib/jcprepct.c" | |||
| #include "jpglib/jcsample.c" | |||
| #include "jpglib/jctrans.c" | |||
| #include "jpglib/jdapistd.c" | |||
| #include "jpglib/jdapimin.c" | |||
| #include "jpglib/jdatasrc.c" | |||
| #include "jpglib/jdcoefct.c" | |||
| #undef FIX | |||
| #include "jpglib/jdcolor.c" | |||
| #undef FIX | |||
| #include "jpglib/jddctmgr.c" | |||
| #undef CONST_BITS | |||
| #undef ASSIGN_STATE | |||
| #include "jpglib/jdhuff.c" | |||
| #include "jpglib/jdinput.c" | |||
| #include "jpglib/jdmainct.c" | |||
| #include "jpglib/jdmarker.c" | |||
| #include "jpglib/jdmaster.c" | |||
| #undef FIX | |||
| #include "jpglib/jdmerge.c" | |||
| #undef ASSIGN_STATE | |||
| #include "jpglib/jdphuff.c" | |||
| #include "jpglib/jdpostct.c" | |||
| #undef FIX | |||
| #include "jpglib/jdsample.c" | |||
| #include "jpglib/jdtrans.c" | |||
| #include "jpglib/jfdctflt.c" | |||
| #include "jpglib/jfdctint.c" | |||
| #undef CONST_BITS | |||
| #undef MULTIPLY | |||
| #undef FIX_0_541196100 | |||
| #include "jpglib/jfdctfst.c" | |||
| #undef FIX_0_541196100 | |||
| #include "jpglib/jidctflt.c" | |||
| #undef CONST_BITS | |||
| #undef FIX_1_847759065 | |||
| #undef MULTIPLY | |||
| #undef DEQUANTIZE | |||
| #undef DESCALE | |||
| #include "jpglib/jidctfst.c" | |||
| #undef CONST_BITS | |||
| #undef FIX_1_847759065 | |||
| #undef MULTIPLY | |||
| #undef DEQUANTIZE | |||
| #include "jpglib/jidctint.c" | |||
| #include "jpglib/jidctred.c" | |||
| #include "jpglib/jmemmgr.c" | |||
| #include "jpglib/jmemnobs.c" | |||
| #include "jpglib/jquant1.c" | |||
| #include "jpglib/jquant2.c" | |||
| #include "jpglib/jutils.c" | |||
| #include "jpglib/transupp.c" | |||
| } | |||
| } | |||
| #if JUCE_MSVC | |||
| #pragma warning (pop) | |||
| #endif | |||
| BEGIN_JUCE_NAMESPACE | |||
| #include "../juce_Image.h" | |||
| #include "../../../../../juce_core/io/juce_InputStream.h" | |||
| #include "../../../../../juce_core/io/juce_OutputStream.h" | |||
| #include "../../colour/juce_PixelFormats.h" | |||
| using namespace jpeglibNamespace; | |||
| //============================================================================== | |||
| struct JPEGDecodingFailure {}; | |||
| static void fatalErrorHandler (j_common_ptr) | |||
| { | |||
| throw JPEGDecodingFailure(); | |||
| } | |||
| static void silentErrorCallback1 (j_common_ptr) {} | |||
| static void silentErrorCallback2 (j_common_ptr, int) {} | |||
| static void silentErrorCallback3 (j_common_ptr, char*) {} | |||
| static void setupSilentErrorHandler (struct jpeg_error_mgr& err) | |||
| { | |||
| zerostruct (err); | |||
| err.error_exit = fatalErrorHandler; | |||
| err.emit_message = silentErrorCallback2; | |||
| err.output_message = silentErrorCallback1; | |||
| err.format_message = silentErrorCallback3; | |||
| err.reset_error_mgr = silentErrorCallback1; | |||
| } | |||
| //============================================================================== | |||
| static void dummyCallback1 (j_decompress_ptr) throw() | |||
| { | |||
| } | |||
| static void jpegSkip (j_decompress_ptr decompStruct, long num) throw() | |||
| { | |||
| decompStruct->src->next_input_byte += num; | |||
| num = jmin (num, (int) decompStruct->src->bytes_in_buffer); | |||
| decompStruct->src->bytes_in_buffer -= num; | |||
| } | |||
| static boolean jpegFill (j_decompress_ptr) throw() | |||
| { | |||
| return 0; | |||
| } | |||
| //============================================================================== | |||
| Image* juce_loadJPEGImageFromStream (InputStream& in) throw() | |||
| { | |||
| MemoryBlock mb; | |||
| in.readIntoMemoryBlock (mb); | |||
| Image* image = 0; | |||
| if (mb.getSize() > 16) | |||
| { | |||
| struct jpeg_decompress_struct jpegDecompStruct; | |||
| struct jpeg_error_mgr jerr; | |||
| setupSilentErrorHandler (jerr); | |||
| jpegDecompStruct.err = &jerr; | |||
| jpeg_create_decompress (&jpegDecompStruct); | |||
| jpegDecompStruct.src = (jpeg_source_mgr*)(jpegDecompStruct.mem->alloc_small) | |||
| ((j_common_ptr)(&jpegDecompStruct), JPOOL_PERMANENT, sizeof (jpeg_source_mgr)); | |||
| jpegDecompStruct.src->init_source = dummyCallback1; | |||
| jpegDecompStruct.src->fill_input_buffer = jpegFill; | |||
| jpegDecompStruct.src->skip_input_data = jpegSkip; | |||
| jpegDecompStruct.src->resync_to_restart = jpeg_resync_to_restart; | |||
| jpegDecompStruct.src->term_source = dummyCallback1; | |||
| jpegDecompStruct.src->next_input_byte = (const unsigned char*) mb.getData(); | |||
| jpegDecompStruct.src->bytes_in_buffer = mb.getSize(); | |||
| try | |||
| { | |||
| jpeg_read_header (&jpegDecompStruct, TRUE); | |||
| jpeg_calc_output_dimensions (&jpegDecompStruct); | |||
| const int width = jpegDecompStruct.output_width; | |||
| const int height = jpegDecompStruct.output_height; | |||
| jpegDecompStruct.out_color_space = JCS_RGB; | |||
| JSAMPARRAY buffer | |||
| = (*jpegDecompStruct.mem->alloc_sarray) ((j_common_ptr) &jpegDecompStruct, | |||
| JPOOL_IMAGE, | |||
| width * 3, 1); | |||
| if (jpeg_start_decompress (&jpegDecompStruct)) | |||
| { | |||
| image = new Image (Image::RGB, width, height, false); | |||
| for (int y = 0; y < height; ++y) | |||
| { | |||
| jpeg_read_scanlines (&jpegDecompStruct, buffer, 1); | |||
| int stride, pixelStride; | |||
| uint8* pixels = image->lockPixelDataReadWrite (0, y, width, 1, stride, pixelStride); | |||
| const uint8* src = *buffer; | |||
| uint8* dest = pixels; | |||
| for (int i = width; --i >= 0;) | |||
| { | |||
| ((PixelRGB*) dest)->setARGB (0, src[0], src[1], src[2]); | |||
| dest += pixelStride; | |||
| src += 3; | |||
| } | |||
| image->releasePixelDataReadWrite (pixels); | |||
| } | |||
| jpeg_finish_decompress (&jpegDecompStruct); | |||
| } | |||
| jpeg_destroy_decompress (&jpegDecompStruct); | |||
| } | |||
| catch (...) | |||
| {} | |||
| } | |||
| return image; | |||
| } | |||
| //============================================================================== | |||
| static const int bufferSize = 512; | |||
| struct JuceJpegDest : public jpeg_destination_mgr | |||
| { | |||
| OutputStream* output; | |||
| char* buffer; | |||
| }; | |||
| static void jpegWriteInit (j_compress_ptr) throw() | |||
| { | |||
| } | |||
| static void jpegWriteTerminate (j_compress_ptr cinfo) throw() | |||
| { | |||
| JuceJpegDest* const dest = (JuceJpegDest*) cinfo->dest; | |||
| const int numToWrite = bufferSize - dest->free_in_buffer; | |||
| dest->output->write (dest->buffer, numToWrite); | |||
| } | |||
| static boolean jpegWriteFlush (j_compress_ptr cinfo) throw() | |||
| { | |||
| JuceJpegDest* const dest = (JuceJpegDest*) cinfo->dest; | |||
| const int numToWrite = bufferSize; | |||
| dest->next_output_byte = (JOCTET*) dest->buffer; | |||
| dest->free_in_buffer = bufferSize; | |||
| return dest->output->write (dest->buffer, numToWrite); | |||
| } | |||
| //============================================================================== | |||
| bool juce_writeJPEGImageToStream (const Image& image, | |||
| OutputStream& out, | |||
| float quality) throw() | |||
| { | |||
| if (image.hasAlphaChannel()) | |||
| { | |||
| // this method could fill the background in white and still save the image.. | |||
| jassertfalse | |||
| return true; | |||
| } | |||
| struct jpeg_compress_struct jpegCompStruct; | |||
| struct jpeg_error_mgr jerr; | |||
| setupSilentErrorHandler (jerr); | |||
| jpegCompStruct.err = &jerr; | |||
| jpeg_create_compress (&jpegCompStruct); | |||
| JuceJpegDest dest; | |||
| jpegCompStruct.dest = &dest; | |||
| dest.output = &out; | |||
| dest.buffer = (char*) juce_malloc (bufferSize); | |||
| dest.next_output_byte = (JOCTET*) dest.buffer; | |||
| dest.free_in_buffer = bufferSize; | |||
| dest.init_destination = jpegWriteInit; | |||
| dest.empty_output_buffer = jpegWriteFlush; | |||
| dest.term_destination = jpegWriteTerminate; | |||
| jpegCompStruct.image_width = image.getWidth(); | |||
| jpegCompStruct.image_height = image.getHeight(); | |||
| jpegCompStruct.input_components = 3; | |||
| jpegCompStruct.in_color_space = JCS_RGB; | |||
| jpegCompStruct.write_JFIF_header = 1; | |||
| jpegCompStruct.X_density = 72; | |||
| jpegCompStruct.Y_density = 72; | |||
| jpeg_set_defaults (&jpegCompStruct); | |||
| jpegCompStruct.dct_method = JDCT_FLOAT; | |||
| jpegCompStruct.optimize_coding = 1; | |||
| // jpegCompStruct.smoothing_factor = 10; | |||
| if (quality < 0.0f) | |||
| quality = 0.85f; | |||
| jpeg_set_quality (&jpegCompStruct, jlimit (0, 100, roundFloatToInt (quality * 100.0f)), TRUE); | |||
| jpeg_start_compress (&jpegCompStruct, TRUE); | |||
| const int strideBytes = jpegCompStruct.image_width * jpegCompStruct.input_components; | |||
| JSAMPARRAY buffer = (*jpegCompStruct.mem->alloc_sarray) ((j_common_ptr) &jpegCompStruct, | |||
| JPOOL_IMAGE, | |||
| strideBytes, 1); | |||
| while (jpegCompStruct.next_scanline < jpegCompStruct.image_height) | |||
| { | |||
| int stride, pixelStride; | |||
| const uint8* pixels = image.lockPixelDataReadOnly (0, jpegCompStruct.next_scanline, jpegCompStruct.image_width, 1, stride, pixelStride); | |||
| const uint8* src = pixels; | |||
| uint8* dst = *buffer; | |||
| for (int i = jpegCompStruct.image_width; --i >= 0;) | |||
| { | |||
| *dst++ = ((const PixelRGB*) src)->getRed(); | |||
| *dst++ = ((const PixelRGB*) src)->getGreen(); | |||
| *dst++ = ((const PixelRGB*) src)->getBlue(); | |||
| src += pixelStride; | |||
| } | |||
| jpeg_write_scanlines (&jpegCompStruct, buffer, 1); | |||
| image.releasePixelDataReadOnly (pixels); | |||
| } | |||
| jpeg_finish_compress (&jpegCompStruct); | |||
| jpeg_destroy_compress (&jpegCompStruct); | |||
| juce_free (dest.buffer); | |||
| out.flush(); | |||
| return true; | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-7 by Raw Material Software ltd. | |||
| ------------------------------------------------------------------------------ | |||
| JUCE can be redistributed and/or modified under the terms of the | |||
| GNU General Public License, as published by the Free Software Foundation; | |||
| either version 2 of the License, or (at your option) any later version. | |||
| JUCE is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU General Public License for more details. | |||
| You should have received a copy of the GNU General Public License | |||
| along with JUCE; if not, visit www.gnu.org/licenses or write to the | |||
| Free Software Foundation, Inc., 59 Temple Place, Suite 330, | |||
| Boston, MA 02111-1307 USA | |||
| ------------------------------------------------------------------------------ | |||
| If you'd like to release a closed-source product which uses JUCE, commercial | |||
| licenses are also available: visit www.rawmaterialsoftware.com/juce for | |||
| more information. | |||
| ============================================================================== | |||
| */ | |||
| #include "../../../../../juce_core/basics/juce_StandardHeader.h" | |||
| #if JUCE_MSVC | |||
| #pragma warning (push) | |||
| #endif | |||
| namespace jpeglibNamespace | |||
| { | |||
| extern "C" | |||
| { | |||
| #define JPEG_INTERNALS | |||
| #undef FAR | |||
| #include "jpglib/jpeglib.h" | |||
| #include "jpglib/jcapimin.c" | |||
| #include "jpglib/jcapistd.c" | |||
| #include "jpglib/jccoefct.c" | |||
| #include "jpglib/jccolor.c" | |||
| #undef FIX | |||
| #include "jpglib/jcdctmgr.c" | |||
| #undef CONST_BITS | |||
| #include "jpglib/jchuff.c" | |||
| #undef emit_byte | |||
| #include "jpglib/jcinit.c" | |||
| #include "jpglib/jcmainct.c" | |||
| #include "jpglib/jcmarker.c" | |||
| #include "jpglib/jcmaster.c" | |||
| #include "jpglib/jcomapi.c" | |||
| #include "jpglib/jcparam.c" | |||
| #include "jpglib/jcphuff.c" | |||
| #include "jpglib/jcprepct.c" | |||
| #include "jpglib/jcsample.c" | |||
| #include "jpglib/jctrans.c" | |||
| #include "jpglib/jdapistd.c" | |||
| #include "jpglib/jdapimin.c" | |||
| #include "jpglib/jdatasrc.c" | |||
| #include "jpglib/jdcoefct.c" | |||
| #undef FIX | |||
| #include "jpglib/jdcolor.c" | |||
| #undef FIX | |||
| #include "jpglib/jddctmgr.c" | |||
| #undef CONST_BITS | |||
| #undef ASSIGN_STATE | |||
| #include "jpglib/jdhuff.c" | |||
| #include "jpglib/jdinput.c" | |||
| #include "jpglib/jdmainct.c" | |||
| #include "jpglib/jdmarker.c" | |||
| #include "jpglib/jdmaster.c" | |||
| #undef FIX | |||
| #include "jpglib/jdmerge.c" | |||
| #undef ASSIGN_STATE | |||
| #include "jpglib/jdphuff.c" | |||
| #include "jpglib/jdpostct.c" | |||
| #undef FIX | |||
| #include "jpglib/jdsample.c" | |||
| #include "jpglib/jdtrans.c" | |||
| #include "jpglib/jfdctflt.c" | |||
| #include "jpglib/jfdctint.c" | |||
| #undef CONST_BITS | |||
| #undef MULTIPLY | |||
| #undef FIX_0_541196100 | |||
| #include "jpglib/jfdctfst.c" | |||
| #undef FIX_0_541196100 | |||
| #include "jpglib/jidctflt.c" | |||
| #undef CONST_BITS | |||
| #undef FIX_1_847759065 | |||
| #undef MULTIPLY | |||
| #undef DEQUANTIZE | |||
| #undef DESCALE | |||
| #include "jpglib/jidctfst.c" | |||
| #undef CONST_BITS | |||
| #undef FIX_1_847759065 | |||
| #undef MULTIPLY | |||
| #undef DEQUANTIZE | |||
| #include "jpglib/jidctint.c" | |||
| #include "jpglib/jidctred.c" | |||
| #include "jpglib/jmemmgr.c" | |||
| #include "jpglib/jmemnobs.c" | |||
| #include "jpglib/jquant1.c" | |||
| #include "jpglib/jquant2.c" | |||
| #include "jpglib/jutils.c" | |||
| #include "jpglib/transupp.c" | |||
| } | |||
| } | |||
| #if JUCE_MSVC | |||
| #pragma warning (pop) | |||
| #endif | |||
| BEGIN_JUCE_NAMESPACE | |||
| #include "../juce_Image.h" | |||
| #include "../../../../../juce_core/io/juce_InputStream.h" | |||
| #include "../../../../../juce_core/io/juce_OutputStream.h" | |||
| #include "../../colour/juce_PixelFormats.h" | |||
| using namespace jpeglibNamespace; | |||
| //============================================================================== | |||
| struct JPEGDecodingFailure {}; | |||
| static void fatalErrorHandler (j_common_ptr) | |||
| { | |||
| throw JPEGDecodingFailure(); | |||
| } | |||
| static void silentErrorCallback1 (j_common_ptr) {} | |||
| static void silentErrorCallback2 (j_common_ptr, int) {} | |||
| static void silentErrorCallback3 (j_common_ptr, char*) {} | |||
| static void setupSilentErrorHandler (struct jpeg_error_mgr& err) | |||
| { | |||
| zerostruct (err); | |||
| err.error_exit = fatalErrorHandler; | |||
| err.emit_message = silentErrorCallback2; | |||
| err.output_message = silentErrorCallback1; | |||
| err.format_message = silentErrorCallback3; | |||
| err.reset_error_mgr = silentErrorCallback1; | |||
| } | |||
| //============================================================================== | |||
| static void dummyCallback1 (j_decompress_ptr) throw() | |||
| { | |||
| } | |||
| static void jpegSkip (j_decompress_ptr decompStruct, long num) throw() | |||
| { | |||
| decompStruct->src->next_input_byte += num; | |||
| num = jmin (num, (int) decompStruct->src->bytes_in_buffer); | |||
| decompStruct->src->bytes_in_buffer -= num; | |||
| } | |||
| static boolean jpegFill (j_decompress_ptr) throw() | |||
| { | |||
| return 0; | |||
| } | |||
| //============================================================================== | |||
| Image* juce_loadJPEGImageFromStream (InputStream& in) throw() | |||
| { | |||
| MemoryBlock mb; | |||
| in.readIntoMemoryBlock (mb); | |||
| Image* image = 0; | |||
| if (mb.getSize() > 16) | |||
| { | |||
| struct jpeg_decompress_struct jpegDecompStruct; | |||
| struct jpeg_error_mgr jerr; | |||
| setupSilentErrorHandler (jerr); | |||
| jpegDecompStruct.err = &jerr; | |||
| jpeg_create_decompress (&jpegDecompStruct); | |||
| jpegDecompStruct.src = (jpeg_source_mgr*)(jpegDecompStruct.mem->alloc_small) | |||
| ((j_common_ptr)(&jpegDecompStruct), JPOOL_PERMANENT, sizeof (jpeg_source_mgr)); | |||
| jpegDecompStruct.src->init_source = dummyCallback1; | |||
| jpegDecompStruct.src->fill_input_buffer = jpegFill; | |||
| jpegDecompStruct.src->skip_input_data = jpegSkip; | |||
| jpegDecompStruct.src->resync_to_restart = jpeg_resync_to_restart; | |||
| jpegDecompStruct.src->term_source = dummyCallback1; | |||
| jpegDecompStruct.src->next_input_byte = (const unsigned char*) mb.getData(); | |||
| jpegDecompStruct.src->bytes_in_buffer = mb.getSize(); | |||
| try | |||
| { | |||
| jpeg_read_header (&jpegDecompStruct, TRUE); | |||
| jpeg_calc_output_dimensions (&jpegDecompStruct); | |||
| const int width = jpegDecompStruct.output_width; | |||
| const int height = jpegDecompStruct.output_height; | |||
| jpegDecompStruct.out_color_space = JCS_RGB; | |||
| JSAMPARRAY buffer | |||
| = (*jpegDecompStruct.mem->alloc_sarray) ((j_common_ptr) &jpegDecompStruct, | |||
| JPOOL_IMAGE, | |||
| width * 3, 1); | |||
| if (jpeg_start_decompress (&jpegDecompStruct)) | |||
| { | |||
| image = new Image (Image::RGB, width, height, false); | |||
| for (int y = 0; y < height; ++y) | |||
| { | |||
| jpeg_read_scanlines (&jpegDecompStruct, buffer, 1); | |||
| int stride, pixelStride; | |||
| uint8* pixels = image->lockPixelDataReadWrite (0, y, width, 1, stride, pixelStride); | |||
| const uint8* src = *buffer; | |||
| uint8* dest = pixels; | |||
| for (int i = width; --i >= 0;) | |||
| { | |||
| ((PixelRGB*) dest)->setARGB (0, src[0], src[1], src[2]); | |||
| dest += pixelStride; | |||
| src += 3; | |||
| } | |||
| image->releasePixelDataReadWrite (pixels); | |||
| } | |||
| jpeg_finish_decompress (&jpegDecompStruct); | |||
| } | |||
| jpeg_destroy_decompress (&jpegDecompStruct); | |||
| } | |||
| catch (...) | |||
| {} | |||
| in.setPosition (((char*) jpegDecompStruct.src->next_input_byte) - (char*) mb.getData()); | |||
| } | |||
| return image; | |||
| } | |||
| //============================================================================== | |||
| static const int bufferSize = 512; | |||
| struct JuceJpegDest : public jpeg_destination_mgr | |||
| { | |||
| OutputStream* output; | |||
| char* buffer; | |||
| }; | |||
| static void jpegWriteInit (j_compress_ptr) throw() | |||
| { | |||
| } | |||
| static void jpegWriteTerminate (j_compress_ptr cinfo) throw() | |||
| { | |||
| JuceJpegDest* const dest = (JuceJpegDest*) cinfo->dest; | |||
| const int numToWrite = bufferSize - dest->free_in_buffer; | |||
| dest->output->write (dest->buffer, numToWrite); | |||
| } | |||
| static boolean jpegWriteFlush (j_compress_ptr cinfo) throw() | |||
| { | |||
| JuceJpegDest* const dest = (JuceJpegDest*) cinfo->dest; | |||
| const int numToWrite = bufferSize; | |||
| dest->next_output_byte = (JOCTET*) dest->buffer; | |||
| dest->free_in_buffer = bufferSize; | |||
| return dest->output->write (dest->buffer, numToWrite); | |||
| } | |||
| //============================================================================== | |||
| bool juce_writeJPEGImageToStream (const Image& image, | |||
| OutputStream& out, | |||
| float quality) throw() | |||
| { | |||
| if (image.hasAlphaChannel()) | |||
| { | |||
| // this method could fill the background in white and still save the image.. | |||
| jassertfalse | |||
| return true; | |||
| } | |||
| struct jpeg_compress_struct jpegCompStruct; | |||
| struct jpeg_error_mgr jerr; | |||
| setupSilentErrorHandler (jerr); | |||
| jpegCompStruct.err = &jerr; | |||
| jpeg_create_compress (&jpegCompStruct); | |||
| JuceJpegDest dest; | |||
| jpegCompStruct.dest = &dest; | |||
| dest.output = &out; | |||
| dest.buffer = (char*) juce_malloc (bufferSize); | |||
| dest.next_output_byte = (JOCTET*) dest.buffer; | |||
| dest.free_in_buffer = bufferSize; | |||
| dest.init_destination = jpegWriteInit; | |||
| dest.empty_output_buffer = jpegWriteFlush; | |||
| dest.term_destination = jpegWriteTerminate; | |||
| jpegCompStruct.image_width = image.getWidth(); | |||
| jpegCompStruct.image_height = image.getHeight(); | |||
| jpegCompStruct.input_components = 3; | |||
| jpegCompStruct.in_color_space = JCS_RGB; | |||
| jpegCompStruct.write_JFIF_header = 1; | |||
| jpegCompStruct.X_density = 72; | |||
| jpegCompStruct.Y_density = 72; | |||
| jpeg_set_defaults (&jpegCompStruct); | |||
| jpegCompStruct.dct_method = JDCT_FLOAT; | |||
| jpegCompStruct.optimize_coding = 1; | |||
| // jpegCompStruct.smoothing_factor = 10; | |||
| if (quality < 0.0f) | |||
| quality = 0.85f; | |||
| jpeg_set_quality (&jpegCompStruct, jlimit (0, 100, roundFloatToInt (quality * 100.0f)), TRUE); | |||
| jpeg_start_compress (&jpegCompStruct, TRUE); | |||
| const int strideBytes = jpegCompStruct.image_width * jpegCompStruct.input_components; | |||
| JSAMPARRAY buffer = (*jpegCompStruct.mem->alloc_sarray) ((j_common_ptr) &jpegCompStruct, | |||
| JPOOL_IMAGE, | |||
| strideBytes, 1); | |||
| while (jpegCompStruct.next_scanline < jpegCompStruct.image_height) | |||
| { | |||
| int stride, pixelStride; | |||
| const uint8* pixels = image.lockPixelDataReadOnly (0, jpegCompStruct.next_scanline, jpegCompStruct.image_width, 1, stride, pixelStride); | |||
| const uint8* src = pixels; | |||
| uint8* dst = *buffer; | |||
| for (int i = jpegCompStruct.image_width; --i >= 0;) | |||
| { | |||
| *dst++ = ((const PixelRGB*) src)->getRed(); | |||
| *dst++ = ((const PixelRGB*) src)->getGreen(); | |||
| *dst++ = ((const PixelRGB*) src)->getBlue(); | |||
| src += pixelStride; | |||
| } | |||
| jpeg_write_scanlines (&jpegCompStruct, buffer, 1); | |||
| image.releasePixelDataReadOnly (pixels); | |||
| } | |||
| jpeg_finish_compress (&jpegCompStruct); | |||
| jpeg_destroy_compress (&jpegCompStruct); | |||
| juce_free (dest.buffer); | |||
| out.flush(); | |||
| return true; | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -81,6 +81,7 @@ | |||
| #include <stdexcept> | |||
| #include <typeinfo> | |||
| #include <cstring> | |||
| #include <cstdio> | |||
| #if JUCE_MAC || JUCE_LINUX | |||
| #include <pthread.h> | |||
| @@ -958,7 +958,7 @@ void BitArray::parseString (const String& text, | |||
| const MemoryBlock BitArray::toMemoryBlock() const throw() | |||
| { | |||
| const int numBytes = (getHighestBit() + 7) >> 3; | |||
| const int numBytes = (getHighestBit() + 8) >> 3; | |||
| MemoryBlock mb (numBytes); | |||
| for (int i = 0; i < numBytes; ++i) | |||
| @@ -1068,14 +1068,8 @@ const String File::getRelativePathFrom (const File& dir) const throw() | |||
| --commonBitLength; | |||
| // if the only common bit is the root, then just return the full path.. | |||
| #if JUCE_WIN32 | |||
| if (commonBitLength <= 0 | |||
| || (commonBitLength == 1 && thisPath [1] == File::separator) | |||
| || (commonBitLength <= 3 && thisPath [1] == T(':'))) | |||
| #else | |||
| if (commonBitLength <= 0 | |||
| || (commonBitLength == 1 && thisPath [1] == File::separator)) | |||
| #endif | |||
| return fullPath; | |||
| thisPath = thisPath.substring (commonBitLength); | |||
| @@ -1946,15 +1946,11 @@ const String String::toHexString (const unsigned char* data, | |||
| *d++ = hexDigits [(*data) & 0xf]; | |||
| ++data; | |||
| if (groupSize > 0 && (i % groupSize) == 0) | |||
| if (groupSize > 0 && (i % groupSize) == (groupSize - 1) && i < (size - 1)) | |||
| *d++ = T(' '); | |||
| } | |||
| if (groupSize > 0) | |||
| --d; | |||
| *d = 0; | |||
| return s; | |||
| } | |||