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