| @@ -161,7 +161,7 @@ const String AudioDeviceManager::initialise (const int numInputChannelsNeeded, | |||||
| if (e->getStringAttribute (T("audioDeviceName")).isNotEmpty()) | if (e->getStringAttribute (T("audioDeviceName")).isNotEmpty()) | ||||
| { | { | ||||
| setup.inputDeviceName = setup.outputDeviceName | |||||
| setup.inputDeviceName = setup.outputDeviceName | |||||
| = e->getStringAttribute (T("audioDeviceName")); | = e->getStringAttribute (T("audioDeviceName")); | ||||
| } | } | ||||
| else | else | ||||
| @@ -425,7 +425,7 @@ const String AudioDeviceManager::setAudioDeviceSetup (const AudioDeviceSetup& ne | |||||
| error = currentAudioDevice->open (inputChannels, | error = currentAudioDevice->open (inputChannels, | ||||
| outputChannels, | outputChannels, | ||||
| currentSetup.sampleRate, | |||||
| currentSetup.sampleRate, | |||||
| currentSetup.bufferSize); | currentSetup.bufferSize); | ||||
| if (error.isEmpty()) | if (error.isEmpty()) | ||||
| @@ -511,7 +511,7 @@ void AudioDeviceManager::restartLastAudioDevice() | |||||
| { | { | ||||
| if (currentAudioDevice == 0) | if (currentAudioDevice == 0) | ||||
| { | { | ||||
| if (currentSetup.inputDeviceName.isEmpty() | |||||
| if (currentSetup.inputDeviceName.isEmpty() | |||||
| && currentSetup.outputDeviceName.isEmpty()) | && currentSetup.outputDeviceName.isEmpty()) | ||||
| { | { | ||||
| // This method will only reload the last device that was running | // This method will only reload the last device that was running | ||||
| @@ -33,7 +33,7 @@ | |||||
| #define __JUCE_MESSAGE_JUCEHEADER__ | #define __JUCE_MESSAGE_JUCEHEADER__ | ||||
| class MessageListener; | class MessageListener; | ||||
| class MessageManager; | |||||
| //============================================================================== | //============================================================================== | ||||
| /** The base class for objects that can be delivered to a MessageListener. | /** The base class for objects that can be delivered to a MessageListener. | ||||
| @@ -1,433 +1,433 @@ | |||||
| /* | |||||
| ============================================================================== | |||||
| 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" | |||||
| #include "../LookAndFeel/juce_LookAndFeel.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), | |||||
| minimumHorizontalScale (0.7f), | |||||
| 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) | |||||
| { | |||||
| getLookAndFeel().drawLabel (g, *this); | |||||
| } | |||||
| 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" | |||||
| #include "../LookAndFeel/juce_LookAndFeel.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), | |||||
| minimumHorizontalScale (0.7f), | |||||
| 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) | |||||
| { | |||||
| getLookAndFeel().drawLabel (g, *this); | |||||
| } | |||||
| 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 | |||||
| @@ -154,11 +154,11 @@ public: | |||||
| */ | */ | ||||
| void setBorderSize (int horizontalBorder, int verticalBorder); | void setBorderSize (int horizontalBorder, int verticalBorder); | ||||
| /** | |||||
| /** Returns the size of the horizontal gap being left around the text. | |||||
| */ | */ | ||||
| int getHorizontalBorderSize() const throw() { return horizontalBorderSize; } | int getHorizontalBorderSize() const throw() { return horizontalBorderSize; } | ||||
| /** | |||||
| /** Returns the size of the vertical gap being left around the text. | |||||
| */ | */ | ||||
| int getVerticalBorderSize() const throw() { return verticalBorderSize; } | int getVerticalBorderSize() const throw() { return verticalBorderSize; } | ||||
| @@ -44,7 +44,7 @@ Image* juce_createIconForFile (const File& file); | |||||
| //============================================================================== | //============================================================================== | ||||
| FileListComponent::FileListComponent (DirectoryContentsList& listToShow) | FileListComponent::FileListComponent (DirectoryContentsList& listToShow) | ||||
| : ListBox (String::empty, 0), | : ListBox (String::empty, 0), | ||||
| DirectoryContentsDisplayComponent (listToShow) | |||||
| DirectoryContentsDisplayComponent (listToShow) | |||||
| { | { | ||||
| setModel (this); | setModel (this); | ||||
| fileList.addChangeListener (this); | fileList.addChangeListener (this); | ||||
| @@ -1200,31 +1200,31 @@ void LookAndFeel::positionComboBoxText (ComboBox& box, Label& label) | |||||
| //============================================================================== | //============================================================================== | ||||
| void LookAndFeel::drawLabel (Graphics& g, Label& label) | void LookAndFeel::drawLabel (Graphics& g, Label& label) | ||||
| { | { | ||||
| g.fillAll (label.findColour (Label::backgroundColourId)); | |||||
| if (! label.isBeingEdited()) | |||||
| { | |||||
| const float alpha = label.isEnabled() ? 1.0f : 0.5f; | |||||
| g.setColour (label.findColour (Label::textColourId).withMultipliedAlpha (alpha)); | |||||
| g.setFont (label.getFont()); | |||||
| g.drawFittedText (label.getText(), | |||||
| label.getHorizontalBorderSize(), | |||||
| label.getVerticalBorderSize(), | |||||
| label.getWidth() - 2 * label.getHorizontalBorderSize(), | |||||
| label.getHeight() - 2 * label.getVerticalBorderSize(), | |||||
| label.getJustificationType(), | |||||
| jmax (1, (int) (label.getHeight() / label.getFont().getHeight())), | |||||
| label.getMinimumHorizontalScale()); | |||||
| g.setColour (label.findColour (Label::outlineColourId).withMultipliedAlpha (alpha)); | |||||
| g.drawRect (0, 0, label.getWidth(), label.getHeight()); | |||||
| } | |||||
| else if (label.isEnabled()) | |||||
| { | |||||
| g.setColour (label.findColour (Label::outlineColourId)); | |||||
| g.drawRect (0, 0, label.getWidth(), label.getHeight()); | |||||
| } | |||||
| g.fillAll (label.findColour (Label::backgroundColourId)); | |||||
| if (! label.isBeingEdited()) | |||||
| { | |||||
| const float alpha = label.isEnabled() ? 1.0f : 0.5f; | |||||
| g.setColour (label.findColour (Label::textColourId).withMultipliedAlpha (alpha)); | |||||
| g.setFont (label.getFont()); | |||||
| g.drawFittedText (label.getText(), | |||||
| label.getHorizontalBorderSize(), | |||||
| label.getVerticalBorderSize(), | |||||
| label.getWidth() - 2 * label.getHorizontalBorderSize(), | |||||
| label.getHeight() - 2 * label.getVerticalBorderSize(), | |||||
| label.getJustificationType(), | |||||
| jmax (1, (int) (label.getHeight() / label.getFont().getHeight())), | |||||
| label.getMinimumHorizontalScale()); | |||||
| g.setColour (label.findColour (Label::outlineColourId).withMultipliedAlpha (alpha)); | |||||
| g.drawRect (0, 0, label.getWidth(), label.getHeight()); | |||||
| } | |||||
| else if (label.isEnabled()) | |||||
| { | |||||
| g.setColour (label.findColour (Label::outlineColourId)); | |||||
| g.drawRect (0, 0, label.getWidth(), label.getHeight()); | |||||
| } | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -1,442 +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; | |||||
| } | |||||
| } | |||||
| } | |||||
| 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 | |||||
| /* | |||||
| ============================================================================== | |||||
| 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 | |||||
| @@ -42,7 +42,7 @@ BEGIN_JUCE_NAMESPACE | |||||
| //============================================================================== | //============================================================================== | ||||
| class SimpleDeviceManagerInputLevelMeter : public Component, | |||||
| class SimpleDeviceManagerInputLevelMeter : public Component, | |||||
| public Timer | public Timer | ||||
| { | { | ||||
| public: | public: | ||||
| @@ -214,7 +214,7 @@ class AudioDeviceSettingsPanel : public Component, | |||||
| public ButtonListener | public ButtonListener | ||||
| { | { | ||||
| public: | public: | ||||
| AudioDeviceSettingsPanel (AudioIODeviceType* type_, | |||||
| AudioDeviceSettingsPanel (AudioIODeviceType* type_, | |||||
| AudioIODeviceType::DeviceSetupDetails& setup_, | AudioIODeviceType::DeviceSetupDetails& setup_, | ||||
| const bool hideAdvancedOptionsWithButton) | const bool hideAdvancedOptionsWithButton) | ||||
| : type (type_), | : type (type_), | ||||
| @@ -279,7 +279,7 @@ public: | |||||
| outputDeviceDropDown->setBounds (lx, y, w, h); | outputDeviceDropDown->setBounds (lx, y, w, h); | ||||
| if (testButton != 0) | if (testButton != 0) | ||||
| testButton->setBounds (proportionOfWidth (0.77f), | |||||
| testButton->setBounds (proportionOfWidth (0.77f), | |||||
| outputDeviceDropDown->getY(), | outputDeviceDropDown->getY(), | ||||
| proportionOfWidth (0.18f), | proportionOfWidth (0.18f), | ||||
| h); | h); | ||||
| @@ -290,7 +290,7 @@ public: | |||||
| { | { | ||||
| inputDeviceDropDown->setBounds (lx, y, w, h); | inputDeviceDropDown->setBounds (lx, y, w, h); | ||||
| inputLevelMeter->setBounds (proportionOfWidth (0.77f), | |||||
| inputLevelMeter->setBounds (proportionOfWidth (0.77f), | |||||
| inputDeviceDropDown->getY(), | inputDeviceDropDown->getY(), | ||||
| proportionOfWidth (0.18f), | proportionOfWidth (0.18f), | ||||
| h); | h); | ||||
| @@ -323,7 +323,7 @@ public: | |||||
| if (sampleRateDropDown != 0) | if (sampleRateDropDown != 0) | ||||
| { | { | ||||
| sampleRateDropDown->setVisible (showAdvancedSettingsButton == 0 | |||||
| sampleRateDropDown->setVisible (showAdvancedSettingsButton == 0 | |||||
| || ! showAdvancedSettingsButton->isVisible()); | || ! showAdvancedSettingsButton->isVisible()); | ||||
| sampleRateDropDown->setBounds (lx, y, w, h); | sampleRateDropDown->setBounds (lx, y, w, h); | ||||
| @@ -360,11 +360,11 @@ public: | |||||
| || comboBoxThatHasChanged == inputDeviceDropDown) | || comboBoxThatHasChanged == inputDeviceDropDown) | ||||
| { | { | ||||
| if (outputDeviceDropDown != 0) | if (outputDeviceDropDown != 0) | ||||
| config.outputDeviceName = outputDeviceDropDown->getSelectedId() < 0 ? String::empty | |||||
| config.outputDeviceName = outputDeviceDropDown->getSelectedId() < 0 ? String::empty | |||||
| : outputDeviceDropDown->getText(); | : outputDeviceDropDown->getText(); | ||||
| if (inputDeviceDropDown != 0) | if (inputDeviceDropDown != 0) | ||||
| config.inputDeviceName = inputDeviceDropDown->getSelectedId() < 0 ? String::empty | |||||
| config.inputDeviceName = inputDeviceDropDown->getSelectedId() < 0 ? String::empty | |||||
| : inputDeviceDropDown->getText(); | : inputDeviceDropDown->getText(); | ||||
| if (! type->hasSeparateInputsAndOutputs()) | if (! type->hasSeparateInputsAndOutputs()) | ||||
| @@ -460,7 +460,7 @@ public: | |||||
| outputDeviceDropDown->addListener (this); | outputDeviceDropDown->addListener (this); | ||||
| addAndMakeVisible (outputDeviceDropDown); | addAndMakeVisible (outputDeviceDropDown); | ||||
| outputDeviceLabel = new Label (String::empty, | |||||
| outputDeviceLabel = new Label (String::empty, | |||||
| type->hasSeparateInputsAndOutputs() ? TRANS ("output:") | type->hasSeparateInputsAndOutputs() ? TRANS ("output:") | ||||
| : TRANS ("device:")); | : TRANS ("device:")); | ||||
| outputDeviceLabel->attachToComponent (outputDeviceDropDown, true); | outputDeviceLabel->attachToComponent (outputDeviceDropDown, true); | ||||
| @@ -486,7 +486,7 @@ public: | |||||
| inputDeviceLabel = new Label (String::empty, TRANS ("input:")); | inputDeviceLabel = new Label (String::empty, TRANS ("input:")); | ||||
| inputDeviceLabel->attachToComponent (inputDeviceDropDown, true); | inputDeviceLabel->attachToComponent (inputDeviceDropDown, true); | ||||
| addAndMakeVisible (inputLevelMeter | |||||
| addAndMakeVisible (inputLevelMeter | |||||
| = new SimpleDeviceManagerInputLevelMeter (setup.manager)); | = new SimpleDeviceManagerInputLevelMeter (setup.manager)); | ||||
| } | } | ||||
| @@ -499,13 +499,13 @@ public: | |||||
| if (currentDevice != 0) | if (currentDevice != 0) | ||||
| { | { | ||||
| if (setup.maxNumOutputChannels > 0 | |||||
| if (setup.maxNumOutputChannels > 0 | |||||
| && setup.minNumOutputChannels < setup.manager->getCurrentAudioDevice()->getOutputChannelNames().size()) | && setup.minNumOutputChannels < setup.manager->getCurrentAudioDevice()->getOutputChannelNames().size()) | ||||
| { | { | ||||
| if (outputChanList == 0) | if (outputChanList == 0) | ||||
| { | { | ||||
| addAndMakeVisible (outputChanList | |||||
| = new ChannelSelectorListBox (setup, ChannelSelectorListBox::audioOutputType, | |||||
| addAndMakeVisible (outputChanList | |||||
| = new ChannelSelectorListBox (setup, ChannelSelectorListBox::audioOutputType, | |||||
| TRANS ("(no audio output channels found)"))); | TRANS ("(no audio output channels found)"))); | ||||
| outputChanLabel = new Label (String::empty, TRANS ("active output channels:")); | outputChanLabel = new Label (String::empty, TRANS ("active output channels:")); | ||||
| outputChanLabel->attachToComponent (outputChanList, true); | outputChanLabel->attachToComponent (outputChanList, true); | ||||
| @@ -525,7 +525,7 @@ public: | |||||
| if (inputChanList == 0) | if (inputChanList == 0) | ||||
| { | { | ||||
| addAndMakeVisible (inputChanList | addAndMakeVisible (inputChanList | ||||
| = new ChannelSelectorListBox (setup, ChannelSelectorListBox::audioInputType, | |||||
| = new ChannelSelectorListBox (setup, ChannelSelectorListBox::audioInputType, | |||||
| TRANS ("(no audio input channels found)"))); | TRANS ("(no audio input channels found)"))); | ||||
| inputChanLabel = new Label (String::empty, TRANS ("active input channels:")); | inputChanLabel = new Label (String::empty, TRANS ("active input channels:")); | ||||
| inputChanLabel->attachToComponent (inputChanList, true); | inputChanLabel->attachToComponent (inputChanList, true); | ||||
| @@ -832,7 +832,7 @@ private: | |||||
| int getBestHeight (int maxHeight) | int getBestHeight (int maxHeight) | ||||
| { | { | ||||
| return getRowHeight() * jlimit (2, jmax (2, maxHeight / getRowHeight()), | |||||
| return getRowHeight() * jlimit (2, jmax (2, maxHeight / getRowHeight()), | |||||
| getNumRows()) | getNumRows()) | ||||
| + getOutlineThickness() * 2; | + getOutlineThickness() * 2; | ||||
| } | } | ||||
| @@ -974,7 +974,7 @@ AudioDeviceSelectorComponent::AudioDeviceSelectorComponent (AudioDeviceManager& | |||||
| for (int i = 0; i < deviceManager_.getAvailableDeviceTypes().size(); ++i) | for (int i = 0; i < deviceManager_.getAvailableDeviceTypes().size(); ++i) | ||||
| { | { | ||||
| deviceTypeDropDown | deviceTypeDropDown | ||||
| ->addItem (deviceManager_.getAvailableDeviceTypes().getUnchecked(i)->getTypeName(), | |||||
| ->addItem (deviceManager_.getAvailableDeviceTypes().getUnchecked(i)->getTypeName(), | |||||
| i + 1); | i + 1); | ||||
| } | } | ||||
| @@ -1100,15 +1100,15 @@ void AudioDeviceSelectorComponent::changeListenerCallback (void*) | |||||
| deviceTypeDropDown->setText (deviceManager.getCurrentAudioDeviceType(), false); | deviceTypeDropDown->setText (deviceManager.getCurrentAudioDeviceType(), false); | ||||
| } | } | ||||
| if (audioDeviceSettingsComp == 0 | |||||
| if (audioDeviceSettingsComp == 0 | |||||
| || audioDeviceSettingsCompType != deviceManager.getCurrentAudioDeviceType()) | || audioDeviceSettingsCompType != deviceManager.getCurrentAudioDeviceType()) | ||||
| { | { | ||||
| audioDeviceSettingsCompType = deviceManager.getCurrentAudioDeviceType(); | audioDeviceSettingsCompType = deviceManager.getCurrentAudioDeviceType(); | ||||
| deleteAndZero (audioDeviceSettingsComp); | deleteAndZero (audioDeviceSettingsComp); | ||||
| AudioIODeviceType* const type | |||||
| = deviceManager.getAvailableDeviceTypes() [deviceTypeDropDown == 0 | |||||
| AudioIODeviceType* const type | |||||
| = deviceManager.getAvailableDeviceTypes() [deviceTypeDropDown == 0 | |||||
| ? 0 : deviceTypeDropDown->getSelectedId() - 1]; | ? 0 : deviceTypeDropDown->getSelectedId() - 1]; | ||||
| if (type != 0) | if (type != 0) | ||||
| @@ -66,7 +66,7 @@ public: | |||||
| @param maxAudioOutputChannels the maximum number of audio output channels that the application needs | @param maxAudioOutputChannels the maximum number of audio output channels that the application needs | ||||
| @param showMidiInputOptions if true, the component will allow the user to select which midi inputs are enabled | @param showMidiInputOptions if true, the component will allow the user to select which midi inputs are enabled | ||||
| @param showMidiOutputSelector if true, the component will let the user choose a default midi output device | @param showMidiOutputSelector if true, the component will let the user choose a default midi output device | ||||
| @param showChannelsAsStereoPairs if true, channels will be treated as pairs; if false, channels will be | |||||
| @param showChannelsAsStereoPairs if true, channels will be treated as pairs; if false, channels will be | |||||
| treated as a set of separate mono channels. | treated as a set of separate mono channels. | ||||
| @param hideAdvancedOptionsWithButton if true, only the minimum amount of UI components | @param hideAdvancedOptionsWithButton if true, only the minimum amount of UI components | ||||
| are shown, with an "advanced" button that shows the rest of them | are shown, with an "advanced" button that shows the rest of them | ||||
| @@ -1,378 +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 (...) | |||||
| {} | |||||
| 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 | |||||
| /* | |||||
| ============================================================================== | |||||
| 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 | |||||
| @@ -104,7 +104,7 @@ protected: | |||||
| elements = 0; | elements = 0; | ||||
| } | } | ||||
| numAllocated = numElements; | |||||
| numAllocated = numElements; | |||||
| } | } | ||||
| } | } | ||||